From 8ab428b6600e0d0263b2986aea02553dc3fc931d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 17:42:44 -0700 Subject: [PATCH 001/364] try new gcd Signed-off-by: Nikolaj Bjorner --- src/util/mpz.cpp | 120 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 33 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 39ea428a7..409ca325f 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -45,43 +45,97 @@ Revision History: #define LEHMER_GCD #endif -template -static T gcd_core(T u, T v) { - if (u == 0) - return v; - if (v == 0) - return u; - int k; - - for (k = 0; ((u | v) & 1) == 0; ++k) { - u >>= 1; - v >>= 1; - } - - while ((u & 1) == 0) - u >>= 1; - +#if 1 +#include + +#define _trailing_zeros32(x) _tzcnt_u32(x) + +#ifdef _AMD64_ +#define _trailing_zeros64(x) _tzcnt_u64(x) +#else +inline uint64 _trailing_zeros64(uint64 x) { + uint64 r = 0; + for (; 0 == (x & 1) && r < 64; ++r, x >>= 1); + return r; +} +#endif + +#else + +inline unsigned _trailing_zeros32(unsigned x) { + unsigned r = 0; + for (; 0 == (x & 1) && r < 32; ++r, x >>= 1); + return r; +} +#endif + +#define _bit_min(x, y) (y + ((x - y) & ((int)(x - y) >> 31))) +#define _bit_max(x, y) (x - ((x - y) & ((int)(x - y) >> 31))) + + + +unsigned u_gcd(unsigned u, unsigned v) { + if (u == 0) return v; + if (v == 0) return u; + unsigned shift = _trailing_zeros32(u | v); + u >>= _trailing_zeros32(u); + v >>= _trailing_zeros32(v); + if (u == 1 || v == 1) return 1 << shift; + if (u == v) return u << shift; do { - while ((v & 1) == 0) - v >>= 1; - - if (u < v) { - v -= u; - } - else { - T diff = u - v; - u = v; - v = diff; - } - v >>= 1; - } while (v != 0); - - return u << k; +#if 1 + unsigned diff = u - v; + unsigned mdiff = diff & (unsigned)((int)diff >> 31); + u = v + mdiff; // min + v = diff - 2 * mdiff; // if v <= u: u - v, if v > u: v - u = u - v - 2 * (u - v) +#endif +#if 0 + unsigned t = _bit_max(u, v); + u = _bit_min(u, v); + v = t; + v -= u; +#endif +#if 0 + unsigned t = std::max(u, v); + u = std::min(u,v); + v = t; + v -= u; +#endif +#if 0 + if (u > v) std::swap(u, v); + v -= u; +#endif +#if 0 + unsigned d1 = u - v; + unsigned d2 = v - u; + unsigned md21 = d2 & (unsigned)((int)d1 >> 31); + unsigned md12 = d1 & (unsigned)((int)d2 >> 31); + u = _bit_min(u, v); + v = md12 | md21; +#endif + + v >>= _trailing_zeros32(v); + } + while (v != 0); + return u << shift; +} + +uint64 u64_gcd(uint64 u, uint64 v) { + if (u == 0) return v; + if (v == 0) return u; + if (u == 1 || v == 1) return 1; + uint64 shift = _trailing_zeros64(u | v); + u >>= _trailing_zeros64(u); + do { + v >>= _trailing_zeros64(v); + if (u > v) std::swap(u, v); + v -= u; + } + while (v != 0); + return u << shift; } -unsigned u_gcd(unsigned u, unsigned v) { return gcd_core(u, v); } -uint64_t u64_gcd(uint64_t u, uint64_t v) { return gcd_core(u, v); } template mpz_manager::mpz_manager(): From 99bdb461583e34f16df6b7f6508eebeafaf9d893 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 17:48:30 -0700 Subject: [PATCH 002/364] int64_t Signed-off-by: Nikolaj Bjorner --- src/util/mpz.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 409ca325f..8f65a3602 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -74,7 +74,6 @@ inline unsigned _trailing_zeros32(unsigned x) { #define _bit_max(x, y) (x - ((x - y) & ((int)(x - y) >> 31))) - unsigned u_gcd(unsigned u, unsigned v) { if (u == 0) return v; if (v == 0) return u; @@ -121,11 +120,11 @@ unsigned u_gcd(unsigned u, unsigned v) { return u << shift; } -uint64 u64_gcd(uint64 u, uint64 v) { +uint64_t u64_gcd(uint64_t u, uint64_t v) { if (u == 0) return v; if (v == 0) return u; if (u == 1 || v == 1) return 1; - uint64 shift = _trailing_zeros64(u | v); + auto shift = _trailing_zeros64(u | v); u >>= _trailing_zeros64(u); do { v >>= _trailing_zeros64(v); From d7f51f2443edd5ee57255fc652a2da5e7f990aae Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 18:20:23 -0700 Subject: [PATCH 003/364] try flags to fix gcc build Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a086afd71..f34e400f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,7 +381,7 @@ endif() # FIXME: Support ARM "-mfpu=vfp -mfloat-abi=hard" if (("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") OR ("${TARGET_ARCHITECTURE}" STREQUAL "i686")) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) - set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2") + set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2" "-mfsr") # FIXME: Remove "x.." when CMP0054 is set to NEW elseif ("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") set(SSE_FLAGS "/arch:SSE2") From ad67424987aeae720054b21f3196471dac08f474 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 18:23:04 -0700 Subject: [PATCH 004/364] deal with shift exponent error Signed-off-by: Nikolaj Bjorner --- src/util/mpz.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 8f65a3602..6387fccd9 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -79,10 +79,10 @@ unsigned u_gcd(unsigned u, unsigned v) { if (v == 0) return u; unsigned shift = _trailing_zeros32(u | v); u >>= _trailing_zeros32(u); - v >>= _trailing_zeros32(v); if (u == 1 || v == 1) return 1 << shift; if (u == v) return u << shift; do { + v >>= _trailing_zeros32(v); #if 1 unsigned diff = u - v; unsigned mdiff = diff & (unsigned)((int)diff >> 31); @@ -113,8 +113,6 @@ unsigned u_gcd(unsigned u, unsigned v) { u = _bit_min(u, v); v = md12 | md21; #endif - - v >>= _trailing_zeros32(v); } while (v != 0); return u << shift; From 8565de2c5b38cd355ea15f1da3f460f352162f86 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 19:17:37 -0700 Subject: [PATCH 005/364] deal with shift exponent error Signed-off-by: Nikolaj Bjorner --- src/util/mpz.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 6387fccd9..2a85320a5 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -49,9 +49,13 @@ Revision History: #if 1 #include -#define _trailing_zeros32(x) _tzcnt_u32(x) +#if defined(__GNUC__) +#define _trailing_zeros32(X) __builtin_ctz(X) +#else +#define _trailing_zeros32(X) _tzcnt_u32(X) +#endif -#ifdef _AMD64_ +#if defined(_AMD64_) && !defined(__GNUC__) #define _trailing_zeros64(x) _tzcnt_u64(x) #else inline uint64 _trailing_zeros64(uint64 x) { From bb5306031369dd22cfcc291ea7aeeb83b2c8fc63 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 19:26:40 -0700 Subject: [PATCH 006/364] int64_t Signed-off-by: Nikolaj Bjorner --- src/util/mpz.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 2a85320a5..50f0d68e7 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -58,8 +58,8 @@ Revision History: #if defined(_AMD64_) && !defined(__GNUC__) #define _trailing_zeros64(x) _tzcnt_u64(x) #else -inline uint64 _trailing_zeros64(uint64 x) { - uint64 r = 0; +inline uint64_t _trailing_zeros64(uint64_t x) { + uint64_t r = 0; for (; 0 == (x & 1) && r < 64; ++r, x >>= 1); return r; } From 88ead235f0f8ab16e90efc52bc4b2b5df7269123 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 19:30:56 -0700 Subject: [PATCH 007/364] gcc mode Signed-off-by: Nikolaj Bjorner --- src/util/mpz.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 50f0d68e7..ddca84922 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -55,8 +55,12 @@ Revision History: #define _trailing_zeros32(X) _tzcnt_u32(X) #endif -#if defined(_AMD64_) && !defined(__GNUC__) -#define _trailing_zeros64(x) _tzcnt_u64(x) +#if defined(_AMD64_) + #if defined(__GNUC__) + #define _trailing_zeros64(X) __builtin_ctzll(X) + #else + #define _trailing_zeros64(X) _tzcnt_u64(X) + #endif #else inline uint64_t _trailing_zeros64(uint64_t x) { uint64_t r = 0; From 9e916edcb04e35bb39a43a42911f3e062f003bcc Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 7 Jun 2018 15:40:04 +0100 Subject: [PATCH 008/364] z3.py: add overflow checks to PB API --- src/api/python/z3/z3.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 7657fb347..ab2217e13 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -90,6 +90,9 @@ def _z3_assert(cond, msg): if not cond: raise Z3Exception(msg) +def _z3_check_cint_overflow(n, name): + _z3_assert(ctypes.c_int(n).value == n, name + " is too large") + def open_log(fname): """Log interaction to a file. This function must be invoked immediately after init(). """ Z3_open_log(fname) @@ -8128,6 +8131,7 @@ def _pb_args_coeffs(args, default_ctx = None): _args, sz = _to_ast_array(args) _coeffs = (ctypes.c_int * len(coeffs))() for i in range(len(coeffs)): + _z3_check_cint_overflow(coeffs[i], "coefficient") _coeffs[i] = coeffs[i] return ctx, sz, _args, _coeffs @@ -8137,6 +8141,7 @@ def PbLe(args, k): >>> a, b, c = Bools('a b c') >>> f = PbLe(((a,1),(b,3),(c,2)), 3) """ + _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pble(ctx.ref(), sz, _args, _coeffs, k), ctx) @@ -8146,6 +8151,7 @@ def PbGe(args, k): >>> a, b, c = Bools('a b c') >>> f = PbGe(((a,1),(b,3),(c,2)), 3) """ + _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pbge(ctx.ref(), sz, _args, _coeffs, k), ctx) @@ -8155,6 +8161,7 @@ def PbEq(args, k, ctx = None): >>> a, b, c = Bools('a b c') >>> f = PbEq(((a,1),(b,3),(c,2)), 3) """ + _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pbeq(ctx.ref(), sz, _args, _coeffs, k), ctx) From 29c26724071362cc87c23f9e9cad6a63b3334f68 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 7 Jun 2018 21:43:37 -0700 Subject: [PATCH 009/364] fix bugs exposed by Nuno's PB example Signed-off-by: Nikolaj Bjorner --- src/sat/ba_solver.cpp | 25 ++++++++++++++++++++----- src/sat/ba_solver.h | 8 ++++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp index 4fbf7570d..669389d2a 100644 --- a/src/sat/ba_solver.cpp +++ b/src/sat/ba_solver.cpp @@ -166,10 +166,11 @@ namespace sat { unsigned w = 0; for (unsigned i = 0; i < m_size; ++i) { m_wlits[i].second.neg(); + VERIFY(w + m_wlits[i].first >= w); w += m_wlits[i].first; - } + } m_k = w - m_k + 1; - SASSERT(w >= m_k && m_k > 0); + VERIFY(w >= m_k && m_k > 0); } bool ba_solver::pb::is_watching(literal l) const { @@ -526,11 +527,13 @@ namespace sat { bool ba_solver::init_watch(pb& p) { clear_watch(p); if (p.lit() != null_literal && value(p.lit()) == l_false) { + //IF_VERBOSE(0, verbose_stream() << "negate: " << p.k() << "\n"); p.negate(); } VERIFY(p.lit() == null_literal || value(p.lit()) == l_true); unsigned sz = p.size(), bound = p.k(); + //IF_VERBOSE(0, verbose_stream() << "bound: " << p.k() << "\n"); // put the non-false literals into the head. unsigned slack = 0, slack1 = 0, num_watch = 0, j = 0; @@ -833,9 +836,19 @@ namespace sat { remove_constraint(p, "recompiled to cardinality"); return; } - else { p.set_size(sz); + p.update_max_sum(); + if (p.max_sum() < k) { + if (p.lit() == null_literal) { + s().set_conflict(justification()); + } + else { + s().assign(~p.lit(), justification()); + } + remove_constraint(p, "recompiled to false"); + return; + } p.set_k(k); SASSERT(p.well_formed()); @@ -3210,6 +3223,7 @@ namespace sat { if (is_marked(l) && m_weights[l.index()] <= p2.get_coeff(i)) { ++num_sub; } + if (p1.size() + i > p2.size() + num_sub) return false; } return num_sub == p1.size(); } @@ -3364,8 +3378,9 @@ namespace sat { m_weights.setx(l.second.index(), l.first, 0); mark_visited(l.second); } - for (unsigned i = 0; i < p1.num_watch(); ++i) { - subsumes(p1, p1[i].second); + for (unsigned i = 0; i < std::min(10u, p1.num_watch()); ++i) { + unsigned j = s().m_rand() % p1.num_watch(); + subsumes(p1, p1[j].second); } for (wliteral l : p1) { m_weights[l.second.index()] = 0; diff --git a/src/sat/ba_solver.h b/src/sat/ba_solver.h index 07e7cfd58..e947cee96 100644 --- a/src/sat/ba_solver.h +++ b/src/sat/ba_solver.h @@ -124,8 +124,8 @@ namespace sat { protected: unsigned m_k; public: - pb_base(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k): constraint(t, id, l, sz, osz), m_k(k) {} - virtual void set_k(unsigned k) { m_k = k; } + pb_base(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k): constraint(t, id, l, sz, osz), m_k(k) { VERIFY(k < 4000000000); } + virtual void set_k(unsigned k) { VERIFY(k < 4000000000); m_k = k; } virtual unsigned get_coeff(unsigned i) const { UNREACHABLE(); return 0; } unsigned k() const { return m_k; } virtual bool well_formed() const; @@ -157,7 +157,6 @@ namespace sat { unsigned m_num_watch; unsigned m_max_sum; wliteral m_wlits[0]; - void update_max_sum(); public: static size_t get_obj_size(unsigned num_lits) { return sizeof(pb) + num_lits * sizeof(wliteral); } pb(unsigned id, literal lit, svector const& wlits, unsigned k); @@ -171,10 +170,11 @@ namespace sat { void set_slack(unsigned s) { m_slack = s; } unsigned num_watch() const { return m_num_watch; } unsigned max_sum() const { return m_max_sum; } + void update_max_sum(); void set_num_watch(unsigned s) { m_num_watch = s; } bool is_cardinality() const; virtual void negate(); - virtual void set_k(unsigned k) { m_k = k; update_max_sum(); } + virtual void set_k(unsigned k) { m_k = k; VERIFY(k < 4000000000); update_max_sum(); } virtual void swap(unsigned i, unsigned j) { std::swap(m_wlits[i], m_wlits[j]); } virtual literal_vector literals() const { literal_vector lits; for (auto wl : *this) lits.push_back(wl.second); return lits; } virtual bool is_watching(literal l) const; From 4547f2c001d894808987487a5e98c713debff411 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 7 Jun 2018 22:03:03 -0700 Subject: [PATCH 010/364] enable non-expression bodies of quantifiers to fix #1667 Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 10 +++++++--- src/util/mpz.cpp | 9 --------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 7657fb347..89615e11e 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1898,13 +1898,17 @@ def is_quantifier(a): def _mk_quantifier(is_forall, vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): if __debug__: - _z3_assert(is_bool(body), "Z3 expression expected") + _z3_assert(is_bool(body) or is_app(vs) or (len(vs) > 0 and is_app(vs[0])), "Z3 expression expected") _z3_assert(is_const(vs) or (len(vs) > 0 and all([ is_const(v) for v in vs])), "Invalid bounded variable(s)") _z3_assert(all([is_pattern(a) or is_expr(a) for a in patterns]), "Z3 patterns expected") - _z3_assert(all([is_expr(p) for p in no_patterns]), "no patterns are Z3 expressions") - ctx = body.ctx + _z3_assert(all([is_expr(p) for p in no_patterns]), "no patterns are Z3 expressions") if is_app(vs): + ctx = vs.ctx vs = [vs] + else: + ctx = vs[0].ctx + if not is_expr(body): + body = BoolVal(body, ctx) num_vars = len(vs) if num_vars == 0: return body diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index ddca84922..861a31cfb 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -46,7 +46,6 @@ Revision History: #endif -#if 1 #include #if defined(__GNUC__) @@ -69,14 +68,6 @@ inline uint64_t _trailing_zeros64(uint64_t x) { } #endif -#else - -inline unsigned _trailing_zeros32(unsigned x) { - unsigned r = 0; - for (; 0 == (x & 1) && r < 32; ++r, x >>= 1); - return r; -} -#endif #define _bit_min(x, y) (y + ((x - y) & ((int)(x - y) >> 31))) #define _bit_max(x, y) (x - ((x - y) & ((int)(x - y) >> 31))) From 0520d1a1f694339039a68796353c4ee040b94e46 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 8 Jun 2018 07:38:30 -0700 Subject: [PATCH 011/364] remove trial with mfsr flag Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f34e400f5..a086afd71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,7 +381,7 @@ endif() # FIXME: Support ARM "-mfpu=vfp -mfloat-abi=hard" if (("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") OR ("${TARGET_ARCHITECTURE}" STREQUAL "i686")) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) - set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2" "-mfsr") + set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2") # FIXME: Remove "x.." when CMP0054 is set to NEW elseif ("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") set(SSE_FLAGS "/arch:SSE2") From 63a1b2e7147ac2cd447c1ef3a3565bcc389eb4b0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 8 Jun 2018 10:30:20 -0700 Subject: [PATCH 012/364] fix #1665 Signed-off-by: Nikolaj Bjorner --- src/ast/static_features.cpp | 6 +++++- src/ast/static_features.h | 1 + src/smt/smt_setup.cpp | 21 +++++++++++---------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/ast/static_features.cpp b/src/ast/static_features.cpp index 9f52ea9a5..c8a1adcbe 100644 --- a/src/ast/static_features.cpp +++ b/src/ast/static_features.cpp @@ -81,6 +81,7 @@ void static_features::reset() { m_has_str = false; m_has_seq_non_str = false; m_has_arrays = false; + m_has_ext_arrays = false; m_arith_k_sum .reset(); m_num_arith_terms = 0; m_num_arith_eqs = 0; @@ -271,8 +272,11 @@ void static_features::update_core(expr * e) { m_has_bv = true; if (!m_has_fpa && (m_fpautil.is_float(e) || m_fpautil.is_rm(e))) m_has_fpa = true; - if (!m_has_arrays && m_arrayutil.is_array(e)) + if (!m_has_arrays && m_arrayutil.is_array(e)) m_has_arrays = true; + if (!m_has_ext_arrays && m_arrayutil.is_array(e) && + !m_arrayutil.is_select(e) && !m_arrayutil.is_store(e)) + m_has_ext_arrays = true; if (!m_has_str && m_sequtil.str.is_string_term(e)) m_has_str = true; if (!m_has_seq_non_str && m_sequtil.str.is_non_string_sequence(e)) { diff --git a/src/ast/static_features.h b/src/ast/static_features.h index 5473ba0ff..197947026 100644 --- a/src/ast/static_features.h +++ b/src/ast/static_features.h @@ -82,6 +82,7 @@ struct static_features { bool m_has_str; // has String-typed terms bool m_has_seq_non_str; // has non-String-typed Sequence terms bool m_has_arrays; // + bool m_has_ext_arrays; // does this use extended array theory. rational m_arith_k_sum; // sum of the numerals in arith atoms. unsigned m_num_arith_terms; unsigned m_num_arith_eqs; // equalities of the form t = k where k is a numeral diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 83b15fd90..55ea55663 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -576,17 +576,18 @@ namespace smt { m_params.m_nnf_cnf = false; m_params.m_propagate_booleans = true; m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_QF_AX() { + TRACE("setup", tout << "QF_AX\n";); m_params.m_array_mode = AR_SIMPLE; m_params.m_nnf_cnf = false; - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_QF_AX(static_features const & st) { - m_params.m_array_mode = AR_SIMPLE; + m_params.m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; m_params.m_nnf_cnf = false; if (st.m_num_clauses == st.m_num_units) { m_params.m_relevancy_lvl = 0; @@ -595,7 +596,7 @@ namespace smt { else { m_params.m_relevancy_lvl = 2; } - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_QF_AUFLIA() { @@ -607,11 +608,11 @@ namespace smt { m_params.m_restart_factor = 1.5; m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; setup_i_arith(); - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_QF_AUFLIA(static_features const & st) { - m_params.m_array_mode = AR_SIMPLE; + m_params.m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); m_params.m_nnf_cnf = false; @@ -631,7 +632,7 @@ namespace smt { // m_context.register_plugin(new smt::theory_si_arith(m_manager, m_params)); // else setup_i_arith(); - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_AUFLIA(bool simple_array) { @@ -992,17 +993,17 @@ namespace smt { } if (st.num_theories() == 1 && st.m_has_arrays) { - setup_QF_AX(); + setup_QF_AX(st); return; } - if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && st.m_has_bv) { + if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && !st.m_has_ext_arrays && st.m_has_bv) { setup_QF_AUFBV(); return; } if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && st.m_has_int) { - setup_QF_AUFLIA(); + setup_QF_AUFLIA(st); return; } From e94b97376c50b80fea4b8c8b01e8c29ee27d8f0f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 14 Jun 2018 10:16:03 -0700 Subject: [PATCH 013/364] fix memory leak in relation_manager, use for loops Signed-off-by: Nikolaj Bjorner --- src/muz/rel/dl_relation_manager.cpp | 102 ++++++++++------------------ 1 file changed, 36 insertions(+), 66 deletions(-) diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp index c9fc4f173..ff12e66c9 100644 --- a/src/muz/rel/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -210,11 +210,9 @@ namespace datalog { if(m_favourite_relation_plugin && m_favourite_relation_plugin->can_handle_signature(s)) { return m_favourite_relation_plugin; } - relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); - relation_plugin_vector::iterator rpend = m_relation_plugins.end(); - for(; rpit!=rpend; ++rpit) { - if((*rpit)->can_handle_signature(s)) { - return *rpit; + for (auto * r : m_relation_plugins) { + if (r->can_handle_signature(s)) { + return r; } } return nullptr; @@ -232,11 +230,9 @@ namespace datalog { if (m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { return m_favourite_table_plugin; } - table_plugin_vector::iterator tpit = m_table_plugins.begin(); - table_plugin_vector::iterator tpend = m_table_plugins.end(); - for(; tpit!=tpend; ++tpit) { - if((*tpit)->can_handle_signature(t)) { - return *tpit; + for (auto * a : m_table_plugins) { + if (a->can_handle_signature(t)) { + return a; } } return nullptr; @@ -251,11 +247,9 @@ namespace datalog { } relation_plugin * relation_manager::get_relation_plugin(symbol const& s) { - relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); - relation_plugin_vector::iterator rpend = m_relation_plugins.end(); - for(; rpit!=rpend; ++rpit) { - if((*rpit)->get_name()==s) { - return *rpit; + for (auto* r : m_relation_plugins) { + if(r->get_name() == s) { + return r; } } return nullptr; @@ -480,46 +474,37 @@ namespace datalog { std::string relation_manager::to_nice_string(const relation_signature & s) const { std::string res("["); bool first = true; - relation_signature::const_iterator it = s.begin(); - relation_signature::const_iterator end = s.end(); - for(; it!=end; ++it) { - if(first) { + for (auto const& sig : s) { + if (first) { first = false; } else { - res+=','; + res += ','; } - res+=to_nice_string(*it); + res += to_nice_string(sig); } - res+=']'; + res += ']'; return res; } void relation_manager::display(std::ostream & out) const { - relation_map::iterator it=m_relations.begin(); - relation_map::iterator end=m_relations.end(); - for(;it!=end;++it) { - out << "Table " << it->m_key->get_name() << "\n"; - it->m_value->display(out); + for (auto const& kv : m_relations) { + out << "Table " << kv.m_key->get_name() << "\n"; + kv.m_value->display(out); } } void relation_manager::display_relation_sizes(std::ostream & out) const { - relation_map::iterator it=m_relations.begin(); - relation_map::iterator end=m_relations.end(); - for(;it!=end;++it) { - out << "Relation " << it->m_key->get_name() << " has size " - << it->m_value->get_size_estimate_rows() << "\n"; + for (auto const& kv : m_relations) { + out << "Relation " << kv.m_key->get_name() << " has size " + << kv.m_value->get_size_estimate_rows() << "\n"; } } void relation_manager::display_output_tables(rule_set const& rules, std::ostream & out) const { const decl_set & output_preds = rules.get_output_predicates(); - decl_set::iterator it=output_preds.begin(); - decl_set::iterator end=output_preds.end(); - for(; it!=end; ++it) { - func_decl * pred = *it; + for (func_decl * pred : output_preds) { relation_base * rel = try_get_relation(pred); if (!rel) { out << "Tuples in " << pred->get_name() << ": \n"; @@ -1016,11 +1001,8 @@ namespace datalog { SASSERT(plugin.can_handle_signature(res_sign)); table_base * res = plugin.mk_empty(res_sign); - table_base::iterator it = t.begin(); - table_base::iterator end = t.end(); - - for(; it!=end; ++it) { - it->get_fact(m_row); + for (table_base::row_interface& a : t) { + a.get_fact(m_row); modify_fact(m_row); res->add_fact(m_row); } @@ -1191,13 +1173,10 @@ namespace datalog { table_fact m_row; public: void operator()(table_base & tgt, const table_base & src, table_base * delta) override { - table_base::iterator it = src.begin(); - table_base::iterator iend = src.end(); + for (table_base::row_interface& a : src) { + a.get_fact(m_row); - for(; it!=iend; ++it) { - it->get_fact(m_row); - - if(delta) { + if (delta) { if(!tgt.contains_fact(m_row)) { tgt.add_new_fact(m_row); delta->add_fact(m_row); @@ -1260,11 +1239,9 @@ namespace datalog { void operator()(table_base & r) { m_to_remove.reset(); unsigned sz = 0; - table_base::iterator it = r.begin(); - table_base::iterator iend = r.end(); - for(; it!=iend; ++it) { - it->get_fact(m_row); - if(should_remove(m_row)) { + for (table_base::row_interface& a : r) { + a.get_fact(m_row); + if (should_remove(m_row)) { m_to_remove.append(m_row.size(), m_row.c_ptr()); ++sz; } @@ -1456,7 +1433,7 @@ namespace datalog { m_removed_cols(removed_col_cnt, removed_cols) {} table_base* operator()(const table_base & tb) override { - table_base *t2 = tb.clone(); + scoped_rel t2 = tb.clone(); (*m_filter)(*t2); if (!m_project) { relation_manager & rmgr = t2->get_plugin().get_manager(); @@ -1572,8 +1549,7 @@ namespace datalog { TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); scoped_rel aux = t1.clone(); (*m_filter)(*aux); - table_base * res = (*m_project)(*aux); - return res; + return (*m_project)(*aux); } }; @@ -1614,11 +1590,8 @@ namespace datalog { m_aux_table->reset(); } - - table_base::iterator it = t.begin(); - table_base::iterator iend = t.end(); - for(; it!=iend; ++it) { - it->get_fact(m_curr_fact); + for (table_base::row_interface& a : t) { + a.get_fact(m_curr_fact); if((*m_mapper)(m_curr_fact.c_ptr()+m_first_functional)) { m_aux_table->add_fact(m_curr_fact); } @@ -1699,13 +1672,10 @@ namespace datalog { SASSERT(plugin.can_handle_signature(res_sign)); table_base * res = plugin.mk_empty(res_sign); - table_base::iterator it = t.begin(); - table_base::iterator end = t.end(); - - - for(; it!=end; ++it) { + table_base::iterator it = t.begin(), end = t.end(); + for (; it != end; ++it) { mk_project(it); - if(!res->suggest_fact(m_former_row)) { + if (!res->suggest_fact(m_former_row)) { (*m_reducer)(m_former_row.c_ptr()+m_res_first_functional, m_row.c_ptr()+m_res_first_functional); res->ensure_fact(m_former_row); } From 5da07532691df81889c1ee6c8ac469560b618e1f Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 3 Aug 2017 19:49:50 -0400 Subject: [PATCH 014/364] (spacer) add instances even when a q-lemma already exists It is possible that a new instance of a quantified lemma is discovered even though a quantified lemma it already known. In this case, the instance should be added to a corresponding context, even though the lemma is not new. --- src/muz/spacer/spacer_context.cpp | 38 +++++++++++++++++++------------ src/muz/spacer/spacer_context.h | 5 ++-- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 7995f030c..4a61688f8 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -314,7 +314,8 @@ bool pred_transformer::propagate_to_next_level (unsigned src_level) /// \brief adds a lema to the solver and to child solvers -void pred_transformer::add_lemma_core(lemma* lemma) +void pred_transformer::add_lemma_core(lemma* lemma, + bool ground_only) { unsigned lvl = lemma->level(); expr* l = lemma->get_expr(); @@ -346,7 +347,8 @@ void pred_transformer::add_lemma_core(lemma* lemma) } for (unsigned i = 0, sz = m_use.size (); i < sz; ++i) - { m_use [i]->add_lemma_from_child(*this, lemma, next_level(lvl)); } + { m_use [i]->add_lemma_from_child(*this, lemma, + next_level(lvl), ground_only); } } bool pred_transformer::add_lemma (expr *e, unsigned lvl) { @@ -355,7 +357,8 @@ bool pred_transformer::add_lemma (expr *e, unsigned lvl) { } void pred_transformer::add_lemma_from_child (pred_transformer& child, - lemma* lemma, unsigned lvl) + lemma* lemma, unsigned lvl, + bool ground_only) { ensure_level(lvl); expr_ref_vector fmls(m); @@ -365,21 +368,25 @@ void pred_transformer::add_lemma_from_child (pred_transformer& child, expr_ref_vector inst(m); expr* a = to_app(fmls.get(i))->get_arg(0); expr* l = to_app(fmls.get(i))->get_arg(1); - if (get_context().use_instantiate()) - { lemma->mk_insts(inst, l); } + if (get_context().use_instantiate()) { + lemma->mk_insts(inst, l); + } for (unsigned j=0; j < inst.size(); j++) { inst.set(j, m.mk_implies(a, inst.get(j))); } - if (lemma->is_ground() || get_context().use_qlemmas()) - { inst.push_back(fmls.get(i)); } + if (lemma->is_ground() || (get_context().use_qlemmas() && !ground_only)) { + inst.push_back(fmls.get(i)); + } SASSERT (!inst.empty ()); for (unsigned j = 0; j < inst.size(); ++j) { TRACE("spacer_detail", tout << "child property: " << mk_pp(inst.get (j), m) << "\n";); - if (is_infty_level(lvl)) - { m_solver.assert_expr(inst.get(j)); } - else - { m_solver.assert_expr(inst.get(j), lvl); } + if (is_infty_level(lvl)) { + m_solver.assert_expr(inst.get(j)); + } + else { + m_solver.assert_expr(inst.get(j), lvl); + } } } @@ -1263,18 +1270,21 @@ bool pred_transformer::frames::add_lemma(lemma *lem) if (!lem->get_bindings().empty()) { m_lemmas [i]->add_binding(lem->get_bindings()); } - // if the lemma is at a higher level, skip it - // XXX if there are new bindings, we need to assert new instances + // if the lemma is at a higher level, skip it, + // but still assert any new instances if (m_lemmas [i]->level() >= lem->level()) { TRACE("spacer", tout << "Already at a higher level: " << pp_level(m_lemmas [i]->level()) << "\n";); + if (!lem->get_bindings().empty()) { + m_pt.add_lemma_core(m_lemmas[i], true); + } return false; } // update level of the existing lemma m_lemmas [i]->set_level(lem->level()); // assert lemma in the solver - m_pt.add_lemma_core(m_lemmas[i]); + m_pt.add_lemma_core(m_lemmas[i], false); // move the lemma to its new place to maintain sortedness for (unsigned j = i; (j + 1) < sz && m_lt(m_lemmas [j + 1], m_lemmas[j]); ++j) { m_lemmas.swap (j, j+1); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index a95b1bdb9..421d9e8c8 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -294,8 +294,9 @@ class pred_transformer { void init_sig(); void ensure_level(unsigned level); - void add_lemma_core (lemma *lemma); - void add_lemma_from_child (pred_transformer &child, lemma *lemma, unsigned lvl); + void add_lemma_core (lemma *lemma, bool ground_only = false); + void add_lemma_from_child (pred_transformer &child, lemma *lemma, + unsigned lvl, bool ground_only = false); void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result); From 9f0eb367b17ca7082cb174ca660fee16b3028cdd Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 4 Aug 2017 16:12:32 -0400 Subject: [PATCH 015/364] ground lemmas during propagation when qlemmas are disabled When asserting quantified lemmas are disabled, ground a lemma explicitly during propagate to make sure that it is ground using our local set of skolem constants. --- src/muz/spacer/spacer_context.cpp | 2 +- src/muz/spacer/spacer_util.cpp | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 4a61688f8..1ab15ae34 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -769,7 +769,7 @@ bool pred_transformer::is_invariant(unsigned level, expr* lemma, expr_ref_vector conj(m), aux(m); expr_ref glemma(m); - if (false && is_quantifier(lemma)) { + if (!get_context().use_qlemmas() && is_quantifier(lemma)) { SASSERT(is_forall(lemma)); app_ref_vector tmp(m); ground_expr(to_quantifier(lemma)->get_expr (), glemma, tmp); diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 8b8da8a69..1156b2f9e 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -1166,21 +1166,21 @@ void mk_epp::rw(expr *e, expr_ref &out) arw(e, out); } - void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars) - { - expr_free_vars fv; - ast_manager &m = out.get_manager (); - fv (e); - if (vars.size () < fv.size ()) - { vars.resize(fv.size()); } - for (unsigned i = 0, sz = fv.size (); i < sz; ++i) { - SASSERT (fv[i]); - std::string str = "zk!" + datalog::to_string(sz - 1 - i); - vars [i] = m.mk_const (symbol(str.c_str()), fv [i]); - } - var_subst vs(m); - vs (e, vars.size (), (expr**) vars.c_ptr (), out); +void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { + expr_free_vars fv; + ast_manager &m = out.get_manager(); + + fv(e); + if (vars.size() < fv.size()) { + vars.resize(fv.size()); } + for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { + sort *s = fv[i] ? fv[i] : m.mk_bool_sort(); + vars[i] = mk_zk_const(m, i, s); + var_subst vs(m, false); + vs(e, vars.size(), (expr * *) vars.c_ptr(), out); + } +} struct index_term_finder { From 6cf68bee80461a88048146cdc02dd7123e38d6f2 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 7 Aug 2017 11:47:57 +0200 Subject: [PATCH 016/364] app ordering that puts special skolem constants first --- src/muz/spacer/spacer_manager.cpp | 22 ++++++++++++++++++++++ src/muz/spacer/spacer_manager.h | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index ba4ca0da7..d583813a8 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -384,4 +384,26 @@ bool has_zk_const(expr *e){ return false; } +bool is_zk_const (const app *a, int &n) { + if (!is_uninterp_const(a)) return false; + + const symbol &name = a->get_decl()->get_name(); + if (name.str().compare (0, 3, "sk!") != 0) { + return false; + } + + n = std::stoi(name.str().substr(3)); + return true; +} +bool sk_lt_proc::operator()(const app *a1, const app *a2) { + if (a1 == a2) return false; + int n1, n2; + bool z1, z2; + z1 = is_zk_const(a1, n1); + z2 = is_zk_const(a2, n2); + if (z1 && z2) return n1 < n2; + if (z1 != z2) return z1; + return ast_lt_proc()(a1, a2); +} + } diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index f2382c15d..f49aa63fc 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -341,6 +341,10 @@ public: app* mk_zk_const (ast_manager &m, unsigned idx, sort *s); void find_zk_const(expr* e, app_ref_vector &out); bool has_zk_const(expr* e); + +struct sk_lt_proc { + bool operator()(const app* a1, const app* a2); +}; } #endif From 1d478bd8d37719a2e22a2a50c5faa2c29bfdc3c1 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 7 Aug 2017 11:49:05 +0200 Subject: [PATCH 017/364] using sk_lt_proc order instead of ast_lt_proc when creating a lemma --- src/muz/spacer/spacer_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 1ab15ae34..6be4b78a3 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1787,7 +1787,7 @@ void pob::set_post(expr* post, app_ref_vector const &b) { m_binding.append(b); - std::sort (m_binding.c_ptr(), m_binding.c_ptr() + m_binding.size(), ast_lt_proc()); + std::sort (m_binding.c_ptr(), m_binding.c_ptr() + m_binding.size(), sk_lt_proc()); // skolemize implicit existential quantifier ast_manager &m = get_ast_manager(); From 6917aa3eb9accc51984edf76ca631ce0a4e01fef Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 7 Aug 2017 11:49:43 +0200 Subject: [PATCH 018/364] debug print --- src/muz/spacer/spacer_context.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 6be4b78a3..29cc39851 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -333,7 +333,18 @@ void pred_transformer::add_lemma_core(lemma* lemma, STRACE ("spacer.expand-add", tout << "add-lemma: " << pp_level (lvl) << " " << head ()->get_name () << " " - << mk_epp (l, m) << "\n\n";); + << mk_epp (l, m) << "\n"; + + if (!lemma->is_ground()) { + expr_ref_vector inst(m); + lemma->mk_insts(inst); + for (unsigned i = 0, sz = inst.size(); i < sz; ++i) { + tout << mk_epp(inst.get(i), m) << "\n"; + } + + } + tout << "\n"; + ); if (is_infty_level(lvl)) { m_stats.m_num_invariants++; } @@ -3046,6 +3057,8 @@ bool context::propagate(unsigned min_prop_lvl, if (m_params.pdr_simplify_formulas_pre()) { simplify_formulas(); } + STRACE ("spacer.expand-add", tout << "Propagating\n";); + IF_VERBOSE (1, verbose_stream () << "Propagating: " << std::flush;); for (unsigned lvl = min_prop_lvl; lvl <= full_prop_lvl; lvl++) { From e8befc072c9470a6c579567231a614f2051c32f1 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 7 Aug 2017 11:51:21 +0200 Subject: [PATCH 019/364] cleaned up lemma instantiation code --- src/muz/spacer/spacer_context.cpp | 54 ++++++++++++++++++++++++------- src/muz/spacer/spacer_context.h | 4 ++- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 29cc39851..7c4afa3f7 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1248,27 +1248,59 @@ void lemma::update_cube (pob_ref const &p, expr_ref_vector &cube) { if (m_cube.empty()) {m_cube.push_back(m.mk_true());} } -void lemma::mk_insts(expr_ref_vector &out, expr* e) -{ +bool lemma::has_binding(app_ref_vector const &binding) { + expr *lem = get_expr(); + unsigned num_decls = to_quantifier(lem)->get_num_decls(); + + SASSERT(bindings.size() == num_decls); + + for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { + unsigned i = 0; + for (; i < num_decls; ++i) { + if (m_bindings.get(off + i) != binding.get(i)) { + break; + } + } + if (i == num_decls) return true; + } + return false; +} +void lemma::add_binding(app_ref_vector const &binding) { + if (!has_binding(binding)) { + m_bindings.append(binding); + + TRACE("spacer", + tout << "new binding: "; + for (unsigned i = 0; i < binding.size(); i++) + tout << mk_pp(binding.get(i), m) << " "; + tout << "\n";); + } +} +void lemma::instantiate(expr * const * exprs, expr_ref &result, expr *e) { expr *lem = e == nullptr ? get_expr() : e; if (!is_quantifier (lem) || m_bindings.empty()) {return;} expr *body = to_quantifier(lem)->get_expr(); unsigned num_decls = to_quantifier(lem)->get_num_decls(); - expr_ref inst(m); var_subst vs(m, false); - for (unsigned i = 0, - sz = m_bindings.size() / num_decls, - off = 0; - i < sz; - ++i, off += num_decls) { - inst.reset(); - vs.reset(); - vs(body, num_decls, (expr**) m_bindings.c_ptr() + off, inst); + vs(body, num_decls, exprs, result); +} + +void lemma::mk_insts(expr_ref_vector &out, expr* e) +{ + expr *lem = e == nullptr ? get_expr() : e; + if (!is_quantifier (lem) || m_bindings.empty()) {return;} + + unsigned num_decls = to_quantifier(lem)->get_num_decls(); + expr_ref inst(m); + for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { + instantiate((expr * const *) m_bindings.c_ptr() + off, inst, e); out.push_back(inst); + inst.reset(); } } + bool pred_transformer::frames::add_lemma(lemma *lem) { TRACE("spacer", tout << "add-lemma: " << pp_level(lem->level()) << " " diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 421d9e8c8..a18855afe 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -135,7 +135,9 @@ public: unsigned level () const {return m_lvl;} void set_level (unsigned lvl) {m_lvl = lvl;} app_ref_vector& get_bindings() {return m_bindings;} - void add_binding(app_ref_vector const &binding) {m_bindings.append(binding);} + bool has_binding(app_ref_vector const &binding); + void add_binding(app_ref_vector const &binding); + void instantiate(expr * const * exprs, expr_ref &result, expr *e = nullptr); void mk_insts(expr_ref_vector& inst, expr* e = nullptr); bool is_ground () {return !is_quantifier (get_expr());} From 135a4a765fed0ccf7f391974e8303ee5751fd185 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 Aug 2017 17:23:55 -0400 Subject: [PATCH 020/364] Adding grounding of the current lemma In addition to adding the necessary instance of a quantified lemma, add its grounding over the global set of skolems. --- src/muz/spacer/spacer_context.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 7c4afa3f7..4968a44e4 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -379,8 +379,15 @@ void pred_transformer::add_lemma_from_child (pred_transformer& child, expr_ref_vector inst(m); expr* a = to_app(fmls.get(i))->get_arg(0); expr* l = to_app(fmls.get(i))->get_arg(1); - if (get_context().use_instantiate()) { + if (!lemma->is_ground() && get_context().use_instantiate()) { + expr_ref grnd_lemma(m); + app_ref_vector tmp(m); lemma->mk_insts(inst, l); + // -- take ground instance of the current lemma + ground_expr(to_quantifier(l)->get_expr(), grnd_lemma, tmp); + STRACE("spacer.expand-add", + tout << "Adding instance: " << mk_epp(grnd_lemma, m) << "\n";); + inst.push_back(grnd_lemma); } for (unsigned j=0; j < inst.size(); j++) { inst.set(j, m.mk_implies(a, inst.get(j))); From 27d8fa4a34468e4048ccdacd946311b6288d51b0 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 Aug 2017 17:18:09 -0400 Subject: [PATCH 021/364] hard-code quantifier weight to 15 With default settings, the eager threshold is 10 and lazy is 20. 15 puts us in the middle ensuring that lemmas are instantiated when UNSAT and otherwise delayed. --- src/muz/spacer/spacer_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 4968a44e4..ba4958d06 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1183,7 +1183,7 @@ void lemma::mk_expr_core() { m_body = m.mk_quantifier(true, zks.size(), sorts.c_ptr(), names.c_ptr(), - m_body, 0, symbol(m_body->get_id())); + m_body, 15, symbol(m_body->get_id())); if (m_new_pob) { add_binding(m_pob->get_binding()); } From 371ba4fbc0a06710d9946cec7c411077892be5c7 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 Aug 2017 17:50:47 -0400 Subject: [PATCH 022/364] added parameters that seem to work well with quantifiers and arith --- src/muz/spacer/spacer_context.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index ba4958d06..c42bfbbc3 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2212,6 +2212,19 @@ void context::init_lemma_generalizers(datalog::rule_set& rules) fparams.m_mbqi = m_params.spacer_mbqi(); + if (!m_params.spacer_ground_cti()) { + fparams.m_pi_use_database = true; + fparams.m_phase_selection = PS_CACHING_CONSERVATIVE2; + fparams.m_restart_strategy = RS_GEOMETRIC; + fparams.m_restart_factor = 1.5; + fparams.m_eliminate_bounds = true; + fparams.m_qi_quick_checker = MC_UNSAT; + fparams.m_propagate_booleans = true; + fparams.m_qi_eager_threshold = 10; + fparams.m_qi_lazy_threshold = 20; + fparams.m_ng_lift_ite = LI_FULL; + } + if (get_params().spacer_use_eqclass()) { m_lemma_generalizers.push_back (alloc(lemma_eq_generalizer, *this)); } From b8b3703511a1224cd681ff4362baf9b0e7fe3bb7 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 Aug 2017 17:51:54 -0400 Subject: [PATCH 023/364] improved implementation of is_qblocked() Disabled by default. Has no effect if ran with the default set of options where qlemmas=true and instantiate=true --- src/muz/spacer/spacer_context.cpp | 46 +++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index c42bfbbc3..12ab70351 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -51,6 +51,7 @@ Notes: #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" +#include "smt/smt_solver.h" namespace spacer { // ---------------- @@ -648,20 +649,44 @@ bool pred_transformer::is_blocked (pob &n, unsigned &uses_level) return res == l_false; } -bool pred_transformer::is_qblocked (pob &n) -{ - // XXX Trivial implementation to get us started - smt::kernel solver (m, get_manager ().fparams2()); + +bool pred_transformer::is_qblocked (pob &n) { + // XXX currently disabled + return false; + params_ref p; + p.set_bool("arith.ignore_int", true); + p.set_bool("array.weak", true); + p.set_bool("mbqi", false); + scoped_ptr s; + s = mk_smt_solver(m, p, symbol::null); + s->updt_params(p); + // XXX force parameters to be set + s->push(); + s->pop(1); + expr_ref_vector frame_lemmas(m); m_frames.get_frame_geq_lemmas (n.level (), frame_lemmas); // assert all lemmas + bool has_quant = false; for (unsigned i = 0, sz = frame_lemmas.size (); i < sz; ++i) - { solver.assert_expr(frame_lemmas.get(i)); } - // assert cti - solver.assert_expr (n.post ()); - lbool res = solver.check (); + { + has_quant = has_quant || is_quantifier(frame_lemmas.get(i)); + s->assert_expr(frame_lemmas.get(i)); + } + if (!has_quant) return false; + // assert cti + s->assert_expr(n.post()); + lbool res = s->check_sat(0, 0); + + // if (res == l_false) { + // expr_ref_vector core(m); + // solver->get_itp_core(core); + // expr_ref c(m); + // c = mk_and(core); + // STRACE("spacer.expand-add", tout << "core: " << mk_epp(c,m) << "\n";); + // } return res == l_false; } @@ -2939,6 +2964,11 @@ lbool context::expand_node(pob& n) return l_false; } + if (n.pt().is_qblocked(n)) { + STRACE("spacer.expand-add", + tout << "This pob can be blocked by instantiation\n";); + } + smt_params &fparams = m_pm.fparams(); flet _arith_ignore_int_(fparams.m_arith_ignore_int, m_weak_abs && n.weakness() < 1); From 68518b0e320b01f6bf22375675980a790fad65f0 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 Aug 2017 22:37:45 -0400 Subject: [PATCH 024/364] propagate weakness from pob down to all related checks If a pob was discharged with a weak solver, propagate the level of weakness to inductive generalization and to lemma propagation. --- src/muz/spacer/spacer_context.cpp | 36 ++++++++++++++------------ src/muz/spacer/spacer_context.h | 13 +++++++--- src/muz/spacer/spacer_generalizers.cpp | 13 +++++++--- src/muz/spacer/spacer_prop_solver.h | 15 +++++++++++ 4 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 12ab70351..772eca1e1 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -715,6 +715,8 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, // prepare the solver prop_solver::scoped_level _sl(m_solver, n.level()); prop_solver::scoped_subset_core _sc (m_solver, !n.use_farkas_generalizer ()); + prop_solver::scoped_weakness _sw(m_solver, 0, + ctx.weak_abs() ? n.weakness() : UINT_MAX); m_solver.set_core(core); m_solver.set_model(model); @@ -806,17 +808,20 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, return l_undef; } -bool pred_transformer::is_invariant(unsigned level, expr* lemma, +bool pred_transformer::is_invariant(unsigned level, lemma* lem, unsigned& solver_level, expr_ref_vector* core) { - expr_ref_vector conj(m), aux(m); - expr_ref glemma(m); + expr_ref lemma(m); + lemma = lem->get_expr(); - if (!get_context().use_qlemmas() && is_quantifier(lemma)) { - SASSERT(is_forall(lemma)); + expr_ref_vector conj(m), aux(m); + expr_ref gnd_lemma(m); + + + if (!get_context().use_qlemmas() && !lem->is_ground()) { app_ref_vector tmp(m); - ground_expr(to_quantifier(lemma)->get_expr (), glemma, tmp); - lemma = glemma.get(); + ground_expr(to_quantifier(lemma)->get_expr (), gnd_lemma, tmp); + lemma = gnd_lemma.get(); } conj.push_back(mk_not(m, lemma)); @@ -824,6 +829,8 @@ bool pred_transformer::is_invariant(unsigned level, expr* lemma, prop_solver::scoped_level _sl(m_solver, level); prop_solver::scoped_subset_core _sc (m_solver, true); + prop_solver::scoped_weakness _sw (m_solver, 1, + ctx.weak_abs() ? lem->weakness() : UINT_MAX); m_solver.set_core(core); m_solver.set_model(nullptr); expr * bg = m_extend_lit.get (); @@ -839,7 +846,7 @@ bool pred_transformer::is_invariant(unsigned level, expr* lemma, } bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, - unsigned& uses_level) + unsigned& uses_level, unsigned weakness) { manager& pm = get_manager(); expr_ref_vector conj(m), core(m); @@ -848,6 +855,8 @@ bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, mk_assumptions(head(), states, conj); prop_solver::scoped_level _sl(m_solver, level); prop_solver::scoped_subset_core _sc (m_solver, true); + prop_solver::scoped_weakness _sw (m_solver, 1, + ctx.weak_abs() ? weakness : UINT_MAX); m_solver.set_core(&core); m_solver.set_model (nullptr); expr_ref_vector aux (m); @@ -1412,10 +1421,9 @@ bool pred_transformer::frames::propagate_to_next_level (unsigned level) unsigned solver_level; - expr * curr = m_lemmas [i]->get_expr (); - if (m_pt.is_invariant(tgt_level, curr, solver_level)) { + if (m_pt.is_invariant(tgt_level, m_lemmas.get(i), solver_level)) { m_lemmas [i]->set_level (solver_level); - m_pt.add_lemma_core (m_lemmas [i]); + m_pt.add_lemma_core (m_lemmas.get(i)); // percolate the lemma up to its new place for (unsigned j = i; (j+1) < sz && m_lt (m_lemmas[j+1], m_lemmas[j]); ++j) { @@ -2969,12 +2977,6 @@ lbool context::expand_node(pob& n) tout << "This pob can be blocked by instantiation\n";); } - smt_params &fparams = m_pm.fparams(); - flet _arith_ignore_int_(fparams.m_arith_ignore_int, - m_weak_abs && n.weakness() < 1); - flet _array_weak_(fparams.m_array_weak, - m_weak_abs && n.weakness() < 2); - lbool res = n.pt ().is_reachable (n, &cube, &model, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); checkpoint (); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index a18855afe..61317e93b 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -131,6 +131,7 @@ public: bool has_pob() {return m_pob;} pob_ref &get_pob() {return m_pob;} + inline unsigned weakness(); unsigned level () const {return m_lvl;} void set_level (unsigned lvl) {m_lvl = lvl;} @@ -399,10 +400,15 @@ public: datalog::rule const*& r, vector& reach_pred_used, unsigned& num_reuse_reach); - bool is_invariant(unsigned level, expr* lemma, + bool is_invariant(unsigned level, lemma* lem, unsigned& solver_level, expr_ref_vector* core = nullptr); + + bool is_invariant(unsigned level, expr* lem, + unsigned& solver_level, expr_ref_vector* core = nullptr) + { UNREACHABLE();} + bool check_inductive(unsigned level, expr_ref_vector& state, - unsigned& assumes_level); + unsigned& assumes_level, unsigned weakness = UINT_MAX); expr_ref get_formulas(unsigned level, bool add_axioms); @@ -549,7 +555,7 @@ struct pob_ref_gt : {return gt (n1.get (), n2.get ());} }; - +inline unsigned lemma::weakness() {return m_pob ? m_pob->weakness() : UINT_MAX;} /** */ class derivation { @@ -774,6 +780,7 @@ public: bool use_native_mbp () {return m_use_native_mbp;} bool use_ground_cti () {return m_ground_cti;} bool use_instantiate () { return m_instantiate; } + bool weak_abs() {return m_weak_abs;} bool use_qlemmas () {return m_use_qlemmas; } ast_manager& get_ast_manager() const { return m; } diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 19989e440..e2058e93f 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -33,7 +33,8 @@ void lemma_sanity_checker::operator()(lemma_ref &lemma) { expr_ref_vector cube(lemma->get_ast_manager()); cube.append(lemma->get_cube()); ENSURE(lemma->get_pob()->pt().check_inductive(lemma->level(), - cube, uses_level)); + cube, uses_level, + lemma->weakness())); } @@ -58,6 +59,8 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { ptr_vector processed; expr_ref_vector extra_lits(m); + unsigned weakness = lemma->weakness(); + unsigned i = 0, num_failures = 0; while (i < cube.size() && (!m_failure_limit || num_failures < m_failure_limit)) { @@ -65,7 +68,7 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { lit = cube.get(i); cube[i] = true_expr; if (cube.size() > 1 && - pt.check_inductive(lemma->level(), cube, uses_level)) { + pt.check_inductive(lemma->level(), cube, uses_level, weakness)) { num_failures = 0; dirty = true; for (i = 0; i < cube.size() && @@ -82,7 +85,7 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { SASSERT(extra_lits.size() > 1); for (unsigned j = 0, sz = extra_lits.size(); !found && j < sz; ++j) { cube[i] = extra_lits.get(j); - if (pt.check_inductive(lemma->level(), cube, uses_level)) { + if (pt.check_inductive(lemma->level(), cube, uses_level, weakness)) { num_failures = 0; dirty = true; found = true; @@ -185,6 +188,8 @@ void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) manager &pm = m_ctx.get_manager(); (void)pm; + unsigned weakness = lemma->weakness(); + expr_ref_vector core(m); expr_ref v(m); func_decl_set symb; @@ -264,7 +269,7 @@ void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) pred_transformer &pt = lemma->get_pob()->pt(); // -- check if it is consistent with the transition relation unsigned uses_level1; - if (pt.check_inductive(lemma->level(), lits, uses_level1)) { + if (pt.check_inductive(lemma->level(), lits, uses_level1, weakness)) { TRACE("core_array_eq", tout << "Inductive!\n";); lemma->update_cube(lemma->get_pob(),lits); lemma->set_level(uses_level1); diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 0cbcecfbf..c46063137 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -136,7 +136,22 @@ public: ~scoped_delta_level() {m_delta = false;} }; + class scoped_weakness { + smt_params &m_params; + bool m_arith_ignore_int; + bool m_array_weak; + public: + scoped_weakness(prop_solver &ps, unsigned solver_id, unsigned weakness) : + m_params(*ps.m_fparams[solver_id == 0 ? 0 : 0 /*1*/]) { + m_params.m_arith_ignore_int = weakness < 1; + m_params.m_array_weak = weakness < 2; + } + ~scoped_weakness() { + m_params.m_arith_ignore_int = m_arith_ignore_int; + m_params.m_array_weak = m_array_weak; + } + }; }; } From 890bc0f7c9f318852d2ec216418d8b93f968db79 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 6 Sep 2017 14:51:53 -0400 Subject: [PATCH 025/364] fix scoped_weakness forgot to save current state of params before resetting them --- src/muz/spacer/spacer_prop_solver.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index c46063137..e01dfba2e 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -144,6 +144,9 @@ public: public: scoped_weakness(prop_solver &ps, unsigned solver_id, unsigned weakness) : m_params(*ps.m_fparams[solver_id == 0 ? 0 : 0 /*1*/]) { + m_arith_ignore_int = m_params.m_arith_ignore_int; + m_array_weak = m_params.m_array_weak; + m_params.m_arith_ignore_int = weakness < 1; m_params.m_array_weak = weakness < 2; } From 321cad70d6854211f8acbd7d13d11feff9275be6 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 6 Sep 2017 17:39:13 -0400 Subject: [PATCH 026/364] improve comments for scoped_weakness --- src/muz/spacer/spacer_prop_solver.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index e01dfba2e..4aedb9676 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -144,9 +144,11 @@ public: public: scoped_weakness(prop_solver &ps, unsigned solver_id, unsigned weakness) : m_params(*ps.m_fparams[solver_id == 0 ? 0 : 0 /*1*/]) { + // save current values m_arith_ignore_int = m_params.m_arith_ignore_int; m_array_weak = m_params.m_array_weak; + // set values based on weakness score m_params.m_arith_ignore_int = weakness < 1; m_params.m_array_weak = weakness < 2; } From e7815c703cedc43e09d8b4f4375b6d86a0354c85 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 29 Sep 2017 16:19:48 -0400 Subject: [PATCH 027/364] Fix a typo --- src/muz/spacer/spacer_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 772eca1e1..81120cea2 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1293,7 +1293,7 @@ bool lemma::has_binding(app_ref_vector const &binding) { expr *lem = get_expr(); unsigned num_decls = to_quantifier(lem)->get_num_decls(); - SASSERT(bindings.size() == num_decls); + SASSERT(binding.size() == num_decls); for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { unsigned i = 0; From 9b050e8d307888905ff74413e08dd1d56538577a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 29 Sep 2017 16:23:22 -0400 Subject: [PATCH 028/364] Fix benign warning --- src/muz/spacer/spacer_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 61317e93b..3dd26db9a 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -405,7 +405,7 @@ public: bool is_invariant(unsigned level, expr* lem, unsigned& solver_level, expr_ref_vector* core = nullptr) - { UNREACHABLE();} + { UNREACHABLE(); return false; } bool check_inductive(unsigned level, expr_ref_vector& state, unsigned& assumes_level, unsigned weakness = UINT_MAX); From 4148ee128cbe4cbbc4dd57ac05c4fc4887a7570c Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Thu, 12 Oct 2017 16:05:31 +0200 Subject: [PATCH 029/364] fixed bug, which added too many edges between super-source and source in the case where the source was used by multiple inferences --- src/muz/spacer/spacer_unsat_core_plugin.cpp | 18 ++++++++++++++---- src/muz/spacer/spacer_unsat_core_plugin.h | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index a1a937de0..0323fff0a 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -707,6 +707,8 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector j (only relevant if i is the supersource)) + if (!(i == nullptr && m_connected_to_s.is_marked(j))) + { + m_min_cut.add_edge(node_i, node_j, 1); + } + + if (i == nullptr) + { + m_connected_to_s.mark(j, true); + } } /* diff --git a/src/muz/spacer/spacer_unsat_core_plugin.h b/src/muz/spacer/spacer_unsat_core_plugin.h index 2ea4b4b51..6b679f3f2 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.h +++ b/src/muz/spacer/spacer_unsat_core_plugin.h @@ -108,6 +108,7 @@ private: void add_edge(proof* i, proof* j); vector m_node_to_formula; // maps each node to the corresponding formula in the original proof + ast_mark m_connected_to_s; // remember which nodes have already been connected to the supersource, in order to avoid multiple edges. min_cut m_min_cut; }; From 56fcb8e6fdb94e036d8a2654b96d2db50cb2b2f6 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Thu, 12 Oct 2017 16:36:11 +0200 Subject: [PATCH 030/364] added option fixedpoint.spacer.print_farkas_stats to print number of Farkas lemmas in each proof --- src/muz/base/fixedpoint_params.pyg | 1 + src/muz/spacer/spacer_itp_solver.cpp | 2 +- src/muz/spacer/spacer_itp_solver.h | 6 ++- src/muz/spacer/spacer_prop_solver.cpp | 2 +- src/muz/spacer/spacer_unsat_core_learner.cpp | 46 ++++++++++++++++++++ src/muz/spacer/spacer_unsat_core_learner.h | 4 +- 6 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 8d6c750d5..94c5a8989 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -187,6 +187,7 @@ def_module_params('fixedpoint', ('spacer.farkas_a_const', BOOL, True, 'if the unoptimized farkas plugin is used, use the constants from A while constructing unsat_cores'), ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), + ('spacer.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints') )) diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index 7ca66fbfd..cf481f179 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -274,7 +274,7 @@ void itp_solver::get_itp_core (expr_ref_vector &core) simplify_bounds (core); // XXX potentially redundant } else { // new code - unsat_core_learner learner(m); + unsat_core_learner learner(m,m_print_farkas_stats); if (m_farkas_optimized) { if (true) // TODO: proper options diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index 466e0a2f1..4245332d2 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -62,6 +62,7 @@ private: bool m_minimize_unsat_core; bool m_farkas_optimized; bool m_farkas_a_const; + bool m_print_farkas_stats; bool is_proxy(expr *e, app_ref &def); void undo_proxies_in_core(ptr_vector &v); @@ -69,7 +70,7 @@ private: app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); public: - itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, bool farkas_optimized, bool farkas_a_const, bool split_literals = false) : + itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, bool farkas_optimized, bool farkas_a_const, bool print_farkas_stats, bool split_literals = false) : m(solver.get_manager()), m_solver(solver), m_proxies(m), @@ -83,7 +84,8 @@ public: m_new_unsat_core(new_unsat_core), m_minimize_unsat_core(minimize_unsat_core), m_farkas_optimized(farkas_optimized), - m_farkas_a_const(farkas_a_const) + m_farkas_a_const(farkas_a_const), + m_print_farkas_stats(print_farkas_stats) {} ~itp_solver() override {} diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 059374e39..239e8d7f0 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -60,7 +60,7 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_solvers[1] = pm.mk_fresh2(); m_fparams[1] = &pm.fparams2(); - m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals()); + m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals()); for (unsigned i = 0; i < 2; ++i) diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index f36143c5f..cf1eb979f 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -130,6 +130,52 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e // TODO: remove duplicates from unsat core? + // count both number of all Farkas lemmas and number of Farkas lemmas in the cut + if (m_print_farkas_stats) + { + unsigned farkas_counter = 0; + unsigned farkas_counter2 = 0; + + ProofIteratorPostOrder it3(root, m); + while (it3.hasNext()) + { + proof* currentNode = it3.next(); + + // if node is theory lemma + if (currentNode->get_decl_kind() == PR_TH_LEMMA) + { + func_decl* d = currentNode->get_decl(); + symbol sym; + // and theory lemma is Farkas lemma + if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step + d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", + d->get_parameter(1).is_symbol(sym) && sym == "farkas") + { + farkas_counter++; + + // check whether farkas lemma is to be interpolated (could potentially miss farkas lemmas, which are interpolated, because we potentially don't want to use the lowest cut) + bool has_no_mixed_parents = true; + for (int i = 0; i < m.get_num_parents(currentNode); ++i) + { + proof* premise = to_app(currentNode->get_arg(i)); + if (is_a_marked(premise) && is_b_marked(premise)) + { + has_no_mixed_parents = false; + } + } + if (has_no_mixed_parents && is_a_marked(currentNode) && is_b_marked(currentNode)) + { + farkas_counter2++; + } + + } + } + } + + verbose_stream() << "\nThis proof contains " << farkas_counter << " Farkas lemmas. " << farkas_counter2 << " Farkas lemmas participate in the lowest cut\n"; + } + + bool debug_proof = false; if(debug_proof) { diff --git a/src/muz/spacer/spacer_unsat_core_learner.h b/src/muz/spacer/spacer_unsat_core_learner.h index 87238b5fd..2f04a9e06 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.h +++ b/src/muz/spacer/spacer_unsat_core_learner.h @@ -31,7 +31,7 @@ namespace spacer { typedef obj_hashtable expr_set; public: - unsat_core_learner(ast_manager& m) : m(m), m_unsat_core(m) {}; + unsat_core_learner(ast_manager& m, bool print_farkas_stats = false) : m(m), m_unsat_core(m), m_print_farkas_stats(print_farkas_stats) {}; virtual ~unsat_core_learner(); ast_manager& m; @@ -100,6 +100,8 @@ namespace spacer { * finalize computation of unsat-core */ void finalize(); + + bool m_print_farkas_stats; }; } From fba995294d8b288643c09b3cb1a5f3fabbc48343 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Thu, 12 Oct 2017 17:01:30 +0200 Subject: [PATCH 031/364] refactored options regarding farkas lemma handling --- src/muz/base/fixedpoint_params.pyg | 3 +-- src/muz/spacer/spacer_itp_solver.cpp | 34 +++++++++++++++------------ src/muz/spacer/spacer_itp_solver.h | 8 +++---- src/muz/spacer/spacer_prop_solver.cpp | 4 ++-- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 94c5a8989..d1a23dcdd 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -183,8 +183,7 @@ def_module_params('fixedpoint', ('spacer.qlemmas', BOOL, True, 'allow quantified lemmas in frames'), ('spacer.new_unsat_core', BOOL, True, 'use the new implementation of unsat-core-generation'), ('spacer.minimize_unsat_core', BOOL, False, 'compute unsat-core by min-cut'), - ('spacer.farkas_optimized', BOOL, True, 'use the optimized farkas plugin, which performs gaussian elimination'), - ('spacer.farkas_a_const', BOOL, True, 'if the unoptimized farkas plugin is used, use the constants from A while constructing unsat_cores'), + ('spacer.farkas_plugin', UINT, 2, '0 = use unoptimized Farkas plugin, 1 = use unoptimized Farkas plugin with flipped polarity, 2 = use Gaussian elimination ideas, 3 = use additive IUC plugin'), ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), ('spacer.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index cf481f179..7af5c21fc 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -276,23 +276,27 @@ void itp_solver::get_itp_core (expr_ref_vector &core) // new code unsat_core_learner learner(m,m_print_farkas_stats); - if (m_farkas_optimized) { - if (true) // TODO: proper options - { - unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); - learner.register_plugin(plugin_farkas_lemma_optimized); - } - else - { - unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); - learner.register_plugin(plugin_farkas_lemma_bounded); - } - - } else { - unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, m_farkas_a_const); + if (m_farkas_plugin == 0 || m_farkas_plugin > 3) + { + unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, false); learner.register_plugin(plugin_farkas_lemma); } - + else if (m_farkas_plugin == 1) + { + unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, true); + learner.register_plugin(plugin_farkas_lemma); + } + else if (m_farkas_plugin == 2) + { + unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); + learner.register_plugin(plugin_farkas_lemma_optimized); + } + else if(m_farkas_plugin == 3) + { + unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); + learner.register_plugin(plugin_farkas_lemma_bounded); + } + if (m_minimize_unsat_core) { unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m); learner.register_plugin(plugin_min_cut); diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index 4245332d2..719eb3f7c 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -60,8 +60,7 @@ private: bool m_split_literals; bool m_new_unsat_core; bool m_minimize_unsat_core; - bool m_farkas_optimized; - bool m_farkas_a_const; + unsigned m_farkas_plugin; bool m_print_farkas_stats; bool is_proxy(expr *e, app_ref &def); @@ -70,7 +69,7 @@ private: app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); public: - itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, bool farkas_optimized, bool farkas_a_const, bool print_farkas_stats, bool split_literals = false) : + itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, unsigned farkas_plugin, bool print_farkas_stats, bool split_literals = false) : m(solver.get_manager()), m_solver(solver), m_proxies(m), @@ -83,8 +82,7 @@ public: m_split_literals(split_literals), m_new_unsat_core(new_unsat_core), m_minimize_unsat_core(minimize_unsat_core), - m_farkas_optimized(farkas_optimized), - m_farkas_a_const(farkas_a_const), + m_farkas_plugin(farkas_plugin), m_print_farkas_stats(print_farkas_stats) {} diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 239e8d7f0..1e44921f1 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -60,8 +60,8 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_solvers[1] = pm.mk_fresh2(); m_fparams[1] = &pm.fparams2(); - m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); - m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals()); + m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_plugin(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); + m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_plugin(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); for (unsigned i = 0; i < 2; ++i) { m_contexts[i]->assert_expr(m_pm.get_background()); } From 00a99f01b4e0bba92c665584dc04e842f23b882f Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Thu, 12 Oct 2017 17:31:39 +0200 Subject: [PATCH 032/364] improved options for IUC computation --- src/muz/base/fixedpoint_params.pyg | 5 ++--- src/muz/spacer/spacer_itp_solver.cpp | 22 ++++++++++++++-------- src/muz/spacer/spacer_itp_solver.h | 12 +++++------- src/muz/spacer/spacer_prop_solver.cpp | 4 ++-- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index d1a23dcdd..a46d06245 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -181,9 +181,8 @@ def_module_params('fixedpoint', ('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'), ('spacer.instantiate', BOOL, True, 'instantiate quantified lemmas'), ('spacer.qlemmas', BOOL, True, 'allow quantified lemmas in frames'), - ('spacer.new_unsat_core', BOOL, True, 'use the new implementation of unsat-core-generation'), - ('spacer.minimize_unsat_core', BOOL, False, 'compute unsat-core by min-cut'), - ('spacer.farkas_plugin', UINT, 2, '0 = use unoptimized Farkas plugin, 1 = use unoptimized Farkas plugin with flipped polarity, 2 = use Gaussian elimination ideas, 3 = use additive IUC plugin'), + ('spacer.iuc', UINT, 1, '0 = use old implementation of unsat-core-generation, 1 = use new implementation of IUC generation, 2 = use new implementation of IUC + min-cut optimization'), + ('spacer.iuc.arith', UINT, 2, '0 = use simple Farkas plugin, 1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation), 2 = use Gaussian elimination optimization, 3 = use additive IUC plugin'), ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), ('spacer.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index 7af5c21fc..c9bf3c443 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -264,7 +264,8 @@ void itp_solver::get_itp_core (expr_ref_vector &core) proof_ref pr(m); pr = get_proof (); - if (!m_new_unsat_core) { + if (m_iuc == 0) + { // old code farkas_learner learner_old; learner_old.set_split_literals(m_split_literals); @@ -272,35 +273,40 @@ void itp_solver::get_itp_core (expr_ref_vector &core) learner_old.get_lemmas (pr, B, core); elim_proxies (core); simplify_bounds (core); // XXX potentially redundant - } else { + } + else + { // new code unsat_core_learner learner(m,m_print_farkas_stats); - if (m_farkas_plugin == 0 || m_farkas_plugin > 3) + if (m_iuc_arith == 0 || m_iuc_arith > 3) { unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, false); learner.register_plugin(plugin_farkas_lemma); } - else if (m_farkas_plugin == 1) + else if (m_iuc_arith == 1) { unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, true); learner.register_plugin(plugin_farkas_lemma); } - else if (m_farkas_plugin == 2) + else if (m_iuc_arith == 2) { unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); learner.register_plugin(plugin_farkas_lemma_optimized); } - else if(m_farkas_plugin == 3) + else if(m_iuc_arith == 3) { unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); learner.register_plugin(plugin_farkas_lemma_bounded); } - if (m_minimize_unsat_core) { + if (m_iuc == 2) + { unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m); learner.register_plugin(plugin_min_cut); - } else { + } + else + { unsat_core_plugin_lemma* plugin_lemma = alloc(unsat_core_plugin_lemma, learner); learner.register_plugin(plugin_lemma); } diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index 719eb3f7c..0b01f0032 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -58,9 +58,8 @@ private: expr_substitution m_elim_proxies_sub; bool m_split_literals; - bool m_new_unsat_core; - bool m_minimize_unsat_core; - unsigned m_farkas_plugin; + unsigned m_iuc; + unsigned m_iuc_arith; bool m_print_farkas_stats; bool is_proxy(expr *e, app_ref &def); @@ -69,7 +68,7 @@ private: app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); public: - itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, unsigned farkas_plugin, bool print_farkas_stats, bool split_literals = false) : + itp_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool split_literals = false) : m(solver.get_manager()), m_solver(solver), m_proxies(m), @@ -80,9 +79,8 @@ public: m_is_proxied(false), m_elim_proxies_sub(m, false, true), m_split_literals(split_literals), - m_new_unsat_core(new_unsat_core), - m_minimize_unsat_core(minimize_unsat_core), - m_farkas_plugin(farkas_plugin), + m_iuc(iuc), + m_iuc_arith(iuc_arith), m_print_farkas_stats(print_farkas_stats) {} diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 1e44921f1..cf981cfeb 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -60,8 +60,8 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_solvers[1] = pm.mk_fresh2(); m_fparams[1] = &pm.fparams2(); - m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_plugin(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); - m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_plugin(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); + m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); + m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); for (unsigned i = 0; i < 2; ++i) { m_contexts[i]->assert_expr(m_pm.get_background()); } From 5a37518e581ef1826efa6a31d06d7f4eda146965 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 18 Oct 2017 13:55:22 -0400 Subject: [PATCH 033/364] Improve statistics from spacer --- src/muz/spacer/spacer_context.cpp | 35 ++++++++++++++++++++++++++++--- src/muz/spacer/spacer_context.h | 1 + 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 81120cea2..8f4b63224 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -103,11 +103,20 @@ std::ostream& pred_transformer::display(std::ostream& out) const void pred_transformer::collect_statistics(statistics& st) const { m_solver.collect_statistics(st); - st.update("SPACER num propagations", m_stats.m_num_propagations); - st.update("SPACER num properties", m_frames.lemma_size ()); - st.update("SPACER num invariants", m_stats.m_num_invariants); + // -- number of times a lemma has been propagated to a higher level + // -- during push + st.update("SPACER num propagations", m_stats.m_num_propagations); + // -- number of lemmas in all current frames + st.update("SPACER num active lemmas", m_frames.lemma_size ()); + // -- number of lemmas that are inductive invariants + st.update("SPACER num invariants", m_stats.m_num_invariants); + // -- number of proof obligations (0 if pobs are not reused) + st.update("SPACER num pobs", m_pobs.size()); + + // -- time in rule initialization st.update ("time.spacer.init_rules.pt.init", m_initialize_watch.get_seconds ()); + // -- time is must_reachable() st.update ("time.spacer.solve.pt.must_reachable", m_must_reachable_watch.get_seconds ()); } @@ -1429,6 +1438,7 @@ bool pred_transformer::frames::propagate_to_next_level (unsigned level) for (unsigned j = i; (j+1) < sz && m_lt (m_lemmas[j+1], m_lemmas[j]); ++j) { m_lemmas.swap(j, j + 1); } + ++m_pt.m_stats.m_num_propagations; } else { all = false; ++i; @@ -3412,22 +3422,40 @@ void context::collect_statistics(statistics& st) const for (it = m_rels.begin(); it != end; ++it) { it->m_value->collect_statistics(st); } + + // -- number of times a pob for some predicate transformer has + // -- been created st.update("SPACER num queries", m_stats.m_num_queries); + // -- number of reach facts created st.update("SPACER num reach queries", m_stats.m_num_reach_queries); + // -- number of times a reach fact was true in some model st.update("SPACER num reuse reach facts", m_stats.m_num_reuse_reach); + // -- maximum level at which any query was asked st.update("SPACER max query lvl", m_stats.m_max_query_lvl); + // -- maximum depth st.update("SPACER max depth", m_stats.m_max_depth); + // -- level at which safe inductive invariant was found st.update("SPACER inductive level", m_inductive_lvl); + // -- length of the counterexample st.update("SPACER cex depth", m_stats.m_cex_depth); + // -- number of times expand_node resulted in undef st.update("SPACER expand node undef", m_stats.m_expand_node_undef); + // -- number of distinct lemmas constructed st.update("SPACER num lemmas", m_stats.m_num_lemmas); + // -- number of restarts taken st.update("SPACER restarts", m_stats.m_num_restarts); + // -- time to initialize the rules st.update ("time.spacer.init_rules", m_init_rules_watch.get_seconds ()); + // -- time in the main solve loop st.update ("time.spacer.solve", m_solve_watch.get_seconds ()); + // -- time in lemma propagation (i.e., pushing) st.update ("time.spacer.solve.propagate", m_propagate_watch.get_seconds ()); + // -- time in reachability (i.e., blocking) st.update ("time.spacer.solve.reach", m_reach_watch.get_seconds ()); + // -- time in deciding whether a pob is must-reachable st.update ("time.spacer.solve.reach.is-reach", m_is_reach_watch.get_seconds ()); + // -- time in creating new predecessors st.update ("time.spacer.solve.reach.children", m_create_children_watch.get_seconds ()); m_pm.collect_statistics(st); @@ -3439,6 +3467,7 @@ void context::collect_statistics(statistics& st) const // brunch out verbose_stream () << "BRUNCH_STAT max_query_lvl " << m_stats.m_max_query_lvl << "\n"; verbose_stream () << "BRUNCH_STAT num_queries " << m_stats.m_num_queries << "\n"; + verbose_stream () << "BRUNCH_STAT num_lemmas " << m_stats.m_num_lemmas << "\n"; verbose_stream () << "BRUNCH_STAT num_reach_queries " << m_stats.m_num_reach_queries << "\n"; verbose_stream () << "BRUNCH_STAT num_reach_reuse " << m_stats.m_num_reuse_reach << "\n"; verbose_stream () << "BRUNCH_STAT inductive_lvl " << m_inductive_lvl << "\n"; diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 3dd26db9a..181a55142 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -253,6 +253,7 @@ class pred_transformer { app_ref_vector b(m_pt.get_ast_manager()); return mk_pob (parent, level, depth, post, b); } + unsigned size() const {return m_pinned.size();} }; From 6818eb3340a9f99a17d46773b45013c206b6d2e4 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 22 Nov 2017 18:29:08 -0500 Subject: [PATCH 034/364] Improve factor equalities --- src/ast/factor_equivs.cpp | 55 ++++++++++++++++++++++++++++++++------- src/ast/factor_equivs.h | 7 +++++ 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/ast/factor_equivs.cpp b/src/ast/factor_equivs.cpp index c33b3a18e..be402e628 100644 --- a/src/ast/factor_equivs.cpp +++ b/src/ast/factor_equivs.cpp @@ -27,7 +27,9 @@ Revision History: #include "ast/factor_equivs.h" #include "ast/arith_decl_plugin.h" - +#include "ast/for_each_expr.h" +#include "ast/ast_pp.h" +#include "ast/rewriter/expr_safe_replace.h" /** Factors input vector v into equivalence classes and the rest */ @@ -59,8 +61,8 @@ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) { equiv.merge(e1, e2); } else { - if (j < i) { - v[j] = v.get(i); + if (j < i) { + v[j] = v.get(i); } j++; } @@ -68,19 +70,52 @@ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) { v.shrink(j); } +/** + * Chooses a representative of an equivalence class + */ +expr *choose_rep(expr_equiv_class::eq_class &clazz, ast_manager &m) { + expr *rep = nullptr; + unsigned rep_sz, elem_sz; + for (expr *elem : clazz) { + if (!m.is_value(elem)) { + elem_sz = get_num_exprs(elem); + if (!rep || (rep && rep_sz > elem_sz)) { + rep = elem; + rep_sz = elem_sz; + } + } + } + TRACE("equiv", + tout << "Rep: " << mk_pp(rep, m) << "\n"; + for (expr *el : clazz) + tout << mk_pp(el, m) << "\n"; + tout << "RepEnd\n";); + + return rep; +} + +void rewrite_eqs (expr_ref_vector &v, expr_equiv_class &equiv) { + ast_manager &m = v.m(); + expr_safe_replace sub(m); + for (auto eq_class : equiv) { + expr *rep = choose_rep(eq_class, m); + for (expr *el : eq_class) { + if (el != rep) { + sub.insert (el, rep); + } + } + } + sub(v); +} + + /** * converts equivalence classes to equalities */ void equiv_to_expr(expr_equiv_class &equiv, expr_ref_vector &out) { ast_manager &m = out.get_manager(); for (auto eq_class : equiv) { - expr *rep = nullptr; - for (expr *elem : eq_class) { - if (!m.is_value(elem)) { - rep = elem; - break; - } - } + expr *rep = choose_rep(eq_class, m); SASSERT(rep); for (expr *elem : eq_class) { if (rep != elem) { diff --git a/src/ast/factor_equivs.h b/src/ast/factor_equivs.h index f0ce1608d..5d306bad8 100644 --- a/src/ast/factor_equivs.h +++ b/src/ast/factor_equivs.h @@ -60,6 +60,8 @@ public: obj_equiv_class(Manager& m) : m_to_obj(m) {} + Manager &m() const {return m_to_obj.m();} + void add_elem(OBJ*o) { SASSERT(!m_to_int.find(o)); add_elem_impl(o); @@ -169,6 +171,11 @@ typedef obj_equiv_class expr_equiv_class; * Factors input vector v into equivalence classes and the rest */ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv); +/** + * Rewrite expressions in v by choosing a representative from the + * equivalence class. + */ +void rewrite_eqs(expr_ref_vector &v, expr_equiv_class &equiv); /** * converts equivalence classes to equalities */ From 880fc776559adbccc4ed48cc43c92b289903ccf8 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 22 Nov 2017 18:29:22 -0500 Subject: [PATCH 035/364] Further rewrite equalities --- src/muz/spacer/spacer_util.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 1156b2f9e..1d8a59d36 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -1065,6 +1065,7 @@ void normalize (expr *e, expr_ref &out, // equivalence classes expr_equiv_class eq_classes(out.m()); factor_eqs(v, eq_classes); + rewrite_eqs(v, eq_classes); equiv_to_expr(eq_classes, v); } From 370667722dd4bf3ff676aa200ac31a2c9c53e534 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Thu, 12 Oct 2017 16:36:11 +0200 Subject: [PATCH 036/364] New option fixedpoint.spacer.print_farkas_stats Prints the number of Farkas lemmas in each proof --- src/muz/spacer/spacer_itp_solver.cpp | 18 +++++++++--------- src/muz/spacer/spacer_itp_solver.h | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index c9bf3c443..c2c5dbe0d 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -290,16 +290,16 @@ void itp_solver::get_itp_core (expr_ref_vector &core) learner.register_plugin(plugin_farkas_lemma); } else if (m_iuc_arith == 2) - { - unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); - learner.register_plugin(plugin_farkas_lemma_optimized); - } + { + unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); + learner.register_plugin(plugin_farkas_lemma_optimized); + } else if(m_iuc_arith == 3) - { - unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); - learner.register_plugin(plugin_farkas_lemma_bounded); - } - + { + unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); + learner.register_plugin(plugin_farkas_lemma_bounded); + } + if (m_iuc == 2) { unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m); diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index 0b01f0032..d81a9764d 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -61,6 +61,7 @@ private: unsigned m_iuc; unsigned m_iuc_arith; bool m_print_farkas_stats; + bool m_print_farkas_stats; bool is_proxy(expr *e, app_ref &def); void undo_proxies_in_core(ptr_vector &v); From c3a66217e1c22b22ccc1a4f9ad98bc356bb3359d Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Thu, 12 Oct 2017 17:31:39 +0200 Subject: [PATCH 037/364] improved options for IUC computation --- src/muz/spacer/spacer_itp_solver.cpp | 18 +++++++++--------- src/muz/spacer/spacer_itp_solver.h | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index c2c5dbe0d..c9bf3c443 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -290,16 +290,16 @@ void itp_solver::get_itp_core (expr_ref_vector &core) learner.register_plugin(plugin_farkas_lemma); } else if (m_iuc_arith == 2) - { - unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); - learner.register_plugin(plugin_farkas_lemma_optimized); - } + { + unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); + learner.register_plugin(plugin_farkas_lemma_optimized); + } else if(m_iuc_arith == 3) - { - unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); - learner.register_plugin(plugin_farkas_lemma_bounded); - } - + { + unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); + learner.register_plugin(plugin_farkas_lemma_bounded); + } + if (m_iuc == 2) { unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m); diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index d81a9764d..0b01f0032 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -61,7 +61,6 @@ private: unsigned m_iuc; unsigned m_iuc_arith; bool m_print_farkas_stats; - bool m_print_farkas_stats; bool is_proxy(expr *e, app_ref &def); void undo_proxies_in_core(ptr_vector &v); From 25fad153d11736393c4c5baa80854622dd1139a3 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Mon, 23 Oct 2017 15:39:59 +0200 Subject: [PATCH 038/364] added option fixedpoint.spacer.iuc.debug_proof to debug proof which is used for generation of iuc --- src/muz/base/fixedpoint_params.pyg | 1 + src/muz/spacer/spacer_itp_solver.cpp | 2 +- src/muz/spacer/spacer_itp_solver.h | 7 ++-- src/muz/spacer/spacer_prop_solver.cpp | 4 +-- src/muz/spacer/spacer_unsat_core_learner.cpp | 35 +++++++++++++++----- src/muz/spacer/spacer_unsat_core_learner.h | 3 +- 6 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index a46d06245..f0f4e0881 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -186,6 +186,7 @@ def_module_params('fixedpoint', ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), ('spacer.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), + ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes'), ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints') )) diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index c9bf3c443..9cccdf43c 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -277,7 +277,7 @@ void itp_solver::get_itp_core (expr_ref_vector &core) else { // new code - unsat_core_learner learner(m,m_print_farkas_stats); + unsat_core_learner learner(m, m_print_farkas_stats, m_iuc_debug_proof); if (m_iuc_arith == 0 || m_iuc_arith > 3) { diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index 0b01f0032..5e5c2d39e 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -61,14 +61,14 @@ private: unsigned m_iuc; unsigned m_iuc_arith; bool m_print_farkas_stats; - + bool m_iuc_debug_proof; bool is_proxy(expr *e, app_ref &def); void undo_proxies_in_core(ptr_vector &v); app* mk_proxy(expr *v); app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); public: - itp_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool split_literals = false) : + itp_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool iuc_debug_proof, bool split_literals = false) : m(solver.get_manager()), m_solver(solver), m_proxies(m), @@ -81,7 +81,8 @@ public: m_split_literals(split_literals), m_iuc(iuc), m_iuc_arith(iuc_arith), - m_print_farkas_stats(print_farkas_stats) + m_print_farkas_stats(print_farkas_stats), + m_iuc_debug_proof(iuc_debug_proof) {} ~itp_solver() override {} diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index cf981cfeb..60deac214 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -60,8 +60,8 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_solvers[1] = pm.mk_fresh2(); m_fparams[1] = &pm.fparams2(); - m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); - m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_split_farkas_literals()); + m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_iuc_debug_proof(), p.spacer_split_farkas_literals()); + m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_iuc_debug_proof(), p.spacer_split_farkas_literals()); for (unsigned i = 0; i < 2; ++i) { m_contexts[i]->assert_expr(m_pm.get_background()); } diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index cf1eb979f..4adf67209 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -174,13 +174,11 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e verbose_stream() << "\nThis proof contains " << farkas_counter << " Farkas lemmas. " << farkas_counter2 << " Farkas lemmas participate in the lowest cut\n"; } - - bool debug_proof = false; - if(debug_proof) + if(m_iuc_debug_proof) { // print proof for debugging - verbose_stream() << "\n\nProof:\n"; + verbose_stream() << "Proof:\n"; std::unordered_map id_to_small_id; unsigned counter = 0; @@ -223,8 +221,23 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e verbose_stream() << "hypothesis"; break; default: - verbose_stream() << "unknown axiom-type"; - break; + if (currentNode->get_decl_kind() == PR_TH_LEMMA) + { + verbose_stream() << "th_axiom"; + func_decl* d = currentNode->get_decl(); + symbol sym; + if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step + d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", + d->get_parameter(1).is_symbol(sym) && sym == "farkas") + { + verbose_stream() << "(farkas)"; + } + } + else + { + verbose_stream() << "unknown axiom-type"; + break; + } } } else @@ -269,17 +282,21 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e } } - if (currentNode->get_decl_kind() == PR_TH_LEMMA || (is_a_marked(currentNode) && is_b_marked(currentNode)) || is_h_marked(currentNode) || (!is_a_marked(currentNode) && !is_b_marked(currentNode))) +// if (currentNode->get_decl_kind() == PR_TH_LEMMA || (is_a_marked(currentNode) && is_b_marked(currentNode)) || is_h_marked(currentNode) || (!is_a_marked(currentNode) && !is_b_marked(currentNode))) + if (false) { - verbose_stream() << std::endl; + verbose_stream() << "\n"; } else { - verbose_stream() << ": " << mk_pp(m.get_fact(currentNode), m) << std::endl; + verbose_stream() << ": " << mk_pp(m.get_fact(currentNode), m) << "\n"; } ++counter; } } + + verbose_stream() << std::endl; + // move all lemmas into vector for (expr* const* it = m_unsat_core.begin(); it != m_unsat_core.end(); ++it) { diff --git a/src/muz/spacer/spacer_unsat_core_learner.h b/src/muz/spacer/spacer_unsat_core_learner.h index 2f04a9e06..4b5ca981d 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.h +++ b/src/muz/spacer/spacer_unsat_core_learner.h @@ -31,7 +31,7 @@ namespace spacer { typedef obj_hashtable expr_set; public: - unsat_core_learner(ast_manager& m, bool print_farkas_stats = false) : m(m), m_unsat_core(m), m_print_farkas_stats(print_farkas_stats) {}; + unsat_core_learner(ast_manager& m, bool print_farkas_stats = false, bool iuc_debug_proof = false) : m(m), m_unsat_core(m), m_print_farkas_stats(print_farkas_stats), m_iuc_debug_proof(iuc_debug_proof) {}; virtual ~unsat_core_learner(); ast_manager& m; @@ -102,6 +102,7 @@ namespace spacer { void finalize(); bool m_print_farkas_stats; + bool m_iuc_debug_proof; }; } From 088bd3ed8ecdf385f3ac8f0eb4fd05f844128c87 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 12 Oct 2017 12:40:42 -0400 Subject: [PATCH 039/364] Fix compiler warning --- src/muz/spacer/spacer_unsat_core_learner.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index 4adf67209..12b2a5614 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -155,7 +155,7 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e // check whether farkas lemma is to be interpolated (could potentially miss farkas lemmas, which are interpolated, because we potentially don't want to use the lowest cut) bool has_no_mixed_parents = true; - for (int i = 0; i < m.get_num_parents(currentNode); ++i) + for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { proof* premise = to_app(currentNode->get_arg(i)); if (is_a_marked(premise) && is_b_marked(premise)) @@ -267,11 +267,11 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e verbose_stream() << "step"; } verbose_stream() << " from "; - for (int i = m.get_num_parents(currentNode) - 1; i >= 0 ; --i) + for (unsigned i = m.get_num_parents(currentNode); i > 0 ; --i) { proof* premise = to_app(currentNode->get_arg(i)); unsigned premise_small_id = id_to_small_id[premise->get_id()]; - if (i > 0) + if (i > 1) { verbose_stream() << premise_small_id << ", "; } From 6407ec872533a2787744243e867a3d3581762837 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 12 Dec 2017 22:13:58 -0500 Subject: [PATCH 040/364] spacer_term_graph: an egraph of terms Used to determine and factor out equalities --- src/muz/spacer/CMakeLists.txt | 1 + src/muz/spacer/spacer_term_graph.cpp | 449 +++++++++++++++++++++++++++ src/muz/spacer/spacer_term_graph.h | 91 ++++++ 3 files changed, 541 insertions(+) create mode 100644 src/muz/spacer/spacer_term_graph.cpp create mode 100644 src/muz/spacer/spacer_term_graph.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 37bc7f352..f27f6e726 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -20,6 +20,7 @@ z3_add_component(spacer spacer_antiunify.cpp spacer_mev_array.cpp spacer_qe_project.cpp + spacer_term_graph.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_term_graph.cpp b/src/muz/spacer/spacer_term_graph.cpp new file mode 100644 index 000000000..c34022d48 --- /dev/null +++ b/src/muz/spacer/spacer_term_graph.cpp @@ -0,0 +1,449 @@ +#include "muz/spacer/spacer_term_graph.h" +#include "util/util.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/for_each_expr.h" + +namespace spacer { + +class term { + // -- an app represented by this term + app* m_app; + // -- root of the equivalence class + term* m_root; + // -- next element in the equivalence class (cyclic linked list) + term* m_next; + // -- eq class size + unsigned m_class_size; + + // -- general purpose mark + unsigned m_mark:1; + // -- general purpose second mark + unsigned m_mark2:1; + // -- is an interpreted constant + unsigned m_interpreted:1; + + // -- terms that contain this term as a child + //ptr_vector m_uses; + + // ptr_vector m_args; + +public: + term(app* a) : m_app(a), m_root(this), m_next(this), + m_class_size(1), m_mark(false), m_mark2(false), + m_interpreted(false) {} + + ~term() {} + + unsigned get_id() const {return m_app->get_id();} + + bool is_marked() const {return m_mark;} + void set_mark(bool v){m_mark = v;} + bool is_marked2() const {return m_mark2;} + void set_mark2(bool v){m_mark2 = v;} + + bool is_interpreted() const {return m_interpreted;} + void mark_as_interpreted() {m_interpreted=true;} + app* get_app() const {return m_app;} + + term &get_root() const {return *m_root;} + bool is_root() const {return m_root == this;} + void set_root(term &r) {m_root = &r;} + term &get_next() const {return *m_next;} + + unsigned get_class_size() const {return m_class_size;} + + void merge_eq_class(term &b) { + std::swap(this->m_next, b.m_next); + m_class_size += b.get_class_size(); + // -- reset (useful for debugging) + b.m_class_size = 0; + } + + // -- make this term the root of its equivalence class + void mk_root() { + if (is_root()) return; + + term *curr = this; + do { + if (curr->is_root()) { + // found previous root + SASSERT(curr != this); + m_class_size = curr->get_class_size(); + curr->m_class_size = 0; + } + curr->set_root(*this); + curr = &curr->get_next(); + } + while (curr != this); + } +}; + +class arith_term_graph_plugin : public term_graph_plugin { + term_graph &m_g; + ast_manager &m; + arith_util m_arith; + +public: + arith_term_graph_plugin(term_graph &g) : + term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), + m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} + + virtual ~arith_term_graph_plugin() {} + + bool mk_eq_core (expr *_e1, expr *_e2, app* &res) { + expr *e1, *e2; + e1 = _e1; + e2 = _e2; + + if (m_arith.is_zero(e1)) { + std::swap(e1, e2); + } + // y + -1*x == 0 --> y = x + expr *a0 = 0, *a1 = 0, *x = 0; + if (m_arith.is_zero(e2) && m_arith.is_add(e1, a0, a1)) { + if (m_arith.is_times_minus_one(a1, x)) { + e1 = a0; + e2 = x; + } + else if (m_arith.is_times_minus_one(a0, x)) { + e1 = a1; + e2 = x; + } + } + res = m.mk_eq(e1, e2); + return true; + } + + bool mk_le_core (expr *arg1, expr * arg2, app* &result) { + // t <= -1 ==> t < 0 ==> ! (t >= 0) + if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { + result = m.mk_not (m_arith.mk_ge (arg1, mk_zero ())); + return true; + } + return false; + } + + expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} + bool is_one (expr const * n) const { + rational val; + return m_arith.is_numeral (n, val) && val.is_one (); + } + + bool mk_ge_core (expr * arg1, expr * arg2, app* &result) { + // t >= 1 ==> t > 0 ==> ! (t <= 0) + if (m_arith.is_int (arg1) && is_one (arg2)) { + result = m.mk_not (m_arith.mk_le (arg1, mk_zero ())); + return true; + } + return false; + } + + virtual app* process_lit (app *lit) { + expr *e1, *e2; + + + app *res = lit; + if (m.is_eq (lit, e1, e2)) { + mk_eq_core(e1, e2, res); + } + else if (m_arith.is_le(lit, e1, e2)) { + mk_le_core(e1, e2, res); + } + else if (m_arith.is_ge(lit, e1, e2)) { + mk_ge_core(e1, e2, res); + } + return res; + } +}; + +term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { + m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); +} +term_graph::~term_graph() { + std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); +} + +static family_id get_family_id(ast_manager &m, app *lit) { + family_id fid = null_family_id; + + expr *e1, *e2, *e3; + // strip negation + if (!m.is_not (lit, e1)) { e1 = lit; } + + // deal with equality using sort of range + if (m.is_eq (e1, e2, e3)) { + fid = get_sort (e2)->get_family_id(); + } + // extract family_id of top level app + else { + fid = to_app(e1)->get_decl()->get_family_id(); + } + + return fid; +} + +void term_graph::add_lit(app *l) { + app_ref lit(m); + + family_id fid = get_family_id (m, l); + term_graph_plugin *pin = m_plugins.get_plugin(fid); + if (pin) { + lit = pin->process_lit(l); + } else { + lit = l; + } + m_lits.push_back(lit); + internalize_lit(lit); +} + +bool term_graph::is_internalized(app *a) { + return m_app2term.contains(a->get_id()); +} + +term* term_graph::get_term(app *a) { + term *res; + return m_app2term.find (a->get_id(), res) ? res : nullptr; +} + +term *term_graph::mk_term(app *a) { + term *t; + t = alloc(term, a); + if (a->get_num_args() == 0 && m.is_unique_value(a)){ + t->mark_as_interpreted(); + } + + m_terms.push_back(t); + m_app2term.insert(a->get_id(), t); + return t; +} + +term *term_graph::internalize_term(app *t) { + term *res = get_term(t); + + if (!res) { + for (unsigned i=0, sz=t->get_num_args(); i < sz; ++i) { + expr *arg = t->get_arg(i); + SASSERT(is_app(arg)); + internalize_term(::to_app(arg)); + } + res = mk_term(t); + } + return res; +} + +void term_graph::internalize_eq(app *a1, app* a2) { + internalize_lit(a1); + internalize_lit(a2); + + term *t1, *t2; + t1 = get_term(a1); + t2 = get_term(a2); + SASSERT(t1); + SASSERT(t2); + + merge(t1->get_root(), t2->get_root()); +} + +void term_graph::internalize_lit(app* lit) { + if (is_internalized(lit)) return; + + expr *e1, *e2; + if (m.is_eq (lit, e1, e2)) { + SASSERT(is_app(e1)); + SASSERT(is_app(e2)); + internalize_eq (::to_app(e1), ::to_app(e2)); + } + else { + internalize_term(lit); + } +} + +void term_graph::merge (term &t1, term &t2) { + SASSERT(t1.is_root()); + SASSERT(t2.is_root()); + + if (&t1 == &t2) return; + + term *a = &t1; + term *b = &t2; + if (a->get_class_size() > b->get_class_size()) { + std::swap(a, b); + } + + // make 'a' be the root of the equivalence class of 'b' + b->set_root(*a); + for (term *it = &b->get_next(); it != b; it = &it->get_next()) { + it->set_root(*a); + } + + // merge equivalence classes + a->merge_eq_class(*b); + + // -- merge might have invalidated term2map cache + m_term2app.reset(); + m_pinned.reset(); +} + +app* term_graph::mk_app_core (app *a) { + expr_ref_vector kids(m); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + kids.push_back (mk_app(::to_app(a->get_arg(i)))); + } + + app* res = m.mk_app(a->get_decl(), kids.c_ptr()); + m_pinned.push_back(res); + + return res; +} + +app_ref term_graph::mk_app(term const &r) { + SASSERT(r.is_root()); + + if (r.get_app()->get_num_args() == 0) { + return app_ref(r.get_app(), m); + } + + app* res; + if (m_term2app.find(r.get_id(), res)) { + return app_ref(res, m); + } + + res = mk_app_core (r.get_app()); + m_term2app.insert(r.get_id(), res); + return app_ref(res, m); + +} +app_ref term_graph::mk_app(app *a) { + term *t = get_term(a); + if (!t) {return app_ref(a, m);} + + term &r = t->get_root(); + return mk_app(r); + +} + +void term_graph::mk_equalities(term const &t, app_ref_vector &out) { + SASSERT(t.is_root()); + app_ref rep(m); + rep = mk_app(t); + + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + app* mem; + mem = mk_app_core(it->get_app()); + out.push_back (m.mk_eq (rep, mem)); + } +} + +void term_graph::mk_all_equalities(term const &t, app_ref_vector &out) { + mk_equalities(t, out); + + for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { + app* a1; + a1 = mk_app_core (it->get_app()); + for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { + app *a2; + a2 = mk_app_core(it2->get_app()); + out.push_back (m.mk_eq (a1, a2)); + } + } +} + +void term_graph::reset_marks() { + for (unsigned i = 0, sz = m_terms.size(); i < sz; ++i) { + term *t = m_terms.get(i); + t->set_mark(false); + } +} + +/// Order of preference for roots of equivalence classes +/// XXX This should be factored out to let clients control the preference +bool term_graph::term_le(term const &t1, term const &t2) { + + // prefer constants over applications + // prefer uninterpreted constants over values + // prefer smaller expressions over larger ones + app *a1, *a2; + a1 = t1.get_app(); + a2 = t2.get_app(); + if (a1->get_num_args() == 0 && a2->get_num_args() > 0) { + return true; + } + if (a1->get_num_args() == a2->get_num_args()) { + return m.is_value(a2); + } + + unsigned sz1 = get_num_exprs(a1); + unsigned sz2 = get_num_exprs(a2); + return sz1 < sz2; +} + +void term_graph::pick_root (term &t) { + term *r = &t; + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + it->set_mark(true); + if (term_le(*it, *r)) { r = it; } + } + + // -- if found something better, make it the new root + if (r != &t) { + r->mk_root(); + } +} +/// Choose better roots for equivalence classes +void term_graph::pick_roots() { + for (unsigned i = 0, sz = m_terms.size(); i < sz; ++i) { + term *t = m_terms.get(i); + if (t->is_marked() || !t->is_root()) {continue;} + pick_root(*t); + } + reset_marks(); +} + +void term_graph::display(std::ostream &out) { + for (unsigned i = 0, sz = m_terms.size(); i < sz; ++i) { + term *t = m_terms.get(i); + out << mk_pp(t->get_app(), m) << " is root " << t->is_root() + << " cls sz " << t->get_class_size() + << " term " << t + << "\n"; + } +} +void term_graph::to_lits (app_ref_vector &lits, bool all_equalities) { + pick_roots(); + + for (unsigned i = 0, sz = m_lits.size(); i < sz; ++i) { + app *a = m_lits.get(i); + if (is_internalized(a)) { + lits.push_back (mk_app(a)); + } + } + + for (unsigned i = 0, sz = m_terms.size(); i < sz; ++i) { + term *t = m_terms.get(i); + if (!t->is_root()) {continue;} + + if (all_equalities) { + mk_all_equalities (*t, lits); + } else { + mk_equalities(*t, lits); + } + } + +} + +app_ref term_graph::to_app() { + app_ref_vector lits(m); + to_lits(lits); + return mk_and(lits); +} + +void term_graph::reset() { + m_term2app.reset(); + m_pinned.reset(); + m_app2term.reset(); + m_terms.reset(); + m_lits.reset(); +} + +} diff --git a/src/muz/spacer/spacer_term_graph.h b/src/muz/spacer/spacer_term_graph.h new file mode 100644 index 000000000..6e5ea9a6a --- /dev/null +++ b/src/muz/spacer/spacer_term_graph.h @@ -0,0 +1,91 @@ +/**++ +Copyright (c) Arie Gurfinkel + +Module Name: + + spacer_term_graph.h + +Abstract: + + Equivalence graph of terms + +Author: + + Arie Gurfinkel + +Notes: + +--*/ +#ifndef _SPACER_TERM_GRAPH_H_ +#define _SPACER_TERM_GRAPH_H_ + +#include "ast/ast.h" + +#include "util/plugin_manager.h" + +namespace spacer { + +class term; + +class term_graph_plugin { + family_id m_id; +public: + term_graph_plugin(family_id fid) : m_id(fid) {} + virtual ~term_graph_plugin() {} + + family_id get_family_id() const {return m_id;} + + /// Process (and potentially augment) a literal + virtual app* process_lit (app *lit) {return lit;} +}; + +class term_graph { + ast_manager &m; + ptr_vector m_terms; + app_ref_vector m_lits; + u_map m_app2term; + + app_ref_vector m_pinned; + u_map m_term2app; + + plugin_manager m_plugins; + + void merge(term &t1, term &t2); + + term *mk_term(app *t); + term *get_term(app *t); + + term *internalize_term(app *t); + void internalize_eq(app *a1, app *a2); + void internalize_lit(app *lit); + + bool is_internalized(app *a); + + bool term_le(term const &t1, term const &t2); + void pick_root (term &t); + void pick_roots(); + + void reset_marks(); + + app *mk_app_core(app* a); + app_ref mk_app(term const &t); + app_ref mk_app(app *a); + void mk_equalities(term const &t, app_ref_vector &out); + void mk_all_equalities(term const &t, app_ref_vector &out); + void display(std::ostream &out); +public: + term_graph(ast_manager &man); + ~term_graph(); + + ast_manager &get_ast_manager() const {return m;} + + void add_lit(app *lit); + + void reset(); + void to_lits (app_ref_vector &lits, bool all_equalities = false); + app_ref to_app(); + +}; + +} +#endif From be77b1de394ea45e781477181ffc4e5cc70a21df Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Dec 2017 16:24:33 -0500 Subject: [PATCH 041/364] Improve interface of term_graph --- src/muz/spacer/spacer_term_graph.cpp | 19 +++++++++++++------ src/muz/spacer/spacer_term_graph.h | 10 ++++++++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/muz/spacer/spacer_term_graph.cpp b/src/muz/spacer/spacer_term_graph.cpp index c34022d48..61f7fb369 100644 --- a/src/muz/spacer/spacer_term_graph.cpp +++ b/src/muz/spacer/spacer_term_graph.cpp @@ -91,7 +91,7 @@ public: virtual ~arith_term_graph_plugin() {} - bool mk_eq_core (expr *_e1, expr *_e2, app* &res) { + bool mk_eq_core (expr *_e1, expr *_e2, app_ref &res) { expr *e1, *e2; e1 = _e1; e2 = _e2; @@ -115,7 +115,7 @@ public: return true; } - bool mk_le_core (expr *arg1, expr * arg2, app* &result) { + bool mk_le_core (expr *arg1, expr * arg2, app_ref &result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { result = m.mk_not (m_arith.mk_ge (arg1, mk_zero ())); @@ -130,7 +130,7 @@ public: return m_arith.is_numeral (n, val) && val.is_one (); } - bool mk_ge_core (expr * arg1, expr * arg2, app* &result) { + bool mk_ge_core (expr * arg1, expr * arg2, app_ref &result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) if (m_arith.is_int (arg1) && is_one (arg2)) { result = m.mk_not (m_arith.mk_le (arg1, mk_zero ())); @@ -139,11 +139,12 @@ public: return false; } - virtual app* process_lit (app *lit) { + virtual app_ref process_lit (app *lit) { expr *e1, *e2; - app *res = lit; + app_ref res(m); + res = lit; if (m.is_eq (lit, e1, e2)) { mk_eq_core(e1, e2, res); } @@ -429,7 +430,13 @@ void term_graph::to_lits (app_ref_vector &lits, bool all_equalities) { mk_equalities(*t, lits); } } - +} +void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { + app_ref_vector out(m); + to_lits (out, all_equalities); + for (unsigned i = 0, sz = out.size(); i < sz; ++i) { + lits.push_back(out.get(i)); + } } app_ref term_graph::to_app() { diff --git a/src/muz/spacer/spacer_term_graph.h b/src/muz/spacer/spacer_term_graph.h index 6e5ea9a6a..b547adef3 100644 --- a/src/muz/spacer/spacer_term_graph.h +++ b/src/muz/spacer/spacer_term_graph.h @@ -36,7 +36,7 @@ public: family_id get_family_id() const {return m_id;} /// Process (and potentially augment) a literal - virtual app* process_lit (app *lit) {return lit;} + virtual app_ref process_lit (app *lit) = 0; }; class term_graph { @@ -80,9 +80,15 @@ public: ast_manager &get_ast_manager() const {return m;} void add_lit(app *lit); + void add_lits(expr_ref_vector const &lits) { + for (unsigned i = 0, sz = lits.size(); i < sz; ++i) { + add_lit(::to_app(lits.get(i))); + } + } void reset(); - void to_lits (app_ref_vector &lits, bool all_equalities = false); + void to_lits(app_ref_vector &lits, bool all_equalities = false); + void to_lits(expr_ref_vector &lits, bool all_equalities = false); app_ref to_app(); }; From 09d54c10a67235296d4335ec68933f0d2aa0d1ce Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Dec 2017 16:25:00 -0500 Subject: [PATCH 042/364] Wire term graph into spacer normalizer --- src/muz/spacer/spacer_util.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 1d8a59d36..9a0148784 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -9,6 +9,7 @@ Abstract: Utility functions for SPACER. + Author: Krystof Hoder (t-khoder) 2011-8-19. @@ -64,6 +65,7 @@ Notes: #include "tactic/arith/arith_bounds_tactic.h" #include "ast/factor_equivs.h" +#include "muz/spacer/spacer_term_graph.h" namespace spacer { @@ -1061,14 +1063,24 @@ void normalize (expr *e, expr_ref &out, simplify_bounds (v); } if (use_factor_eqs) { - // pick non-constant value representative for - // equivalence classes - expr_equiv_class eq_classes(out.m()); - factor_eqs(v, eq_classes); - rewrite_eqs(v, eq_classes); - equiv_to_expr(eq_classes, v); + // -- refactor equivalence classes and choose a representative + spacer::term_graph egraph(out.m()); + egraph.add_lits (v); + v.reset(); + egraph.to_lits(v); } + TRACE("spacer_normalize", + tout << "Normalized:\n" + << out << "\n" + << "to\n" + << mk_and(v) << "\n";); + TRACE("spacer_normalize", + spacer::term_graph egraph(out.m()); + for (unsigned i = 0, sz = v.size(); i < sz; ++i) + egraph.add_lit (to_app(v.get(i))); + tout << "Reduced app:\n" + << mk_pp(egraph.to_app(), out.m()) << "\n";); out = mk_and (v); } } From 9bc11b2122a618209d6fb8c90f3b33c81729ac10 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Dec 2017 16:25:14 -0500 Subject: [PATCH 043/364] Wire term graph into eq_generalizer --- src/muz/spacer/spacer_generalizers.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index e2058e93f..dd75f5974 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -24,8 +24,7 @@ Revision History: #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" -#include "ast/factor_equivs.h" - +#include "muz/spacer/spacer_term_graph.h" namespace spacer { void lemma_sanity_checker::operator()(lemma_ref &lemma) { @@ -282,16 +281,20 @@ void lemma_eq_generalizer::operator() (lemma_ref &lemma) { TRACE("core_eq", tout << "Transforming equivalence classes\n";); - ast_manager &m = m_ctx.get_ast_manager(); - expr_ref_vector core(m); - core.append (lemma->get_cube()); + if (lemma->get_cube().empty()) return; - bool dirty; - expr_equiv_class eq_classes(m); - factor_eqs(core, eq_classes); - // create all possible equalities to allow for simple inductive generalization - dirty = equiv_to_expr_full(eq_classes, core); - if (dirty) { + ast_manager &m = m_ctx.get_ast_manager(); + spacer::term_graph egraph(m); + egraph.add_lits(lemma->get_cube()); + + // -- expand the cube with all derived equalities + expr_ref_vector core(m); + egraph.to_lits(core, true); + + // -- if the core looks different from the original cube + if (core.size() != lemma->get_cube().size() || + core.get(0) != lemma->get_cube().get(0)) { + // -- update the lemma lemma->update_cube(lemma->get_pob(), core); } } From ea73acef45e937d2ee76b23f2d8fd2389b912525 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 14 Dec 2017 17:05:25 -0500 Subject: [PATCH 044/364] Implements mk_num_pat Abstracts interpreted numeric constants with variables in a ground expression --- src/muz/spacer/spacer_antiunify.cpp | 77 +++++++++++++++++++++++++++++ src/muz/spacer/spacer_antiunify.h | 3 ++ 2 files changed, 80 insertions(+) diff --git a/src/muz/spacer/spacer_antiunify.cpp b/src/muz/spacer/spacer_antiunify.cpp index 6baf9e93d..6c38e4898 100644 --- a/src/muz/spacer/spacer_antiunify.cpp +++ b/src/muz/spacer/spacer_antiunify.cpp @@ -28,6 +28,7 @@ Revision History: namespace spacer { + // Abstracts numeric values by variables struct var_abs_rewriter : public default_rewriter_cfg { ast_manager &m; @@ -453,7 +454,83 @@ void naive_convex_closure::substitute_vars_by_const(ast_manager& m, expr* t, subs_rw (t, res); } + +/// Construct a pattern by abstracting all numbers by variables +struct mk_num_pat_rewriter : public default_rewriter_cfg { + ast_manager &m; + arith_util m_arith; + + // -- mark already seen expressions + ast_mark m_seen; + // -- true if the expression is known to have a number as a sub-expression + ast_mark m_has_num; + // -- expressions created during the transformation + expr_ref_vector m_pinned; + // -- map from introduced variables to expressions they replace + app_ref_vector &m_subs; + + + // -- stack of expressions being processed to have access to expressions + // -- before rewriting + ptr_buffer m_stack; + + mk_num_pat_rewriter (ast_manager &manager, app_ref_vector& subs) : + m(manager), m_arith(m), m_pinned(m), m_subs(subs) {} + + bool pre_visit(expr * t) { + // -- don't touch multiplication + if (m_arith.is_mul(t)) return false; + + bool r = (!m_seen.is_marked(t) || m_has_num.is_marked(t)); + if (r) {m_stack.push_back (t);} + return r; + } + + + br_status reduce_app (func_decl * f, unsigned num, expr * const * args, + expr_ref & result, proof_ref & result_pr) { + expr *s; + s = m_stack.back(); + m_stack.pop_back(); + if (is_app(s)) { + app *a = to_app(s); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + if (m_has_num.is_marked(a->get_arg(i))) { + m_has_num.mark(a, true); + break; + } + } + } + return BR_FAILED; + } + + bool cache_all_results() const { return false; } + bool cache_results() const { return false; } + + bool get_subst(expr * s, expr * & t, proof * & t_pr) { + if (m_arith.is_numeral(s)) { + t = m.mk_var(m_subs.size(), m.get_sort(s)); + m_pinned.push_back(t); + m_subs.push_back(to_app(s)); + + m_has_num.mark(t, true); + m_seen.mark(t, true); + return true; + } + return false; + } + +}; + +void mk_num_pat(expr *e, expr_ref &result, app_ref_vector &subs) { + SASSERT(subs.empty()); + mk_num_pat_rewriter rw_cfg(result.m(), subs); + rewriter_tpl rw(result.m(), false, rw_cfg); + rw(e, result); +} + } template class rewriter_tpl; template class rewriter_tpl; +template class rewriter_tpl; diff --git a/src/muz/spacer/spacer_antiunify.h b/src/muz/spacer/spacer_antiunify.h index 2b86f67ec..fb0933f0d 100644 --- a/src/muz/spacer/spacer_antiunify.h +++ b/src/muz/spacer/spacer_antiunify.h @@ -63,5 +63,8 @@ private: expr_ref& res); }; +/// Abstracts numbers in the given ground expression by variables +/// Returns the created pattern and the corresponding substitution. +void mk_num_pat(expr *e, expr_ref &result, app_ref_vector &subs); } #endif From f51c07adf6c1781e6cd75062fa08c3df02942ba2 Mon Sep 17 00:00:00 2001 From: Yakir Vizel Date: Mon, 18 Dec 2017 09:14:38 -0500 Subject: [PATCH 045/364] Moving skolems to lemma --- src/muz/spacer/spacer_context.cpp | 17 +++++++++-------- src/muz/spacer/spacer_context.h | 2 ++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 8f4b63224..ac5dc7477 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -40,7 +40,7 @@ Notes: #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" -#include "ast/proofs/proof_checker.h" +#include "ast/proof_checker/proof_checker.h" #include "smt/smt_value_sort.h" #include "ast/scoped_proof.h" #include "muz/spacer/spacer_qe_project.h" @@ -1177,7 +1177,7 @@ void pred_transformer::inherit_properties(pred_transformer& other) lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), - m_bindings(m), m_lvl(lvl), + m_bindings(m), m_zks(m), m_lvl(lvl), m_pob(nullptr), m_new_pob(false) { SASSERT(m_body); normalize(m_body, m_body); @@ -1186,16 +1186,17 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), - m_bindings(m), m_lvl(p->level()), - m_pob(p), m_new_pob(m_pob) {SASSERT(m_pob);} + m_bindings(m), m_zks(m), m_lvl(p->level()), + m_pob(p), m_new_pob(m_pob) {SASSERT(m_pob); m_pob->get_skolems(m_zks);} lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), - m_bindings(m), m_lvl(p->level()), + m_bindings(m), m_zks(m), m_lvl(p->level()), m_pob(p), m_new_pob(m_pob) { + m_pob->get_skolems(m_zks); update_cube(p, cube); set_level(lvl); } @@ -1210,9 +1211,9 @@ void lemma::mk_expr_core() { m_body = ::push_not(::mk_and(m_cube)); normalize(m_body, m_body); - if (!m_pob->is_ground() && has_zk_const(m_body)) { + if (!m_zks.empty() && has_zk_const(m_body)) { app_ref_vector zks(m); - m_pob->get_skolems(zks); + zks.append(m_zks); zks.reverse(); expr_abstract(m, 0, zks.size(), (expr* const*)zks.c_ptr(), m_body, @@ -2163,8 +2164,8 @@ bool context::validate() expr_ref_vector refs(m); expr_ref tmp(m); model_ref model; - model_converter_ref mc; vector rs; + model_converter_ref mc; get_level_property(m_inductive_lvl, refs, rs); inductive_property ex(m, mc, rs); ex.to_model(model); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 181a55142..708497460 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -111,6 +111,7 @@ class lemma { expr_ref m_body; expr_ref_vector m_cube; app_ref_vector m_bindings; + app_ref_vector m_zks; unsigned m_lvl; pob_ref m_pob; bool m_new_pob; @@ -133,6 +134,7 @@ public: pob_ref &get_pob() {return m_pob;} inline unsigned weakness(); + void set_skolems(app_ref_vector &zks) { m_zks.append(zks); } unsigned level () const {return m_lvl;} void set_level (unsigned lvl) {m_lvl = lvl;} app_ref_vector& get_bindings() {return m_bindings;} From 981e521b18648c0f1408fc02e77082d7ab1915eb Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 18 Dec 2017 12:06:10 -0500 Subject: [PATCH 046/364] Cleanup lemma definition exposes a potential bug. See comments in code. --- src/muz/spacer/spacer_context.cpp | 11 +++++++---- src/muz/spacer/spacer_context.h | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index ac5dc7477..9d1aafc9a 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1177,7 +1177,7 @@ void pred_transformer::inherit_properties(pred_transformer& other) lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), - m_bindings(m), m_zks(m), m_lvl(lvl), + m_zks(m), m_bindings(m), m_lvl(lvl), m_pob(nullptr), m_new_pob(false) { SASSERT(m_body); normalize(m_body, m_body); @@ -1186,17 +1186,17 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), - m_bindings(m), m_zks(m), m_lvl(p->level()), + m_zks(m), m_bindings(m), m_lvl(p->level()), m_pob(p), m_new_pob(m_pob) {SASSERT(m_pob); m_pob->get_skolems(m_zks);} lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), - m_bindings(m), m_zks(m), m_lvl(p->level()), + m_zks(m), m_bindings(m), m_lvl(p->level()), m_pob(p), m_new_pob(m_pob) { - m_pob->get_skolems(m_zks); + if (m_pob) {m_pob->get_skolems(m_zks);} update_cube(p, cube); set_level(lvl); } @@ -1229,6 +1229,9 @@ void lemma::mk_expr_core() { names.c_ptr(), m_body, 15, symbol(m_body->get_id())); if (m_new_pob) { + // XXX This assertion will fail when a lemma is + // XXX generalized with additional quantified variables + SASSERT(m_pob->get_binding().size() == m_zks.size()); add_binding(m_pob->get_binding()); } } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 708497460..b90d84793 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -110,8 +110,8 @@ class lemma { ast_manager &m; expr_ref m_body; expr_ref_vector m_cube; - app_ref_vector m_bindings; app_ref_vector m_zks; + app_ref_vector m_bindings; unsigned m_lvl; pob_ref m_pob; bool m_new_pob; @@ -134,7 +134,9 @@ public: pob_ref &get_pob() {return m_pob;} inline unsigned weakness(); - void set_skolems(app_ref_vector &zks) { m_zks.append(zks); } + void add_skolems(app_ref_vector &zks) {m_zks.append(zks);} + void add_skolem(app *zk) {m_zks.push_back(zk);} + unsigned level () const {return m_lvl;} void set_level (unsigned lvl) {m_lvl = lvl;} app_ref_vector& get_bindings() {return m_bindings;} From 7b82ec1beef79b0c13bdfc07ffc284a98f91a20f Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 18 Dec 2017 13:04:19 -0500 Subject: [PATCH 047/364] Attempt bug fix --- src/muz/spacer/spacer_context.cpp | 80 +++++++++++++++---------------- src/muz/spacer/spacer_context.h | 4 +- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 9d1aafc9a..80c21d61c 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1178,7 +1178,7 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(lvl), - m_pob(nullptr), m_new_pob(false) { + m_pob(nullptr) { SASSERT(m_body); normalize(m_body, m_body); } @@ -1187,64 +1187,64 @@ lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(p->level()), - m_pob(p), m_new_pob(m_pob) {SASSERT(m_pob); m_pob->get_skolems(m_zks);} + m_pob(p) { + SASSERT(m_pob); + m_pob->get_skolems(m_zks); + add_binding(m_pob->get_binding()); +} lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(p->level()), - m_pob(p), m_new_pob(m_pob) + m_pob(p) { - if (m_pob) {m_pob->get_skolems(m_zks);} + if (m_pob) { + m_pob->get_skolems(m_zks); + add_binding(m_pob->get_binding()); + } update_cube(p, cube); set_level(lvl); } +void lemma::add_skolem(app *zk, app *b) { + SASSERT(m_bindings.size() == m_zks.size()); + // extend bindings + m_bindings.push_back(b); + // extend skolems + m_zks.push_back(zk); +} + + void lemma::mk_expr_core() { if (m_body) return; if (m_pob) { mk_cube_core(); + } - // make a clause by negating the cube - m_body = ::push_not(::mk_and(m_cube)); - normalize(m_body, m_body); + SASSERT(!m_cube.empty()); + m_body = ::push_not(::mk_and(m_cube)); + normalize(m_body, m_body); - if (!m_zks.empty() && has_zk_const(m_body)) { - app_ref_vector zks(m); - zks.append(m_zks); - zks.reverse(); - expr_abstract(m, 0, - zks.size(), (expr* const*)zks.c_ptr(), m_body, - m_body); - ptr_buffer sorts; - svector names; - for (unsigned i=0, sz=zks.size(); i < sz; ++i) { - sorts.push_back(get_sort(zks.get(i))); - names.push_back(zks.get(i)->get_decl()->get_name()); - } - m_body = m.mk_quantifier(true, zks.size(), - sorts.c_ptr(), - names.c_ptr(), - m_body, 15, symbol(m_body->get_id())); - if (m_new_pob) { - // XXX This assertion will fail when a lemma is - // XXX generalized with additional quantified variables - SASSERT(m_pob->get_binding().size() == m_zks.size()); - add_binding(m_pob->get_binding()); - } + if (!m_zks.empty() && has_zk_const(m_body)) { + app_ref_vector zks(m); + zks.append(m_zks); + zks.reverse(); + expr_abstract(m, 0, + zks.size(), (expr* const*)zks.c_ptr(), m_body, + m_body); + ptr_buffer sorts; + svector names; + for (unsigned i=0, sz=zks.size(); i < sz; ++i) { + sorts.push_back(get_sort(zks.get(i))); + names.push_back(zks.get(i)->get_decl()->get_name()); } - m_new_pob = false; - return; - } - else if (!m_cube.empty()) { - m_body = ::push_not(::mk_and(m_cube)); - normalize(m_body, m_body); - return; - } - else { - UNREACHABLE(); + m_body = m.mk_quantifier(true, zks.size(), + sorts.c_ptr(), + names.c_ptr(), + m_body, 15, symbol(m_body->get_id())); } SASSERT(m_body); } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index b90d84793..10bb06317 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -114,7 +114,6 @@ class lemma { app_ref_vector m_bindings; unsigned m_lvl; pob_ref m_pob; - bool m_new_pob; void mk_expr_core(); void mk_cube_core(); @@ -134,8 +133,7 @@ public: pob_ref &get_pob() {return m_pob;} inline unsigned weakness(); - void add_skolems(app_ref_vector &zks) {m_zks.append(zks);} - void add_skolem(app *zk) {m_zks.push_back(zk);} + void add_skolem(app *zk, app* b); unsigned level () const {return m_lvl;} void set_level (unsigned lvl) {m_lvl = lvl;} From 05e876d6842e904d93629e2f1f3ed1c9b21274c8 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 19 Dec 2017 15:03:52 -0500 Subject: [PATCH 048/364] Fix n-arry applications in spacer_term_graph --- src/muz/spacer/spacer_term_graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_term_graph.cpp b/src/muz/spacer/spacer_term_graph.cpp index 61f7fb369..7c2b64a50 100644 --- a/src/muz/spacer/spacer_term_graph.cpp +++ b/src/muz/spacer/spacer_term_graph.cpp @@ -292,7 +292,7 @@ app* term_graph::mk_app_core (app *a) { kids.push_back (mk_app(::to_app(a->get_arg(i)))); } - app* res = m.mk_app(a->get_decl(), kids.c_ptr()); + app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); m_pinned.push_back(res); return res; From ec179da0fa3a260bb61524d7569b14dd48ed9f3d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 4 Jan 2018 13:43:20 -0500 Subject: [PATCH 049/364] API to get num of free variables in a pob --- src/muz/spacer/spacer_context.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 10bb06317..494da06a7 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -524,6 +524,7 @@ public: void erase_child (pob &v) {m_kids.erase (&v);} bool is_ground () { return m_binding.empty (); } + unsigned get_free_vars_size() { return m_binding.size(); } app_ref_vector const &get_binding() const {return m_binding;} /* * Return skolem variables that appear in post From a8318b8822da5d56f0859a9fdc84196e2a3e3984 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 19 Dec 2017 09:38:35 -0500 Subject: [PATCH 050/364] Fix lemma::has_binding() --- src/muz/spacer/spacer_context.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 80c21d61c..c7f9f448f 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1303,8 +1303,7 @@ void lemma::update_cube (pob_ref const &p, expr_ref_vector &cube) { } bool lemma::has_binding(app_ref_vector const &binding) { - expr *lem = get_expr(); - unsigned num_decls = to_quantifier(lem)->get_num_decls(); + unsigned num_decls = m_zks.size(); SASSERT(binding.size() == num_decls); From ecf9c629b044c7fa8cf0ca40cdec9a144285dbf7 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 26 Dec 2017 16:48:37 -0500 Subject: [PATCH 051/364] Fix binding handling for quantifier free lemmas --- src/muz/spacer/spacer_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index c7f9f448f..1151909f1 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1307,6 +1307,8 @@ bool lemma::has_binding(app_ref_vector const &binding) { SASSERT(binding.size() == num_decls); + if (num_decls == 0) return true; + for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { unsigned i = 0; for (; i < num_decls; ++i) { From 0c1ef7155ac165c1dffdbd9f41e79c2886149a29 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 22 Dec 2017 23:15:10 -0500 Subject: [PATCH 052/364] Option to rewrite expression in term_graph Rewrite expressions to minimize uses of constants 0 and 1 Currently disabled due to interaction with quic --- src/muz/spacer/spacer_term_graph.cpp | 57 ++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_term_graph.cpp b/src/muz/spacer/spacer_term_graph.cpp index 7c2b64a50..859111c6c 100644 --- a/src/muz/spacer/spacer_term_graph.cpp +++ b/src/muz/spacer/spacer_term_graph.cpp @@ -115,10 +115,45 @@ public: return true; } + app* mk_le_zero(expr *arg) { + expr *e1, *e2, *e3; + // XXX currently disabled + if (false && m_arith.is_add(arg, e1, e2)) { + // e1-e2<=0 --> e1<=e2 + if (m_arith.is_times_minus_one(e2, e3)) { + return m_arith.mk_le(e1, e3); + } + // -e1+e2<=0 --> e2<=e1 + else if (m_arith.is_times_minus_one(e1, e3)) { + return m_arith.mk_le(e2, e3); + } + } + return m_arith.mk_le(arg, mk_zero()); + } + app* mk_ge_zero(expr *arg) { + expr *e1, *e2, *e3; + // XXX currently disabled + if (false && m_arith.is_add(arg, e1, e2)) { + // e1-e2>=0 --> e1>=e2 + if (m_arith.is_times_minus_one(e2, e3)) { + return m_arith.mk_ge(e1, e3); + } + // -e1+e2>=0 --> e2>=e1 + else if (m_arith.is_times_minus_one(e1, e3)) { + return m_arith.mk_ge(e2, e3); + } + } + return m_arith.mk_ge(arg, mk_zero()); + } + bool mk_le_core (expr *arg1, expr * arg2, app_ref &result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { - result = m.mk_not (m_arith.mk_ge (arg1, mk_zero ())); + result = m.mk_not (mk_ge_zero (arg1)); + return true; + } + else if (m_arith.is_zero(arg2)) { + result = mk_le_zero(arg1); return true; } return false; @@ -133,15 +168,25 @@ public: bool mk_ge_core (expr * arg1, expr * arg2, app_ref &result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) if (m_arith.is_int (arg1) && is_one (arg2)) { - result = m.mk_not (m_arith.mk_le (arg1, mk_zero ())); + result = m.mk_not (mk_le_zero (arg1)); + return true; + } + else if (m_arith.is_zero(arg2)) { + result = mk_ge_zero(arg1); return true; } return false; } - virtual app_ref process_lit (app *lit) { + virtual app_ref process_lit (app *_lit) { + app *lit = _lit; expr *e1, *e2; + // strip negation + bool is_neg = m.is_not(lit); + if (is_neg) { + lit = to_app(to_app(lit)->get_arg(0)); + } app_ref res(m); res = lit; @@ -154,6 +199,12 @@ public: else if (m_arith.is_ge(lit, e1, e2)) { mk_ge_core(e1, e2, res); } + + // restore negation + if (is_neg) { + res = m.mk_not(res); + } + return res; } }; From 068b77d43a96acc33dd92e13a69b09a153eb76df Mon Sep 17 00:00:00 2001 From: Yakir Vizel Date: Wed, 27 Dec 2017 12:44:20 -0500 Subject: [PATCH 053/364] Normalizing LE and GE with constants --- src/muz/spacer/spacer_term_graph.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/muz/spacer/spacer_term_graph.cpp b/src/muz/spacer/spacer_term_graph.cpp index 859111c6c..5cd531870 100644 --- a/src/muz/spacer/spacer_term_graph.cpp +++ b/src/muz/spacer/spacer_term_graph.cpp @@ -118,7 +118,7 @@ public: app* mk_le_zero(expr *arg) { expr *e1, *e2, *e3; // XXX currently disabled - if (false && m_arith.is_add(arg, e1, e2)) { + if (m_arith.is_add(arg, e1, e2)) { // e1-e2<=0 --> e1<=e2 if (m_arith.is_times_minus_one(e2, e3)) { return m_arith.mk_le(e1, e3); @@ -133,7 +133,7 @@ public: app* mk_ge_zero(expr *arg) { expr *e1, *e2, *e3; // XXX currently disabled - if (false && m_arith.is_add(arg, e1, e2)) { + if (m_arith.is_add(arg, e1, e2)) { // e1-e2>=0 --> e1>=e2 if (m_arith.is_times_minus_one(e2, e3)) { return m_arith.mk_ge(e1, e3); @@ -148,6 +148,7 @@ public: bool mk_le_core (expr *arg1, expr * arg2, app_ref &result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) + rational n; if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { result = m.mk_not (mk_ge_zero (arg1)); return true; @@ -156,6 +157,11 @@ public: result = mk_le_zero(arg1); return true; } + else if (m_arith.is_numeral(arg2, n)) { + // t <= n ==> t < n + 1 ==> ! (t >= n + 1) + result = m.mk_not(m_arith.mk_ge(arg1, m_arith.mk_numeral(n+1, true))); + return true; + } return false; } @@ -167,6 +173,7 @@ public: bool mk_ge_core (expr * arg1, expr * arg2, app_ref &result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) + rational n; if (m_arith.is_int (arg1) && is_one (arg2)) { result = m.mk_not (mk_le_zero (arg1)); return true; @@ -175,6 +182,11 @@ public: result = mk_ge_zero(arg1); return true; } + else if (m_arith.is_numeral(arg2, n)) { + // t >= n ==> t > n - 1 ==> ! (t <= n - 1) + result = m.mk_not(m_arith.mk_le(arg1, m_arith.mk_numeral(n-1, true))); + return true; + } return false; } From 46fb0d928a4585ab9fc3eae3d945f70c576cad08 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 27 Dec 2017 13:01:54 -0500 Subject: [PATCH 054/364] Fix in spacer_term_graph --- src/muz/spacer/spacer_term_graph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/muz/spacer/spacer_term_graph.cpp b/src/muz/spacer/spacer_term_graph.cpp index 5cd531870..0dc29a7c5 100644 --- a/src/muz/spacer/spacer_term_graph.cpp +++ b/src/muz/spacer/spacer_term_graph.cpp @@ -157,7 +157,7 @@ public: result = mk_le_zero(arg1); return true; } - else if (m_arith.is_numeral(arg2, n)) { + else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n < 0) { // t <= n ==> t < n + 1 ==> ! (t >= n + 1) result = m.mk_not(m_arith.mk_ge(arg1, m_arith.mk_numeral(n+1, true))); return true; @@ -182,7 +182,7 @@ public: result = mk_ge_zero(arg1); return true; } - else if (m_arith.is_numeral(arg2, n)) { + else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n > 0) { // t >= n ==> t > n - 1 ==> ! (t <= n - 1) result = m.mk_not(m_arith.mk_le(arg1, m_arith.mk_numeral(n-1, true))); return true; From 7dee36358df189a3f50d3612bc84b8c8c24ccc82 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 26 Dec 2017 16:49:48 -0500 Subject: [PATCH 055/364] Allow bool_ind_generalizer to skip non-array literals Currently a hack to skip generalizing some literals. Used together with quic generalizer to remove all array terms if possible before quic generalization --- src/muz/spacer/spacer_generalizers.cpp | 28 ++++++++++++++++++++++++++ src/muz/spacer/spacer_generalizers.h | 7 +++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index dd75f5974..c87bdc892 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -24,8 +24,15 @@ Revision History: #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" + #include "muz/spacer/spacer_term_graph.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/substitution/matcher.h" + +#include "ast/expr_functors.h" + + namespace spacer { void lemma_sanity_checker::operator()(lemma_ref &lemma) { unsigned uses_level; @@ -36,6 +43,19 @@ void lemma_sanity_checker::operator()(lemma_ref &lemma) { lemma->weakness())); } +namespace{ + class contains_array_op_proc : public i_expr_pred { + ast_manager &m; + family_id m_array_fid; + public: + contains_array_op_proc(ast_manager &manager) : + m(manager), m_array_fid(m.mk_family_id("array")) + {} + virtual bool operator()(expr *e) { + return is_app(e) && to_app(e)->get_family_id() == m_array_fid; + } + }; +} // ------------------------ // lemma_bool_inductive_generalizer @@ -50,6 +70,9 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { pred_transformer &pt = lemma->get_pob()->pt(); ast_manager &m = pt.get_ast_manager(); + contains_array_op_proc has_array_op(m); + check_pred has_arrays(has_array_op, m); + expr_ref_vector cube(m); cube.append(lemma->get_cube()); @@ -65,6 +88,11 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { (!m_failure_limit || num_failures < m_failure_limit)) { expr_ref lit(m); lit = cube.get(i); + if (m_array_only && !has_arrays(lit)) { + processed.push_back(lit); + ++i; + continue; + } cube[i] = true_expr; if (cube.size() > 1 && pt.check_inductive(lemma->level(), cube, uses_level, weakness)) { diff --git a/src/muz/spacer/spacer_generalizers.h b/src/muz/spacer/spacer_generalizers.h index 1962e74e0..17298495c 100644 --- a/src/muz/spacer/spacer_generalizers.h +++ b/src/muz/spacer/spacer_generalizers.h @@ -48,11 +48,14 @@ class lemma_bool_inductive_generalizer : public lemma_generalizer { }; unsigned m_failure_limit; + bool m_array_only; stats m_st; public: - lemma_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : - lemma_generalizer(ctx), m_failure_limit(failure_limit) {} + lemma_bool_inductive_generalizer(context& ctx, unsigned failure_limit, + bool array_only = false) : + lemma_generalizer(ctx), m_failure_limit(failure_limit), + m_array_only(array_only) {} ~lemma_bool_inductive_generalizer() override {} void operator()(lemma_ref &lemma) override; From a1efb88318ec7182b0845db5be465d1856af39c9 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 27 Dec 2017 12:54:35 -0500 Subject: [PATCH 056/364] Semantic matcher Extends matcher with rewrites based on semantics of arithmetic operations Like matcher, but uses arithmetic and logic rewrites to try to get a semantic match. --- src/muz/spacer/CMakeLists.txt | 2 + src/muz/spacer/spacer_sem_matcher.cpp | 147 ++++++++++++++++++++++++++ src/muz/spacer/spacer_sem_matcher.h | 69 ++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 src/muz/spacer/spacer_sem_matcher.cpp create mode 100644 src/muz/spacer/spacer_sem_matcher.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index f27f6e726..16ce3e4f9 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -21,6 +21,8 @@ z3_add_component(spacer spacer_mev_array.cpp spacer_qe_project.cpp spacer_term_graph.cpp + spacer_sem_matcher.cpp + spacer_quant_generalizer.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_sem_matcher.cpp b/src/muz/spacer/spacer_sem_matcher.cpp new file mode 100644 index 000000000..f3c2af8a9 --- /dev/null +++ b/src/muz/spacer/spacer_sem_matcher.cpp @@ -0,0 +1,147 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation and Arie Gurfinkel + +Module Name: + + sem_matcher.cpp + +Abstract: + + + +Author: + + Leonardo de Moura (leonardo) 2008-02-02. + Arie Gurfinkel + +Revision History: + +--*/ +#include "muz/spacer/spacer_sem_matcher.h" + +namespace spacer { + +sem_matcher::sem_matcher(ast_manager &man) : m(man), m_arith(m), m_pinned(m) {} + +bool sem_matcher::match_var (var *v, expr *e) { + expr_offset r; + if (m_subst->find(v, 0, r)) { + if (!m.are_equal(r.get_expr(), e)) { + return false; + } + } + else { + m_subst->insert(v, 0, expr_offset(e, 1)); + } + return true; +} +bool sem_matcher::operator()(expr * e1, expr * e2, substitution & s, bool &pos) { + reset(); + m_subst = &s; + m_todo.push_back(expr_pair(e1, e2)); + + // true on the first run through the loop + bool top = true; + pos = true; + while (!m_todo.empty()) { + expr_pair const & p = m_todo.back(); + + if (is_var(p.first)) { + if (!match_var(to_var(p.first), p.second)) { + return false; + } + m_todo.pop_back(); + top = false; + continue; + } + + + if (is_var(p.second)) + return false; + if (!is_app(p.first)) + return false; + if (!is_app(p.second)) + return false; + + app * n1 = to_app(p.first); + app * n2 = to_app(p.second); + + expr *t = nullptr; + + // strip negation + if (top && n1->get_decl() != n2->get_decl()) { + if (m.is_not(n1, t) && !m.is_not(n2) && is_app(t) && + to_app(t)->get_decl() == n2->get_decl()) { + pos = false; + n1 = to_app(e1); + } + else if (!m.is_not(n1) && m.is_not(n2, t) && is_app(t) && + to_app(t)->get_decl() == n1->get_decl()) { + pos = false; + n2 = to_app(t); + } + } + top = false; + + if (n1->get_decl() != n2->get_decl()) { + expr *e1 = nullptr, *e2 = nullptr; + rational val1, val2; + + // x<=y == !(x>y) + if (m_arith.is_le(n1) && m.is_not(n2, t) && m_arith.is_gt(t)) { + n2 = to_app(t); + } + else if (m_arith.is_le(n2) && m.is_not(n1, t) && m_arith.is_gt(t)) { + n1 = to_app(t); + } + // x>=y == !(xget_num_args(); + if (num_args1 != n2->get_num_args()) + return false; + + m_todo.pop_back(); + + if (num_args1 == 0) + continue; + + unsigned j = num_args1; + while (j > 0) { + --j; + m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j))); + } + } + return true; +} + +void sem_matcher::reset() { + m_todo.reset(); + m_pinned.reset(); +} +} diff --git a/src/muz/spacer/spacer_sem_matcher.h b/src/muz/spacer/spacer_sem_matcher.h new file mode 100644 index 000000000..7d169166e --- /dev/null +++ b/src/muz/spacer/spacer_sem_matcher.h @@ -0,0 +1,69 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation and Arie Gurfinkel + +Module Name: + + sem_matcher.h + +Abstract: + + Semantic matcher + +Author: + + Leonardo de Moura (leonardo) 2008-02-02. + Arie Gurfinkel + +Revision History: + +--*/ +#ifndef SPACER_SEM_MATCHER_H_ +#define SPACER_SEM_MATCHER_H_ + +#include "ast/substitution/substitution.h" +#include "ast/arith_decl_plugin.h" +#include "util/hashtable.h" + +namespace spacer { +/** + \brief Functor for matching expressions. +*/ +class sem_matcher { + typedef std::pair expr_pair; + typedef pair_hash, obj_ptr_hash > expr_pair_hash; + typedef hashtable > cache; + + ast_manager &m; + arith_util m_arith; + expr_ref_vector m_pinned; + substitution * m_subst; + svector m_todo; + + void reset(); + + bool match_var(var *v, expr *e); +public: + sem_matcher(ast_manager &man); + + /** + \brief Return true if e2 is an instance of e1. + In case of success (result is true), it will store the substitution that makes e1 equals to e2 into s. + Sets pos to true if the match is positive and to false if it is negative (i.e., e1 equals !e2) + + For example: + 1) e1 = f(g(x), x), e2 = f(g(h(a)), h(a)) + The result is true, and s will contain x -> h(a) + + 2) e1 = f(a, x) e2 = f(x, a) + The result is false. + + 3) e1 = f(x, x) e2 = f(y, a) + The result is false + + 4) e1 = f(x, y) e2 = f(h(z), a) + The result is true, and s contains x->h(z) and y->a + */ + bool operator()(expr * e1, expr * e2, substitution & s, bool &pos); +}; +} +#endif /* SPACER_SEM_MATCHER_H_ */ From 23a8e59493f95a18e62bdeebe007700ae29d6be3 Mon Sep 17 00:00:00 2001 From: Yakir Vizel Date: Mon, 18 Dec 2017 13:52:53 -0500 Subject: [PATCH 057/364] Initial commit of QGen Controlled by fixedpoint.spacer.use_quanti_generalizer measure cumulative time, number of invocations, and number of failed SMT calls Relaxing equality in a pattern: if a variable equals a numeral, relax with GE pob::get_skolems() returns all skolems that might appear in the pob. New skolems must be added above the largest index in that map, even if they are not used in the pob itself. pattern generalization should be done before the pattern is skolemized and added into the new cube. --- src/muz/base/fixedpoint_params.pyg | 3 +- src/muz/spacer/spacer_context.cpp | 7 +- src/muz/spacer/spacer_context.h | 4 +- src/muz/spacer/spacer_generalizers.cpp | 2 + src/muz/spacer/spacer_generalizers.h | 48 ++ src/muz/spacer/spacer_manager.cpp | 21 +- src/muz/spacer/spacer_manager.h | 6 +- src/muz/spacer/spacer_quant_generalizer.cpp | 499 ++++++++++++++++++++ src/muz/spacer/spacer_util.cpp | 4 +- src/muz/spacer/spacer_util.h | 5 +- 10 files changed, 583 insertions(+), 16 deletions(-) create mode 100644 src/muz/spacer/spacer_quant_generalizer.cpp diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index f0f4e0881..e91eb86fe 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -187,7 +187,8 @@ def_module_params('fixedpoint', ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), ('spacer.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes'), - ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints') + ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints'), + ('spacer.use_quant_generalizer', BOOL, False, 'use quantified lemma generalizer'), )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 1151909f1..607bf2018 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1894,7 +1894,6 @@ void pob::set_post(expr* post, app_ref_vector const &b) { expr_safe_replace sub(m); for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { expr* e; - e = m_binding.get(i); pinned.push_back (mk_zk_const (m, i, get_sort(e))); sub.insert (e, pinned.back()); @@ -1939,7 +1938,6 @@ void pob::close () { void pob::get_skolems(app_ref_vector &v) { for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { expr* e; - e = m_binding.get(i); v.push_back (mk_zk_const (get_ast_manager(), i, get_sort(e))); } @@ -2273,6 +2271,11 @@ void context::init_lemma_generalizers(datalog::rule_set& rules) fparams.m_ng_lift_ite = LI_FULL; } + if (m_params.spacer_use_quant_generalizer()) { + m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, false)); + m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this)); + } + if (get_params().spacer_use_eqclass()) { m_lemma_generalizers.push_back (alloc(lemma_eq_generalizer, *this)); } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 494da06a7..e909cd9ad 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -527,7 +527,9 @@ public: unsigned get_free_vars_size() { return m_binding.size(); } app_ref_vector const &get_binding() const {return m_binding;} /* - * Return skolem variables that appear in post + * Returns a map from variable id to skolems that implicitly + * represent them in the pob. Note that only some (or none) of the + * skolems returned actually appear in the post of the pob. */ void get_skolems(app_ref_vector& v); diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index c87bdc892..342e43ad3 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -326,4 +326,6 @@ void lemma_eq_generalizer::operator() (lemma_ref &lemma) lemma->update_cube(lemma->get_pob(), core); } } + + }; diff --git a/src/muz/spacer/spacer_generalizers.h b/src/muz/spacer/spacer_generalizers.h index 17298495c..46ad0c4a3 100644 --- a/src/muz/spacer/spacer_generalizers.h +++ b/src/muz/spacer/spacer_generalizers.h @@ -97,6 +97,54 @@ public: void operator()(lemma_ref &lemma) override; }; +class lemma_quantifier_generalizer : public lemma_generalizer { + struct stats { + unsigned count; + unsigned num_failures; + stopwatch watch; + stats() {reset();} + void reset() {count = 0; num_failures = 0; watch.reset();} + }; + ast_manager &m; + arith_util m_arith; + stats m_st; +public: + lemma_quantifier_generalizer(context &ctx); + virtual ~lemma_quantifier_generalizer() {} + virtual void operator()(lemma_ref &lemma); + + virtual void collect_statistics(statistics& st) const; + virtual void reset_statistics() {m_st.reset();} +private: + bool match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk); + void cleanup(expr_ref_vector& cube, app_ref_vector const &zks, expr_ref &bind); + void generalize_pattern_lits(expr_ref_vector &pats); + void find_candidates( + expr *e, + app_ref_vector &candidate); + void generate_patterns( + expr_ref_vector const &cube, + app_ref_vector const &candidates, + var_ref_vector &subs, + expr_ref_vector &patterns, + unsigned offset); + void find_matching_expressions( + expr_ref_vector const &cube, + var_ref_vector const &subs, + expr_ref_vector &patterns, + vector &idx_instances, + vector &dirty); + void find_guards( + expr_ref_vector const &indices, + expr_ref &lower, + expr_ref &upper); + void add_lower_bounds( + var_ref_vector const &subs, + app_ref_vector const &zks, + vector const &idx_instances, + expr_ref_vector &cube); }; +} + #endif diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index d583813a8..c46c31924 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -340,22 +340,27 @@ app* mk_zk_const(ast_manager &m, unsigned idx, sort *s) { namespace find_zk_const_ns { struct proc { + int m_max; app_ref_vector &m_out; - proc (app_ref_vector &out) : m_out(out) {} + proc (app_ref_vector &out) : m_max(-1), m_out(out) {} void operator() (var const * n) const {} - void operator() (app *n) const { - if (is_uninterp_const(n) && - n->get_decl()->get_name().str().compare (0, 3, "sk!") == 0) { - m_out.push_back (n); + void operator() (app *n) { + int idx; + if (is_zk_const(n, idx)) { + m_out.push_back(n); + if (idx > m_max) { + m_max = idx; + } } } void operator() (quantifier const *n) const {} }; } -void find_zk_const(expr *e, app_ref_vector &res) { +int find_zk_const(expr *e, app_ref_vector &res) { find_zk_const_ns::proc p(res); for_each_expr (p, e); + return p.m_max; } namespace has_zk_const_ns { @@ -363,8 +368,8 @@ struct found {}; struct proc { void operator() (var const *n) const {} void operator() (app const *n) const { - if (is_uninterp_const(n) && - n->get_decl()->get_name().str().compare(0, 3, "sk!") == 0) { + int idx; + if (is_zk_const(n, idx)) { throw found(); } } diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index f49aa63fc..32cf61d57 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -339,8 +339,12 @@ public: }; app* mk_zk_const (ast_manager &m, unsigned idx, sort *s); -void find_zk_const(expr* e, app_ref_vector &out); +int find_zk_const(expr* e, app_ref_vector &out); +inline int find_zk_const(expr_ref_vector const &v, app_ref_vector &out) { + return find_zk_const (mk_and(v), out); +} bool has_zk_const(expr* e); +bool is_zk_const (const app *a, int &n); struct sk_lt_proc { bool operator()(const app* a1, const app* a2); diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp new file mode 100644 index 000000000..8be97122c --- /dev/null +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -0,0 +1,499 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel + +Module Name: + + spacer_quant_generalizer.cpp + +Abstract: + + Quantified lemma generalizer. + +Author: + + + Yakir Vizel + Arie Gurfinkel + +Revision History: + +--*/ + + +#include "muz/spacer/spacer_context.h" +#include "muz/spacer/spacer_generalizers.h" +#include "muz/spacer/spacer_manager.h" +#include "ast/expr_abstract.h" +#include "ast/rewriter/var_subst.h" +#include "ast/for_each_expr.h" +#include "ast/factor_equivs.h" +#include "muz/spacer/spacer_term_graph.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/substitution/matcher.h" +#include "ast/expr_functors.h" + +#include "muz/spacer/spacer_sem_matcher.h" + +using namespace spacer; + +namespace { +struct index_lt_proc : public std::binary_function { + arith_util m_arith; + index_lt_proc(ast_manager &m) : m_arith(m) {} + bool operator() (app *a, app *b) { + // XXX This order is a bit strange. + // XXX It does the job in our current application, but only because + // XXX we assume that we only compare expressions of the form (B + k), + // XXX where B is fixed and k is a number. + // XXX Might be better to specialize just for that specific use case. + rational val1, val2; + bool is_num1 = m_arith.is_numeral(a, val1); + bool is_num2 = m_arith.is_numeral(b, val2); + + if (is_num1 && is_num2) { + return val1 < val2; + } + else if (is_num1 != is_num2) { + return is_num1; + } + + is_num1 = false; + is_num2 = false; + // compare the first numeric argument of a to first numeric argument of b + // if available + for (unsigned i = 0, sz = a->get_num_args(); !is_num1 && i < sz; ++i) { + is_num1 = m_arith.is_numeral (a->get_arg(i), val1); + } + for (unsigned i = 0, sz = b->get_num_args(); !is_num2 && i < sz; ++i) { + is_num2 = m_arith.is_numeral(b->get_arg(i), val2); + } + + if (is_num1 && is_num2) { + return val1 < val2; + } + else if (is_num1 != is_num2) { + return is_num1; + } + else { + return a->get_id() < b->get_id(); + } + + } +}; + +} + +namespace spacer { + +lemma_quantifier_generalizer::lemma_quantifier_generalizer(context &ctx) : + lemma_generalizer(ctx), m(ctx.get_ast_manager()), m_arith(m) {} + +void lemma_quantifier_generalizer::collect_statistics(statistics &st) const +{ + st.update("time.spacer.solve.reach.gen.quant", m_st.watch.get_seconds()); + st.update("quantifier gen", m_st.count); + st.update("quantifier gen failures", m_st.num_failures); +} + +// XXX What does this do? +void lemma_quantifier_generalizer::find_candidates( + expr *e, app_ref_vector &candidates) +{ + if (!contains_selects(e, m)) return; + + app_ref_vector indices(m); + get_select_indices(e, indices, m); + + app_ref_vector extra(m); + expr_sparse_mark marked_args; + + // Make sure not to try and quantify already-quantified indices + for (unsigned idx=0, sz = indices.size(); idx < sz; idx++) { + // skip expressions that already contain a quantified variable + if (has_zk_const(indices.get(idx))) { + continue; + } + + app *index = indices.get(idx); + TRACE ("spacer_qgen", + tout << "Candidate: "<< mk_pp(index, m) + << " in " << mk_pp(e, m) << "\n";); + extra.push_back(index); + // XXX expand one level of arguments. Might want to go deeper. + for (unsigned j = 0, asz = index->get_num_args(); j < asz; j++) { + expr *arg = index->get_arg(j); + if (!is_app(arg) || marked_args.is_marked(arg)) {continue;} + marked_args.mark(arg); + candidates.push_back (to_app(arg)); + } + } + + std::sort(candidates.c_ptr(), candidates.c_ptr() + candidates.size(), + index_lt_proc(m)); + // keep actual select indecies in the order found at the back of + // candidate list + + // XXX this corresponds to current implementation. Probably should + // XXX be sorted together with the rest of candidates + candidates.append(extra); +} + +/* subs are the variables that might appear in the patterns */ +void lemma_quantifier_generalizer::generate_patterns( + expr_ref_vector const &cube, app_ref_vector const &candidates, + var_ref_vector &subs, expr_ref_vector &patterns, unsigned offset) +{ + if (candidates.empty()) return; + + expr_safe_replace ses(m); + + // Setup substitution + for (unsigned i=0; i < candidates.size(); i++) { + expr *cand = candidates.get(i); + var *var = m.mk_var(i+offset, get_sort(cand)); + ses.insert(cand, var); + rational val; + if (m_arith.is_numeral(cand, val)) { + bool is_int = val.is_int(); + ses.insert( + m_arith.mk_numeral(rational(val+1), is_int), + m_arith.mk_add(var, m_arith.mk_numeral(rational(1), is_int))); + ses.insert( + m_arith.mk_numeral(rational(-1*(val+1)), is_int), + m_arith.mk_add(m_arith.mk_sub(m_arith.mk_numeral(rational(0), is_int),var), m_arith.mk_numeral(rational(-1), is_int))); + } + subs.push_back(var); + } + + // Generate patterns + + // for every literal + for (unsigned j=0; j < cube.size(); j++) { + // abstract terms by variables + expr_ref pat(m); + ses(cube.get(j), pat); + + if (pat.get() != cube.get(j)) { + // if abstraction is not identity + TRACE("spacer_qgen", + tout << "Pattern is: " << mk_pp(pat, m) << "\n";); + + // store the pattern + patterns.push_back(pat); + } + } +} + +void lemma_quantifier_generalizer::find_matching_expressions( + expr_ref_vector const &cube, + var_ref_vector const &subs, expr_ref_vector &patterns, + vector &idx_instances, + vector &dirty) +{ + idx_instances.resize(subs.size(), expr_ref_vector(m)); + // -- for every pattern + for (unsigned p = 0; p < patterns.size(); p++) { + expr *pattern = patterns.get(p); + // -- for every literal + for (unsigned j = 0; j < cube.size(); j++) { + if (dirty[j] || !is_app(cube.get(j))) continue; + app *lit = to_app(cube.get(j)); + + sem_matcher match(m); + bool pos; + substitution v_sub(m); + v_sub.reserve(2, subs.size()+1); + + if (!match(pattern, lit, v_sub, pos)) { + continue; + } + // expect positive matches only + if (!pos) {continue;} + + dirty[j] = true; + for (unsigned v = 0; v < subs.size(); v++) { + expr_offset idx; + if (v_sub.find(subs.get(v), 0, idx)) { + TRACE ("spacer_qgen", tout << "INSTANCE IS: " << mk_pp(idx.get_expr(), m) << "\n";); + idx_instances[v].push_back(idx.get_expr()); + } + } + } + } +} + + +void lemma_quantifier_generalizer::find_guards( + expr_ref_vector const &indices, + expr_ref &lower, expr_ref &upper) +{ + if (indices.empty()) return; + + auto minmax = std::minmax_element((app * *) indices.c_ptr(), + (app * *) indices.c_ptr() + indices.size(), + index_lt_proc(m)); + lower = *minmax.first; + upper = *minmax.second; +} + +void lemma_quantifier_generalizer::add_lower_bounds( + var_ref_vector const &subs, + app_ref_vector const &zks, + vector const &idx_instances, + expr_ref_vector &cube) +{ + for (unsigned v = 0; v < subs.size(); v++) { + var *var = subs.get(v); + if (idx_instances[v].empty()) continue; + TRACE("spacer_qgen", + tout << "Finding lower bounds for " << mk_pp(var, m) << "\n";); + expr_ref low(m); + expr_ref up(m); + find_guards(idx_instances[v], low, up); + cube.push_back(m_arith.mk_ge(zks.get(var->get_idx()), low)); + } +} + +/// returns true if expression e contains a sub-expression of the form (select A idx) where +/// idx contains exactly one skolem from zks. Returns idx and the skolem +bool lemma_quantifier_generalizer::match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk) { + if (zks.size() != 1) return false; + contains_app has_zk(m, zks.get(0)); + + if (!contains_selects(e, m)) return false; + app_ref_vector indicies(m); + get_select_indices(e, indicies, m); + if (indicies.size() > 2) return false; + + unsigned i=0; + if (indicies.size() == 1) { + if (!has_zk(indicies.get(0))) return false; + } + else { + if (has_zk(indicies.get(0)) && !has_zk(indicies.get(1))) + i = 0; + else if (!has_zk(indicies.get(0)) && has_zk(indicies.get(1))) + i = 1; + else if (!has_zk(indicies.get(0)) && !has_zk(indicies.get(1))) + return false; + } + + idx = indicies.get(i); + sk = zks.get(0); + return true; +} + +expr* times_minus_one(expr *e, arith_util &arith) { + expr *r; + if (arith.is_times_minus_one (e, r)) { return r; } + + return arith.mk_mul(arith.mk_numeral(rational(-1), arith.is_int(get_sort(e))), e); +} + +/** Attempts to rewrite a cube so that quantified variable appears as + a top level argument of select-term + + Find sub-expression of the form (select A (+ sk!0 t)) and replaces + (+ sk!0 t) --> sk!0 and sk!0 --> (+ sk!0 (* (- 1) t)) + + Current implementation is an ugly hack for one special case +*/ +void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector const &zks, expr_ref &bind) { + if (zks.size() != 1) return; + + arith_util arith(m); + expr *idx = nullptr; + app *sk = nullptr; + expr_ref rep(m); + + for (expr *e : cube) { + if (match_sk_idx(e, zks, idx, sk)) { + CTRACE("spacer_qgen", idx != sk, + tout << "Possible cleanup of " << mk_pp(idx, m) << " in " + << mk_pp(e, m) << " on " << mk_pp(sk, m) << "\n";); + + if (!arith.is_add(idx)) continue; + app *a = to_app(idx); + bool found = false; + expr_ref_vector kids(m); + expr_ref_vector kids_bind(m); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + if (a->get_arg(i) == sk) { + found = true; + kids.push_back(a->get_arg(i)); + kids_bind.push_back(bind); + } + else { + kids.push_back (times_minus_one(a->get_arg(i), arith)); + kids_bind.push_back (times_minus_one(a->get_arg(i), arith)); + } + } + if (!found) continue; + + rep = arith.mk_add(kids.size(), kids.c_ptr()); + bind = arith.mk_add(kids_bind.size(), kids_bind.c_ptr()); + TRACE("spacer_qgen", + tout << "replace " << mk_pp(idx, m) << " with " << mk_pp(rep, m) << "\n";); + break; + } + } + + if (rep) { + expr_safe_replace rw(m); + rw.insert(sk, rep); + rw.insert(idx, sk); + rw(cube); + TRACE("spacer_qgen", + tout << "Cleaned cube to: " << mk_and(cube) << "\n";); + } +} + +void lemma_quantifier_generalizer::generalize_pattern_lits(expr_ref_vector &pats) { + // generalize pattern literals using 'x=t' --> 'x>=t' + for (unsigned i = 0, sz = pats.size(); i < sz; ++i) { + expr *e1, *e2; + expr *p = pats.get(i); + if (m.is_eq(p, e1, e2) && (is_var(e1) || is_var(e2))) { + if (m_arith.is_numeral(e1)) { + p = m_arith.mk_ge(e2, e1); + } + else if (m_arith.is_numeral(e2)) { + p = m_arith.mk_ge(e1, e2); + } + } + if (p != pats.get(i)) { + pats.set(i, p); + } + } +} +void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { + if (lemma->get_cube().empty()) return; + if (!lemma->has_pob()) return; + + m_st.count++; + scoped_watch _w_(m_st.watch); + + expr_ref_vector cube(m); + cube.append(lemma->get_cube()); + + TRACE("spacer_qgen", + tout << "initial cube: " << mk_and(lemma->get_cube()) << "\n";); + if (false) { + // -- re-normalize the cube + expr_ref c(m); + c = mk_and(cube); + normalize(c, c, true, true); + cube.reset(); + flatten_and(c, cube); + } + + app_ref_vector skolems(m); + lemma->get_pob()->get_skolems(skolems); + int offset = skolems.size(); + + // for every literal + for (unsigned i=0; i < cube.size(); i++) { + expr *r = cube.get(i); + + // generate candidates + app_ref_vector candidates(m); + find_candidates(r, candidates); + + // XXX Can candidates be empty and arguments not empty? + // XXX Why separate candidates and arguments? + // XXX Why are arguments processed before candidates? + if (candidates.empty()) continue; + + + // for every candidate + for (unsigned arg=0, sz = candidates.size(); arg < sz; arg++) { + app_ref_vector cand(m); + cand.push_back(to_app(candidates.get(arg))); + var_ref_vector subs(m); + expr_ref_vector patterns(m); + // generate patterns for a candidate + generate_patterns(cube, cand, subs, patterns, offset); + + // Currently, only support one variable per pattern + SASSERT(subs.size() == cand.size()); + SASSERT(cand.size() == 1); + + // Find matching expressions + vector dirty; + dirty.resize(cube.size(), false); + vector idx_instances; + find_matching_expressions(cube, subs, patterns, idx_instances, dirty); + + expr_ref_vector new_cube(m); + + // move all unmatched expressions to the new cube + for (unsigned c=0; c < cube.size(); c++) { + if (dirty[c] == false) { + new_cube.push_back(cube.get(c)); + } + } + + // everything moved, nothing left + if (new_cube.size() == cube.size()) continue; + + // -- generalize equality in patterns + generalize_pattern_lits(patterns); + + // ground quantified patterns in skolems + expr_ref gnd(m); + app_ref_vector zks(m); + ground_expr(mk_and(patterns), gnd, zks); + flatten_and(gnd, new_cube); + + // compute lower bounds for quantified variables + add_lower_bounds(subs, zks, idx_instances, new_cube); + + TRACE("spacer_qgen", + tout << "New CUBE is: " << mk_pp(mk_and(new_cube),m) << "\n";); + + // check if the result is a lemma + unsigned uses_level = 0; + unsigned weakness = lemma->weakness(); + pred_transformer &pt = lemma->get_pob()->pt(); + if (pt.check_inductive(lemma->level(), new_cube, uses_level, weakness)) { + TRACE("spacer", + tout << "Quantifier Generalization Succeeded!\n" + << "New CUBE is: " << mk_pp(mk_and(new_cube),m) << "\n";); + SASSERT(zks.size() >= offset); + SASSERT(cand.size() == 1); + + // lift quantified variables to top of select + expr_ref bind(m); + bind = cand.get(0); + cleanup(new_cube, zks, bind); + + // XXX better do that check before changing bind in cleanup() + // XXX Or not because substitution might introduce _n variable into bind + if (m_ctx.get_manager().is_n_formula(bind)) + // XXX this creates an instance, but not necessarily the needed one + + // XXX This is sound because any instance of + // XXX universal quantifier is sound + + // XXX needs better long term solution. leave + // comment here for the future + m_ctx.get_manager().formula_n2o(bind, bind, 0); + + lemma->update_cube(lemma->get_pob(), new_cube); + lemma->set_level(uses_level); + + // XXX Assumes that offset + 1 == zks.size(); + for (unsigned sk=offset; sk < zks.size(); sk++) + lemma->add_skolem(zks.get(sk), to_app(bind)); + return; + } + else { + ++m_st.num_failures; + } + } + } +} + + + +} diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 9a0148784..cb11afc9f 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -1317,9 +1317,9 @@ bool contains_selects(expr* fml, ast_manager& m) return false; } -void get_select_indices(expr* fml, app_ref_vector& indices, ast_manager& m) +void get_select_indices(expr* fml, app_ref_vector &indices, ast_manager& m) { - array_util a_util(m); + array_util a_util(m); if (!is_app(fml)) { return; } ast_mark done; ptr_vector todo; diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index 7be2ee4b3..dabfa494a 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -144,7 +144,10 @@ void compute_implicant_literals (model_evaluator_util &mev, void simplify_bounds (expr_ref_vector &lemmas); void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false); -/** ground expression by replacing all free variables by skolem constants */ +/** Ground expression by replacing all free variables by skolem + ** constants. On return, out is the resulting expression, and vars is + ** a map from variable ids to corresponding skolem constants. + */ void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); From 9cdb63ae4ab6a8c869060bd1b54ac99d3cf5c2e8 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 29 Dec 2017 12:03:48 -0500 Subject: [PATCH 058/364] Handle conversion of quantified lemma to quantifier free When a cube is updated, a lemma might loose all of its quantified variables. In this case, it is effectively quantifier free and might be a version of an already existing lemma. For that reason, we convert it to quantifier free lemma when this happens. --- src/muz/spacer/spacer_context.cpp | 14 +++++++++++++- src/muz/spacer/spacer_quant_generalizer.cpp | 17 ++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 607bf2018..90103503b 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1300,6 +1300,18 @@ void lemma::update_cube (pob_ref const &p, expr_ref_vector &cube) { m_body.reset(); m_cube.append(cube); if (m_cube.empty()) {m_cube.push_back(m.mk_true());} + + // after the cube is updated, if there are no skolems, + // convert the lemma to quantifier-free + bool is_quant = false; + for (unsigned i = 0, sz = cube.size(); !is_quant && i < sz; ++i) { + is_quant = has_zk_const(cube.get(i)); + } + + if (!is_quant) { + m_zks.reset(); + m_bindings.reset(); + } } bool lemma::has_binding(app_ref_vector const &binding) { @@ -2272,7 +2284,7 @@ void context::init_lemma_generalizers(datalog::rule_set& rules) } if (m_params.spacer_use_quant_generalizer()) { - m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, false)); + m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, true)); m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this)); } diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index 8be97122c..942fbfc6a 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -202,7 +202,11 @@ void lemma_quantifier_generalizer::find_matching_expressions( sem_matcher match(m); bool pos; substitution v_sub(m); - v_sub.reserve(2, subs.size()+1); + + // allocate space for the largest variable in the pattern + unsigned max_idx = 0; + for (var* v : subs) {max_idx = std::max(max_idx, v->get_idx());} + v_sub.reserve(2, max_idx + 1); if (!match(pattern, lit, v_sub, pos)) { continue; @@ -378,13 +382,15 @@ void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { TRACE("spacer_qgen", tout << "initial cube: " << mk_and(lemma->get_cube()) << "\n";); - if (false) { + if (true) { // -- re-normalize the cube expr_ref c(m); c = mk_and(cube); normalize(c, c, true, true); cube.reset(); flatten_and(c, cube); + TRACE("spacer_qgen", + tout << "normalized cube:\n" << mk_and(cube) << "\n";); } app_ref_vector skolems(m); @@ -398,10 +404,6 @@ void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { // generate candidates app_ref_vector candidates(m); find_candidates(r, candidates); - - // XXX Can candidates be empty and arguments not empty? - // XXX Why separate candidates and arguments? - // XXX Why are arguments processed before candidates? if (candidates.empty()) continue; @@ -456,7 +458,7 @@ void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { unsigned weakness = lemma->weakness(); pred_transformer &pt = lemma->get_pob()->pt(); if (pt.check_inductive(lemma->level(), new_cube, uses_level, weakness)) { - TRACE("spacer", + TRACE("spacer_qgen", tout << "Quantifier Generalization Succeeded!\n" << "New CUBE is: " << mk_pp(mk_and(new_cube),m) << "\n";); SASSERT(zks.size() >= offset); @@ -482,6 +484,7 @@ void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { lemma->update_cube(lemma->get_pob(), new_cube); lemma->set_level(uses_level); + SASSERT(offset + 1 == zks.size()); // XXX Assumes that offset + 1 == zks.size(); for (unsigned sk=offset; sk < zks.size(); sk++) lemma->add_skolem(zks.get(sk), to_app(bind)); From 852e181fed8f5c7c5547f48af13d951e12f9340e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 4 Jan 2018 13:43:45 -0500 Subject: [PATCH 059/364] New quic3 lemma generalizer --- src/muz/spacer/spacer_generalizers.h | 41 +- src/muz/spacer/spacer_quant_generalizer.cpp | 532 +++++++++++--------- 2 files changed, 321 insertions(+), 252 deletions(-) diff --git a/src/muz/spacer/spacer_generalizers.h b/src/muz/spacer/spacer_generalizers.h index 46ad0c4a3..bc549a949 100644 --- a/src/muz/spacer/spacer_generalizers.h +++ b/src/muz/spacer/spacer_generalizers.h @@ -109,41 +109,30 @@ class lemma_quantifier_generalizer : public lemma_generalizer { ast_manager &m; arith_util m_arith; stats m_st; + expr_ref_vector m_cube; + + bool m_normalize_cube; + int m_offset; public: - lemma_quantifier_generalizer(context &ctx); + lemma_quantifier_generalizer(context &ctx, bool normalize_cube = true); virtual ~lemma_quantifier_generalizer() {} virtual void operator()(lemma_ref &lemma); virtual void collect_statistics(statistics& st) const; virtual void reset_statistics() {m_st.reset();} private: + bool generalize(lemma_ref &lemma, app *term); + + void find_candidates(expr *e, app_ref_vector &candidate); + bool is_ub(var *var, expr *e); + bool is_lb(var *var, expr *e); + void mk_abs_cube (app *term, var *var, + expr_ref_vector &gnd_cube, + expr_ref_vector &abs_cube, + expr *&lb, expr *&ub); + bool match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk); void cleanup(expr_ref_vector& cube, app_ref_vector const &zks, expr_ref &bind); - void generalize_pattern_lits(expr_ref_vector &pats); - void find_candidates( - expr *e, - app_ref_vector &candidate); - void generate_patterns( - expr_ref_vector const &cube, - app_ref_vector const &candidates, - var_ref_vector &subs, - expr_ref_vector &patterns, - unsigned offset); - void find_matching_expressions( - expr_ref_vector const &cube, - var_ref_vector const &subs, - expr_ref_vector &patterns, - vector &idx_instances, - vector &dirty); - void find_guards( - expr_ref_vector const &indices, - expr_ref &lower, - expr_ref &upper); - void add_lower_bounds( - var_ref_vector const &subs, - app_ref_vector const &zks, - vector const &idx_instances, - expr_ref_vector &cube); }; } diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index 942fbfc6a..bbd086749 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -85,8 +85,10 @@ struct index_lt_proc : public std::binary_function { namespace spacer { -lemma_quantifier_generalizer::lemma_quantifier_generalizer(context &ctx) : - lemma_generalizer(ctx), m(ctx.get_ast_manager()), m_arith(m) {} +lemma_quantifier_generalizer::lemma_quantifier_generalizer(context &ctx, + bool normalize_cube) : + lemma_generalizer(ctx), m(ctx.get_ast_manager()), m_arith(m), m_cube(m), + m_normalize_cube(normalize_cube), m_offset(0) {} void lemma_quantifier_generalizer::collect_statistics(statistics &st) const { @@ -95,10 +97,21 @@ void lemma_quantifier_generalizer::collect_statistics(statistics &st) const st.update("quantifier gen failures", m_st.num_failures); } -// XXX What does this do? -void lemma_quantifier_generalizer::find_candidates( - expr *e, app_ref_vector &candidates) -{ +/** + Finds candidates terms to be existentially abstracted. + A term t is a candidate if + (a) t is ground + + (b) t appears in an expression of the form (select A t) for some array A + + (c) t appears in an expression of the form (select A (+ t v)) + where v is ground + + The goal is to pick candidates that might result in a lemma in the + essentially uninterpreted fragment of FOL or its extensions. + */ +void lemma_quantifier_generalizer::find_candidates(expr *e, + app_ref_vector &candidates) { if (!contains_selects(e, m)) return; app_ref_vector indices(m); @@ -115,11 +128,10 @@ void lemma_quantifier_generalizer::find_candidates( } app *index = indices.get(idx); - TRACE ("spacer_qgen", - tout << "Candidate: "<< mk_pp(index, m) + TRACE ("spacer_qgen", tout << "Candidate: "<< mk_pp(index, m) << " in " << mk_pp(e, m) << "\n";); extra.push_back(index); - // XXX expand one level of arguments. Might want to go deeper. + if (m_arith.is_add(index)) { for (unsigned j = 0, asz = index->get_num_args(); j < asz; j++) { expr *arg = index->get_arg(j); if (!is_app(arg) || marked_args.is_marked(arg)) {continue;} @@ -127,136 +139,15 @@ void lemma_quantifier_generalizer::find_candidates( candidates.push_back (to_app(arg)); } } + } std::sort(candidates.c_ptr(), candidates.c_ptr() + candidates.size(), index_lt_proc(m)); // keep actual select indecies in the order found at the back of - // candidate list - - // XXX this corresponds to current implementation. Probably should - // XXX be sorted together with the rest of candidates + // candidate list. There is no particular reason for this order candidates.append(extra); } -/* subs are the variables that might appear in the patterns */ -void lemma_quantifier_generalizer::generate_patterns( - expr_ref_vector const &cube, app_ref_vector const &candidates, - var_ref_vector &subs, expr_ref_vector &patterns, unsigned offset) -{ - if (candidates.empty()) return; - - expr_safe_replace ses(m); - - // Setup substitution - for (unsigned i=0; i < candidates.size(); i++) { - expr *cand = candidates.get(i); - var *var = m.mk_var(i+offset, get_sort(cand)); - ses.insert(cand, var); - rational val; - if (m_arith.is_numeral(cand, val)) { - bool is_int = val.is_int(); - ses.insert( - m_arith.mk_numeral(rational(val+1), is_int), - m_arith.mk_add(var, m_arith.mk_numeral(rational(1), is_int))); - ses.insert( - m_arith.mk_numeral(rational(-1*(val+1)), is_int), - m_arith.mk_add(m_arith.mk_sub(m_arith.mk_numeral(rational(0), is_int),var), m_arith.mk_numeral(rational(-1), is_int))); - } - subs.push_back(var); - } - - // Generate patterns - - // for every literal - for (unsigned j=0; j < cube.size(); j++) { - // abstract terms by variables - expr_ref pat(m); - ses(cube.get(j), pat); - - if (pat.get() != cube.get(j)) { - // if abstraction is not identity - TRACE("spacer_qgen", - tout << "Pattern is: " << mk_pp(pat, m) << "\n";); - - // store the pattern - patterns.push_back(pat); - } - } -} - -void lemma_quantifier_generalizer::find_matching_expressions( - expr_ref_vector const &cube, - var_ref_vector const &subs, expr_ref_vector &patterns, - vector &idx_instances, - vector &dirty) -{ - idx_instances.resize(subs.size(), expr_ref_vector(m)); - // -- for every pattern - for (unsigned p = 0; p < patterns.size(); p++) { - expr *pattern = patterns.get(p); - // -- for every literal - for (unsigned j = 0; j < cube.size(); j++) { - if (dirty[j] || !is_app(cube.get(j))) continue; - app *lit = to_app(cube.get(j)); - - sem_matcher match(m); - bool pos; - substitution v_sub(m); - - // allocate space for the largest variable in the pattern - unsigned max_idx = 0; - for (var* v : subs) {max_idx = std::max(max_idx, v->get_idx());} - v_sub.reserve(2, max_idx + 1); - - if (!match(pattern, lit, v_sub, pos)) { - continue; - } - // expect positive matches only - if (!pos) {continue;} - - dirty[j] = true; - for (unsigned v = 0; v < subs.size(); v++) { - expr_offset idx; - if (v_sub.find(subs.get(v), 0, idx)) { - TRACE ("spacer_qgen", tout << "INSTANCE IS: " << mk_pp(idx.get_expr(), m) << "\n";); - idx_instances[v].push_back(idx.get_expr()); - } - } - } - } -} - - -void lemma_quantifier_generalizer::find_guards( - expr_ref_vector const &indices, - expr_ref &lower, expr_ref &upper) -{ - if (indices.empty()) return; - - auto minmax = std::minmax_element((app * *) indices.c_ptr(), - (app * *) indices.c_ptr() + indices.size(), - index_lt_proc(m)); - lower = *minmax.first; - upper = *minmax.second; -} - -void lemma_quantifier_generalizer::add_lower_bounds( - var_ref_vector const &subs, - app_ref_vector const &zks, - vector const &idx_instances, - expr_ref_vector &cube) -{ - for (unsigned v = 0; v < subs.size(); v++) { - var *var = subs.get(v); - if (idx_instances[v].empty()) continue; - TRACE("spacer_qgen", - tout << "Finding lower bounds for " << mk_pp(var, m) << "\n";); - expr_ref low(m); - expr_ref up(m); - find_guards(idx_instances[v], low, up); - cube.push_back(m_arith.mk_ge(zks.get(var->get_idx()), low)); - } -} /// returns true if expression e contains a sub-expression of the form (select A idx) where /// idx contains exactly one skolem from zks. Returns idx and the skolem @@ -287,12 +178,14 @@ bool lemma_quantifier_generalizer::match_sk_idx(expr *e, app_ref_vector const &z return true; } +namespace { expr* times_minus_one(expr *e, arith_util &arith) { expr *r; if (arith.is_times_minus_one (e, r)) { return r; } return arith.mk_mul(arith.mk_numeral(rational(-1), arith.is_int(get_sort(e))), e); } +} /** Attempts to rewrite a cube so that quantified variable appears as a top level argument of select-term @@ -352,126 +245,267 @@ void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector } } -void lemma_quantifier_generalizer::generalize_pattern_lits(expr_ref_vector &pats) { - // generalize pattern literals using 'x=t' --> 'x>=t' - for (unsigned i = 0, sz = pats.size(); i < sz; ++i) { - expr *e1, *e2; - expr *p = pats.get(i); - if (m.is_eq(p, e1, e2) && (is_var(e1) || is_var(e2))) { - if (m_arith.is_numeral(e1)) { - p = m_arith.mk_ge(e2, e1); - } - else if (m_arith.is_numeral(e2)) { - p = m_arith.mk_ge(e1, e2); - } +/** + Create an abstract cube by abstracting a given term with a given variable. + On return, + gnd_cube contains all ground literals from m_cube + abs_cube contains all newly quantified literals from m_cube + lb contains an expression determining the lower bound on the variable + ub contains an expression determining the upper bound on the variable + + Conjunction of gnd_cube and abs_cube is the new quantified cube + + lb and ub are null if no bound was found + */ +void lemma_quantifier_generalizer::mk_abs_cube(app *term, var *var, + expr_ref_vector &gnd_cube, + expr_ref_vector &abs_cube, + expr *&lb, expr *&ub) { + + // create an abstraction function that maps candidate term to variables + expr_safe_replace sub(m); + // term -> var + sub.insert(term , var); + rational val; + if (m_arith.is_numeral(term, val)) { + bool is_int = val.is_int(); + expr_ref minus_one(m); + minus_one = m_arith.mk_numeral(rational(-1), is_int); + + // term+1 -> var+1 if term is a number + sub.insert( + m_arith.mk_numeral(val + 1, is_int), + m_arith.mk_add(var, m_arith.mk_numeral(rational(1), is_int))); + // -term-1 -> -1*var + -1 if term is a number + sub.insert( + m_arith.mk_numeral(-1*val + -1, is_int), + m_arith.mk_add (m_arith.mk_mul (minus_one, var), minus_one)); + } + + lb = nullptr; + ub = nullptr; + + for (expr *lit : m_cube) { + expr_ref abs_lit(m); + sub (lit, abs_lit); + if (lit == abs_lit) { + gnd_cube.push_back(lit); } - if (p != pats.get(i)) { - pats.set(i, p); + else { + expr *e1, *e2; + // generalize v=num into v>=num + if (m.is_eq(abs_lit, e1, e2) && (e1 == var || e2 == var)) { + if (m_arith.is_numeral(e1)) { + abs_lit = m_arith.mk_ge (var, e1); + } else if (m_arith.is_numeral(e2)) { + abs_lit = m_arith.mk_ge(var, e2); + } + } + abs_cube.push_back(abs_lit); + if (!lb && is_lb(var, abs_lit)) { + lb = abs_lit; + } + else if (!ub && is_ub(var, abs_lit)) { + ub = abs_lit; } } } -void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { - if (lemma->get_cube().empty()) return; - if (!lemma->has_pob()) return; +} - m_st.count++; - scoped_watch _w_(m_st.watch); - - expr_ref_vector cube(m); - cube.append(lemma->get_cube()); - - TRACE("spacer_qgen", - tout << "initial cube: " << mk_and(lemma->get_cube()) << "\n";); - if (true) { - // -- re-normalize the cube - expr_ref c(m); - c = mk_and(cube); - normalize(c, c, true, true); - cube.reset(); - flatten_and(c, cube); - TRACE("spacer_qgen", - tout << "normalized cube:\n" << mk_and(cube) << "\n";); +// -- returns true if e is an upper bound for var +bool lemma_quantifier_generalizer::is_ub(var *var, expr *e) { + expr *e1, *e2; + // var <= e2 + if ((m_arith.is_le (e, e1, e2) || m_arith.is_lt(e, e1, e2)) && var == e1) { + return true; + } + // e1 >= var + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && var == e2) { + return true; } - app_ref_vector skolems(m); - lemma->get_pob()->get_skolems(skolems); - int offset = skolems.size(); + // t <= -1*var + if ((m_arith.is_le (e, e1, e2) || m_arith.is_lt(e, e1, e2)) + && m_arith.is_times_minus_one(e2, e2) && e2 == var) { + return true; + } + // -1*var >= t + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_times_minus_one(e1, e1) && e1 == var) { + return true; + } + // ! (var >= e2) + if (m.is_not (e, e1) && is_lb(var, e1)) { + return true; + } + // var + t1 <= t2 + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_add(e1)) { + app *a = to_app(e1); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + expr *arg = a->get_arg(i); + if (arg == var) {return true;} + } + } + // t1 <= t2 + -1*var + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_add(e2)) { + app *a = to_app(e2); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + expr *arg = a->get_arg(i); + if (m_arith.is_times_minus_one(arg, arg) && arg == var) + {return true;} + } + } + // t1 >= t2 + var + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_add(e2)) { + app *a = to_app(e2); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + expr *arg = a->get_arg(i); + if (arg == var) {return true;} + } + } + // -1*var + t1 >= t2 + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_add(e1)) { + app *a = to_app(e1); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + expr *arg = a->get_arg(i); + if (m_arith.is_times_minus_one(arg, arg) && arg == var) + {return true;} + } + } + return false; +} - // for every literal - for (unsigned i=0; i < cube.size(); i++) { - expr *r = cube.get(i); +// -- returns true if e is a lower bound for var +bool lemma_quantifier_generalizer::is_lb(var *var, expr *e) { + expr *e1, *e2; + // var >= e2 + if ((m_arith.is_ge (e, e1, e2) || m_arith.is_gt(e, e1, e2)) && var == e1) { + return true; + } + // e1 <= var + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && var == e2) { + return true; + } + // t >= -1*var + if ((m_arith.is_ge (e, e1, e2) || m_arith.is_gt(e, e1, e2)) + && m_arith.is_times_minus_one(e2, e2) && e2 == var) { + return true; + } + // -1*var <= t + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_times_minus_one(e1, e1) && e1 == var) { + return true; + } + // ! (var <= e2) + if (m.is_not (e, e1) && is_ub(var, e1)) { + return true; + } + // var + t1 >= t2 + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_add(e1)) { + app *a = to_app(e1); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + expr *arg = a->get_arg(i); + if (arg == var) {return true;} + } + } + // t1 >= t2 + -1*var + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_add(e2)) { + app *a = to_app(e2); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + expr *arg = a->get_arg(i); + if (m_arith.is_times_minus_one(arg, arg) && arg == var) + {return true;} + } + } + // t1 <= t2 + var + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_add(e2)) { + app *a = to_app(e2); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + expr *arg = a->get_arg(i); + if (arg == var) {return true;} + } + } + // -1*var + t1 <= t2 + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_add(e1)) { + app *a = to_app(e1); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + expr *arg = a->get_arg(i); + if (m_arith.is_times_minus_one(arg, arg) && arg == var) + {return true;} + } + } - // generate candidates - app_ref_vector candidates(m); - find_candidates(r, candidates); - if (candidates.empty()) continue; + return false; +} +bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { - // for every candidate - for (unsigned arg=0, sz = candidates.size(); arg < sz; arg++) { - app_ref_vector cand(m); - cand.push_back(to_app(candidates.get(arg))); - var_ref_vector subs(m); - expr_ref_vector patterns(m); - // generate patterns for a candidate - generate_patterns(cube, cand, subs, patterns, offset); + expr *lb = nullptr, *ub = nullptr; + expr_ref_vector gnd_cube(m); + expr_ref_vector abs_cube(m); - // Currently, only support one variable per pattern - SASSERT(subs.size() == cand.size()); - SASSERT(cand.size() == 1); + var_ref var(m); + var = m.mk_var (m_offset, get_sort(term)); - // Find matching expressions - vector dirty; - dirty.resize(cube.size(), false); - vector idx_instances; - find_matching_expressions(cube, subs, patterns, idx_instances, dirty); + mk_abs_cube(term, var, gnd_cube, abs_cube, lb, ub); + if (abs_cube.empty()) {return false;} - expr_ref_vector new_cube(m); + TRACE("spacer_qgen", + tout << "abs_cube is: " << mk_and(abs_cube) << "\n"; + tout << "lb = "; + if (lb) tout << mk_pp(lb, m); else tout << "none"; + tout << "\n"; - // move all unmatched expressions to the new cube - for (unsigned c=0; c < cube.size(); c++) { - if (dirty[c] == false) { - new_cube.push_back(cube.get(c)); + tout << "ub = "; + if (ub) tout << mk_pp(ub, m); else tout << "none"; + tout << "\n";); + + if (!lb && !ub) {return false;} + + // -- guess lower or upper bound if missing + if (!lb) { + abs_cube.push_back (m_arith.mk_ge (var, term)); + lb = abs_cube.back(); } + if (!ub) { + abs_cube.push_back (m_arith.mk_lt(var, term)); + ub = abs_cube.back(); } - // everything moved, nothing left - if (new_cube.size() == cube.size()) continue; - - // -- generalize equality in patterns - generalize_pattern_lits(patterns); - - // ground quantified patterns in skolems + // skolemize expr_ref gnd(m); app_ref_vector zks(m); - ground_expr(mk_and(patterns), gnd, zks); - flatten_and(gnd, new_cube); - - // compute lower bounds for quantified variables - add_lower_bounds(subs, zks, idx_instances, new_cube); + ground_expr(mk_and(abs_cube), gnd, zks); + flatten_and(gnd, gnd_cube); TRACE("spacer_qgen", - tout << "New CUBE is: " << mk_pp(mk_and(new_cube),m) << "\n";); + tout << "New CUBE is: " << mk_pp(mk_and(gnd_cube),m) << "\n";); - // check if the result is a lemma + // check if the result is a true lemma unsigned uses_level = 0; - unsigned weakness = lemma->weakness(); pred_transformer &pt = lemma->get_pob()->pt(); - if (pt.check_inductive(lemma->level(), new_cube, uses_level, weakness)) { + if (pt.check_inductive(lemma->level(), gnd_cube, uses_level, lemma->weakness())) { TRACE("spacer_qgen", tout << "Quantifier Generalization Succeeded!\n" - << "New CUBE is: " << mk_pp(mk_and(new_cube),m) << "\n";); - SASSERT(zks.size() >= offset); - SASSERT(cand.size() == 1); + << "New CUBE is: " << mk_pp(mk_and(gnd_cube),m) << "\n";); + SASSERT(zks.size() >= m_offset); // lift quantified variables to top of select - expr_ref bind(m); - bind = cand.get(0); - cleanup(new_cube, zks, bind); + expr_ref ext_bind(m); + ext_bind = term; + cleanup(gnd_cube, zks, ext_bind); // XXX better do that check before changing bind in cleanup() // XXX Or not because substitution might introduce _n variable into bind - if (m_ctx.get_manager().is_n_formula(bind)) + if (m_ctx.get_manager().is_n_formula(ext_bind)) { // XXX this creates an instance, but not necessarily the needed one // XXX This is sound because any instance of @@ -479,15 +513,61 @@ void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { // XXX needs better long term solution. leave // comment here for the future - m_ctx.get_manager().formula_n2o(bind, bind, 0); + m_ctx.get_manager().formula_n2o(ext_bind, ext_bind, 0); + } - lemma->update_cube(lemma->get_pob(), new_cube); + lemma->update_cube(lemma->get_pob(), gnd_cube); lemma->set_level(uses_level); - SASSERT(offset + 1 == zks.size()); - // XXX Assumes that offset + 1 == zks.size(); - for (unsigned sk=offset; sk < zks.size(); sk++) - lemma->add_skolem(zks.get(sk), to_app(bind)); + SASSERT(var->get_idx() < zks.size()); + SASSERT(is_app(ext_bind)); + lemma->add_skolem(zks.get(var->get_idx()), to_app(ext_bind)); + return true; + } + + return false; +} + +void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { + if (lemma->get_cube().empty()) return; + if (!lemma->has_pob()) return; + + m_st.count++; + scoped_watch _w_(m_st.watch); + + TRACE("spacer_qgen", + tout << "initial cube: " << mk_and(lemma->get_cube()) << "\n";); + + // setup the cube + m_cube.reset(); + m_cube.append(lemma->get_cube()); + + if (m_normalize_cube) { + // -- re-normalize the cube + expr_ref c(m); + c = mk_and(m_cube); + normalize(c, c, false, true); + m_cube.reset(); + flatten_and(c, m_cube); + TRACE("spacer_qgen", + tout << "normalized cube:\n" << mk_and(m_cube) << "\n";); + } + + // first unused free variable + m_offset = lemma->get_pob()->get_free_vars_size(); + + // for every literal, find a candidate term to abstract + for (unsigned i=0; i < m_cube.size(); i++) { + expr *r = m_cube.get(i); + + // generate candidates for abstraction + app_ref_vector candidates(m); + find_candidates(r, candidates); + if (candidates.empty()) continue; + + // for every candidate + for (unsigned arg=0, sz = candidates.size(); arg < sz; arg++) { + if (generalize (lemma, candidates.get(arg))) { return; } else { From 04a778f2fd8542b14ee1308eb195e353cb533467 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 4 Jan 2018 15:59:44 -0500 Subject: [PATCH 060/364] Option to enable cube normalization in quic generalizer --- src/muz/base/fixedpoint_params.pyg | 1 + src/muz/spacer/spacer_context.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index e91eb86fe..277399b9f 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -189,6 +189,7 @@ def_module_params('fixedpoint', ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes'), ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints'), ('spacer.use_quant_generalizer', BOOL, False, 'use quantified lemma generalizer'), + ('spacer.quic_gen_normalize', BOOL, True, 'normalize cube before quantified generalization'), )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 90103503b..eba51c793 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2284,8 +2284,10 @@ void context::init_lemma_generalizers(datalog::rule_set& rules) } if (m_params.spacer_use_quant_generalizer()) { - m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, true)); - m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this)); + m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, + *this, 0, true)); + m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this, + m_params.spacer_quic_gen_normalize())); } if (get_params().spacer_use_eqclass()) { From 5df7a08d1c3ac349cee2eca84b3577a7c7b0e1b3 Mon Sep 17 00:00:00 2001 From: Yakir Vizel Date: Thu, 11 Jan 2018 15:02:09 -0500 Subject: [PATCH 061/364] A simple version for finding the stride between different indices in a POB This current version is very limited. It assumes a pre-defined structure (namely, an ADDER). --- src/muz/spacer/spacer_generalizers.h | 6 +- src/muz/spacer/spacer_quant_generalizer.cpp | 118 ++++++++++++++++++-- 2 files changed, 112 insertions(+), 12 deletions(-) diff --git a/src/muz/spacer/spacer_generalizers.h b/src/muz/spacer/spacer_generalizers.h index bc549a949..518b09f6c 100644 --- a/src/muz/spacer/spacer_generalizers.h +++ b/src/muz/spacer/spacer_generalizers.h @@ -126,13 +126,15 @@ private: void find_candidates(expr *e, app_ref_vector &candidate); bool is_ub(var *var, expr *e); bool is_lb(var *var, expr *e); - void mk_abs_cube (app *term, var *var, + void mk_abs_cube (lemma_ref &lemma, app *term, var *var, expr_ref_vector &gnd_cube, expr_ref_vector &abs_cube, - expr *&lb, expr *&ub); + expr *&lb, expr *&ub, unsigned &stride); bool match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk); void cleanup(expr_ref_vector& cube, app_ref_vector const &zks, expr_ref &bind); + + bool find_stride(expr_ref_vector &c, expr_ref &pattern, unsigned &stride); }; } diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index bbd086749..cf056271d 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -257,10 +257,10 @@ void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector lb and ub are null if no bound was found */ -void lemma_quantifier_generalizer::mk_abs_cube(app *term, var *var, +void lemma_quantifier_generalizer::mk_abs_cube(lemma_ref &lemma, app *term, var *var, expr_ref_vector &gnd_cube, expr_ref_vector &abs_cube, - expr *&lb, expr *&ub) { + expr *&lb, expr *&ub, unsigned &stride) { // create an abstraction function that maps candidate term to variables expr_safe_replace sub(m); @@ -302,6 +302,12 @@ void lemma_quantifier_generalizer::mk_abs_cube(app *term, var *var, } } abs_cube.push_back(abs_lit); + if (contains_selects(abs_lit, m)) { + expr_ref_vector pob_cube(m); + flatten_and(lemma->get_pob()->post(), pob_cube); + find_stride(pob_cube, abs_lit, stride); + } + if (!lb && is_lb(var, abs_lit)) { lb = abs_lit; } @@ -449,13 +455,14 @@ bool lemma_quantifier_generalizer::is_lb(var *var, expr *e) { bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { expr *lb = nullptr, *ub = nullptr; + unsigned stride = 1; expr_ref_vector gnd_cube(m); expr_ref_vector abs_cube(m); var_ref var(m); var = m.mk_var (m_offset, get_sort(term)); - mk_abs_cube(term, var, gnd_cube, abs_cube, lb, ub); + mk_abs_cube(lemma, term, var, gnd_cube, abs_cube, lb, ub, stride); if (abs_cube.empty()) {return false;} TRACE("spacer_qgen", @@ -480,21 +487,37 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { ub = abs_cube.back(); } + rational init; + expr_ref constant(m); + if (is_var(to_app(lb)->get_arg(0))) + constant = to_app(lb)->get_arg(1); + else + constant = to_app(lb)->get_arg(0); + + if (stride > 1 && m_arith.is_numeral(constant, init)) { + unsigned mod = init.get_unsigned() % stride; + TRACE("spacer_qgen", tout << "mod=" << mod << " init=" << init << " stride=" << stride << "\n";); + tout.flush(); + abs_cube.push_back(m.mk_eq( + m_arith.mk_mod(var, m_arith.mk_numeral(rational(stride), true)), + m_arith.mk_numeral(rational(mod), true))); + } + // skolemize - expr_ref gnd(m); - app_ref_vector zks(m); + expr_ref gnd(m); + app_ref_vector zks(m); ground_expr(mk_and(abs_cube), gnd, zks); flatten_and(gnd, gnd_cube); - TRACE("spacer_qgen", + TRACE("spacer_qgen", tout << "New CUBE is: " << mk_pp(mk_and(gnd_cube),m) << "\n";); // check if the result is a true lemma - unsigned uses_level = 0; - pred_transformer &pt = lemma->get_pob()->pt(); + unsigned uses_level = 0; + pred_transformer &pt = lemma->get_pob()->pt(); if (pt.check_inductive(lemma->level(), gnd_cube, uses_level, lemma->weakness())) { - TRACE("spacer_qgen", - tout << "Quantifier Generalization Succeeded!\n" + TRACE("spacer_qgen", + tout << "Quantifier Generalization Succeeded!\n" << "New CUBE is: " << mk_pp(mk_and(gnd_cube),m) << "\n";); SASSERT(zks.size() >= m_offset); @@ -528,6 +551,81 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { return false; } +bool lemma_quantifier_generalizer::find_stride(expr_ref_vector &c, expr_ref &pattern, unsigned &stride) { + expr_ref tmp(m); + tmp = mk_and(c); + normalize(tmp, tmp, false, true); + c.reset(); + flatten_and(tmp, c); + + app_ref_vector indices(m); + get_select_indices(pattern, indices, m); + + // TODO + if (indices.size() > 1) + return false; + + app *p_index = indices.get(0); + if (is_var(p_index)) return false; + + std::vector instances; + for (unsigned i=0; i < c.size(); i++) { + expr *lit = c.get(i); + + if (!contains_selects(lit, m)) + continue; + + indices.reset(); + get_select_indices(lit, indices, m); + + // TODO: + if (indices.size() > 1) + continue; + + app *candidate = indices.get(0); + + unsigned size = p_index->get_num_args(); + unsigned matched = 0; + for (unsigned p=0; p < size; p++) { + expr *arg = p_index->get_arg(p); + if (is_var(arg)) + { + rational val; + if (p < candidate->get_num_args() && m_arith.is_numeral(candidate->get_arg(p), val)) { + instances.push_back(val.get_unsigned()); + } + } + else { + for (unsigned j=0; j < candidate->get_num_args(); j++) { + if (candidate->get_arg(j) == arg) { + matched++; + break; + } + } + } + } + + if (matched < size - 1) + continue; + + if (candidate->get_num_args() == matched) + instances.push_back(0); + + TRACE("spacer_qgen", + tout << "Match succeeded!\n";); + } + + if (instances.size() <= 1) + return false; + + std::sort(instances.begin(), instances.end()); + + stride = instances[1]-instances[0]; + TRACE("spacer_qgen", tout << "Index Stride is: " << stride << "\n";); + + return true; +} + void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { if (lemma->get_cube().empty()) return; if (!lemma->has_pob()) return; From fd7bcc7afc2c2fd635b03ab4814a4dfad0410777 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 14 May 2018 14:41:12 -0700 Subject: [PATCH 062/364] Format --- src/muz/spacer/spacer_generalizers.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 342e43ad3..2f9627398 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -24,12 +24,10 @@ Revision History: #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" - +#include "ast/factor_equivs.h" #include "muz/spacer/spacer_term_graph.h" - #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" - #include "ast/expr_functors.h" From b51251f394aefc962fb6fe71d5849a26859ef142 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 08:30:35 -0700 Subject: [PATCH 063/364] Move tout under TRACE --- src/muz/spacer/spacer_quant_generalizer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index cf056271d..3dace0cdd 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -496,8 +496,9 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { if (stride > 1 && m_arith.is_numeral(constant, init)) { unsigned mod = init.get_unsigned() % stride; - TRACE("spacer_qgen", tout << "mod=" << mod << " init=" << init << " stride=" << stride << "\n";); - tout.flush(); + TRACE("spacer_qgen", + tout << "mod=" << mod << " init=" << init << " stride=" << stride << "\n"; + tout.flush();); abs_cube.push_back(m.mk_eq( m_arith.mk_mod(var, m_arith.mk_numeral(rational(stride), true)), m_arith.mk_numeral(rational(mod), true))); From 3c7165780c981957dd76cc48543f3c6576f9eeda Mon Sep 17 00:00:00 2001 From: Matteo Date: Thu, 5 Oct 2017 14:07:11 +0200 Subject: [PATCH 064/364] Extend spacer with callback events Callback events allow the client of spacer to get events during exection. The events include new lemmas and unfolding. --- src/api/api_datalog.cpp | 17 ++++++- src/api/z3_fixedpoint.h | 10 ++++ src/muz/base/dl_context.h | 8 ++++ src/muz/base/dl_engine_base.h | 10 ++++ src/muz/base/fixedpoint_params.pyg | 4 +- src/muz/spacer/CMakeLists.txt | 1 + src/muz/spacer/spacer_callback.cpp | 38 +++++++++++++++ src/muz/spacer/spacer_callback.h | 65 ++++++++++++++++++++++++++ src/muz/spacer/spacer_context.cpp | 57 ++++++++++++++++++++-- src/muz/spacer/spacer_context.h | 39 +++++++++++++++- src/muz/spacer/spacer_dl_interface.cpp | 8 ++++ src/muz/spacer/spacer_dl_interface.h | 5 ++ 12 files changed, 254 insertions(+), 8 deletions(-) create mode 100644 src/muz/spacer/spacer_callback.cpp create mode 100644 src/muz/spacer/spacer_callback.h diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index 2bc3d01e1..7d2e38269 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -603,7 +603,22 @@ extern "C" { Z3_CATCH; } - + + void Z3_API Z3_fixedpoint_add_callback(Z3_context c, Z3_fixedpoint d, + void *state, + Z3_fixedpoint_new_lemma_eh new_lemma_eh, + Z3_fixedpoint_predecessor_eh predecessor_eh, + Z3_fixedpoint_unfold_eh unfold_eh){ + Z3_TRY; + // not logged + to_fixedpoint_ref(d)->ctx().add_callback(state, + reinterpret_cast(new_lemma_eh), + reinterpret_cast(predecessor_eh), + reinterpret_cast(unfold_eh)); + + Z3_CATCH; + } + #include "api_datalog_spacer.inc" }; diff --git a/src/api/z3_fixedpoint.h b/src/api/z3_fixedpoint.h index a651993ce..6836c4766 100644 --- a/src/api/z3_fixedpoint.h +++ b/src/api/z3_fixedpoint.h @@ -367,6 +367,16 @@ extern "C" { void Z3_API Z3_fixedpoint_set_reduce_app_callback( Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr cb); + typedef void (*Z3_fixedpoint_new_lemma_eh)(void *state, Z3_ast *lemma, unsigned level); + typedef void (*Z3_fixedpoint_predecessor_eh)(void *state); + typedef void (*Z3_fixedpoint_unfold_eh)(void *state); + + /** \brief set export callback for lemmas */ + void Z3_API Z3_fixedpoint_add_callback(Z3_context ctx, Z3_fixedpoint f, void *state, + Z3_fixedpoint_new_lemma_eh new_lemma_eh, + Z3_fixedpoint_predecessor_eh predecessor_eh, + Z3_fixedpoint_unfold_eh unfold_eh); + /*@}*/ /*@}*/ diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 865c746db..a8567637c 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -588,6 +588,14 @@ namespace datalog { rel_context_base* get_rel_context() { ensure_engine(); return m_rel; } + void add_callback(void *state, + const datalog::t_new_lemma_eh new_lemma_eh, + const datalog::t_predecessor_eh predecessor_eh, + const datalog::t_unfold_eh unfold_eh) { + ensure_engine(); + m_engine->add_callback(state, new_lemma_eh, predecessor_eh, unfold_eh); + } + private: /** diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index b9ac6e7b5..910dd2695 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.h @@ -36,6 +36,10 @@ namespace datalog { LAST_ENGINE }; + typedef void (*t_new_lemma_eh)(void *state, expr *lemma, unsigned level); + typedef void (*t_predecessor_eh)(void *state); + typedef void (*t_unfold_eh)(void *state); + class engine_base { ast_manager& m; std::string m_name; @@ -102,6 +106,12 @@ namespace datalog { virtual proof_ref get_proof() { return proof_ref(m.mk_asserted(m.mk_true()), m); } + virtual void add_callback(void *state, + const t_new_lemma_eh new_lemma_eh, + const t_predecessor_eh predecessor_eh, + const t_unfold_eh unfold_eh) { + throw default_exception(std::string("add_lemma_exchange_callbacks is not supported for ") + m_name); + } virtual void updt_params() {} virtual void cancel() {} virtual void cleanup() {} diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 277399b9f..07b555095 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -190,7 +190,9 @@ def_module_params('fixedpoint', ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints'), ('spacer.use_quant_generalizer', BOOL, False, 'use quantified lemma generalizer'), ('spacer.quic_gen_normalize', BOOL, True, 'normalize cube before quantified generalization'), - )) + ('spacer.share_lemmas', BOOL, False, "Share frame lemmas"), + ('spacer.share_invariants', BOOL, False, "Share invariants lemmas"), +)) diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 16ce3e4f9..a351ea353 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -23,6 +23,7 @@ z3_add_component(spacer spacer_term_graph.cpp spacer_sem_matcher.cpp spacer_quant_generalizer.cpp + spacer_callback.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_callback.cpp b/src/muz/spacer/spacer_callback.cpp new file mode 100644 index 000000000..9c7c88ad9 --- /dev/null +++ b/src/muz/spacer/spacer_callback.cpp @@ -0,0 +1,38 @@ +/**++ +Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti + +Module Name: + + spacer_callback.cpp + +Abstract: + + SPACER plugin for handling events + +Author: + + Matteo Marescotti + +Notes: + +--*/ + +#include "spacer_callback.h" +#include "muz/spacer/spacer_context.h" + + +namespace spacer { + + void user_callback::new_lemma_eh(expr *lemma, unsigned level) { + m_new_lemma_eh(m_state, lemma, level); + } + + void user_callback::predecessor_eh() { + m_predecessor_eh(m_state); + } + + void user_callback::unfold_eh() { + m_unfold_eh(m_state); + } + +} \ No newline at end of file diff --git a/src/muz/spacer/spacer_callback.h b/src/muz/spacer/spacer_callback.h new file mode 100644 index 000000000..d5b47b90e --- /dev/null +++ b/src/muz/spacer/spacer_callback.h @@ -0,0 +1,65 @@ +/**++ +Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti + +Module Name: + + spacer_callback.h + +Abstract: + + SPACER plugin for handling events + +Author: + + Matteo Marescotti + +Notes: + +--*/ + +#ifndef _SPACER_CALLBACK_H_ +#define _SPACER_CALLBACK_H_ + +#include "muz/spacer/spacer_context.h" +#include "muz/base/dl_engine_base.h" + + +namespace spacer { + + class user_callback : public spacer_callback { + private: + void *m_state; + const datalog::t_new_lemma_eh m_new_lemma_eh; + const datalog::t_predecessor_eh m_predecessor_eh; + const datalog::t_unfold_eh m_unfold_eh; + + public: + user_callback(context &context, + void *state, + const datalog::t_new_lemma_eh new_lemma_eh, + const datalog::t_predecessor_eh predecessor_eh, + const datalog::t_unfold_eh unfold_eh) : + spacer_callback(context), + m_state(state), + m_new_lemma_eh(new_lemma_eh), + m_predecessor_eh(predecessor_eh), + m_unfold_eh(unfold_eh) {} + + inline bool new_lemma() override { return true; } + + void new_lemma_eh(expr *lemma, unsigned level) override; + + inline bool predecessor() override { return true; } + + void predecessor_eh() override; + + inline bool unfold() override { return true; } + + void unfold_eh() override; + + }; + +} + + +#endif //_SPACER_CALLBACK_H_ diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index eba51c793..726edadc1 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1178,7 +1178,7 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(lvl), - m_pob(nullptr) { + m_pob(nullptr), m_external(false) { SASSERT(m_body); normalize(m_body, m_body); } @@ -1187,7 +1187,7 @@ lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(p->level()), - m_pob(p) { + m_pob(p), m_external(false) { SASSERT(m_pob); m_pob->get_skolems(m_zks); add_binding(m_pob->get_binding()); @@ -1198,7 +1198,7 @@ lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(p->level()), - m_pob(p) + m_pob(p), m_external(false) { if (m_pob) { m_pob->get_skolems(m_zks); @@ -1408,6 +1408,10 @@ bool pred_transformer::frames::add_lemma(lemma *lem) m_lemmas.push_back(lem); m_sorted = false; m_pt.add_lemma_core(lem); + + if (!lem->external()) { + m_pt.get_context().new_lemma_eh(m_pt, lem); + } return true; } @@ -2726,6 +2730,11 @@ lbool context::solve_core (unsigned from_lvl) if (lvl > 0 && !get_params ().spacer_skip_propagate ()) if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { return l_false; } + for (unsigned i = 0; i < m_callbacks.size(); i++){ + if (m_callbacks[i]->unfold()) + m_callbacks[i]->unfold_eh(); + } + m_pob_queue.inc_level (); lvl = m_pob_queue.max_level (); m_stats.m_max_depth = std::max(m_stats.m_max_depth, lvl); @@ -3009,6 +3018,11 @@ lbool context::expand_node(pob& n) tout << "This pob can be blocked by instantiation\n";); } + for (unsigned i = 0; i < m_callbacks.size(); i++){ + if(m_callbacks[i]->predecessor()) + m_callbacks[i]->predecessor_eh(); + } + lbool res = n.pt ().is_reachable (n, &cube, &model, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); checkpoint (); @@ -3480,6 +3494,9 @@ void context::collect_statistics(statistics& st) const // -- time in creating new predecessors st.update ("time.spacer.solve.reach.children", m_create_children_watch.get_seconds ()); + st.update("spacer.random_seed", m_params.spacer_random_seed()); + st.update("spacer.lemmas_imported", m_stats.m_num_lemmas_imported); + st.update("spacer.lemmas_discarded", m_stats.m_num_lemmas_discarded); m_pm.collect_statistics(st); for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) { @@ -3588,8 +3605,38 @@ void context::add_constraints (unsigned level, const expr_ref& c) if (m.is_implies(c, e1, e2)) { SASSERT (is_app (e1)); pred_transformer *r = nullptr; - if (m_rels.find (to_app (e1)->get_decl (), r)) - { r->add_lemma(e2, level); } + if (m_rels.find (to_app (e1)->get_decl (), r)){ + lemma_ref lem = alloc(lemma, m, e2, level); + lem.get()->set_external(true); + if (r->add_lemma(lem.get())) { + this->m_stats.m_num_lemmas_imported++; + } + else{ + this->m_stats.m_num_lemmas_discarded++; + } + } + } + } +} + +void context::new_lemma_eh(pred_transformer &pt, lemma *lem) { + bool handle=false; + for (unsigned i = 0; i < m_callbacks.size(); i++) { + handle|=m_callbacks[i]->new_lemma(); + } + if (!handle) + return; + if ((is_infty_level(lem->level()) && m_params.spacer_share_invariants()) || + (!is_infty_level(lem->level()) && m_params.spacer_share_lemmas())) { + expr_ref_vector args(m); + for (unsigned i = 0; i < pt.sig_size(); ++i) { + args.push_back(m.mk_const(pt.get_manager().o2n(pt.sig(i), 0))); + } + expr *app = m.mk_app(pt.head(), pt.sig_size(), args.c_ptr()); + expr *lemma = m.mk_implies(app, lem->get_expr()); + for (unsigned i = 0; i < m_callbacks.size(); i++) { + if (m_callbacks[i]->new_lemma()) + m_callbacks[i]->new_lemma_eh(lemma, lem->level()); } } } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index e909cd9ad..8b8055ad5 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -28,7 +28,7 @@ Notes: #undef max #endif #include - +#include "util/scoped_ptr_vector.h" #include "muz/spacer/spacer_manager.h" #include "muz/spacer/spacer_prop_solver.h" @@ -114,6 +114,7 @@ class lemma { app_ref_vector m_bindings; unsigned m_lvl; pob_ref m_pob; + bool m_external; void mk_expr_core(); void mk_cube_core(); @@ -135,6 +136,9 @@ public: void add_skolem(app *zk, app* b); + inline void set_external(bool ext){m_external = ext;} + inline bool external() { return m_external;} + unsigned level () const {return m_lvl;} void set_level (unsigned lvl) {m_lvl = lvl;} app_ref_vector& get_bindings() {return m_bindings;} @@ -685,6 +689,32 @@ public: }; +class spacer_callback { +protected: + context &m_context; + +public: + spacer_callback(context &context) : m_context(context) {} + + virtual ~spacer_callback() = default; + + context &get_context() { return m_context; } + + virtual inline bool new_lemma() { return false; } + + virtual void new_lemma_eh(expr *lemma, unsigned level) {} + + virtual inline bool predecessor() { return false; } + + virtual void predecessor_eh() {} + + virtual inline bool unfold() { return false; } + + virtual void unfold_eh() {} + +}; + + class context { struct stats { @@ -697,6 +727,8 @@ class context { unsigned m_expand_node_undef; unsigned m_num_lemmas; unsigned m_num_restarts; + unsigned m_num_lemmas_imported; + unsigned m_num_lemmas_discarded; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; @@ -731,6 +763,7 @@ class context { bool m_weak_abs; bool m_use_restarts; unsigned m_restart_initial_threshold; + scoped_ptr_vector m_callbacks; // Functions used by search. lbool solve_core (unsigned from_lvl = 0); @@ -850,6 +883,10 @@ public: expr_ref get_constraints (unsigned lvl); void add_constraints (unsigned lvl, const expr_ref& c); + + void new_lemma_eh(pred_transformer &pt, lemma *lem); + + scoped_ptr_vector &callbacks() {return m_callbacks;} }; inline bool pred_transformer::use_native_mbp () {return ctx.use_native_mbp ();} diff --git a/src/muz/spacer/spacer_dl_interface.cpp b/src/muz/spacer/spacer_dl_interface.cpp index 52209454d..571687e16 100644 --- a/src/muz/spacer/spacer_dl_interface.cpp +++ b/src/muz/spacer/spacer_dl_interface.cpp @@ -34,6 +34,7 @@ Revision History: #include "model/model_smt2_pp.h" #include "ast/scoped_proof.h" #include "muz/transforms/dl_transforms.h" +#include "muz/spacer/spacer_callback.h" using namespace spacer; @@ -352,3 +353,10 @@ proof_ref dl_interface::get_proof() { return m_context->get_proof(); } + +void dl_interface::add_callback(void *state, + const datalog::t_new_lemma_eh new_lemma_eh, + const datalog::t_predecessor_eh predecessor_eh, + const datalog::t_unfold_eh unfold_eh){ + m_context->callbacks().push_back(alloc(user_callback, *m_context, state, new_lemma_eh, predecessor_eh, unfold_eh)); +} diff --git a/src/muz/spacer/spacer_dl_interface.h b/src/muz/spacer/spacer_dl_interface.h index e5a41427d..fb5ac3803 100644 --- a/src/muz/spacer/spacer_dl_interface.h +++ b/src/muz/spacer/spacer_dl_interface.h @@ -79,6 +79,11 @@ public: proof_ref get_proof() override; + void add_callback(void *state, + const datalog::t_new_lemma_eh new_lemma_eh, + const datalog::t_predecessor_eh predecessor_eh, + const datalog::t_unfold_eh unfold_eh); + }; } From 65885f7ebad4c4e8227c248f8073e20c5262c174 Mon Sep 17 00:00:00 2001 From: Matteo Date: Tue, 17 Oct 2017 16:55:52 +0200 Subject: [PATCH 065/364] add_constraint API --- src/api/api_datalog.cpp | 4 +++ src/api/z3_fixedpoint.h | 4 ++- src/muz/base/dl_context.h | 5 ++++ src/muz/base/dl_engine_base.h | 3 +++ src/muz/spacer/spacer_callback.h | 6 ++--- src/muz/spacer/spacer_context.cpp | 37 +++++++++++--------------- src/muz/spacer/spacer_context.h | 2 +- src/muz/spacer/spacer_dl_interface.cpp | 4 +++ src/muz/spacer/spacer_dl_interface.h | 2 ++ 9 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index 7d2e38269..fd31a65f8 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -619,6 +619,10 @@ extern "C" { Z3_CATCH; } + void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl){ + to_fixedpoint_ref(d)->ctx().add_constraint(to_expr(e), lvl); + } + #include "api_datalog_spacer.inc" }; diff --git a/src/api/z3_fixedpoint.h b/src/api/z3_fixedpoint.h index 6836c4766..b4c3eee49 100644 --- a/src/api/z3_fixedpoint.h +++ b/src/api/z3_fixedpoint.h @@ -367,7 +367,7 @@ extern "C" { void Z3_API Z3_fixedpoint_set_reduce_app_callback( Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr cb); - typedef void (*Z3_fixedpoint_new_lemma_eh)(void *state, Z3_ast *lemma, unsigned level); + typedef void (*Z3_fixedpoint_new_lemma_eh)(void *state, Z3_ast lemma, unsigned level); typedef void (*Z3_fixedpoint_predecessor_eh)(void *state); typedef void (*Z3_fixedpoint_unfold_eh)(void *state); @@ -377,6 +377,8 @@ extern "C" { Z3_fixedpoint_predecessor_eh predecessor_eh, Z3_fixedpoint_unfold_eh unfold_eh); + void Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl); + /*@}*/ /*@}*/ diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index a8567637c..b49c7e665 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -596,6 +596,11 @@ namespace datalog { m_engine->add_callback(state, new_lemma_eh, predecessor_eh, unfold_eh); } + void add_constraint (expr *c, unsigned lvl){ + ensure_engine(); + m_engine->add_constraint(c, lvl); + } + private: /** diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index 910dd2695..9fc90ab1d 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.h @@ -112,6 +112,9 @@ namespace datalog { const t_unfold_eh unfold_eh) { throw default_exception(std::string("add_lemma_exchange_callbacks is not supported for ") + m_name); } + virtual void add_constraint (expr *c, unsigned lvl){ + throw default_exception(std::string("add_constraint is not supported for ") + m_name); + } virtual void updt_params() {} virtual void cancel() {} virtual void cleanup() {} diff --git a/src/muz/spacer/spacer_callback.h b/src/muz/spacer/spacer_callback.h index d5b47b90e..35805c7bc 100644 --- a/src/muz/spacer/spacer_callback.h +++ b/src/muz/spacer/spacer_callback.h @@ -45,15 +45,15 @@ namespace spacer { m_predecessor_eh(predecessor_eh), m_unfold_eh(unfold_eh) {} - inline bool new_lemma() override { return true; } + inline bool new_lemma() override { return m_new_lemma_eh != nullptr; } void new_lemma_eh(expr *lemma, unsigned level) override; - inline bool predecessor() override { return true; } + inline bool predecessor() override { return m_predecessor_eh != nullptr; } void predecessor_eh() override; - inline bool unfold() override { return true; } + inline bool unfold() override { return m_unfold_eh != nullptr; } void unfold_eh() override; diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 726edadc1..a4faf88cf 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1229,21 +1229,21 @@ void lemma::mk_expr_core() { normalize(m_body, m_body); if (!m_zks.empty() && has_zk_const(m_body)) { - app_ref_vector zks(m); + app_ref_vector zks(m); zks.append(m_zks); - zks.reverse(); - expr_abstract(m, 0, - zks.size(), (expr* const*)zks.c_ptr(), m_body, - m_body); - ptr_buffer sorts; - svector names; - for (unsigned i=0, sz=zks.size(); i < sz; ++i) { - sorts.push_back(get_sort(zks.get(i))); - names.push_back(zks.get(i)->get_decl()->get_name()); - } - m_body = m.mk_quantifier(true, zks.size(), - sorts.c_ptr(), - names.c_ptr(), + zks.reverse(); + expr_abstract(m, 0, + zks.size(), (expr* const*)zks.c_ptr(), m_body, + m_body); + ptr_buffer sorts; + svector names; + for (unsigned i=0, sz=zks.size(); i < sz; ++i) { + sorts.push_back(get_sort(zks.get(i))); + names.push_back(zks.get(i)->get_decl()->get_name()); + } + m_body = m.mk_quantifier(true, zks.size(), + sorts.c_ptr(), + names.c_ptr(), m_body, 15, symbol(m_body->get_id())); } SASSERT(m_body); @@ -3590,17 +3590,11 @@ expr_ref context::get_constraints (unsigned level) return m_pm.mk_and (constraints); } -void context::add_constraints (unsigned level, const expr_ref& c) +void context::add_constraint (unsigned level, const expr_ref& c) { if (!c.get()) { return; } if (m.is_true(c)) { return; } - expr_ref_vector constraints (m); - constraints.push_back (c); - flatten_and (constraints); - - for (unsigned i = 0, sz = constraints.size(); i < sz; ++i) { - expr *c = constraints.get (i); expr *e1, *e2; if (m.is_implies(c, e1, e2)) { SASSERT (is_app (e1)); @@ -3613,7 +3607,6 @@ void context::add_constraints (unsigned level, const expr_ref& c) } else{ this->m_stats.m_num_lemmas_discarded++; - } } } } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 8b8055ad5..9d6d8d0a5 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -882,7 +882,7 @@ public: pob& get_root() const { return m_pob_queue.get_root(); } expr_ref get_constraints (unsigned lvl); - void add_constraints (unsigned lvl, const expr_ref& c); + void add_constraint (unsigned lvl, const expr_ref& c); void new_lemma_eh(pred_transformer &pt, lemma *lem); diff --git a/src/muz/spacer/spacer_dl_interface.cpp b/src/muz/spacer/spacer_dl_interface.cpp index 571687e16..57c225b38 100644 --- a/src/muz/spacer/spacer_dl_interface.cpp +++ b/src/muz/spacer/spacer_dl_interface.cpp @@ -360,3 +360,7 @@ void dl_interface::add_callback(void *state, const datalog::t_unfold_eh unfold_eh){ m_context->callbacks().push_back(alloc(user_callback, *m_context, state, new_lemma_eh, predecessor_eh, unfold_eh)); } + +void dl_interface::add_constraint (expr *c, unsigned lvl){ + m_context->add_constraint(c,lvl); +} diff --git a/src/muz/spacer/spacer_dl_interface.h b/src/muz/spacer/spacer_dl_interface.h index fb5ac3803..2980e2a0f 100644 --- a/src/muz/spacer/spacer_dl_interface.h +++ b/src/muz/spacer/spacer_dl_interface.h @@ -84,6 +84,8 @@ public: const datalog::t_predecessor_eh predecessor_eh, const datalog::t_unfold_eh unfold_eh); + void add_constraint (expr *c, unsigned lvl); + }; } From ff7c949be89dab7973583ee605104bc2a325b0d9 Mon Sep 17 00:00:00 2001 From: Matteo Marescotti Date: Thu, 15 Mar 2018 16:28:45 -0400 Subject: [PATCH 066/364] Fix: call collect_statistics() in virtual_solver --- src/muz/spacer/spacer_virtual_solver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_virtual_solver.h b/src/muz/spacer/spacer_virtual_solver.h index 9dc20c241..162f56178 100644 --- a/src/muz/spacer/spacer_virtual_solver.h +++ b/src/muz/spacer/spacer_virtual_solver.h @@ -80,7 +80,7 @@ public: void get_unsat_core(ptr_vector &r) override; void assert_expr_core(expr *e) override; - void collect_statistics(statistics &st) const override {} + void collect_statistics(statistics &st) const override {m_context.collect_statistics(st);} void get_model_core(model_ref &m) override {m_context.get_model(m);} proof* get_proof() override; std::string reason_unknown() const override From 28ef9ab9d14f220268df53db721969bea9e0458e Mon Sep 17 00:00:00 2001 From: Matteo Marescotti Date: Thu, 15 Mar 2018 16:30:42 -0400 Subject: [PATCH 067/364] User option to enable starting spacer from a given level --- src/muz/base/fixedpoint_params.pyg | 3 ++- src/muz/spacer/spacer_dl_interface.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 07b555095..dc051bd71 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -192,7 +192,8 @@ def_module_params('fixedpoint', ('spacer.quic_gen_normalize', BOOL, True, 'normalize cube before quantified generalization'), ('spacer.share_lemmas', BOOL, False, "Share frame lemmas"), ('spacer.share_invariants', BOOL, False, "Share invariants lemmas"), -)) + ('spacer.from_level', UINT, 0, 'starting level to explore') + )) diff --git a/src/muz/spacer/spacer_dl_interface.cpp b/src/muz/spacer/spacer_dl_interface.cpp index 57c225b38..69e5d76ff 100644 --- a/src/muz/spacer/spacer_dl_interface.cpp +++ b/src/muz/spacer/spacer_dl_interface.cpp @@ -170,7 +170,7 @@ lbool dl_interface::query(expr * query) return l_false; } - return m_context->solve(); + return m_context->solve(m_ctx.get_params().spacer_from_level()); } From 3248f57434c84ad4e310aad0bbb3d8ce9d51283c Mon Sep 17 00:00:00 2001 From: Matteo Marescotti Date: Thu, 15 Mar 2018 16:31:22 -0400 Subject: [PATCH 068/364] Add support for printing spacer pobs in JSON --- src/muz/spacer/CMakeLists.txt | 1 + src/muz/spacer/spacer_json.cpp | 155 +++++++++++++++++++++++++++++++++ src/muz/spacer/spacer_json.h | 68 +++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 src/muz/spacer/spacer_json.cpp create mode 100644 src/muz/spacer/spacer_json.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index a351ea353..50e0c9382 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -24,6 +24,7 @@ z3_add_component(spacer spacer_sem_matcher.cpp spacer_quant_generalizer.cpp spacer_callback.cpp + spacer_json.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_json.cpp b/src/muz/spacer/spacer_json.cpp new file mode 100644 index 000000000..0bfa091c1 --- /dev/null +++ b/src/muz/spacer/spacer_json.cpp @@ -0,0 +1,155 @@ +/**++ +Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti + +Module Name: + + spacer_json.cpp + +Abstract: + + SPACER json marshalling support + +Author: + + Matteo Marescotti + +Notes: + +--*/ + +#include +#include "spacer_context.h" +#include "spacer_json.h" +#include "spacer_util.h" + +namespace spacer { + + std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m) { + + mk_epp pp = mk_epp(t, m); + std::ostringstream ss; + ss << pp; + out << "\""; + for (auto &c:ss.str()) { + switch (c) { + case '"': + out << "\\\""; + break; + case '\\': + out << "\\\\"; + break; + case '\b': + out << "\\b"; + break; + case '\f': + out << "\\f"; + break; + case '\n': + out << "\\n"; + break; + case '\r': + out << "\\r"; + break; + case '\t': + out << "\\t"; + break; + default: + if ('\x00' <= c && c <= '\x1f') { + out << "\\u" + << std::hex << std::setw(4) << std::setfill('0') << (int) c; + } else { + out << c; + } + } + } + out << "\""; + return out; + } + + std::ostream &json_marshal(std::ostream &out, lemma *l) { + out << R"({"level":")" << l->level() << R"(", "expr":)"; + json_marshal(out, l->get_expr(), l->get_ast_manager()); + out << "}"; + return out; + } + + std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector lemmas) { + + std::ostringstream ls; + for (auto &l:lemmas) { + ls << (ls.tellp() == 0 ? "" : ","); + json_marshal(ls, l); + } + out << "[" << ls.str() << "]"; + return out; + } + + + void json_marshaller::pob_blocked_by_lemma_eh(pob *p, lemma *l) { + //if(m_ctx->get_params().spacer_pr) + m_relations[p][p->depth()].push_back(l); + } + + void json_marshaller::new_pob_eh(pob *p) { + m_relations[p]; + } + + std::ostream &spacer::json_marshaller::marshal(std::ostream &out) const { + std::ostringstream nodes; + std::ostringstream edges; + std::ostringstream lemmas; + + unsigned pob_id = 0; + for (auto &pob_map:m_relations) { + std::ostringstream pob_lemmas; + for (auto &depth_lemmas : pob_map.second) { + pob_lemmas << (pob_lemmas.tellp() == 0 ? "" : ",") << "\"" << depth_lemmas.first << "\":"; + json_marshal(pob_lemmas, depth_lemmas.second); + } + if (pob_lemmas.tellp()) { + lemmas << (lemmas.tellp() == 0 ? "" : ",\n"); + lemmas << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; + } + } + + unsigned depth = 0; + while (true) { + double root_expand_time = m_ctx->get_root().get_expand_time(depth); + bool a = false; + unsigned i = 0; + for (auto &pob_map:m_relations) { + pob_ref n = pob_map.first; + double expand_time = n->get_expand_time(depth); + if (expand_time > 0) { + a = true; + std::ostringstream pob_expr; + json_marshal(pob_expr, n->post(), n->get_ast_manager()); + + nodes << (nodes.tellp() == 0 ? "" : ",\n") << + "{\"id\":\"" << depth << n << + "\",\"relative_time\":\"" << expand_time / root_expand_time << + "\",\"absolute_time\":\"" << std::setprecision(2) << expand_time << + "\",\"predicate\":\"" << n->pt().head()->get_name() << + "\",\"expr_id\":\"" << n->post()->get_id() << + "\",\"pob_id\":\"" << i << + "\",\"depth\":\"" << depth << + "\",\"expr\":" << pob_expr.str() << "}"; + if (n->parent()) { + edges << (edges.tellp() == 0 ? "" : ",\n") << + "{\"from\":\"" << depth << n->parent() << + "\",\"to\":\"" << depth << n << "\"}"; + } + } + } + if (!a) { + break; + } + depth++; + } + out << "{\n\"nodes\":[\n" << nodes.str() << "\n],\n"; + out << "\"edges\":[\n" << edges.str() << "\n],\n"; + out << "\"lemmas\":{\n" << lemmas.str() << "\n}\n}\n"; + return out; + } + +} diff --git a/src/muz/spacer/spacer_json.h b/src/muz/spacer/spacer_json.h new file mode 100644 index 000000000..dc411f67c --- /dev/null +++ b/src/muz/spacer/spacer_json.h @@ -0,0 +1,68 @@ +/**++ +Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti + +Module Name: + + spacer_json.h + +Abstract: + + SPACER json marshalling support + +Author: + + Matteo Marescotti + +Notes: + +--*/ + +#ifndef Z3_SPACER_JSON_H +#define Z3_SPACER_JSON_H + +#include +#include +#include "ref.h" +#include "ref_vector.h" + +class ast; + +class ast_manager; + +namespace spacer { + + class lemma; + + typedef sref_vector lemma_ref_vector; + + class context; + + class pob; + + typedef ref pob_ref; + + std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m); + + std::ostream &json_marshal(std::ostream &out, lemma *l); + + std::ostream &json_marshal(std::ostream &out, lemma_ref_vector &lemmas); + + + class json_marshaller { + context *m_ctx; + std::map> m_relations; + + public: + json_marshaller(context *ctx) : m_ctx(ctx) {} + + void pob_blocked_by_lemma_eh(pob *p, lemma *l); + + void new_pob_eh(pob *p); + + std::ostream &marshal(std::ostream &out) const; + }; + +} + + +#endif //Z3_SPACER_JSON_H From a4e67b8bb6be95e1447f11edd0bd6fae96781146 Mon Sep 17 00:00:00 2001 From: Matteo Marescotti Date: Thu, 15 Mar 2018 19:11:47 -0400 Subject: [PATCH 069/364] Wire JSON printing into Spacer --- src/muz/base/fixedpoint_params.pyg | 3 ++- src/muz/spacer/spacer_context.cpp | 43 +++++++++++++++++++++++++++--- src/muz/spacer/spacer_context.h | 40 +++++++++++++++++++++++++-- src/muz/spacer/spacer_json.cpp | 21 ++++++++------- src/muz/spacer/spacer_json.h | 8 +++--- 5 files changed, 95 insertions(+), 20 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index dc051bd71..74903baf1 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -192,7 +192,8 @@ def_module_params('fixedpoint', ('spacer.quic_gen_normalize', BOOL, True, 'normalize cube before quantified generalization'), ('spacer.share_lemmas', BOOL, False, "Share frame lemmas"), ('spacer.share_invariants', BOOL, False, "Share invariants lemmas"), - ('spacer.from_level', UINT, 0, 'starting level to explore') + ('spacer.from_level', UINT, 0, 'starting level to explore'), + ('spacer.print_json', SYMBOL, '', 'print pobs tree in JSON format to a given file') )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index a4faf88cf..75a9d7d81 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1353,6 +1353,14 @@ void lemma::instantiate(expr * const * exprs, expr_ref &result, expr *e) { vs(body, num_decls, exprs, result); } +void lemma::set_level (unsigned lvl) { + if(m_pob){ + m_pob->blocked_at(lvl); + } + m_lvl = lvl; +} + + void lemma::mk_insts(expr_ref_vector &out, expr* e) { expr *lem = e == nullptr ? get_expr() : e; @@ -1376,6 +1384,7 @@ bool pred_transformer::frames::add_lemma(lemma *lem) for (unsigned i = 0, sz = m_lemmas.size(); i < sz; ++i) { if (m_lemmas [i]->get_expr() == lem->get_expr()) { + m_pt.get_context().new_lemma_eh(m_pt, lem); // extend bindings if needed if (!lem->get_bindings().empty()) { m_lemmas [i]->add_binding(lem->get_bindings()); @@ -1879,7 +1888,8 @@ pob::pob (pob* parent, pred_transformer& pt, m_binding(m_pt.get_ast_manager()), m_new_post (m_pt.get_ast_manager ()), m_level (level), m_depth (depth), - m_open (true), m_use_farkas (true), m_weakness(0) { + m_open (true), m_use_farkas (true), m_weakness(0), + m_blocked_lvl(0) { if(add_to_parent && m_parent) { m_parent->add_child(*this); } @@ -1994,6 +2004,11 @@ void pob_queue::reset() if (m_root) { m_obligations.push(m_root); } } +void pob_queue::push(pob &n) { + m_obligations.push (&n); + n.get_context().new_pob_eh(&n); +} + // ---------------- // context @@ -2015,7 +2030,8 @@ context::context(fixedpoint_params const& params, m_use_qlemmas (params.spacer_qlemmas ()), m_weak_abs(params.spacer_weak_abs()), m_use_restarts(params.spacer_restarts()), - m_restart_initial_threshold(params.spacer_restart_initial_threshold()) + m_restart_initial_threshold(params.spacer_restart_initial_threshold()), + m_json_marshaller(this) {} context::~context() @@ -2728,7 +2744,13 @@ lbool context::solve_core (unsigned from_lvl) if (check_reachability()) { return l_true; } if (lvl > 0 && !get_params ().spacer_skip_propagate ()) - if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { return l_false; } + if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { dump_json(); return l_false; } + + dump_json(); + + if (is_inductive()){ + return l_false; + } for (unsigned i = 0; i < m_callbacks.size(); i++){ if (m_callbacks[i]->unfold()) @@ -2963,6 +2985,7 @@ bool context::is_reachable(pob &n) //this processes a goal and creates sub-goal lbool context::expand_node(pob& n) { + pob::on_expand_event _evt(n); TRACE ("spacer", tout << "expand-node: " << n.pt().head()->get_name() << " level: " << n.level() @@ -3613,6 +3636,8 @@ void context::add_constraint (unsigned level, const expr_ref& c) } void context::new_lemma_eh(pred_transformer &pt, lemma *lem) { + if (m_params.spacer_print_json().size()) + m_json_marshaller.register_lemma(lem); bool handle=false; for (unsigned i = 0; i < m_callbacks.size(); i++) { handle|=m_callbacks[i]->new_lemma(); @@ -3634,6 +3659,18 @@ void context::new_lemma_eh(pred_transformer &pt, lemma *lem) { } } +void context::new_pob_eh(pob *p) { + if (m_params.spacer_print_json().size()) + m_json_marshaller.register_pob(p); +} + +bool context::is_inductive() { + // check that inductive level (F infinity) of the query predicate + // contains a constant false + + return false; +} + inline bool pob_lt::operator() (const pob *pn1, const pob *pn2) const { SASSERT (pn1); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 9d6d8d0a5..ff0e90cfa 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -31,6 +31,7 @@ Notes: #include "util/scoped_ptr_vector.h" #include "muz/spacer/spacer_manager.h" #include "muz/spacer/spacer_prop_solver.h" +#include "muz/spacer/spacer_json.h" #include "muz/base/fixedpoint_params.hpp" @@ -140,7 +141,7 @@ public: inline bool external() { return m_external;} unsigned level () const {return m_lvl;} - void set_level (unsigned lvl) {m_lvl = lvl;} + void set_level (unsigned lvl); app_ref_vector& get_bindings() {return m_bindings;} bool has_binding(app_ref_vector const &binding); void add_binding(app_ref_vector const &binding); @@ -475,6 +476,10 @@ class pob { scoped_ptr m_derivation; ptr_vector m_kids; + + // depth -> watch + std::map m_expand_watches; + unsigned m_blocked_lvl; public: pob (pob* parent, pred_transformer& pt, unsigned level, unsigned depth=0, bool add_to_parent=true); @@ -504,6 +509,8 @@ public: unsigned level () const { return m_level; } unsigned depth () const {return m_depth;} + unsigned width () const {return m_kids.size();} + unsigned blocked_at(unsigned lvl=0){return (m_blocked_lvl = std::max(lvl, m_blocked_lvl)); } bool use_farkas_generalizer () const {return m_use_farkas;} void set_farkas_generalizer (bool v) {m_use_farkas = v;} @@ -537,6 +544,10 @@ public: */ void get_skolems(app_ref_vector& v); + void on_expand() { m_expand_watches[m_depth].start(); if(m_parent.get()){m_parent.get()->on_expand();} } + void off_expand() { m_expand_watches[m_depth].stop(); if(m_parent.get()){m_parent.get()->off_expand();} }; + double get_expand_time(unsigned depth) { return m_expand_watches[depth].get_seconds();} + void inc_ref () {++m_ref_count;} void dec_ref () { @@ -544,6 +555,13 @@ public: if(m_ref_count == 0) { dealloc(this); } } + class on_expand_event + { + pob &m_p; + public: + on_expand_event(pob &p) : m_p(p) {m_p.on_expand();} + ~on_expand_event() {m_p.off_expand();} + }; }; @@ -653,7 +671,7 @@ public: void reset(); pob * top (); void pop () {m_obligations.pop ();} - void push (pob &n) {m_obligations.push (&n);} + void push (pob &n); void inc_level () { @@ -712,6 +730,10 @@ public: virtual void unfold_eh() {} + virtual inline bool propagate() { return false; } + + virtual void propagate_eh() {} + }; @@ -764,6 +786,7 @@ class context { bool m_use_restarts; unsigned m_restart_initial_threshold; scoped_ptr_vector m_callbacks; + json_marshaller m_json_marshaller; // Functions used by search. lbool solve_core (unsigned from_lvl = 0); @@ -803,6 +826,15 @@ class context { unsigned get_cex_depth (); + void dump_json() { + if(m_params.spacer_print_json().size()) { + std::ofstream of; + of.open(m_params.spacer_print_json().bare_str()); + m_json_marshaller.marshal(of); + of.close(); + } + } + public: /** Initial values of predicates are stored in corresponding relations in dctx. @@ -887,6 +919,10 @@ public: void new_lemma_eh(pred_transformer &pt, lemma *lem); scoped_ptr_vector &callbacks() {return m_callbacks;} + + void new_pob_eh(pob *p); + + bool is_inductive(); }; inline bool pred_transformer::use_native_mbp () {return ctx.use_native_mbp ();} diff --git a/src/muz/spacer/spacer_json.cpp b/src/muz/spacer/spacer_json.cpp index 0bfa091c1..8b7a0878e 100644 --- a/src/muz/spacer/spacer_json.cpp +++ b/src/muz/spacer/spacer_json.cpp @@ -73,10 +73,10 @@ namespace spacer { return out; } - std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector lemmas) { + std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector &lemmas) { std::ostringstream ls; - for (auto &l:lemmas) { + for (auto l:lemmas) { ls << (ls.tellp() == 0 ? "" : ","); json_marshal(ls, l); } @@ -85,12 +85,13 @@ namespace spacer { } - void json_marshaller::pob_blocked_by_lemma_eh(pob *p, lemma *l) { - //if(m_ctx->get_params().spacer_pr) - m_relations[p][p->depth()].push_back(l); + void json_marshaller::register_lemma(lemma *l) { + if (l->has_pob()) { + m_relations[&*l->get_pob()][l->get_pob()->depth()].push_back(l); + } } - void json_marshaller::new_pob_eh(pob *p) { + void json_marshaller::register_pob(pob *p) { m_relations[p]; } @@ -110,15 +111,16 @@ namespace spacer { lemmas << (lemmas.tellp() == 0 ? "" : ",\n"); lemmas << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; } + pob_id++; } unsigned depth = 0; while (true) { double root_expand_time = m_ctx->get_root().get_expand_time(depth); bool a = false; - unsigned i = 0; + pob_id = 0; for (auto &pob_map:m_relations) { - pob_ref n = pob_map.first; + pob *n = pob_map.first; double expand_time = n->get_expand_time(depth); if (expand_time > 0) { a = true; @@ -131,7 +133,7 @@ namespace spacer { "\",\"absolute_time\":\"" << std::setprecision(2) << expand_time << "\",\"predicate\":\"" << n->pt().head()->get_name() << "\",\"expr_id\":\"" << n->post()->get_id() << - "\",\"pob_id\":\"" << i << + "\",\"pob_id\":\"" << pob_id << "\",\"depth\":\"" << depth << "\",\"expr\":" << pob_expr.str() << "}"; if (n->parent()) { @@ -140,6 +142,7 @@ namespace spacer { "\",\"to\":\"" << depth << n << "\"}"; } } + pob_id++; } if (!a) { break; diff --git a/src/muz/spacer/spacer_json.h b/src/muz/spacer/spacer_json.h index dc411f67c..c75110371 100644 --- a/src/muz/spacer/spacer_json.h +++ b/src/muz/spacer/spacer_json.h @@ -39,8 +39,6 @@ namespace spacer { class pob; - typedef ref pob_ref; - std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m); std::ostream &json_marshal(std::ostream &out, lemma *l); @@ -50,14 +48,14 @@ namespace spacer { class json_marshaller { context *m_ctx; - std::map> m_relations; + std::map> m_relations; public: json_marshaller(context *ctx) : m_ctx(ctx) {} - void pob_blocked_by_lemma_eh(pob *p, lemma *l); + void register_lemma(lemma *l); - void new_pob_eh(pob *p); + void register_pob(pob *p); std::ostream &marshal(std::ostream &out) const; }; From 247c570e6b35e5ac8dc680e71eaa2ad0a8dae308 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Mon, 27 Nov 2017 16:46:14 +0100 Subject: [PATCH 070/364] Debug sanity check in spacer_context Triggered by bugs in hypothesis remover only sanitycheck lemmas in debug-mode --- src/muz/spacer/spacer_context.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 75a9d7d81..f700ce83e 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -3133,7 +3133,12 @@ lbool context::expand_node(pob& n) checkpoint (); (*m_lemma_generalizers[i])(lemma); } + DEBUG_CODE( + lemma_sanity_checker sanity_checker(*this); + sanity_checker(lemma); + ); + TRACE("spacer", tout << "invariant state: " << (is_infty_level(lemma->level())?"(inductive)":"") << mk_pp(lemma->get_expr(), m) << "\n";); From 10106e8e125e011f408b91f7b45997057a5ab144 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 09:43:01 -0700 Subject: [PATCH 071/364] Minor fixes to ast_pp_dot --- src/ast/ast_pp_dot.cpp | 3 ++- src/ast/ast_pp_dot.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ast/ast_pp_dot.cpp b/src/ast/ast_pp_dot.cpp index 1dfbe9aae..47da4d4f4 100644 --- a/src/ast/ast_pp_dot.cpp +++ b/src/ast/ast_pp_dot.cpp @@ -9,7 +9,8 @@ Abstract: Pretty-printer for proofs in Graphviz format #include "ast/ast_pp_dot.h" // string escaping for DOT -std::string escape_dot(std::string const & s) { +std::string escape_dot(const std::string &s) +{ std::string res; res.reserve(s.size()); // preallocate for (auto c : s) { diff --git a/src/ast/ast_pp_dot.h b/src/ast/ast_pp_dot.h index 537754e83..d233c4be1 100644 --- a/src/ast/ast_pp_dot.h +++ b/src/ast/ast_pp_dot.h @@ -4,10 +4,11 @@ Abstract: Pretty-printer for proofs in Graphviz format --*/ -#pragma once +#ifndef _AST_PP_DOT_ +#define _AST_PP_DOT_ #include -#include "ast_pp.h" +#include "ast/ast_pp.h" class ast_pp_dot { ast_manager & m_manager; @@ -21,4 +22,8 @@ class ast_pp_dot { ast_manager & get_manager() const { return m_manager; } }; -std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p); \ No newline at end of file +std::string escape_dot(std::string const & s); + +std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p); + +#endif /* AST_PP_DOT */ From 56114a5f6d34e8b49fd08dab6d8dc591baff5f4d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 09:43:31 -0700 Subject: [PATCH 072/364] Refactor iuc_proof as a separate class This also adds DOT printing support to interpolating proofs (color for different parts) iuc_proof is a proof used for IUC computation --- src/muz/spacer/CMakeLists.txt | 1 + src/muz/spacer/spacer_itp_solver.cpp | 46 +- src/muz/spacer/spacer_iuc_proof.cpp | 235 ++++++++ src/muz/spacer/spacer_iuc_proof.h | 65 +++ src/muz/spacer/spacer_proof_utils.cpp | 535 +++++++++++++++++++ src/muz/spacer/spacer_proof_utils.h | 53 ++ src/muz/spacer/spacer_unsat_core_learner.cpp | 318 +---------- src/muz/spacer/spacer_unsat_core_learner.h | 37 +- src/muz/spacer/spacer_unsat_core_plugin.cpp | 49 +- 9 files changed, 960 insertions(+), 379 deletions(-) create mode 100644 src/muz/spacer/spacer_iuc_proof.cpp create mode 100644 src/muz/spacer/spacer_iuc_proof.h create mode 100644 src/muz/spacer/spacer_proof_utils.cpp create mode 100644 src/muz/spacer/spacer_proof_utils.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 50e0c9382..310d9a942 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -25,6 +25,7 @@ z3_add_component(spacer spacer_quant_generalizer.cpp spacer_callback.cpp spacer_json.cpp + spacer_iuc_proof.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index 9cccdf43c..07c5f5871 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -23,6 +23,7 @@ Notes: #include"ast/rewriter/expr_replacer.h" #include"muz/spacer/spacer_unsat_core_learner.h" #include"muz/spacer/spacer_unsat_core_plugin.h" +#include "muz/spacer/spacer_iuc_proof.h" namespace spacer { void itp_solver::push () @@ -261,11 +262,11 @@ void itp_solver::get_itp_core (expr_ref_vector &core) B.insert (a); } - proof_ref pr(m); - pr = get_proof (); - if (m_iuc == 0) { + proof_ref pr(m); + pr = get_proof (); + // old code farkas_learner learner_old; learner_old.set_split_literals(m_split_literals); @@ -277,7 +278,19 @@ void itp_solver::get_itp_core (expr_ref_vector &core) else { // new code - unsat_core_learner learner(m, m_print_farkas_stats, m_iuc_debug_proof); + // preprocess proof in order to get a proof which is better suited for unsat-core-extraction + proof_ref pr(get_proof(), m); + + spacer::reduce_hypotheses(pr); + STRACE("spacer.unsat_core_learner", + verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n"; + ); + + // construct proof object with contains partition information + iuc_proof iuc_pr(m, get_proof(), B); + + // configure learner + unsat_core_learner learner(m, iuc_pr, m_print_farkas_stats, m_iuc_debug_proof); if (m_iuc_arith == 0 || m_iuc_arith > 3) { @@ -311,31 +324,12 @@ void itp_solver::get_itp_core (expr_ref_vector &core) learner.register_plugin(plugin_lemma); } - learner.compute_unsat_core(pr, B, core); + // compute interpolating unsat core + learner.compute_unsat_core(core); + // postprocessing, TODO: elim_proxies should be done inside iuc_proof elim_proxies (core); simplify_bounds (core); // XXX potentially redundant - -// // debug -// expr_ref_vector core2(m); -// unsat_core_learner learner2(m); -// -// unsat_core_plugin_farkas_lemma* plugin_farkas_lemma2 = alloc(unsat_core_plugin_farkas_lemma, learner2, m_split_literals); -// learner2.register_plugin(plugin_farkas_lemma2); -// unsat_core_plugin_lemma* plugin_lemma2 = alloc(unsat_core_plugin_lemma, learner2); -// learner2.register_plugin(plugin_lemma2); -// learner2.compute_unsat_core(pr, B, core2); -// -// elim_proxies (core2); -// simplify_bounds (core2); -// -// IF_VERBOSE(2, -// verbose_stream () << "Itp Core:\n" -// << mk_pp (mk_and (core), m) << "\n";); -// IF_VERBOSE(2, -// verbose_stream () << "Itp Core2:\n" -// << mk_pp (mk_and (core2), m) << "\n";); - //SASSERT(mk_and (core) == mk_and (core2)); } IF_VERBOSE(2, diff --git a/src/muz/spacer/spacer_iuc_proof.cpp b/src/muz/spacer/spacer_iuc_proof.cpp new file mode 100644 index 000000000..889f06af2 --- /dev/null +++ b/src/muz/spacer/spacer_iuc_proof.cpp @@ -0,0 +1,235 @@ + + +#include "muz/spacer/spacer_iuc_proof.h" +#include "ast/for_each_expr.h" +#include "ast/array_decl_plugin.h" +#include "muz/spacer/spacer_proof_utils.h" + +namespace spacer { + + /* + * ==================================== + * init + * ==================================== + */ + iuc_proof::iuc_proof(ast_manager& m, proof* pr, expr_set& b_conjuncts) : m(m), m_pr(pr,m) + { + // init A-marks and B-marks + collect_symbols_b(b_conjuncts); + compute_marks(b_conjuncts); + } + + proof* iuc_proof::get() + { + return m_pr.get(); + } + + /* + * ==================================== + * methods for computing symbol colors + * ==================================== + */ + class collect_pure_proc { + func_decl_set& m_symbs; + public: + collect_pure_proc(func_decl_set& s):m_symbs(s) {} + + void operator()(app* a) { + if (a->get_family_id() == null_family_id) { + m_symbs.insert(a->get_decl()); + } + } + void operator()(var*) {} + void operator()(quantifier*) {} + }; + + void iuc_proof::collect_symbols_b(expr_set& b_conjuncts) + { + expr_mark visited; + collect_pure_proc proc(m_symbols_b); + for (expr_set::iterator it = b_conjuncts.begin(); it != b_conjuncts.end(); ++it) + { + for_each_expr(proc, visited, *it); + } + } + + class is_pure_expr_proc { + func_decl_set const& m_symbs; + array_util m_au; + public: + struct non_pure {}; + + is_pure_expr_proc(func_decl_set const& s, ast_manager& m): + m_symbs(s), + m_au (m) + {} + + void operator()(app* a) { + if (a->get_family_id() == null_family_id) { + if (!m_symbs.contains(a->get_decl())) { + throw non_pure(); + } + } + else if (a->get_family_id () == m_au.get_family_id () && + a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) { + throw non_pure(); + } + } + void operator()(var*) {} + void operator()(quantifier*) {} + }; + + // requires that m_symbols_b has already been computed, which is done during initialization. + bool iuc_proof::only_contains_symbols_b(expr* expr) const + { + is_pure_expr_proc proc(m_symbols_b, m); + try { + for_each_expr(proc, expr); + } + catch (is_pure_expr_proc::non_pure) + { + return false; + } + return true; + } + + /* + * ==================================== + * methods for computing which premises + * have been used to derive the conclusions + * ==================================== + */ + + void iuc_proof::compute_marks(expr_set& b_conjuncts) + { + ProofIteratorPostOrder it(m_pr, m); + while (it.hasNext()) + { + proof* currentNode = it.next(); + + if (m.get_num_parents(currentNode) == 0) + { + switch(currentNode->get_decl_kind()) + { + + case PR_ASSERTED: // currentNode is an axiom + { + if (b_conjuncts.contains(m.get_fact(currentNode))) + { + m_b_mark.mark(currentNode, true); + } + else + { + m_a_mark.mark(currentNode, true); + } + break; + } + // currentNode is a hypothesis: + case PR_HYPOTHESIS: + { + m_h_mark.mark(currentNode, true); + break; + } + default: + { + break; + } + } + } + else + { + // collect from parents whether derivation of current node contains A-axioms, B-axioms and hypothesis + bool need_to_mark_a = false; + bool need_to_mark_b = false; + bool need_to_mark_h = false; + + for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) + { + SASSERT(m.is_proof(currentNode->get_arg(i))); + proof* premise = to_app(currentNode->get_arg(i)); + + need_to_mark_a = need_to_mark_a || m_a_mark.is_marked(premise); + need_to_mark_b = need_to_mark_b || m_b_mark.is_marked(premise); + need_to_mark_h = need_to_mark_h || m_h_mark.is_marked(premise); + } + + // if current node is application of lemma, we know that all hypothesis are removed + if(currentNode->get_decl_kind() == PR_LEMMA) + { + need_to_mark_h = false; + } + + // save results + m_a_mark.mark(currentNode, need_to_mark_a); + m_b_mark.mark(currentNode, need_to_mark_b); + m_h_mark.mark(currentNode, need_to_mark_h); + } + } + } + + bool iuc_proof::is_a_marked(proof* p) + { + return m_a_mark.is_marked(p); + } + bool iuc_proof::is_b_marked(proof* p) + { + return m_b_mark.is_marked(p); + } + bool iuc_proof::is_h_marked(proof* p) + { + return m_h_mark.is_marked(p); + } + + /* + * ==================================== + * methods for dot printing + * ==================================== + */ + void iuc_proof::pp_dot() + { + pp_proof_dot(m, m_pr, this); + } + + /* + * ==================================== + * statistics + * ==================================== + */ + + void iuc_proof::print_farkas_stats() + { + unsigned farkas_counter = 0; + unsigned farkas_counter2 = 0; + + ProofIteratorPostOrder it3(m_pr, m); + while (it3.hasNext()) + { + proof* currentNode = it3.next(); + + // if node is theory lemma + if (is_farkas_lemma(m, currentNode)) + { + farkas_counter++; + + // check whether farkas lemma is to be interpolated (could potentially miss farkas lemmas, which are interpolated, because we potentially don't want to use the lowest cut) + bool has_blue_nonred_parent = false; + for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) + { + proof* premise = to_app(currentNode->get_arg(i)); + if (!is_a_marked(premise) && is_b_marked(premise)) + { + has_blue_nonred_parent = true; + break; + } + } + if (has_blue_nonred_parent && is_a_marked(currentNode)) + { + SASSERT(is_b_marked(currentNode)); + farkas_counter2++; + } + } + } + + verbose_stream() << "\nThis proof contains " << farkas_counter << " Farkas lemmas. " << farkas_counter2 << " Farkas lemmas participate in the lowest cut\n"; + } +} diff --git a/src/muz/spacer/spacer_iuc_proof.h b/src/muz/spacer/spacer_iuc_proof.h new file mode 100644 index 000000000..205648109 --- /dev/null +++ b/src/muz/spacer/spacer_iuc_proof.h @@ -0,0 +1,65 @@ +#ifndef IUC_PROOF_H_ +#define IUC_PROOF_H_ + +#include "ast/ast.h" + +namespace spacer { + typedef obj_hashtable expr_set; + typedef obj_hashtable func_decl_set; + + /* + * an iuc_proof is a proof together with information of the coloring of the axioms. + */ + class iuc_proof + { + public: + + iuc_proof(ast_manager& m, proof* pr, expr_set& b_conjuncts); + + proof* get(); + + /* + * returns whether symbol contains only symbols which occur in B. + */ + bool only_contains_symbols_b(expr* expr) const; + + bool is_a_marked(proof* p); + bool is_b_marked(proof* p); + bool is_h_marked(proof* p); + + bool is_b_pure (proof *p) + {return !is_h_marked (p) && only_contains_symbols_b (m.get_fact (p));} + + /* + * print dot-representation to file proof.dot + * use Graphviz's dot with option -Tpdf to convert dot-representation into pdf + */ + void pp_dot(); + + void print_farkas_stats(); + private: + ast_manager& m; + proof_ref m_pr; + + ast_mark m_a_mark; + ast_mark m_b_mark; + ast_mark m_h_mark; + + func_decl_set m_symbols_b; // symbols, which occur in any b-asserted formula + + // collect symbols occuring in B + void collect_symbols_b(expr_set& b_conjuncts); + + // compute for each formula of the proof whether it derives from A and whether it derives from B + void compute_marks(expr_set& b_conjuncts); + + void pp_dot_to_stream(std::ofstream& dotstream); + std::string escape_dot(const std::string &s); + + void post_process_dot(std::string dot_filepath, std::ofstream& dotstream); + }; + + +} + +#endif /* IUC_PROOF_H_ */ diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp new file mode 100644 index 000000000..ba102e20e --- /dev/null +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -0,0 +1,535 @@ +/*++ +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + spacer_proof_utils.cpp + +Abstract: + Utilities to traverse and manipulate proofs + +Author: + Bernhard Gleiss + Arie Gurfinkel + +Revision History: + +--*/ + +#include "muz/spacer/spacer_proof_utils.h" +#include "ast/ast_util.h" +#include "ast/ast_pp.h" + +#include "ast/proof_checker/proof_checker.h" +#include +#include "params.h" +#include "muz/spacer/spacer_iuc_proof.h" + +namespace spacer { + + /* + * ==================================== + * methods for proof traversal + * ==================================== + */ +ProofIteratorPostOrder::ProofIteratorPostOrder(proof* root, ast_manager& manager) : m(manager) +{m_todo.push_back(root);} + +bool ProofIteratorPostOrder::hasNext() +{return !m_todo.empty();} + +/* + * iterative post-order depth-first search (DFS) through the proof DAG + */ +proof* ProofIteratorPostOrder::next() +{ + while (!m_todo.empty()) { + proof* currentNode = m_todo.back(); + + // if we haven't already visited the current unit + if (!m_visited.is_marked(currentNode)) { + bool existsUnvisitedParent = false; + + // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result + // for currentProof now, but wait until those unprocessed premises are processed. + for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { + SASSERT(m.is_proof(currentNode->get_arg(i))); + proof* premise = to_app(currentNode->get_arg(i)); + + // if we haven't visited the current premise yet + if (!m_visited.is_marked(premise)) { + // add it to the stack + m_todo.push_back(premise); + existsUnvisitedParent = true; + } + } + + // if we already visited all parent-inferences, we can visit the inference too + if (!existsUnvisitedParent) { + m_visited.mark(currentNode, true); + m_todo.pop_back(); + return currentNode; + } + } else { + m_todo.pop_back(); + } + } + // we have already iterated through all inferences + return NULL; +} + + /* + * ==================================== + * methods for dot printing + * ==================================== + */ + void pp_proof_dot_to_stream(ast_manager& m, std::ofstream& dotstream, proof* pr, iuc_proof* iuc_pr = nullptr); + std::string escape_dot(const std::string &s); + void pp_proof_post_process_dot(std::string dot_filepath, std::ofstream &dotstream); + + void pp_proof_dot(ast_manager& m, proof* pr, iuc_proof* iuc_pr) + { + // open temporary dot-file + std::string dotfile_path = "proof.dot"; + std::ofstream dotstream(dotfile_path); + + // dump dot representation to stream + pp_proof_dot_to_stream(m, dotstream, pr, iuc_pr); + + // post process dot-file, TODO: factor this out to a different place + pp_proof_post_process_dot(dotfile_path,dotstream); + } + + void pp_proof_dot_to_stream(ast_manager& m, std::ofstream& dotstream, proof* pr, iuc_proof* iuc_pr) + { + dotstream << "digraph proof { \n"; + std::unordered_map id_to_small_id; + unsigned counter = 0; + + ProofIteratorPostOrder it2(pr, m); + while (it2.hasNext()) + { + proof* currentNode = it2.next(); + + SASSERT(id_to_small_id.find(currentNode->get_id()) == id_to_small_id.end()); + id_to_small_id.insert(std::make_pair(currentNode->get_id(), counter)); + + std::string color = "white"; + if (iuc_pr != nullptr) + { + if (iuc_pr->is_a_marked(currentNode) && !iuc_pr->is_b_marked(currentNode)) + { + color = "red"; + } + else if(iuc_pr->is_b_marked(currentNode) && !iuc_pr->is_a_marked(currentNode)) + { + color = "blue"; + } + else if(iuc_pr->is_b_marked(currentNode) && iuc_pr->is_a_marked(currentNode)) + { + color = "purple"; + } + } + + // compute label + params_ref p; + p.set_uint("max_depth", 4294967295u); + p.set_uint("min_alias_size", 4294967295u); + + std::ostringstream label_ostream; + label_ostream << mk_pp(m.get_fact(currentNode),m,p) << "\n"; + std::string label = escape_dot(label_ostream.str()); + + // compute edge-label + std::string edge_label = ""; + if (m.get_num_parents(currentNode) == 0) + { + switch (currentNode->get_decl_kind()) + { + case PR_ASSERTED: + edge_label = "asserted:"; + break; + case PR_HYPOTHESIS: + edge_label = "hyp:"; + color = "grey"; + break; + default: + if (currentNode->get_decl_kind() == PR_TH_LEMMA) + { + edge_label = "th_axiom:"; + func_decl* d = currentNode->get_decl(); + symbol sym; + if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step + d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", + d->get_parameter(1).is_symbol(sym) && sym == "farkas") + { + edge_label = "th_axiom(farkas):"; + } + } + else + { + edge_label = "unknown axiom-type:"; + break; + } + } + } + else + { + if (currentNode->get_decl_kind() == PR_LEMMA) + { + edge_label = "lemma:"; + } + else if (currentNode->get_decl_kind() == PR_TH_LEMMA) + { + func_decl* d = currentNode->get_decl(); + symbol sym; + if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step + d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", + d->get_parameter(1).is_symbol(sym) && sym == "farkas") + { + edge_label = "th_lemma(farkas):"; + } + else + { + edge_label = "th_lemma(other):"; + } + } + } + + // generate entry for node in dot-file + dotstream << "node_" << counter << " " + << "[" + << "shape=box,style=\"filled\"," + << "label=\"" << edge_label << " " << label << "\", " + << "fillcolor=\"" << color << "\"" + << "]\n"; + + // add entry for each edge to that node + for (unsigned i = m.get_num_parents(currentNode); i > 0 ; --i) + { + proof* premise = to_app(currentNode->get_arg(i-1)); + unsigned premise_small_id = id_to_small_id[premise->get_id()]; + dotstream << "node_" << premise_small_id + << " -> " + << "node_" << counter + << ";\n"; + } + + ++counter; + } + dotstream << "\n}" << std::endl; + } + + std::string escape_dot(const std::string &s) + { + std::string res; + res.reserve(s.size()); // preallocate + for (auto c : s) { + if (c == '\n') + res.append("\\l"); + else + res.push_back(c); + } + return res; + } + + void pp_proof_post_process_dot(std::string dot_filepath, std::ofstream &dotstream) + { + // replace variables in the dotfiles with nicer descriptions (hack: hard coded replacement for now) + std::vector > predicates; + std::vector l1 = {"L1","i","n","A"}; + predicates.push_back(l1); + std::vector l2 = {"L2","j","m","B"}; + predicates.push_back(l2); + + for(auto& predicate : predicates) + { + std::string predicate_name = predicate[0]; + for (unsigned i=0; i+1 < predicate.size(); ++i) + { + std::string new_name = predicate[i+1]; + std::string substring0 = predicate_name + "_" + std::to_string(i) + "_0"; + std::string substringN = predicate_name + "_" + std::to_string(i) + "_n"; + + std::string command0 = "sed -i '.bak' 's/" + substring0 + "/" + new_name + "/g' " + dot_filepath; + verbose_stream() << command0 << std::endl; + system(command0.c_str()); + + std::string commandN = "sed -i '.bak' s/" + substringN + "/" + new_name + "\\'" + "/g " + dot_filepath; + verbose_stream() << commandN << std::endl; + system(commandN.c_str()); + } + } + + verbose_stream() << "end of postprocessing"; + } + + /* + * ==================================== + * methods for reducing hypothesis + * ==================================== + */ + +class reduce_hypotheses { + ast_manager &m; + // tracking all created expressions + expr_ref_vector m_pinned; + + // cache for the transformation + obj_map m_cache; + + // map from unit literals to their hypotheses-free derivations + obj_map m_units; + + // -- all hypotheses in the the proof + obj_hashtable m_hyps; + + // marks hypothetical proofs + ast_mark m_hypmark; + + + // stack + ptr_vector m_todo; + + void reset() + { + m_cache.reset(); + m_units.reset(); + m_hyps.reset(); + m_hypmark.reset(); + m_pinned.reset(); + } + + bool compute_mark1(proof *pr) + { + bool hyp_mark = false; + // lemmas clear all hypotheses + if (!m.is_lemma(pr)) { + for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) { + if (m_hypmark.is_marked(m.get_parent(pr, i))) { + hyp_mark = true; + break; + } + } + } + m_hypmark.mark(pr, hyp_mark); + return hyp_mark; + } + + void compute_marks(proof* pr) + { + proof *p; + ProofIteratorPostOrder pit(pr, m); + while (pit.hasNext()) { + p = pit.next(); + if (m.is_hypothesis(p)) { + m_hypmark.mark(p, true); + m_hyps.insert(m.get_fact(p)); + } else { + bool hyp_mark = compute_mark1(p); + // collect units that are hyp-free and are used as hypotheses somewhere + if (!hyp_mark && m.has_fact(p) && m_hyps.contains(m.get_fact(p))) + { m_units.insert(m.get_fact(p), p); } + } + } + } + void find_units(proof *pr) + { + // optional. not implemented yet. + } + + void reduce(proof* pf, proof_ref &out) + { + proof *res = NULL; + + m_todo.reset(); + m_todo.push_back(pf); + ptr_buffer args; + bool dirty = false; + + while (!m_todo.empty()) { + proof *p, *tmp, *pp; + unsigned todo_sz; + + p = m_todo.back(); + if (m_cache.find(p, tmp)) { + res = tmp; + m_todo.pop_back(); + continue; + } + + dirty = false; + args.reset(); + todo_sz = m_todo.size(); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + pp = m.get_parent(p, i); + if (m_cache.find(pp, tmp)) { + args.push_back(tmp); + dirty = dirty || pp != tmp; + } else { + m_todo.push_back(pp); + } + } + + if (todo_sz < m_todo.size()) { continue; } + else { m_todo.pop_back(); } + + if (m.is_hypothesis(p)) { + // hyp: replace by a corresponding unit + if (m_units.find(m.get_fact(p), tmp)) { + res = tmp; + } else { res = p; } + } + + else if (!dirty) { res = p; } + + else if (m.is_lemma(p)) { + //lemma: reduce the premise; remove reduced consequences from conclusion + SASSERT(args.size() == 1); + res = mk_lemma_core(args.get(0), m.get_fact(p)); + compute_mark1(res); + } else if (m.is_unit_resolution(p)) { + // unit: reduce untis; reduce the first premise; rebuild unit resolution + res = mk_unit_resolution_core(args.size(), args.c_ptr()); + compute_mark1(res); + } else { + // other: reduce all premises; reapply + if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } + SASSERT(p->get_decl()->get_arity() == args.size()); + res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + compute_mark1(res); + } + + SASSERT(res); + m_cache.insert(p, res); + + if (m.has_fact(res) && m.is_false(m.get_fact(res))) { break; } + } + + out = res; + } + + // returns true if (hypothesis (not a)) would be reduced + bool is_reduced(expr *a) + { + expr_ref e(m); + if (m.is_not(a)) { e = to_app(a)->get_arg(0); } + else { e = m.mk_not(a); } + + return m_units.contains(e); + } + proof *mk_lemma_core(proof *pf, expr *fact) + { + ptr_buffer args; + expr_ref lemma(m); + + if (m.is_or(fact)) { + for (unsigned i = 0, sz = to_app(fact)->get_num_args(); i < sz; ++i) { + expr *a = to_app(fact)->get_arg(i); + if (!is_reduced(a)) + { args.push_back(a); } + } + } else if (!is_reduced(fact)) + { args.push_back(fact); } + + + if (args.size() == 0) { return pf; } + else if (args.size() == 1) { + lemma = args.get(0); + } else { + lemma = m.mk_or(args.size(), args.c_ptr()); + } + proof* res = m.mk_lemma(pf, lemma); + m_pinned.push_back(res); + + // XXX this is wrong because lemma is a proof and m_hyps only + // XXX contains expressions. + // XXX Not sure this is ever needed. + if (m_hyps.contains(lemma)) { + m_units.insert(lemma, res); + } + return res; + } + + proof *mk_unit_resolution_core(unsigned num_args, proof* const *args) + { + + ptr_buffer pf_args; + pf_args.push_back(args [0]); + + app *cls_fact = to_app(m.get_fact(args[0])); + ptr_buffer cls; + if (m.is_or(cls_fact)) { + for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) + { cls.push_back(cls_fact->get_arg(i)); } + } else { cls.push_back(cls_fact); } + + // construct new resolvent + ptr_buffer new_fact_cls; + bool found; + // XXX quadratic + for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { + found = false; + for (unsigned j = 1; j < num_args; ++j) { + if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { + found = true; + pf_args.push_back(args [j]); + break; + } + } + if (!found) { + new_fact_cls.push_back(cls.get(i)); + } + } + + SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size()); + expr_ref new_fact(m); + new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); + + // create new proof step + proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); + m_pinned.push_back(res); + return res; + } + + // reduce all units, if any unit reduces to false return true and put its proof into out + bool reduce_units(proof_ref &out) + { + proof_ref res(m); + for (auto entry : m_units) { + reduce(entry.get_value(), res); + if (m.is_false(m.get_fact(res))) { + out = res; + return true; + } + res.reset(); + } + return false; + } + + +public: + reduce_hypotheses(ast_manager &m) : m(m), m_pinned(m) {} + + + void operator()(proof_ref &pr) + { + compute_marks(pr); + if (!reduce_units(pr)) { + reduce(pr.get(), pr); + } + reset(); + } +}; +void reduce_hypotheses(proof_ref &pr) +{ + ast_manager &m = pr.get_manager(); + class reduce_hypotheses hypred(m); + hypred(pr); + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(pr, side)); + ); +} +} diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h new file mode 100644 index 000000000..93b512c8f --- /dev/null +++ b/src/muz/spacer/spacer_proof_utils.h @@ -0,0 +1,53 @@ +/*++ +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + spacer_proof_utils.cpp + +Abstract: + Utilities to traverse and manipulate proofs + +Author: + Bernhard Gleiss + Arie Gurfinkel + +Revision History: + +--*/ + +#ifndef _SPACER_PROOF_UTILS_H_ +#define _SPACER_PROOF_UTILS_H_ +#include "ast/ast.h" + +namespace spacer { + + bool is_farkas_lemma(ast_manager& m, proof* pr); +/* + * iterator, which traverses the proof in depth-first post-order. + */ +class ProofIteratorPostOrder { +public: + ProofIteratorPostOrder(proof* refutation, ast_manager& manager); + bool hasNext(); + proof* next(); + +private: + ptr_vector m_todo; + ast_mark m_visited; // the proof nodes we have already visited + + ast_manager& m; +}; + + /* + * prints the proof pr in dot representation to the file proof.dot + * if iuc_pr is not nullptr, then it is queried for coloring partitions + */ + class iuc_proof; + void pp_proof_dot(ast_manager& m, proof* pr, iuc_proof* iuc_pr = nullptr); + + + +void reduce_hypotheses(proof_ref &pr); +} +#endif diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index 12b2a5614..fa5e17239 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -19,16 +19,18 @@ Revision History: #include "muz/spacer/spacer_unsat_core_learner.h" #include "muz/spacer/spacer_unsat_core_plugin.h" +#include "muz/spacer/spacer_iuc_proof.h" #include "ast/for_each_expr.h" +#include "muz/spacer/spacer_util.h" + + namespace spacer { - unsat_core_learner::~unsat_core_learner() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); - } void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) @@ -36,60 +38,16 @@ void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) m_plugins.push_back(plugin); } -void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, expr_ref_vector& unsat_core) +void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { - // transform proof in order to get a proof which is better suited for unsat-core-extraction - proof_ref pr(root, m); - - reduce_hypotheses(pr); - STRACE("spacer.unsat_core_learner", - verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n"; - ); - - // compute symbols occurring in B - collect_symbols_b(asserted_b); - // traverse proof - proof_post_order it(root, m); + proof_post_order it(m_pr.get(), m); while (it.hasNext()) { proof* currentNode = it.next(); - if (m.get_num_parents(currentNode) == 0) + if (m.get_num_parents(currentNode) > 0) { - switch(currentNode->get_decl_kind()) - { - - case PR_ASSERTED: // currentNode is an axiom - { - if (asserted_b.contains(m.get_fact(currentNode))) - { - m_b_mark.mark(currentNode, true); - } - else - { - m_a_mark.mark(currentNode, true); - } - break; - } - // currentNode is a hypothesis: - case PR_HYPOTHESIS: - { - m_h_mark.mark(currentNode, true); - break; - } - default: - { - break; - } - } - } - else - { - // collect from parents whether derivation of current node contains A-axioms, B-axioms and hypothesis - bool need_to_mark_a = false; - bool need_to_mark_b = false; - bool need_to_mark_h = false; bool need_to_mark_closed = true; for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) @@ -97,31 +55,18 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e SASSERT(m.is_proof(currentNode->get_arg(i))); proof* premise = to_app(currentNode->get_arg(i)); - need_to_mark_a = need_to_mark_a || m_a_mark.is_marked(premise); - need_to_mark_b = need_to_mark_b || m_b_mark.is_marked(premise); - need_to_mark_h = need_to_mark_h || m_h_mark.is_marked(premise); - need_to_mark_closed = need_to_mark_closed && (!m_b_mark.is_marked(premise) || m_closed.is_marked(premise)); + need_to_mark_closed = need_to_mark_closed && (!m_pr.is_b_marked(premise) || m_closed.is_marked(premise)); } - // if current node is application of lemma, we know that all hypothesis are removed - if(currentNode->get_decl_kind() == PR_LEMMA) - { - need_to_mark_h = false; - } - - // save results - m_a_mark.mark(currentNode, need_to_mark_a); - m_b_mark.mark(currentNode, need_to_mark_b); - m_h_mark.mark(currentNode, need_to_mark_h); + // save result m_closed.mark(currentNode, need_to_mark_closed); } // we have now collected all necessary information, so we can visit the node // if the node mixes A-reasoning and B-reasoning and contains non-closed premises - if (m_a_mark.is_marked(currentNode) && m_b_mark.is_marked(currentNode) && !m_closed.is_marked(currentNode)) + if (m_pr.is_a_marked(currentNode) && m_pr.is_b_marked(currentNode) && !m_closed.is_marked(currentNode)) { compute_partial_core(currentNode); // then we need to compute a partial core - // SASSERT(!(m_a_mark.is_marked(currentNode) && m_b_mark.is_marked(currentNode)) || m_closed.is_marked(currentNode)); TODO: doesn't hold anymore if we do the mincut-thing! } } @@ -133,170 +78,14 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e // count both number of all Farkas lemmas and number of Farkas lemmas in the cut if (m_print_farkas_stats) { - unsigned farkas_counter = 0; - unsigned farkas_counter2 = 0; - - ProofIteratorPostOrder it3(root, m); - while (it3.hasNext()) - { - proof* currentNode = it3.next(); - - // if node is theory lemma - if (currentNode->get_decl_kind() == PR_TH_LEMMA) - { - func_decl* d = currentNode->get_decl(); - symbol sym; - // and theory lemma is Farkas lemma - if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas") - { - farkas_counter++; - - // check whether farkas lemma is to be interpolated (could potentially miss farkas lemmas, which are interpolated, because we potentially don't want to use the lowest cut) - bool has_no_mixed_parents = true; - for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) - { - proof* premise = to_app(currentNode->get_arg(i)); - if (is_a_marked(premise) && is_b_marked(premise)) - { - has_no_mixed_parents = false; + m_pr.print_farkas_stats(); } - } - if (has_no_mixed_parents && is_a_marked(currentNode) && is_b_marked(currentNode)) - { - farkas_counter2++; - } - - } - } - } - - verbose_stream() << "\nThis proof contains " << farkas_counter << " Farkas lemmas. " << farkas_counter2 << " Farkas lemmas participate in the lowest cut\n"; - } - + //TODO remove this if(m_iuc_debug_proof) { - // print proof for debugging - verbose_stream() << "Proof:\n"; - std::unordered_map id_to_small_id; - unsigned counter = 0; - - proof_post_order it2(root, m); - while (it2.hasNext()) - { - proof* currentNode = it2.next(); - - SASSERT(id_to_small_id.find(currentNode->get_id()) == id_to_small_id.end()); - id_to_small_id.insert(std::make_pair(currentNode->get_id(), counter)); - - verbose_stream() << counter << " "; - verbose_stream() << "["; - if (is_a_marked(currentNode)) - { - verbose_stream() << "a"; - } - if (is_b_marked(currentNode)) - { - verbose_stream() << "b"; - } - if (is_h_marked(currentNode)) - { - verbose_stream() << "h"; - } - if (is_closed(currentNode)) - { - verbose_stream() << "c"; - } - verbose_stream() << "] "; - - if (m.get_num_parents(currentNode) == 0) - { - switch (currentNode->get_decl_kind()) - { - case PR_ASSERTED: - verbose_stream() << "asserted"; - break; - case PR_HYPOTHESIS: - verbose_stream() << "hypothesis"; - break; - default: - if (currentNode->get_decl_kind() == PR_TH_LEMMA) - { - verbose_stream() << "th_axiom"; - func_decl* d = currentNode->get_decl(); - symbol sym; - if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas") - { - verbose_stream() << "(farkas)"; - } - } - else - { - verbose_stream() << "unknown axiom-type"; - break; - } - } - } - else - { - if (currentNode->get_decl_kind() == PR_LEMMA) - { - verbose_stream() << "lemma"; - } - else if (currentNode->get_decl_kind() == PR_TH_LEMMA) - { - verbose_stream() << "th_lemma"; - func_decl* d = currentNode->get_decl(); - symbol sym; - if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas") - { - verbose_stream() << "(farkas)"; - } - else - { - verbose_stream() << "(other)"; - } - } - else - { - verbose_stream() << "step"; - } - verbose_stream() << " from "; - for (unsigned i = m.get_num_parents(currentNode); i > 0 ; --i) - { - proof* premise = to_app(currentNode->get_arg(i)); - unsigned premise_small_id = id_to_small_id[premise->get_id()]; - if (i > 1) - { - verbose_stream() << premise_small_id << ", "; - } - else - { - verbose_stream() << premise_small_id; - } - - } - } -// if (currentNode->get_decl_kind() == PR_TH_LEMMA || (is_a_marked(currentNode) && is_b_marked(currentNode)) || is_h_marked(currentNode) || (!is_a_marked(currentNode) && !is_b_marked(currentNode))) - if (false) - { - verbose_stream() << "\n"; - } - else - { - verbose_stream() << ": " << mk_pp(m.get_fact(currentNode), m) << "\n"; - } - ++counter; - } } verbose_stream() << std::endl; - // move all lemmas into vector for (expr* const* it = m_unsat_core.begin(); it != m_unsat_core.end(); ++it) { @@ -322,19 +111,6 @@ void unsat_core_learner::finalize() } } - -bool unsat_core_learner::is_a_marked(proof* p) -{ - return m_a_mark.is_marked(p); -} -bool unsat_core_learner::is_b_marked(proof* p) -{ - return m_b_mark.is_marked(p); -} -bool unsat_core_learner::is_h_marked(proof* p) -{ - return m_h_mark.is_marked(p); -} bool unsat_core_learner::is_closed(proof*p) { return m_closed.is_marked(p); @@ -344,75 +120,13 @@ void unsat_core_learner::set_closed(proof* p, bool value) m_closed.mark(p, value); } - void unsat_core_learner::add_lemma_to_core(expr* lemma) +bool unsat_core_learner::is_b_open(proof *p) { - m_unsat_core.push_back(lemma); + return m_pr.is_b_marked(p) && !is_closed (p); } - -class collect_pure_proc { - func_decl_set& m_symbs; -public: - collect_pure_proc(func_decl_set& s):m_symbs(s) {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - m_symbs.insert(a->get_decl()); - } - } - void operator()(var*) {} - void operator()(quantifier*) {} -}; - -void unsat_core_learner::collect_symbols_b(const expr_set& axioms_b) +void unsat_core_learner::add_lemma_to_core(expr* lemma) { - expr_mark visited; - collect_pure_proc proc(m_symbols_b); - for (expr_set::iterator it = axioms_b.begin(); it != axioms_b.end(); ++it) - { - for_each_expr(proc, visited, *it); + m_unsat_core.push_back(lemma); } } - -class is_pure_expr_proc { - func_decl_set const& m_symbs; - array_util m_au; -public: - struct non_pure {}; - - is_pure_expr_proc(func_decl_set const& s, ast_manager& m): - m_symbs(s), - m_au (m) - {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - if (!m_symbs.contains(a->get_decl())) { - throw non_pure(); - } - } - else if (a->get_family_id () == m_au.get_family_id () && - a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) { - throw non_pure(); - } - } - void operator()(var*) {} - void operator()(quantifier*) {} -}; - -bool unsat_core_learner::only_contains_symbols_b(expr* expr) const -{ - is_pure_expr_proc proc(m_symbols_b, m); - try { - for_each_expr(proc, expr); - } - catch (is_pure_expr_proc::non_pure) - { - return false; - } - return true; -} - - - -} diff --git a/src/muz/spacer/spacer_unsat_core_learner.h b/src/muz/spacer/spacer_unsat_core_learner.h index 4b5ca981d..16b27f4ba 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.h +++ b/src/muz/spacer/spacer_unsat_core_learner.h @@ -20,21 +20,23 @@ Revision History: #include "ast/ast.h" #include "muz/spacer/spacer_util.h" -#include "ast/proofs/proof_utils.h" +#include "muz/spacer/spacer_proof_utils.h" namespace spacer { class unsat_core_plugin; + class iuc_proof; class unsat_core_learner { typedef obj_hashtable expr_set; public: - unsat_core_learner(ast_manager& m, bool print_farkas_stats = false, bool iuc_debug_proof = false) : m(m), m_unsat_core(m), m_print_farkas_stats(print_farkas_stats), m_iuc_debug_proof(iuc_debug_proof) {}; + unsat_core_learner(ast_manager& m, iuc_proof& pr, bool print_farkas_stats = false, bool iuc_debug_proof = false) : m(m), m_pr(pr), m_unsat_core(m), m_print_farkas_stats(print_farkas_stats), m_iuc_debug_proof(iuc_debug_proof) {}; virtual ~unsat_core_learner(); ast_manager& m; + iuc_proof& m_pr; /* * register a plugin for computation of partial unsat cores @@ -45,48 +47,29 @@ namespace spacer { /* * compute unsat core using the registered unsat-core-plugins */ - void compute_unsat_core(proof* root, expr_set& asserted_b, expr_ref_vector& unsat_core); + void compute_unsat_core(expr_ref_vector& unsat_core); /* * getter/setter methods for data structures exposed to plugins - * the following invariants can be assumed and need to be maintained by the plugins: - * - a node is a-marked iff it is derived using at least one asserted proof step from A. - * - a node is b-marked iff its derivation contains no asserted proof steps from A and - * no hypothesis (with the additional complication that lemmas conceptually remove hypothesis) - * - a node is h-marked, iff it is derived using at least one hypothesis + * the following invariant can be assumed and need to be maintained by the plugins: * - a node is closed, iff it has already been interpolated, i.e. its contribution is * already covered by the unsat-core. */ - bool is_a_marked(proof* p); - bool is_b_marked(proof* p); - bool is_h_marked(proof* p); + bool is_closed(proof* p); void set_closed(proof* p, bool value); + bool is_b_open (proof *p); + /* * adds a lemma to the unsat core */ void add_lemma_to_core(expr* lemma); - /* - * helper method, which can be used by plugins - * returns true iff all symbols of expr occur in some b-asserted formula. - * must only be called after a call to collect_symbols_b. - */ - bool only_contains_symbols_b(expr* expr) const; - bool is_b_pure (proof *p) - {return !is_h_marked (p) && only_contains_symbols_b (m.get_fact (p));} - bool is_b_open (proof *p) - { return is_b_marked (p) && !is_closed (p); } + private: ptr_vector m_plugins; - func_decl_set m_symbols_b; // symbols, which occur in any b-asserted formula - void collect_symbols_b(const expr_set& axioms_b); - - ast_mark m_a_mark; - ast_mark m_b_mark; - ast_mark m_h_mark; ast_mark m_closed; expr_ref_vector m_unsat_core; // collects the lemmas of the unsat-core, will at the end be inserted into unsat_core. diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index 0323fff0a..863023d5b 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -30,6 +30,7 @@ Revision History: #include "muz/spacer/spacer_matrix.h" #include "muz/spacer/spacer_unsat_core_plugin.h" #include "muz/spacer/spacer_unsat_core_learner.h" +#include "muz/spacer/spacer_iuc_proof.h" namespace spacer { @@ -37,8 +38,8 @@ namespace spacer void unsat_core_plugin_lemma::compute_partial_core(proof* step) { - SASSERT(m_learner.is_a_marked(step)); - SASSERT(m_learner.is_b_marked(step)); + SASSERT(m_learner.m_pr.is_a_marked(step)); + SASSERT(m_learner.m_pr.is_b_marked(step)); for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) { @@ -48,7 +49,7 @@ void unsat_core_plugin_lemma::compute_partial_core(proof* step) if (m_learner.is_b_open (premise)) { // by IH, premises that are AB marked are already closed - SASSERT(!m_learner.is_a_marked(premise)); + SASSERT(!m_learner.m_pr.is_a_marked(premise)); add_lowest_split_to_core(premise); } } @@ -75,13 +76,13 @@ void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const // the step is b-marked and not closed. // by I.H. the step must be already visited // so if it is also a-marked, it must be closed - SASSERT(m_learner.is_b_marked(pf)); - SASSERT(!m_learner.is_a_marked(pf)); + SASSERT(m_learner.m_pr.is_b_marked(pf)); + SASSERT(!m_learner.m_pr.is_a_marked(pf)); // the current step needs to be interpolated: expr* fact = m_learner.m.get_fact(pf); // if we trust the current step and we are able to use it - if (m_learner.is_b_pure (pf) && + if (m_learner.m_pr.is_b_pure (pf) && (m.is_asserted(pf) || is_literal(m, fact))) { // just add it to the core @@ -109,8 +110,8 @@ void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) { ast_manager &m = m_learner.m; - SASSERT(m_learner.is_a_marked(step)); - SASSERT(m_learner.is_b_marked(step)); + SASSERT(m_learner.m_pr.is_a_marked(step)); + SASSERT(m_learner.m_pr.is_b_marked(step)); // XXX this assertion should be true so there is no need to check for it SASSERT (!m_learner.is_closed (step)); func_decl* d = step->get_decl(); @@ -162,7 +163,7 @@ void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) rational coef; VERIFY(params[i].is_rational(coef)); - bool b_pure = m_learner.is_b_pure (prem); + bool b_pure = m_learner.m_pr.is_b_pure (prem); verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n"; } ); @@ -176,9 +177,9 @@ void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) if (m_learner.is_b_open (premise)) { - SASSERT(!m_learner.is_a_marked(premise)); + SASSERT(!m_learner.m_pr.is_a_marked(premise)); - if (m_learner.is_b_pure (step)) + if (m_learner.m_pr.is_b_pure (step)) { if (!m_use_constant_from_a) { @@ -287,8 +288,8 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vectorget_decl(); symbol sym; @@ -315,7 +316,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vectorget_arg(i))); proof * premise = m.get_parent (step, i); - if (m_learner.is_b_marked(premise) && !m_learner.is_closed(premise)) + if (m_learner.m_pr.is_b_marked(premise) && !m_learner.is_closed(premise)) { - SASSERT(!m_learner.is_a_marked(premise)); + SASSERT(!m_learner.m_pr.is_a_marked(premise)); - if (m_learner.only_contains_symbols_b(m_learner.m.get_fact(premise)) && !m_learner.is_h_marked(premise)) + if (m_learner.m_pr.only_contains_symbols_b(m_learner.m.get_fact(premise)) && !m_learner.m_pr.is_h_marked(premise)) { rational coefficient; VERIFY(params[i].is_rational(coefficient)); @@ -603,8 +604,8 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector todo; - SASSERT(m_learner.is_a_marked(step)); - SASSERT(m_learner.is_b_marked(step)); + SASSERT(m_learner.m_pr.is_a_marked(step)); + SASSERT(m_learner.m_pr.is_b_marked(step)); SASSERT(m.get_num_parents(step) > 0); SASSERT(!m_learner.is_closed(step)); todo.push_back(step); @@ -641,7 +642,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector Date: Mon, 27 Nov 2017 14:39:52 -0500 Subject: [PATCH 073/364] Fix several bugs in hyp_reducer - compute_marks didn't find all units - call to m.mk_unit_resolution expects that there is at least one unit - hyp-reduced proof wasn't used - bug in early termination - any hypothesis was replaced with the old derivation of the literal - handle the case of a single literal premise under hypothesis that is replaced by an empty clause under hypothesis --- src/muz/spacer/spacer_proof_utils.cpp | 84 +++++++++++++++++++++------ 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index ba102e20e..a8d00c142 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -326,10 +326,19 @@ class reduce_hypotheses { m_hypmark.mark(p, true); m_hyps.insert(m.get_fact(p)); } else { - bool hyp_mark = compute_mark1(p); + compute_mark1(p); + } + } + ProofIteratorPostOrder pit2(pr, m); + while (pit2.hasNext()) { + p = pit2.next(); + if (!m.is_hypothesis(p)) + { // collect units that are hyp-free and are used as hypotheses somewhere - if (!hyp_mark && m.has_fact(p) && m_hyps.contains(m.get_fact(p))) - { m_units.insert(m.get_fact(p), p); } + if (!m_hypmark.is_marked(p) && m.has_fact(p) && m_hyps.contains(m.get_fact(p))) + { + m_units.insert(m.get_fact(p), p); + } } } } @@ -348,7 +357,7 @@ class reduce_hypotheses { bool dirty = false; while (!m_todo.empty()) { - proof *p, *tmp, *pp; + proof *p, *tmp, *tmp2, *pp; unsigned todo_sz; p = m_todo.back(); @@ -377,7 +386,17 @@ class reduce_hypotheses { if (m.is_hypothesis(p)) { // hyp: replace by a corresponding unit if (m_units.find(m.get_fact(p), tmp)) { - res = tmp; + // if the transformed subproof ending in the unit has already been computed, use it + if (m_cache.find(tmp,tmp2)) + { + res = tmp2; + } + // otherwise first compute the transformed subproof ending in the unit + else + { + m_todo.push_back(tmp); + continue; + } } else { res = p; } } @@ -393,18 +412,33 @@ class reduce_hypotheses { res = mk_unit_resolution_core(args.size(), args.c_ptr()); compute_mark1(res); } else { - // other: reduce all premises; reapply - if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } - SASSERT(p->get_decl()->get_arity() == args.size()); - res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); - m_pinned.push_back(res); - compute_mark1(res); + // if any literal is false, we don't need a step + bool has_empty_clause_premise = false; + for (unsigned i = 0; i < args.size(); ++i) + { + if (m.is_false(m.get_fact(args[i]))) + { + has_empty_clause_premise = true; + res = args[i]; + } + } + + // otherwise: + if (!has_empty_clause_premise) + { + // other: reduce all premises; reapply + if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } + SASSERT(p->get_decl()->get_arity() == args.size()); + res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + compute_mark1(res); + } } SASSERT(res); m_cache.insert(p, res); - if (m.has_fact(res) && m.is_false(m.get_fact(res))) { break; } + if (!m_hypmark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) { break; } } out = res; @@ -458,6 +492,15 @@ class reduce_hypotheses { ptr_buffer pf_args; pf_args.push_back(args [0]); + // if any literal is false, we don't need a unit resolution step + for (unsigned i = 1; i < num_args; ++i) + { + if (m.is_false(m.get_fact(args[i]))) + { + return args[i]; + } + } + app *cls_fact = to_app(m.get_fact(args[0])); ptr_buffer cls; if (m.is_or(cls_fact)) { @@ -488,9 +531,16 @@ class reduce_hypotheses { new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); // create new proof step - proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); - m_pinned.push_back(res); - return res; + if (pf_args.size() == 1) // the only premise is the clause itself + { + return args[0]; + } + else + { + proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); + m_pinned.push_back(res); + return res; + } } // reduce all units, if any unit reduces to false return true and put its proof into out @@ -516,9 +566,7 @@ public: void operator()(proof_ref &pr) { compute_marks(pr); - if (!reduce_units(pr)) { - reduce(pr.get(), pr); - } + reduce(pr.get(), pr); reset(); } }; From df2eb771ef160aaa06719cb60e8ec41e86bfd30a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 09:55:04 -0700 Subject: [PATCH 074/364] Fix in spacer_itp_solver: use pr.get() instead of get_proof() --- src/muz/spacer/spacer_itp_solver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index 07c5f5871..f91cc6bb3 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -287,8 +287,8 @@ void itp_solver::get_itp_core (expr_ref_vector &core) ); // construct proof object with contains partition information - iuc_proof iuc_pr(m, get_proof(), B); - + iuc_proof iuc_pr(m, pr.get(), B); + // configure learner unsat_core_learner learner(m, iuc_pr, m_print_farkas_stats, m_iuc_debug_proof); From de31b070086a8524b310db1e5613a74353534963 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Fri, 1 Dec 2017 14:21:20 +0100 Subject: [PATCH 075/364] arith-theory-axiom reducer to handle arithmetic axioms --- src/muz/spacer/spacer_proof_utils.cpp | 311 +++++++++++++++++--------- src/muz/spacer/spacer_proof_utils.h | 66 +++++- 2 files changed, 270 insertions(+), 107 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index a8d00c142..81fb7286a 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -27,6 +27,38 @@ Revision History: namespace spacer { + // arith lemmas: second parameter specifies exact type of lemma, could be "farkas", "triangle-eq", "eq-propagate", "assign-bounds", maybe also something else + bool is_arith_lemma(ast_manager& m, proof* pr) + { + if (pr->get_decl_kind() == PR_TH_LEMMA) + { + func_decl* d = pr->get_decl(); + symbol sym; + if (d->get_num_parameters() >= 1 && + d->get_parameter(0).is_symbol(sym) && sym == "arith") + { + return true; + } + } + return false; + } + + bool is_farkas_lemma(ast_manager& m, proof* pr) + { + if (pr->get_decl_kind() == PR_TH_LEMMA) + { + func_decl* d = pr->get_decl(); + symbol sym; + if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step + d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", + d->get_parameter(1).is_symbol(sym) && sym == "farkas") + { + return true; + } + } + return false; + } + /* * ==================================== * methods for proof traversal @@ -153,24 +185,18 @@ proof* ProofIteratorPostOrder::next() edge_label = "hyp:"; color = "grey"; break; - default: - if (currentNode->get_decl_kind() == PR_TH_LEMMA) + case PR_TH_LEMMA: + if (is_farkas_lemma(m, currentNode)) { - edge_label = "th_axiom:"; - func_decl* d = currentNode->get_decl(); - symbol sym; - if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas") - { - edge_label = "th_axiom(farkas):"; - } + edge_label = "th_axiom(farkas):"; } else { - edge_label = "unknown axiom-type:"; - break; + edge_label = "th_axiom:"; } + break; + default: + edge_label = "unknown axiom-type:"; } } else @@ -266,32 +292,111 @@ proof* ProofIteratorPostOrder::next() /* * ==================================== - * methods for reducing hypothesis + * methods for transforming proofs * ==================================== */ -class reduce_hypotheses { - ast_manager &m; - // tracking all created expressions - expr_ref_vector m_pinned; + void theory_axiom_reducer::reset() + { + m_cache.reset(); + m_pinned.reset(); + } + + proof_ref theory_axiom_reducer::reduce(proof* pr) + { + ProofIteratorPostOrder pit(pr, m); + while (pit.hasNext()) + { + proof* p = pit.next(); + + if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) + { + // we have an arith-theory-axiom and want to get rid of it + // we need to replace the axiom with 1a) corresponding hypothesis', 1b) a theory lemma and a 1c) a lemma. Furthermore update datastructures + app *cls_fact = to_app(m.get_fact(p)); + ptr_buffer cls; + if (m.is_or(cls_fact)) { + for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) + { cls.push_back(cls_fact->get_arg(i)); } + } else { cls.push_back(cls_fact); } + + // 1a) create hypothesis' + ptr_buffer hyps; + for (unsigned i=0; i < cls.size(); ++i) + { + expr* hyp_fact = m.is_not(cls[i]) ? to_app(cls[i])->get_arg(0) : m.mk_not(cls[i]); + proof* hyp = m.mk_hypothesis(hyp_fact); + m_pinned.push_back(hyp); + hyps.push_back(hyp); + } + + // 1b) create farkas lemma: need to rebuild parameters since mk_th_lemma adds tid as first parameter + unsigned num_params = p->get_decl()->get_num_parameters(); + parameter const* params = p->get_decl()->get_parameters(); + vector parameters; + for (unsigned i = 1; i < num_params; ++i) { + parameters.push_back(params[i]); + } + + SASSERT(params[0].is_symbol()); + family_id tid = m.mk_family_id(params[0].get_symbol()); + SASSERT(tid != null_family_id); + + proof* th_lemma = m.mk_th_lemma(tid, m.mk_false(),hyps.size(), hyps.c_ptr(), num_params-1, parameters.c_ptr()); + SASSERT(is_arith_lemma(m, th_lemma)); + + // 1c) create lemma + proof* res = m.mk_lemma(th_lemma, cls_fact); + SASSERT(m.get_fact(res) == m.get_fact(p)); + m_pinned.push_back(res); + m_cache.insert(p, res); + } + else + { + bool dirty = false; // proof is dirty, if a subproof of one of its premises has been transformed - // cache for the transformation - obj_map m_cache; + ptr_buffer args; + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) + { + proof* pp = m.get_parent(p, i); + proof* tmp; + if (m_cache.find(pp, tmp)) + { + args.push_back(tmp); + dirty = dirty || pp != tmp; + } + else + { + SASSERT(false); + } + } + if (!dirty) // if not dirty just use the old step + { + m_cache.insert(p, p); + } + else // otherwise create new step with the corresponding proofs of the premises + { + if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } + SASSERT(p->get_decl()->get_arity() == args.size()); + proof* res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + m_cache.insert(p, res); + } + } + } + + proof* res; + bool found = m_cache.find(pr,res); + SASSERT(found); + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side)); + ); + + return proof_ref(res,m); + } - // map from unit literals to their hypotheses-free derivations - obj_map m_units; - - // -- all hypotheses in the the proof - obj_hashtable m_hyps; - - // marks hypothetical proofs - ast_mark m_hypmark; - - - // stack - ptr_vector m_todo; - - void reset() + void hypothesis_reducer::reset() { m_cache.reset(); m_units.reset(); @@ -299,14 +404,35 @@ class reduce_hypotheses { m_hypmark.reset(); m_pinned.reset(); } + + void hypothesis_reducer::compute_hypmarks_and_hyps(proof* pr) + { + proof *p; + ProofIteratorPostOrder pit(pr, m); + while (pit.hasNext()) { + p = pit.next(); + if (m.is_hypothesis(p)) + { + m_hypmark.mark(p, true); + m_hyps.insert(m.get_fact(p)); + } + else + { + compute_hypmark_from_parents(p); + } + } + } - bool compute_mark1(proof *pr) + bool hypothesis_reducer::compute_hypmark_from_parents(proof *pr) { bool hyp_mark = false; - // lemmas clear all hypotheses - if (!m.is_lemma(pr)) { - for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) { - if (m_hypmark.is_marked(m.get_parent(pr, i))) { + + if (!m.is_lemma(pr)) // lemmas clear all hypotheses + { + for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) + { + if (m_hypmark.is_marked(m.get_parent(pr, i))) + { hyp_mark = true; break; } @@ -316,22 +442,13 @@ class reduce_hypotheses { return hyp_mark; } - void compute_marks(proof* pr) + // collect all units that are hyp-free and are used as hypotheses somewhere + // requires that m_hypmarks and m_hyps have been computed + void hypothesis_reducer::collect_units(proof* pr) { - proof *p; ProofIteratorPostOrder pit(pr, m); while (pit.hasNext()) { - p = pit.next(); - if (m.is_hypothesis(p)) { - m_hypmark.mark(p, true); - m_hyps.insert(m.get_fact(p)); - } else { - compute_mark1(p); - } - } - ProofIteratorPostOrder pit2(pr, m); - while (pit2.hasNext()) { - p = pit2.next(); + proof* p = pit.next(); if (!m.is_hypothesis(p)) { // collect units that are hyp-free and are used as hypotheses somewhere @@ -342,12 +459,25 @@ class reduce_hypotheses { } } } - void find_units(proof *pr) - { - // optional. not implemented yet. - } - void reduce(proof* pf, proof_ref &out) + proof_ref hypothesis_reducer::reduce(proof* pr) + { + compute_hypmarks_and_hyps(pr); + collect_units(pr); + proof* res = compute_transformed_proof(pr); + SASSERT(res != nullptr); + + proof_ref res_ref(res,m); + + reset(); + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side)); + ); + return res_ref; + } + + proof* hypothesis_reducer::compute_transformed_proof(proof* pf) { proof *res = NULL; @@ -383,9 +513,14 @@ class reduce_hypotheses { if (todo_sz < m_todo.size()) { continue; } else { m_todo.pop_back(); } - if (m.is_hypothesis(p)) { + // here the transformation begins + // INV: for each premise of p, we have computed the transformed proof. + + if (m.is_hypothesis(p)) + { // hyp: replace by a corresponding unit - if (m_units.find(m.get_fact(p), tmp)) { + if (m_units.find(m.get_fact(p), tmp)) + { // if the transformed subproof ending in the unit has already been computed, use it if (m_cache.find(tmp,tmp2)) { @@ -406,11 +541,11 @@ class reduce_hypotheses { //lemma: reduce the premise; remove reduced consequences from conclusion SASSERT(args.size() == 1); res = mk_lemma_core(args.get(0), m.get_fact(p)); - compute_mark1(res); + compute_hypmark_from_parents(res); } else if (m.is_unit_resolution(p)) { // unit: reduce untis; reduce the first premise; rebuild unit resolution res = mk_unit_resolution_core(args.size(), args.c_ptr()); - compute_mark1(res); + compute_hypmark_from_parents(res); } else { // if any literal is false, we don't need a step bool has_empty_clause_premise = false; @@ -431,21 +566,22 @@ class reduce_hypotheses { SASSERT(p->get_decl()->get_arity() == args.size()); res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); m_pinned.push_back(res); - compute_mark1(res); + compute_hypmark_from_parents(res); } } SASSERT(res); m_cache.insert(p, res); - if (!m_hypmark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) { break; } + if (!m_hypmark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) + { + return res; + } } - - out = res; } // returns true if (hypothesis (not a)) would be reduced - bool is_reduced(expr *a) + bool hypothesis_reducer::is_reduced(expr *a) { expr_ref e(m); if (m.is_not(a)) { e = to_app(a)->get_arg(0); } @@ -453,7 +589,8 @@ class reduce_hypotheses { return m_units.contains(e); } - proof *mk_lemma_core(proof *pf, expr *fact) + + proof* hypothesis_reducer::mk_lemma_core(proof *pf, expr *fact) { ptr_buffer args; expr_ref lemma(m); @@ -486,7 +623,7 @@ class reduce_hypotheses { return res; } - proof *mk_unit_resolution_core(unsigned num_args, proof* const *args) + proof* hypothesis_reducer::mk_unit_resolution_core(unsigned num_args, proof* const *args) { ptr_buffer pf_args; @@ -542,42 +679,4 @@ class reduce_hypotheses { return res; } } - - // reduce all units, if any unit reduces to false return true and put its proof into out - bool reduce_units(proof_ref &out) - { - proof_ref res(m); - for (auto entry : m_units) { - reduce(entry.get_value(), res); - if (m.is_false(m.get_fact(res))) { - out = res; - return true; - } - res.reset(); - } - return false; - } - - -public: - reduce_hypotheses(ast_manager &m) : m(m), m_pinned(m) {} - - - void operator()(proof_ref &pr) - { - compute_marks(pr); - reduce(pr.get(), pr); - reset(); - } }; -void reduce_hypotheses(proof_ref &pr) -{ - ast_manager &m = pr.get_manager(); - class reduce_hypotheses hypred(m); - hypred(pr); - DEBUG_CODE(proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(pr, side)); - ); -} -} diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index 93b512c8f..a0031fc07 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -22,6 +22,7 @@ Revision History: namespace spacer { + bool is_arith_lemma(ast_manager& m, proof* pr); bool is_farkas_lemma(ast_manager& m, proof* pr); /* * iterator, which traverses the proof in depth-first post-order. @@ -46,8 +47,71 @@ private: class iuc_proof; void pp_proof_dot(ast_manager& m, proof* pr, iuc_proof* iuc_pr = nullptr); + class theory_axiom_reducer + { + public: + theory_axiom_reducer(ast_manager& m) : m(m), m_pinned(m) {} + // reduce theory axioms and return transformed proof + proof_ref reduce(proof* pr); + + private: + ast_manager &m; + + // tracking all created expressions + expr_ref_vector m_pinned; + + // maps each proof of a clause to the transformed subproof of that clause + obj_map m_cache; + + void reset(); + }; -void reduce_hypotheses(proof_ref &pr); + class hypothesis_reducer + { + public: + hypothesis_reducer(ast_manager &m) : m(m), m_pinned(m) {} + + // reduce hypothesis and return transformed proof + proof_ref reduce(proof* pf); + + private: + typedef obj_hashtable expr_set; + + ast_manager &m; + // tracking all created expressions + expr_ref_vector m_pinned; + + // maps each proof of a clause to the transformed subproof of that clause + obj_map m_cache; + + // maps each unit literals to the transformed subproof of that unit + obj_map m_units; + + // -- all hypotheses in the the proof + obj_hashtable m_hyps; + + // marks hypothetical proofs + ast_mark m_hypmark; + + std::vector m_pinned_hyp_sets; // tracking all created sets of hypothesis + obj_map m_hyp_anchestor; // maps each proof to the set of hypothesis it contains, needed to avoid creating cycles in the proof. + + // stack + ptr_vector m_todo; + + void reset(); + proof* compute_transformed_proof(proof* pf); + + void compute_hypmarks_and_hyps(proof* pr); + bool compute_hypmark_from_parents(proof *pr); + void collect_units(proof* pr); + + // returns true if (hypothesis (not a)) would be reduced + bool is_reduced(expr *a); + + proof* mk_lemma_core(proof *pf, expr *fact); + proof* mk_unit_resolution_core(unsigned num_args, proof* const *args); + }; } #endif From 0f25e9e831a85fb3647fb8cc8e45e369bc01dd21 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Fri, 1 Dec 2017 15:23:07 +0100 Subject: [PATCH 076/364] Moved farkas stats printing to before and after the hyp-reduction --- src/muz/spacer/spacer_itp_solver.cpp | 16 ++++++++++++++ src/muz/spacer/spacer_unsat_core_learner.cpp | 22 ++++++-------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index f91cc6bb3..9c11e0c1c 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -281,7 +281,23 @@ void itp_solver::get_itp_core (expr_ref_vector &core) // preprocess proof in order to get a proof which is better suited for unsat-core-extraction proof_ref pr(get_proof(), m); + if (m_print_farkas_stats) + { + iuc_proof iuc_before(m, pr.get(), B); + verbose_stream() << "Stats before transformation:"; + iuc_before.print_farkas_stats(); + } + spacer::reduce_hypotheses(pr); + spacer::reduce_hypotheses(pr); + + if (m_print_farkas_stats) + { + iuc_proof iuc_after(m, pr.get(), B); + verbose_stream() << "Stats after transformation:"; + iuc_after.print_farkas_stats(); + } + STRACE("spacer.unsat_core_learner", verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n"; ); diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index fa5e17239..cb7ebff76 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -41,7 +41,7 @@ void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { // traverse proof - proof_post_order it(m_pr.get(), m); + proof_post_order it(m_pr.get(), m); while (it.hasNext()) { proof* currentNode = it.next(); @@ -54,7 +54,7 @@ void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { SASSERT(m.is_proof(currentNode->get_arg(i))); proof* premise = to_app(currentNode->get_arg(i)); - + need_to_mark_closed = need_to_mark_closed && (!m_pr.is_b_marked(premise) || m_closed.is_marked(premise)); } @@ -75,16 +75,6 @@ void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) // TODO: remove duplicates from unsat core? - // count both number of all Farkas lemmas and number of Farkas lemmas in the cut - if (m_print_farkas_stats) - { - m_pr.print_farkas_stats(); - } - //TODO remove this - if(m_iuc_debug_proof) - { - } - verbose_stream() << std::endl; // move all lemmas into vector for (expr* const* it = m_unsat_core.begin(); it != m_unsat_core.end(); ++it) @@ -119,14 +109,14 @@ void unsat_core_learner::set_closed(proof* p, bool value) { m_closed.mark(p, value); } - + bool unsat_core_learner::is_b_open(proof *p) - { +{ return m_pr.is_b_marked(p) && !is_closed (p); - } +} void unsat_core_learner::add_lemma_to_core(expr* lemma) { m_unsat_core.push_back(lemma); - } +} } From f0f75d525415adc8cdb309dea50353e768e32ee7 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 10:42:57 -0700 Subject: [PATCH 077/364] Wire in arith-axiom-reducer --- src/muz/spacer/spacer_itp_solver.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index 9c11e0c1c..187a4a30f 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -287,23 +287,29 @@ void itp_solver::get_itp_core (expr_ref_vector &core) verbose_stream() << "Stats before transformation:"; iuc_before.print_farkas_stats(); } - - spacer::reduce_hypotheses(pr); - spacer::reduce_hypotheses(pr); - + + theory_axiom_reducer ta_reducer(m); + proof_ref pr_without_theory_lemmas = ta_reducer.reduce(pr.get()); + if (m_print_farkas_stats) { - iuc_proof iuc_after(m, pr.get(), B); - verbose_stream() << "Stats after transformation:"; + iuc_proof iuc_mid(m, pr_without_theory_lemmas.get(), B); + verbose_stream() << "Stats after first transformation:"; + iuc_mid.print_farkas_stats(); + } + + hypothesis_reducer hyp_reducer(m); + proof_ref pr_with_less_hypothesis = hyp_reducer.reduce(pr_without_theory_lemmas); + + if (m_print_farkas_stats) + { + iuc_proof iuc_after(m, pr_with_less_hypothesis.get(), B); + verbose_stream() << "Stats after second transformation:"; iuc_after.print_farkas_stats(); } - STRACE("spacer.unsat_core_learner", - verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n"; - ); - // construct proof object with contains partition information - iuc_proof iuc_pr(m, pr.get(), B); + iuc_proof iuc_pr(m, pr_with_less_hypothesis, B); // configure learner unsat_core_learner learner(m, iuc_pr, m_print_farkas_stats, m_iuc_debug_proof); From 85c58e344c14073afc8e6073cd59831797b60cf5 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Mon, 18 Dec 2017 16:45:58 +0100 Subject: [PATCH 078/364] Add option to use old_hyp_reducer --- src/muz/base/fixedpoint_params.pyg | 3 +- src/muz/spacer/spacer_itp_solver.cpp | 72 +++++++++++++++++---------- src/muz/spacer/spacer_itp_solver.h | 6 ++- src/muz/spacer/spacer_prop_solver.cpp | 4 +- 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 74903baf1..65d89e420 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -182,7 +182,8 @@ def_module_params('fixedpoint', ('spacer.instantiate', BOOL, True, 'instantiate quantified lemmas'), ('spacer.qlemmas', BOOL, True, 'allow quantified lemmas in frames'), ('spacer.iuc', UINT, 1, '0 = use old implementation of unsat-core-generation, 1 = use new implementation of IUC generation, 2 = use new implementation of IUC + min-cut optimization'), - ('spacer.iuc.arith', UINT, 2, '0 = use simple Farkas plugin, 1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation), 2 = use Gaussian elimination optimization, 3 = use additive IUC plugin'), + ('spacer.iuc.arith', UINT, 2, '0 = use simple Farkas plugin, 1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation), 2 = use Gaussian elimination optimization, 3 = use additive IUC plugin'), + ('spacer.old_hyp_reducer', BOOL, False, 'use old hyp reducer instead of new implementation, for debugging only'), ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), ('spacer.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index 187a4a30f..35e06f9a2 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -19,6 +19,7 @@ Notes: #include"muz/spacer/spacer_itp_solver.h" #include"ast/ast.h" #include"muz/spacer/spacer_util.h" +#include"muz/base/proof_utils.h" #include"muz/spacer/spacer_farkas_learner.h" #include"ast/rewriter/expr_replacer.h" #include"muz/spacer/spacer_unsat_core_learner.h" @@ -277,39 +278,56 @@ void itp_solver::get_itp_core (expr_ref_vector &core) } else { + proof_ref res(get_proof(),m); + // new code - // preprocess proof in order to get a proof which is better suited for unsat-core-extraction - proof_ref pr(get_proof(), m); - - if (m_print_farkas_stats) + if (m_old_hyp_reducer) { - iuc_proof iuc_before(m, pr.get(), B); - verbose_stream() << "Stats before transformation:"; - iuc_before.print_farkas_stats(); + // preprocess proof in order to get a proof which is better suited for unsat-core-extraction + if (m_print_farkas_stats) + { + iuc_proof iuc_before(m, res.get(), B); + verbose_stream() << "\nStats before transformation:"; + iuc_before.print_farkas_stats(); + } + + proof_utils::reduce_hypotheses(res); + proof_utils::permute_unit_resolution(res); + + if (m_print_farkas_stats) + { + iuc_proof iuc_after(m, res.get(), B); + verbose_stream() << "Stats after transformation:"; + iuc_after.print_farkas_stats(); + } } - - theory_axiom_reducer ta_reducer(m); - proof_ref pr_without_theory_lemmas = ta_reducer.reduce(pr.get()); - - if (m_print_farkas_stats) + else { - iuc_proof iuc_mid(m, pr_without_theory_lemmas.get(), B); - verbose_stream() << "Stats after first transformation:"; - iuc_mid.print_farkas_stats(); + // preprocess proof in order to get a proof which is better suited for unsat-core-extraction + if (m_print_farkas_stats) + { + iuc_proof iuc_before(m, res.get(), B); + verbose_stream() << "\nStats before transformation:"; + iuc_before.print_farkas_stats(); + } + + theory_axiom_reducer ta_reducer(m); + proof_ref pr_without_theory_lemmas = ta_reducer.reduce(res.get()); + + hypothesis_reducer hyp_reducer(m); + proof_ref pr_with_less_hypothesis = hyp_reducer.reduce(pr_without_theory_lemmas); + + if (m_print_farkas_stats) + { + iuc_proof iuc_after(m, pr_with_less_hypothesis.get(), B); + verbose_stream() << "Stats after transformation:"; + iuc_after.print_farkas_stats(); + } + res = pr_with_less_hypothesis; } - - hypothesis_reducer hyp_reducer(m); - proof_ref pr_with_less_hypothesis = hyp_reducer.reduce(pr_without_theory_lemmas); - - if (m_print_farkas_stats) - { - iuc_proof iuc_after(m, pr_with_less_hypothesis.get(), B); - verbose_stream() << "Stats after second transformation:"; - iuc_after.print_farkas_stats(); - } - + // construct proof object with contains partition information - iuc_proof iuc_pr(m, pr_with_less_hypothesis, B); + iuc_proof iuc_pr(m, res, B); // configure learner unsat_core_learner learner(m, iuc_pr, m_print_farkas_stats, m_iuc_debug_proof); diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index 5e5c2d39e..6f6a2bf8f 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -62,13 +62,14 @@ private: unsigned m_iuc_arith; bool m_print_farkas_stats; bool m_iuc_debug_proof; + bool m_old_hyp_reducer; bool is_proxy(expr *e, app_ref &def); void undo_proxies_in_core(ptr_vector &v); app* mk_proxy(expr *v); app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); public: - itp_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool iuc_debug_proof, bool split_literals = false) : + itp_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool iuc_debug_proof, bool old_hyp_reducer, bool split_literals = false) : m(solver.get_manager()), m_solver(solver), m_proxies(m), @@ -82,7 +83,8 @@ public: m_iuc(iuc), m_iuc_arith(iuc_arith), m_print_farkas_stats(print_farkas_stats), - m_iuc_debug_proof(iuc_debug_proof) + m_iuc_debug_proof(iuc_debug_proof), + m_old_hyp_reducer(old_hyp_reducer) {} ~itp_solver() override {} diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 60deac214..03c9b858b 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -60,8 +60,8 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_solvers[1] = pm.mk_fresh2(); m_fparams[1] = &pm.fparams2(); - m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_iuc_debug_proof(), p.spacer_split_farkas_literals()); - m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_iuc_debug_proof(), p.spacer_split_farkas_literals()); + m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_iuc_debug_proof(), p.spacer_old_hyp_reducer(), p.spacer_split_farkas_literals()); + m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_iuc_debug_proof(), p.spacer_old_hyp_reducer(), p.spacer_split_farkas_literals()); for (unsigned i = 0; i < 2; ++i) { m_contexts[i]->assert_expr(m_pm.get_background()); } From 295d16bfae929e2a1bf4498fcfe3ab0b765d0ec4 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Tue, 19 Dec 2017 15:28:53 +0100 Subject: [PATCH 079/364] Rewrite hyp-reducer This is a new version that conceptually addresses the bugs in all previous version. However, it had a hard-to-debug memory corruption. The bug appeared only in optimized compilation under Linux with GCC. This code is suspect and should be reviewed and further tested --- src/muz/spacer/spacer_proof_utils.cpp | 338 ++++++++++++++++---------- src/muz/spacer/spacer_proof_utils.h | 42 ++-- 2 files changed, 220 insertions(+), 160 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 81fb7286a..4625cbdac 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -18,12 +18,14 @@ Revision History: #include "muz/spacer/spacer_proof_utils.h" #include "ast/ast_util.h" + #include "ast/ast_pp.h" #include "ast/proof_checker/proof_checker.h" #include #include "params.h" #include "muz/spacer/spacer_iuc_proof.h" +#include "muz/base/dl_util.h" namespace spacer { @@ -355,7 +357,7 @@ proof* ProofIteratorPostOrder::next() { bool dirty = false; // proof is dirty, if a subproof of one of its premises has been transformed - ptr_buffer args; + ptr_buffer args; for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { proof* pp = m.get_parent(p, i); @@ -376,7 +378,7 @@ proof* ProofIteratorPostOrder::next() } else // otherwise create new step with the corresponding proofs of the premises { - if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } + if (m.has_fact(p)) { args.push_back(m.get_fact(p)); } SASSERT(p->get_decl()->get_arity() == args.size()); proof* res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); m_pinned.push_back(res); @@ -400,59 +402,103 @@ proof* ProofIteratorPostOrder::next() { m_cache.reset(); m_units.reset(); - m_hyps.reset(); - m_hypmark.reset(); + m_active_hyps.reset(); + m_parent_hyps.reset(); + m_pinned_active_hyps.reset(); + m_pinned_parent_hyps.reset(); m_pinned.reset(); } - void hypothesis_reducer::compute_hypmarks_and_hyps(proof* pr) + void hypothesis_reducer::compute_hypsets(proof* pr) { - proof *p; - ProofIteratorPostOrder pit(pr, m); - while (pit.hasNext()) { - p = pit.next(); - if (m.is_hypothesis(p)) + ptr_vector todo; + todo.push_back(pr); + + while (!todo.empty()) + { + proof* p = todo.back(); + + if (m_active_hyps.contains(p)) { - m_hypmark.mark(p, true); - m_hyps.insert(m.get_fact(p)); + SASSERT(m_parent_hyps.contains(p)); + todo.pop_back(); } + // if we haven't already visited the current unit else { - compute_hypmark_from_parents(p); - } - } - } - - bool hypothesis_reducer::compute_hypmark_from_parents(proof *pr) - { - bool hyp_mark = false; - - if (!m.is_lemma(pr)) // lemmas clear all hypotheses - { - for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) - { - if (m_hypmark.is_marked(m.get_parent(pr, i))) + bool existsUnvisitedParent = false; + + // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result + // for p now, but wait until those unprocessed premises are processed. + for (unsigned i = 0; i < m.get_num_parents(p); ++i) { + SASSERT(m.is_proof(p->get_arg(i))); + proof* premise = to_app(p->get_arg(i)); + + // if we haven't visited the premise yet + if (!m_active_hyps.contains(premise)) + { + SASSERT(!m_parent_hyps.contains(premise)); + // add it to the stack + todo.push_back(premise); + existsUnvisitedParent = true; + } + } + + // if we already visited all premises, we can visit p too + if (!existsUnvisitedParent) { - hyp_mark = true; - break; + todo.pop_back(); + + // create active_hyps-set and parent_hyps-set for step p + proof_set* active_hyps = alloc(proof_set); + m_pinned_active_hyps.insert(active_hyps); + m_active_hyps.insert(p, active_hyps); + + expr_set* parent_hyps = alloc(expr_set); + m_pinned_parent_hyps.insert(parent_hyps); + m_parent_hyps.insert(p, parent_hyps); + + // fill both sets + if (m.is_hypothesis(p)) + { + active_hyps->insert(p); + parent_hyps->insert(m.get_fact(p)); + } + else + { + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) + { + proof* pp = m.get_parent(p, i); + datalog::set_union(*parent_hyps, *m_parent_hyps.find(pp)); + + if (!m.is_lemma(p)) // lemmas clear all hypotheses + { + datalog::set_union(*active_hyps, *m_active_hyps.find(pp)); + } + } + } } } } - m_hypmark.mark(pr, hyp_mark); - return hyp_mark; } - + // collect all units that are hyp-free and are used as hypotheses somewhere - // requires that m_hypmarks and m_hyps have been computed + // requires that m_active_hyps and m_parent_hyps have been computed void hypothesis_reducer::collect_units(proof* pr) { + expr_set* all_hyps = m_parent_hyps.find(pr); + SASSERT(all_hyps != nullptr); + ProofIteratorPostOrder pit(pr, m); while (pit.hasNext()) { proof* p = pit.next(); if (!m.is_hypothesis(p)) { - // collect units that are hyp-free and are used as hypotheses somewhere - if (!m_hypmark.is_marked(p) && m.has_fact(p) && m_hyps.contains(m.get_fact(p))) + proof_set* active_hyps = m_active_hyps.find(p); + SASSERT(active_hyps != nullptr); + + // collect units that are hyp-free and are used as hypotheses in the proof pr + if (active_hyps->empty() && m.has_fact(p) && all_hyps->contains(m.get_fact(p))) { m_units.insert(m.get_fact(p), p); } @@ -462,8 +508,9 @@ proof* ProofIteratorPostOrder::next() proof_ref hypothesis_reducer::reduce(proof* pr) { - compute_hypmarks_and_hyps(pr); + compute_hypsets(pr); collect_units(pr); + proof* res = compute_transformed_proof(pr); SASSERT(res != nullptr); @@ -481,156 +528,162 @@ proof* ProofIteratorPostOrder::next() { proof *res = NULL; - m_todo.reset(); - m_todo.push_back(pf); + ptr_vector todo; + todo.push_back(pf); ptr_buffer args; bool dirty = false; - while (!m_todo.empty()) { - proof *p, *tmp, *tmp2, *pp; + while (!todo.empty()) { + proof *p, *tmp, *pp; unsigned todo_sz; - p = m_todo.back(); + p = todo.back(); if (m_cache.find(p, tmp)) { - res = tmp; - m_todo.pop_back(); + res = tmp; // TODO: shouldn't this line be removed? + todo.pop_back(); continue; } dirty = false; args.reset(); - todo_sz = m_todo.size(); + todo_sz = todo.size(); for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { pp = m.get_parent(p, i); if (m_cache.find(pp, tmp)) { args.push_back(tmp); dirty = dirty || pp != tmp; } else { - m_todo.push_back(pp); + todo.push_back(pp); } } - if (todo_sz < m_todo.size()) { continue; } - else { m_todo.pop_back(); } + if (todo_sz < todo.size()) { continue; } + else { todo.pop_back(); } - // here the transformation begins - // INV: for each premise of p, we have computed the transformed proof. + // here the proof transformation begins + // INV: whenever we visit p, active_hyps and parent_hyps have been computed for the args. if (m.is_hypothesis(p)) { // hyp: replace by a corresponding unit if (m_units.find(m.get_fact(p), tmp)) { - // if the transformed subproof ending in the unit has already been computed, use it - if (m_cache.find(tmp,tmp2)) + // look up the proof of the unit: + // if there is a transformed proof use that one + // otherwise use the original proof + proof* proof_of_unit; + if (!m_cache.find(tmp,proof_of_unit)) { - res = tmp2; + proof_of_unit = tmp; } - // otherwise first compute the transformed subproof ending in the unit + + // compute hypsets (have not been computed in general, since the unit can be anywhere in the proof) + compute_hypsets(proof_of_unit); + + // if the transformation doesn't create a cycle, perform it + SASSERT(m_parent_hyps.contains(proof_of_unit)); + expr_set* parent_hyps = m_parent_hyps.find(proof_of_unit); + if (!parent_hyps->contains(p)) + { + res = proof_of_unit; + // hypsets have already been computed for proof_of_unit + } + // otherwise don't transform the proof and just use the hypothesis else { - m_todo.push_back(tmp); - continue; + res = p; + // hypsets have already been computed for p } - } else { res = p; } + } + else + { + res = p; + // hypsets have already been computed for p + } } - else if (!dirty) { res = p; } - else if (m.is_lemma(p)) { + else if (m.is_lemma(p)) + { //lemma: reduce the premise; remove reduced consequences from conclusion SASSERT(args.size() == 1); - res = mk_lemma_core(args.get(0), m.get_fact(p)); - compute_hypmark_from_parents(res); - } else if (m.is_unit_resolution(p)) { + res = mk_lemma_core(args[0], m.get_fact(p)); + compute_hypsets(res); + } + else if (m.is_unit_resolution(p)) + { // unit: reduce untis; reduce the first premise; rebuild unit resolution - res = mk_unit_resolution_core(args.size(), args.c_ptr()); - compute_hypmark_from_parents(res); - } else { - // if any literal is false, we don't need a step - bool has_empty_clause_premise = false; - for (unsigned i = 0; i < args.size(); ++i) - { - if (m.is_false(m.get_fact(args[i]))) - { - has_empty_clause_premise = true; - res = args[i]; - } - } - - // otherwise: - if (!has_empty_clause_premise) - { - // other: reduce all premises; reapply - if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } - SASSERT(p->get_decl()->get_arity() == args.size()); - res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); - m_pinned.push_back(res); - compute_hypmark_from_parents(res); - } + res = mk_unit_resolution_core(args); + compute_hypsets(res); + } + else + { + res = mk_step_core(p, args); + compute_hypsets(res); } SASSERT(res); - m_cache.insert(p, res); - - if (!m_hypmark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) + m_cache.insert(p, res); + + SASSERT(m_active_hyps.contains(res)); + proof_set* active_hyps = m_active_hyps.find(res); + if (active_hyps->empty() && m.has_fact(res) && m.is_false(m.get_fact(res))) { return res; } } - } - - // returns true if (hypothesis (not a)) would be reduced - bool hypothesis_reducer::is_reduced(expr *a) - { - expr_ref e(m); - if (m.is_not(a)) { e = to_app(a)->get_arg(0); } - else { e = m.mk_not(a); } - - return m_units.contains(e); + UNREACHABLE(); + return nullptr; } - proof* hypothesis_reducer::mk_lemma_core(proof *pf, expr *fact) + proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { - ptr_buffer args; - expr_ref lemma(m); + SASSERT(m.is_false(m.get_fact(premise))); + + SASSERT(m_active_hyps.contains(premise)); + proof_set* active_hyps = m_active_hyps.find(premise); - if (m.is_or(fact)) { - for (unsigned i = 0, sz = to_app(fact)->get_num_args(); i < sz; ++i) { - expr *a = to_app(fact)->get_arg(i); - if (!is_reduced(a)) - { args.push_back(a); } + // if there is no active hypothesis return the premise + if (active_hyps->empty()) + { + return premise; + } + // otherwise build disjunction of the negated active hypothesis' and add lemma step. + else + { + expr_ref_buffer args(m); + for (auto hyp : *active_hyps) + { + expr* hyp_fact = m.get_fact(hyp); + expr_ref negated_hyp_fact(m); + negated_hyp_fact = m.is_not(hyp_fact) ? to_app(hyp_fact)->get_arg(0) : m.mk_not(hyp_fact); + args.push_back(negated_hyp_fact); } - } else if (!is_reduced(fact)) - { args.push_back(fact); } - - - if (args.size() == 0) { return pf; } - else if (args.size() == 1) { - lemma = args.get(0); - } else { - lemma = m.mk_or(args.size(), args.c_ptr()); + + expr_ref lemma(m); + if (args.size() == 1) + { + lemma = args[0]; + } + else + { + lemma = m.mk_or(args.size(), args.c_ptr()); + } + proof_ref res(m); + res = m.mk_lemma(premise, lemma); + m_pinned.push_back(res); + return res; } - proof* res = m.mk_lemma(pf, lemma); - m_pinned.push_back(res); - - // XXX this is wrong because lemma is a proof and m_hyps only - // XXX contains expressions. - // XXX Not sure this is ever needed. - if (m_hyps.contains(lemma)) { - m_units.insert(lemma, res); - } - return res; } - proof* hypothesis_reducer::mk_unit_resolution_core(unsigned num_args, proof* const *args) + proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) { - - ptr_buffer pf_args; - pf_args.push_back(args [0]); + ptr_buffer pf_args; // the arguments of the transformed unit resolution step + pf_args.push_back(args [0]); // the first element of args is the clause to resolve with // if any literal is false, we don't need a unit resolution step - for (unsigned i = 1; i < num_args; ++i) + // could be the case due to transformations which already have been done + for (unsigned i = 1; i < args.size(); ++i) { if (m.is_false(m.get_fact(args[i]))) { @@ -638,7 +691,7 @@ proof* ProofIteratorPostOrder::next() } } - app *cls_fact = to_app(m.get_fact(args[0])); + app *cls_fact = to_app(m.get_fact(args[0])); // BUG: I guess this shouldn't work with quantifiers (since they are no apps) ptr_buffer cls; if (m.is_or(cls_fact)) { for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) @@ -651,7 +704,7 @@ proof* ProofIteratorPostOrder::next() // XXX quadratic for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { found = false; - for (unsigned j = 1; j < num_args; ++j) { + for (unsigned j = 1; j < args.size(); ++j) { if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { found = true; pf_args.push_back(args [j]); @@ -674,9 +727,30 @@ proof* ProofIteratorPostOrder::next() } else { - proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); + proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); m_pinned.push_back(res); return res; } } + + proof* hypothesis_reducer::mk_step_core(proof* old_step, ptr_buffer& args) + { + // if any of the literals is false, we don't need a step + for (unsigned i = 0; i < args.size(); ++i) + { + if (m.is_false(m.get_fact(args[i]))) + { + return args[i]; + } + } + + // otherwise build step + args.push_back(to_app(m.get_fact(old_step))); // BUG: I guess this doesn't work with quantifiers (since they are no apps) + + SASSERT(old_step->get_decl()->get_arity() == args.size()); + proof* res = m.mk_app(old_step->get_decl(), args.size(), (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + return res; + } + }; diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index a0031fc07..abd612b57 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -77,41 +77,27 @@ private: private: typedef obj_hashtable expr_set; - + typedef obj_hashtable proof_set; + ast_manager &m; - // tracking all created expressions - expr_ref_vector m_pinned; - // maps each proof of a clause to the transformed subproof of that clause - obj_map m_cache; - - // maps each unit literals to the transformed subproof of that unit - obj_map m_units; - - // -- all hypotheses in the the proof - obj_hashtable m_hyps; - - // marks hypothetical proofs - ast_mark m_hypmark; - - std::vector m_pinned_hyp_sets; // tracking all created sets of hypothesis - obj_map m_hyp_anchestor; // maps each proof to the set of hypothesis it contains, needed to avoid creating cycles in the proof. - - // stack - ptr_vector m_todo; + expr_ref_vector m_pinned; // tracking all created expressions + ptr_vector m_pinned_active_hyps; // tracking all created sets of active hypothesis + ptr_vector m_pinned_parent_hyps; // tracking all created sets of parent hypothesis + + obj_map m_cache; // maps each proof of a clause to the transformed subproof of that clause + obj_map m_units; // maps each unit literal to the subproof of that unit + obj_map m_active_hyps; // maps each proof of a clause to the set of proofs of active hypothesis' of the clause + obj_map m_parent_hyps; // maps each proof of a clause to the hypothesis-fact, which are transitive parents of that clause, needed to avoid creating cycles in the proof. void reset(); + void compute_hypsets(proof* pr); // compute active_hyps and parent_hyps for pr + void collect_units(proof* pr); // compute m_units proof* compute_transformed_proof(proof* pf); - - void compute_hypmarks_and_hyps(proof* pr); - bool compute_hypmark_from_parents(proof *pr); - void collect_units(proof* pr); - - // returns true if (hypothesis (not a)) would be reduced - bool is_reduced(expr *a); proof* mk_lemma_core(proof *pf, expr *fact); - proof* mk_unit_resolution_core(unsigned num_args, proof* const *args); + proof* mk_unit_resolution_core(ptr_buffer& args); + proof* mk_step_core(proof* old_step, ptr_buffer& args); }; } #endif From 4b6921dffbbf7e6f0e41ade7db732dbb34d75ed3 Mon Sep 17 00:00:00 2001 From: Bernhard Gleiss Date: Wed, 10 Jan 2018 12:02:16 +0100 Subject: [PATCH 080/364] removed unnecessary assignment --- src/muz/spacer/spacer_proof_utils.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 4625cbdac..bbe53c362 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -539,7 +539,6 @@ proof* ProofIteratorPostOrder::next() p = todo.back(); if (m_cache.find(p, tmp)) { - res = tmp; // TODO: shouldn't this line be removed? todo.pop_back(); continue; } From 7c727ee9223c71a60575c1c57c145f6c0afeb864 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 12:13:18 -0700 Subject: [PATCH 081/364] Fix compiler warnings --- src/muz/spacer/spacer_generalizers.cpp | 1 + src/muz/spacer/spacer_itp_solver.cpp | 18 ++-- src/muz/spacer/spacer_itp_solver.h | 4 +- src/muz/spacer/spacer_proof_utils.cpp | 107 ++++++++++----------- src/muz/spacer/spacer_prop_solver.cpp | 10 +- src/muz/spacer/spacer_unsat_core_learner.h | 13 +-- 6 files changed, 79 insertions(+), 74 deletions(-) diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 2f9627398..4b0c3d15c 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -158,6 +158,7 @@ void unsat_core_generalizer::operator()(lemma_ref &lemma) unsigned old_sz = lemma->get_cube().size(); unsigned old_level = lemma->level(); + (void)old_level; unsigned uses_level; expr_ref_vector core(m); diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index 35e06f9a2..71587b4c6 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -279,7 +279,7 @@ void itp_solver::get_itp_core (expr_ref_vector &core) else { proof_ref res(get_proof(),m); - + // new code if (m_old_hyp_reducer) { @@ -290,10 +290,10 @@ void itp_solver::get_itp_core (expr_ref_vector &core) verbose_stream() << "\nStats before transformation:"; iuc_before.print_farkas_stats(); } - + proof_utils::reduce_hypotheses(res); proof_utils::permute_unit_resolution(res); - + if (m_print_farkas_stats) { iuc_proof iuc_after(m, res.get(), B); @@ -310,13 +310,13 @@ void itp_solver::get_itp_core (expr_ref_vector &core) verbose_stream() << "\nStats before transformation:"; iuc_before.print_farkas_stats(); } - + theory_axiom_reducer ta_reducer(m); proof_ref pr_without_theory_lemmas = ta_reducer.reduce(res.get()); - + hypothesis_reducer hyp_reducer(m); proof_ref pr_with_less_hypothesis = hyp_reducer.reduce(pr_without_theory_lemmas); - + if (m_print_farkas_stats) { iuc_proof iuc_after(m, pr_with_less_hypothesis.get(), B); @@ -328,9 +328,9 @@ void itp_solver::get_itp_core (expr_ref_vector &core) // construct proof object with contains partition information iuc_proof iuc_pr(m, res, B); - + // configure learner - unsat_core_learner learner(m, iuc_pr, m_print_farkas_stats, m_iuc_debug_proof); + unsat_core_learner learner(m, iuc_pr); if (m_iuc_arith == 0 || m_iuc_arith > 3) { @@ -352,7 +352,7 @@ void itp_solver::get_itp_core (expr_ref_vector &core) unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); learner.register_plugin(plugin_farkas_lemma_bounded); } - + if (m_iuc == 2) { unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m); diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index 6f6a2bf8f..0b3527bb3 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -61,7 +61,6 @@ private: unsigned m_iuc; unsigned m_iuc_arith; bool m_print_farkas_stats; - bool m_iuc_debug_proof; bool m_old_hyp_reducer; bool is_proxy(expr *e, app_ref &def); void undo_proxies_in_core(ptr_vector &v); @@ -69,7 +68,7 @@ private: app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); public: - itp_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool iuc_debug_proof, bool old_hyp_reducer, bool split_literals = false) : + itp_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool old_hyp_reducer, bool split_literals = false) : m(solver.get_manager()), m_solver(solver), m_proxies(m), @@ -83,7 +82,6 @@ public: m_iuc(iuc), m_iuc_arith(iuc_arith), m_print_farkas_stats(print_farkas_stats), - m_iuc_debug_proof(iuc_debug_proof), m_old_hyp_reducer(old_hyp_reducer) {} diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index bbe53c362..735f94518 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -28,7 +28,7 @@ Revision History: #include "muz/base/dl_util.h" namespace spacer { - + // arith lemmas: second parameter specifies exact type of lemma, could be "farkas", "triangle-eq", "eq-propagate", "assign-bounds", maybe also something else bool is_arith_lemma(ast_manager& m, proof* pr) { @@ -44,7 +44,7 @@ namespace spacer { } return false; } - + bool is_farkas_lemma(ast_manager& m, proof* pr) { if (pr->get_decl_kind() == PR_TH_LEMMA) @@ -126,28 +126,28 @@ proof* ProofIteratorPostOrder::next() // open temporary dot-file std::string dotfile_path = "proof.dot"; std::ofstream dotstream(dotfile_path); - + // dump dot representation to stream pp_proof_dot_to_stream(m, dotstream, pr, iuc_pr); - + // post process dot-file, TODO: factor this out to a different place pp_proof_post_process_dot(dotfile_path,dotstream); } - + void pp_proof_dot_to_stream(ast_manager& m, std::ofstream& dotstream, proof* pr, iuc_proof* iuc_pr) { dotstream << "digraph proof { \n"; std::unordered_map id_to_small_id; unsigned counter = 0; - + ProofIteratorPostOrder it2(pr, m); while (it2.hasNext()) { proof* currentNode = it2.next(); - + SASSERT(id_to_small_id.find(currentNode->get_id()) == id_to_small_id.end()); id_to_small_id.insert(std::make_pair(currentNode->get_id(), counter)); - + std::string color = "white"; if (iuc_pr != nullptr) { @@ -169,11 +169,11 @@ proof* ProofIteratorPostOrder::next() params_ref p; p.set_uint("max_depth", 4294967295u); p.set_uint("min_alias_size", 4294967295u); - + std::ostringstream label_ostream; label_ostream << mk_pp(m.get_fact(currentNode),m,p) << "\n"; std::string label = escape_dot(label_ostream.str()); - + // compute edge-label std::string edge_label = ""; if (m.get_num_parents(currentNode) == 0) @@ -223,7 +223,7 @@ proof* ProofIteratorPostOrder::next() } } } - + // generate entry for node in dot-file dotstream << "node_" << counter << " " << "[" @@ -231,7 +231,7 @@ proof* ProofIteratorPostOrder::next() << "label=\"" << edge_label << " " << label << "\", " << "fillcolor=\"" << color << "\"" << "]\n"; - + // add entry for each edge to that node for (unsigned i = m.get_num_parents(currentNode); i > 0 ; --i) { @@ -242,12 +242,12 @@ proof* ProofIteratorPostOrder::next() << "node_" << counter << ";\n"; } - + ++counter; } dotstream << "\n}" << std::endl; } - + std::string escape_dot(const std::string &s) { std::string res; @@ -260,7 +260,7 @@ proof* ProofIteratorPostOrder::next() } return res; } - + void pp_proof_post_process_dot(std::string dot_filepath, std::ofstream &dotstream) { // replace variables in the dotfiles with nicer descriptions (hack: hard coded replacement for now) @@ -269,7 +269,7 @@ proof* ProofIteratorPostOrder::next() predicates.push_back(l1); std::vector l2 = {"L2","j","m","B"}; predicates.push_back(l2); - + for(auto& predicate : predicates) { std::string predicate_name = predicate[0]; @@ -278,17 +278,17 @@ proof* ProofIteratorPostOrder::next() std::string new_name = predicate[i+1]; std::string substring0 = predicate_name + "_" + std::to_string(i) + "_0"; std::string substringN = predicate_name + "_" + std::to_string(i) + "_n"; - + std::string command0 = "sed -i '.bak' 's/" + substring0 + "/" + new_name + "/g' " + dot_filepath; verbose_stream() << command0 << std::endl; system(command0.c_str()); - + std::string commandN = "sed -i '.bak' s/" + substringN + "/" + new_name + "\\'" + "/g " + dot_filepath; verbose_stream() << commandN << std::endl; system(commandN.c_str()); } } - + verbose_stream() << "end of postprocessing"; } @@ -297,20 +297,20 @@ proof* ProofIteratorPostOrder::next() * methods for transforming proofs * ==================================== */ - + void theory_axiom_reducer::reset() { m_cache.reset(); m_pinned.reset(); } - + proof_ref theory_axiom_reducer::reduce(proof* pr) { ProofIteratorPostOrder pit(pr, m); while (pit.hasNext()) { proof* p = pit.next(); - + if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) { // we have an arith-theory-axiom and want to get rid of it @@ -321,7 +321,7 @@ proof* ProofIteratorPostOrder::next() for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) { cls.push_back(cls_fact->get_arg(i)); } } else { cls.push_back(cls_fact); } - + // 1a) create hypothesis' ptr_buffer hyps; for (unsigned i=0; i < cls.size(); ++i) @@ -331,7 +331,7 @@ proof* ProofIteratorPostOrder::next() m_pinned.push_back(hyp); hyps.push_back(hyp); } - + // 1b) create farkas lemma: need to rebuild parameters since mk_th_lemma adds tid as first parameter unsigned num_params = p->get_decl()->get_num_parameters(); parameter const* params = p->get_decl()->get_parameters(); @@ -339,14 +339,14 @@ proof* ProofIteratorPostOrder::next() for (unsigned i = 1; i < num_params; ++i) { parameters.push_back(params[i]); } - + SASSERT(params[0].is_symbol()); family_id tid = m.mk_family_id(params[0].get_symbol()); SASSERT(tid != null_family_id); - + proof* th_lemma = m.mk_th_lemma(tid, m.mk_false(),hyps.size(), hyps.c_ptr(), num_params-1, parameters.c_ptr()); SASSERT(is_arith_lemma(m, th_lemma)); - + // 1c) create lemma proof* res = m.mk_lemma(th_lemma, cls_fact); SASSERT(m.get_fact(res) == m.get_fact(p)); @@ -386,15 +386,14 @@ proof* ProofIteratorPostOrder::next() } } } - + proof* res; - bool found = m_cache.find(pr,res); - SASSERT(found); + VERIFY(m_cache.find(pr,res)); DEBUG_CODE(proof_checker pc(m); expr_ref_vector side(m); SASSERT(pc.check(res, side)); ); - + return proof_ref(res,m); } @@ -408,16 +407,16 @@ proof* ProofIteratorPostOrder::next() m_pinned_parent_hyps.reset(); m_pinned.reset(); } - + void hypothesis_reducer::compute_hypsets(proof* pr) { ptr_vector todo; todo.push_back(pr); - + while (!todo.empty()) { proof* p = todo.back(); - + if (m_active_hyps.contains(p)) { SASSERT(m_parent_hyps.contains(p)); @@ -427,13 +426,13 @@ proof* ProofIteratorPostOrder::next() else { bool existsUnvisitedParent = false; - + // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result // for p now, but wait until those unprocessed premises are processed. for (unsigned i = 0; i < m.get_num_parents(p); ++i) { SASSERT(m.is_proof(p->get_arg(i))); proof* premise = to_app(p->get_arg(i)); - + // if we haven't visited the premise yet if (!m_active_hyps.contains(premise)) { @@ -443,7 +442,7 @@ proof* ProofIteratorPostOrder::next() existsUnvisitedParent = true; } } - + // if we already visited all premises, we can visit p too if (!existsUnvisitedParent) { @@ -453,11 +452,11 @@ proof* ProofIteratorPostOrder::next() proof_set* active_hyps = alloc(proof_set); m_pinned_active_hyps.insert(active_hyps); m_active_hyps.insert(p, active_hyps); - + expr_set* parent_hyps = alloc(expr_set); m_pinned_parent_hyps.insert(parent_hyps); m_parent_hyps.insert(p, parent_hyps); - + // fill both sets if (m.is_hypothesis(p)) { @@ -481,7 +480,7 @@ proof* ProofIteratorPostOrder::next() } } } - + // collect all units that are hyp-free and are used as hypotheses somewhere // requires that m_active_hyps and m_parent_hyps have been computed void hypothesis_reducer::collect_units(proof* pr) @@ -510,12 +509,12 @@ proof* ProofIteratorPostOrder::next() { compute_hypsets(pr); collect_units(pr); - + proof* res = compute_transformed_proof(pr); SASSERT(res != nullptr); - + proof_ref res_ref(res,m); - + reset(); DEBUG_CODE(proof_checker pc(m); expr_ref_vector side(m); @@ -523,7 +522,7 @@ proof* ProofIteratorPostOrder::next() ); return res_ref; } - + proof* hypothesis_reducer::compute_transformed_proof(proof* pf) { proof *res = NULL; @@ -559,7 +558,7 @@ proof* ProofIteratorPostOrder::next() if (todo_sz < todo.size()) { continue; } else { todo.pop_back(); } - + // here the proof transformation begins // INV: whenever we visit p, active_hyps and parent_hyps have been computed for the args. if (m.is_hypothesis(p)) @@ -578,7 +577,7 @@ proof* ProofIteratorPostOrder::next() // compute hypsets (have not been computed in general, since the unit can be anywhere in the proof) compute_hypsets(proof_of_unit); - + // if the transformation doesn't create a cycle, perform it SASSERT(m_parent_hyps.contains(proof_of_unit)); expr_set* parent_hyps = m_parent_hyps.find(proof_of_unit); @@ -622,8 +621,8 @@ proof* ProofIteratorPostOrder::next() } SASSERT(res); - m_cache.insert(p, res); - + m_cache.insert(p, res); + SASSERT(m_active_hyps.contains(res)); proof_set* active_hyps = m_active_hyps.find(res); if (active_hyps->empty() && m.has_fact(res) && m.is_false(m.get_fact(res))) @@ -634,11 +633,11 @@ proof* ProofIteratorPostOrder::next() UNREACHABLE(); return nullptr; } - + proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { SASSERT(m.is_false(m.get_fact(premise))); - + SASSERT(m_active_hyps.contains(premise)); proof_set* active_hyps = m_active_hyps.find(premise); @@ -658,7 +657,7 @@ proof* ProofIteratorPostOrder::next() negated_hyp_fact = m.is_not(hyp_fact) ? to_app(hyp_fact)->get_arg(0) : m.mk_not(hyp_fact); args.push_back(negated_hyp_fact); } - + expr_ref lemma(m); if (args.size() == 1) { @@ -731,7 +730,7 @@ proof* ProofIteratorPostOrder::next() return res; } } - + proof* hypothesis_reducer::mk_step_core(proof* old_step, ptr_buffer& args) { // if any of the literals is false, we don't need a step @@ -742,10 +741,10 @@ proof* ProofIteratorPostOrder::next() return args[i]; } } - + // otherwise build step args.push_back(to_app(m.get_fact(old_step))); // BUG: I guess this doesn't work with quantifiers (since they are no apps) - + SASSERT(old_step->get_decl()->get_arity() == args.size()); proof* res = m.mk_app(old_step->get_decl(), args.size(), (expr * const*)args.c_ptr()); m_pinned.push_back(res); diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 03c9b858b..d14cd6e91 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -60,8 +60,14 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_solvers[1] = pm.mk_fresh2(); m_fparams[1] = &pm.fparams2(); - m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_iuc_debug_proof(), p.spacer_old_hyp_reducer(), p.spacer_split_farkas_literals()); - m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_print_farkas_stats(), p.spacer_iuc_debug_proof(), p.spacer_old_hyp_reducer(), p.spacer_split_farkas_literals()); + m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), + p.spacer_iuc_arith(), + p.spacer_old_hyp_reducer(), + p.spacer_split_farkas_literals()); + m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), + p.spacer_iuc_arith(), + p.spacer_old_hyp_reducer(), + p.spacer_split_farkas_literals()); for (unsigned i = 0; i < 2; ++i) { m_contexts[i]->assert_expr(m_pm.get_background()); } diff --git a/src/muz/spacer/spacer_unsat_core_learner.h b/src/muz/spacer/spacer_unsat_core_learner.h index 16b27f4ba..a8b5965fc 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.h +++ b/src/muz/spacer/spacer_unsat_core_learner.h @@ -32,7 +32,8 @@ namespace spacer { typedef obj_hashtable expr_set; public: - unsat_core_learner(ast_manager& m, iuc_proof& pr, bool print_farkas_stats = false, bool iuc_debug_proof = false) : m(m), m_pr(pr), m_unsat_core(m), m_print_farkas_stats(print_farkas_stats), m_iuc_debug_proof(iuc_debug_proof) {}; + unsat_core_learner(ast_manager& m, iuc_proof& pr) : + m(m), m_pr(pr), m_unsat_core(m) {}; virtual ~unsat_core_learner(); ast_manager& m; @@ -60,7 +61,7 @@ namespace spacer { void set_closed(proof* p, bool value); bool is_b_open (proof *p); - + /* * adds a lemma to the unsat core */ @@ -72,7 +73,9 @@ namespace spacer { ptr_vector m_plugins; ast_mark m_closed; - expr_ref_vector m_unsat_core; // collects the lemmas of the unsat-core, will at the end be inserted into unsat_core. + // collects the lemmas of the unsat-core + // will at the end be inserted into unsat_core. + expr_ref_vector m_unsat_core; /* * computes partial core for step by delegating computation to plugins @@ -83,9 +86,7 @@ namespace spacer { * finalize computation of unsat-core */ void finalize(); - - bool m_print_farkas_stats; - bool m_iuc_debug_proof; + }; } From 83adb6742eca2e1b27d027f06fbb47d3d3f46a88 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 12:17:30 -0700 Subject: [PATCH 082/364] Remove whitespace --- src/muz/spacer/spacer_farkas_learner.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/muz/spacer/spacer_farkas_learner.h b/src/muz/spacer/spacer_farkas_learner.h index 724b18b73..0e696f4f1 100644 --- a/src/muz/spacer/spacer_farkas_learner.h +++ b/src/muz/spacer/spacer_farkas_learner.h @@ -24,12 +24,6 @@ Revision History: namespace spacer { - - - - - - class farkas_learner { typedef obj_hashtable expr_set; From 39bdecf9c283cc68966fb8aaa56aa5967e548564 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 12:26:28 -0700 Subject: [PATCH 083/364] Minor code cleanup --- src/muz/spacer/spacer_itp_solver.cpp | 34 +++++++++++++---------- src/muz/spacer/spacer_unsat_core_plugin.h | 7 ++++- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp index 71587b4c6..fabe9a57f 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_itp_solver.cpp @@ -265,10 +265,10 @@ void itp_solver::get_itp_core (expr_ref_vector &core) if (m_iuc == 0) { + // ORIGINAL PDR CODE proof_ref pr(m); pr = get_proof (); - // old code farkas_learner learner_old; learner_old.set_split_literals(m_split_literals); @@ -278,9 +278,10 @@ void itp_solver::get_itp_core (expr_ref_vector &core) } else { - proof_ref res(get_proof(),m); + // NEW IUC + proof_ref res(get_proof(), m); - // new code + // -- old hypothesis reducer while the new one is broken if (m_old_hyp_reducer) { // preprocess proof in order to get a proof which is better suited for unsat-core-extraction @@ -301,7 +302,7 @@ void itp_solver::get_itp_core (expr_ref_vector &core) iuc_after.print_farkas_stats(); } } - else + else // -- new hypothesis reducer { // preprocess proof in order to get a proof which is better suited for unsat-core-extraction if (m_print_farkas_stats) @@ -312,44 +313,49 @@ void itp_solver::get_itp_core (expr_ref_vector &core) } theory_axiom_reducer ta_reducer(m); - proof_ref pr_without_theory_lemmas = ta_reducer.reduce(res.get()); + proof_ref pr1(ta_reducer.reduce (res.get()), m); hypothesis_reducer hyp_reducer(m); - proof_ref pr_with_less_hypothesis = hyp_reducer.reduce(pr_without_theory_lemmas); + proof_ref pr2(hyp_reducer.reduce(pr1), m); + + res = pr2; if (m_print_farkas_stats) { - iuc_proof iuc_after(m, pr_with_less_hypothesis.get(), B); + iuc_proof iuc_after(m, res.get(), B); verbose_stream() << "Stats after transformation:"; iuc_after.print_farkas_stats(); } - res = pr_with_less_hypothesis; } // construct proof object with contains partition information - iuc_proof iuc_pr(m, res, B); + iuc_proof iuc_pf(m, res, B); // configure learner - unsat_core_learner learner(m, iuc_pr); + unsat_core_learner learner(m, iuc_pf); if (m_iuc_arith == 0 || m_iuc_arith > 3) { - unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, false); + unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, + learner, m_split_literals, + false /* use constants from A */); learner.register_plugin(plugin_farkas_lemma); } else if (m_iuc_arith == 1) { - unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, true); + unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, + learner, m_split_literals, + true /* use constants from A */); learner.register_plugin(plugin_farkas_lemma); } else if (m_iuc_arith == 2) { - unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); + unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m); learner.register_plugin(plugin_farkas_lemma_optimized); } else if(m_iuc_arith == 3) { - unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); + unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m); learner.register_plugin(plugin_farkas_lemma_bounded); } diff --git a/src/muz/spacer/spacer_unsat_core_plugin.h b/src/muz/spacer/spacer_unsat_core_plugin.h index 6b679f3f2..cd361000a 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.h +++ b/src/muz/spacer/spacer_unsat_core_plugin.h @@ -53,7 +53,12 @@ private: class unsat_core_plugin_farkas_lemma : public unsat_core_plugin { public: - unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, bool split_literals, bool use_constant_from_a=true) : unsat_core_plugin(learner), m_split_literals(split_literals), m_use_constant_from_a(use_constant_from_a) {}; + unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, + bool split_literals, + bool use_constant_from_a=true) : + unsat_core_plugin(learner), + m_split_literals(split_literals), + m_use_constant_from_a(use_constant_from_a) {}; void compute_partial_core(proof* step) override; From 5d3b515a5070859fa4566e6007a7b2f437089c6d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 12:36:54 -0700 Subject: [PATCH 084/364] Cleanup option names and default values --- src/muz/base/fixedpoint_params.pyg | 31 +++++++++++++++------------ src/muz/spacer/spacer_context.cpp | 14 ++++++------ src/muz/spacer/spacer_prop_solver.cpp | 8 +++---- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 65d89e420..799e06fe6 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -166,7 +166,7 @@ def_module_params('fixedpoint', ('spacer.nondet_tie_break', BOOL, False, "Break ties in obligation queue non-deterministically"), ('spacer.reach_dnf', BOOL, True, "Restrict reachability facts to DNF"), ('bmc.linear_unrolling_depth', UINT, UINT_MAX, "Maximal level to explore"), - ('spacer.split_farkas_literals', BOOL, False, "Split Farkas literals"), + ('spacer.iuc.split_farkas_literals', BOOL, False, "Split Farkas literals"), ('spacer.native_mbp', BOOL, False, "Use native mbp of Z3"), ('spacer.eq_prop', BOOL, True, "Enable equality and bound propagation in arithmetic"), ('spacer.weak_abs', BOOL, True, "Weak abstraction"), @@ -179,23 +179,26 @@ def_module_params('fixedpoint', ('spacer.vs.recheck', BOOL, False, 're-check locally during benchmark dumping'), ('spacer.mbqi', BOOL, True, 'use model-based quantifier instantiation'), ('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'), - ('spacer.instantiate', BOOL, True, 'instantiate quantified lemmas'), - ('spacer.qlemmas', BOOL, True, 'allow quantified lemmas in frames'), - ('spacer.iuc', UINT, 1, '0 = use old implementation of unsat-core-generation, 1 = use new implementation of IUC generation, 2 = use new implementation of IUC + min-cut optimization'), - ('spacer.iuc.arith', UINT, 2, '0 = use simple Farkas plugin, 1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation), 2 = use Gaussian elimination optimization, 3 = use additive IUC plugin'), - ('spacer.old_hyp_reducer', BOOL, False, 'use old hyp reducer instead of new implementation, for debugging only'), + ('spacer.q3.instantiate', BOOL, True, 'instantiate quantified lemmas'), + ('spacer.q3', BOOL, True, 'allow quantified lemmas in frames'), + ('spacer.iuc', UINT, 0, + '0 = use old implementation of unsat-core-generation, ' + + '1 = use new implementation of IUC generation, ' + + '2 = use new implementation of IUC + min-cut optimization'), + ('spacer.iuc.arith', UINT, 0, + '0 = use simple Farkas plugin, ' + + '1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation),' + + '2 = use Gaussian elimination optimization (broken), 3 = use additive IUC plugin'), + ('spacer.iuc.old_hyp_reducer', BOOL, True, 'use old hyp reducer instead of new implementation, for debugging only'), ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), - ('spacer.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), + ('spacer.iuc.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes'), ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints'), - ('spacer.use_quant_generalizer', BOOL, False, 'use quantified lemma generalizer'), - ('spacer.quic_gen_normalize', BOOL, True, 'normalize cube before quantified generalization'), - ('spacer.share_lemmas', BOOL, False, "Share frame lemmas"), - ('spacer.share_invariants', BOOL, False, "Share invariants lemmas"), + ('spacer.q3.use_qgen', BOOL, False, 'use quantified lemma generalizer'), + ('spacer.q3.qgen.normalize', BOOL, True, 'normalize cube before quantified generalization'), + ('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'), + ('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"), ('spacer.from_level', UINT, 0, 'starting level to explore'), ('spacer.print_json', SYMBOL, '', 'print pobs tree in JSON format to a given file') )) - - - diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index f700ce83e..60f6b9a85 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2026,8 +2026,8 @@ context::context(fixedpoint_params const& params, m_expanded_lvl(0), m_use_native_mbp(params.spacer_native_mbp ()), m_ground_cti (params.spacer_ground_cti ()), - m_instantiate (params.spacer_instantiate ()), - m_use_qlemmas (params.spacer_qlemmas ()), + m_instantiate (params.spacer_q3_instantiate ()), + m_use_qlemmas (params.spacer_q3()), m_weak_abs(params.spacer_weak_abs()), m_use_restarts(params.spacer_restarts()), m_restart_initial_threshold(params.spacer_restart_initial_threshold()), @@ -2303,11 +2303,11 @@ void context::init_lemma_generalizers(datalog::rule_set& rules) fparams.m_ng_lift_ite = LI_FULL; } - if (m_params.spacer_use_quant_generalizer()) { + if (m_params.spacer_q3_use_qgen()) { m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, true)); m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this, - m_params.spacer_quic_gen_normalize())); + m_params.spacer_q3_qgen_normalize())); } if (get_params().spacer_use_eqclass()) { @@ -3138,7 +3138,7 @@ lbool context::expand_node(pob& n) sanity_checker(lemma); ); - + TRACE("spacer", tout << "invariant state: " << (is_infty_level(lemma->level())?"(inductive)":"") << mk_pp(lemma->get_expr(), m) << "\n";); @@ -3649,8 +3649,8 @@ void context::new_lemma_eh(pred_transformer &pt, lemma *lem) { } if (!handle) return; - if ((is_infty_level(lem->level()) && m_params.spacer_share_invariants()) || - (!is_infty_level(lem->level()) && m_params.spacer_share_lemmas())) { + if ((is_infty_level(lem->level()) && m_params.spacer_p3_share_invariants()) || + (!is_infty_level(lem->level()) && m_params.spacer_p3_share_lemmas())) { expr_ref_vector args(m); for (unsigned i = 0; i < pt.sig_size(); ++i) { args.push_back(m.mk_const(pt.get_manager().o2n(pt.sig(i), 0))); diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index d14cd6e91..9850f9e6e 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -62,12 +62,12 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), - p.spacer_old_hyp_reducer(), - p.spacer_split_farkas_literals()); + p.spacer_iuc_old_hyp_reducer(), + p.spacer_iuc_split_farkas_literals()); m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), - p.spacer_old_hyp_reducer(), - p.spacer_split_farkas_literals()); + p.spacer_iuc_old_hyp_reducer(), + p.spacer_iuc_split_farkas_literals()); for (unsigned i = 0; i < 2; ++i) { m_contexts[i]->assert_expr(m_pm.get_background()); } From 649bab2f58867460d5f98b78bdda15a12d6ae73e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 12:44:07 -0700 Subject: [PATCH 085/364] Rename itp_solver into iuc_solver IUC stands for Interpolanted UNSAT Core --- src/muz/spacer/CMakeLists.txt | 2 +- ...r_itp_solver.cpp => spacer_iuc_solver.cpp} | 60 +++++++++---------- ...pacer_itp_solver.h => spacer_iuc_solver.h} | 34 ++++++----- src/muz/spacer/spacer_prop_solver.cpp | 10 ++-- src/muz/spacer/spacer_prop_solver.h | 6 +- 5 files changed, 57 insertions(+), 55 deletions(-) rename src/muz/spacer/{spacer_itp_solver.cpp => spacer_iuc_solver.cpp} (88%) rename src/muz/spacer/{spacer_itp_solver.h => spacer_iuc_solver.h} (85%) diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 310d9a942..dad8dfa98 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -11,7 +11,7 @@ z3_add_component(spacer spacer_smt_context_manager.cpp spacer_sym_mux.cpp spacer_util.cpp - spacer_itp_solver.cpp + spacer_iuc_solver.cpp spacer_virtual_solver.cpp spacer_legacy_mbp.cpp spacer_unsat_core_learner.cpp diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp similarity index 88% rename from src/muz/spacer/spacer_itp_solver.cpp rename to src/muz/spacer/spacer_iuc_solver.cpp index fabe9a57f..90a28c341 100644 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -3,11 +3,11 @@ Copyright (c) 2017 Arie Gurfinkel Module Name: - spacer_itp_solver.cpp + spacer_iuc_solver.cpp Abstract: - A solver that produces interpolated unsat cores + A solver that produces interpolated unsat cores (IUCs) Author: @@ -16,7 +16,7 @@ Author: Notes: --*/ -#include"muz/spacer/spacer_itp_solver.h" +#include"muz/spacer/spacer_iuc_solver.h" #include"ast/ast.h" #include"muz/spacer/spacer_util.h" #include"muz/base/proof_utils.h" @@ -27,13 +27,13 @@ Notes: #include "muz/spacer/spacer_iuc_proof.h" namespace spacer { -void itp_solver::push () +void iuc_solver::push () { m_defs.push_back (def_manager (*this)); m_solver.push (); } -void itp_solver::pop (unsigned n) +void iuc_solver::pop (unsigned n) { m_solver.pop (n); unsigned lvl = m_defs.size (); @@ -45,7 +45,7 @@ void itp_solver::pop (unsigned n) } } -app* itp_solver::fresh_proxy () +app* iuc_solver::fresh_proxy () { if (m_num_proxies == m_proxies.size()) { std::stringstream name; @@ -64,7 +64,7 @@ app* itp_solver::fresh_proxy () return m_proxies.get (m_num_proxies++); } -app* itp_solver::mk_proxy (expr *v) +app* iuc_solver::mk_proxy (expr *v) { { expr *e = v; @@ -76,7 +76,7 @@ app* itp_solver::mk_proxy (expr *v) return def.mk_proxy (v); } -bool itp_solver::mk_proxies (expr_ref_vector &v, unsigned from) +bool iuc_solver::mk_proxies (expr_ref_vector &v, unsigned from) { bool dirty = false; for (unsigned i = from, sz = v.size(); i < sz; ++i) { @@ -87,7 +87,7 @@ bool itp_solver::mk_proxies (expr_ref_vector &v, unsigned from) return dirty; } -void itp_solver::push_bg (expr *e) +void iuc_solver::push_bg (expr *e) { if (m_assumptions.size () > m_first_assumption) { m_assumptions.shrink(m_first_assumption); } @@ -95,7 +95,7 @@ void itp_solver::push_bg (expr *e) m_first_assumption = m_assumptions.size (); } -void itp_solver::pop_bg (unsigned n) +void iuc_solver::pop_bg (unsigned n) { if (n == 0) { return; } @@ -105,9 +105,9 @@ void itp_solver::pop_bg (unsigned n) m_assumptions.shrink (m_first_assumption); } -unsigned itp_solver::get_num_bg () {return m_first_assumption;} +unsigned iuc_solver::get_num_bg () {return m_first_assumption;} -lbool itp_solver::check_sat (unsigned num_assumptions, expr * const *assumptions) +lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions) { // -- remove any old assumptions if (m_assumptions.size () > m_first_assumption) @@ -128,7 +128,7 @@ lbool itp_solver::check_sat (unsigned num_assumptions, expr * const *assumptions } -app* itp_solver::def_manager::mk_proxy (expr *v) +app* iuc_solver::def_manager::mk_proxy (expr *v) { app* r; if (m_expr2proxy.find(v, r)) { return r; } @@ -146,7 +146,7 @@ app* itp_solver::def_manager::mk_proxy (expr *v) return proxy; } -bool itp_solver::def_manager::is_proxy (app *k, app_ref &def) +bool iuc_solver::def_manager::is_proxy (app *k, app_ref &def) { app *r = nullptr; bool found = m_proxy2def.find (k, r); @@ -154,20 +154,20 @@ bool itp_solver::def_manager::is_proxy (app *k, app_ref &def) return found; } -void itp_solver::def_manager::reset () +void iuc_solver::def_manager::reset () { m_expr2proxy.reset (); m_proxy2def.reset (); m_defs.reset (); } -bool itp_solver::def_manager::is_proxy_def (expr *v) +bool iuc_solver::def_manager::is_proxy_def (expr *v) { // XXX This might not be the most robust way to check return m_defs.contains (v); } -bool itp_solver::is_proxy(expr *e, app_ref &def) +bool iuc_solver::is_proxy(expr *e, app_ref &def) { if (!is_uninterp_const(e)) { return false; } @@ -183,23 +183,23 @@ bool itp_solver::is_proxy(expr *e, app_ref &def) return false; } -void itp_solver::collect_statistics (statistics &st) const +void iuc_solver::collect_statistics (statistics &st) const { m_solver.collect_statistics (st); - st.update ("time.itp_solver.itp_core", m_itp_watch.get_seconds ()); + st.update ("time.iuc_solver.iuc_core", m_iuc_watch.get_seconds ()); } -void itp_solver::reset_statistics () +void iuc_solver::reset_statistics () { - m_itp_watch.reset (); + m_iuc_watch.reset (); } -void itp_solver::get_unsat_core (ptr_vector &core) +void iuc_solver::get_unsat_core (ptr_vector &core) { m_solver.get_unsat_core (core); undo_proxies_in_core (core); } -void itp_solver::undo_proxies_in_core (ptr_vector &r) +void iuc_solver::undo_proxies_in_core (ptr_vector &r) { app_ref e(m); expr_fast_mark1 bg; @@ -222,7 +222,7 @@ void itp_solver::undo_proxies_in_core (ptr_vector &r) r.shrink (j); } -void itp_solver::undo_proxies (expr_ref_vector &r) +void iuc_solver::undo_proxies (expr_ref_vector &r) { app_ref e(m); // expand proxies @@ -233,14 +233,14 @@ void itp_solver::undo_proxies (expr_ref_vector &r) } } -void itp_solver::get_unsat_core (expr_ref_vector &_core) +void iuc_solver::get_unsat_core (expr_ref_vector &_core) { ptr_vector core; get_unsat_core (core); _core.append (core.size (), core.c_ptr ()); } -void itp_solver::elim_proxies (expr_ref_vector &v) +void iuc_solver::elim_proxies (expr_ref_vector &v) { expr_ref f = mk_and (v); scoped_ptr rep = mk_expr_simp_replacer (m); @@ -250,9 +250,9 @@ void itp_solver::elim_proxies (expr_ref_vector &v) flatten_and (f, v); } -void itp_solver::get_itp_core (expr_ref_vector &core) +void iuc_solver::get_iuc(expr_ref_vector &core) { - scoped_watch _t_ (m_itp_watch); + scoped_watch _t_ (m_iuc_watch); typedef obj_hashtable expr_set; expr_set B; @@ -379,12 +379,12 @@ void itp_solver::get_itp_core (expr_ref_vector &core) } IF_VERBOSE(2, - verbose_stream () << "Itp Core:\n" + verbose_stream () << "IUC Core:\n" << mk_pp (mk_and (core), m) << "\n";); } -void itp_solver::refresh () +void iuc_solver::refresh () { // only refresh in non-pushed state SASSERT (m_defs.size () == 0); diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_iuc_solver.h similarity index 85% rename from src/muz/spacer/spacer_itp_solver.h rename to src/muz/spacer/spacer_iuc_solver.h index 0b3527bb3..568124629 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -3,7 +3,7 @@ Copyright (c) 2017 Arie Gurfinkel Module Name: - spacer_itp_solver.h + spacer_iuc_solver.h Abstract: @@ -16,23 +16,23 @@ Author: Notes: --*/ -#ifndef SPACER_ITP_SOLVER_H_ -#define SPACER_ITP_SOLVER_H_ +#ifndef SPACER_IUC_SOLVER_H_ +#define SPACER_IUC_SOLVER_H_ #include"solver/solver.h" #include"ast/expr_substitution.h" #include"util/stopwatch.h" namespace spacer { -class itp_solver : public solver { +class iuc_solver : public solver { private: struct def_manager { - itp_solver &m_parent; + iuc_solver &m_parent; obj_map m_expr2proxy; obj_map m_proxy2def; expr_ref_vector m_defs; - def_manager(itp_solver &parent) : + def_manager(iuc_solver &parent) : m_parent(parent), m_defs(m_parent.m) {} @@ -54,7 +54,7 @@ private: unsigned m_first_assumption; bool m_is_proxied; - stopwatch m_itp_watch; + stopwatch m_iuc_watch; expr_substitution m_elim_proxies_sub; bool m_split_literals; @@ -68,7 +68,9 @@ private: app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); public: - itp_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool old_hyp_reducer, bool split_literals = false) : + iuc_solver(solver &solver, unsigned iuc, unsigned iuc_arith, + bool print_farkas_stats, bool old_hyp_reducer, + bool split_literals = false) : m(solver.get_manager()), m_solver(solver), m_proxies(m), @@ -85,11 +87,11 @@ public: m_old_hyp_reducer(old_hyp_reducer) {} - ~itp_solver() override {} + ~iuc_solver() override {} - /* itp solver specific */ + /* iuc solver specific */ void get_unsat_core(expr_ref_vector &core) override; - virtual void get_itp_core(expr_ref_vector &core); + virtual void get_iuc(expr_ref_vector &core); void set_split_literals(bool v) {m_split_literals = v;} bool mk_proxies(expr_ref_vector &v, unsigned from = 0); void undo_proxies(expr_ref_vector &v); @@ -149,22 +151,22 @@ public: virtual void refresh(); class scoped_mk_proxy { - itp_solver &m_s; + iuc_solver &m_s; expr_ref_vector &m_v; public: - scoped_mk_proxy(itp_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) + scoped_mk_proxy(iuc_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) {m_s.mk_proxies(m_v);} ~scoped_mk_proxy() {m_s.undo_proxies(m_v);} }; class scoped_bg { - itp_solver &m_s; + iuc_solver &m_s; unsigned m_bg_sz; public: - scoped_bg(itp_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} + scoped_bg(iuc_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} ~scoped_bg() - {if (m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }} + {if(m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }} }; }; } diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 9850f9e6e..39db6129c 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -60,11 +60,11 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_solvers[1] = pm.mk_fresh2(); m_fparams[1] = &pm.fparams2(); - m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_iuc(), + m_contexts[0] = alloc(spacer::iuc_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_iuc_old_hyp_reducer(), p.spacer_iuc_split_farkas_literals()); - m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_iuc(), + m_contexts[1] = alloc(spacer::iuc_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_iuc_old_hyp_reducer(), p.spacer_iuc_split_farkas_literals()); @@ -138,7 +138,7 @@ void prop_solver::assert_expr(expr * form, unsigned level) lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) { // replace expressions by assumption literals - itp_solver::scoped_mk_proxy _p_(*m_ctx, hard); + iuc_solver::scoped_mk_proxy _p_(*m_ctx, hard); unsigned hard_sz = hard.size(); // assume soft constraints are propositional literals (no need to proxy) hard.append(soft); @@ -234,7 +234,7 @@ lbool prop_solver::internal_check_assumptions( if (result == l_false && m_core && m.proofs_enabled() && !m_subset_based_core) { TRACE("spacer", tout << "theory core\n";); m_core->reset(); - m_ctx->get_itp_core(*m_core); + m_ctx->get_iuc(*m_core); } else if (result == l_false && m_core) { m_core->reset(); m_ctx->get_unsat_core(*m_core); @@ -263,7 +263,7 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, // can be disabled if use_push_bg == true // solver::scoped_push _s_(*m_ctx); if (!m_use_push_bg) { m_ctx->push(); } - itp_solver::scoped_bg _b_(*m_ctx); + iuc_solver::scoped_bg _b_(*m_ctx); for (unsigned i = 0; i < num_bg; ++i) if (m_use_push_bg) { m_ctx->push_bg(bg [i]); } diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 4aedb9676..295b47982 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -31,7 +31,7 @@ Revision History: #include "util/vector.h" #include "muz/spacer/spacer_manager.h" #include "muz/spacer/spacer_smt_context_manager.h" -#include "muz/spacer/spacer_itp_solver.h" +#include "muz/spacer/spacer_iuc_solver.h" struct fixedpoint_params; @@ -45,8 +45,8 @@ private: symbol m_name; smt_params* m_fparams[2]; solver* m_solvers[2]; - scoped_ptr m_contexts[2]; - itp_solver * m_ctx; + scoped_ptr m_contexts[2]; + iuc_solver * m_ctx; smt_params * m_ctx_fparams; decl_vector m_level_preds; app_ref_vector m_pos_level_atoms; // atoms used to identify level From 3bc3b00fdd0f34b22722b07c2a72b285b4d82302 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 15 May 2018 15:29:29 -0700 Subject: [PATCH 086/364] Post merge compile fixes --- src/muz/spacer/CMakeLists.txt | 1 + src/muz/spacer/spacer_context.cpp | 6 +- src/muz/spacer/spacer_context.h | 2 +- src/muz/spacer/spacer_dl_interface.cpp | 2 +- src/muz/spacer/spacer_dl_interface.h | 4 +- src/muz/spacer/spacer_generalizers.cpp | 2 +- src/muz/spacer/spacer_iuc_proof.cpp | 51 ++++++------ src/muz/spacer/spacer_iuc_solver.cpp | 2 +- src/muz/spacer/spacer_json.h | 4 +- src/muz/spacer/spacer_proof_utils.cpp | 81 ++++---------------- src/muz/spacer/spacer_proof_utils.h | 35 +++------ src/muz/spacer/spacer_unsat_core_learner.cpp | 5 +- 12 files changed, 66 insertions(+), 129 deletions(-) diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index dad8dfa98..a7bc74b88 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -14,6 +14,7 @@ z3_add_component(spacer spacer_iuc_solver.cpp spacer_virtual_solver.cpp spacer_legacy_mbp.cpp + spacer_proof_utils.cpp spacer_unsat_core_learner.cpp spacer_unsat_core_plugin.cpp spacer_matrix.cpp diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 60f6b9a85..5de817435 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -40,7 +40,7 @@ Notes: #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" -#include "ast/proof_checker/proof_checker.h" +#include "ast/proofs/proof_checker.h" #include "smt/smt_value_sort.h" #include "ast/scoped_proof.h" #include "muz/spacer/spacer_qe_project.h" @@ -3618,9 +3618,9 @@ expr_ref context::get_constraints (unsigned level) return m_pm.mk_and (constraints); } -void context::add_constraint (unsigned level, const expr_ref& c) +void context::add_constraint (expr *c, unsigned level) { - if (!c.get()) { return; } + if (!c) { return; } if (m.is_true(c)) { return; } expr *e1, *e2; diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index ff0e90cfa..596e5e047 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -914,7 +914,7 @@ public: pob& get_root() const { return m_pob_queue.get_root(); } expr_ref get_constraints (unsigned lvl); - void add_constraint (unsigned lvl, const expr_ref& c); + void add_constraint (expr *c, unsigned lvl); void new_lemma_eh(pred_transformer &pt, lemma *lem); diff --git a/src/muz/spacer/spacer_dl_interface.cpp b/src/muz/spacer/spacer_dl_interface.cpp index 69e5d76ff..b121cf50a 100644 --- a/src/muz/spacer/spacer_dl_interface.cpp +++ b/src/muz/spacer/spacer_dl_interface.cpp @@ -362,5 +362,5 @@ void dl_interface::add_callback(void *state, } void dl_interface::add_constraint (expr *c, unsigned lvl){ - m_context->add_constraint(c,lvl); + m_context->add_constraint(c, lvl); } diff --git a/src/muz/spacer/spacer_dl_interface.h b/src/muz/spacer/spacer_dl_interface.h index 2980e2a0f..9fc60dcf0 100644 --- a/src/muz/spacer/spacer_dl_interface.h +++ b/src/muz/spacer/spacer_dl_interface.h @@ -82,9 +82,9 @@ public: void add_callback(void *state, const datalog::t_new_lemma_eh new_lemma_eh, const datalog::t_predecessor_eh predecessor_eh, - const datalog::t_unfold_eh unfold_eh); + const datalog::t_unfold_eh unfold_eh) override; - void add_constraint (expr *c, unsigned lvl); + void add_constraint (expr *c, unsigned lvl) override; }; } diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 4b0c3d15c..3f6e28be6 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -162,7 +162,7 @@ void unsat_core_generalizer::operator()(lemma_ref &lemma) unsigned uses_level; expr_ref_vector core(m); - VERIFY(pt.is_invariant(old_level, lemma->get_expr(), uses_level, &core)); + VERIFY(pt.is_invariant(lemma->level(), lemma.get(), uses_level, &core)); CTRACE("spacer", old_sz > core.size(), tout << "unsat core reduced lemma from: " diff --git a/src/muz/spacer/spacer_iuc_proof.cpp b/src/muz/spacer/spacer_iuc_proof.cpp index 889f06af2..966b59df4 100644 --- a/src/muz/spacer/spacer_iuc_proof.cpp +++ b/src/muz/spacer/spacer_iuc_proof.cpp @@ -1,8 +1,7 @@ - - #include "muz/spacer/spacer_iuc_proof.h" #include "ast/for_each_expr.h" #include "ast/array_decl_plugin.h" +#include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_proof_utils.h" namespace spacer { @@ -18,7 +17,7 @@ namespace spacer { collect_symbols_b(b_conjuncts); compute_marks(b_conjuncts); } - + proof* iuc_proof::get() { return m_pr.get(); @@ -33,7 +32,7 @@ namespace spacer { func_decl_set& m_symbs; public: collect_pure_proc(func_decl_set& s):m_symbs(s) {} - + void operator()(app* a) { if (a->get_family_id() == null_family_id) { m_symbs.insert(a->get_decl()); @@ -42,7 +41,7 @@ namespace spacer { void operator()(var*) {} void operator()(quantifier*) {} }; - + void iuc_proof::collect_symbols_b(expr_set& b_conjuncts) { expr_mark visited; @@ -52,18 +51,18 @@ namespace spacer { for_each_expr(proc, visited, *it); } } - + class is_pure_expr_proc { func_decl_set const& m_symbs; array_util m_au; public: struct non_pure {}; - + is_pure_expr_proc(func_decl_set const& s, ast_manager& m): m_symbs(s), m_au (m) {} - + void operator()(app* a) { if (a->get_family_id() == null_family_id) { if (!m_symbs.contains(a->get_decl())) { @@ -78,7 +77,7 @@ namespace spacer { void operator()(var*) {} void operator()(quantifier*) {} }; - + // requires that m_symbols_b has already been computed, which is done during initialization. bool iuc_proof::only_contains_symbols_b(expr* expr) const { @@ -92,26 +91,26 @@ namespace spacer { } return true; } - + /* * ==================================== * methods for computing which premises * have been used to derive the conclusions * ==================================== */ - + void iuc_proof::compute_marks(expr_set& b_conjuncts) { - ProofIteratorPostOrder it(m_pr, m); + proof_post_order it(m_pr, m); while (it.hasNext()) { proof* currentNode = it.next(); - + if (m.get_num_parents(currentNode) == 0) { switch(currentNode->get_decl_kind()) { - + case PR_ASSERTED: // currentNode is an axiom { if (b_conjuncts.contains(m.get_fact(currentNode))) @@ -142,23 +141,23 @@ namespace spacer { bool need_to_mark_a = false; bool need_to_mark_b = false; bool need_to_mark_h = false; - + for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { SASSERT(m.is_proof(currentNode->get_arg(i))); proof* premise = to_app(currentNode->get_arg(i)); - + need_to_mark_a = need_to_mark_a || m_a_mark.is_marked(premise); need_to_mark_b = need_to_mark_b || m_b_mark.is_marked(premise); need_to_mark_h = need_to_mark_h || m_h_mark.is_marked(premise); } - + // if current node is application of lemma, we know that all hypothesis are removed if(currentNode->get_decl_kind() == PR_LEMMA) { need_to_mark_h = false; } - + // save results m_a_mark.mark(currentNode, need_to_mark_a); m_b_mark.mark(currentNode, need_to_mark_b); @@ -166,7 +165,7 @@ namespace spacer { } } } - + bool iuc_proof::is_a_marked(proof* p) { return m_a_mark.is_marked(p); @@ -179,7 +178,7 @@ namespace spacer { { return m_h_mark.is_marked(p); } - + /* * ==================================== * methods for dot printing @@ -195,22 +194,22 @@ namespace spacer { * statistics * ==================================== */ - + void iuc_proof::print_farkas_stats() { unsigned farkas_counter = 0; unsigned farkas_counter2 = 0; - - ProofIteratorPostOrder it3(m_pr, m); + + proof_post_order it3(m_pr, m); while (it3.hasNext()) { proof* currentNode = it3.next(); - + // if node is theory lemma if (is_farkas_lemma(m, currentNode)) { farkas_counter++; - + // check whether farkas lemma is to be interpolated (could potentially miss farkas lemmas, which are interpolated, because we potentially don't want to use the lowest cut) bool has_blue_nonred_parent = false; for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) @@ -229,7 +228,7 @@ namespace spacer { } } } - + verbose_stream() << "\nThis proof contains " << farkas_counter << " Farkas lemmas. " << farkas_counter2 << " Farkas lemmas participate in the lowest cut\n"; } } diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 90a28c341..e2334f1ba 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -19,7 +19,7 @@ Notes: #include"muz/spacer/spacer_iuc_solver.h" #include"ast/ast.h" #include"muz/spacer/spacer_util.h" -#include"muz/base/proof_utils.h" +#include"ast/proofs/proof_utils.h" #include"muz/spacer/spacer_farkas_learner.h" #include"ast/rewriter/expr_replacer.h" #include"muz/spacer/spacer_unsat_core_learner.h" diff --git a/src/muz/spacer/spacer_json.h b/src/muz/spacer/spacer_json.h index c75110371..b100a87dc 100644 --- a/src/muz/spacer/spacer_json.h +++ b/src/muz/spacer/spacer_json.h @@ -22,8 +22,8 @@ Notes: #include #include -#include "ref.h" -#include "ref_vector.h" +#include "util/ref.h" +#include "util/ref_vector.h" class ast; diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 735f94518..32391db91 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -16,20 +16,22 @@ Revision History: --*/ -#include "muz/spacer/spacer_proof_utils.h" -#include "ast/ast_util.h" - -#include "ast/ast_pp.h" - -#include "ast/proof_checker/proof_checker.h" #include -#include "params.h" -#include "muz/spacer/spacer_iuc_proof.h" +#include "util/params.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/proofs/proof_checker.h" #include "muz/base/dl_util.h" +#include "muz/spacer/spacer_iuc_proof.h" + +#include "ast/proofs/proof_utils.h" +#include "muz/spacer/spacer_proof_utils.h" namespace spacer { - // arith lemmas: second parameter specifies exact type of lemma, could be "farkas", "triangle-eq", "eq-propagate", "assign-bounds", maybe also something else + // arith lemmas: second parameter specifies exact type of lemma, + // could be "farkas", "triangle-eq", "eq-propagate", + // "assign-bounds", maybe also something else bool is_arith_lemma(ast_manager& m, proof* pr) { if (pr->get_decl_kind() == PR_TH_LEMMA) @@ -61,57 +63,6 @@ namespace spacer { return false; } - /* - * ==================================== - * methods for proof traversal - * ==================================== - */ -ProofIteratorPostOrder::ProofIteratorPostOrder(proof* root, ast_manager& manager) : m(manager) -{m_todo.push_back(root);} - -bool ProofIteratorPostOrder::hasNext() -{return !m_todo.empty();} - -/* - * iterative post-order depth-first search (DFS) through the proof DAG - */ -proof* ProofIteratorPostOrder::next() -{ - while (!m_todo.empty()) { - proof* currentNode = m_todo.back(); - - // if we haven't already visited the current unit - if (!m_visited.is_marked(currentNode)) { - bool existsUnvisitedParent = false; - - // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result - // for currentProof now, but wait until those unprocessed premises are processed. - for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { - SASSERT(m.is_proof(currentNode->get_arg(i))); - proof* premise = to_app(currentNode->get_arg(i)); - - // if we haven't visited the current premise yet - if (!m_visited.is_marked(premise)) { - // add it to the stack - m_todo.push_back(premise); - existsUnvisitedParent = true; - } - } - - // if we already visited all parent-inferences, we can visit the inference too - if (!existsUnvisitedParent) { - m_visited.mark(currentNode, true); - m_todo.pop_back(); - return currentNode; - } - } else { - m_todo.pop_back(); - } - } - // we have already iterated through all inferences - return NULL; -} - /* * ==================================== * methods for dot printing @@ -140,7 +91,7 @@ proof* ProofIteratorPostOrder::next() std::unordered_map id_to_small_id; unsigned counter = 0; - ProofIteratorPostOrder it2(pr, m); + proof_post_order it2(pr, m); while (it2.hasNext()) { proof* currentNode = it2.next(); @@ -306,7 +257,7 @@ proof* ProofIteratorPostOrder::next() proof_ref theory_axiom_reducer::reduce(proof* pr) { - ProofIteratorPostOrder pit(pr, m); + proof_post_order pit(pr, m); while (pit.hasNext()) { proof* p = pit.next(); @@ -468,11 +419,11 @@ proof* ProofIteratorPostOrder::next() for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { proof* pp = m.get_parent(p, i); - datalog::set_union(*parent_hyps, *m_parent_hyps.find(pp)); + set_union(*parent_hyps, *m_parent_hyps.find(pp)); if (!m.is_lemma(p)) // lemmas clear all hypotheses { - datalog::set_union(*active_hyps, *m_active_hyps.find(pp)); + set_union(*active_hyps, *m_active_hyps.find(pp)); } } } @@ -488,7 +439,7 @@ proof* ProofIteratorPostOrder::next() expr_set* all_hyps = m_parent_hyps.find(pr); SASSERT(all_hyps != nullptr); - ProofIteratorPostOrder pit(pr, m); + proof_post_order pit(pr, m); while (pit.hasNext()) { proof* p = pit.next(); if (!m.is_hypothesis(p)) diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index abd612b57..e47c9882a 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -21,24 +21,9 @@ Revision History: #include "ast/ast.h" namespace spacer { - + bool is_arith_lemma(ast_manager& m, proof* pr); bool is_farkas_lemma(ast_manager& m, proof* pr); -/* - * iterator, which traverses the proof in depth-first post-order. - */ -class ProofIteratorPostOrder { -public: - ProofIteratorPostOrder(proof* refutation, ast_manager& manager); - bool hasNext(); - proof* next(); - -private: - ptr_vector m_todo; - ast_mark m_visited; // the proof nodes we have already visited - - ast_manager& m; -}; /* * prints the proof pr in dot representation to the file proof.dot @@ -46,7 +31,7 @@ private: */ class iuc_proof; void pp_proof_dot(ast_manager& m, proof* pr, iuc_proof* iuc_pr = nullptr); - + class theory_axiom_reducer { public: @@ -54,16 +39,16 @@ private: // reduce theory axioms and return transformed proof proof_ref reduce(proof* pr); - + private: ast_manager &m; - + // tracking all created expressions expr_ref_vector m_pinned; - + // maps each proof of a clause to the transformed subproof of that clause obj_map m_cache; - + void reset(); }; @@ -71,7 +56,7 @@ private: { public: hypothesis_reducer(ast_manager &m) : m(m), m_pinned(m) {} - + // reduce hypothesis and return transformed proof proof_ref reduce(proof* pf); @@ -80,7 +65,7 @@ private: typedef obj_hashtable proof_set; ast_manager &m; - + expr_ref_vector m_pinned; // tracking all created expressions ptr_vector m_pinned_active_hyps; // tracking all created sets of active hypothesis ptr_vector m_pinned_parent_hyps; // tracking all created sets of parent hypothesis @@ -89,12 +74,12 @@ private: obj_map m_units; // maps each unit literal to the subproof of that unit obj_map m_active_hyps; // maps each proof of a clause to the set of proofs of active hypothesis' of the clause obj_map m_parent_hyps; // maps each proof of a clause to the hypothesis-fact, which are transitive parents of that clause, needed to avoid creating cycles in the proof. - + void reset(); void compute_hypsets(proof* pr); // compute active_hyps and parent_hyps for pr void collect_units(proof* pr); // compute m_units proof* compute_transformed_proof(proof* pf); - + proof* mk_lemma_core(proof *pf, expr *fact); proof* mk_unit_resolution_core(ptr_buffer& args); proof* mk_step_core(proof* old_step, ptr_buffer& args); diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index cb7ebff76..bb50cc5c7 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -22,6 +22,7 @@ Revision History: #include "muz/spacer/spacer_iuc_proof.h" #include "ast/for_each_expr.h" +#include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_util.h" @@ -54,7 +55,7 @@ void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { SASSERT(m.is_proof(currentNode->get_arg(i))); proof* premise = to_app(currentNode->get_arg(i)); - + need_to_mark_closed = need_to_mark_closed && (!m_pr.is_b_marked(premise) || m_closed.is_marked(premise)); } @@ -109,7 +110,7 @@ void unsat_core_learner::set_closed(proof* p, bool value) { m_closed.mark(p, value); } - + bool unsat_core_learner::is_b_open(proof *p) { return m_pr.is_b_marked(p) && !is_closed (p); From ffdefa4f65c9f0dbbcc9f8e8877a3066366d56e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 May 2018 12:51:59 -0700 Subject: [PATCH 087/364] add todo Signed-off-by: Nikolaj Bjorner --- todo.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 todo.txt diff --git a/todo.txt b/todo.txt new file mode 100644 index 000000000..82038c3b4 --- /dev/null +++ b/todo.txt @@ -0,0 +1,9 @@ +- consolidate virtual-solver and pool solver + +- fixup mbp/mbi + +- API additions to expose functionality + +- Equality solver + +- Generalizer index \ No newline at end of file From b649cd93cb259d357754737c26a7a57b298fa191 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 15 May 2018 07:40:37 -0700 Subject: [PATCH 088/364] change pool solver to enable external control of pool allocation Signed-off-by: Nikolaj Bjorner --- src/solver/solver_pool.cpp | 44 ++++++++++++++++++-------------------- src/solver/solver_pool.h | 21 +++++++++++++----- src/test/CMakeLists.txt | 1 + src/test/main.cpp | 1 + src/test/solver_pool.cpp | 34 +++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 src/test/solver_pool.cpp diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 6d08b95af..132a2bec4 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -17,10 +17,10 @@ Notes: --*/ -#include "solver/solver_pool.h" -#include "solver/solver_na2as.h" #include "ast/proofs/proof_utils.h" #include "ast/ast_util.h" +#include "solver/solver_pool.h" + class pool_solver : public solver_na2as { solver_pool& m_pool; @@ -33,7 +33,6 @@ class pool_solver : public solver_na2as { bool m_pushed; bool m_in_delayed_scope; unsigned m_dump_counter; - bool is_virtual() const { return !m.is_true(m_pred); } public: pool_solver(solver* b, solver_pool& pool, app_ref& pred): @@ -240,10 +239,8 @@ public: } }; -solver_pool::solver_pool(solver* base_solver, unsigned num_solvers_per_pool): - m_base_solver(base_solver), - m_num_solvers_per_pool(num_solvers_per_pool), - m_num_solvers_in_last_pool(0) +solver_pool::solver_pool(solver* base_solver): + m_base_solver(base_solver) {} @@ -261,10 +258,10 @@ ptr_vector solver_pool::get_base_solvers() const { void solver_pool::collect_statistics(statistics &st) const { ptr_vector solvers = get_base_solvers(); for (solver* s : solvers) s->collect_statistics(st); - st.update("time.pool_solver.smt.total", m_check_watch.get_seconds()); - st.update("time.pool_solver.smt.total.sat", m_check_sat_watch.get_seconds()); - st.update("time.pool_solver.smt.total.undef", m_check_undef_watch.get_seconds()); - st.update("time.pool_solver.proof", m_proof_watch.get_seconds()); + st.update("pool_solver.time.total", m_check_watch.get_seconds()); + st.update("pool_solver.time.total.sat", m_check_sat_watch.get_seconds()); + st.update("pool_solver.time.total.undef", m_check_undef_watch.get_seconds()); + st.update("pool_solver.time.proof", m_proof_watch.get_seconds()); st.update("pool_solver.checks", m_stats.m_num_checks); st.update("pool_solver.checks.sat", m_stats.m_num_sat_checks); st.update("pool_solver.checks.undef", m_stats.m_num_undef_checks); @@ -285,24 +282,25 @@ void solver_pool::reset_statistics() { } solver* solver_pool::mk_solver() { - ref base_solver; ast_manager& m = m_base_solver->get_manager(); - if (m_solvers.empty() || m_num_solvers_in_last_pool == m_num_solvers_per_pool) { - base_solver = m_base_solver->translate(m, m_base_solver->get_params()); - m_num_solvers_in_last_pool = 0; - } - else { - base_solver = dynamic_cast(m_solvers.back())->base_solver(); - } - m_num_solvers_in_last_pool++; - std::stringstream name; - name << "vsolver#" << m_solvers.size(); - app_ref pred(m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()), m); + ref base_solver = m_base_solver->translate(m, m_base_solver->get_params()); + app_ref pred(m.mk_true(), m); pool_solver* solver = alloc(pool_solver, base_solver.get(), *this, pred); m_solvers.push_back(solver); return solver; } +solver* solver_pool::clone_solver(solver* psolver) { + ast_manager& m = m_base_solver->get_manager(); + solver* base_solver = dynamic_cast(psolver)->base_solver(); + std::stringstream name; + name << "vsolver#" << m_solvers.size(); + app_ref pred(m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()), m); + pool_solver* solver = alloc(pool_solver, base_solver, *this, pred); + m_solvers.push_back(solver); + return solver; +} + void solver_pool::reset_solver(solver* s) { pool_solver* ps = dynamic_cast(s); SASSERT(ps); diff --git a/src/solver/solver_pool.h b/src/solver/solver_pool.h index d676ca54d..a1f650eb0 100644 --- a/src/solver/solver_pool.h +++ b/src/solver/solver_pool.h @@ -18,11 +18,20 @@ Notes: This is a revision of spacer_virtual_solver by Arie Gurfinkel + +it is not quite the same + there are good reasons to do that management at a higher level. +not the same == in spacer, there is an upper bound on the number of base solvers (i.e., number of pools). +Then the pools are distributed between different predicate transformers. I can't just switch to solver_pool, +since this, in most configuration, will result in either few solvers (which is bad for most of my benchmarks), +or one solver per predicate transformer (which is bad for a few benchmarks with many predicates). + + --*/ #ifndef SOLVER_POOL_H_ #define SOLVER_POOL_H_ #include "solver/solver.h" +#include "solver/solver_na2as.h" #include "util/stopwatch.h" class pool_solver; @@ -39,11 +48,9 @@ class solver_pool { void reset() { memset(this, 0, sizeof(*this)); } }; - ref m_base_solver; - unsigned m_num_solvers_per_pool; - unsigned m_num_solvers_in_last_pool; + ref m_base_solver; sref_vector m_solvers; - stats m_stats; + stats m_stats; stopwatch m_check_watch; stopwatch m_check_sat_watch; @@ -55,13 +62,17 @@ class solver_pool { ptr_vector get_base_solvers() const; public: - solver_pool(solver* base_solver, unsigned num_solvers_per_pool); + solver_pool(solver* base_solver); void collect_statistics(statistics &st) const; void reset_statistics(); + // create a fresh pool solver solver* mk_solver(); + // clone an existing pool solver + solver* clone_solver(solver* pool_solver); + void reset_solver(solver* s); }; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index b487fe9ba..da4194a2c 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -102,6 +102,7 @@ add_executable(test-z3 small_object_allocator.cpp smt2print_parse.cpp smt_context.cpp + solver_pool.cpp sorting_network.cpp stack.cpp string_buffer.cpp diff --git a/src/test/main.cpp b/src/test/main.cpp index 64f754667..c1f169a3a 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -248,6 +248,7 @@ int main(int argc, char ** argv) { TST_ARGV(sat_local_search); TST_ARGV(cnf_backbones); TST(bdd); + TST(solver_pool); //TST_ARGV(hs); } diff --git a/src/test/solver_pool.cpp b/src/test/solver_pool.cpp new file mode 100644 index 000000000..a0b6832c2 --- /dev/null +++ b/src/test/solver_pool.cpp @@ -0,0 +1,34 @@ +#include "ast/reg_decl_plugins.h" +#include "solver/solver_pool.h" +#include "smt/smt_solver.h" + +void tst_solver_pool() { + ast_manager m; + reg_decl_plugins(m); + params_ref p; + ref base = mk_smt_solver(m, p, symbol::null); + solver_pool pool(base.get()); + + ref s1 = pool.mk_solver(); + ref s2 = pool.clone_solver(s1.get()); + ref s3 = pool.clone_solver(s1.get()); + + ref s4 = pool.mk_solver(); + ref s5 = pool.clone_solver(s4.get()); + ref s6 = pool.clone_solver(s4.get()); + + expr_ref a(m.mk_const(symbol("a"), m.mk_bool_sort()), m); + expr_ref b(m.mk_const(symbol("b"), m.mk_bool_sort()), m); + expr_ref c(m.mk_const(symbol("c"), m.mk_bool_sort()), m); + expr_ref d(m.mk_const(symbol("d"), m.mk_bool_sort()), m); + expr_ref fml(m); + fml = m.mk_or(a, b); + s1->assert_expr(fml); + fml = m.mk_and(a,b); + s2->assert_expr(fml); + expr_ref_vector asms(m); + asms.push_back(m.mk_not(a)); + std::cout << s1->check_sat(asms) << "\n"; + std::cout << s2->check_sat(asms) << "\n"; + std::cout << s3->check_sat(asms) << "\n"; +} From c893740e131eb39ee9dfdb5b187d7d45f545ac88 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 May 2018 08:57:48 -0700 Subject: [PATCH 089/364] Fix compilation --- src/muz/spacer/spacer_json.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/muz/spacer/spacer_json.cpp b/src/muz/spacer/spacer_json.cpp index 8b7a0878e..c1094f192 100644 --- a/src/muz/spacer/spacer_json.cpp +++ b/src/muz/spacer/spacer_json.cpp @@ -77,7 +77,7 @@ namespace spacer { std::ostringstream ls; for (auto l:lemmas) { - ls << (ls.tellp() == 0 ? "" : ","); + ls << ((unsigned)ls.tellp() == 0 ? "" : ","); json_marshal(ls, l); } out << "[" << ls.str() << "]"; @@ -104,11 +104,11 @@ namespace spacer { for (auto &pob_map:m_relations) { std::ostringstream pob_lemmas; for (auto &depth_lemmas : pob_map.second) { - pob_lemmas << (pob_lemmas.tellp() == 0 ? "" : ",") << "\"" << depth_lemmas.first << "\":"; + pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") << "\"" << depth_lemmas.first << "\":"; json_marshal(pob_lemmas, depth_lemmas.second); } if (pob_lemmas.tellp()) { - lemmas << (lemmas.tellp() == 0 ? "" : ",\n"); + lemmas << ((unsigned)lemmas.tellp() == 0 ? "" : ",\n"); lemmas << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; } pob_id++; @@ -127,7 +127,7 @@ namespace spacer { std::ostringstream pob_expr; json_marshal(pob_expr, n->post(), n->get_ast_manager()); - nodes << (nodes.tellp() == 0 ? "" : ",\n") << + nodes << ((unsigned)nodes.tellp() == 0 ? "" : ",\n") << "{\"id\":\"" << depth << n << "\",\"relative_time\":\"" << expand_time / root_expand_time << "\",\"absolute_time\":\"" << std::setprecision(2) << expand_time << @@ -137,7 +137,7 @@ namespace spacer { "\",\"depth\":\"" << depth << "\",\"expr\":" << pob_expr.str() << "}"; if (n->parent()) { - edges << (edges.tellp() == 0 ? "" : ",\n") << + edges << ((unsigned)edges.tellp() == 0 ? "" : ",\n") << "{\"from\":\"" << depth << n->parent() << "\",\"to\":\"" << depth << n << "\"}"; } From 49821e7c91fe6931c938edf629300ee87891bef2 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 May 2018 08:58:37 -0700 Subject: [PATCH 090/364] Revised solver_pool --- src/solver/solver.h | 4 ++ src/solver/solver_pool.cpp | 134 +++++++++++++++++++++++-------------- src/solver/solver_pool.h | 22 ++---- src/test/solver_pool.cpp | 27 +++++--- 4 files changed, 110 insertions(+), 77 deletions(-) diff --git a/src/solver/solver.h b/src/solver/solver.h index 2c056ff90..bb330636f 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -229,4 +229,8 @@ protected: typedef ref solver_ref; +inline std::ostream& operator<<(std::ostream& out, solver const& s) { + return s.display(out); +} + #endif diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 132a2bec4..3cf39a64c 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -17,10 +17,10 @@ Notes: --*/ +#include "solver/solver_pool.h" +#include "solver/solver_na2as.h" #include "ast/proofs/proof_utils.h" #include "ast/ast_util.h" -#include "solver/solver_pool.h" - class pool_solver : public solver_na2as { solver_pool& m_pool; @@ -33,6 +33,7 @@ class pool_solver : public solver_na2as { bool m_pushed; bool m_in_delayed_scope; unsigned m_dump_counter; + bool is_virtual() const { return !m.is_true(m_pred); } public: pool_solver(solver* b, solver_pool& pool, app_ref& pred): @@ -66,6 +67,8 @@ public: void updt_params(params_ref const& p) override { solver::updt_params(p); m_base->updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_base->collect_param_descrs(r); } void collect_statistics(statistics & st) const override { m_base->collect_statistics(st); } + unsigned get_num_assertions() const override { return m_base->get_num_assertions(); } + expr * get_assertion(unsigned idx) const override { return m_base->get_assertion(idx); } void get_unsat_core(ptr_vector & r) override { m_base->get_unsat_core(r); @@ -81,6 +84,7 @@ public: return is_virtual() ? sz - 1 : sz; } + proof * get_proof() override { scoped_watch _t_(m_pool.m_proof_watch); if (!m_proof.get()) { @@ -126,33 +130,7 @@ public: set_status(res); if (false /*m_dump_benchmarks && sw.get_seconds() >= m_pool.fparams().m_dump_min_time*/) { - std::stringstream file_name; - file_name << "virt_solver"; - if (is_virtual()) { file_name << "_" << m_pred->get_decl()->get_name(); } - file_name << "_" << (m_dump_counter++) << ".smt2"; - - std::ofstream out(file_name.str().c_str()); - if (!out) { verbose_stream() << "could not open file " << file_name.str() << " for output\n"; } - - out << "(set-info :status "; - switch (res) { - case l_true: out << "sat"; break; - case l_false: out << "unsat"; break; - case l_undef: out << "unknown"; break; - } - out << ")\n"; - m_base->display(out, num_assumptions, assumptions); - out << "(check-sat"; - for (unsigned i = 0; i < num_assumptions; ++i) { - out << " " << mk_pp(assumptions[i], m); - } - out << ")"; - out << "(exit)\n"; - ::statistics st; - m_base->collect_statistics(st); - st.update("time", sw.get_seconds()); - st.display_smt2(out); - out.close(); + dump_benchmark(num_assumptions, assumptions, res, sw); } return res; } @@ -171,21 +149,21 @@ public: m_in_delayed_scope = true; } else { - SASSERT(m_pushed); SASSERT(!m_in_delayed_scope); m_base->push(); } } void pop_core(unsigned n) override { - SASSERT(!m_pushed || get_scope_level() > 0); + unsigned lvl = get_scope_level(); + SASSERT(!m_pushed || lvl > 0); if (m_pushed) { SASSERT(!m_in_delayed_scope); m_base->pop(n); - m_pushed = get_scope_level() - n > 0; + m_pushed = lvl - n > 0; } else { - m_in_delayed_scope = get_scope_level() - n > 0; + m_in_delayed_scope = lvl - n > 0; } } @@ -237,11 +215,58 @@ public: m_assertions.reset(); m_pool.refresh(m_base.get()); } + +private: + + void dump_benchmark(unsigned num_assumptions, expr * const * assumptions, lbool res, stopwatch& sw) { + std::string file_name = mk_file_name(); + std::ofstream out(file_name); + if (!out) { + IF_VERBOSE(0, verbose_stream() << "could not open file " << file_name << " for output\n"); + return; + } + + out << "(set-info :status " << lbool2status(res) << ")\n"; + m_base->display(out, num_assumptions, assumptions); + out << "(check-sat"; + for (unsigned i = 0; i < num_assumptions; ++i) { + out << " " << mk_pp(assumptions[i], m); + } + out << ")"; + out << "(exit)\n"; + ::statistics st; + m_base->collect_statistics(st); + st.update("time", sw.get_seconds()); + st.display_smt2(out); + out.close(); + } + + char const* lbool2status(lbool r) const { + switch (r) { + case l_true: return "sat"; + case l_false: return "unsat"; + case l_undef: return "unknown"; + } + return "?"; + } + + std::string mk_file_name() { + std::stringstream file_name; + file_name << "virt_solver"; + if (is_virtual()) file_name << "_" << m_pred->get_decl()->get_name(); + file_name << "_" << (m_dump_counter++) << ".smt2"; + return file_name.str(); + } + }; -solver_pool::solver_pool(solver* base_solver): - m_base_solver(base_solver) -{} +solver_pool::solver_pool(solver* base_solver, unsigned num_pools): + m_base_solver(base_solver), + m_num_pools(num_pools), + m_current_pool(0) +{ + SASSERT(num_pools > 0); +} ptr_vector solver_pool::get_base_solvers() const { @@ -258,10 +283,10 @@ ptr_vector solver_pool::get_base_solvers() const { void solver_pool::collect_statistics(statistics &st) const { ptr_vector solvers = get_base_solvers(); for (solver* s : solvers) s->collect_statistics(st); - st.update("pool_solver.time.total", m_check_watch.get_seconds()); - st.update("pool_solver.time.total.sat", m_check_sat_watch.get_seconds()); - st.update("pool_solver.time.total.undef", m_check_undef_watch.get_seconds()); - st.update("pool_solver.time.proof", m_proof_watch.get_seconds()); + st.update("time.pool_solver.smt.total", m_check_watch.get_seconds()); + st.update("time.pool_solver.smt.total.sat", m_check_sat_watch.get_seconds()); + st.update("time.pool_solver.smt.total.undef", m_check_undef_watch.get_seconds()); + st.update("time.pool_solver.proof", m_proof_watch.get_seconds()); st.update("pool_solver.checks", m_stats.m_num_checks); st.update("pool_solver.checks.sat", m_stats.m_num_sat_checks); st.update("pool_solver.checks.undef", m_stats.m_num_undef_checks); @@ -281,24 +306,29 @@ void solver_pool::reset_statistics() { m_proof_watch.reset(); } +/** + \brief Create a fresh solver instance. + The first num_pools solvers are independent and + use a fresh instance of the base solver. + Subsequent solvers reuse the first num_polls base solvers, rotating + among the first num_pools. +*/ solver* solver_pool::mk_solver() { + ref base_solver; ast_manager& m = m_base_solver->get_manager(); - ref base_solver = m_base_solver->translate(m, m_base_solver->get_params()); - app_ref pred(m.mk_true(), m); - pool_solver* solver = alloc(pool_solver, base_solver.get(), *this, pred); - m_solvers.push_back(solver); - return solver; -} - -solver* solver_pool::clone_solver(solver* psolver) { - ast_manager& m = m_base_solver->get_manager(); - solver* base_solver = dynamic_cast(psolver)->base_solver(); + if (m_solvers.size() < m_num_pools) { + base_solver = m_base_solver->translate(m, m_base_solver->get_params()); + } + else { + solver* s = m_solvers[(m_current_pool++) % m_num_pools]; + base_solver = dynamic_cast(s)->base_solver(); + } std::stringstream name; name << "vsolver#" << m_solvers.size(); app_ref pred(m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()), m); - pool_solver* solver = alloc(pool_solver, base_solver, *this, pred); + pool_solver* solver = alloc(pool_solver, base_solver.get(), *this, pred); m_solvers.push_back(solver); - return solver; + return solver; } void solver_pool::reset_solver(solver* s) { diff --git a/src/solver/solver_pool.h b/src/solver/solver_pool.h index a1f650eb0..f279dfd4b 100644 --- a/src/solver/solver_pool.h +++ b/src/solver/solver_pool.h @@ -16,22 +16,16 @@ Author: Notes: - This is a revision of spacer_virtual_solver by Arie Gurfinkel - - -it is not quite the same + there are good reasons to do that management at a higher level. -not the same == in spacer, there is an upper bound on the number of base solvers (i.e., number of pools). -Then the pools are distributed between different predicate transformers. I can't just switch to solver_pool, -since this, in most configuration, will result in either few solvers (which is bad for most of my benchmarks), -or one solver per predicate transformer (which is bad for a few benchmarks with many predicates). - + This is a revision of spacer_virtual_solver + consolidated with spacer_smt_context_manager + by Arie Gurfinkel + Use this module as a replacement for spacer_smt_context_manager. --*/ #ifndef SOLVER_POOL_H_ #define SOLVER_POOL_H_ #include "solver/solver.h" -#include "solver/solver_na2as.h" #include "util/stopwatch.h" class pool_solver; @@ -49,6 +43,8 @@ class solver_pool { }; ref m_base_solver; + unsigned m_num_pools; + unsigned m_current_pool; sref_vector m_solvers; stats m_stats; @@ -62,17 +58,13 @@ class solver_pool { ptr_vector get_base_solvers() const; public: - solver_pool(solver* base_solver); + solver_pool(solver* base_solver, unsigned num_pools); void collect_statistics(statistics &st) const; void reset_statistics(); - // create a fresh pool solver solver* mk_solver(); - // clone an existing pool solver - solver* clone_solver(solver* pool_solver); - void reset_solver(solver* s); }; diff --git a/src/test/solver_pool.cpp b/src/test/solver_pool.cpp index a0b6832c2..4a2e533bf 100644 --- a/src/test/solver_pool.cpp +++ b/src/test/solver_pool.cpp @@ -7,28 +7,35 @@ void tst_solver_pool() { reg_decl_plugins(m); params_ref p; ref base = mk_smt_solver(m, p, symbol::null); - solver_pool pool(base.get()); - ref s1 = pool.mk_solver(); - ref s2 = pool.clone_solver(s1.get()); - ref s3 = pool.clone_solver(s1.get()); - - ref s4 = pool.mk_solver(); - ref s5 = pool.clone_solver(s4.get()); - ref s6 = pool.clone_solver(s4.get()); - expr_ref a(m.mk_const(symbol("a"), m.mk_bool_sort()), m); expr_ref b(m.mk_const(symbol("b"), m.mk_bool_sort()), m); expr_ref c(m.mk_const(symbol("c"), m.mk_bool_sort()), m); expr_ref d(m.mk_const(symbol("d"), m.mk_bool_sort()), m); expr_ref fml(m); fml = m.mk_or(a, b); + base->assert_expr(fml); + + solver_pool pool(base.get(), 3); + + // base solver is cloned so any assertions + // added to base after mk_solver() are ignored. + ref s1 = pool.mk_solver(); + ref s2 = pool.mk_solver(); + ref s3 = pool.mk_solver(); + ref s4 = pool.mk_solver(); + + fml = m.mk_and(b, c); s1->assert_expr(fml); - fml = m.mk_and(a,b); + fml = m.mk_and(a, b); s2->assert_expr(fml); expr_ref_vector asms(m); asms.push_back(m.mk_not(a)); + std::cout << base->check_sat(asms) << "\n"; std::cout << s1->check_sat(asms) << "\n"; std::cout << s2->check_sat(asms) << "\n"; std::cout << s3->check_sat(asms) << "\n"; + std::cout << *s1; + std::cout << *s2; + std::cout << *base; } From ebf6b1882121309be2997476c193a2d9d8d225a4 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 May 2018 08:59:11 -0700 Subject: [PATCH 091/364] maxsat standalone mode --- src/opt/maxsmt.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++++- src/opt/maxsmt.h | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index afecf7d2b..fc5bd6bbb 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -21,6 +21,7 @@ Notes: #include "opt/maxsmt.h" #include "opt/maxres.h" #include "opt/wmax.h" +#include "opt/opt_params.hpp" #include "ast/ast_pp.h" #include "util/uint_set.h" #include "opt/opt_context.h" @@ -409,6 +410,59 @@ namespace opt { m_c.model_updated(mdl); } + class solver_maxsat_context : public maxsat_context { + params_ref m_params; + solver_ref m_solver; + model_ref m_model; + ref m_fm; + symbol m_maxsat_engine; + public: + solver_maxsat_context(params_ref& p, solver* s, model * m): + m_params(p), + m_solver(s), + m_model(m), + m_fm(alloc(generic_model_converter, s->get_manager(), "maxsmt")) { + opt_params _p(p); + m_maxsat_engine = _p.maxsat_engine(); + } + generic_model_converter& fm() override { return *m_fm.get(); } + bool sat_enabled() const override { return false; } + solver& get_solver() override { return *m_solver.get(); } + ast_manager& get_manager() const override { return m_solver->get_manager(); } + params_ref& params() override { return m_params; } + void enable_sls(bool force) override { } // no op + symbol const& maxsat_engine() const override { return m_maxsat_engine; } + void get_base_model(model_ref& _m) override { _m = m_model; }; + smt::context& smt_context() override { + throw default_exception("stand-alone maxsat context does not support wmax"); + } + unsigned num_objectives() override { return 1; } + bool verify_model(unsigned id, model* mdl, rational const& v) override { return true; }; + void set_model(model_ref& _m) override { m_model = _m; } + void model_updated(model* mdl) override { } // no-op + }; - + lbool maxsmt_wrapper::operator()(vector>& soft) { + solver_maxsat_context ctx(m_params, m_solver.get(), m_model.get()); + maxsmt maxsmt(ctx, 0); + for (auto const& p : soft) { + maxsmt.add(p.first, p.second); + } + lbool r = maxsmt(); + if (r == l_true) { + ast_manager& m = m_solver->get_manager(); + svector labels; + maxsmt.get_model(m_model, labels); + // TBD: is m_fm applied or not? + unsigned j = 0; + expr_ref tmp(m); + for (unsigned i = 0; i < soft.size(); ++i) { + if (m_model->eval(soft[i].first, tmp) && m.is_true(tmp)) { + soft[j++] = soft[i]; + } + } + soft.shrink(j); + } + return r; + } }; diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index 9e52481f2..422cf26b9 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -153,6 +153,48 @@ namespace opt { solver& s(); }; + /** + \brief Standalone MaxSMT solver. + + It takes as input a solver object and provides a MaxSAT solver routine. + + It assumes the solver state is satisfiable and therefore there is a model + associated with the constraints asserted to the solver. A model of the + solver state must be supplied as a last argument. + + It assumes that the caller manages scope on the solver such that + the solver can be left in a stronger or inconsistent state upon return. + Callers should therefore use this feature under a push/pop. + */ + class maxsmt_wrapper { + params_ref m_params; + ref m_solver; + model_ref m_model; + public: + maxsmt_wrapper(params_ref & p, solver* s, model* m): + m_params(p), + m_solver(s), + m_model(m) {} + + lbool operator()(expr_ref_vector& soft) { + vector> _soft; + for (expr* e : soft) _soft.push_back(std::make_pair(e, rational::one())); + lbool r = (*this)(_soft); + soft.reset(); + for (auto const& p : _soft) soft.push_back(p.first); + return r; + } + + /** + \brief takes a vector of weighted soft constraints. + Returns a modified list of soft constraints that are + satisfied in the maximal satisfying assignment. + */ + lbool operator()(vector> & soft); + + model_ref get_model() { return m_model; } + }; + }; #endif From abe67705d3cbead80481f06bf21f3ea4a7208118 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 May 2018 10:09:26 -0700 Subject: [PATCH 092/364] Cleanup iuc_proof --- src/muz/spacer/spacer_iuc_proof.cpp | 345 +++++++++----------- src/muz/spacer/spacer_iuc_proof.h | 94 +++--- src/muz/spacer/spacer_iuc_solver.cpp | 8 +- src/muz/spacer/spacer_unsat_core_plugin.cpp | 24 +- 4 files changed, 211 insertions(+), 260 deletions(-) diff --git a/src/muz/spacer/spacer_iuc_proof.cpp b/src/muz/spacer/spacer_iuc_proof.cpp index 966b59df4..4ee887df5 100644 --- a/src/muz/spacer/spacer_iuc_proof.cpp +++ b/src/muz/spacer/spacer_iuc_proof.cpp @@ -6,229 +6,182 @@ namespace spacer { - /* - * ==================================== - * init - * ==================================== - */ - iuc_proof::iuc_proof(ast_manager& m, proof* pr, expr_set& b_conjuncts) : m(m), m_pr(pr,m) - { - // init A-marks and B-marks - collect_symbols_b(b_conjuncts); - compute_marks(b_conjuncts); - } +/* + * ==================================== + * init + * ==================================== + */ +iuc_proof::iuc_proof(ast_manager& m, proof* pr, expr_set& core_lits) : + m(m), m_pr(pr,m) { + // init A-marks and B-marks + collect_core_symbols(core_lits); + compute_marks(core_lits); +} - proof* iuc_proof::get() - { - return m_pr.get(); - } +/* + * ==================================== + * methods for computing symbol colors + * ==================================== + */ +class collect_pure_proc { + func_decl_set& m_symbs; +public: + collect_pure_proc(func_decl_set& s):m_symbs(s) {} - /* - * ==================================== - * methods for computing symbol colors - * ==================================== - */ - class collect_pure_proc { - func_decl_set& m_symbs; - public: - collect_pure_proc(func_decl_set& s):m_symbs(s) {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - m_symbs.insert(a->get_decl()); - } - } - void operator()(var*) {} - void operator()(quantifier*) {} - }; - - void iuc_proof::collect_symbols_b(expr_set& b_conjuncts) - { - expr_mark visited; - collect_pure_proc proc(m_symbols_b); - for (expr_set::iterator it = b_conjuncts.begin(); it != b_conjuncts.end(); ++it) - { - for_each_expr(proc, visited, *it); + void operator()(app* a) { + if (a->get_family_id() == null_family_id) { + m_symbs.insert(a->get_decl()); } } + void operator()(var*) {} + void operator()(quantifier*) {} +}; - class is_pure_expr_proc { - func_decl_set const& m_symbs; - array_util m_au; - public: - struct non_pure {}; +void iuc_proof::collect_core_symbols(expr_set& core_lits) +{ + expr_mark visited; + collect_pure_proc proc(m_core_symbols); + for (expr_set::iterator it = core_lits.begin(); it != core_lits.end(); ++it) { + for_each_expr(proc, visited, *it); + } +} - is_pure_expr_proc(func_decl_set const& s, ast_manager& m): +class is_pure_expr_proc { + func_decl_set const& m_symbs; + array_util m_au; +public: + struct non_pure {}; + + is_pure_expr_proc(func_decl_set const& s, ast_manager& m): m_symbs(s), m_au (m) {} - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - if (!m_symbs.contains(a->get_decl())) { - throw non_pure(); - } - } - else if (a->get_family_id () == m_au.get_family_id () && - a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) { + void operator()(app* a) { + if (a->get_family_id() == null_family_id) { + if (!m_symbs.contains(a->get_decl())) { throw non_pure(); } } - void operator()(var*) {} - void operator()(quantifier*) {} - }; - - // requires that m_symbols_b has already been computed, which is done during initialization. - bool iuc_proof::only_contains_symbols_b(expr* expr) const - { - is_pure_expr_proc proc(m_symbols_b, m); - try { - for_each_expr(proc, expr); + else if (a->get_family_id () == m_au.get_family_id () && + a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) { + throw non_pure(); } - catch (is_pure_expr_proc::non_pure) - { - return false; - } - return true; } + void operator()(var*) {} + void operator()(quantifier*) {} +}; - /* - * ==================================== - * methods for computing which premises - * have been used to derive the conclusions - * ==================================== - */ +bool iuc_proof::is_core_pure(expr* e) const +{ + is_pure_expr_proc proc(m_core_symbols, m); + try { + for_each_expr(proc, e); + } + catch (is_pure_expr_proc::non_pure) + {return false;} - void iuc_proof::compute_marks(expr_set& b_conjuncts) + return true; +} + +void iuc_proof::compute_marks(expr_set& core_lits) +{ + proof_post_order it(m_pr, m); + while (it.hasNext()) { - proof_post_order it(m_pr, m); - while (it.hasNext()) + proof* cur = it.next(); + if (m.get_num_parents(cur) == 0) { - proof* currentNode = it.next(); - - if (m.get_num_parents(currentNode) == 0) + switch(cur->get_decl_kind()) { - switch(currentNode->get_decl_kind()) - { - - case PR_ASSERTED: // currentNode is an axiom - { - if (b_conjuncts.contains(m.get_fact(currentNode))) - { - m_b_mark.mark(currentNode, true); - } - else - { - m_a_mark.mark(currentNode, true); - } - break; - } - // currentNode is a hypothesis: - case PR_HYPOTHESIS: - { - m_h_mark.mark(currentNode, true); - break; - } - default: - { - break; - } - } - } - else - { - // collect from parents whether derivation of current node contains A-axioms, B-axioms and hypothesis - bool need_to_mark_a = false; - bool need_to_mark_b = false; - bool need_to_mark_h = false; - - for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) - { - SASSERT(m.is_proof(currentNode->get_arg(i))); - proof* premise = to_app(currentNode->get_arg(i)); - - need_to_mark_a = need_to_mark_a || m_a_mark.is_marked(premise); - need_to_mark_b = need_to_mark_b || m_b_mark.is_marked(premise); - need_to_mark_h = need_to_mark_h || m_h_mark.is_marked(premise); - } - - // if current node is application of lemma, we know that all hypothesis are removed - if(currentNode->get_decl_kind() == PR_LEMMA) - { - need_to_mark_h = false; - } - - // save results - m_a_mark.mark(currentNode, need_to_mark_a); - m_b_mark.mark(currentNode, need_to_mark_b); - m_h_mark.mark(currentNode, need_to_mark_h); + case PR_ASSERTED: + if (core_lits.contains(m.get_fact(cur))) + m_b_mark.mark(cur, true); + else + m_a_mark.mark(cur, true); + break; + case PR_HYPOTHESIS: + m_h_mark.mark(cur, true); + break; + default: + break; } } - } - - bool iuc_proof::is_a_marked(proof* p) - { - return m_a_mark.is_marked(p); - } - bool iuc_proof::is_b_marked(proof* p) - { - return m_b_mark.is_marked(p); - } - bool iuc_proof::is_h_marked(proof* p) - { - return m_h_mark.is_marked(p); - } - - /* - * ==================================== - * methods for dot printing - * ==================================== - */ - void iuc_proof::pp_dot() - { - pp_proof_dot(m, m_pr, this); - } - - /* - * ==================================== - * statistics - * ==================================== - */ - - void iuc_proof::print_farkas_stats() - { - unsigned farkas_counter = 0; - unsigned farkas_counter2 = 0; - - proof_post_order it3(m_pr, m); - while (it3.hasNext()) + else { - proof* currentNode = it3.next(); + // collect from parents whether derivation of current node + // contains A-axioms, B-axioms and hypothesis + bool need_to_mark_a = false; + bool need_to_mark_b = false; + bool need_to_mark_h = false; - // if node is theory lemma - if (is_farkas_lemma(m, currentNode)) + for (unsigned i = 0; i < m.get_num_parents(cur); ++i) { - farkas_counter++; + SASSERT(m.is_proof(cur->get_arg(i))); + proof* premise = to_app(cur->get_arg(i)); - // check whether farkas lemma is to be interpolated (could potentially miss farkas lemmas, which are interpolated, because we potentially don't want to use the lowest cut) - bool has_blue_nonred_parent = false; - for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) - { - proof* premise = to_app(currentNode->get_arg(i)); - if (!is_a_marked(premise) && is_b_marked(premise)) - { - has_blue_nonred_parent = true; - break; - } - } - if (has_blue_nonred_parent && is_a_marked(currentNode)) - { - SASSERT(is_b_marked(currentNode)); - farkas_counter2++; - } + need_to_mark_a |= m_a_mark.is_marked(premise); + need_to_mark_b |= m_b_mark.is_marked(premise); + need_to_mark_h |= m_h_mark.is_marked(premise); } - } - verbose_stream() << "\nThis proof contains " << farkas_counter << " Farkas lemmas. " << farkas_counter2 << " Farkas lemmas participate in the lowest cut\n"; + // if current node is application of a lemma, then all + // active hypotheses are removed + if(cur->get_decl_kind() == PR_LEMMA) need_to_mark_h = false; + + // save results + m_a_mark.mark(cur, need_to_mark_a); + m_b_mark.mark(cur, need_to_mark_b); + m_h_mark.mark(cur, need_to_mark_h); + } } } + +/* + * ==================================== + * statistics + * ==================================== + */ + +// debug method +void iuc_proof::dump_farkas_stats() +{ + unsigned fl_total = 0; + unsigned fl_lowcut = 0; + + proof_post_order it(m_pr, m); + while (it.hasNext()) + { + proof* cur = it.next(); + + // if node is theory lemma + if (is_farkas_lemma(m, cur)) + { + fl_total++; + + // check whether farkas lemma is to be interpolated (could + // potentially miss farkas lemmas, which are interpolated, + // because we potentially don't want to use the lowest + // cut) + bool has_blue_nonred_parent = false; + for (unsigned i = 0; i < m.get_num_parents(cur); ++i) { + proof* premise = to_app(cur->get_arg(i)); + if (!is_a_marked(premise) && is_b_marked(premise)) { + has_blue_nonred_parent = true; + break; + } + } + + if (has_blue_nonred_parent && is_a_marked(cur)) + { + SASSERT(is_b_marked(cur)); + fl_lowcut++; + } + } + } + + IF_VERBOSE(1, verbose_stream() + << "\n total farkas lemmas " << fl_total + << " farkas lemmas in lowest cut " << fl_lowcut << "\n";); +} +} diff --git a/src/muz/spacer/spacer_iuc_proof.h b/src/muz/spacer/spacer_iuc_proof.h index 205648109..97c00ea9b 100644 --- a/src/muz/spacer/spacer_iuc_proof.h +++ b/src/muz/spacer/spacer_iuc_proof.h @@ -4,62 +4,58 @@ #include "ast/ast.h" namespace spacer { - typedef obj_hashtable expr_set; - typedef obj_hashtable func_decl_set; +typedef obj_hashtable expr_set; +typedef obj_hashtable func_decl_set; - /* - * an iuc_proof is a proof together with information of the coloring of the axioms. - */ - class iuc_proof - { - public: - - iuc_proof(ast_manager& m, proof* pr, expr_set& b_conjuncts); - - proof* get(); +/* + * An iuc_proof is a proof together with information of the + * coloring of the axioms. + */ +class iuc_proof +{ +public: - /* - * returns whether symbol contains only symbols which occur in B. - */ - bool only_contains_symbols_b(expr* expr) const; + // Constructs an iuc_proof given an ast_manager, a proof, and a set of + // literals core_lits that might be included in the unsat core + iuc_proof(ast_manager& m, proof* pr, expr_set& core_lits); - bool is_a_marked(proof* p); - bool is_b_marked(proof* p); - bool is_h_marked(proof* p); + // returns the proof object + proof* get() {return m_pr.get();} - bool is_b_pure (proof *p) - {return !is_h_marked (p) && only_contains_symbols_b (m.get_fact (p));} + // returns true if all uninterpreted symbols of e are from the core literals + // requires that m_core_symbols has already been computed + bool is_core_pure(expr* e) const; - /* - * print dot-representation to file proof.dot - * use Graphviz's dot with option -Tpdf to convert dot-representation into pdf - */ - void pp_dot(); - - void print_farkas_stats(); - private: - ast_manager& m; - proof_ref m_pr; - - ast_mark m_a_mark; - ast_mark m_b_mark; - ast_mark m_h_mark; - - func_decl_set m_symbols_b; // symbols, which occur in any b-asserted formula + bool is_a_marked(proof* p) {return m_a_mark.is_marked(p);} + bool is_b_marked(proof* p) {return m_b_mark.is_marked(p);} + bool is_h_marked(proof* p) {return m_h_mark.is_marked(p);} + + bool is_b_pure (proof *p) { + return !is_h_marked (p) && is_core_pure(m.get_fact (p)); + } + + // debug method + void dump_farkas_stats(); +private: + ast_manager& m; + proof_ref m_pr; + + ast_mark m_a_mark; + ast_mark m_b_mark; + ast_mark m_h_mark; + + // symbols that occur in any literals in the core + func_decl_set m_core_symbols; + + // collect symbols occuring in B (the core) + void collect_core_symbols(expr_set& core_lits); + + // compute for each formula of the proof whether it derives + // from A or from B + void compute_marks(expr_set& core_lits); +}; - // collect symbols occuring in B - void collect_symbols_b(expr_set& b_conjuncts); - // compute for each formula of the proof whether it derives from A and whether it derives from B - void compute_marks(expr_set& b_conjuncts); - - void pp_dot_to_stream(std::ofstream& dotstream); - std::string escape_dot(const std::string &s); - - void post_process_dot(std::string dot_filepath, std::ofstream& dotstream); - }; - - } #endif /* IUC_PROOF_H_ */ diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index e2334f1ba..7c159746f 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -289,7 +289,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core) { iuc_proof iuc_before(m, res.get(), B); verbose_stream() << "\nStats before transformation:"; - iuc_before.print_farkas_stats(); + iuc_before.dump_farkas_stats(); } proof_utils::reduce_hypotheses(res); @@ -299,7 +299,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core) { iuc_proof iuc_after(m, res.get(), B); verbose_stream() << "Stats after transformation:"; - iuc_after.print_farkas_stats(); + iuc_after.dump_farkas_stats(); } } else // -- new hypothesis reducer @@ -309,7 +309,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core) { iuc_proof iuc_before(m, res.get(), B); verbose_stream() << "\nStats before transformation:"; - iuc_before.print_farkas_stats(); + iuc_before.dump_farkas_stats(); } theory_axiom_reducer ta_reducer(m); @@ -324,7 +324,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core) { iuc_proof iuc_after(m, res.get(), B); verbose_stream() << "Stats after transformation:"; - iuc_after.print_farkas_stats(); + iuc_after.dump_farkas_stats(); } } diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index 863023d5b..557983628 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -331,11 +331,13 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vectorassert_expr(lb); s->assert_expr(ub); } } - + // assert: forall i,j: a_ij = sum_k w_ik * s_jk for (unsigned i=0; i < matrix.num_rows(); ++i) { @@ -563,13 +565,13 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vectorget_model(model); - + for (unsigned k=0; k < n; ++k) { ptr_vector literals; vector coefficients; for (unsigned j=0; j < matrix.num_cols(); ++j) { expr_ref evaluation(m); - + model.get()->eval(bounded_vectors[j][k].get(), evaluation, false); if (!util.is_zero(evaluation)) { literals.push_back(ordered_basis[j]); @@ -579,7 +581,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector& todo) { bool is_sink = true; @@ -709,7 +711,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector Date: Wed, 16 May 2018 12:57:35 -0700 Subject: [PATCH 093/364] Cleanup iuc_proof --- src/muz/spacer/spacer_iuc_proof.cpp | 25 ++++++++++++++++--------- src/muz/spacer/spacer_iuc_proof.h | 12 ++++++++---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/muz/spacer/spacer_iuc_proof.cpp b/src/muz/spacer/spacer_iuc_proof.cpp index 4ee887df5..1123a7c76 100644 --- a/src/muz/spacer/spacer_iuc_proof.cpp +++ b/src/muz/spacer/spacer_iuc_proof.cpp @@ -11,13 +11,21 @@ namespace spacer { * init * ==================================== */ -iuc_proof::iuc_proof(ast_manager& m, proof* pr, expr_set& core_lits) : +iuc_proof::iuc_proof(ast_manager& m, proof* pr, const expr_set& core_lits) : m(m), m_pr(pr,m) { + for (auto lit : core_lits) m_core_lits.insert(lit); // init A-marks and B-marks - collect_core_symbols(core_lits); - compute_marks(core_lits); + collect_core_symbols(); + compute_marks(); } +iuc_proof::iuc_proof(ast_manager& m, proof* pr, const expr_ref_vector& core_lits) : + m(m), m_pr(pr,m) { + for (auto lit : core_lits) m_core_lits.insert(lit); + // init A-marks and B-marks + collect_core_symbols(); + compute_marks(); +} /* * ==================================== * methods for computing symbol colors @@ -37,13 +45,12 @@ public: void operator()(quantifier*) {} }; -void iuc_proof::collect_core_symbols(expr_set& core_lits) +void iuc_proof::collect_core_symbols() { expr_mark visited; collect_pure_proc proc(m_core_symbols); - for (expr_set::iterator it = core_lits.begin(); it != core_lits.end(); ++it) { - for_each_expr(proc, visited, *it); - } + for (auto lit : m_core_lits) + for_each_expr(proc, visited, lit); } class is_pure_expr_proc { @@ -84,7 +91,7 @@ bool iuc_proof::is_core_pure(expr* e) const return true; } -void iuc_proof::compute_marks(expr_set& core_lits) +void iuc_proof::compute_marks() { proof_post_order it(m_pr, m); while (it.hasNext()) @@ -95,7 +102,7 @@ void iuc_proof::compute_marks(expr_set& core_lits) switch(cur->get_decl_kind()) { case PR_ASSERTED: - if (core_lits.contains(m.get_fact(cur))) + if (m_core_lits.contains(m.get_fact(cur))) m_b_mark.mark(cur, true); else m_a_mark.mark(cur, true); diff --git a/src/muz/spacer/spacer_iuc_proof.h b/src/muz/spacer/spacer_iuc_proof.h index 97c00ea9b..aeb18ef37 100644 --- a/src/muz/spacer/spacer_iuc_proof.h +++ b/src/muz/spacer/spacer_iuc_proof.h @@ -17,7 +17,8 @@ public: // Constructs an iuc_proof given an ast_manager, a proof, and a set of // literals core_lits that might be included in the unsat core - iuc_proof(ast_manager& m, proof* pr, expr_set& core_lits); + iuc_proof(ast_manager& m, proof* pr, const expr_set& core_lits); + iuc_proof(ast_manager& m, proof* pr, const expr_ref_vector &core_lits); // returns the proof object proof* get() {return m_pr.get();} @@ -44,15 +45,18 @@ private: ast_mark m_b_mark; ast_mark m_h_mark; + // -- literals that are part of the core + expr_set m_core_lits; + // symbols that occur in any literals in the core func_decl_set m_core_symbols; - // collect symbols occuring in B (the core) - void collect_core_symbols(expr_set& core_lits); + // collect symbols occurring in B (the core) + void collect_core_symbols(); // compute for each formula of the proof whether it derives // from A or from B - void compute_marks(expr_set& core_lits); + void compute_marks(); }; From 07ad67ebad7fda14a64bd5974b705ac3910a766e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 May 2018 13:32:28 -0700 Subject: [PATCH 094/364] Move proof dot printing into iuc_proof --- src/muz/spacer/spacer_iuc_proof.cpp | 88 ++- src/muz/spacer/spacer_iuc_proof.h | 2 + src/muz/spacer/spacer_proof_utils.cpp | 987 +++++++++++--------------- src/muz/spacer/spacer_proof_utils.h | 101 +-- 4 files changed, 545 insertions(+), 633 deletions(-) diff --git a/src/muz/spacer/spacer_iuc_proof.cpp b/src/muz/spacer/spacer_iuc_proof.cpp index 1123a7c76..6033c744f 100644 --- a/src/muz/spacer/spacer_iuc_proof.cpp +++ b/src/muz/spacer/spacer_iuc_proof.cpp @@ -1,9 +1,12 @@ +#include +#include "ast/ast_pp_dot.h" + #include "muz/spacer/spacer_iuc_proof.h" #include "ast/for_each_expr.h" #include "ast/array_decl_plugin.h" #include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_proof_utils.h" - +#include "muz/spacer/spacer_util.h" namespace spacer { /* @@ -191,4 +194,87 @@ void iuc_proof::dump_farkas_stats() << "\n total farkas lemmas " << fl_total << " farkas lemmas in lowest cut " << fl_lowcut << "\n";); } + +void iuc_proof::display_dot(std::ostream& out) { + out << "digraph proof { \n"; + + std::unordered_map ids; + unsigned last_id = 0; + + proof_post_order it(m_pr, m); + while (it.hasNext()) + { + proof* curr = it.next(); + + SASSERT(ids.count(curr->get_id()) == 0); + ids.insert(std::make_pair(curr->get_id(), last_id)); + + std::string color = "white"; + if (this->is_a_marked(curr) && !this->is_b_marked(curr)) + color = "red"; + else if(!this->is_a_marked(curr) && this->is_b_marked(curr)) + color = "blue"; + else if(this->is_a_marked(curr) && this->is_b_marked(curr) ) + color = "purple"; + + // compute node label + std::ostringstream label_ostream; + label_ostream << mk_epp(m.get_fact(curr), m) << "\n"; + std::string label = escape_dot(label_ostream.str()); + + // compute edge-label + std::string edge_label = ""; + if (m.get_num_parents(curr) == 0) { + switch (curr->get_decl_kind()) + { + case PR_ASSERTED: + edge_label = "asserted:"; + break; + case PR_HYPOTHESIS: + edge_label = "hyp:"; + color = "grey"; + break; + case PR_TH_LEMMA: + if (is_farkas_lemma(m, curr)) + edge_label = "th_axiom(farkas):"; + else if (is_arith_lemma(m, curr)) + edge_label = "th_axiom(arith):"; + else + edge_label = "th_axiom:"; + break; + default: + edge_label = "unknown axiom:"; + } + } + else { + if (curr->get_decl_kind() == PR_LEMMA) + edge_label = "lemma:"; + else if (curr->get_decl_kind() == PR_TH_LEMMA) { + if (is_farkas_lemma(m, curr)) + edge_label = "th_lemma(farkas):"; + else if (is_arith_lemma(m, curr)) + edge_label = "th_lemma(arith):"; + else + edge_label = "th_lemma(other):"; + } + } + + // generate entry for node in dot-file + out << "node_" << last_id << " " << "[" + << "shape=box,style=\"filled\"," + << "label=\"" << edge_label << " " << label << "\", " + << "fillcolor=\"" << color << "\"" << "]\n"; + + // add entry for each edge to that node + for (unsigned i = m.get_num_parents(curr); i > 0 ; --i) + { + proof* premise = to_app(curr->get_arg(i-1)); + unsigned pid = ids.at(premise->get_id()); + out << "node_" << pid << " -> " << "node_" << last_id << ";\n"; + } + + ++last_id; + } + out << "\n}" << std::endl; +} } diff --git a/src/muz/spacer/spacer_iuc_proof.h b/src/muz/spacer/spacer_iuc_proof.h index aeb18ef37..a3044ca53 100644 --- a/src/muz/spacer/spacer_iuc_proof.h +++ b/src/muz/spacer/spacer_iuc_proof.h @@ -1,6 +1,7 @@ #ifndef IUC_PROOF_H_ #define IUC_PROOF_H_ +#include #include "ast/ast.h" namespace spacer { @@ -35,6 +36,7 @@ public: return !is_h_marked (p) && is_core_pure(m.get_fact (p)); } + void display_dot(std::ostream &out); // debug method void dump_farkas_stats(); private: diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 32391db91..146436db9 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -16,7 +16,6 @@ Revision History: --*/ -#include #include "util/params.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" @@ -26,680 +25,498 @@ Revision History: #include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_proof_utils.h" +#include "muz/spacer/spacer_util.h" namespace spacer { +// arithmetic lemma recognizer +bool is_arith_lemma(ast_manager& m, proof* pr) +{ // arith lemmas: second parameter specifies exact type of lemma, // could be "farkas", "triangle-eq", "eq-propagate", // "assign-bounds", maybe also something else - bool is_arith_lemma(ast_manager& m, proof* pr) + if (pr->get_decl_kind() == PR_TH_LEMMA) { + func_decl* d = pr->get_decl(); + symbol sym; + return d->get_num_parameters() >= 1 && + d->get_parameter(0).is_symbol(sym) && + sym == "arith"; + } + return false; +} + +// farkas lemma recognizer +bool is_farkas_lemma(ast_manager& m, proof* pr) +{ + if (pr->get_decl_kind() == PR_TH_LEMMA) { - if (pr->get_decl_kind() == PR_TH_LEMMA) + func_decl* d = pr->get_decl(); + symbol sym; + return d->get_num_parameters() >= 2 && + d->get_parameter(0).is_symbol(sym) && sym == "arith" && + d->get_parameter(1).is_symbol(sym) && sym == "farkas"; + } + return false; +} + + +/* + * ==================================== + * methods for transforming proofs + * ==================================== + */ + +void theory_axiom_reducer::reset() +{ + m_cache.reset(); + m_pinned.reset(); +} + +proof_ref theory_axiom_reducer::reduce(proof* pr) +{ + proof_post_order pit(pr, m); + while (pit.hasNext()) + { + proof* p = pit.next(); + + if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) { - func_decl* d = pr->get_decl(); - symbol sym; - if (d->get_num_parameters() >= 1 && - d->get_parameter(0).is_symbol(sym) && sym == "arith") - { - return true; - } - } - return false; - } + // we have an arith-theory-axiom and want to get rid of it + // we need to replace the axiom with 1a) corresponding hypothesis', 1b) a theory lemma and a 1c) a lemma. Furthermore update datastructures + app *cls_fact = to_app(m.get_fact(p)); + ptr_buffer cls; + if (m.is_or(cls_fact)) { + for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) + { cls.push_back(cls_fact->get_arg(i)); } + } else { cls.push_back(cls_fact); } - bool is_farkas_lemma(ast_manager& m, proof* pr) - { - if (pr->get_decl_kind() == PR_TH_LEMMA) + // 1a) create hypothesis' + ptr_buffer hyps; + for (unsigned i=0; i < cls.size(); ++i) + { + expr* hyp_fact = m.is_not(cls[i]) ? to_app(cls[i])->get_arg(0) : m.mk_not(cls[i]); + proof* hyp = m.mk_hypothesis(hyp_fact); + m_pinned.push_back(hyp); + hyps.push_back(hyp); + } + + // 1b) create farkas lemma: need to rebuild parameters since mk_th_lemma adds tid as first parameter + unsigned num_params = p->get_decl()->get_num_parameters(); + parameter const* params = p->get_decl()->get_parameters(); + vector parameters; + for (unsigned i = 1; i < num_params; ++i) { + parameters.push_back(params[i]); + } + + SASSERT(params[0].is_symbol()); + family_id tid = m.mk_family_id(params[0].get_symbol()); + SASSERT(tid != null_family_id); + + proof* th_lemma = m.mk_th_lemma(tid, m.mk_false(),hyps.size(), hyps.c_ptr(), num_params-1, parameters.c_ptr()); + SASSERT(is_arith_lemma(m, th_lemma)); + + // 1c) create lemma + proof* res = m.mk_lemma(th_lemma, cls_fact); + SASSERT(m.get_fact(res) == m.get_fact(p)); + m_pinned.push_back(res); + m_cache.insert(p, res); + } + else { - func_decl* d = pr->get_decl(); - symbol sym; - if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas") + bool dirty = false; // proof is dirty, if a subproof of one of its premises has been transformed + + ptr_buffer args; + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - return true; - } - } - return false; - } - - /* - * ==================================== - * methods for dot printing - * ==================================== - */ - void pp_proof_dot_to_stream(ast_manager& m, std::ofstream& dotstream, proof* pr, iuc_proof* iuc_pr = nullptr); - std::string escape_dot(const std::string &s); - void pp_proof_post_process_dot(std::string dot_filepath, std::ofstream &dotstream); - - void pp_proof_dot(ast_manager& m, proof* pr, iuc_proof* iuc_pr) - { - // open temporary dot-file - std::string dotfile_path = "proof.dot"; - std::ofstream dotstream(dotfile_path); - - // dump dot representation to stream - pp_proof_dot_to_stream(m, dotstream, pr, iuc_pr); - - // post process dot-file, TODO: factor this out to a different place - pp_proof_post_process_dot(dotfile_path,dotstream); - } - - void pp_proof_dot_to_stream(ast_manager& m, std::ofstream& dotstream, proof* pr, iuc_proof* iuc_pr) - { - dotstream << "digraph proof { \n"; - std::unordered_map id_to_small_id; - unsigned counter = 0; - - proof_post_order it2(pr, m); - while (it2.hasNext()) - { - proof* currentNode = it2.next(); - - SASSERT(id_to_small_id.find(currentNode->get_id()) == id_to_small_id.end()); - id_to_small_id.insert(std::make_pair(currentNode->get_id(), counter)); - - std::string color = "white"; - if (iuc_pr != nullptr) - { - if (iuc_pr->is_a_marked(currentNode) && !iuc_pr->is_b_marked(currentNode)) + proof* pp = m.get_parent(p, i); + proof* tmp; + if (m_cache.find(pp, tmp)) { - color = "red"; + args.push_back(tmp); + dirty = dirty || pp != tmp; } - else if(iuc_pr->is_b_marked(currentNode) && !iuc_pr->is_a_marked(currentNode)) + else { - color = "blue"; - } - else if(iuc_pr->is_b_marked(currentNode) && iuc_pr->is_a_marked(currentNode)) - { - color = "purple"; + SASSERT(false); } } - - // compute label - params_ref p; - p.set_uint("max_depth", 4294967295u); - p.set_uint("min_alias_size", 4294967295u); - - std::ostringstream label_ostream; - label_ostream << mk_pp(m.get_fact(currentNode),m,p) << "\n"; - std::string label = escape_dot(label_ostream.str()); - - // compute edge-label - std::string edge_label = ""; - if (m.get_num_parents(currentNode) == 0) + if (!dirty) // if not dirty just use the old step { - switch (currentNode->get_decl_kind()) - { - case PR_ASSERTED: - edge_label = "asserted:"; - break; - case PR_HYPOTHESIS: - edge_label = "hyp:"; - color = "grey"; - break; - case PR_TH_LEMMA: - if (is_farkas_lemma(m, currentNode)) - { - edge_label = "th_axiom(farkas):"; - } - else - { - edge_label = "th_axiom:"; - } - break; - default: - edge_label = "unknown axiom-type:"; - } + m_cache.insert(p, p); } - else + else // otherwise create new step with the corresponding proofs of the premises { - if (currentNode->get_decl_kind() == PR_LEMMA) - { - edge_label = "lemma:"; - } - else if (currentNode->get_decl_kind() == PR_TH_LEMMA) - { - func_decl* d = currentNode->get_decl(); - symbol sym; - if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas") - { - edge_label = "th_lemma(farkas):"; - } - else - { - edge_label = "th_lemma(other):"; - } - } - } - - // generate entry for node in dot-file - dotstream << "node_" << counter << " " - << "[" - << "shape=box,style=\"filled\"," - << "label=\"" << edge_label << " " << label << "\", " - << "fillcolor=\"" << color << "\"" - << "]\n"; - - // add entry for each edge to that node - for (unsigned i = m.get_num_parents(currentNode); i > 0 ; --i) - { - proof* premise = to_app(currentNode->get_arg(i-1)); - unsigned premise_small_id = id_to_small_id[premise->get_id()]; - dotstream << "node_" << premise_small_id - << " -> " - << "node_" << counter - << ";\n"; - } - - ++counter; - } - dotstream << "\n}" << std::endl; - } - - std::string escape_dot(const std::string &s) - { - std::string res; - res.reserve(s.size()); // preallocate - for (auto c : s) { - if (c == '\n') - res.append("\\l"); - else - res.push_back(c); - } - return res; - } - - void pp_proof_post_process_dot(std::string dot_filepath, std::ofstream &dotstream) - { - // replace variables in the dotfiles with nicer descriptions (hack: hard coded replacement for now) - std::vector > predicates; - std::vector l1 = {"L1","i","n","A"}; - predicates.push_back(l1); - std::vector l2 = {"L2","j","m","B"}; - predicates.push_back(l2); - - for(auto& predicate : predicates) - { - std::string predicate_name = predicate[0]; - for (unsigned i=0; i+1 < predicate.size(); ++i) - { - std::string new_name = predicate[i+1]; - std::string substring0 = predicate_name + "_" + std::to_string(i) + "_0"; - std::string substringN = predicate_name + "_" + std::to_string(i) + "_n"; - - std::string command0 = "sed -i '.bak' 's/" + substring0 + "/" + new_name + "/g' " + dot_filepath; - verbose_stream() << command0 << std::endl; - system(command0.c_str()); - - std::string commandN = "sed -i '.bak' s/" + substringN + "/" + new_name + "\\'" + "/g " + dot_filepath; - verbose_stream() << commandN << std::endl; - system(commandN.c_str()); - } - } - - verbose_stream() << "end of postprocessing"; - } - - /* - * ==================================== - * methods for transforming proofs - * ==================================== - */ - - void theory_axiom_reducer::reset() - { - m_cache.reset(); - m_pinned.reset(); - } - - proof_ref theory_axiom_reducer::reduce(proof* pr) - { - proof_post_order pit(pr, m); - while (pit.hasNext()) - { - proof* p = pit.next(); - - if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) - { - // we have an arith-theory-axiom and want to get rid of it - // we need to replace the axiom with 1a) corresponding hypothesis', 1b) a theory lemma and a 1c) a lemma. Furthermore update datastructures - app *cls_fact = to_app(m.get_fact(p)); - ptr_buffer cls; - if (m.is_or(cls_fact)) { - for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) - { cls.push_back(cls_fact->get_arg(i)); } - } else { cls.push_back(cls_fact); } - - // 1a) create hypothesis' - ptr_buffer hyps; - for (unsigned i=0; i < cls.size(); ++i) - { - expr* hyp_fact = m.is_not(cls[i]) ? to_app(cls[i])->get_arg(0) : m.mk_not(cls[i]); - proof* hyp = m.mk_hypothesis(hyp_fact); - m_pinned.push_back(hyp); - hyps.push_back(hyp); - } - - // 1b) create farkas lemma: need to rebuild parameters since mk_th_lemma adds tid as first parameter - unsigned num_params = p->get_decl()->get_num_parameters(); - parameter const* params = p->get_decl()->get_parameters(); - vector parameters; - for (unsigned i = 1; i < num_params; ++i) { - parameters.push_back(params[i]); - } - - SASSERT(params[0].is_symbol()); - family_id tid = m.mk_family_id(params[0].get_symbol()); - SASSERT(tid != null_family_id); - - proof* th_lemma = m.mk_th_lemma(tid, m.mk_false(),hyps.size(), hyps.c_ptr(), num_params-1, parameters.c_ptr()); - SASSERT(is_arith_lemma(m, th_lemma)); - - // 1c) create lemma - proof* res = m.mk_lemma(th_lemma, cls_fact); - SASSERT(m.get_fact(res) == m.get_fact(p)); + if (m.has_fact(p)) { args.push_back(m.get_fact(p)); } + SASSERT(p->get_decl()->get_arity() == args.size()); + proof* res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); m_pinned.push_back(res); m_cache.insert(p, res); } - else - { - bool dirty = false; // proof is dirty, if a subproof of one of its premises has been transformed - - ptr_buffer args; - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) - { - proof* pp = m.get_parent(p, i); - proof* tmp; - if (m_cache.find(pp, tmp)) - { - args.push_back(tmp); - dirty = dirty || pp != tmp; - } - else - { - SASSERT(false); - } - } - if (!dirty) // if not dirty just use the old step - { - m_cache.insert(p, p); - } - else // otherwise create new step with the corresponding proofs of the premises - { - if (m.has_fact(p)) { args.push_back(m.get_fact(p)); } - SASSERT(p->get_decl()->get_arity() == args.size()); - proof* res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); - m_pinned.push_back(res); - m_cache.insert(p, res); - } - } } - - proof* res; - VERIFY(m_cache.find(pr,res)); - DEBUG_CODE(proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(res, side)); - ); - - return proof_ref(res,m); } - void hypothesis_reducer::reset() - { - m_cache.reset(); - m_units.reset(); - m_active_hyps.reset(); - m_parent_hyps.reset(); - m_pinned_active_hyps.reset(); - m_pinned_parent_hyps.reset(); - m_pinned.reset(); - } + proof* res; + VERIFY(m_cache.find(pr,res)); + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side)); + ); - void hypothesis_reducer::compute_hypsets(proof* pr) - { - ptr_vector todo; - todo.push_back(pr); + return proof_ref(res,m); +} - while (!todo.empty()) +void hypothesis_reducer::reset() +{ + m_cache.reset(); + m_units.reset(); + m_active_hyps.reset(); + m_parent_hyps.reset(); + m_pinned_active_hyps.reset(); + m_pinned_parent_hyps.reset(); + m_pinned.reset(); +} + +void hypothesis_reducer::compute_hypsets(proof* pr) +{ + ptr_vector todo; + todo.push_back(pr); + + while (!todo.empty()) + { + proof* p = todo.back(); + + if (m_active_hyps.contains(p)) { - proof* p = todo.back(); + SASSERT(m_parent_hyps.contains(p)); + todo.pop_back(); + } + // if we haven't already visited the current unit + else + { + bool existsUnvisitedParent = false; - if (m_active_hyps.contains(p)) - { - SASSERT(m_parent_hyps.contains(p)); - todo.pop_back(); - } - // if we haven't already visited the current unit - else - { - bool existsUnvisitedParent = false; + // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result + // for p now, but wait until those unprocessed premises are processed. + for (unsigned i = 0; i < m.get_num_parents(p); ++i) { + SASSERT(m.is_proof(p->get_arg(i))); + proof* premise = to_app(p->get_arg(i)); - // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result - // for p now, but wait until those unprocessed premises are processed. - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - SASSERT(m.is_proof(p->get_arg(i))); - proof* premise = to_app(p->get_arg(i)); - - // if we haven't visited the premise yet - if (!m_active_hyps.contains(premise)) - { - SASSERT(!m_parent_hyps.contains(premise)); - // add it to the stack - todo.push_back(premise); - existsUnvisitedParent = true; - } - } - - // if we already visited all premises, we can visit p too - if (!existsUnvisitedParent) + // if we haven't visited the premise yet + if (!m_active_hyps.contains(premise)) { - todo.pop_back(); + SASSERT(!m_parent_hyps.contains(premise)); + // add it to the stack + todo.push_back(premise); + existsUnvisitedParent = true; + } + } - // create active_hyps-set and parent_hyps-set for step p - proof_set* active_hyps = alloc(proof_set); - m_pinned_active_hyps.insert(active_hyps); - m_active_hyps.insert(p, active_hyps); + // if we already visited all premises, we can visit p too + if (!existsUnvisitedParent) + { + todo.pop_back(); - expr_set* parent_hyps = alloc(expr_set); - m_pinned_parent_hyps.insert(parent_hyps); - m_parent_hyps.insert(p, parent_hyps); + // create active_hyps-set and parent_hyps-set for step p + proof_set* active_hyps = alloc(proof_set); + m_pinned_active_hyps.insert(active_hyps); + m_active_hyps.insert(p, active_hyps); - // fill both sets - if (m.is_hypothesis(p)) + expr_set* parent_hyps = alloc(expr_set); + m_pinned_parent_hyps.insert(parent_hyps); + m_parent_hyps.insert(p, parent_hyps); + + // fill both sets + if (m.is_hypothesis(p)) + { + active_hyps->insert(p); + parent_hyps->insert(m.get_fact(p)); + } + else + { + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - active_hyps->insert(p); - parent_hyps->insert(m.get_fact(p)); - } - else - { - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) + proof* pp = m.get_parent(p, i); + set_union(*parent_hyps, *m_parent_hyps.find(pp)); + + if (!m.is_lemma(p)) // lemmas clear all hypotheses { - proof* pp = m.get_parent(p, i); - set_union(*parent_hyps, *m_parent_hyps.find(pp)); - - if (!m.is_lemma(p)) // lemmas clear all hypotheses - { - set_union(*active_hyps, *m_active_hyps.find(pp)); - } + set_union(*active_hyps, *m_active_hyps.find(pp)); } } } } } } +} - // collect all units that are hyp-free and are used as hypotheses somewhere - // requires that m_active_hyps and m_parent_hyps have been computed - void hypothesis_reducer::collect_units(proof* pr) - { - expr_set* all_hyps = m_parent_hyps.find(pr); - SASSERT(all_hyps != nullptr); +// collect all units that are hyp-free and are used as hypotheses somewhere +// requires that m_active_hyps and m_parent_hyps have been computed +void hypothesis_reducer::collect_units(proof* pr) +{ + expr_set* all_hyps = m_parent_hyps.find(pr); + SASSERT(all_hyps != nullptr); - proof_post_order pit(pr, m); - while (pit.hasNext()) { - proof* p = pit.next(); - if (!m.is_hypothesis(p)) + proof_post_order pit(pr, m); + while (pit.hasNext()) { + proof* p = pit.next(); + if (!m.is_hypothesis(p)) + { + proof_set* active_hyps = m_active_hyps.find(p); + SASSERT(active_hyps != nullptr); + + // collect units that are hyp-free and are used as hypotheses in the proof pr + if (active_hyps->empty() && m.has_fact(p) && all_hyps->contains(m.get_fact(p))) { - proof_set* active_hyps = m_active_hyps.find(p); - SASSERT(active_hyps != nullptr); - - // collect units that are hyp-free and are used as hypotheses in the proof pr - if (active_hyps->empty() && m.has_fact(p) && all_hyps->contains(m.get_fact(p))) - { - m_units.insert(m.get_fact(p), p); - } + m_units.insert(m.get_fact(p), p); } } } +} - proof_ref hypothesis_reducer::reduce(proof* pr) - { - compute_hypsets(pr); - collect_units(pr); +proof_ref hypothesis_reducer::reduce(proof* pr) +{ + compute_hypsets(pr); + collect_units(pr); - proof* res = compute_transformed_proof(pr); - SASSERT(res != nullptr); + proof* res = compute_transformed_proof(pr); + SASSERT(res != nullptr); - proof_ref res_ref(res,m); + proof_ref res_ref(res,m); - reset(); - DEBUG_CODE(proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(res, side)); - ); - return res_ref; - } + reset(); + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side)); + ); + return res_ref; +} - proof* hypothesis_reducer::compute_transformed_proof(proof* pf) - { - proof *res = NULL; +proof* hypothesis_reducer::compute_transformed_proof(proof* pf) +{ + proof *res = NULL; - ptr_vector todo; - todo.push_back(pf); - ptr_buffer args; - bool dirty = false; + ptr_vector todo; + todo.push_back(pf); + ptr_buffer args; + bool dirty = false; - while (!todo.empty()) { - proof *p, *tmp, *pp; - unsigned todo_sz; + while (!todo.empty()) { + proof *p, *tmp, *pp; + unsigned todo_sz; - p = todo.back(); - if (m_cache.find(p, tmp)) { - todo.pop_back(); - continue; + p = todo.back(); + if (m_cache.find(p, tmp)) { + todo.pop_back(); + continue; + } + + dirty = false; + args.reset(); + todo_sz = todo.size(); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + pp = m.get_parent(p, i); + if (m_cache.find(pp, tmp)) { + args.push_back(tmp); + dirty = dirty || pp != tmp; + } else { + todo.push_back(pp); } + } - dirty = false; - args.reset(); - todo_sz = todo.size(); - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - pp = m.get_parent(p, i); - if (m_cache.find(pp, tmp)) { - args.push_back(tmp); - dirty = dirty || pp != tmp; - } else { - todo.push_back(pp); - } - } - - if (todo_sz < todo.size()) { continue; } - else { todo.pop_back(); } + if (todo_sz < todo.size()) { continue; } + else { todo.pop_back(); } - // here the proof transformation begins - // INV: whenever we visit p, active_hyps and parent_hyps have been computed for the args. - if (m.is_hypothesis(p)) + // here the proof transformation begins + // INV: whenever we visit p, active_hyps and parent_hyps have been computed for the args. + if (m.is_hypothesis(p)) + { + // hyp: replace by a corresponding unit + if (m_units.find(m.get_fact(p), tmp)) { - // hyp: replace by a corresponding unit - if (m_units.find(m.get_fact(p), tmp)) + // look up the proof of the unit: + // if there is a transformed proof use that one + // otherwise use the original proof + proof* proof_of_unit; + if (!m_cache.find(tmp,proof_of_unit)) { - // look up the proof of the unit: - // if there is a transformed proof use that one - // otherwise use the original proof - proof* proof_of_unit; - if (!m_cache.find(tmp,proof_of_unit)) - { - proof_of_unit = tmp; - } - - // compute hypsets (have not been computed in general, since the unit can be anywhere in the proof) - compute_hypsets(proof_of_unit); - - // if the transformation doesn't create a cycle, perform it - SASSERT(m_parent_hyps.contains(proof_of_unit)); - expr_set* parent_hyps = m_parent_hyps.find(proof_of_unit); - if (!parent_hyps->contains(p)) - { - res = proof_of_unit; - // hypsets have already been computed for proof_of_unit - } - // otherwise don't transform the proof and just use the hypothesis - else - { - res = p; - // hypsets have already been computed for p - } + proof_of_unit = tmp; } + + // compute hypsets (have not been computed in general, since the unit can be anywhere in the proof) + compute_hypsets(proof_of_unit); + + // if the transformation doesn't create a cycle, perform it + SASSERT(m_parent_hyps.contains(proof_of_unit)); + expr_set* parent_hyps = m_parent_hyps.find(proof_of_unit); + if (!parent_hyps->contains(p)) + { + res = proof_of_unit; + // hypsets have already been computed for proof_of_unit + } + // otherwise don't transform the proof and just use the hypothesis else { res = p; // hypsets have already been computed for p } } - else if (!dirty) { res = p; } - - else if (m.is_lemma(p)) - { - //lemma: reduce the premise; remove reduced consequences from conclusion - SASSERT(args.size() == 1); - res = mk_lemma_core(args[0], m.get_fact(p)); - compute_hypsets(res); - } - else if (m.is_unit_resolution(p)) - { - // unit: reduce untis; reduce the first premise; rebuild unit resolution - res = mk_unit_resolution_core(args); - compute_hypsets(res); - } else { - res = mk_step_core(p, args); - compute_hypsets(res); - } - - SASSERT(res); - m_cache.insert(p, res); - - SASSERT(m_active_hyps.contains(res)); - proof_set* active_hyps = m_active_hyps.find(res); - if (active_hyps->empty() && m.has_fact(res) && m.is_false(m.get_fact(res))) - { - return res; + res = p; + // hypsets have already been computed for p } } - UNREACHABLE(); - return nullptr; - } + else if (!dirty) { res = p; } - proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) - { - SASSERT(m.is_false(m.get_fact(premise))); - - SASSERT(m_active_hyps.contains(premise)); - proof_set* active_hyps = m_active_hyps.find(premise); - - // if there is no active hypothesis return the premise - if (active_hyps->empty()) + else if (m.is_lemma(p)) { - return premise; + //lemma: reduce the premise; remove reduced consequences from conclusion + SASSERT(args.size() == 1); + res = mk_lemma_core(args[0], m.get_fact(p)); + compute_hypsets(res); } - // otherwise build disjunction of the negated active hypothesis' and add lemma step. - else + else if (m.is_unit_resolution(p)) { - expr_ref_buffer args(m); - for (auto hyp : *active_hyps) - { - expr* hyp_fact = m.get_fact(hyp); - expr_ref negated_hyp_fact(m); - negated_hyp_fact = m.is_not(hyp_fact) ? to_app(hyp_fact)->get_arg(0) : m.mk_not(hyp_fact); - args.push_back(negated_hyp_fact); - } - - expr_ref lemma(m); - if (args.size() == 1) - { - lemma = args[0]; - } - else - { - lemma = m.mk_or(args.size(), args.c_ptr()); - } - proof_ref res(m); - res = m.mk_lemma(premise, lemma); - m_pinned.push_back(res); - return res; - } - } - - proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) - { - ptr_buffer pf_args; // the arguments of the transformed unit resolution step - pf_args.push_back(args [0]); // the first element of args is the clause to resolve with - - // if any literal is false, we don't need a unit resolution step - // could be the case due to transformations which already have been done - for (unsigned i = 1; i < args.size(); ++i) - { - if (m.is_false(m.get_fact(args[i]))) - { - return args[i]; - } - } - - app *cls_fact = to_app(m.get_fact(args[0])); // BUG: I guess this shouldn't work with quantifiers (since they are no apps) - ptr_buffer cls; - if (m.is_or(cls_fact)) { - for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) - { cls.push_back(cls_fact->get_arg(i)); } - } else { cls.push_back(cls_fact); } - - // construct new resolvent - ptr_buffer new_fact_cls; - bool found; - // XXX quadratic - for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { - found = false; - for (unsigned j = 1; j < args.size(); ++j) { - if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { - found = true; - pf_args.push_back(args [j]); - break; - } - } - if (!found) { - new_fact_cls.push_back(cls.get(i)); - } - } - - SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size()); - expr_ref new_fact(m); - new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); - - // create new proof step - if (pf_args.size() == 1) // the only premise is the clause itself - { - return args[0]; + // unit: reduce untis; reduce the first premise; rebuild unit resolution + res = mk_unit_resolution_core(args); + compute_hypsets(res); } else { - proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); - m_pinned.push_back(res); + res = mk_step_core(p, args); + compute_hypsets(res); + } + + SASSERT(res); + m_cache.insert(p, res); + + SASSERT(m_active_hyps.contains(res)); + proof_set* active_hyps = m_active_hyps.find(res); + if (active_hyps->empty() && m.has_fact(res) && m.is_false(m.get_fact(res))) + { return res; } } + UNREACHABLE(); + return nullptr; +} - proof* hypothesis_reducer::mk_step_core(proof* old_step, ptr_buffer& args) +proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) +{ + SASSERT(m.is_false(m.get_fact(premise))); + + SASSERT(m_active_hyps.contains(premise)); + proof_set* active_hyps = m_active_hyps.find(premise); + + // if there is no active hypothesis return the premise + if (active_hyps->empty()) { - // if any of the literals is false, we don't need a step - for (unsigned i = 0; i < args.size(); ++i) + return premise; + } + // otherwise build disjunction of the negated active hypothesis' and add lemma step. + else + { + expr_ref_buffer args(m); + for (auto hyp : *active_hyps) { - if (m.is_false(m.get_fact(args[i]))) - { - return args[i]; - } + expr* hyp_fact = m.get_fact(hyp); + expr_ref negated_hyp_fact(m); + negated_hyp_fact = m.is_not(hyp_fact) ? to_app(hyp_fact)->get_arg(0) : m.mk_not(hyp_fact); + args.push_back(negated_hyp_fact); } - // otherwise build step - args.push_back(to_app(m.get_fact(old_step))); // BUG: I guess this doesn't work with quantifiers (since they are no apps) - - SASSERT(old_step->get_decl()->get_arity() == args.size()); - proof* res = m.mk_app(old_step->get_decl(), args.size(), (expr * const*)args.c_ptr()); + expr_ref lemma(m); + if (args.size() == 1) + { + lemma = args[0]; + } + else + { + lemma = m.mk_or(args.size(), args.c_ptr()); + } + proof_ref res(m); + res = m.mk_lemma(premise, lemma); m_pinned.push_back(res); return res; } +} + +proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) +{ + ptr_buffer pf_args; // the arguments of the transformed unit resolution step + pf_args.push_back(args [0]); // the first element of args is the clause to resolve with + + // if any literal is false, we don't need a unit resolution step + // could be the case due to transformations which already have been done + for (unsigned i = 1; i < args.size(); ++i) + { + if (m.is_false(m.get_fact(args[i]))) + { + return args[i]; + } + } + + app *cls_fact = to_app(m.get_fact(args[0])); // BUG: I guess this shouldn't work with quantifiers (since they are no apps) + ptr_buffer cls; + if (m.is_or(cls_fact)) { + for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) + { cls.push_back(cls_fact->get_arg(i)); } + } else { cls.push_back(cls_fact); } + + // construct new resolvent + ptr_buffer new_fact_cls; + bool found; + // XXX quadratic + for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { + found = false; + for (unsigned j = 1; j < args.size(); ++j) { + if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { + found = true; + pf_args.push_back(args [j]); + break; + } + } + if (!found) { + new_fact_cls.push_back(cls.get(i)); + } + } + + SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size()); + expr_ref new_fact(m); + new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); + + // create new proof step + if (pf_args.size() == 1) // the only premise is the clause itself + { + return args[0]; + } + else + { + proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); + m_pinned.push_back(res); + return res; + } +} + +proof* hypothesis_reducer::mk_step_core(proof* old_step, ptr_buffer& args) +{ + // if any of the literals is false, we don't need a step + for (unsigned i = 0; i < args.size(); ++i) + { + if (m.is_false(m.get_fact(args[i]))) + { + return args[i]; + } + } + + // otherwise build step + args.push_back(to_app(m.get_fact(old_step))); // BUG: I guess this doesn't work with quantifiers (since they are no apps) + + SASSERT(old_step->get_decl()->get_arity() == args.size()); + proof* res = m.mk_app(old_step->get_decl(), args.size(), (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + return res; +} }; diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index e47c9882a..671fda783 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -22,67 +22,74 @@ Revision History: namespace spacer { - bool is_arith_lemma(ast_manager& m, proof* pr); - bool is_farkas_lemma(ast_manager& m, proof* pr); +bool is_arith_lemma(ast_manager& m, proof* pr); +bool is_farkas_lemma(ast_manager& m, proof* pr); - /* - * prints the proof pr in dot representation to the file proof.dot - * if iuc_pr is not nullptr, then it is queried for coloring partitions - */ - class iuc_proof; - void pp_proof_dot(ast_manager& m, proof* pr, iuc_proof* iuc_pr = nullptr); +class theory_axiom_reducer { +public: + theory_axiom_reducer(ast_manager& m) : m(m), m_pinned(m) {} - class theory_axiom_reducer - { - public: - theory_axiom_reducer(ast_manager& m) : m(m), m_pinned(m) {} + // reduce theory axioms and return transformed proof + proof_ref reduce(proof* pr); - // reduce theory axioms and return transformed proof - proof_ref reduce(proof* pr); +private: + ast_manager &m; - private: - ast_manager &m; + // tracking all created expressions + expr_ref_vector m_pinned; - // tracking all created expressions - expr_ref_vector m_pinned; + // maps each proof of a clause to the transformed subproof of + // that clause + obj_map m_cache; - // maps each proof of a clause to the transformed subproof of that clause - obj_map m_cache; + void reset(); +}; - void reset(); - }; +class hypothesis_reducer +{ +public: + hypothesis_reducer(ast_manager &m) : m(m), m_pinned(m) {} - class hypothesis_reducer - { - public: - hypothesis_reducer(ast_manager &m) : m(m), m_pinned(m) {} + // reduce hypothesis and return transformed proof + proof_ref reduce(proof* pf); - // reduce hypothesis and return transformed proof - proof_ref reduce(proof* pf); +private: + typedef obj_hashtable expr_set; + typedef obj_hashtable proof_set; - private: - typedef obj_hashtable expr_set; - typedef obj_hashtable proof_set; + ast_manager &m; - ast_manager &m; + // created expressions + expr_ref_vector m_pinned; - expr_ref_vector m_pinned; // tracking all created expressions - ptr_vector m_pinned_active_hyps; // tracking all created sets of active hypothesis - ptr_vector m_pinned_parent_hyps; // tracking all created sets of parent hypothesis + // created sets of active hypothesis + ptr_vector m_pinned_active_hyps; + // created sets of parent hypothesis + ptr_vector m_pinned_parent_hyps; - obj_map m_cache; // maps each proof of a clause to the transformed subproof of that clause - obj_map m_units; // maps each unit literal to the subproof of that unit - obj_map m_active_hyps; // maps each proof of a clause to the set of proofs of active hypothesis' of the clause - obj_map m_parent_hyps; // maps each proof of a clause to the hypothesis-fact, which are transitive parents of that clause, needed to avoid creating cycles in the proof. + // maps a proof to the transformed proof + obj_map m_cache; - void reset(); - void compute_hypsets(proof* pr); // compute active_hyps and parent_hyps for pr - void collect_units(proof* pr); // compute m_units - proof* compute_transformed_proof(proof* pf); + // maps a unit literal to its derivation + obj_map m_units; - proof* mk_lemma_core(proof *pf, expr *fact); - proof* mk_unit_resolution_core(ptr_buffer& args); - proof* mk_step_core(proof* old_step, ptr_buffer& args); - }; + // maps a proof to the set of proofs of active hypotheses + obj_map m_active_hyps; + // maps a proof to the hypothesis-fact that are transitive + // parents of that proof. Used for cycle detection and avoidance. + obj_map m_parent_hyps; + + void reset(); + + // compute active_hyps and parent_hyps for pr + void compute_hypsets(proof* pr); + // compute m_units + void collect_units(proof* pr); + proof* compute_transformed_proof(proof* pf); + + proof* mk_lemma_core(proof *pf, expr *fact); + proof* mk_unit_resolution_core(ptr_buffer& args); + proof* mk_step_core(proof* old_step, ptr_buffer& args); +}; } #endif From 2db38fedd6348d9edb28dc0bf1e6fbbf0e51feb3 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 May 2018 13:58:13 -0700 Subject: [PATCH 095/364] Cleanup of theory_axiom_reducer proof trasfomation --- src/muz/spacer/spacer_proof_utils.cpp | 116 ++++++++++++++------------ src/muz/spacer/spacer_proof_utils.h | 1 + 2 files changed, 63 insertions(+), 54 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 146436db9..749c3fbea 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -66,89 +66,96 @@ bool is_farkas_lemma(ast_manager& m, proof* pr) * ==================================== */ -void theory_axiom_reducer::reset() -{ +void theory_axiom_reducer::reset() { m_cache.reset(); m_pinned.reset(); } -proof_ref theory_axiom_reducer::reduce(proof* pr) -{ +// -- rewrite theory axioms into theory lemmas +proof_ref theory_axiom_reducer::reduce(proof* pr) { proof_post_order pit(pr, m); - while (pit.hasNext()) - { + while (pit.hasNext()) { proof* p = pit.next(); - if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) - { + if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) { // we have an arith-theory-axiom and want to get rid of it - // we need to replace the axiom with 1a) corresponding hypothesis', 1b) a theory lemma and a 1c) a lemma. Furthermore update datastructures - app *cls_fact = to_app(m.get_fact(p)); + // we need to replace the axiom with + // (a) corresponding hypothesis, + // (b) a theory lemma, and + // (c) a lemma. + // Furthermore update data-structures + app *fact = to_app(m.get_fact(p)); ptr_buffer cls; - if (m.is_or(cls_fact)) { - for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) - { cls.push_back(cls_fact->get_arg(i)); } - } else { cls.push_back(cls_fact); } + if (m.is_or(fact)) { + for (unsigned i = 0, sz = fact->get_num_args(); i < sz; ++i) + cls.push_back(fact->get_arg(i)); + } + else + cls.push_back(fact); - // 1a) create hypothesis' + // (a) create hypothesis ptr_buffer hyps; - for (unsigned i=0; i < cls.size(); ++i) - { - expr* hyp_fact = m.is_not(cls[i]) ? to_app(cls[i])->get_arg(0) : m.mk_not(cls[i]); + for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { + expr *c; + expr_ref hyp_fact(m); + if (m.is_not(cls[i], c)) + hyp_fact = c; + else + hyp_fact = m.mk_not (cls[i]); + proof* hyp = m.mk_hypothesis(hyp_fact); m_pinned.push_back(hyp); hyps.push_back(hyp); } - // 1b) create farkas lemma: need to rebuild parameters since mk_th_lemma adds tid as first parameter + // (b) create farkas lemma. Rebuild parameters since + // mk_th_lemma() adds tid as first parameter unsigned num_params = p->get_decl()->get_num_parameters(); parameter const* params = p->get_decl()->get_parameters(); vector parameters; - for (unsigned i = 1; i < num_params; ++i) { - parameters.push_back(params[i]); - } + for (unsigned i = 1; i < num_params; ++i) parameters.push_back(params[i]); SASSERT(params[0].is_symbol()); family_id tid = m.mk_family_id(params[0].get_symbol()); SASSERT(tid != null_family_id); - proof* th_lemma = m.mk_th_lemma(tid, m.mk_false(),hyps.size(), hyps.c_ptr(), num_params-1, parameters.c_ptr()); + proof* th_lemma = m.mk_th_lemma(tid, m.mk_false(), + hyps.size(), hyps.c_ptr(), + num_params-1, parameters.c_ptr()); + m_pinned.push_back(th_lemma); SASSERT(is_arith_lemma(m, th_lemma)); - // 1c) create lemma - proof* res = m.mk_lemma(th_lemma, cls_fact); - SASSERT(m.get_fact(res) == m.get_fact(p)); + // (c) create lemma + proof* res = m.mk_lemma(th_lemma, fact); m_pinned.push_back(res); m_cache.insert(p, res); + + SASSERT(m.get_fact(res) == m.get_fact(p)); } - else - { - bool dirty = false; // proof is dirty, if a subproof of one of its premises has been transformed + else { + // proof is dirty, if a subproof of one of its premises + // has been transformed + bool dirty = false; ptr_buffer args; - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) - { - proof* pp = m.get_parent(p, i); - proof* tmp; - if (m_cache.find(pp, tmp)) - { - args.push_back(tmp); - dirty = dirty || pp != tmp; - } - else - { - SASSERT(false); - } + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + proof *pp, *tmp; + pp = m.get_parent(p, i); + VERIFY(m_cache.find(pp, tmp)); + args.push_back(tmp); + dirty |= (pp != tmp); } - if (!dirty) // if not dirty just use the old step - { - m_cache.insert(p, p); - } - else // otherwise create new step with the corresponding proofs of the premises - { - if (m.has_fact(p)) { args.push_back(m.get_fact(p)); } + // if not dirty just use the old step + if (!dirty) m_cache.insert(p, p); + // otherwise create new proof with the corresponding proofs + // of the premises + else { + if (m.has_fact(p)) args.push_back(m.get_fact(p)); + SASSERT(p->get_decl()->get_arity() == args.size()); - proof* res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); + + proof* res = m.mk_app(p->get_decl(), + args.size(), (expr * const*)args.c_ptr()); m_pinned.push_back(res); m_cache.insert(p, res); } @@ -157,12 +164,13 @@ proof_ref theory_axiom_reducer::reduce(proof* pr) proof* res; VERIFY(m_cache.find(pr,res)); - DEBUG_CODE(proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(res, side)); + DEBUG_CODE( + proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side)); ); - return proof_ref(res,m); + return proof_ref(res, m); } void hypothesis_reducer::reset() diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index 671fda783..c741ce19e 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -25,6 +25,7 @@ namespace spacer { bool is_arith_lemma(ast_manager& m, proof* pr); bool is_farkas_lemma(ast_manager& m, proof* pr); +/// rewrites theory axioms into theory lemmas class theory_axiom_reducer { public: theory_axiom_reducer(ast_manager& m) : m(m), m_pinned(m) {} From 8d312f9d1f78a70d77744921cef2476b934973cb Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 May 2018 16:25:49 -0700 Subject: [PATCH 096/364] Cleanup of hypothesis_reducer --- src/muz/spacer/spacer_proof_utils.cpp | 345 ++++++++++++-------------- src/muz/spacer/spacer_proof_utils.h | 4 +- 2 files changed, 160 insertions(+), 189 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 749c3fbea..42cdcf50b 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -133,7 +133,7 @@ proof_ref theory_axiom_reducer::reduce(proof* pr) { SASSERT(m.get_fact(res) == m.get_fact(p)); } else { - // proof is dirty, if a subproof of one of its premises + // proof is dirty, if a sub-proof of one of its premises // has been transformed bool dirty = false; @@ -173,85 +173,67 @@ proof_ref theory_axiom_reducer::reduce(proof* pr) { return proof_ref(res, m); } -void hypothesis_reducer::reset() -{ +void hypothesis_reducer::reset() { m_cache.reset(); m_units.reset(); m_active_hyps.reset(); m_parent_hyps.reset(); + for (auto t : m_pinned_active_hyps) dealloc(t); m_pinned_active_hyps.reset(); + for (auto t : m_pinned_parent_hyps) dealloc(t); m_pinned_parent_hyps.reset(); - m_pinned.reset(); } -void hypothesis_reducer::compute_hypsets(proof* pr) -{ +void hypothesis_reducer::compute_hypsets(proof *pr) { ptr_vector todo; todo.push_back(pr); - while (!todo.empty()) - { + while (!todo.empty()) { proof* p = todo.back(); - if (m_active_hyps.contains(p)) - { + if (m_active_hyps.contains(p)) { SASSERT(m_parent_hyps.contains(p)); todo.pop_back(); + continue; } - // if we haven't already visited the current unit - else - { - bool existsUnvisitedParent = false; - // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result - // for p now, but wait until those unprocessed premises are processed. - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - SASSERT(m.is_proof(p->get_arg(i))); - proof* premise = to_app(p->get_arg(i)); + bool dirty = false; + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + SASSERT(m.is_proof(p->get_arg(i))); + proof *parent = to_app(p->get_arg(i)); - // if we haven't visited the premise yet - if (!m_active_hyps.contains(premise)) - { - SASSERT(!m_parent_hyps.contains(premise)); - // add it to the stack - todo.push_back(premise); - existsUnvisitedParent = true; - } + if (!m_active_hyps.contains(parent)) { + SASSERT(!m_parent_hyps.contains(parent)); + todo.push_back(parent); + dirty = true; } + } + if (dirty) continue; - // if we already visited all premises, we can visit p too - if (!existsUnvisitedParent) - { - todo.pop_back(); + todo.pop_back(); - // create active_hyps-set and parent_hyps-set for step p - proof_set* active_hyps = alloc(proof_set); - m_pinned_active_hyps.insert(active_hyps); - m_active_hyps.insert(p, active_hyps); + // create active_hyps-set and parent_hyps-set for step p + proof_set* active_hyps = alloc(proof_set); + m_pinned_active_hyps.insert(active_hyps); + m_active_hyps.insert(p, active_hyps); - expr_set* parent_hyps = alloc(expr_set); - m_pinned_parent_hyps.insert(parent_hyps); - m_parent_hyps.insert(p, parent_hyps); + expr_set* parent_hyps = alloc(expr_set); + m_pinned_parent_hyps.insert(parent_hyps); + m_parent_hyps.insert(p, parent_hyps); - // fill both sets - if (m.is_hypothesis(p)) - { - active_hyps->insert(p); - parent_hyps->insert(m.get_fact(p)); - } - else - { - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) - { - proof* pp = m.get_parent(p, i); - set_union(*parent_hyps, *m_parent_hyps.find(pp)); + // fill both sets + if (m.is_hypothesis(p)) { + active_hyps->insert(p); + parent_hyps->insert(m.get_fact(p)); + } + else { + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + proof* parent = m.get_parent(p, i); + set_union(*parent_hyps, *m_parent_hyps.find(parent)); - if (!m.is_lemma(p)) // lemmas clear all hypotheses - { - set_union(*active_hyps, *m_active_hyps.find(pp)); - } - } - } + if (!m.is_lemma(p)) + // lemmas clear all hypotheses + set_union(*active_hyps, *m_active_hyps.find(parent)); } } } @@ -259,48 +241,41 @@ void hypothesis_reducer::compute_hypsets(proof* pr) // collect all units that are hyp-free and are used as hypotheses somewhere // requires that m_active_hyps and m_parent_hyps have been computed -void hypothesis_reducer::collect_units(proof* pr) -{ +void hypothesis_reducer::collect_units(proof* pr) { expr_set* all_hyps = m_parent_hyps.find(pr); - SASSERT(all_hyps != nullptr); + SASSERT(all_hyps); proof_post_order pit(pr, m); while (pit.hasNext()) { proof* p = pit.next(); - if (!m.is_hypothesis(p)) - { + if (!m.is_hypothesis(p)) { proof_set* active_hyps = m_active_hyps.find(p); - SASSERT(active_hyps != nullptr); + SASSERT(active_hyps); - // collect units that are hyp-free and are used as hypotheses in the proof pr - if (active_hyps->empty() && m.has_fact(p) && all_hyps->contains(m.get_fact(p))) - { + // collect units that are hyp-free and are used as + // hypotheses in the proof pr + if (active_hyps->empty() && m.has_fact(p) && + all_hyps->contains(m.get_fact(p))) m_units.insert(m.get_fact(p), p); - } } } } -proof_ref hypothesis_reducer::reduce(proof* pr) -{ +proof_ref hypothesis_reducer::reduce(proof* pr) { compute_hypsets(pr); collect_units(pr); - proof* res = compute_transformed_proof(pr); - SASSERT(res != nullptr); - - proof_ref res_ref(res,m); - + proof_ref res(compute_transformed_proof(pr), m); + SASSERT(res); reset(); + DEBUG_CODE(proof_checker pc(m); expr_ref_vector side(m); - SASSERT(pc.check(res, side)); - ); - return res_ref; + SASSERT(pc.check(res, side));); + return res; } -proof* hypothesis_reducer::compute_transformed_proof(proof* pf) -{ +proof* hypothesis_reducer::compute_transformed_proof(proof* pf) { proof *res = NULL; ptr_vector todo; @@ -325,204 +300,198 @@ proof* hypothesis_reducer::compute_transformed_proof(proof* pf) pp = m.get_parent(p, i); if (m_cache.find(pp, tmp)) { args.push_back(tmp); - dirty = dirty || pp != tmp; + dirty |= pp != tmp; } else { todo.push_back(pp); } } - if (todo_sz < todo.size()) { continue; } - else { todo.pop_back(); } + if (todo_sz < todo.size()) continue; + todo.pop_back(); - // here the proof transformation begins - // INV: whenever we visit p, active_hyps and parent_hyps have been computed for the args. - if (m.is_hypothesis(p)) - { + // transform the proof + + // INV: whenever p is visited, active_hyps and parent_hyps + // have already been computed for everything in args. + if (m.is_hypothesis(p)) { // hyp: replace by a corresponding unit - if (m_units.find(m.get_fact(p), tmp)) - { + if (m_units.find(m.get_fact(p), tmp)) { // look up the proof of the unit: // if there is a transformed proof use that one // otherwise use the original proof proof* proof_of_unit; - if (!m_cache.find(tmp,proof_of_unit)) - { + if (!m_cache.find(tmp, proof_of_unit)) { proof_of_unit = tmp; } - // compute hypsets (have not been computed in general, since the unit can be anywhere in the proof) + // compute hypsets (have not been computed in general, + // since the unit can be anywhere in the proof) compute_hypsets(proof_of_unit); // if the transformation doesn't create a cycle, perform it SASSERT(m_parent_hyps.contains(proof_of_unit)); expr_set* parent_hyps = m_parent_hyps.find(proof_of_unit); if (!parent_hyps->contains(p)) - { - res = proof_of_unit; // hypsets have already been computed for proof_of_unit - } - // otherwise don't transform the proof and just use the hypothesis + res = proof_of_unit; + // otherwise don't transform the proof and just use + // the hypothesis else - { - res = p; // hypsets have already been computed for p - } + res = p; } else - { - res = p; // hypsets have already been computed for p - } + res = p; } - else if (!dirty) { res = p; } - - else if (m.is_lemma(p)) - { - //lemma: reduce the premise; remove reduced consequences from conclusion + else if (!dirty) + res = p; + else if (m.is_lemma(p)) { + //lemma: reduce the premise; remove reduced consequences + //from conclusion SASSERT(args.size() == 1); res = mk_lemma_core(args[0], m.get_fact(p)); compute_hypsets(res); } - else if (m.is_unit_resolution(p)) - { - // unit: reduce untis; reduce the first premise; rebuild unit resolution + else if (m.is_unit_resolution(p)) { + // unit: reduce untis; reduce the first premise; rebuild + // unit resolution res = mk_unit_resolution_core(args); compute_hypsets(res); } - else - { - res = mk_step_core(p, args); + else { + res = mk_proof_core(p, args); compute_hypsets(res); } SASSERT(res); m_cache.insert(p, res); + // bail out as soon as found a sub-proof of false SASSERT(m_active_hyps.contains(res)); proof_set* active_hyps = m_active_hyps.find(res); if (active_hyps->empty() && m.has_fact(res) && m.is_false(m.get_fact(res))) - { return res; - } } UNREACHABLE(); return nullptr; } -proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) -{ +proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { SASSERT(m.is_false(m.get_fact(premise))); - SASSERT(m_active_hyps.contains(premise)); + proof_set* active_hyps = m_active_hyps.find(premise); // if there is no active hypothesis return the premise - if (active_hyps->empty()) - { + if (active_hyps->empty()) { + // XXX just in case premise might go away + m_pinned.push_back(premise); return premise; } - // otherwise build disjunction of the negated active hypothesis' and add lemma step. - else - { - expr_ref_buffer args(m); - for (auto hyp : *active_hyps) - { - expr* hyp_fact = m.get_fact(hyp); - expr_ref negated_hyp_fact(m); - negated_hyp_fact = m.is_not(hyp_fact) ? to_app(hyp_fact)->get_arg(0) : m.mk_not(hyp_fact); - args.push_back(negated_hyp_fact); - } - expr_ref lemma(m); - if (args.size() == 1) - { - lemma = args[0]; - } + // otherwise, build a disjunction of the negated active hypotheses + // and add a lemma proof step + expr_ref_buffer args(m); + for (auto hyp : *active_hyps) { + expr *hyp_fact, *t; + hyp_fact = m.get_fact(hyp); + if (m.is_not(hyp_fact, t)) + args.push_back(t); else - { - lemma = m.mk_or(args.size(), args.c_ptr()); - } - proof_ref res(m); - res = m.mk_lemma(premise, lemma); - m_pinned.push_back(res); - return res; + args.push_back(m.mk_not(hyp_fact)); } + + expr_ref lemma(m); + lemma = mk_or(m, args.size(), args.c_ptr()); + + proof* res; + res = m.mk_lemma(premise, lemma); + m_pinned.push_back(res); + return res; } -proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) -{ - ptr_buffer pf_args; // the arguments of the transformed unit resolution step - pf_args.push_back(args [0]); // the first element of args is the clause to resolve with - +proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) { // if any literal is false, we don't need a unit resolution step - // could be the case due to transformations which already have been done - for (unsigned i = 1; i < args.size(); ++i) - { - if (m.is_false(m.get_fact(args[i]))) - { + // This can be the case due to some previous transformations + for (unsigned i = 1, sz = args.size(); i < sz; ++i) { + if (m.is_false(m.get_fact(args[i]))) { + // XXX just in case + m_pinned.push_back(args[i]); return args[i]; } } - app *cls_fact = to_app(m.get_fact(args[0])); // BUG: I guess this shouldn't work with quantifiers (since they are no apps) - ptr_buffer cls; - if (m.is_or(cls_fact)) { - for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) - { cls.push_back(cls_fact->get_arg(i)); } - } else { cls.push_back(cls_fact); } + proof* arg0 = args[0]; + ptr_buffer pf_args; + pf_args.push_back(arg0); - // construct new resolvent - ptr_buffer new_fact_cls; + // BUG: I guess this shouldn't work with quantifiers (since they + // are not apps) + // AG: who is "I"? What is the bug? + app *fact = to_app(m.get_fact(arg0)); + ptr_buffer cls; + if (m.is_or(fact)) { + for (unsigned i = 0, sz = fact->get_num_args(); i < sz; ++i) + cls.push_back(fact->get_arg(i)); + } + else + cls.push_back(fact); + + // construct the new resolvent + ptr_buffer new_fact; bool found; - // XXX quadratic + + // -- find all literals that are resolved on + // XXX quadratic implementation for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { found = false; for (unsigned j = 1; j < args.size(); ++j) { - if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { + if (m.is_complement(cls.get(i), m.get_fact(args[j]))) { found = true; - pf_args.push_back(args [j]); + pf_args.push_back(args[j]); break; } } - if (!found) { - new_fact_cls.push_back(cls.get(i)); - } + if (!found) new_fact.push_back(cls.get(i)); } - SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size()); - expr_ref new_fact(m); - new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); + SASSERT(new_fact.size() + pf_args.size() - 1 == cls.size()); - // create new proof step - if (pf_args.size() == 1) // the only premise is the clause itself - { - return args[0]; - } - else - { - proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); - m_pinned.push_back(res); - return res; + // unit resolution got reduced to noop + if (pf_args.size() == 1) { + // XXX just in case + m_pinned.push_back(arg0); + return arg0; } + + // make unit resolution proof step + expr_ref tmp(m); + tmp = mk_or(m, new_fact.size(), new_fact.c_ptr()); + proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); + m_pinned.push_back(res); + return res; } -proof* hypothesis_reducer::mk_step_core(proof* old_step, ptr_buffer& args) -{ - // if any of the literals is false, we don't need a step - for (unsigned i = 0; i < args.size(); ++i) - { - if (m.is_false(m.get_fact(args[i]))) - { +proof* hypothesis_reducer::mk_proof_core(proof* old, ptr_buffer& args) { + // if any of the literals are false, we don't need a step + for (unsigned i = 0; i < args.size(); ++i) { + if (m.is_false(m.get_fact(args[i]))) { + // XXX just in case + m_pinned.push_back(args[i]); return args[i]; } } // otherwise build step - args.push_back(to_app(m.get_fact(old_step))); // BUG: I guess this doesn't work with quantifiers (since they are no apps) + // BUG: I guess this doesn't work with quantifiers (since they are no apps) + args.push_back(to_app(m.get_fact(old))); - SASSERT(old_step->get_decl()->get_arity() == args.size()); - proof* res = m.mk_app(old_step->get_decl(), args.size(), (expr * const*)args.c_ptr()); + SASSERT(old->get_decl()->get_arity() == args.size()); + + proof* res = m.mk_app(old->get_decl(), args.size(), + (expr * const*)args.c_ptr()); m_pinned.push_back(res); return res; } diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index c741ce19e..f348e71a6 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -46,10 +46,12 @@ private: void reset(); }; +/// reduces the number of hypotheses in a proof class hypothesis_reducer { public: hypothesis_reducer(ast_manager &m) : m(m), m_pinned(m) {} + ~hypothesis_reducer() {reset();} // reduce hypothesis and return transformed proof proof_ref reduce(proof* pf); @@ -90,7 +92,7 @@ private: proof* mk_lemma_core(proof *pf, expr *fact); proof* mk_unit_resolution_core(ptr_buffer& args); - proof* mk_step_core(proof* old_step, ptr_buffer& args); + proof* mk_proof_core(proof* old, ptr_buffer& args); }; } #endif From ecf15ab07d006d213f8754fd5aeb8dd2e7ac7bc8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 16 May 2018 16:42:51 -0700 Subject: [PATCH 097/364] add model_evaluator_util features to model_evalautor Signed-off-by: Nikolaj Bjorner --- src/model/model_evaluator.cpp | 43 +++++++++++++++++++++++++++++++++++ src/model/model_evaluator.h | 10 +++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 0b25a250b..07ee125f8 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -562,6 +562,12 @@ void model_evaluator::reset(params_ref const & p) { updt_params(p); } +void model_evaluator::reset(model_core &model, params_ref const& p) { + dealloc(m_imp); + m_imp = alloc(imp, model, p); +} + + void model_evaluator::operator()(expr * t, expr_ref & result) { TRACE("model_evaluator", tout << mk_ismt2_pp(t, m()) << "\n";); m_imp->operator()(t, result); @@ -574,3 +580,40 @@ expr_ref model_evaluator::operator()(expr * t) { this->operator()(t, result); return result; } + +bool model_evaluator::is_true(expr* t) { + expr_ref tmp(m()); + return eval(t, tmp, true) && m().is_true(tmp); +} + +bool model_evaluator::is_false(expr* t) { + expr_ref tmp(m()); + return eval(t, tmp, true) && m().is_false(tmp); +} + +bool model_evaluator::is_true(expr_ref_vector const& ts) { + for (expr* t : ts) if (!is_true(t)) return false; + return true; +} + +bool model_evaluator::eval(expr* t, expr_ref& r, bool model_completion) { + set_model_completion(model_completion); + try { + r = (*this)(t); + return true; + } + catch (model_evaluator_exception &ex) { + (void)ex; + TRACE("model_evaluator", tout << ex.msg () << "\n";); + return false; + } +} + +bool model_evaluator::eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion) { + expr_ref tmp(m()); + tmp = mk_and(ts); + return eval(tmp, r, model_completion); +} + + + diff --git a/src/model/model_evaluator.h b/src/model/model_evaluator.h index bd2b2d664..6ae7d8891 100644 --- a/src/model/model_evaluator.h +++ b/src/model/model_evaluator.h @@ -43,11 +43,19 @@ public: static void get_param_descrs(param_descrs & r); void operator()(expr * t, expr_ref & r); - expr_ref operator()(expr* t); + // exception safe + bool eval(expr* t, expr_ref& r, bool model_completion = true); + bool eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion = true); + + bool is_true(expr * t); + bool is_false(expr * t); + bool is_true(expr_ref_vector const& ts); + void cleanup(params_ref const & p = params_ref()); void reset(params_ref const & p = params_ref()); + void reset(model_core& model, params_ref const & p = params_ref()); unsigned get_num_steps() const; }; From ff0f2571025cf590658fa136e71e9442e0a74cc1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 16 May 2018 18:35:38 -0700 Subject: [PATCH 098/364] remove iff Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 3 +- src/ast/ast.cpp | 24 +-- src/ast/ast.h | 19 +- src/ast/ast_smt2_pp.cpp | 4 - src/ast/ast_smt_pp.cpp | 3 - src/ast/macro_substitution.cpp | 4 +- src/ast/macros/macro_manager.cpp | 5 +- src/ast/macros/macro_util.cpp | 9 +- src/ast/macros/quasi_macros.cpp | 2 +- src/ast/normal_forms/nnf.cpp | 5 +- src/ast/proofs/proof_checker.cpp | 6 +- src/ast/proofs/proof_utils.h | 4 +- src/ast/rewriter/bool_rewriter.cpp | 5 +- src/ast/rewriter/bool_rewriter.h | 2 +- src/ast/rewriter/der.cpp | 11 +- src/ast/rewriter/quant_hoist.cpp | 2 +- src/ast/rewriter/th_rewriter.cpp | 2 +- src/ast/static_features.cpp | 8 +- src/model/model_implicant.cpp | 5 - src/muz/base/rule_properties.cpp | 6 +- src/muz/pdr/pdr_context.cpp | 2 +- src/muz/rel/udoc_relation.cpp | 2 +- src/muz/spacer/spacer_legacy_mev.cpp | 5 - src/muz/spacer/spacer_util.cpp | 192 ++++++++---------- src/muz/transforms/dl_mk_array_blast.cpp | 2 +- .../dl_mk_quantifier_instantiation.cpp | 2 +- src/nlsat/tactic/goal2nlsat.cpp | 1 - src/parsers/util/cost_parser.cpp | 2 +- src/qe/qe.cpp | 2 +- src/qe/qe_lite.cpp | 2 +- src/sat/tactic/atom2bool_var.cpp | 2 +- src/sat/tactic/goal2sat.cpp | 2 - src/smt/asserted_formulas.cpp | 7 +- src/smt/cost_evaluator.cpp | 3 +- src/smt/expr_context_simplifier.cpp | 18 +- src/smt/smt_checker.cpp | 28 +-- src/smt/smt_conflict_resolution.cpp | 4 +- src/smt/smt_internalizer.cpp | 16 +- src/smt/smt_model_finder.cpp | 3 - src/smt/smt_quantifier_stat.cpp | 12 +- src/smt/smt_quick_checker.cpp | 12 +- src/tactic/aig/aig.cpp | 4 - src/tactic/core/cofactor_elim_term_ite.cpp | 1 - src/tactic/core/dom_simplify_tactic.cpp | 2 +- src/tactic/core/elim_uncnstr_tactic.cpp | 1 - src/tactic/core/solve_eqs_tactic.cpp | 5 +- src/tactic/core/tseitin_cnf_tactic.cpp | 2 - 47 files changed, 199 insertions(+), 264 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 536b94fb2..34168f1f4 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -190,7 +190,7 @@ extern "C" { MK_UNARY(Z3_mk_not, mk_c(c)->get_basic_fid(), OP_NOT, SKIP); MK_BINARY(Z3_mk_eq, mk_c(c)->get_basic_fid(), OP_EQ, SKIP); MK_NARY(Z3_mk_distinct, mk_c(c)->get_basic_fid(), OP_DISTINCT, SKIP); - MK_BINARY(Z3_mk_iff, mk_c(c)->get_basic_fid(), OP_IFF, SKIP); + MK_BINARY(Z3_mk_iff, mk_c(c)->get_basic_fid(), OP_EQ, SKIP); MK_BINARY(Z3_mk_implies, mk_c(c)->get_basic_fid(), OP_IMPLIES, SKIP); MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP); MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP); @@ -894,7 +894,6 @@ extern "C" { case OP_ITE: return Z3_OP_ITE; case OP_AND: return Z3_OP_AND; case OP_OR: return Z3_OP_OR; - case OP_IFF: return Z3_OP_IFF; case OP_XOR: return Z3_OP_XOR; case OP_NOT: return Z3_OP_NOT; case OP_IMPLIES: return Z3_OP_IMPLIES; diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index cae1618c0..681f64a25 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -643,7 +643,6 @@ basic_decl_plugin::basic_decl_plugin(): m_false_decl(nullptr), m_and_decl(nullptr), m_or_decl(nullptr), - m_iff_decl(nullptr), m_xor_decl(nullptr), m_not_decl(nullptr), m_implies_decl(nullptr), @@ -861,7 +860,6 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) { m_false_decl = mk_bool_op_decl("false", OP_FALSE); m_and_decl = mk_bool_op_decl("and", OP_AND, 2, true, true, true, true); m_or_decl = mk_bool_op_decl("or", OP_OR, 2, true, true, true, true); - m_iff_decl = mk_bool_op_decl("iff", OP_IFF, 2, false, true, false, false, true); m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true); m_not_decl = mk_bool_op_decl("not", OP_NOT, 1); m_implies_decl = mk_implies_decl(); @@ -892,13 +890,13 @@ void basic_decl_plugin::get_op_names(svector & op_names, symbol co if (logic == symbol::null) { // user friendly aliases op_names.push_back(builtin_name("implies", OP_IMPLIES)); - op_names.push_back(builtin_name("iff", OP_IFF)); + op_names.push_back(builtin_name("iff", OP_EQ)); op_names.push_back(builtin_name("if_then_else", OP_ITE)); op_names.push_back(builtin_name("if", OP_ITE)); op_names.push_back(builtin_name("&&", OP_AND)); op_names.push_back(builtin_name("||", OP_OR)); op_names.push_back(builtin_name("equals", OP_EQ)); - op_names.push_back(builtin_name("equiv", OP_IFF)); + op_names.push_back(builtin_name("equiv", OP_EQ)); } } @@ -919,7 +917,6 @@ void basic_decl_plugin::finalize() { DEC_REF(m_and_decl); DEC_REF(m_or_decl); DEC_REF(m_not_decl); - DEC_REF(m_iff_decl); DEC_REF(m_xor_decl); DEC_REF(m_implies_decl); DEC_ARRAY_REF(m_eq_decls); @@ -1051,7 +1048,6 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; - case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : nullptr; @@ -1093,7 +1089,6 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; - case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): nullptr; @@ -2636,10 +2631,10 @@ proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { if (!p1 || !p2) return nullptr; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); - CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))), + CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_eq(get_fact(p2)) || is_oeq(get_fact(p2))), tout << mk_ll_pp(p1, *this) << "\n"; tout << mk_ll_pp(p2, *this) << "\n";); - SASSERT(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))); + SASSERT(is_implies(get_fact(p2)) || is_eq(get_fact(p2)) || is_oeq(get_fact(p2))); CTRACE("mk_modus_ponens", to_app(get_fact(p2))->get_arg(0) != get_fact(p1), tout << mk_pp(get_fact(p1), *this) << "\n" << mk_pp(get_fact(p2), *this) << "\n";); SASSERT(to_app(get_fact(p2))->get_arg(0) == get_fact(p1)); @@ -2717,8 +2712,6 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n"; tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";); SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() || - ((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) && - (is_iff(get_fact(p2)) || is_eq(get_fact(p2)))) || ( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) && (is_eq(get_fact(p2)) || is_oeq(get_fact(p2))))); CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), @@ -2797,7 +2790,7 @@ proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) if (!p) return nullptr; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); - SASSERT(is_iff(get_fact(p))); + SASSERT(is_eq(get_fact(p))); return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_iff(q1, q2)); } @@ -2884,8 +2877,7 @@ bool ast_manager::is_quant_inst(expr const* e, expr*& not_q_or_i, ptr_vectorget_arg(0), r1, r2) || - is_iff(to_app(e)->get_arg(0), r1, r2)); + VERIFY (is_eq(to_app(e)->get_arg(0), r1, r2)); return true; } else { @@ -2913,7 +2905,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro fact = mk_false(); } else { - CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_pp(f1, *this) << " " << mk_pp(f2, *this) << "\n";); + CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_ll_pp(f1, *this) << "\n" << mk_ll_pp(f2, *this) << "\n";); SASSERT(is_or(f1)); ptr_buffer new_lits; app const * cls = to_app(f1); @@ -3044,7 +3036,7 @@ proof * ast_manager::mk_iff_oeq(proof * p) { if (!p) return p; SASSERT(has_fact(p)); - SASSERT(is_iff(get_fact(p)) || is_oeq(get_fact(p))); + SASSERT(is_eq(get_fact(p)) || is_oeq(get_fact(p))); if (is_oeq(get_fact(p))) return p; diff --git a/src/ast/ast.h b/src/ast/ast.h index e85f164e1..ce193e8b1 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1041,7 +1041,7 @@ enum basic_sort_kind { }; enum basic_op_kind { - OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP, + OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP, PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO, PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT, @@ -1060,7 +1060,6 @@ protected: func_decl * m_false_decl; func_decl * m_and_decl; func_decl * m_or_decl; - func_decl * m_iff_decl; func_decl * m_xor_decl; func_decl * m_not_decl; func_decl * m_implies_decl; @@ -1344,9 +1343,9 @@ public: bool is_and(expr const * n) const { return is_app_of(n, m_fid, OP_AND); } bool is_not(expr const * n) const { return is_app_of(n, m_fid, OP_NOT); } bool is_eq(expr const * n) const { return is_app_of(n, m_fid, OP_EQ); } + bool is_iff(expr const* n) const { return is_eq(n) && is_bool(to_app(n)->get_arg(0)); } bool is_oeq(expr const * n) const { return is_app_of(n, m_fid, OP_OEQ); } bool is_distinct(expr const * n) const { return is_app_of(n, m_fid, OP_DISTINCT); } - bool is_iff(expr const * n) const { return is_app_of(n, m_fid, OP_IFF); } bool is_xor(expr const * n) const { return is_app_of(n, m_fid, OP_XOR); } bool is_ite(expr const * n) const { return is_app_of(n, m_fid, OP_ITE); } bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); } @@ -1361,7 +1360,6 @@ public: bool is_and(func_decl const * d) const { return is_decl_of(d, m_fid, OP_AND); } bool is_not(func_decl const * d) const { return is_decl_of(d, m_fid, OP_NOT); } bool is_eq(func_decl const * d) const { return is_decl_of(d, m_fid, OP_EQ); } - bool is_iff(func_decl const * d) const { return is_decl_of(d, m_fid, OP_IFF); } bool is_xor(func_decl const * d) const { return is_decl_of(d, m_fid, OP_XOR); } bool is_ite(func_decl const * d) const { return is_decl_of(d, m_fid, OP_ITE); } bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); } @@ -1369,13 +1367,13 @@ public: MATCH_UNARY(is_not); MATCH_BINARY(is_eq); - MATCH_BINARY(is_iff); MATCH_BINARY(is_implies); MATCH_BINARY(is_and); MATCH_BINARY(is_or); MATCH_BINARY(is_xor); MATCH_TERNARY(is_and); MATCH_TERNARY(is_or); + bool is_iff(expr const* n, expr*& lhs, expr*& rhs) const { return is_eq(n, lhs, rhs) && is_bool(lhs); } bool is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const; }; @@ -1663,7 +1661,7 @@ public: bool is_bool(expr const * n) const; bool is_bool(sort const * s) const { return s == m_bool_sort; } - decl_kind get_eq_op(expr const * n) const { return is_bool(n) ? OP_IFF : OP_EQ; } + decl_kind get_eq_op(expr const * n) const { return OP_EQ; } private: sort * mk_sort(symbol const & name, sort_info * info); @@ -1987,9 +1985,9 @@ public: bool is_and(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_AND); } bool is_not(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_NOT); } bool is_eq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_EQ); } + bool is_iff(expr const * n) const { return is_eq(n) && is_bool(to_app(n)->get_arg(0)); } bool is_oeq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_OEQ); } bool is_distinct(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_DISTINCT); } - bool is_iff(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_IFF); } bool is_xor(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_XOR); } bool is_ite(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_ITE); } bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); } @@ -2005,7 +2003,7 @@ public: bool is_and(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_AND); } bool is_not(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_NOT); } bool is_eq(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ); } - bool is_iff(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_IFF); } + bool is_iff(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ) && is_bool(d->get_range()); } bool is_xor(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_XOR); } bool is_ite(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_ITE); } bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); } @@ -2015,7 +2013,6 @@ public: MATCH_UNARY(is_not); MATCH_BINARY(is_eq); - MATCH_BINARY(is_iff); MATCH_BINARY(is_implies); MATCH_BINARY(is_and); MATCH_BINARY(is_or); @@ -2023,6 +2020,8 @@ public: MATCH_TERNARY(is_and); MATCH_TERNARY(is_or); + bool is_iff(expr const* n, expr*& lhs, expr*& rhs) const { return is_eq(n, lhs, rhs) && is_bool(lhs); } + bool is_ite(expr const* n, expr*& t1, expr*& t2, expr*& t3) const { if (is_ite(n)) { t1 = to_app(n)->get_arg(0); @@ -2035,7 +2034,7 @@ public: public: app * mk_eq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, get_eq_op(lhs), lhs, rhs); } - app * mk_iff(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_IFF, lhs, rhs); } + app * mk_iff(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_EQ, lhs, rhs); } app * mk_oeq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_OEQ, lhs, rhs); } app * mk_xor(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_XOR, lhs, rhs); } app * mk_ite(expr * c, expr * t, expr * e) { return mk_app(m_basic_family_id, OP_ITE, c, t, e); } diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index a2958e3c9..9d27ffb24 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -63,10 +63,6 @@ format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const len = 3; return mk_string(m, "ite"); } - else if (m.is_iff(f)) { - len = 1; - return mk_string(m, "="); - } else { symbol s = f->get_name(); return pp_fdecl_name(s, len, f->is_skolem()); diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 635899d23..22adf42d9 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -225,9 +225,6 @@ class smt_printer { else if (m_manager.is_ite(d)) { m_out << "ite"; } - else if (m_manager.is_iff(d)) { - m_out << "="; - } else if (m_manager.is_implies(d)) { m_out << "=>"; } diff --git a/src/ast/macro_substitution.cpp b/src/ast/macro_substitution.cpp index a51e1a066..2868a1876 100644 --- a/src/ast/macro_substitution.cpp +++ b/src/ast/macro_substitution.cpp @@ -77,7 +77,7 @@ void macro_substitution::cleanup() { void macro_substitution::insert(func_decl * f, quantifier * q, proof * pr, expr_dependency * dep) { DEBUG_CODE({ app * body = to_app(q->get_expr()); - SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body)); + SASSERT(m_manager.is_eq(body)); expr * lhs = body->get_arg(0); expr * rhs = body->get_arg(1); SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f)); @@ -146,7 +146,7 @@ void macro_substitution::erase(func_decl * f) { void macro_substitution::get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def) { app * body = to_app(q->get_expr()); - SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body)); + SASSERT(m_manager.is_eq(body)); expr * lhs = to_app(body)->get_arg(0); expr * rhs = to_app(body)->get_arg(1); SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f)); diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp index 71bdac0a4..2f429ccf7 100644 --- a/src/ast/macros/macro_manager.cpp +++ b/src/ast/macros/macro_manager.cpp @@ -169,9 +169,8 @@ void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) { void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const { app * body = to_app(q->get_expr()); - SASSERT(m.is_eq(body) || m.is_iff(body)); - expr * lhs = to_app(body)->get_arg(0); - expr * rhs = to_app(body)->get_arg(1); + expr * lhs = nullptr, *rhs = nullptr; + VERIFY(m.is_eq(body, lhs, rhs)); SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d)); SASSERT(!is_app_of(lhs, d) || !is_app_of(rhs, d)); if (is_app_of(lhs, d)) { diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp index 77290c95f..b2f31e374 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -175,7 +175,7 @@ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const { */ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const { - if (m_manager.is_eq(n) || m_manager.is_iff(n)) { + if (m_manager.is_eq(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (is_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) && @@ -207,7 +207,7 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & he */ bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const { - if (m_manager.is_eq(n) || m_manager.is_iff(n)) { + if (m_manager.is_eq(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (is_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) && @@ -339,10 +339,9 @@ bool macro_util::is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";); expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); - if (!m_manager.is_iff(body)) + expr * lhs, *rhs; + if (!m_manager.is_iff(body, lhs, rhs)) return false; - expr * lhs = to_app(body)->get_arg(0); - expr * rhs = to_app(body)->get_arg(1); if (is_pseudo_head(lhs, num_decls, head, t) && !is_forbidden(head->get_decl()) && !occurs(head->get_decl(), rhs)) { diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp index a308b5b17..3a0735e25 100644 --- a/src/ast/macros/quasi_macros.cpp +++ b/src/ast/macros/quasi_macros.cpp @@ -158,7 +158,7 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { if (is_quantifier(e) && to_quantifier(e)->is_forall()) { quantifier * q = to_quantifier(e); expr * qe = q->get_expr(); - if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) { + if ((m_manager.is_eq(qe))) { expr * lhs = to_app(qe)->get_arg(0); expr * rhs = to_app(qe)->get_arg(1); diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 11461680d..c7f20ced6 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -582,7 +582,7 @@ struct nnf::imp { return true; } - bool is_eq(app * t) const { return m().is_eq(t) || m().is_iff(t); } + bool is_eq(app * t) const { return m().is_eq(t); } bool process_iff_xor(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); @@ -630,7 +630,7 @@ struct nnf::imp { } bool process_eq(app * t, frame & fr) { - if (m().is_bool(t->get_arg(0))) + if (m().is_iff(t)) return process_iff_xor(t, fr); else return process_default(t, fr); @@ -725,7 +725,6 @@ struct nnf::imp { return process_implies(t, fr); case OP_ITE: return process_ite(t, fr); - case OP_IFF: case OP_XOR: return process_iff_xor(t, fr); case OP_EQ: diff --git a/src/ast/proofs/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp index 0e16abd11..24841016c 100644 --- a/src/ast/proofs/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -12,7 +12,7 @@ Copyright (c) 2015 Microsoft Corporation #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/var_subst.h" -#define IS_EQUIV(_e_) (m.is_eq(_e_) || m.is_iff(_e_)) +#define IS_EQUIV(_e_) m.is_eq(_e_) #define SAME_OP(_d1_, _d2_) ((_d1_ == _d2_) || (IS_EQUIV(_d1_) && IS_EQUIV(_d2_))) @@ -1032,7 +1032,7 @@ bool proof_checker::match_and(expr const* e, expr_ref_vector& terms) const { } bool proof_checker::match_iff(expr const* e, expr_ref& t1, expr_ref& t2) const { - return match_op(e, OP_IFF, t1, t2); + return match_op(e, OP_EQ, t1, t2) && m.is_bool(t1); } bool proof_checker::match_equiv(expr const* e, expr_ref& t1, expr_ref& t2) const { @@ -1044,7 +1044,7 @@ bool proof_checker::match_implies(expr const* e, expr_ref& t1, expr_ref& t2) con } bool proof_checker::match_eq(expr const* e, expr_ref& t1, expr_ref& t2) const { - return match_op(e, OP_EQ, t1, t2) || match_iff(e, t1, t2); + return match_op(e, OP_EQ, t1, t2); } bool proof_checker::match_oeq(expr const* e, expr_ref& t1, expr_ref& t2) const { diff --git a/src/ast/proofs/proof_utils.h b/src/ast/proofs/proof_utils.h index 455f39c4f..729a30eb0 100644 --- a/src/ast/proofs/proof_utils.h +++ b/src/ast/proofs/proof_utils.h @@ -110,10 +110,10 @@ public: if (m.is_or(decl)) { mk_or_core(args, res); } - else if (m.is_iff(decl) && args.size() == 2) + else if (m.is_eq(decl) && args.size() == 2) // avoiding simplifying equalities. In particular, // we don't want (= (not a) (not b)) to be reduced to (= a b) - { res = m.mk_iff(args.get(0), args.get(1)); } + { res = m.mk_eq(args.get(0), args.get(1)); } else { brwr.mk_app(decl, args.size(), args.c_ptr(), res); } } diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 5cf25335b..46613a12e 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -39,7 +39,6 @@ br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * co SASSERT(f->get_family_id() == m().get_basic_family_id()); switch (f->get_decl_kind()) { case OP_EQ: - case OP_IFF: SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result); case OP_DISTINCT: @@ -428,7 +427,7 @@ bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, exp neg = true; t = to_app(t)->get_arg(0); } - if (m().is_iff(t) || m().is_eq(t)) { + if (m().is_eq(t)) { bool modified = false; expr * new_lhs = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified); expr * new_rhs = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified); @@ -708,7 +707,7 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { expr *la, *lb, *ra, *rb; // fold (iff (iff a b) (iff (not a) b)) to false - if (m().is_iff(lhs, la, lb) && m().is_iff(rhs, ra, rb)) { + if (m().is_eq(lhs, la, lb) && m().is_eq(rhs, ra, rb)) { expr *n; if ((la == ra && ((m().is_not(rb, n) && n == lb) || (m().is_not(lb, n) && n == rb))) || diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 34e03c1ed..83ece2aae 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -81,7 +81,7 @@ public: bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); } ast_manager & m() const { return m_manager; } family_id get_fid() const { return m().get_basic_family_id(); } - bool is_eq(expr * t) const { return m().is_eq(t) || m().is_iff(t); } + bool is_eq(expr * t) const { return m().is_eq(t); } bool flat() const { return m_flat; } void set_flat(bool f) { m_flat = f; } diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index 021943585..54409e9c2 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -40,11 +40,8 @@ static bool is_neg_var(ast_manager & m, expr * e, unsigned num_decls) { */ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { // (not (= VAR t)) and (not (iff VAR t)) cases - if (m_manager.is_not(e) && (m_manager.is_eq(to_app(e)->get_arg(0)) || m_manager.is_iff(to_app(e)->get_arg(0)))) { - app * eq = to_app(to_app(e)->get_arg(0)); - SASSERT(m_manager.is_eq(eq) || m_manager.is_iff(eq)); - expr * lhs = eq->get_arg(0); - expr * rhs = eq->get_arg(1); + expr *eq, * lhs, *rhs; + if (m_manager.is_not(e, eq) && m_manager.is_eq(eq, lhs, rhs)) { if (!is_var(lhs, num_decls) && !is_var(rhs, num_decls)) return false; if (!is_var(lhs, num_decls)) @@ -60,9 +57,7 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { return true; } // (iff VAR t) and (iff (not VAR) t) cases - else if (m_manager.is_iff(e)) { - expr * lhs = to_app(e)->get_arg(0); - expr * rhs = to_app(e)->get_arg(1); + else if (m_manager.is_eq(e, lhs, rhs) && m_manager.is_bool(lhs)) { // (iff VAR t) case if (is_var(lhs, num_decls) || is_var(rhs, num_decls)) { if (!is_var(lhs, num_decls)) diff --git a/src/ast/rewriter/quant_hoist.cpp b/src/ast/rewriter/quant_hoist.cpp index 3592f84cd..2f1116299 100644 --- a/src/ast/rewriter/quant_hoist.cpp +++ b/src/ast/rewriter/quant_hoist.cpp @@ -259,7 +259,7 @@ private: result = m.mk_ite(t1, tt2, tt3); } } - else if ((m.is_eq(fml, t1, t2) && m.is_bool(t1)) || m.is_iff(fml, t1, t2)) { + else if (m.is_eq(fml, t1, t2) && m.is_bool(t1)) { expr_ref tt1(m), tt2(m), ntt1(m), ntt2(m), nt1(m), nt2(m); pull_quantifier(t1, qt, vars, tt1, use_fresh, rewrite_ok); pull_quantifier(t2, qt, vars, tt2, use_fresh, rewrite_ok); diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index f5a0ff0f7..912df0fc9 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -187,7 +187,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { if (st != BR_FAILED) return st; } - if (k == OP_EQ || k == OP_IFF) { + if (k == OP_EQ) { SASSERT(num == 2); st = apply_tamagotchi(args[0], args[1], result); if (st != BR_FAILED) diff --git a/src/ast/static_features.cpp b/src/ast/static_features.cpp index c8a1adcbe..e3530b4b5 100644 --- a/src/ast/static_features.cpp +++ b/src/ast/static_features.cpp @@ -154,8 +154,10 @@ bool static_features::is_diff_atom(expr const * e) const { bool static_features::is_gate(expr const * e) const { if (is_basic_expr(e)) { switch (to_app(e)->get_decl_kind()) { - case OP_ITE: case OP_AND: case OP_OR: case OP_IFF: case OP_XOR: case OP_IMPLIES: + case OP_ITE: case OP_AND: case OP_OR: case OP_XOR: case OP_IMPLIES: return true; + case OP_EQ: + return m_manager.is_bool(e); } } return false; @@ -207,7 +209,7 @@ void static_features::update_core(expr * e) { case OP_OR: m_num_ors++; break; - case OP_IFF: + case OP_EQ: m_num_iffs++; break; } @@ -418,7 +420,7 @@ void static_features::process(expr * e, bool form_ctx, bool or_and_ctx, bool ite form_ctx_new = true; or_and_ctx_new = true; break; - case OP_IFF: + case OP_EQ: form_ctx_new = true; break; } diff --git a/src/model/model_implicant.cpp b/src/model/model_implicant.cpp index 3456c2746..0cdd80c11 100644 --- a/src/model/model_implicant.cpp +++ b/src/model/model_implicant.cpp @@ -172,7 +172,6 @@ void model_implicant::process_formula(app* e, ptr_vector& todo, ptr_vector case OP_FALSE: break; case OP_EQ: - case OP_IFF: if (args[0] == args[1]) { SASSERT(v); // no-op @@ -742,10 +741,6 @@ void model_implicant::eval_basic(app* e) { set_x(e); } break; - case OP_IFF: - VERIFY(m.is_iff(e, arg1, arg2)); - eval_eq(e, arg1, arg2); - break; case OP_ITE: VERIFY(m.is_ite(e, argCond, argThen, argElse)); if (is_true(argCond)) { diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp index 21317a07c..315765acc 100644 --- a/src/muz/base/rule_properties.cpp +++ b/src/muz/base/rule_properties.cpp @@ -146,12 +146,10 @@ void rule_properties::check_existential_tail() { else if (is_quantifier(e)) { tocheck.push_back(to_quantifier(e)->get_expr()); } - else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && - m.is_true(e1)) { + else if (m.is_eq(e, e1, e2) && m.is_true(e1)) { todo.push_back(e2); } - else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && - m.is_true(e2)) { + else if (m.is_eq(e, e1, e2) && m.is_true(e2)) { todo.push_back(e1); } else { diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index 77b79ba04..c2cd94ee0 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -830,7 +830,7 @@ namespace pdr { flatten_and(state(), conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(), *e1, *e2; - if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { + if (m.is_eq(e, e1, e2)) { if (m.is_value(e2)) { model.insert(e1, e2); } diff --git a/src/muz/rel/udoc_relation.cpp b/src/muz/rel/udoc_relation.cpp index 2e9ab693c..ea292a29e 100644 --- a/src/muz/rel/udoc_relation.cpp +++ b/src/muz/rel/udoc_relation.cpp @@ -869,7 +869,7 @@ namespace datalog { dm.set(*d, idx, BIT_1); result.intersect(dm, *d); } - else if ((m.is_eq(g, e1, e2) || m.is_iff(g, e1, e2)) && m.is_bool(e1)) { + else if (m.is_iff(g, e1, e2)) { udoc diff1, diff2; diff1.push_back(dm.allocateX()); diff2.push_back(dm.allocateX()); diff --git a/src/muz/spacer/spacer_legacy_mev.cpp b/src/muz/spacer/spacer_legacy_mev.cpp index fc3eabc56..5feaed1fa 100644 --- a/src/muz/spacer/spacer_legacy_mev.cpp +++ b/src/muz/spacer/spacer_legacy_mev.cpp @@ -138,7 +138,6 @@ void model_evaluator::process_formula(app* e, ptr_vector& todo, ptr_vector case OP_FALSE: break; case OP_EQ: - case OP_IFF: if (args[0] == args[1]) { SASSERT(v); // no-op @@ -634,10 +633,6 @@ void model_evaluator::eval_basic(app* e) set_x(e); } break; - case OP_IFF: - VERIFY(m.is_iff(e, arg1, arg2)); - eval_eq(e, arg1, arg2); - break; case OP_XOR: VERIFY(m.is_xor(e, arg1, arg2)); eval_eq(e, arg1, arg2); diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index cb11afc9f..b4e5e7710 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -403,27 +403,26 @@ namespace spacer { public: test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - + void test_for_utvpi() { m_test_for_utvpi = true; } - - void operator()(expr* e) - { + + void operator()(expr* e) { if (!m_is_dl) { return; } if (a.is_le(e) || a.is_ge(e)) { m_is_dl = test_ineq(e); - } else if (m.is_eq(e)) { + } else if (m.is_eq(e)) { m_is_dl = test_eq(e); - } else if (is_non_arith_or_basic(e)) { + } else if (is_non_arith_or_basic(e)) { m_is_dl = false; - } else if (is_app(e)) { + } else if (is_app(e)) { app* a = to_app(e); for (unsigned i = 0; m_is_dl && i < a->get_num_args(); ++i) { m_is_dl = test_term(a->get_arg(i)); } } - + if (!m_is_dl) { char const* msg = "non-diff: "; if (m_test_for_utvpi) { @@ -432,12 +431,11 @@ namespace spacer { IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } - + bool is_dl() const { return m_is_dl; } }; - -bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) -{ + + bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); expr_fast_mark1 mark; for (unsigned i = 0; i < num_fmls; ++i) { @@ -445,9 +443,8 @@ bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) } return test.is_dl(); } - -bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) -{ + + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); test.test_for_utvpi(); expr_fast_mark1 mark; @@ -458,14 +455,13 @@ bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) } - void subst_vars (ast_manager& m, app_ref_vector const& vars, - model* M, expr_ref& fml) -{ + void subst_vars(ast_manager& m, + app_ref_vector const& vars, + model* M, expr_ref& fml) { expr_safe_replace sub (m); model_evaluator_util mev (m); mev.set_model(*M); - for (unsigned i = 0; i < vars.size (); i++) { - app* v = vars.get (i); + for (app * v : vars) { expr_ref val (m); VERIFY(mev.eval (v, val, true)); sub.insert (v, val); @@ -477,30 +473,23 @@ bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) * eliminate simple equalities using qe_lite * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays */ -void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, const model_ref& M, bool reduce_all_selects, bool use_native_mbp, - bool dont_sub) -{ + bool dont_sub) { th_rewriter rw (m); TRACE ("spacer_mbp", - tout << "Before projection:\n"; - tout << mk_pp (fml, m) << "\n"; - tout << "Vars:\n"; - for (unsigned i = 0; i < vars.size(); ++i) { - tout << mk_pp(vars.get (i), m) << "\n"; - } - ); + tout << "Before projection:\n"; + tout << mk_pp (fml, m) << "\n"; + tout << "Vars:\n"; + for (app* v : vars) tout << mk_pp(v, m) << "\n";); { - // Ensure that top-level AND of fml is flat - expr_ref_vector flat(m); - flatten_and (fml, flat); - if (flat.size () == 1) - { fml = flat.get(0); } - else if (flat.size () > 1) - { fml = m.mk_and(flat.size(), flat.c_ptr()); } + // Ensure that top-level AND of fml is flat + expr_ref_vector flat(m); + flatten_and (fml, flat); + fml = mk_and(flat); } - + app_ref_vector arith_vars (m); app_ref_vector array_vars (m); array_util arr_u (m); @@ -511,77 +500,72 @@ void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, while (true) { params_ref p; qe_lite qe(m, p, false); - qe (vars, fml); - rw (fml); + qe (vars, fml); + rw (fml); + + TRACE ("spacer_mbp", + tout << "After qe_lite:\n"; + tout << mk_pp (fml, m) << "\n"; + tout << "Vars:\n"; + for (unsigned i = 0; i < vars.size(); ++i) { + tout << mk_pp(vars.get (i), m) << "\n"; + } + ); + SASSERT (!m.is_false (fml)); - TRACE ("spacer_mbp", - tout << "After qe_lite:\n"; - tout << mk_pp (fml, m) << "\n"; - tout << "Vars:\n"; - for (unsigned i = 0; i < vars.size(); ++i) { - tout << mk_pp(vars.get (i), m) << "\n"; - } - ); - SASSERT (!m.is_false (fml)); - - bool has_bool_vars = false; - - // sort out vars into bools, arith (int/real), and arrays - for (unsigned i = 0; i < vars.size (); i++) { - if (m.is_bool (vars.get (i))) { - // obtain the interpretation of the ith var using model completion - VERIFY (M->eval (vars.get (i), bval, true)); - bool_sub.insert (vars.get (i), bval); - has_bool_vars = true; + bool has_bool_vars = false; + + // sort out vars into bools, arith (int/real), and arrays + for (unsigned i = 0; i < vars.size (); i++) { + if (m.is_bool (vars.get (i))) { + // obtain the interpretation of the ith var using model completion + VERIFY (M->eval (vars.get (i), bval, true)); + bool_sub.insert (vars.get (i), bval); + has_bool_vars = true; } else if (arr_u.is_array(vars.get(i))) { - array_vars.push_back (vars.get (i)); + array_vars.push_back (vars.get (i)); } else { - SASSERT (ari_u.is_int (vars.get (i)) || ari_u.is_real (vars.get (i))); - arith_vars.push_back (vars.get (i)); - } + SASSERT (ari_u.is_int (vars.get (i)) || ari_u.is_real (vars.get (i))); + arith_vars.push_back (vars.get (i)); } - - // substitute Booleans - if (has_bool_vars) { - bool_sub (fml); - // -- bool_sub is not simplifying - rw (fml); - SASSERT (!m.is_false (fml)); - TRACE ("spacer_mbp", - tout << "Projected Booleans:\n" << mk_pp (fml, m) << "\n"; - ); - bool_sub.reset (); - } - + } + + // substitute Booleans + if (has_bool_vars) { + bool_sub (fml); + // -- bool_sub is not simplifying + rw (fml); + SASSERT (!m.is_false (fml)); TRACE ("spacer_mbp", - tout << "Array vars:\n"; - for (unsigned i = 0; i < array_vars.size (); ++i) { - tout << mk_pp (array_vars.get (i), m) << "\n"; - } - ); - - vars.reset (); - - // project arrays - { - scoped_no_proof _sp (m); - // -- local rewriter that is aware of current proof mode - th_rewriter srw(m); - qe::array_project (*M.get (), array_vars, fml, vars, reduce_all_selects); - SASSERT (array_vars.empty ()); - srw (fml); - SASSERT (!m.is_false (fml)); - } - - TRACE ("spacer_mbp", - tout << "extended model:\n"; - model_pp (tout, *M); - tout << "Auxiliary variables of index and value sorts:\n"; - for (unsigned i = 0; i < vars.size (); i++) { - tout << mk_pp (vars.get (i), m) << "\n"; - } - ); - + tout << "Projected Booleans:\n" << mk_pp (fml, m) << "\n"; + ); + bool_sub.reset (); + } + + TRACE ("spacer_mbp", + tout << "Array vars:\n"; + tout << array_vars;); + + vars.reset (); + + // project arrays + { + scoped_no_proof _sp (m); + // -- local rewriter that is aware of current proof mode + th_rewriter srw(m); + qe::array_project (*M.get (), array_vars, fml, vars, reduce_all_selects); + SASSERT (array_vars.empty ()); + srw (fml); + SASSERT (!m.is_false (fml)); + } + + TRACE ("spacer_mbp", + tout << "extended model:\n"; + model_pp (tout, *M); + tout << "Auxiliary variables of index and value sorts:\n"; + tout << vars; + ); + if (vars.empty()) { break; } } diff --git a/src/muz/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 6894cf4fa..34e739bc3 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -42,7 +42,7 @@ namespace datalog { } bool mk_array_blast::is_store_def(expr* e, expr*& x, expr*& y) { - if (m.is_iff(e, x, y) || m.is_eq(e, x, y)) { + if (m.is_eq(e, x, y)) { if (!a.is_store(y)) { std::swap(x,y); } diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index 4cbcc5712..9f6302e05 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -180,7 +180,7 @@ namespace datalog { } m_terms[n] = e; visited.mark(e); - if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { + if (m.is_eq(e, e1, e2)) { m_uf.merge(e1->get_id(), e2->get_id()); } if (is_app(e)) { diff --git a/src/nlsat/tactic/goal2nlsat.cpp b/src/nlsat/tactic/goal2nlsat.cpp index 133652c1d..42ae36564 100644 --- a/src/nlsat/tactic/goal2nlsat.cpp +++ b/src/nlsat/tactic/goal2nlsat.cpp @@ -198,7 +198,6 @@ struct goal2nlsat::imp { throw tactic_exception("apply simplify before applying nlsat"); case OP_AND: case OP_OR: - case OP_IFF: case OP_XOR: case OP_NOT: case OP_IMPLIES: diff --git a/src/parsers/util/cost_parser.cpp b/src/parsers/util/cost_parser.cpp index 765b8ade9..91362b37a 100644 --- a/src/parsers/util/cost_parser.cpp +++ b/src/parsers/util/cost_parser.cpp @@ -32,7 +32,7 @@ cost_parser::cost_parser(ast_manager & m): add_builtin_op("or", fid, OP_OR); add_builtin_op("ite", fid, OP_ITE); add_builtin_op("=", fid, OP_EQ); - add_builtin_op("iff", fid, OP_IFF); + add_builtin_op("iff", fid, OP_EQ); add_builtin_op("xor", fid, OP_XOR); fid = m_util.get_family_id(); diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index daed18f7a..3916e547c 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -614,7 +614,7 @@ namespace qe { else if (m.is_ite(a)) { nnf_ite(a, p); } - else if (m.is_iff(a) || (m.is_eq(a) && m.is_bool(a->get_arg(0)))) { + else if (m.is_iff(a)) { nnf_iff(a, p); } else if (m.is_xor(a)) { diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 6ecdfd835..2a14aa6f2 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -345,7 +345,7 @@ namespace eq { var* v; // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases - if (m.is_eq(e, lhs, rhs) || m.is_iff(e, lhs, rhs)) { + if (m.is_eq(e, lhs, rhs)) { // (iff (not VAR) t) (iff t (not VAR)) cases if (!is_variable(lhs) && !is_variable(rhs) && m.is_bool(lhs)) { if (!is_neg_var(m, lhs, v)) { diff --git a/src/sat/tactic/atom2bool_var.cpp b/src/sat/tactic/atom2bool_var.cpp index e3c9b6767..b79eaa251 100644 --- a/src/sat/tactic/atom2bool_var.cpp +++ b/src/sat/tactic/atom2bool_var.cpp @@ -75,7 +75,7 @@ struct collect_boolean_interface_proc { continue; if (is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0) { decl_kind k = to_app(t)->get_decl_kind(); - if (k == OP_OR || k == OP_NOT || k == OP_IFF || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) { + if (k == OP_OR || k == OP_NOT || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index ea9c5b4ea..75290946a 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -201,7 +201,6 @@ struct goal2sat::imp { case OP_NOT: case OP_OR: case OP_AND: - case OP_IFF: m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; case OP_ITE: @@ -630,7 +629,6 @@ struct goal2sat::imp { case OP_ITE: convert_ite(t, root, sign); break; - case OP_IFF: case OP_EQ: convert_iff(t, root, sign); break; diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index c00fb93b1..c7ae7605f 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -500,8 +500,7 @@ unsigned asserted_formulas::propagate_values(unsigned i) { void asserted_formulas::update_substitution(expr* n, proof* pr) { expr* lhs, *rhs, *n1; - proof_ref pr1(m); - if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) { + if (is_ground(n) && m.is_eq(n, lhs, rhs)) { compute_depth(lhs); compute_depth(rhs); if (is_gt(lhs, rhs)) { @@ -511,12 +510,12 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { } if (is_gt(rhs, lhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); - pr1 = m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr; - m_scoped_substitution.insert(rhs, lhs, pr1); + m_scoped_substitution.insert(rhs, lhs, m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr); return; } TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); } + proof_ref pr1(m); if (m.is_not(n, n1)) { pr1 = m.proofs_enabled() ? m.mk_iff_false(pr) : nullptr; m_scoped_substitution.insert(n1, m.mk_false(), pr1); diff --git a/src/smt/cost_evaluator.cpp b/src/smt/cost_evaluator.cpp index 94151f2b3..0719d0961 100644 --- a/src/smt/cost_evaluator.cpp +++ b/src/smt/cost_evaluator.cpp @@ -47,8 +47,7 @@ float cost_evaluator::eval(expr * f) const { return 1.0f; return 0.0f; case OP_ITE: return E(0) != 0.0f ? E(1) : E(2); - case OP_EQ: - case OP_IFF: return E(0) == E(1) ? 1.0f : 0.0f; + case OP_EQ: return E(0) == E(1) ? 1.0f : 0.0f; case OP_XOR: return E(0) != E(1) ? 1.0f : 0.0f; case OP_IMPLIES: if (E(0) == 0.0f) diff --git a/src/smt/expr_context_simplifier.cpp b/src/smt/expr_context_simplifier.cpp index bce28420d..a57b0299f 100644 --- a/src/smt/expr_context_simplifier.cpp +++ b/src/smt/expr_context_simplifier.cpp @@ -110,13 +110,15 @@ void expr_context_simplifier::reduce_rec(app * a, expr_ref & result) { case OP_OR: reduce_or(a->get_num_args(), a->get_args(), result); return; - case OP_IFF: { - expr_ref tmp1(m_manager), tmp2(m_manager); - reduce_rec(a->get_arg(0), tmp1); - reduce_rec(a->get_arg(1), tmp2); - m_simp.mk_iff(tmp1.get(), tmp2.get(), result); - return; - } + case OP_EQ: + if (m_manager.is_iff(a)) { + expr_ref tmp1(m_manager), tmp2(m_manager); + reduce_rec(a->get_arg(0), tmp1); + reduce_rec(a->get_arg(1), tmp2); + m_simp.mk_iff(tmp1.get(), tmp2.get(), result); + return; + } + break; case OP_XOR: { expr_ref tmp1(m_manager), tmp2(m_manager); reduce_rec(a->get_arg(0), tmp1); @@ -580,7 +582,7 @@ void expr_strong_context_simplifier::simplify_model_based(expr* fml, expr_ref& r } assignment_map.insert(a, value); } - else if (m.is_iff(a, n1, n2) || m.is_eq(a, n1, n2)) { + else if (m.is_eq(a, n1, n2)) { lbool v1 = assignment_map.find(n1); lbool v2 = assignment_map.find(n2); if (v1 == l_undef || v2 == l_undef) { diff --git a/src/smt/smt_checker.cpp b/src/smt/smt_checker.cpp index ed80eaab7..1b6a5d370 100644 --- a/src/smt/smt_checker.cpp +++ b/src/smt/smt_checker.cpp @@ -61,8 +61,20 @@ namespace smt { return is_true ? any_arg(a, true) : all_args(a, false); case OP_AND: return is_true ? all_args(a, true) : any_arg(a, false); - case OP_IFF: - if (is_true) { + case OP_EQ: + if (!m_manager.is_iff(a)) { + enode * lhs = get_enode_eq_to(a->get_arg(0)); + enode * rhs = get_enode_eq_to(a->get_arg(1)); + if (lhs && rhs && m_context.is_relevant(lhs) && m_context.is_relevant(rhs)) { + if (is_true && lhs->get_root() == rhs->get_root()) + return true; + // if (!is_true && m_context.is_ext_diseq(lhs, rhs, 2)) + if (!is_true && m_context.is_diseq(lhs, rhs)) + return true; + } + return false; + } + else if (is_true) { return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || @@ -86,18 +98,6 @@ namespace smt { } return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true); } - case OP_EQ: { - enode * lhs = get_enode_eq_to(a->get_arg(0)); - enode * rhs = get_enode_eq_to(a->get_arg(1)); - if (lhs && rhs && m_context.is_relevant(lhs) && m_context.is_relevant(rhs)) { - if (is_true && lhs->get_root() == rhs->get_root()) - return true; - // if (!is_true && m_context.is_ext_diseq(lhs, rhs, 2)) - if (!is_true && m_context.is_diseq(lhs, rhs)) - return true; - } - return false; - } default: break; } diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 379846ed7..7984fd5f0 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -771,7 +771,7 @@ namespace smt { app * fact = to_app(m_manager.get_fact(pr)); app * n1_owner = n1->get_owner(); app * n2_owner = n2->get_owner(); - bool is_eq = m_manager.is_eq(fact) || m_manager.is_iff(fact); + bool is_eq = m_manager.is_eq(fact); if (!is_eq || (fact->get_arg(0) != n2_owner && fact->get_arg(1) != n2_owner)) { CTRACE("norm_eq_proof_bug", !m_ctx.is_true(n2) && !m_ctx.is_false(n2), tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << "\n"; @@ -794,7 +794,7 @@ namespace smt { TRACE("norm_eq_proof", tout << "#" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n"; tout << mk_ll_pp(pr, m_manager, true, false);); - SASSERT(m_manager.is_eq(fact) || m_manager.is_iff(fact)); + SASSERT(m_manager.is_eq(fact)); SASSERT((fact->get_arg(0) == n1->get_owner() && fact->get_arg(1) == n2->get_owner()) || (fact->get_arg(1) == n1->get_owner() && fact->get_arg(0) == n2->get_owner())); if (fact->get_arg(0) == n1_owner && fact->get_arg(1) == n2_owner) diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index dc7c32cc1..f9ee900ff 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -34,9 +34,10 @@ namespace smt { switch (to_app(n)->get_decl_kind()) { case OP_AND: case OP_OR: - case OP_IFF: case OP_ITE: return true; + case OP_EQ: + return m.is_bool(to_app(n)->get_arg(0)); default: return false; } @@ -229,7 +230,7 @@ namespace smt { add_or_rel_watches(to_app(n)); break; } - case OP_IFF: { + case OP_EQ: { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); internalize(lhs, true); @@ -381,7 +382,7 @@ namespace smt { return; } - if (m_manager.is_eq(n)) + if (m_manager.is_eq(n) && !m_manager.is_iff(n)) internalize_eq(to_app(n), gate_ctx); else if (m_manager.is_distinct(n)) internalize_distinct(to_app(n), gate_ctx); @@ -538,9 +539,7 @@ namespace smt { bool _is_gate = is_gate(m_manager, n) || m_manager.is_not(n); // process args - unsigned num = n->get_num_args(); - for (unsigned i = 0; i < num; i++) { - expr * arg = n->get_arg(i); + for (expr * arg : *n) { internalize(arg, _is_gate); } @@ -596,8 +595,9 @@ namespace smt { mk_or_cnstr(to_app(n)); add_or_rel_watches(to_app(n)); break; - case OP_IFF: - mk_iff_cnstr(to_app(n)); + case OP_EQ: + if (m_manager.is_iff(n)) + mk_iff_cnstr(to_app(n)); break; case OP_ITE: mk_ite_cnstr(to_app(n)); diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 5e21c0dcc..4307d3fdf 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -2315,9 +2315,6 @@ namespace smt { case OP_ITE: process_ite(to_app(curr), pol); break; - case OP_IFF: - process_iff(to_app(curr)); - break; case OP_EQ: if (m_manager.is_bool(to_app(curr)->get_arg(0))) { process_iff(to_app(curr)); diff --git a/src/smt/smt_quantifier_stat.cpp b/src/smt/smt_quantifier_stat.cpp index 7d73ea27c..8c7fd196e 100644 --- a/src/smt/smt_quantifier_stat.cpp +++ b/src/smt/smt_quantifier_stat.cpp @@ -82,11 +82,13 @@ namespace smt { if (depth > 0) m_case_split_factor *= (num_args + 1); break; - case OP_IFF: - if (depth == 0) - m_case_split_factor *= 4; - else - m_case_split_factor *= 9; + case OP_EQ: + if (m_manager.is_iff(n)) { + if (depth == 0) + m_case_split_factor *= 4; + else + m_case_split_factor *= 9; + } break; case OP_ITE: if (depth == 0) diff --git a/src/smt/smt_quick_checker.cpp b/src/smt/smt_quick_checker.cpp index 64c791a0e..72a720d98 100644 --- a/src/smt/smt_quick_checker.cpp +++ b/src/smt/smt_quick_checker.cpp @@ -311,11 +311,6 @@ namespace smt { return is_true ? any_arg(a, true) : all_args(a, false); case OP_AND: return is_true ? all_args(a, true) : any_arg(a, false); - case OP_IFF: - if (is_true) - return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false)); - else - return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true)); case OP_ITE: if (check(a->get_arg(0), true)) return check(a->get_arg(1), is_true); @@ -324,6 +319,13 @@ namespace smt { else return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true); case OP_EQ: + if (m_manager.is_iff(a)) { + if (is_true) + return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false)); + else + return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true)); + } + if (is_true) { return canonize(a->get_arg(0)) == canonize(a->get_arg(1)); } diff --git a/src/tactic/aig/aig.cpp b/src/tactic/aig/aig.cpp index 89196808f..faedf0e0c 100644 --- a/src/tactic/aig/aig.cpp +++ b/src/tactic/aig/aig.cpp @@ -490,7 +490,6 @@ struct aig_manager::imp { case OP_NOT: case OP_OR: case OP_AND: - case OP_IFF: case OP_XOR: case OP_IMPLIES: case OP_ITE: @@ -582,9 +581,6 @@ struct aig_manager::imp { SASSERT(m.m().is_bool(fr.m_t->get_arg(0))); mk_iff(fr.m_spos); break; - case OP_IFF: - mk_iff(fr.m_spos); - break; case OP_XOR: mk_xor(fr.m_spos); break; diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index 2b3cb8414..1b435791c 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -87,7 +87,6 @@ struct cofactor_elim_term_ite::imp { case OP_TRUE: case OP_FALSE: case OP_ITE: - case OP_IFF: return; case OP_EQ: case OP_DISTINCT: diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 43da3bc00..27a5bdc94 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -486,7 +486,7 @@ bool expr_substitution_simplifier::is_gt(expr* lhs, expr* rhs) { void expr_substitution_simplifier::update_substitution(expr* n, proof* pr) { expr* lhs, *rhs, *n1; - if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) { + if (is_ground(n) && m.is_eq(n, lhs, rhs)) { compute_depth(lhs); compute_depth(rhs); m_trail.push_back(lhs); diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index ce77f89d9..577db30cd 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -331,7 +331,6 @@ class elim_uncnstr_tactic : public tactic { return r; } return nullptr; - case OP_IFF: case OP_EQ: SASSERT(num == 2); return process_eq(f, args[0], args[1]); diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index acd75d663..f579b7b4f 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -344,10 +344,7 @@ class solve_eqs_tactic : public tactic { } return false; } - - if (m().is_iff(f)) - return trivial_solve(to_app(f)->get_arg(0), to_app(f)->get_arg(1), var, def, pr); - + #if 0 if (not_bool_eq(f, var, def, pr)) return true; diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index 2abaae0e6..9c57fe791 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -176,7 +176,6 @@ class tseitin_cnf_tactic : public tactic { sign = !sign; goto start; case OP_OR: - case OP_IFF: l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); @@ -223,7 +222,6 @@ class tseitin_cnf_tactic : public tactic { goto start; } case OP_OR: - case OP_IFF: visited = false; push_frame(to_app(n)); return; From 689414d0552a58e5bec84af4c3396eb89f79de6b Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 16 May 2018 21:15:40 -0700 Subject: [PATCH 099/364] Fix debug printing in iuc_solver --- src/muz/spacer/spacer_iuc_solver.cpp | 3 ++- src/muz/spacer/spacer_prop_solver.cpp | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 7c159746f..a01f95c71 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -284,7 +284,8 @@ void iuc_solver::get_iuc(expr_ref_vector &core) // -- old hypothesis reducer while the new one is broken if (m_old_hyp_reducer) { - // preprocess proof in order to get a proof which is better suited for unsat-core-extraction + // preprocess proof in order to get a proof which is + // better suited for unsat-core-extraction if (m_print_farkas_stats) { iuc_proof iuc_before(m, res.get(), B); diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 39db6129c..87f998486 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -60,12 +60,16 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_solvers[1] = pm.mk_fresh2(); m_fparams[1] = &pm.fparams2(); - m_contexts[0] = alloc(spacer::iuc_solver, *(m_solvers[0]), p.spacer_iuc(), + m_contexts[0] = alloc(spacer::iuc_solver, *(m_solvers[0]), + p.spacer_iuc(), p.spacer_iuc_arith(), + p.spacer_iuc_print_farkas_stats(), p.spacer_iuc_old_hyp_reducer(), p.spacer_iuc_split_farkas_literals()); - m_contexts[1] = alloc(spacer::iuc_solver, *(m_solvers[1]), p.spacer_iuc(), + m_contexts[1] = alloc(spacer::iuc_solver, *(m_solvers[1]), + p.spacer_iuc(), p.spacer_iuc_arith(), + p.spacer_iuc_print_farkas_stats(), p.spacer_iuc_old_hyp_reducer(), p.spacer_iuc_split_farkas_literals()); From 9d4784baf63b5d9e6b691b65f1c2b78d67f1b659 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 08:51:33 -0700 Subject: [PATCH 100/364] Fix dealloc order in hypotheses_reducer::reset() --- src/muz/spacer/spacer_proof_utils.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 42cdcf50b..8ed414c2b 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -174,14 +174,15 @@ proof_ref theory_axiom_reducer::reduce(proof* pr) { } void hypothesis_reducer::reset() { - m_cache.reset(); - m_units.reset(); - m_active_hyps.reset(); m_parent_hyps.reset(); - for (auto t : m_pinned_active_hyps) dealloc(t); - m_pinned_active_hyps.reset(); + m_active_hyps.reset(); + m_units.reset(); + m_cache.reset(); for (auto t : m_pinned_parent_hyps) dealloc(t); m_pinned_parent_hyps.reset(); + for (auto t : m_pinned_active_hyps) dealloc(t); + m_pinned_active_hyps.reset(); + m_pinned.reset(); } void hypothesis_reducer::compute_hypsets(proof *pr) { From fd13eb9e0ea8da1e8ed3eafac9de3472fc7876b1 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 09:19:05 -0700 Subject: [PATCH 101/364] Final cleanup of hypothesis_reducer --- src/muz/spacer/spacer_proof_utils.cpp | 85 +++++++++++++++------------ src/muz/spacer/spacer_proof_utils.h | 15 +++-- 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 8ed414c2b..e8f56f200 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -173,6 +173,24 @@ proof_ref theory_axiom_reducer::reduce(proof* pr) { return proof_ref(res, m); } +/* ------------------------------------------------------------------------- */ +/* hypothesis_reducer */ +/* ------------------------------------------------------------------------- */ + +proof_ref hypothesis_reducer::reduce(proof* pr) { + compute_hypsets(pr); + collect_units(pr); + + proof_ref res(reduce_core(pr), m); + SASSERT(res); + reset(); + + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side));); + return res; +} + void hypothesis_reducer::reset() { m_parent_hyps.reset(); m_active_hyps.reset(); @@ -198,7 +216,7 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { continue; } - bool dirty = false; + unsigned todo_sz = todo.size(); for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { SASSERT(m.is_proof(p->get_arg(i))); proof *parent = to_app(p->get_arg(i)); @@ -206,10 +224,9 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { if (!m_active_hyps.contains(parent)) { SASSERT(!m_parent_hyps.contains(parent)); todo.push_back(parent); - dirty = true; } } - if (dirty) continue; + if (todo.size() > todo_sz) continue; todo.pop_back(); @@ -262,21 +279,10 @@ void hypothesis_reducer::collect_units(proof* pr) { } } -proof_ref hypothesis_reducer::reduce(proof* pr) { - compute_hypsets(pr); - collect_units(pr); - proof_ref res(compute_transformed_proof(pr), m); - SASSERT(res); - reset(); +proof* hypothesis_reducer::reduce_core(proof* pf) { + SASSERT(m.is_false(m.get_fact(pf))); - DEBUG_CODE(proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(res, side));); - return res; -} - -proof* hypothesis_reducer::compute_transformed_proof(proof* pf) { proof *res = NULL; ptr_vector todo; @@ -284,7 +290,7 @@ proof* hypothesis_reducer::compute_transformed_proof(proof* pf) { ptr_buffer args; bool dirty = false; - while (!todo.empty()) { + while (true) { proof *p, *tmp, *pp; unsigned todo_sz; @@ -311,58 +317,59 @@ proof* hypothesis_reducer::compute_transformed_proof(proof* pf) { todo.pop_back(); - // transform the proof + // transform the current proof node - // INV: whenever p is visited, active_hyps and parent_hyps - // have already been computed for everything in args. if (m.is_hypothesis(p)) { - // hyp: replace by a corresponding unit + // if possible, replace a hypothesis by a unit derivation if (m_units.find(m.get_fact(p), tmp)) { - // look up the proof of the unit: - // if there is a transformed proof use that one - // otherwise use the original proof + // use already transformed proof of the unit if it is available proof* proof_of_unit; if (!m_cache.find(tmp, proof_of_unit)) { proof_of_unit = tmp; } - // compute hypsets (have not been computed in general, - // since the unit can be anywhere in the proof) + // make sure hypsets for the unit are computed + // AG: is this needed? compute_hypsets(proof_of_unit); + SASSERT(m_parent_hyps.contains(proof_of_unit)); // if the transformation doesn't create a cycle, perform it - SASSERT(m_parent_hyps.contains(proof_of_unit)); expr_set* parent_hyps = m_parent_hyps.find(proof_of_unit); - if (!parent_hyps->contains(p)) - // hypsets have already been computed for proof_of_unit + if (!parent_hyps->contains(p)) { res = proof_of_unit; - // otherwise don't transform the proof and just use - // the hypothesis - else - // hypsets have already been computed for p + } + else { + // -- failed to transform the proof, perhaps bad + // -- choice of the proof of unit res = p; + } } - else - // hypsets have already been computed for p + else { + // -- no unit found to replace the hypothesis res = p; + } } - else if (!dirty) - res = p; + + else if (!dirty) {res = p;} + else if (m.is_lemma(p)) { - //lemma: reduce the premise; remove reduced consequences - //from conclusion + // lemma: reduce the premise; remove reduced consequences + // from conclusion SASSERT(args.size() == 1); res = mk_lemma_core(args[0], m.get_fact(p)); + // -- re-compute hypsets compute_hypsets(res); } else if (m.is_unit_resolution(p)) { // unit: reduce untis; reduce the first premise; rebuild // unit resolution res = mk_unit_resolution_core(args); + // -- re-compute hypsets compute_hypsets(res); } else { res = mk_proof_core(p, args); + // -- re-compute hypsets compute_hypsets(res); } diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index f348e71a6..2e1129896 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -76,19 +76,24 @@ private: // maps a unit literal to its derivation obj_map m_units; - // maps a proof to the set of proofs of active hypotheses + // maps a proof node to the set of its active (i.e., in scope) hypotheses obj_map m_active_hyps; - // maps a proof to the hypothesis-fact that are transitive - // parents of that proof. Used for cycle detection and avoidance. + + // maps a proof node to the set of all hypothesis-facts (active or + // not) that can reach it. Used for cycle detection and avoidance + // during proof transformation obj_map m_parent_hyps; void reset(); - // compute active_hyps and parent_hyps for pr + // compute active_hyps and parent_hyps for a given proof node and + // all its ancestors void compute_hypsets(proof* pr); // compute m_units void collect_units(proof* pr); - proof* compute_transformed_proof(proof* pf); + + // -- rewrite proof to reduce number of hypotheses used + proof* reduce_core(proof* pf); proof* mk_lemma_core(proof *pf, expr *fact); proof* mk_unit_resolution_core(ptr_buffer& args); From d379b14942e52c4ec4ab46919ccb577053e10c83 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 10:15:51 -0700 Subject: [PATCH 102/364] Cleanup spacer_iuc_solver --- src/muz/spacer/spacer_iuc_solver.cpp | 124 +++++++++++++-------------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index a01f95c71..d924f36d7 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -255,61 +255,58 @@ void iuc_solver::get_iuc(expr_ref_vector &core) scoped_watch _t_ (m_iuc_watch); typedef obj_hashtable expr_set; - expr_set B; + expr_set core_lits; for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) { expr *a = m_assumptions.get (i); app_ref def(m); - if (is_proxy(a, def)) { B.insert(def.get()); } - B.insert (a); + if (is_proxy(a, def)) { core_lits.insert(def.get()); } + core_lits.insert (a); } - if (m_iuc == 0) - { + if (m_iuc == 0) { // ORIGINAL PDR CODE + // AG: deprecated proof_ref pr(m); pr = get_proof (); farkas_learner learner_old; learner_old.set_split_literals(m_split_literals); - learner_old.get_lemmas (pr, B, core); + learner_old.get_lemmas (pr, core_lits, core); elim_proxies (core); simplify_bounds (core); // XXX potentially redundant } - else - { + else { // NEW IUC proof_ref res(get_proof(), m); // -- old hypothesis reducer while the new one is broken - if (m_old_hyp_reducer) - { - // preprocess proof in order to get a proof which is + if (m_old_hyp_reducer) { + // AG: deprecated + // pre-process proof in order to get a proof which is // better suited for unsat-core-extraction - if (m_print_farkas_stats) - { - iuc_proof iuc_before(m, res.get(), B); - verbose_stream() << "\nStats before transformation:"; + if (m_print_farkas_stats) { + iuc_proof iuc_before(m, res.get(), core_lits); + verbose_stream() << "\nOld reduce_hypotheses. Before:"; iuc_before.dump_farkas_stats(); } proof_utils::reduce_hypotheses(res); proof_utils::permute_unit_resolution(res); - if (m_print_farkas_stats) - { - iuc_proof iuc_after(m, res.get(), B); - verbose_stream() << "Stats after transformation:"; + if (m_print_farkas_stats) { + iuc_proof iuc_after(m, res.get(), core_lits); + verbose_stream() << "Old reduce_hypothesis. After:"; iuc_after.dump_farkas_stats(); } } - else // -- new hypothesis reducer + // -- new hypothesis reducer + else { - // preprocess proof in order to get a proof which is better suited for unsat-core-extraction - if (m_print_farkas_stats) - { - iuc_proof iuc_before(m, res.get(), B); - verbose_stream() << "\nStats before transformation:"; + // pre-process proof for better iuc extraction + if (m_print_farkas_stats) { + iuc_proof iuc_before(m, res.get(), core_lits); + verbose_stream() << "\n New hypothesis_reducer. Before:"; iuc_before.dump_farkas_stats(); } @@ -321,68 +318,67 @@ void iuc_solver::get_iuc(expr_ref_vector &core) res = pr2; - if (m_print_farkas_stats) - { - iuc_proof iuc_after(m, res.get(), B); - verbose_stream() << "Stats after transformation:"; + if (m_print_farkas_stats) { + iuc_proof iuc_after(m, res.get(), core_lits); + verbose_stream() << "New hypothesis_reducer. After:"; iuc_after.dump_farkas_stats(); } } - // construct proof object with contains partition information - iuc_proof iuc_pf(m, res, B); + iuc_proof iuc_pf(m, res, core_lits); - // configure learner unsat_core_learner learner(m, iuc_pf); - if (m_iuc_arith == 0 || m_iuc_arith > 3) - { - unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, - learner, m_split_literals, - false /* use constants from A */); - learner.register_plugin(plugin_farkas_lemma); + // -- register iuc plugins + if (m_iuc_arith == 0 || m_iuc_arith == 1) { + unsat_core_plugin_farkas_lemma* plugin = + alloc(unsat_core_plugin_farkas_lemma, + learner, m_split_literals, + (m_iuc_arith == 1) /* use constants from A */); + learner.register_plugin(plugin); } - else if (m_iuc_arith == 1) - { - unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, - learner, m_split_literals, - true /* use constants from A */); - learner.register_plugin(plugin_farkas_lemma); + else if (m_iuc_arith == 2) { + SASSERT(false && "Broken"); + unsat_core_plugin_farkas_lemma_optimized* plugin = + alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m); + learner.register_plugin(plugin); } - else if (m_iuc_arith == 2) - { - unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m); - learner.register_plugin(plugin_farkas_lemma_optimized); + else if(m_iuc_arith == 3) { + unsat_core_plugin_farkas_lemma_bounded* plugin = + alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m); + learner.register_plugin(plugin); } - else if(m_iuc_arith == 3) - { - unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m); - learner.register_plugin(plugin_farkas_lemma_bounded); + else { + UNREACHABLE(); } - if (m_iuc == 2) - { - unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m); - learner.register_plugin(plugin_min_cut); + if (m_iuc == 1) { + // -- iuc based on the lowest cut in the proof + unsat_core_plugin_lemma* plugin = + alloc(unsat_core_plugin_lemma, learner); + learner.register_plugin(plugin); } - else - { - unsat_core_plugin_lemma* plugin_lemma = alloc(unsat_core_plugin_lemma, learner); - learner.register_plugin(plugin_lemma); + else if (m_iuc == 2) { + // -- iuc based on the smallest cut in the proof + unsat_core_plugin_min_cut* plugin = + alloc(unsat_core_plugin_min_cut, learner, m); + learner.register_plugin(plugin); + } + else { + UNREACHABLE(); } // compute interpolating unsat core learner.compute_unsat_core(core); - // postprocessing, TODO: elim_proxies should be done inside iuc_proof elim_proxies (core); - simplify_bounds (core); // XXX potentially redundant + // AG: this should be taken care of by minimizing the iuc cut + simplify_bounds (core); } IF_VERBOSE(2, verbose_stream () << "IUC Core:\n" - << mk_pp (mk_and (core), m) << "\n";); - + << mk_and(core) << "\n";); } void iuc_solver::refresh () From 33466c75a61ff2a1331936defe7b9c7eefb0b2d5 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 11:25:38 -0700 Subject: [PATCH 103/364] mss loop in prop_solver max sat assignment (mss) to replace core-based maxsmt() --- src/muz/spacer/spacer_prop_solver.cpp | 91 +++++++++++++++++++++++++++ src/muz/spacer/spacer_prop_solver.h | 1 + 2 files changed, 92 insertions(+) diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 87f998486..f2fcf5be7 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -35,6 +35,7 @@ Revision History: #include "muz/spacer/spacer_farkas_learner.h" #include "muz/spacer/spacer_prop_solver.h" +#include "model/model_evaluator.h" #include "muz/base/fixedpoint_params.hpp" namespace spacer { @@ -136,6 +137,96 @@ void prop_solver::assert_expr(expr * form, unsigned level) } +/// Local model guided maxsmt +lbool prop_solver::mss(expr_ref_vector &hard, expr_ref_vector &soft) { + // replace expressions by assumption literals + iuc_solver::scoped_mk_proxy _p_(*m_ctx, hard); + unsigned hard_sz = hard.size(); + + lbool res = m_ctx->check_sat(hard.size(), hard.c_ptr()); + // bail out if hard constraints are not sat, or if there are no + // soft constraints + if (res != l_true || soft.empty()) {return res;} + + // the main loop + + model_ref mdl; + m_ctx->get_model(mdl); + + // don't proxy soft literals. Assume that they are propositional. + hard.append(soft); + soft.reset(); + + + // hard is divided into 4 regions + // x < hard_sz ---> hard constraints + // hard_sz <= x < i ---> sat soft constraints + // i <= x < j ---> backbones (unsat soft constraints) + // j <= x < hard.size() ---> unprocessed soft constraints + unsigned i, j; + i = hard_sz; + j = hard_sz; + + while (j < hard.size()) { + model_evaluator mev(*mdl); + + // move all true soft constraints to [hard_sz, i) + for (unsigned k = j; k < hard.size(); ++k) { + expr_ref e(m); + e = hard.get(k); + if (!mev.is_false(e) /* true or unset */) { + expr_ref tmp(m); + tmp = hard.get(i); + hard[i] = e; + if (i < j) { + // tmp is a backbone, put it at j + if (j == k) {hard[j] = tmp;} + else /* j < k */ { + e = hard.get(j); + hard[j] = tmp; + hard[k] = e; + } + j++; + } + else { + // there are no backbone literals + hard[k] = tmp; + j++; + } + i++; + } + } + + // done with the model. Reset to avoid confusion in debugging + mdl.reset(); + + // -- grow the set of backbone literals + for (;j < hard.size(); ++j) { + res = m_ctx->check_sat(j+1, hard.c_ptr()); + if (res == l_false) { + // -- flip non-true literal to be false + hard[j] = m.mk_not(hard.get(j)); + } + else if (res == l_true) { + // -- get the model for the next iteration of the outer loop + m_ctx->get_model(mdl); + break; + } + else if (res == l_undef) { + // -- conservatively bail out + hard.resize(hard_sz); + return l_undef; + } + } + } + + // move sat soft constraints to the output vector + for (unsigned k = i; k < j; ++k) {soft.push_back(hard.get(k));} + // cleanup hard constraints + hard.resize(hard_sz); + return l_true; +} + /// Poor man's maxsat. No guarantees of maximum solution /// Runs maxsat loop on m_ctx Returns l_false if hard is unsat, /// otherwise reduces soft such that hard & soft is sat. diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 295b47982..12d01ac40 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -71,6 +71,7 @@ private: expr_ref_vector &soft); lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft); + lbool mss(expr_ref_vector &hard, expr_ref_vector &soft); public: From ac3bbed311548e28ecf90b2df7adfa1e5ec34a25 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 12:17:39 -0700 Subject: [PATCH 104/364] Remove dead code in spacer_manager - removed bg_assertions. Incompatible with mbp in spacer - removed unique number. Unused - removed mk_and() and switched to ast_util:mk_and() instead spacer_manager::mk_and() uses bool_rewriter to simplify the conjunction --- src/muz/spacer/spacer_context.cpp | 41 ++--- src/muz/spacer/spacer_context.h | 2 - src/muz/spacer/spacer_dl_interface.cpp | 15 +- src/muz/spacer/spacer_generalizers.cpp | 2 +- src/muz/spacer/spacer_manager.cpp | 126 +------------ src/muz/spacer/spacer_manager.h | 242 ++++--------------------- src/muz/spacer/spacer_prop_solver.cpp | 7 +- src/muz/spacer/spacer_prop_solver.h | 4 +- 8 files changed, 73 insertions(+), 366 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 5de817435..7d8ffc0aa 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -312,11 +312,10 @@ expr_ref pred_transformer::get_formulas(unsigned level, bool add_axioms) { expr_ref_vector res(m); if (add_axioms) { - res.push_back(pm.get_background()); res.push_back((level == 0)?initial_state():transition()); } m_frames.get_frame_geq_lemmas (level, res); - return pm.mk_and(res); + return mk_and(res); } bool pred_transformer::propagate_to_next_level (unsigned src_level) @@ -539,7 +538,7 @@ expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) expr_ref_vector lemmas (m); m_frames.get_frame_lemmas (level == -1 ? infty_level() : level, lemmas); - if (!lemmas.empty()) { result = pm.mk_and(lemmas); } + if (!lemmas.empty()) { result = mk_and(lemmas); } // replace local constants by bound variables. expr_substitution sub(m); @@ -611,7 +610,7 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, expr_ref_vector literals (m); compute_implicant_literals (mev, summary, literals); - return get_manager ().mk_and (literals); + return mk_and(literals); } @@ -857,10 +856,10 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, unsigned& uses_level, unsigned weakness) { - manager& pm = get_manager(); expr_ref_vector conj(m), core(m); expr_ref states(m); - states = m.mk_not(pm.mk_and(state)); + states = mk_and(state); + states = m.mk_not(states); mk_assumptions(head(), states, conj); prop_solver::scoped_level _sl(m_solver, level); prop_solver::scoped_subset_core _sc (m_solver, true); @@ -972,7 +971,7 @@ void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transitions.push_back (m.mk_or (pred, m_extend_lit->get_arg (0))); if (!is_init [0]) { init_conds.push_back(m.mk_not(pred)); } - transition = pm.mk_and(transitions); + transition = mk_and(transitions); break; } default: @@ -992,11 +991,11 @@ void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& } } transitions.push_back(m.mk_or(disj.size(), disj.c_ptr())); - transition = pm.mk_and(transitions); + transition = mk_and(transitions); break; } // mk init condition - init = pm.mk_and (init_conds); + init = mk_and (init_conds); if (init_conds.empty ()) { // no rule has uninterpreted tail m_all_init = true; } @@ -1146,7 +1145,6 @@ void pred_transformer::init_atom( void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r) { - r.push_back(pm.get_background()); r.push_back((lvl == 0)?initial_state():transition()); for (unsigned i = 0; i < rules().size(); ++i) { add_premises(pts, lvl, *rules()[i], r); @@ -1736,7 +1734,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) // -- update m_trans with the pre-image of m_trans over the must summaries summaries.push_back (m_trans); - m_trans = get_manager ().mk_and (summaries); + m_trans = mk_and (summaries); summaries.reset (); if (!vars.empty()) { @@ -1765,7 +1763,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) summaries.push_back (m_trans); expr_ref post(m); - post = get_manager ().mk_and (summaries); + post = mk_and (summaries); summaries.reset (); if (!vars.empty()) { timeit _timer2 (is_trace_enabled("spacer_timeit"), @@ -1827,7 +1825,7 @@ pob *derivation::create_next_child () // if not true, bail out, the must summary of m_active is not strong enough // this is possible if m_post was weakened for some reason - if (!pt.is_must_reachable(pm.mk_and(summaries), &model)) { return nullptr; } + if (!pt.is_must_reachable(mk_and(summaries), &model)) { return nullptr; } model_evaluator_util mev (m); mev.set_model (*model); @@ -1840,7 +1838,7 @@ pob *derivation::create_next_child () u.push_back (rf->get ()); compute_implicant_literals (mev, u, lits); expr_ref v(m); - v = pm.mk_and (lits); + v = mk_and (lits); // XXX The summary is not used by anyone after this point m_premises[m_active].set_summary (v, true, &(rf->aux_vars ())); @@ -1860,7 +1858,7 @@ pob *derivation::create_next_child () summaries.reset (); summaries.push_back (v); summaries.push_back (active_trans); - m_trans = pm.mk_and (summaries); + m_trans = mk_and (summaries); // variables to eliminate vars.append (rf->aux_vars ().size (), rf->aux_vars ().c_ptr ()); @@ -3184,8 +3182,7 @@ lbool context::expand_node(pob& n) n.bump_weakness(); return expand_node(n); } - TRACE("spacer", tout << "unknown state: " - << mk_pp(m_pm.mk_and(cube), m) << "\n";); + TRACE("spacer", tout << "unknown state: " << mk_and(cube) << "\n";); throw unknown_exception(); } UNREACHABLE(); @@ -3305,14 +3302,14 @@ reach_fact *context::mk_reach_fact (pob& n, model_evaluator_util &mev, bool elim_aux = get_params ().spacer_elim_aux (); if (elim_aux) { vars.append(aux_vars.size(), aux_vars.c_ptr()); } - res = m_pm.mk_and (path_cons); + res = mk_and (path_cons); // -- pick an implicant from the path condition if (get_params().spacer_reach_dnf()) { expr_ref_vector u(m), lits(m); u.push_back (res); compute_implicant_literals (mev, u, lits); - res = m_pm.mk_and (lits); + res = mk_and (lits); } @@ -3405,7 +3402,7 @@ bool context::create_children(pob& n, datalog::rule const& r, n.get_skolems(vars); - expr_ref phi1 = m_pm.mk_and (Phi); + expr_ref phi1 = mk_and (Phi); qe_project (m, vars, phi1, mev.get_model (), true, m_use_native_mbp, !m_ground_cti); //qe::reduce_array_selects (*mev.get_model (), phi1); @@ -3413,7 +3410,7 @@ bool context::create_children(pob& n, datalog::rule const& r, TRACE ("spacer", tout << "Implicant\n"; - tout << mk_pp (m_pm.mk_and (Phi), m) << "\n"; + tout << mk_and (Phi) << "\n"; tout << "Projected Implicant\n" << mk_pp (phi1, m) << "\n"; ); @@ -3615,7 +3612,7 @@ expr_ref context::get_constraints (unsigned level) } if (constraints.empty()) { return expr_ref(m.mk_true(), m); } - return m_pm.mk_and (constraints); + return mk_and (constraints); } void context::add_constraint (expr *c, unsigned level) diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 596e5e047..9d3456f2b 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -895,8 +895,6 @@ public: void update_rules(datalog::rule_set& rules); - void set_axioms(expr* axioms) { m_pm.set_background(axioms); } - unsigned get_num_levels(func_decl* p); expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p); diff --git a/src/muz/spacer/spacer_dl_interface.cpp b/src/muz/spacer/spacer_dl_interface.cpp index b121cf50a..cadbcfb0e 100644 --- a/src/muz/spacer/spacer_dl_interface.cpp +++ b/src/muz/spacer/spacer_dl_interface.cpp @@ -93,19 +93,14 @@ lbool dl_interface::query(expr * query) datalog::rule_set old_rules(rules0); func_decl_ref query_pred(m); rm.mk_query(query, m_ctx.get_rules()); - expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); TRACE("spacer", - if (!m.is_true(bg_assertion)) { - tout << "axioms:\n"; - tout << mk_pp(bg_assertion, m) << "\n"; - } - tout << "query: " << mk_pp(query, m) << "\n"; - tout << "rules:\n"; - m_ctx.display_rules(tout); - ); + tout << "query: " << mk_pp(query, m) << "\n"; + tout << "rules:\n"; + m_ctx.display_rules(tout); + ); apply_default_transformation(m_ctx); @@ -161,7 +156,6 @@ lbool dl_interface::query(expr * query) m_context->set_proof_converter(m_ctx.get_proof_converter()); m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); - m_context->set_axioms(bg_assertion); m_context->update_rules(m_spacer_rules); if (m_spacer_rules.get_rules().empty()) { @@ -255,7 +249,6 @@ lbool dl_interface::query_from_lvl(expr * query, unsigned lvl) m_context->set_proof_converter(m_ctx.get_proof_converter()); m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); - m_context->set_axioms(bg_assertion); m_context->update_rules(m_spacer_rules); if (m_spacer_rules.get_rules().empty()) { diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 3f6e28be6..34f1eb730 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -289,7 +289,7 @@ void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) // } TRACE("core_array_eq", tout << "new possible core " - << mk_pp(pm.mk_and(lits), m) << "\n";); + << mk_and(lits) << "\n";); pred_transformer &pt = lemma->get_pob()->pt(); diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index c46c31924..04d3c09d4 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -168,8 +168,8 @@ void inductive_property::display(datalog::rule_manager& rm, ptr_vector manager::get_state_suffixes() -{ + +static std::vector state_suffixes() { std::vector res; res.push_back("_n"); return res; @@ -177,15 +177,11 @@ std::vector manager::get_state_suffixes() manager::manager(unsigned max_num_contexts, ast_manager& manager) : m(manager), - m_brwr(m), - m_mux(m, get_state_suffixes()), - m_background(m.mk_true(), m), + m_mux(m, state_suffixes()), m_contexts(m, max_num_contexts), m_contexts2(m, max_num_contexts), - m_contexts3(m, max_num_contexts), - m_next_unique_num(0) -{ -} + m_contexts3(m, max_num_contexts) +{} void manager::add_new_state(func_decl * s) @@ -195,7 +191,6 @@ void manager::add_new_state(func_decl * s) SASSERT(o_index(0) == 1); //we assume this in the number of retrieved symbols m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect); - m_o0_preds.push_back(vect[o_index(0)]); } func_decl * manager::get_o_pred(func_decl* s, unsigned idx) @@ -218,117 +213,6 @@ func_decl * manager::get_n_pred(func_decl* s) return res; } -void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res) -{ - m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res); -} - -void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res) -{ - m_brwr.mk_and(core.size(), core.c_ptr(), res); -} - -void manager::mk_cube_into_lemma(expr * cube, expr_ref & res) -{ - m_brwr.mk_not(cube, res); -} - -void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res) -{ - m_brwr.mk_not(lemma, res); -} - -expr_ref manager::mk_and(unsigned sz, expr* const* exprs) -{ - expr_ref result(m); - m_brwr.mk_and(sz, exprs, result); - return result; -} - -expr_ref manager::mk_or(unsigned sz, expr* const* exprs) -{ - expr_ref result(m); - m_brwr.mk_or(sz, exprs, result); - return result; -} - -expr_ref manager::mk_not_and(expr_ref_vector const& conjs) -{ - expr_ref result(m), e(m); - expr_ref_vector es(conjs); - flatten_and(es); - for (unsigned i = 0; i < es.size(); ++i) { - m_brwr.mk_not(es[i].get(), e); - es[i] = e; - } - m_brwr.mk_or(es.size(), es.c_ptr(), result); - return result; -} - -void manager::get_or(expr* e, expr_ref_vector& result) -{ - result.push_back(e); - for (unsigned i = 0; i < result.size();) { - e = result[i].get(); - if (m.is_or(e)) { - result.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - result[i] = result.back(); - result.pop_back(); - } else { - ++i; - } - } -} - -bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value) -{ - if (!is_app(atom0)) { - return false; - } - app * atom = to_app(atom0); - expr * arg1; - expr * arg2; - app * candidate_state; - app_ref candidate_value(m); - if (m.is_not(atom, arg1)) { - if (!is_app(arg1)) { - return false; - } - candidate_state = to_app(arg1); - candidate_value = m.mk_false(); - } else if (m.is_eq(atom, arg1, arg2)) { - if (!is_app(arg1) || !is_app(arg2)) { - return false; - } - if (!m_mux.is_muxed(to_app(arg1)->get_decl())) { - std::swap(arg1, arg2); - } - candidate_state = to_app(arg1); - candidate_value = to_app(arg2); - } else { - candidate_state = atom; - candidate_value = m.mk_true(); - } - if (!m_mux.is_muxed(candidate_state->get_decl())) { - return false; - } - state = candidate_state; - value = candidate_value; - return true; -} - -bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state) -{ - app_ref dummy_value_holder(m); - app * s; - if (try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) { - state = s->get_decl(); - return true; - } else { - return false; - } -} - /** * Create a new skolem constant */ diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index 32cf61d57..58b5aca07 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -37,9 +37,7 @@ Revision History: #include "muz/spacer/spacer_smt_context_manager.h" #include "muz/base/dl_rule.h" -namespace smt { -class context; -} +namespace smt {class context;} namespace spacer { @@ -67,32 +65,24 @@ public: m_relation_info(relations) {} std::string to_string() const; - expr_ref to_expr() const; - void to_model(model_ref& md) const; - - void display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const; + void display(datalog::rule_manager& rm, + ptr_vector const& rules, + std::ostream& out) const; }; class manager { ast_manager& m; - mutable bool_rewriter m_brwr; - + // manager of multiplexed names sym_mux m_mux; - expr_ref m_background; - decl_vector m_o0_preds; + + // three solver pools for different queries spacer::smt_context_manager m_contexts; spacer::smt_context_manager m_contexts2; spacer::smt_context_manager m_contexts3; - /** whenever we need an unique number, we get this one and increase */ - unsigned m_next_unique_num; - - - static std::vector get_state_suffixes(); - unsigned n_index() const { return 0; } unsigned o_index(unsigned i) const { return i + 1; } @@ -102,218 +92,68 @@ public: manager(unsigned max_num_contexts, ast_manager & manager); ast_manager& get_manager() const { return m; } - bool_rewriter& get_brwr() const { return m_brwr; } - expr_ref mk_and(unsigned sz, expr* const* exprs); - expr_ref mk_and(expr_ref_vector const& exprs) - { - return mk_and(exprs.size(), exprs.c_ptr()); - } - expr_ref mk_and(expr* a, expr* b) - { - expr* args[2] = { a, b }; - return mk_and(2, args); - } - expr_ref mk_or(unsigned sz, expr* const* exprs); - expr_ref mk_or(expr_ref_vector const& exprs) - { - return mk_or(exprs.size(), exprs.c_ptr()); - } - - expr_ref mk_not_and(expr_ref_vector const& exprs); - - void get_or(expr* e, expr_ref_vector& result); + // management of mux names //"o" predicates stand for the old states and "n" for the new states func_decl * get_o_pred(func_decl * s, unsigned idx); func_decl * get_n_pred(func_decl * s); - /** - Marks symbol as non-model which means it will not appear in models collected by - get_state_cube_from_model function. - This is to take care of auxiliary symbols introduced by the disjunction relations - to relativize lemmas coming from disjuncts. - */ - void mark_as_non_model(func_decl * p) - { - m_mux.mark_as_non_model(p); - } - - - func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); } - func_decl * const * end_o0_preds() const { return m_o0_preds.end(); } - - bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); } - func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); } - - bool is_o(func_decl * p, unsigned idx) const - { - return m_mux.has_index(p, o_index(idx)); - } - void get_o_index(func_decl* p, unsigned& idx) const - { + void get_o_index(func_decl* p, unsigned& idx) const { m_mux.try_get_index(p, idx); SASSERT(idx != n_index()); idx--; // m_mux has indices starting at 1 } - bool is_o(expr* e, unsigned idx) const - { - return is_app(e) && is_o(to_app(e)->get_decl(), idx); - } - bool is_o(func_decl * p) const - { + + bool is_o(func_decl * p, unsigned idx) const + {return m_mux.has_index(p, o_index(idx));} + bool is_o(func_decl * p) const { unsigned idx; return m_mux.try_get_index(p, idx) && idx != n_index(); } bool is_o(expr* e) const - { - return is_app(e) && is_o(to_app(e)->get_decl()); - } + {return is_app(e) && is_o(to_app(e)->get_decl());} + bool is_o(expr* e, unsigned idx) const + {return is_app(e) && is_o(to_app(e)->get_decl(), idx);} bool is_n(func_decl * p) const - { - return m_mux.has_index(p, n_index()); - } + {return m_mux.has_index(p, n_index());} bool is_n(expr* e) const - { - return is_app(e) && is_n(to_app(e)->get_decl()); - } - - /** true if p should not appead in models propagates into child relations */ - bool is_non_model_sym(func_decl * p) const - { return m_mux.is_non_model_sym(p); } + {return is_app(e) && is_n(to_app(e)->get_decl());} /** true if f doesn't contain any n predicates */ bool is_o_formula(expr * f) const - { - return !m_mux.contains(f, n_index()); - } - + {return !m_mux.contains(f, n_index());} /** true if f contains only o state preds of index o_idx */ bool is_o_formula(expr * f, unsigned o_idx) const - { - return m_mux.is_homogenous_formula(f, o_index(o_idx)); - } + {return m_mux.is_homogenous_formula(f, o_index(o_idx));} /** true if f doesn't contain any o predicates */ bool is_n_formula(expr * f) const - { - return m_mux.is_homogenous_formula(f, n_index()); - } + {return m_mux.is_homogenous_formula(f, n_index());} func_decl * o2n(func_decl * p, unsigned o_idx) const - { - return m_mux.conv(p, o_index(o_idx), n_index()); - } + {return m_mux.conv(p, o_index(o_idx), n_index());} func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) const - { - return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx)); - } + {return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx));} func_decl * n2o(func_decl * p, unsigned o_idx) const - { - return m_mux.conv(p, n_index(), o_index(o_idx)); - } + {return m_mux.conv(p, n_index(), o_index(o_idx));} void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const - { m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); } + {m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous);} void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const - { m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); } + {m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous);} void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const - { m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); } + {m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous);} - void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous = true) const - { m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); } - - /** - Return true if all state symbols which e contains are of one kind (either "n" or one of "o"). - */ - bool is_homogenous_formula(expr * e) const - { - return m_mux.is_homogenous_formula(e); - } - - /** - Collect indices used in expression. - */ - void collect_indices(expr* e, unsigned_vector& indices) const - { - m_mux.collect_indices(e, indices); - } - - /** - Collect used variables of each index. - */ - void collect_variables(expr* e, vector >& vars) const - { - m_mux.collect_variables(e, vars); - } - - /** - Return true iff both s1 and s2 are either "n" or "o" of the same index. - If one (or both) of them are not state symbol, return false. - */ - bool have_different_state_kinds(func_decl * s1, func_decl * s2) const - { - unsigned i1, i2; - return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1 != i2; - } - - /** - Increase indexes of state symbols in formula by dist. - The 'N' index becomes 'O' index with number dist-1. - */ - void formula_shift(expr * src, expr_ref & tgt, unsigned dist) const - { - SASSERT(n_index() == 0); - SASSERT(o_index(0) == 1); - m_mux.shift_formula(src, dist, tgt); - } - - void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res); - void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res); - void mk_cube_into_lemma(expr * cube, expr_ref & res); - void mk_lemma_into_cube(expr * lemma, expr_ref & res); - - /** - Remove from vec all atoms that do not have an "o" state. - The order of elements in vec may change. - An assumption is that atoms having "o" state of given index - do not have "o" states of other indexes or "n" states. - */ - void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const - { m_mux.filter_idx(vec, o_index(o_idx)); } - void filter_n_atoms(expr_ref_vector& vec) const - { m_mux.filter_idx(vec, n_index()); } - - /** - Partition literals into o_lits and others. - */ - void partition_o_atoms(expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, - unsigned o_idx) const - { - m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx)); - } - - void filter_out_non_model_atoms(expr_ref_vector& vec) const - { m_mux.filter_non_model_lits(vec); } - - bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value); - bool try_get_state_decl_from_atom(expr * atom, func_decl *& state); + void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, + unsigned tgt_idx, bool homogenous = true) const + {m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous);} - std::string pp_model(const model_core & mdl) const - { return m_mux.pp_model(mdl); } - - - void set_background(expr* b) { m_background = b; } - - expr* get_background() const { return m_background; } - - unsigned get_unique_num() { return m_next_unique_num++; } - + // three different solvers with three different sets of parameters + // different solvers are used for different types of queries in spacer solver* mk_fresh() {return m_contexts.mk_fresh();} smt_params& fparams() { return m_contexts.fparams(); } solver* mk_fresh2() {return m_contexts2.mk_fresh();} @@ -323,32 +163,30 @@ public: - void collect_statistics(statistics& st) const - { + void collect_statistics(statistics& st) const { m_contexts.collect_statistics(st); m_contexts2.collect_statistics(st); m_contexts3.collect_statistics(st); } - void reset_statistics() - { + void reset_statistics() { m_contexts.reset_statistics(); m_contexts2.reset_statistics(); m_contexts3.reset_statistics(); } }; +/** Skolem constants for quantified spacer */ app* mk_zk_const (ast_manager &m, unsigned idx, sort *s); int find_zk_const(expr* e, app_ref_vector &out); -inline int find_zk_const(expr_ref_vector const &v, app_ref_vector &out) { - return find_zk_const (mk_and(v), out); -} +inline int find_zk_const(expr_ref_vector const &v, app_ref_vector &out) +{return find_zk_const (mk_and(v), out);} + bool has_zk_const(expr* e); bool is_zk_const (const app *a, int &n); -struct sk_lt_proc { - bool operator()(const app* a1, const app* a2); -}; +struct sk_lt_proc {bool operator()(const app* a1, const app* a2);}; + } #endif diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index f2fcf5be7..1dd4515a7 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -40,9 +40,9 @@ Revision History: namespace spacer { -prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& name) : +prop_solver::prop_solver(spacer::manager& pm, + fixedpoint_params const& p, symbol const& name) : m(pm.get_manager()), - m_pm(pm), m_name(name), m_ctx(nullptr), m_pos_level_atoms(m), @@ -73,9 +73,6 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& p.spacer_iuc_print_farkas_stats(), p.spacer_iuc_old_hyp_reducer(), p.spacer_iuc_split_farkas_literals()); - - for (unsigned i = 0; i < 2; ++i) - { m_contexts[i]->assert_expr(m_pm.get_background()); } } void prop_solver::add_level() diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 12d01ac40..d10ebcfcd 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -41,7 +41,6 @@ class prop_solver { private: ast_manager& m; - manager& m_pm; symbol m_name; smt_params* m_fparams[2]; solver* m_solvers[2]; @@ -75,7 +74,8 @@ private: public: - prop_solver(spacer::manager& pm, fixedpoint_params const& p, symbol const& name); + prop_solver(spacer::manager &manager, + fixedpoint_params const& p, symbol const& name); void set_core(expr_ref_vector* core) { m_core = core; } From 3f9b5bce99d0dcbc1b59fdb36d986bd73c2d83bf Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 13:22:22 -0700 Subject: [PATCH 105/364] Remove debug function --- src/muz/spacer/spacer_context.cpp | 17 +++++++++-------- src/muz/spacer/spacer_context.h | 2 -- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 7d8ffc0aa..eccc156de 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1001,6 +1001,14 @@ void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& } } +static bool is_all_non_null(app_ref_vector const& v) +{ + for (unsigned i = 0; i < v.size(); ++i) { + if (!v[i]) { return false; } + } + return true; +} + void pred_transformer::init_rule( decl2rel const& pts, datalog::rule const& rule, @@ -1035,7 +1043,7 @@ void pred_transformer::init_rule( fml = mk_and (tail); ground_free_vars (fml, var_reprs, aux_vars, ut_size == 0); - SASSERT(check_filled(var_reprs)); + SASSERT(is_all_non_null(var_reprs)); expr_ref tmp(m); var_subst (m, false)(fml, @@ -1074,13 +1082,6 @@ void pred_transformer::init_rule( tout << "\n";); } -bool pred_transformer::check_filled(app_ref_vector const& v) const -{ - for (unsigned i = 0; i < v.size(); ++i) { - if (!v[i]) { return false; } - } - return true; -} // create constants for free variables in tail. void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 9d3456f2b..0d4db2279 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -320,8 +320,6 @@ class pred_transformer { void simplify_formulas(tactic& tac, expr_ref_vector& fmls); // Debugging - bool check_filled(app_ref_vector const& v) const; - void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); expr* mk_fresh_reach_case_var (); From ec8f99fee79420ba334d2239715c476b69d588fb Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 13:31:28 -0700 Subject: [PATCH 106/364] Rename expand_node --> expand_pob --- src/muz/spacer/spacer_context.cpp | 22 +++++++++++----------- src/muz/spacer/spacer_context.h | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index eccc156de..f9603bd62 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2854,7 +2854,7 @@ bool context::check_reachability () node = m_pob_queue.top (); SASSERT (node->level () <= m_pob_queue.max_level ()); - switch (expand_node(*node)) { + switch (expand_pob(*node)) { case l_true: SASSERT (m_pob_queue.top () == node.get ()); m_pob_queue.pop (); @@ -2982,23 +2982,23 @@ bool context::is_reachable(pob &n) } //this processes a goal and creates sub-goal -lbool context::expand_node(pob& n) +lbool context::expand_pob(pob& n) { pob::on_expand_event _evt(n); TRACE ("spacer", - tout << "expand-node: " << n.pt().head()->get_name() + tout << "expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_pp(n.post(), m) << "\n";); STRACE ("spacer.expand-add", - tout << "expand-node: " << n.pt().head()->get_name() + tout << "expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_epp(n.post(), m) << "\n\n";); TRACE ("core_array_eq", - tout << "expand-node: " << n.pt().head()->get_name() + tout << "expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_pp(n.post(), m) << "\n";); @@ -3109,7 +3109,7 @@ lbool context::expand_node(pob& n) // n is unreachable, create new summary facts case l_false: { timeit _timer (is_trace_enabled("spacer_timeit"), - "spacer::expand_node::false", + "spacer::expand_pob::false", verbose_stream ()); // -- only update expanded level when new lemmas are generated at it. @@ -3164,7 +3164,7 @@ lbool context::expand_node(pob& n) if (n.weakness() < 100 /* MAX_WEAKENSS */) { bool has_new_child = false; SASSERT(m_weak_abs); - m_stats.m_expand_node_undef++; + m_stats.m_expand_pob_undef++; if (r && r->get_uninterpreted_tail_size() > 0) { model_evaluator_util mev(m); mev.set_model(*model); @@ -3181,7 +3181,7 @@ lbool context::expand_node(pob& n) // -- failed to create a child, bump weakness and repeat // -- the recursion is bounded by the levels of weakness supported n.bump_weakness(); - return expand_node(n); + return expand_pob(n); } TRACE("spacer", tout << "unknown state: " << mk_and(cube) << "\n";); throw unknown_exception(); @@ -3500,8 +3500,8 @@ void context::collect_statistics(statistics& st) const st.update("SPACER inductive level", m_inductive_lvl); // -- length of the counterexample st.update("SPACER cex depth", m_stats.m_cex_depth); - // -- number of times expand_node resulted in undef - st.update("SPACER expand node undef", m_stats.m_expand_node_undef); + // -- number of times expand_pobresulted in undef + st.update("SPACER expand pob undef", m_stats.m_expand_pob_undef); // -- number of distinct lemmas constructed st.update("SPACER num lemmas", m_stats.m_num_lemmas); // -- number of restarts taken @@ -3730,7 +3730,7 @@ inline bool pob_lt::operator() (const pob *pn1, const pob *pn2) const /** XXX Identical nodes. This should not happen. However, * currently, when propagating reachability, we might call - * expand_node() twice on the same node, causing it to generate + * expand_pob() twice on the same node, causing it to generate * the same proof obligation multiple times */ return &n1 < &n2; } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 0d4db2279..38543f25f 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -744,7 +744,7 @@ class context { unsigned m_max_query_lvl; unsigned m_max_depth; unsigned m_cex_depth; - unsigned m_expand_node_undef; + unsigned m_expand_pob_undef; unsigned m_num_lemmas; unsigned m_num_restarts; unsigned m_num_lemmas_imported; @@ -792,7 +792,7 @@ class context { bool propagate(unsigned min_prop_lvl, unsigned max_prop_lvl, unsigned full_prop_lvl); bool is_reachable(pob &n); - lbool expand_node(pob& n); + lbool expand_pob(pob &n); reach_fact *mk_reach_fact (pob& n, model_evaluator_util &mev, datalog::rule const& r); bool create_children(pob& n, datalog::rule const& r, From a696a40a3af335daea2732d8da358a2ec78114c3 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 13:43:35 -0700 Subject: [PATCH 107/364] Refactoring --- src/muz/spacer/spacer_context.cpp | 32 +++++++++++++++++++++++-------- src/muz/spacer/spacer_context.h | 11 +++-------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index f9603bd62..923ecb5b8 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2981,6 +2981,24 @@ bool context::is_reachable(pob &n) return next ? is_reachable(*next) : true; } +void context::dump_json() +{ + if(m_params.spacer_print_json().size()) { + std::ofstream of; + of.open(m_params.spacer_print_json().bare_str()); + m_json_marshaller.marshal(of); + of.close(); + } +} + +void context::predecessor_eh() +{ + for (unsigned i = 0; i < m_callbacks.size(); i++) { + if(m_callbacks[i]->predecessor()) + m_callbacks[i]->predecessor_eh(); + } +} + //this processes a goal and creates sub-goal lbool context::expand_pob(pob& n) { @@ -3040,10 +3058,7 @@ lbool context::expand_pob(pob& n) tout << "This pob can be blocked by instantiation\n";); } - for (unsigned i = 0; i < m_callbacks.size(); i++){ - if(m_callbacks[i]->predecessor()) - m_callbacks[i]->predecessor_eh(); - } + predecessor_eh(); lbool res = n.pt ().is_reachable (n, &cube, &model, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); @@ -3106,8 +3121,9 @@ lbool context::expand_pob(pob& n) return l_undef; } + case l_false: // n is unreachable, create new summary facts - case l_false: { + { timeit _timer (is_trace_enabled("spacer_timeit"), "spacer::expand_pob::false", verbose_stream ()); @@ -3133,9 +3149,9 @@ lbool context::expand_pob(pob& n) (*m_lemma_generalizers[i])(lemma); } DEBUG_CODE( - lemma_sanity_checker sanity_checker(*this); - sanity_checker(lemma); - ); + lemma_sanity_checker sanity_checker(*this); + sanity_checker(lemma); + ); TRACE("spacer", tout << "invariant state: " diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 38543f25f..5eedb1736 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -824,14 +824,9 @@ class context { unsigned get_cex_depth (); - void dump_json() { - if(m_params.spacer_print_json().size()) { - std::ofstream of; - of.open(m_params.spacer_print_json().bare_str()); - m_json_marshaller.marshal(of); - of.close(); - } - } + void dump_json(); + + void predecessor_eh(); public: /** From 05c8067392e94d22e8fa7e4314a33e94bea5e248 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 14:50:58 -0700 Subject: [PATCH 108/364] Changed pob queue management strategy in spacer_context --- src/muz/spacer/spacer_context.cpp | 87 ++++++++++++++++++++----------- src/muz/spacer/spacer_context.h | 7 ++- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 923ecb5b8..48cbbd258 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2004,6 +2004,8 @@ void pob_queue::reset() } void pob_queue::push(pob &n) { + TRACE("pob_queue", + tout << "pob_queue::push(" << n.post()->get_id() << ")\n";); m_obligations.push (&n); n.get_context().new_pob_eh(&n); } @@ -2787,6 +2789,8 @@ bool context::check_reachability () pob_ref last_reachable; + pob_ref_buffer new_pobs; + if (get_params().spacer_reset_obligation_queue()) { m_pob_queue.reset(); } unsigned initial_size = m_stats.m_num_lemmas; @@ -2853,40 +2857,44 @@ bool context::check_reachability () } node = m_pob_queue.top (); + m_pob_queue.pop(); + unsigned old_sz = m_pob_queue.size(); SASSERT (node->level () <= m_pob_queue.max_level ()); - switch (expand_pob(*node)) { + switch (expand_pob(*node, new_pobs)) { case l_true: - SASSERT (m_pob_queue.top () == node.get ()); - m_pob_queue.pop (); + SASSERT(m_pob_queue.size() == old_sz); + SASSERT(new_pobs.empty()); last_reachable = node; last_reachable->close (); - if (m_pob_queue.is_root(*node)) { return true; } + if (m_pob_queue.is_root(*node)) {return true;} break; case l_false: - SASSERT (m_pob_queue.top () == node.get ()); - m_pob_queue.pop (); + SASSERT(m_pob_queue.size() == old_sz); + for (auto pob : new_pobs) { + if (is_requeue(*pob)) {m_pob_queue.push(*pob);} + } - if (node->is_dirty()) { node->clean(); } - - node->inc_level (); - if (get_params ().pdr_flexible_trace () && - (node->level () >= m_pob_queue.max_level () || - m_pob_queue.max_level () - node->level () - <= get_params ().pdr_flexible_trace_depth ())) - { m_pob_queue.push(*node); } - - if (m_pob_queue.is_root(*node)) { return false; } + if (m_pob_queue.is_root(*node)) {return false;} break; case l_undef: - // SASSERT (m_pob_queue.top () != node.get ()); + SASSERT(m_pob_queue.size() == old_sz); + for (auto pob : new_pobs) {m_pob_queue.push(*pob);} break; } + new_pobs.reset(); } UNREACHABLE(); return false; } +/// returns true if the given pob can be re-scheduled +bool context::is_requeue(pob &n) { + if (!get_params().pdr_flexible_trace()) {return false;} + unsigned max_depth = get_params().pdr_flexible_trace_depth(); + return (n.level() >= m_pob_queue.max_level() || + m_pob_queue.max_level() - n.level() <= max_depth); +} /// check whether node n is concretely reachable bool context::is_reachable(pob &n) { @@ -2999,8 +3007,11 @@ void context::predecessor_eh() } } -//this processes a goal and creates sub-goal -lbool context::expand_pob(pob& n) +/// Checks whether the given pob is reachable +/// returns l_true if reachable, l_false if unreachable +/// returns l_undef if reachability cannot be decided +/// out contains new pobs to add to the queue in case the result is l_undef +lbool context::expand_pob(pob& n, pob_ref_buffer &out) { pob::on_expand_event _evt(n); TRACE ("spacer", @@ -3049,11 +3060,12 @@ lbool context::expand_pob(pob& n) IF_VERBOSE (1, verbose_stream () << " K " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); - + n.inc_level(); + out.push_back(&n); return l_false; } - if (n.pt().is_qblocked(n)) { + if (/* XXX noop */ n.pt().is_qblocked(n)) { STRACE("spacer.expand-add", tout << "This pob can be blocked by instantiation\n";); } @@ -3099,10 +3111,8 @@ lbool context::expand_pob(pob& n) // move derivation over to the next obligation next->set_derivation (deriv.detach()); - // remove the current node from the queue if it is at the top - if (m_pob_queue.top() == &n) { m_pob_queue.pop(); } - - m_pob_queue.push (*next); + // this is the new node to add + out.push_back (next); } } @@ -3114,7 +3124,9 @@ lbool context::expand_pob(pob& n) } // create a child of n - VERIFY(create_children (n, *r, mev, reach_pred_used)); + + out.push_back(&n); + VERIFY(create_children (n, *r, mev, reach_pred_used, out)); IF_VERBOSE(1, verbose_stream () << " U " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); @@ -3165,7 +3177,14 @@ lbool context::expand_pob(pob& n) if (v && get_params().spacer_use_lemma_as_cti()) { n.new_post (mk_and(lemma->get_cube())); n.set_farkas_generalizer (false); + // XXX hack while refactoring is in progress + n.clean(); } + + // schedule the node to be placed back in the queue + n.inc_level(); + out.push_back(&n); + CASSERT("spacer", n.level() == 0 || check_invariant(n.level()-1)); @@ -3187,17 +3206,22 @@ lbool context::expand_pob(pob& n) // do not trust reach_pred_used for (unsigned i = 0, sz = reach_pred_used.size(); i < sz; ++i) { reach_pred_used[i] = false; } - has_new_child = create_children(n,*r,mev,reach_pred_used); + has_new_child = create_children(n,*r,mev,reach_pred_used, out); } IF_VERBOSE(1, verbose_stream() << " UNDEF " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); - if (has_new_child) { return l_undef; } + if (has_new_child) { + // ensure that n is placed back in the queue + out.push_back(&n); + return l_undef; + } // -- failed to create a child, bump weakness and repeat // -- the recursion is bounded by the levels of weakness supported + SASSERT(out.empty()); n.bump_weakness(); - return expand_pob(n); + return expand_pob(n, out); } TRACE("spacer", tout << "unknown state: " << mk_and(cube) << "\n";); throw unknown_exception(); @@ -3372,7 +3396,8 @@ reach_fact *context::mk_reach_fact (pob& n, model_evaluator_util &mev, */ bool context::create_children(pob& n, datalog::rule const& r, model_evaluator_util &mev, - const vector &reach_pred_used) + const vector &reach_pred_used, + pob_ref_buffer &out) { scoped_watch _w_ (m_create_children_watch); @@ -3486,7 +3511,7 @@ bool context::create_children(pob& n, datalog::rule const& r, if (m_weak_abs && (!mev.is_true(T) || !mev.is_true(phi))) { kid->reset_derivation(); } - m_pob_queue.push (*kid); + out.push_back(kid); m_stats.m_num_queries++; return true; } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 5eedb1736..475a7472c 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -53,6 +53,7 @@ typedef obj_map decl2rel; class pob; typedef ref pob_ref; typedef sref_vector pob_ref_vector; +typedef sref_buffer pob_ref_buffer; class reach_fact; typedef ref reach_fact_ref; @@ -788,16 +789,18 @@ class context { // Functions used by search. lbool solve_core (unsigned from_lvl = 0); + bool is_requeue(pob &n); bool check_reachability (); bool propagate(unsigned min_prop_lvl, unsigned max_prop_lvl, unsigned full_prop_lvl); bool is_reachable(pob &n); - lbool expand_pob(pob &n); + lbool expand_pob(pob &n, pob_ref_buffer &out); reach_fact *mk_reach_fact (pob& n, model_evaluator_util &mev, datalog::rule const& r); bool create_children(pob& n, datalog::rule const& r, model_evaluator_util &model, - const vector& reach_pred_used); + const vector& reach_pred_used, + pob_ref_buffer &out); expr_ref mk_sat_answer(); expr_ref mk_unsat_answer() const; From d1a7c0ceb053c3c29e44e66414a43bfa940c48da Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 14:55:28 -0700 Subject: [PATCH 109/364] Remove a print --- src/muz/spacer/spacer_unsat_core_learner.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index bb50cc5c7..a6ab01870 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -76,7 +76,6 @@ void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) // TODO: remove duplicates from unsat core? - verbose_stream() << std::endl; // move all lemmas into vector for (expr* const* it = m_unsat_core.begin(); it != m_unsat_core.end(); ++it) { From 0fe5e6c2a6a581837bf7f899df9e3263dcb6f392 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 17 May 2018 17:01:02 -0700 Subject: [PATCH 110/364] Fix handling of complex literals in hypothesis_reducer In Z3, an arbitrary, even propositional, formula can be a literal. This requires careful handling of restructuring of unit resolution. --- src/muz/spacer/spacer_proof_utils.cpp | 65 ++++++++++++++------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index e8f56f200..3ca9b0c25 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -432,51 +432,56 @@ proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) { } proof* arg0 = args[0]; + app *fact0 = to_app(m.get_fact(arg0)); + ptr_buffer pf_args; + ptr_buffer pf_fact; + pf_args.push_back(arg0); - // BUG: I guess this shouldn't work with quantifiers (since they - // are not apps) - // AG: who is "I"? What is the bug? - app *fact = to_app(m.get_fact(arg0)); - ptr_buffer cls; - if (m.is_or(fact)) { - for (unsigned i = 0, sz = fact->get_num_args(); i < sz; ++i) - cls.push_back(fact->get_arg(i)); - } - else - cls.push_back(fact); - - // construct the new resolvent - ptr_buffer new_fact; - bool found; - - // -- find all literals that are resolved on - // XXX quadratic implementation - for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { - found = false; - for (unsigned j = 1; j < args.size(); ++j) { - if (m.is_complement(cls.get(i), m.get_fact(args[j]))) { - found = true; - pf_args.push_back(args[j]); - break; - } + // check if fact0 can be resolved as a unit + // this is required for handling literals with OR + for (unsigned j = 1; j < args.size(); ++j) { + if (m.is_complement(fact0, m.get_fact(args[j]))) { + pf_args.push_back(args[j]); + break; } - if (!found) new_fact.push_back(cls.get(i)); } - SASSERT(new_fact.size() + pf_args.size() - 1 == cls.size()); + + // if failed to find a resolvent, and the fact is a disjunction, + // attempt to resolve each disjunct + if (pf_args.size() == 1 && m.is_or(fact0)) { + ptr_buffer cls; + for (unsigned i = 0, sz = fact0->get_num_args(); i < sz; ++i) + cls.push_back(fact0->get_arg(i)); + + // -- find all literals that are resolved on + // XXX quadratic implementation + for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { + bool found = false; + for (unsigned j = 1; j < args.size(); ++j) { + if (m.is_complement(cls.get(i), m.get_fact(args[j]))) { + found = true; + pf_args.push_back(args[j]); + break; + } + } + if (!found) pf_fact.push_back(cls.get(i)); + } + SASSERT(pf_fact.size() + pf_args.size() - 1 == cls.size()); + } // unit resolution got reduced to noop if (pf_args.size() == 1) { - // XXX just in case + // XXX pin just in case m_pinned.push_back(arg0); return arg0; } // make unit resolution proof step expr_ref tmp(m); - tmp = mk_or(m, new_fact.size(), new_fact.c_ptr()); + tmp = mk_or(m, pf_fact.size(), pf_fact.c_ptr()); proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); m_pinned.push_back(res); return res; From 7931bd1dfca80c92f27aa7351f1b8c1d9581764f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 May 2018 13:06:47 -0700 Subject: [PATCH 111/364] updates to mbqi Signed-off-by: Nikolaj Bjorner --- src/ast/ast_util.cpp | 7 + src/ast/ast_util.h | 2 + src/muz/spacer/spacer_util.cpp | 232 ++++++++++++------------ src/qe/qe_arith.cpp | 5 +- src/qe/qe_mbp.cpp | 313 ++++++++++++++++++++++++++++----- src/qe/qe_mbp.h | 16 +- 6 files changed, 402 insertions(+), 173 deletions(-) diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index 92a539d88..e2783051a 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -307,6 +307,13 @@ void flatten_and(expr* fml, expr_ref_vector& result) { flatten_and(result); } +void flatten_and(expr_ref& fml) { + expr_ref_vector fmls(fml.get_manager()); + fmls.push_back(fml); + flatten_and(fmls); + fml = mk_and(fmls); +} + void flatten_or(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index 446854f5e..12c11c141 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -150,6 +150,8 @@ expr_ref mk_distinct(expr_ref_vector const& args); void flatten_and(expr_ref_vector& result); +void flatten_and(expr_ref& fml); + void flatten_and(expr* fml, expr_ref_vector& result); void flatten_or(expr_ref_vector& result); diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index b4e5e7710..030f2a386 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -479,9 +479,8 @@ namespace spacer { th_rewriter rw (m); TRACE ("spacer_mbp", tout << "Before projection:\n"; - tout << mk_pp (fml, m) << "\n"; - tout << "Vars:\n"; - for (app* v : vars) tout << mk_pp(v, m) << "\n";); + tout << fml << "\n"; + tout << "Vars:\n" << vars;); { // Ensure that top-level AND of fml is flat @@ -498,47 +497,40 @@ namespace spacer { expr_ref bval (m); while (true) { - params_ref p; - qe_lite qe(m, p, false); - qe (vars, fml); - rw (fml); - - TRACE ("spacer_mbp", - tout << "After qe_lite:\n"; - tout << mk_pp (fml, m) << "\n"; - tout << "Vars:\n"; - for (unsigned i = 0; i < vars.size(); ++i) { - tout << mk_pp(vars.get (i), m) << "\n"; - } - ); + params_ref p; + qe_lite qe(m, p, false); + qe (vars, fml); + rw (fml); + + TRACE ("spacer_mbp", + tout << "After qe_lite:\n"; + tout << mk_pp (fml, m) << "\n"; + tout << "Vars:\n" << vars;); + SASSERT (!m.is_false (fml)); - bool has_bool_vars = false; // sort out vars into bools, arith (int/real), and arrays - for (unsigned i = 0; i < vars.size (); i++) { - if (m.is_bool (vars.get (i))) { + for (app* v : vars) { + if (m.is_bool (v)) { // obtain the interpretation of the ith var using model completion - VERIFY (M->eval (vars.get (i), bval, true)); - bool_sub.insert (vars.get (i), bval); - has_bool_vars = true; - } else if (arr_u.is_array(vars.get(i))) { - array_vars.push_back (vars.get (i)); + VERIFY (M->eval (v, bval, true)); + bool_sub.insert (v, bval); + } else if (arr_u.is_array(v)) { + array_vars.push_back (v); } else { - SASSERT (ari_u.is_int (vars.get (i)) || ari_u.is_real (vars.get (i))); - arith_vars.push_back (vars.get (i)); + SASSERT (ari_u.is_int (v) || ari_u.is_real (v)); + arith_vars.push_back (v); } } // substitute Booleans - if (has_bool_vars) { + if (!bool_sub.empty()) { bool_sub (fml); // -- bool_sub is not simplifying rw (fml); SASSERT (!m.is_false (fml)); - TRACE ("spacer_mbp", - tout << "Projected Booleans:\n" << mk_pp (fml, m) << "\n"; - ); + TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); bool_sub.reset (); } @@ -571,40 +563,31 @@ namespace spacer { // project reals and ints if (!arith_vars.empty ()) { - TRACE ("spacer_mbp", - tout << "Arith vars:\n"; - for (unsigned i = 0; i < arith_vars.size (); ++i) { - tout << mk_pp (arith_vars.get (i), m) << "\n"; - } - ); - + TRACE ("spacer_mbp", tout << "Arith vars:\n" << arith_vars;); + // XXX Does not seem to have an effect // qe_lite qe(m); // qe (arith_vars, fml); // TRACE ("spacer_mbp", // tout << "After second qelite: " << // mk_pp (fml, m) << "\n";); - - if (use_native_mbp) { - qe::mbp mbp (m); - expr_ref_vector fmls(m); - flatten_and (fml, fmls); - - mbp (true, arith_vars, *M.get (), fmls); - fml = mk_and (fmls); - SASSERT (arith_vars.empty ()); - } else { + + if (use_native_mbp) { + qe::mbp mbp (m); + expr_ref_vector fmls(m); + flatten_and (fml, fmls); + + mbp (true, arith_vars, *M.get (), fmls); + fml = mk_and (fmls); + SASSERT (arith_vars.empty ()); + } else { scoped_no_proof _sp (m); qe::arith_project (*M.get (), arith_vars, fml); } - + TRACE ("spacer_mbp", - tout << "Projected arith vars:\n" << mk_pp (fml, m) << "\n"; - tout << "Remaining arith vars:\n"; - for (unsigned i = 0; i < arith_vars.size (); i++) { - tout << mk_pp (arith_vars.get (i), m) << "\n"; - } - ); + tout << "Projected arith vars:\n" << mk_pp (fml, m) << "\n"; + tout << "Remaining arith vars:\n" << arith_vars << "\n";); SASSERT (!m.is_false (fml)); } @@ -632,8 +615,9 @@ namespace spacer { ); vars.reset (); - if (dont_sub && !arith_vars.empty ()) - { vars.append(arith_vars); } + if (dont_sub && !arith_vars.empty ()) { + vars.append(arith_vars); + } } @@ -713,15 +697,15 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) } namespace { -class implicant_picker { + class implicant_picker { model_evaluator_util &m_mev; ast_manager &m; arith_util m_arith; - + expr_ref_vector m_todo; expr_mark m_visited; - + void add_literal (expr *e, expr_ref_vector &out) { SASSERT (m.is_bool (e)); @@ -763,37 +747,37 @@ class implicant_picker { out.push_back (res); } - void process_app(app *a, expr_ref_vector &out) - { - if (m_visited.is_marked(a)) { return; } + void process_app(app *a, expr_ref_vector &out) + { + if (m_visited.is_marked(a)) { return; } SASSERT (m.is_bool (a)); expr_ref v(m); m_mev.eval (a, v, false); - - if (!m.is_true(v) && !m.is_false(v)) { return; } - + + if (!m.is_true(v) && !m.is_false(v)) { return; } + expr *na, *f1, *f2, *f3; - + if (a->get_family_id() != m.get_basic_family_id()) - { add_literal(a, out); } + { add_literal(a, out); } else if (is_uninterp_const(a)) - { add_literal(a, out); } + { add_literal(a, out); } else if (m.is_not(a, na) && m.is_not(na, na)) - { m_todo.push_back(na); } + { m_todo.push_back(na); } else if (m.is_not(a, na)) - { m_todo.push_back(na); } + { m_todo.push_back(na); } else if (m.is_distinct(a)) { if (m.is_false(v)) m_todo.push_back (qe::project_plugin::pick_equality(m, *m_mev.get_model(), a)); else if (a->get_num_args() == 2) - { add_literal(a, out); } + { add_literal(a, out); } else m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), a->get_args())); - } else if (m.is_and(a)) { + } else if (m.is_and(a)) { if (m.is_true(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } + { m_todo.append(a->get_num_args(), a->get_args()); } else if (m.is_false(v)) { for (unsigned i = 0, sz = a->get_num_args (); i < sz; ++i) { if (m_mev.is_false(a->get_arg(i))) { @@ -802,9 +786,9 @@ class implicant_picker { } } } - } else if (m.is_or(a)) { + } else if (m.is_or(a)) { if (m.is_false(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } + { m_todo.append(a->get_num_args(), a->get_args()); } else if (m.is_true(v)) { for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { if (m_mev.is_true(a->get_arg(i))) { @@ -813,52 +797,52 @@ class implicant_picker { } } } - } else if (m.is_iff(a, f1, f2) || m.is_eq(a, f1, f2) || - (m.is_true(v) && m.is_not(a, na) && m.is_xor (na, f1, f2))) { + } else if (m.is_iff(a, f1, f2) || m.is_eq(a, f1, f2) || + (m.is_true(v) && m.is_not(a, na) && m.is_xor (na, f1, f2))) { if (!m.are_equal(f1, f2) && !m.are_distinct(f1, f2)) { if (m.is_bool(f1) && (!is_uninterp_const(f1) || !is_uninterp_const(f2))) - { m_todo.append(a->get_num_args(), a->get_args()); } + { m_todo.append(a->get_num_args(), a->get_args()); } else - { add_literal(a, out); } + { add_literal(a, out); } } - } else if (m.is_ite(a, f1, f2, f3)) { - if (m.are_equal(f2, f3)) { m_todo.push_back(f2); } + } else if (m.is_ite(a, f1, f2, f3)) { + if (m.are_equal(f2, f3)) { m_todo.push_back(f2); } else if (m_mev.is_true (f2) && m_mev.is_true (f3)) { - m_todo.push_back(f2); - m_todo.push_back(f3); - } else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { - m_todo.push_back(f2); - m_todo.push_back(f3); - } else if (m_mev.is_true(f1)) { - m_todo.push_back(f1); - m_todo.push_back(f2); - } else if (m_mev.is_false(f1)) { - m_todo.push_back(f1); - m_todo.push_back(f3); + m_todo.push_back(f2); + m_todo.push_back(f3); + } else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { + m_todo.push_back(f2); + m_todo.push_back(f3); + } else if (m_mev.is_true(f1)) { + m_todo.push_back(f1); + m_todo.push_back(f2); + } else if (m_mev.is_false(f1)) { + m_todo.push_back(f1); + m_todo.push_back(f3); } - } else if (m.is_xor(a, f1, f2)) - { m_todo.append(a->get_num_args(), a->get_args()); } + } else if (m.is_xor(a, f1, f2)) + { m_todo.append(a->get_num_args(), a->get_args()); } else if (m.is_implies(a, f1, f2)) { if (m.is_true (v)) { - if (m_mev.is_true(f2)) { m_todo.push_back(f2); } - else if (m_mev.is_false(f1)) { m_todo.push_back(f1); } + if (m_mev.is_true(f2)) { m_todo.push_back(f2); } + else if (m_mev.is_false(f1)) { m_todo.push_back(f1); } } else if (m.is_false(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } - } else if (m.is_true(a) || m.is_false(a)) { /* nothing */ } + { m_todo.append(a->get_num_args(), a->get_args()); } + } else if (m.is_true(a) || m.is_false(a)) { /* nothing */ } else { verbose_stream () << "Unexpected expression: " << mk_pp(a, m) << "\n"; UNREACHABLE(); } } - - void pick_literals(expr *e, expr_ref_vector &out) - { + + void pick_literals(expr *e, expr_ref_vector &out) + { SASSERT(m_todo.empty()); - if (m_visited.is_marked(e)) { return; } + if (m_visited.is_marked(e)) { return; } SASSERT(is_app(e)); - + m_todo.push_back(e); do { app *a = to_app(m_todo.back()); @@ -867,31 +851,31 @@ class implicant_picker { m_visited.mark(a, true); } while (!m_todo.empty()); } - - bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) - { + + bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) + { m_visited.reset(); expr_ref e(m); e = mk_and (in); bool is_true = m_mev.is_true (e); - + for (unsigned i = 0, sz = in.size (); i < sz; ++i) { if (is_true || m_mev.is_true(in.get(i))) - { pick_literals(in.get(i), out); } + { pick_literals(in.get(i), out); } } - + m_visited.reset (); return is_true; } - + public: implicant_picker (model_evaluator_util &mev) : m_mev (mev), m (m_mev.get_ast_manager ()), m_arith(m), m_todo(m) {} - + void operator() (expr_ref_vector &in, expr_ref_vector& out) {pick_implicant (in, out);} }; - } +} void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, expr_ref_vector &res) @@ -1144,6 +1128,7 @@ struct adhoc_rewriter_rpp : public default_rewriter_cfg { } }; + mk_epp::mk_epp(ast *t, ast_manager &m, unsigned indent, unsigned num_vars, char const * var_prefix) : mk_pp (t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { @@ -1189,14 +1174,13 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { index_term_finder (ast_manager &mgr, app* v, expr_ref_vector &res) : m(mgr), m_array (m), m_var (v, m), m_res (res) {} void operator() (var *n) {} void operator() (quantifier *n) {} - void operator() (app *n) - { + void operator() (app *n) { expr *e1, *e2; if (m_array.is_select (n) && n->get_arg (1) != m_var) { m_res.push_back (n->get_arg (1)); - } else if (m.is_eq(n, e1, e2)) { - if (e1 == m_var) { m_res.push_back(e2); } - else if (e2 == m_var) { m_res.push_back(e1); } + } else if (m.is_eq(n, e1, e2)) { + if (e1 == m_var) { m_res.push_back(e2); } + else if (e2 == m_var) { m_res.push_back(e1); } } } }; @@ -1204,29 +1188,29 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { bool mbqi_project_var (model_evaluator_util &mev, app* var, expr_ref &fml) { ast_manager &m = fml.get_manager (); - + expr_ref val(m); mev.eval (var, val, false); - + TRACE ("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp (var, m) << "\n" << "fml: " << mk_pp (fml, m) << "\n";); expr_ref_vector terms (m); index_term_finder finder (m, var, terms); for_each_expr (finder, fml); - - + + TRACE ("mbqi_project_verbose", tout << "terms:\n"; for (unsigned i = 0, e = terms.size (); i < e; ++i) tout << i << ": " << mk_pp (terms.get (i), m) << "\n"; ); - - for (unsigned i = 0, e = terms.size(); i < e; ++i) { + + for (unsigned i = 0, e = terms.size(); i < e; ++i) { expr* term = terms.get (i); expr_ref tval (m); mev.eval (term, tval, false); - + TRACE ("mbqi_project_verbose", tout << "term: " << mk_pp (term, m) << " tval: " << mk_pp (tval, m) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 6ea4118a0..969d22a81 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -352,10 +352,7 @@ namespace qe { real_vars.push_back(tids.find(v)); } else { - if (i != j) { - vars[j] = v; - } - ++j; + vars[j++] = v; } } vars.resize(j); diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 432569ff9..0b4574951 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -18,17 +18,20 @@ Revision History: --*/ +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/occurs.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/expr_functors.h" +#include "ast/for_each_expr.h" +#include "ast/scoped_proof.h" #include "qe/qe_mbp.h" #include "qe/qe_arith.h" #include "qe/qe_arrays.h" #include "qe/qe_datatypes.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/ast_pp.h" -#include "ast/ast_util.h" -#include "ast/rewriter/th_rewriter.h" -#include "model/model_v2_pp.h" -#include "ast/expr_functors.h" -#include "ast/for_each_expr.h" +#include "qe/qe_lite.h" +#include "model/model_pp.h" #include "model/model_evaluator.h" @@ -122,11 +125,17 @@ void project_plugin::push_back(expr_ref_vector& lits, expr* e) { class mbp::impl { - ast_manager& m; - th_rewriter m_rw; + ast_manager& m; + params_ref m_params; + th_rewriter m_rw; ptr_vector m_plugins; - expr_mark m_visited; - expr_mark m_bool_visited; + expr_mark m_visited; + expr_mark m_bool_visited; + + // parameters + bool m_reduce_all_selects; + bool m_native_mbp; + bool m_dont_sub; void add_plugin(project_plugin* p) { family_id fid = p->get_family_id(); @@ -258,42 +267,122 @@ class mbp::impl { return found_bool; } - void project_bools(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + void project_bools(model& mdl, app_ref_vector& vars, expr_ref_vector& fmls) { expr_safe_replace sub(m); expr_ref val(m); + model_evaluator eval(mdl, m_params); + eval.set_model_completion(true); unsigned j = 0; for (unsigned i = 0; i < vars.size(); ++i) { app* var = vars[i].get(); if (m.is_bool(var)) { - VERIFY(model.eval(var, val)); - sub.insert(var, val); + sub.insert(var, eval(var)); } else { - if (j != i) { - vars[j] = vars[i].get(); - } - ++j; + vars[j++] = var; } } - if (j != vars.size()) { - vars.resize(j); - j = 0; - for (unsigned i = 0; i < fmls.size(); ++i) { - sub(fmls[i].get(), val); - m_rw(val); - if (!m.is_true(val)) { - TRACE("qe", tout << mk_pp(fmls[i].get(), m) << " -> " << val << "\n";); - fmls[i] = val; - if (j != i) { - fmls[j] = fmls[i].get(); - } - ++j; - } - } - if (j != fmls.size()) { - fmls.resize(j); + if (j == vars.size()) { + return; + } + vars.shrink(j); + j = 0; + for (unsigned i = 0; i < fmls.size(); ++i) { + expr* fml = fmls[i].get(); + sub(fml, val); + m_rw(val); + if (!m.is_true(val)) { + TRACE("qe", tout << mk_pp(fml, m) << " -> " << val << "\n";); + fmls[j++] = val; } } + fmls.shrink(j); + } + + + void subst_vars(model_evaluator& eval, app_ref_vector const& vars, expr_ref& fml) { + expr_safe_replace sub (m); + for (app * v : vars) { + sub.insert(v, eval(v)); + } + sub(fml); + } + + struct index_term_finder { + ast_manager& m; + array_util m_array; + app_ref m_var; + expr_ref_vector& m_res; + + index_term_finder (ast_manager &mgr, app* v, expr_ref_vector &res): + m(mgr), m_array (m), m_var (v, m), m_res (res) {} + + void operator() (var *n) {} + void operator() (quantifier *n) {} + void operator() (app *n) { + expr *e1, *e2; + if (m_array.is_select (n)) { + for (unsigned i = 1; i < n->get_num_args(); ++i) { + expr * arg = n->get_arg(i); + if (m.get_sort(arg) == m.get_sort(m_var) && arg != m_var) + m_res.push_back (arg); + } + } + else if (m.is_eq(n, e1, e2)) { + if (e1 == m_var) + m_res.push_back(e2); + else if (e2 == m_var) + m_res.push_back(e1); + } + } + }; + + bool project_var (model_evaluator& eval, app* var, expr_ref& fml) { + expr_ref val = eval(var); + + TRACE ("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp (var, m) << "\n" << "fml: " << fml << "\n";); + expr_ref_vector terms (m); + index_term_finder finder (m, var, terms); + for_each_expr (finder, fml); + + TRACE ("mbqi_project_verbose", tout << "terms:\n" << terms;); + + for (expr * term : terms) { + expr_ref tval = eval(term); + + TRACE ("mbqi_project_verbose", tout << "term: " << mk_pp (term, m) << " tval: " << tval << " val: " << val << "\n";); + + // -- if the term does not contain an occurrence of var + // -- and is in the same equivalence class in the model + if (tval == val && !occurs (var, term)) { + TRACE ("mbqi_project", + tout << "MBQI: replacing " << mk_pp (var, m) << " with " << mk_pp (term, m) << "\n";); + expr_safe_replace sub(m); + sub.insert (var, term); + sub (fml); + return true; + } + } + + TRACE ("mbqi_project", + tout << "MBQI: failed to eliminate " << mk_pp (var, m) << " from " << fml << "\n";); + + return false; + } + + void project_vars (model &M, app_ref_vector &vars, expr_ref &fml) { + model_evaluator eval(M); + eval.set_model_completion(false); + // -- evaluate to initialize eval cache + (void) eval (fml); + unsigned j = 0; + for (unsigned i = 0; i < vars.size (); ++i) { + app* v = vars.get(i); + if (!project_var (eval, v, fml)) { + vars[j++] = v; + } + } + vars.shrink(j); } public: @@ -426,7 +515,7 @@ public: m_bool_visited.reset(); } - impl(ast_manager& m):m(m), m_rw(m) { + impl(ast_manager& m, params_ref const& p):m(m), m_params(p), m_rw(m) { add_plugin(alloc(arith_project_plugin, m)); add_plugin(alloc(datatype_project_plugin, m)); add_plugin(alloc(array_project_plugin, m)); @@ -436,13 +525,20 @@ public: std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); } + void updt_params(params_ref const& p) { + m_params.append(p); + m_reduce_all_selects = m_params.get_bool("reduce_all_selects", false); + m_native_mbp = m_params.get_bool("native_mbp", false); + m_dont_sub = m_params.get_bool("dont_sub", false); + } + void preprocess_solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { extract_literals(model, fmls); bool change = true; while (change && !vars.empty()) { change = solve(model, vars, fmls); - for (unsigned i = 0; i < m_plugins.size(); ++i) { - if (m_plugins[i] && m_plugins[i]->solve(model, vars, fmls)) { + for (auto* p : m_plugins) { + if (p && p->solve(model, vars, fmls)) { change = true; } } @@ -451,8 +547,8 @@ public: bool validate_model(model& model, expr_ref_vector const& fmls) { expr_ref val(m); - for (unsigned i = 0; i < fmls.size(); ++i) { - VERIFY(model.eval(fmls[i], val) && m.is_true(val)); + for (expr * f : fmls) { + VERIFY(model.eval(f, val) && m.is_true(val)); } return true; } @@ -469,8 +565,7 @@ public: while (progress && !vars.empty() && !fmls.empty()) { app_ref_vector new_vars(m); progress = false; - for (unsigned i = 0; i < m_plugins.size(); ++i) { - project_plugin* p = m_plugins[i]; + for (project_plugin * p : m_plugins) { if (p) { (*p)(model, vars, fmls); } @@ -517,28 +612,158 @@ public: TRACE("qe", tout << vars << " " << fmls << "\n";); } + void do_qe_lite(app_ref_vector& vars, expr_ref& fml) { + qe_lite qe(m, m_params, false); + qe (vars, fml); + m_rw (fml); + TRACE ("qe", tout << "After qe_lite:\n" << fml << "\n" << "Vars:\n" << vars;); + SASSERT (!m.is_false (fml)); + } + + void do_qe_bool(model& mdl, app_ref_vector& vars, expr_ref& fml) { + expr_ref_vector fmls(m); + fmls.push_back(fml); + project_bools(mdl, vars, fmls); + fml = mk_and(fmls); + } + + void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) { + TRACE ("qe", tout << "Before projection:\n" << fml << "\n" << "Vars:\n" << vars;); + + model_evaluator eval(mdl, m_params); + eval.set_model_completion(true); + app_ref_vector arith_vars (m); + app_ref_vector array_vars (m); + array_util arr_u (m); + arith_util ari_u (m); + + flatten_and(fml); + + do_qe_lite(vars, fml); + + while (!vars.empty()) { + + do_qe_bool(mdl, vars, fml); + + // sort out vars into bools, arith (int/real), and arrays + for (app* v : vars) { + if (arr_u.is_array(v)) { + array_vars.push_back (v); + } + else if (ari_u.is_int (v) || ari_u.is_real (v)) { + arith_vars.push_back (v); + } + else { + NOT_IMPLEMENTED_YET(); + } + } + + TRACE ("qe", tout << "Array vars:\n" << array_vars;); + + vars.reset (); + + // project arrays + if (!array_vars.empty()) { + NOT_IMPLEMENTED_YET(); + // qe::array_project (mdl, array_vars, fml, vars, m_reduce_all_selects); + SASSERT (array_vars.empty ()); + m_rw (fml); + SASSERT (!m.is_false (fml)); + } + + TRACE ("qe", + tout << "extended model:\n"; + model_pp (tout, mdl); + tout << "Auxiliary variables of index and value sorts:\n"; + tout << vars; + ); + + } + // project reals and ints + if (!arith_vars.empty ()) { + TRACE ("qe", tout << "Arith vars:\n" << arith_vars;); + + if (m_native_mbp) { + expr_ref_vector fmls(m); + flatten_and (fml, fmls); + + (*this)(true, arith_vars, mdl, fmls); + fml = mk_and (fmls); + SASSERT (arith_vars.empty ()); + } + else { + NOT_IMPLEMENTED_YET(); + // qe::arith_project (mdl, arith_vars, fml); + } + + TRACE ("qe", + tout << "Projected arith vars:\n" << fml << "\n"; + tout << "Remaining arith vars:\n" << arith_vars << "\n";); + SASSERT (!m.is_false (fml)); + } + + if (!arith_vars.empty ()) { + project_vars (mdl, arith_vars, fml); + } + + // substitute any remaining arith vars + if (!m_dont_sub && !arith_vars.empty ()) { + subst_vars (eval, arith_vars, fml); + TRACE ("qe", tout << "After substituting remaining arith vars:\n" << fml << "\n";); + // an extra round of simplification because subst_vars is not simplifying + m_rw(fml); + arith_vars.reset(); + } + + SASSERT(eval.is_true(fml)); + + vars.reset (); + vars.append(arith_vars); + } + }; -mbp::mbp(ast_manager& m) { - m_impl = alloc(impl, m); +mbp::mbp(ast_manager& m, params_ref const& p) { + scoped_no_proof _sp (m); + m_impl = alloc(impl, m, p); } mbp::~mbp() { dealloc(m_impl); } + +void mbp::updt_params(params_ref const& p) { + m_impl->updt_params(p); +} + +void mbp::get_param_descrs(param_descrs & r) { + r.insert("reduce_all_selects", CPK_BOOL, "(default: false) reduce selects"); + r.insert("native_mbp", CPK_BOOL, "(default: false) switch between native and spacer tailored mbp"); + r.insert("dont_sub", CPK_BOOL, "(default: false) disable substitution of values for free variables"); +} void mbp::operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls) { + scoped_no_proof _sp (fmls.get_manager()); (*m_impl)(force_elim, vars, mdl, fmls); } +void mbp::spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) { + scoped_no_proof _sp (fml.get_manager()); + m_impl->spacer(vars, mdl, fml); +} + void mbp::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + scoped_no_proof _sp (fmls.get_manager()); m_impl->preprocess_solve(model, vars, fmls); } void mbp::extract_literals(model& model, expr_ref_vector& lits) { + scoped_no_proof _sp (lits.get_manager()); m_impl->extract_literals(model, lits); } + opt::inf_eps mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { + scoped_no_proof _sp (fmls.get_manager()); return m_impl->maximize(fmls, mdl, t, ge, gt); } diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index d1695843c..12b03791a 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -52,9 +52,13 @@ namespace qe { class impl; impl * m_impl; public: - mbp(ast_manager& m); + mbp(ast_manager& m, params_ref const& p = params_ref()); ~mbp(); + + void updt_params(params_ref const& p); + + static void get_param_descrs(param_descrs & r); /** \brief @@ -80,6 +84,16 @@ namespace qe { Maximize objective t under current model for constraints in fmls. */ opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); + + /** + \brief + Apply spacer friendly MBP. + Use parameters to control behavior. + - reduce_all_selects (false) + - native_mbp (false) - to be deprecated + - dont_sub (false) + */ + void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml); }; } From b39c532f19755d8850664b84c3957abc2a97ea68 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 18 May 2018 08:39:59 -0700 Subject: [PATCH 112/364] Order of methods in spacer_context.cpp --- src/muz/spacer/spacer_context.cpp | 1201 +++++++++++++++-------------- 1 file changed, 602 insertions(+), 599 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 48cbbd258..2709593ad 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -54,6 +54,581 @@ Notes: #include "smt/smt_solver.h" namespace spacer { +/// pob -- proof obligation +pob::pob (pob* parent, pred_transformer& pt, + unsigned level, unsigned depth, bool add_to_parent): + m_ref_count (0), + m_parent (parent), m_pt (pt), + m_post (m_pt.get_ast_manager ()), + m_binding(m_pt.get_ast_manager()), + m_new_post (m_pt.get_ast_manager ()), + m_level (level), m_depth (depth), + m_open (true), m_use_farkas (true), m_weakness(0), + m_blocked_lvl(0) { + if(add_to_parent && m_parent) { + m_parent->add_child(*this); + } +} + + +void pob::set_post(expr* post) { + app_ref_vector b(get_ast_manager()); + set_post(post, b); +} + +void pob::set_post(expr* post, app_ref_vector const &b) { + normalize(post, m_post, + m_pt.get_context().get_params().spacer_simplify_pob(), + m_pt.get_context().get_params().spacer_use_eqclass()); + + m_binding.reset(); + if (b.empty()) return; + + m_binding.append(b); + + std::sort (m_binding.c_ptr(), m_binding.c_ptr() + m_binding.size(), sk_lt_proc()); + + // skolemize implicit existential quantifier + ast_manager &m = get_ast_manager(); + app_ref_vector pinned(m); + + expr_safe_replace sub(m); + for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { + expr* e; + e = m_binding.get(i); + pinned.push_back (mk_zk_const (m, i, get_sort(e))); + sub.insert (e, pinned.back()); + } + sub(m_post); +} + +void pob::inherit(pob const &p) { + SASSERT(m_parent == p.m_parent); + SASSERT(&m_pt == &p.m_pt); + SASSERT(m_post == p.m_post); + SASSERT(!m_new_post); + + m_binding.reset(); + m_binding.append(p.m_binding); + + m_level = p.m_level; + m_depth = p.m_depth; + m_open = p.m_open; + m_use_farkas = p.m_use_farkas; + m_weakness = p.m_weakness; + + m_derivation = nullptr; +} + +void pob::clean () { + if(m_new_post) { + m_post = m_new_post; + m_new_post.reset(); + } +} + +void pob::close () { + if(!m_open) { return; } + + reset (); + m_open = false; + for (unsigned i = 0, sz = m_kids.size (); i < sz; ++i) + { m_kids [i]->close(); } +} + +void pob::get_skolems(app_ref_vector &v) { + for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { + expr* e; + e = m_binding.get(i); + v.push_back (mk_zk_const (get_ast_manager(), i, get_sort(e))); + } +} + + + +// ---------------- +// pob_queue + +pob* pob_queue::top () +{ + /// nothing in the queue + if (m_obligations.empty()) { return nullptr; } + /// top queue element is above max level + if (m_obligations.top()->level() > m_max_level) { return nullptr; } + /// top queue element is at the max level, but at a higher than base depth + if (m_obligations.top ()->level () == m_max_level && + m_obligations.top()->depth() > m_min_depth) { return nullptr; } + + /// there is something good in the queue + return m_obligations.top ().get (); +} + +void pob_queue::set_root(pob& root) +{ + m_root = &root; + m_max_level = root.level (); + m_min_depth = root.depth (); + reset(); +} + +pob_queue::~pob_queue() {} + +void pob_queue::reset() +{ + while (!m_obligations.empty()) { m_obligations.pop(); } + if (m_root) { m_obligations.push(m_root); } +} + +void pob_queue::push(pob &n) { + TRACE("pob_queue", + tout << "pob_queue::push(" << n.post()->get_id() << ")\n";); + m_obligations.push (&n); + n.get_context().new_pob_eh(&n); +} + +// ---------------- +// derivation + +derivation::derivation (pob& parent, datalog::rule const& rule, + expr *trans, app_ref_vector const &evars) : + m_parent (parent), + m_rule (rule), + m_premises (), + m_active (0), + m_trans (trans, m_parent.get_ast_manager ()), + m_evars (evars) {} + + + +void derivation::add_premise (pred_transformer &pt, + unsigned oidx, + expr* summary, + bool must, + const ptr_vector *aux_vars) +{m_premises.push_back (premise (pt, oidx, summary, must, aux_vars));} + + + +pob *derivation::create_first_child (model_evaluator_util &mev) +{ + if (m_premises.empty()) { return nullptr; } + m_active = 0; + return create_next_child(mev); +} + +pob *derivation::create_next_child (model_evaluator_util &mev) +{ + timeit _timer (is_trace_enabled("spacer_timeit"), + "spacer::derivation::create_next_child", + verbose_stream ()); + + ast_manager &m = get_ast_manager (); + expr_ref_vector summaries (m); + app_ref_vector vars (m); + + bool use_native_mbp = get_context ().use_native_mbp (); + bool ground = get_context ().use_ground_cti (); + // -- find first may premise + while (m_active < m_premises.size() && m_premises[m_active].is_must()) { + summaries.push_back (m_premises[m_active].get_summary ()); + vars.append (m_premises[m_active].get_ovars ()); + ++m_active; + } + if (m_active >= m_premises.size()) { return nullptr; } + + // -- update m_trans with the pre-image of m_trans over the must summaries + summaries.push_back (m_trans); + m_trans = mk_and (summaries); + summaries.reset (); + + if (!vars.empty()) { + timeit _timer1 (is_trace_enabled("spacer_timeit"), + "create_next_child::qproject1", + verbose_stream ()); + qe_project (m, vars, m_trans, mev.get_model (), true, use_native_mbp, !ground); + //qe::reduce_array_selects (*mev.get_model (), m_trans); + // remember variables that need to be existentially quantified + m_evars.append (vars); + } + + if (!mev.is_true (m_premises[m_active].get_summary())) { + IF_VERBOSE(1, verbose_stream() << "Summary unexpectendly not true\n";); + return nullptr; + } + + + // create the post condition by compute post-image over summaries + // that precede currently active premise + vars.reset (); + for (unsigned i = m_active + 1; i < m_premises.size(); ++i) { + summaries.push_back (m_premises [i].get_summary ()); + vars.append (m_premises [i].get_ovars ()); + } + summaries.push_back (m_trans); + + expr_ref post(m); + post = mk_and (summaries); + summaries.reset (); + if (!vars.empty()) { + timeit _timer2 (is_trace_enabled("spacer_timeit"), + "create_next_child::qproject2", + verbose_stream ()); + qe_project (m, vars, post, mev.get_model (), true, use_native_mbp, !ground); + //qe::reduce_array_selects (*mev.get_model (), post); + + // remember variables that need to be existentially quantified + m_evars.append (vars); + } + + get_manager ().formula_o2n (post.get (), post, + m_premises [m_active].get_oidx (), m_evars.empty()); + + + /* The level and depth are taken from the parent, not the sibling. + The reasoning is that the sibling has not been checked before, + and lower level is a better starting point. */ + pob *n = m_premises[m_active].pt().mk_pob(&m_parent, + prev_level (m_parent.level ()), + m_parent.depth (), post, m_evars); + + IF_VERBOSE (1, verbose_stream () + << "\n\tcreate_child: " << n->pt ().head ()->get_name () + << " (" << n->level () << ", " << n->depth () << ") " + << (n->use_farkas_generalizer () ? "FAR " : "SUB ") + << n->post ()->get_id (); + verbose_stream().flush ();); + return n; +} + +pob *derivation::create_next_child () +{ + if (m_active + 1 >= m_premises.size()) { return nullptr; } + + bool use_native_mbp = get_context ().use_native_mbp (); + bool ground = get_context ().use_ground_cti (); + + // update the summary of the active node to some must summary + + // construct a new model consistent with the must summary of m_active premise + pred_transformer &pt = m_premises[m_active].pt (); + model_ref model; + + ast_manager &m = get_ast_manager (); + manager &pm = get_manager (); + + expr_ref_vector summaries (m); + + for (unsigned i = m_active + 1; i < m_premises.size (); ++i) + { summaries.push_back(m_premises [i].get_summary()); } + + // -- orient transition relation towards m_active premise + expr_ref active_trans (m); + pm.formula_o2n (m_trans, active_trans, + m_premises[m_active].get_oidx (), false); + summaries.push_back (active_trans); + + // if not true, bail out, the must summary of m_active is not strong enough + // this is possible if m_post was weakened for some reason + if (!pt.is_must_reachable(mk_and(summaries), &model)) { return nullptr; } + + model_evaluator_util mev (m); + mev.set_model (*model); + // find must summary used + + reach_fact *rf = pt.get_used_reach_fact (mev, true); + + // get an implicant of the summary + expr_ref_vector u(m), lits (m); + u.push_back (rf->get ()); + compute_implicant_literals (mev, u, lits); + expr_ref v(m); + v = mk_and (lits); + + // XXX The summary is not used by anyone after this point + m_premises[m_active].set_summary (v, true, &(rf->aux_vars ())); + + + /** HACK: needs a rewrite + * compute post over the new must summary this must be done here + * because the must summary is currently described over new + * variables. However, we store it over old-variables, but we do + * not update the model. So we must get rid of all of the + * new-variables at this point. + */ + { + pred_transformer &pt = m_premises[m_active].pt (); + app_ref_vector vars (m); + + summaries.reset (); + summaries.push_back (v); + summaries.push_back (active_trans); + m_trans = mk_and (summaries); + + // variables to eliminate + vars.append (rf->aux_vars ().size (), rf->aux_vars ().c_ptr ()); + for (unsigned i = 0, sz = pt.head ()->get_arity (); i < sz; ++i) + { vars.push_back(m.mk_const(pm.o2n(pt.sig(i), 0))); } + + if (!vars.empty ()) { + qe_project (m, vars, m_trans, mev.get_model (), true, use_native_mbp, + !ground); + // keep track of implicitly quantified variables + m_evars.append (vars); + } + } + + m_active++; + + return create_next_child (mev); +} + +/// derivation::premise + +derivation::premise::premise (pred_transformer &pt, unsigned oidx, + expr *summary, bool must, + const ptr_vector *aux_vars) : + m_pt (pt), m_oidx (oidx), + m_summary (summary, pt.get_ast_manager ()), m_must (must), + m_ovars (pt.get_ast_manager ()) +{ + + ast_manager &m = m_pt.get_ast_manager (); + manager &sm = m_pt.get_manager (); + + unsigned sig_sz = m_pt.head ()->get_arity (); + for (unsigned i = 0; i < sig_sz; ++i) + { m_ovars.push_back(m.mk_const(sm.o2o(pt.sig(i), 0, m_oidx))); } + + if (aux_vars) + for (unsigned i = 0, sz = aux_vars->size (); i < sz; ++i) + { m_ovars.push_back(m.mk_const(sm.n2o(aux_vars->get(i)->get_decl(), m_oidx))); } +} + +derivation::premise::premise (const derivation::premise &p) : + m_pt (p.m_pt), m_oidx (p.m_oidx), m_summary (p.m_summary), m_must (p.m_must), + m_ovars (p.m_ovars) {} + +/// \brief Updated the summary. +/// The new summary is over n-variables. +void derivation::premise::set_summary (expr * summary, bool must, + const ptr_vector *aux_vars) +{ + ast_manager &m = m_pt.get_ast_manager (); + manager &sm = m_pt.get_manager (); + unsigned sig_sz = m_pt.head ()->get_arity (); + + m_must = must; + sm.formula_n2o (summary, m_summary, m_oidx); + + m_ovars.reset (); + for (unsigned i = 0; i < sig_sz; ++i) + { m_ovars.push_back(m.mk_const(sm.o2o(m_pt.sig(i), 0, m_oidx))); } + + if (aux_vars) + for (unsigned i = 0, sz = aux_vars->size (); i < sz; ++i) + m_ovars.push_back (m.mk_const (sm.n2o (aux_vars->get (i)->get_decl (), + m_oidx))); +} + + +/// Lemma + +lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : + m_ref_count(0), m(manager), + m_body(body, m), m_cube(m), + m_zks(m), m_bindings(m), m_lvl(lvl), + m_pob(nullptr), m_external(false) { + SASSERT(m_body); + normalize(m_body, m_body); +} + +lemma::lemma(pob_ref const &p) : + m_ref_count(0), m(p->get_ast_manager()), + m_body(m), m_cube(m), + m_zks(m), m_bindings(m), m_lvl(p->level()), + m_pob(p), m_external(false) { + SASSERT(m_pob); + m_pob->get_skolems(m_zks); + add_binding(m_pob->get_binding()); +} + +lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : + m_ref_count(0), + m(p->get_ast_manager()), + m_body(m), m_cube(m), + m_zks(m), m_bindings(m), m_lvl(p->level()), + m_pob(p), m_external(false) +{ + if (m_pob) { + m_pob->get_skolems(m_zks); + add_binding(m_pob->get_binding()); + } + update_cube(p, cube); + set_level(lvl); +} + +void lemma::add_skolem(app *zk, app *b) { + SASSERT(m_bindings.size() == m_zks.size()); + // extend bindings + m_bindings.push_back(b); + // extend skolems + m_zks.push_back(zk); +} + + +void lemma::mk_expr_core() { + if (m_body) {return;} + + if (m_pob) {mk_cube_core();} + + SASSERT(!m_cube.empty()); + m_body = ::push_not(::mk_and(m_cube)); + normalize(m_body, m_body); + + if (!m_zks.empty() && has_zk_const(m_body)) { + app_ref_vector zks(m); + zks.append(m_zks); + zks.reverse(); + expr_abstract(m, 0, + zks.size(), (expr* const*)zks.c_ptr(), m_body, + m_body); + ptr_buffer sorts; + svector names; + for (unsigned i=0, sz=zks.size(); i < sz; ++i) { + sorts.push_back(get_sort(zks.get(i))); + names.push_back(zks.get(i)->get_decl()->get_name()); + } + m_body = m.mk_quantifier(true, zks.size(), + sorts.c_ptr(), + names.c_ptr(), + m_body, 15, symbol(m_body->get_id())); + } + SASSERT(m_body); +} +void lemma::mk_cube_core() { + if (!m_cube.empty()) {return;} + expr_ref cube(m); + if (m_pob || m_body) { + if(m_pob) {cube = m_pob->post();} + else if (m_body) { + // no quantifiers for now + SASSERT(!is_quantifier(m_body)); + cube = m_body; + cube = ::push_not(cube); + } + flatten_and(cube, m_cube); + if (m_cube.empty()) { + m_cube.push_back(m.mk_true()); + } + else { + std::sort(m_cube.c_ptr(), m_cube.c_ptr() + m_cube.size(), ast_lt_proc()); + } + } + else { + UNREACHABLE(); + } +} +bool lemma::is_false() { + // a lemma is false if + // 1. it is defined by a cube, and the cube contains a single literal 'true' + // 2. it is defined by a body, and the body is a single literal false + // 3. it is defined by a pob, and the pob post is false + if (m_cube.size() == 1) {return m.is_true(m_cube.get(0));} + else if (m_body) {return m.is_false(m_body);} + else if (m_pob) {return m.is_true(m_pob->post());} + + return false; +} +expr* lemma::get_expr() { + mk_expr_core(); + return m_body; +} +expr_ref_vector const &lemma::get_cube() { + mk_cube_core(); + return m_cube; +} + +void lemma::update_cube (pob_ref const &p, expr_ref_vector &cube) { + SASSERT(m_pob); + SASSERT(m_pob.get() == p.get()); + m_cube.reset(); + m_body.reset(); + m_cube.append(cube); + if (m_cube.empty()) {m_cube.push_back(m.mk_true());} + + // after the cube is updated, if there are no skolems, + // convert the lemma to quantifier-free + bool is_quant = false; + for (unsigned i = 0, sz = cube.size(); !is_quant && i < sz; ++i) { + is_quant = has_zk_const(cube.get(i)); + } + + if (!is_quant) { + m_zks.reset(); + m_bindings.reset(); + } +} + +bool lemma::has_binding(app_ref_vector const &binding) { + unsigned num_decls = m_zks.size(); + + SASSERT(binding.size() == num_decls); + + if (num_decls == 0) return true; + + for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { + unsigned i = 0; + for (; i < num_decls; ++i) { + if (m_bindings.get(off + i) != binding.get(i)) { + break; + } + } + if (i == num_decls) return true; + } + return false; +} +void lemma::add_binding(app_ref_vector const &binding) { + if (!has_binding(binding)) { + m_bindings.append(binding); + + TRACE("spacer", + tout << "new binding: "; + for (unsigned i = 0; i < binding.size(); i++) + tout << mk_pp(binding.get(i), m) << " "; + tout << "\n";); + } +} +void lemma::instantiate(expr * const * exprs, expr_ref &result, expr *e) { + expr *lem = e == nullptr ? get_expr() : e; + if (!is_quantifier (lem) || m_bindings.empty()) {return;} + + expr *body = to_quantifier(lem)->get_expr(); + unsigned num_decls = to_quantifier(lem)->get_num_decls(); + var_subst vs(m, false); + vs(body, num_decls, exprs, result); +} + +void lemma::set_level (unsigned lvl) { + if(m_pob){m_pob->blocked_at(lvl);} + m_lvl = lvl; +} + + +void lemma::mk_insts(expr_ref_vector &out, expr* e) +{ + expr *lem = e == nullptr ? get_expr() : e; + if (!is_quantifier (lem) || m_bindings.empty()) {return;} + + unsigned num_decls = to_quantifier(lem)->get_num_decls(); + expr_ref inst(m); + for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { + instantiate((expr * const *) m_bindings.c_ptr() + off, inst, e); + out.push_back(inst); + inst.reset(); + } +} + + // ---------------- // pred_tansformer @@ -303,9 +878,7 @@ void pred_transformer::remove_predecessors(expr_ref_vector& literals) } void pred_transformer::simplify_formulas() -{ - m_frames.simplify_formulas (); -} +{m_frames.simplify_formulas ();} expr_ref pred_transformer::get_formulas(unsigned level, bool add_axioms) @@ -528,9 +1101,7 @@ expr_ref pred_transformer::get_reachable() } expr* pred_transformer::get_last_reach_case_var () const -{ - return m_reach_case_vars.empty () ? nullptr : m_reach_case_vars.back (); -} +{return m_reach_case_vars.empty () ? nullptr : m_reach_case_vars.back ();} expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) { @@ -1168,211 +1739,32 @@ void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, datalog:: } void pred_transformer::inherit_properties(pred_transformer& other) +{m_frames.inherit_frames (other.m_frames);} + +app* pred_transformer::extend_initial (expr *e) { - m_frames.inherit_frames (other.m_frames); + // create fresh extend literal + app_ref v(m); + std::stringstream name; + name << m_head->get_name() << "_ext"; + v = m.mk_fresh_const (name.str ().c_str (), + m.mk_bool_sort ()); + v = m.mk_const (pm.get_n_pred (v->get_decl ())); + + expr_ref ic(m); + + // -- extend the initial condition + ic = m.mk_or (m_extend_lit, e, v); + m_solver.assert_expr (ic); + + // -- remember the new extend literal + m_extend_lit = m.mk_not (v); + + return m_extend_lit; } -lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : - m_ref_count(0), m(manager), - m_body(body, m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(lvl), - m_pob(nullptr), m_external(false) { - SASSERT(m_body); - normalize(m_body, m_body); -} - -lemma::lemma(pob_ref const &p) : - m_ref_count(0), m(p->get_ast_manager()), - m_body(m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(p->level()), - m_pob(p), m_external(false) { - SASSERT(m_pob); - m_pob->get_skolems(m_zks); - add_binding(m_pob->get_binding()); -} - -lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : - m_ref_count(0), - m(p->get_ast_manager()), - m_body(m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(p->level()), - m_pob(p), m_external(false) -{ - if (m_pob) { - m_pob->get_skolems(m_zks); - add_binding(m_pob->get_binding()); - } - update_cube(p, cube); - set_level(lvl); -} - -void lemma::add_skolem(app *zk, app *b) { - SASSERT(m_bindings.size() == m_zks.size()); - // extend bindings - m_bindings.push_back(b); - // extend skolems - m_zks.push_back(zk); -} - - -void lemma::mk_expr_core() { - if (m_body) return; - - if (m_pob) { - mk_cube_core(); - } - - SASSERT(!m_cube.empty()); - m_body = ::push_not(::mk_and(m_cube)); - normalize(m_body, m_body); - - if (!m_zks.empty() && has_zk_const(m_body)) { - app_ref_vector zks(m); - zks.append(m_zks); - zks.reverse(); - expr_abstract(m, 0, - zks.size(), (expr* const*)zks.c_ptr(), m_body, - m_body); - ptr_buffer sorts; - svector names; - for (unsigned i=0, sz=zks.size(); i < sz; ++i) { - sorts.push_back(get_sort(zks.get(i))); - names.push_back(zks.get(i)->get_decl()->get_name()); - } - m_body = m.mk_quantifier(true, zks.size(), - sorts.c_ptr(), - names.c_ptr(), - m_body, 15, symbol(m_body->get_id())); - } - SASSERT(m_body); -} -void lemma::mk_cube_core() { - if (!m_cube.empty()) {return;} - expr_ref cube(m); - if (m_pob || m_body) { - if(m_pob) { - cube = m_pob->post(); - } - else if (m_body) { - // no quantifiers for now - SASSERT(!is_quantifier(m_body)); - cube = m_body; - cube = ::push_not(cube); - } - flatten_and(cube, m_cube); - if (m_cube.empty()) { - m_cube.push_back(m.mk_true()); - } - else { - std::sort(m_cube.c_ptr(), m_cube.c_ptr() + m_cube.size(), ast_lt_proc()); - } - } - else { - UNREACHABLE(); - } -} -bool lemma::is_false() { - // a lemma is false if - // 1. it is defined by a cube, and the cube contains a single literal 'true' - // 2. it is defined by a body, and the body is a single literal false - // 3. it is defined by a pob, and the pob post is false - if (m_cube.size() == 1) {return m.is_true(m_cube.get(0));} - else if (m_body) {return m.is_false(m_body);} - else if (m_pob) {return m.is_true(m_pob->post());} - - return false; -} -expr* lemma::get_expr() { - mk_expr_core(); - return m_body; -} -expr_ref_vector const &lemma::get_cube() { - mk_cube_core(); - return m_cube; -} - -void lemma::update_cube (pob_ref const &p, expr_ref_vector &cube) { - SASSERT(m_pob); - SASSERT(m_pob.get() == p.get()); - m_cube.reset(); - m_body.reset(); - m_cube.append(cube); - if (m_cube.empty()) {m_cube.push_back(m.mk_true());} - - // after the cube is updated, if there are no skolems, - // convert the lemma to quantifier-free - bool is_quant = false; - for (unsigned i = 0, sz = cube.size(); !is_quant && i < sz; ++i) { - is_quant = has_zk_const(cube.get(i)); - } - - if (!is_quant) { - m_zks.reset(); - m_bindings.reset(); - } -} - -bool lemma::has_binding(app_ref_vector const &binding) { - unsigned num_decls = m_zks.size(); - - SASSERT(binding.size() == num_decls); - - if (num_decls == 0) return true; - - for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { - unsigned i = 0; - for (; i < num_decls; ++i) { - if (m_bindings.get(off + i) != binding.get(i)) { - break; - } - } - if (i == num_decls) return true; - } - return false; -} -void lemma::add_binding(app_ref_vector const &binding) { - if (!has_binding(binding)) { - m_bindings.append(binding); - - TRACE("spacer", - tout << "new binding: "; - for (unsigned i = 0; i < binding.size(); i++) - tout << mk_pp(binding.get(i), m) << " "; - tout << "\n";); - } -} -void lemma::instantiate(expr * const * exprs, expr_ref &result, expr *e) { - expr *lem = e == nullptr ? get_expr() : e; - if (!is_quantifier (lem) || m_bindings.empty()) {return;} - - expr *body = to_quantifier(lem)->get_expr(); - unsigned num_decls = to_quantifier(lem)->get_num_decls(); - var_subst vs(m, false); - vs(body, num_decls, exprs, result); -} - -void lemma::set_level (unsigned lvl) { - if(m_pob){ - m_pob->blocked_at(lvl); - } - m_lvl = lvl; -} - - -void lemma::mk_insts(expr_ref_vector &out, expr* e) -{ - expr *lem = e == nullptr ? get_expr() : e; - if (!is_quantifier (lem) || m_bindings.empty()) {return;} - - unsigned num_decls = to_quantifier(lem)->get_num_decls(); - expr_ref inst(m); - for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { - instantiate((expr * const *) m_bindings.c_ptr() + off, inst, e); - out.push_back(inst); - inst.reset(); - } -} +/// pred_transformer::frames bool pred_transformer::frames::add_lemma(lemma *lem) @@ -1575,6 +1967,8 @@ void pred_transformer::frames::simplify_formulas () } } +/// pred_transformer::pobs + pob* pred_transformer::pobs::mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b) { @@ -1615,400 +2009,8 @@ pob* pred_transformer::pobs::mk_pob(pob *parent, return n; } -app* pred_transformer::extend_initial (expr *e) -{ - // create fresh extend literal - app_ref v(m); - std::stringstream name; - name << m_head->get_name() << "_ext"; - v = m.mk_fresh_const (name.str ().c_str (), - m.mk_bool_sort ()); - v = m.mk_const (pm.get_n_pred (v->get_decl ())); - expr_ref ic(m); - // -- extend the initial condition - ic = m.mk_or (m_extend_lit, e, v); - m_solver.assert_expr (ic); - - // -- remember the new extend literal - m_extend_lit = m.mk_not (v); - - return m_extend_lit; -} - - -// ---------------- -// derivation - -derivation::derivation (pob& parent, datalog::rule const& rule, - expr *trans, app_ref_vector const &evars) : - m_parent (parent), - m_rule (rule), - m_premises (), - m_active (0), - m_trans (trans, m_parent.get_ast_manager ()), - m_evars (evars) {} - -derivation::premise::premise (pred_transformer &pt, unsigned oidx, - expr *summary, bool must, - const ptr_vector *aux_vars) : - m_pt (pt), m_oidx (oidx), - m_summary (summary, pt.get_ast_manager ()), m_must (must), - m_ovars (pt.get_ast_manager ()) -{ - - ast_manager &m = m_pt.get_ast_manager (); - manager &sm = m_pt.get_manager (); - - unsigned sig_sz = m_pt.head ()->get_arity (); - for (unsigned i = 0; i < sig_sz; ++i) - { m_ovars.push_back(m.mk_const(sm.o2o(pt.sig(i), 0, m_oidx))); } - - if (aux_vars) - for (unsigned i = 0, sz = aux_vars->size (); i < sz; ++i) - { m_ovars.push_back(m.mk_const(sm.n2o(aux_vars->get(i)->get_decl(), m_oidx))); } -} - -derivation::premise::premise (const derivation::premise &p) : - m_pt (p.m_pt), m_oidx (p.m_oidx), m_summary (p.m_summary), m_must (p.m_must), - m_ovars (p.m_ovars) {} - -/// \brief Updated the summary. -/// The new summary is over n-variables. -void derivation::premise::set_summary (expr * summary, bool must, - const ptr_vector *aux_vars) -{ - ast_manager &m = m_pt.get_ast_manager (); - manager &sm = m_pt.get_manager (); - unsigned sig_sz = m_pt.head ()->get_arity (); - - m_must = must; - sm.formula_n2o (summary, m_summary, m_oidx); - - m_ovars.reset (); - for (unsigned i = 0; i < sig_sz; ++i) - { m_ovars.push_back(m.mk_const(sm.o2o(m_pt.sig(i), 0, m_oidx))); } - - if (aux_vars) - for (unsigned i = 0, sz = aux_vars->size (); i < sz; ++i) - m_ovars.push_back (m.mk_const (sm.n2o (aux_vars->get (i)->get_decl (), - m_oidx))); -} - - -void derivation::add_premise (pred_transformer &pt, - unsigned oidx, - expr* summary, - bool must, - const ptr_vector *aux_vars) -{m_premises.push_back (premise (pt, oidx, summary, must, aux_vars));} - - - -pob *derivation::create_first_child (model_evaluator_util &mev) -{ - if (m_premises.empty()) { return nullptr; } - m_active = 0; - return create_next_child(mev); -} - -pob *derivation::create_next_child (model_evaluator_util &mev) -{ - timeit _timer (is_trace_enabled("spacer_timeit"), - "spacer::derivation::create_next_child", - verbose_stream ()); - - ast_manager &m = get_ast_manager (); - expr_ref_vector summaries (m); - app_ref_vector vars (m); - - bool use_native_mbp = get_context ().use_native_mbp (); - bool ground = get_context ().use_ground_cti (); - // -- find first may premise - while (m_active < m_premises.size() && m_premises[m_active].is_must()) { - summaries.push_back (m_premises[m_active].get_summary ()); - vars.append (m_premises[m_active].get_ovars ()); - ++m_active; - } - if (m_active >= m_premises.size()) { return nullptr; } - - // -- update m_trans with the pre-image of m_trans over the must summaries - summaries.push_back (m_trans); - m_trans = mk_and (summaries); - summaries.reset (); - - if (!vars.empty()) { - timeit _timer1 (is_trace_enabled("spacer_timeit"), - "create_next_child::qproject1", - verbose_stream ()); - qe_project (m, vars, m_trans, mev.get_model (), true, use_native_mbp, !ground); - //qe::reduce_array_selects (*mev.get_model (), m_trans); - // remember variables that need to be existentially quantified - m_evars.append (vars); - } - - if (!mev.is_true (m_premises[m_active].get_summary())) { - IF_VERBOSE(1, verbose_stream() << "Summary unexpectendly not true\n";); - return nullptr; - } - - - // create the post condition by compute post-image over summaries - // that precede currently active premise - vars.reset (); - for (unsigned i = m_active + 1; i < m_premises.size(); ++i) { - summaries.push_back (m_premises [i].get_summary ()); - vars.append (m_premises [i].get_ovars ()); - } - summaries.push_back (m_trans); - - expr_ref post(m); - post = mk_and (summaries); - summaries.reset (); - if (!vars.empty()) { - timeit _timer2 (is_trace_enabled("spacer_timeit"), - "create_next_child::qproject2", - verbose_stream ()); - qe_project (m, vars, post, mev.get_model (), true, use_native_mbp, !ground); - //qe::reduce_array_selects (*mev.get_model (), post); - - // remember variables that need to be existentially quantified - m_evars.append (vars); - } - - get_manager ().formula_o2n (post.get (), post, - m_premises [m_active].get_oidx (), m_evars.empty()); - - - /* The level and depth are taken from the parent, not the sibling. - The reasoning is that the sibling has not been checked before, - and lower level is a better starting point. */ - pob *n = m_premises[m_active].pt().mk_pob(&m_parent, - prev_level (m_parent.level ()), - m_parent.depth (), post, m_evars); - - IF_VERBOSE (1, verbose_stream () - << "\n\tcreate_child: " << n->pt ().head ()->get_name () - << " (" << n->level () << ", " << n->depth () << ") " - << (n->use_farkas_generalizer () ? "FAR " : "SUB ") - << n->post ()->get_id (); - verbose_stream().flush ();); - return n; -} - -pob *derivation::create_next_child () -{ - if (m_active + 1 >= m_premises.size()) { return nullptr; } - - bool use_native_mbp = get_context ().use_native_mbp (); - bool ground = get_context ().use_ground_cti (); - - // update the summary of the active node to some must summary - - // construct a new model consistent with the must summary of m_active premise - pred_transformer &pt = m_premises[m_active].pt (); - model_ref model; - - ast_manager &m = get_ast_manager (); - manager &pm = get_manager (); - - expr_ref_vector summaries (m); - - for (unsigned i = m_active + 1; i < m_premises.size (); ++i) - { summaries.push_back(m_premises [i].get_summary()); } - - // -- orient transition relation towards m_active premise - expr_ref active_trans (m); - pm.formula_o2n (m_trans, active_trans, - m_premises[m_active].get_oidx (), false); - summaries.push_back (active_trans); - - // if not true, bail out, the must summary of m_active is not strong enough - // this is possible if m_post was weakened for some reason - if (!pt.is_must_reachable(mk_and(summaries), &model)) { return nullptr; } - - model_evaluator_util mev (m); - mev.set_model (*model); - // find must summary used - - reach_fact *rf = pt.get_used_reach_fact (mev, true); - - // get an implicant of the summary - expr_ref_vector u(m), lits (m); - u.push_back (rf->get ()); - compute_implicant_literals (mev, u, lits); - expr_ref v(m); - v = mk_and (lits); - - // XXX The summary is not used by anyone after this point - m_premises[m_active].set_summary (v, true, &(rf->aux_vars ())); - - - /** HACK: needs a rewrite - * compute post over the new must summary this must be done here - * because the must summary is currently described over new - * variables. However, we store it over old-variables, but we do - * not update the model. So we must get rid of all of the - * new-variables at this point. - */ - { - pred_transformer &pt = m_premises[m_active].pt (); - app_ref_vector vars (m); - - summaries.reset (); - summaries.push_back (v); - summaries.push_back (active_trans); - m_trans = mk_and (summaries); - - // variables to eliminate - vars.append (rf->aux_vars ().size (), rf->aux_vars ().c_ptr ()); - for (unsigned i = 0, sz = pt.head ()->get_arity (); i < sz; ++i) - { vars.push_back(m.mk_const(pm.o2n(pt.sig(i), 0))); } - - if (!vars.empty ()) { - qe_project (m, vars, m_trans, mev.get_model (), true, use_native_mbp, - !ground); - // keep track of implicitly quantified variables - m_evars.append (vars); - } - } - - m_active++; - - return create_next_child (mev); -} - -pob::pob (pob* parent, pred_transformer& pt, - unsigned level, unsigned depth, bool add_to_parent): - m_ref_count (0), - m_parent (parent), m_pt (pt), - m_post (m_pt.get_ast_manager ()), - m_binding(m_pt.get_ast_manager()), - m_new_post (m_pt.get_ast_manager ()), - m_level (level), m_depth (depth), - m_open (true), m_use_farkas (true), m_weakness(0), - m_blocked_lvl(0) { - if(add_to_parent && m_parent) { - m_parent->add_child(*this); - } -} - - -void pob::set_post(expr* post) { - app_ref_vector b(get_ast_manager()); - set_post(post, b); -} - -void pob::set_post(expr* post, app_ref_vector const &b) { - normalize(post, m_post, - m_pt.get_context().get_params().spacer_simplify_pob(), - m_pt.get_context().get_params().spacer_use_eqclass()); - - m_binding.reset(); - if (b.empty()) return; - - m_binding.append(b); - - std::sort (m_binding.c_ptr(), m_binding.c_ptr() + m_binding.size(), sk_lt_proc()); - - // skolemize implicit existential quantifier - ast_manager &m = get_ast_manager(); - app_ref_vector pinned(m); - - expr_safe_replace sub(m); - for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { - expr* e; - e = m_binding.get(i); - pinned.push_back (mk_zk_const (m, i, get_sort(e))); - sub.insert (e, pinned.back()); - } - sub(m_post); -} - -void pob::inherit(pob const &p) { - SASSERT(m_parent == p.m_parent); - SASSERT(&m_pt == &p.m_pt); - SASSERT(m_post == p.m_post); - SASSERT(!m_new_post); - - m_binding.reset(); - m_binding.append(p.m_binding); - - m_level = p.m_level; - m_depth = p.m_depth; - m_open = p.m_open; - m_use_farkas = p.m_use_farkas; - m_weakness = p.m_weakness; - - m_derivation = nullptr; -} - -void pob::clean () { - if(m_new_post) { - m_post = m_new_post; - m_new_post.reset(); - } -} - -void pob::close () { - if(!m_open) { return; } - - reset (); - m_open = false; - for (unsigned i = 0, sz = m_kids.size (); i < sz; ++i) - { m_kids [i]->close(); } -} - -void pob::get_skolems(app_ref_vector &v) { - for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { - expr* e; - e = m_binding.get(i); - v.push_back (mk_zk_const (get_ast_manager(), i, get_sort(e))); - } -} - - - -// ---------------- -// pob_queue - -pob* pob_queue::top () -{ - /// nothing in the queue - if (m_obligations.empty()) { return nullptr; } - /// top queue element is above max level - if (m_obligations.top()->level() > m_max_level) { return nullptr; } - /// top queue element is at the max level, but at a higher than base depth - if (m_obligations.top ()->level () == m_max_level && - m_obligations.top()->depth() > m_min_depth) { return nullptr; } - - /// there is something good in the queue - return m_obligations.top ().get (); -} - -void pob_queue::set_root(pob& root) -{ - m_root = &root; - m_max_level = root.level (); - m_min_depth = root.depth (); - reset(); -} - -pob_queue::~pob_queue() {} - -void pob_queue::reset() -{ - while (!m_obligations.empty()) { m_obligations.pop(); } - if (m_root) { m_obligations.push(m_root); } -} - -void pob_queue::push(pob &n) { - TRACE("pob_queue", - tout << "pob_queue::push(" << n.post()->get_id() << ")\n";); - m_obligations.push (&n); - n.get_context().new_pob_eh(&n); -} // ---------------- // context @@ -3715,6 +3717,7 @@ bool context::is_inductive() { return false; } +/// pob_lt operator inline bool pob_lt::operator() (const pob *pn1, const pob *pn2) const { SASSERT (pn1); From 5a6bd5e782689c374b7ea983c8d16de5954449d5 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 18 May 2018 14:12:15 -0700 Subject: [PATCH 113/364] hypothesis_reducer: worked around propositional literals propositional formulas (disjunctions) can appear as literals. This makes it tricky to recognize whether a formula is a unit clause when re-building unit resolution. Added work-around that identifies whether a formula is a literal based on its appearance in previous unit resolution step. --- src/muz/spacer/spacer_proof_utils.cpp | 72 ++++++++++++++++----------- src/muz/spacer/spacer_proof_utils.h | 2 +- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 3ca9b0c25..151446a93 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -363,7 +363,7 @@ proof* hypothesis_reducer::reduce_core(proof* pf) { else if (m.is_unit_resolution(p)) { // unit: reduce untis; reduce the first premise; rebuild // unit resolution - res = mk_unit_resolution_core(args); + res = mk_unit_resolution_core(p, args); // -- re-compute hypsets compute_hypsets(res); } @@ -420,12 +420,13 @@ proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { return res; } -proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) { +proof* hypothesis_reducer::mk_unit_resolution_core(proof *ures, + ptr_buffer& args) { // if any literal is false, we don't need a unit resolution step // This can be the case due to some previous transformations for (unsigned i = 1, sz = args.size(); i < sz; ++i) { if (m.is_false(m.get_fact(args[i]))) { - // XXX just in case + // XXX pin just in case m_pinned.push_back(args[i]); return args[i]; } @@ -434,48 +435,58 @@ proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) { proof* arg0 = args[0]; app *fact0 = to_app(m.get_fact(arg0)); + ptr_buffer pf_args; ptr_buffer pf_fact; - pf_args.push_back(arg0); - // check if fact0 can be resolved as a unit - // this is required for handling literals with OR - for (unsigned j = 1; j < args.size(); ++j) { - if (m.is_complement(fact0, m.get_fact(args[j]))) { - pf_args.push_back(args[j]); - break; + // compute literals to be resolved + ptr_buffer lits; + + // fact0 is a literal whenever the original resolution was a + // binary resolution to an empty clause + if (m.get_num_parents(ures) == 2 && m.is_false(m.get_fact(ures))) { + lits.push_back(fact0); + } + // fact0 is a literal unless it is a dijsunction + else if (!m.is_or(fact0)) { + lits.push_back(fact0); + } + // fact0 is a literal only if it appears as a literal in the + // original resolution + else { + lits.reset(); + app* ures_fact = to_app(m.get_fact(m.get_parent(ures, 0))); + for (unsigned i = 0, sz = ures_fact->get_num_args(); i < sz; ++i) { + if (ures_fact->get_arg(i) == fact0) { + lits.push_back(fact0); + break; + } } + if (lits.empty()) { + lits.append(fact0->get_num_args(), fact0->get_args()); + } + } - - // if failed to find a resolvent, and the fact is a disjunction, - // attempt to resolve each disjunct - if (pf_args.size() == 1 && m.is_or(fact0)) { - ptr_buffer cls; - for (unsigned i = 0, sz = fact0->get_num_args(); i < sz; ++i) - cls.push_back(fact0->get_arg(i)); - - // -- find all literals that are resolved on - // XXX quadratic implementation - for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { - bool found = false; - for (unsigned j = 1; j < args.size(); ++j) { - if (m.is_complement(cls.get(i), m.get_fact(args[j]))) { - found = true; - pf_args.push_back(args[j]); - break; - } + // -- find all literals that are resolved on + for (unsigned i = 0, sz = lits.size(); i < sz; ++i) { + bool found = false; + for (unsigned j = 1; j < args.size(); ++j) { + if (m.is_complement(lits.get(i), m.get_fact(args[j]))) { + found = true; + pf_args.push_back(args[j]); + break; } - if (!found) pf_fact.push_back(cls.get(i)); } - SASSERT(pf_fact.size() + pf_args.size() - 1 == cls.size()); + if (!found) {pf_fact.push_back(lits.get(i));} } // unit resolution got reduced to noop if (pf_args.size() == 1) { // XXX pin just in case m_pinned.push_back(arg0); + return arg0; } @@ -484,6 +495,7 @@ proof* hypothesis_reducer::mk_unit_resolution_core(ptr_buffer& args) { tmp = mk_or(m, pf_fact.size(), pf_fact.c_ptr()); proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); m_pinned.push_back(res); + return res; } diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index 2e1129896..7ab022814 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -96,7 +96,7 @@ private: proof* reduce_core(proof* pf); proof* mk_lemma_core(proof *pf, expr *fact); - proof* mk_unit_resolution_core(ptr_buffer& args); + proof* mk_unit_resolution_core(proof* ures, ptr_buffer& args); proof* mk_proof_core(proof* old, ptr_buffer& args); }; } From 2f369d8d4119225b137df04382bd09ee5ef4c778 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 18 May 2018 16:17:27 -0700 Subject: [PATCH 114/364] Simplify code using C++11 conventions --- src/muz/spacer/spacer_context.cpp | 218 ++++++++++++++---------------- src/muz/spacer/spacer_context.h | 3 +- 2 files changed, 100 insertions(+), 121 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 2709593ad..fc0246ec6 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1020,14 +1020,14 @@ void pred_transformer::add_reach_fact (reach_fact *fact) << mk_pp(fact->get (), m) << "\n";); // -- avoid duplicates - if (fact == nullptr || get_reach_fact(fact->get())) { return; } + if (fact == nullptr || get_reach_fact(fact->get())) {return;} // all initial facts are grouped together SASSERT (!fact->is_init () || m_reach_facts.empty () || m_reach_facts.back ()->is_init ()); m_reach_facts.push_back (fact); - if (fact->is_init()) { m_rf_init_sz++; } + if (fact->is_init()) {m_rf_init_sz++;} // update m_reach_ctx @@ -1035,9 +1035,9 @@ void pred_transformer::add_reach_fact (reach_fact *fact) expr_ref new_var (m); expr_ref fml (m); - if (!m_reach_case_vars.empty()) { last_var = m_reach_case_vars.back(); } + if (!m_reach_case_vars.empty()) {last_var = m_reach_case_vars.back();} if (fact->is_init () || !ctx.get_params ().spacer_reach_as_init ()) - { new_var = mk_fresh_reach_case_var(); } + {new_var = mk_fresh_reach_case_var();} else { new_var = extend_initial (fact->get ())->get_arg (0); m_reach_case_vars.push_back (new_var); @@ -1045,10 +1045,8 @@ void pred_transformer::add_reach_fact (reach_fact *fact) SASSERT (m_reach_facts.size () == m_reach_case_vars.size ()); - if (last_var) - { fml = m.mk_or(m.mk_not(last_var), fact->get(), new_var); } - else - { fml = m.mk_or(fact->get(), new_var); } + if (last_var) {fml = m.mk_or(m.mk_not(last_var), fact->get(), new_var);} + else {fml = m.mk_or(fact->get(), new_var);} m_reach_ctx->assert_expr (fml); TRACE ("spacer", @@ -1056,9 +1054,8 @@ void pred_transformer::add_reach_fact (reach_fact *fact) lemma lem(m, fml, infty_level()); // update users; reach facts are independent of levels - for (unsigned i = 0; i < m_use.size(); ++i) { - m_use[i]->add_lemma_from_child (*this, &lem, infty_level ()); - } + for (auto use : m_use) + {use->add_lemma_from_child (*this, &lem, infty_level());} } expr_ref pred_transformer::get_reachable() @@ -1503,13 +1500,12 @@ void pred_transformer::init_reach_facts () expr_ref_vector v(m); reach_fact_ref fact; - rule2expr::iterator it = m_rule2tag.begin (), end = m_rule2tag.end (); - for (; it != end; ++it) { - const datalog::rule* r = it->m_key; + for (auto &entry : m_rule2tag) { + const datalog::rule* r = entry.m_key; if (r->get_uninterpreted_tail_size() == 0) { - fact = alloc (reach_fact, m, *r, m_rule2transition.find (r), - get_aux_vars (*r), true); - add_reach_fact (fact.get ()); + fact = alloc (reach_fact, m, *r, m_rule2transition.find(r), + get_aux_vars(*r), true); + add_reach_fact(fact.get ()); } } } @@ -1517,59 +1513,54 @@ void pred_transformer::init_reach_facts () void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition) { expr_ref_vector transitions(m); - ptr_vector tr_rules; + ptr_vector tr_rules; datalog::rule const* rule; expr_ref_vector disj(m), init_conds (m); app_ref pred(m); vector is_init; - for (unsigned i = 0; i < rules().size(); ++i) { - init_rule(pts, *rules()[i], is_init, tr_rules, transitions); - } + for (auto r : m_rules) {init_rule(pts, *r, is_init, tr_rules, transitions);} SASSERT (is_init.size () == transitions.size ()); + + std::string name; switch(transitions.size()) { case 0: transition = m.mk_false(); break; - case 1: { - std::stringstream name; + case 1: // create a dummy tag. - name << head()->get_name() << "_dummy"; - pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); + name = head()->get_name().str() + "_dummy"; + pred = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); + rule = tr_rules[0]; m_tag2rule.insert(pred, rule); m_rule2tag.insert(rule, pred.get()); - transitions [0] = m.mk_implies (pred, transitions.get (0)); - transitions.push_back (m.mk_or (pred, m_extend_lit->get_arg (0))); - if (!is_init [0]) { init_conds.push_back(m.mk_not(pred)); } + transitions[0] = m.mk_implies (pred, transitions.get(0)); + transitions.push_back (m.mk_or (pred, m_extend_lit->get_arg(0))); + if (!is_init[0]) {init_conds.push_back(m.mk_not(pred));} transition = mk_and(transitions); break; - } default: - disj.push_back (m_extend_lit->get_arg (0)); + disj.push_back (m_extend_lit->get_arg(0)); for (unsigned i = 0; i < transitions.size(); ++i) { - std::stringstream name; - name << head()->get_name() << "_tr" << i; - pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); + name = head()->get_name().str() + "__tr" + std::to_string(i); + pred = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); rule = tr_rules[i]; m_tag2rule.insert(pred, rule); m_rule2tag.insert(rule, pred); disj.push_back(pred); transitions[i] = m.mk_implies(pred, transitions[i].get()); // update init conds - if (!is_init[i]) { - init_conds.push_back (m.mk_not (pred)); - } + if (!is_init[i]) {init_conds.push_back (m.mk_not (pred));} } transitions.push_back(m.mk_or(disj.size(), disj.c_ptr())); transition = mk_and(transitions); break; } // mk init condition - init = mk_and (init_conds); - if (init_conds.empty ()) { // no rule has uninterpreted tail - m_all_init = true; - } + init = mk_and(init_conds); + // no rule has uninterpreted tail + if (init_conds.empty ()) {m_all_init = true;} } static bool is_all_non_null(app_ref_vector const& v) @@ -1580,109 +1571,100 @@ static bool is_all_non_null(app_ref_vector const& v) return true; } -void pred_transformer::init_rule( - decl2rel const& pts, - datalog::rule const& rule, - vector& is_init, - ptr_vector& rules, - expr_ref_vector& transitions) -{ +void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule, + vector& is_init, + ptr_vector& rules, + expr_ref_vector& transitions) { scoped_watch _t_(m_initialize_watch); // Predicates that are variable representatives. Other predicates at // positions the variables occur are made equivalent with these. - expr_ref_vector conj(m); - app_ref_vector& var_reprs = *(alloc(app_ref_vector, m)); + expr_ref_vector side(m); + app_ref_vector* var_reprs = alloc(app_ref_vector, m); + SASSERT(var_reprs); ptr_vector aux_vars; unsigned ut_size = rule.get_uninterpreted_tail_size(); unsigned t_size = rule.get_tail_size(); SASSERT(ut_size <= t_size); - init_atom(pts, rule.get_head(), var_reprs, conj, UINT_MAX); + init_atom(pts, rule.get_head(), *var_reprs, side, UINT_MAX); for (unsigned i = 0; i < ut_size; ++i) { if (rule.is_neg_tail(i)) { - throw default_exception("SPACER does not support negated predicates in rule tails"); + throw default_exception("SPACER does not support " + "negated predicates in rule tails"); } - init_atom(pts, rule.get_tail(i), var_reprs, conj, i); + init_atom(pts, rule.get_tail(i), *var_reprs, side, i); } // -- substitute free variables expr_ref fml(m); { expr_ref_vector tail(m); for (unsigned i = ut_size; i < t_size; ++i) - { tail.push_back(rule.get_tail(i)); } + {tail.push_back(rule.get_tail(i));} fml = mk_and (tail); - ground_free_vars (fml, var_reprs, aux_vars, ut_size == 0); - SASSERT(is_all_non_null(var_reprs)); + ground_free_vars(fml, *var_reprs, aux_vars, ut_size == 0); + SASSERT(is_all_non_null(*var_reprs)); expr_ref tmp(m); - var_subst (m, false)(fml, - var_reprs.size (), (expr*const*)var_reprs.c_ptr(), tmp); - flatten_and (tmp, conj); - fml = mk_and(conj); - conj.reset (); + var_subst (m, false)(fml, var_reprs->size (), + (expr*const*)var_reprs->c_ptr(), tmp); + flatten_and (tmp, side); + fml = mk_and(side); + side.reset (); } + // rewrite and simplify th_rewriter rw(m); rw(fml); - if (ctx.get_params().spacer_blast_term_ite()) { - blast_term_ite (fml); - rw(fml); - } + if (ctx.get_params().spacer_blast_term_ite()) {blast_term_ite(fml); rw(fml);} TRACE("spacer", tout << mk_pp(fml, m) << "\n";); // allow quantifiers in init rule SASSERT(ut_size == 0 || is_ground(fml)); - if (m.is_false(fml)) { - // no-op. - } else { + if (!m.is_false(fml)) { is_init.push_back (ut_size == 0); transitions.push_back(fml); - m.inc_ref(fml); - m_rule2transition.insert(&rule, fml.get()); rules.push_back(&rule); + + m.inc_ref(fml); + m_rule2transition.insert(&rule, fml); } - m_rule2inst.insert(&rule,&var_reprs); + // AG: shouldn't this be under the if-statement above? + m_rule2inst.insert(&rule, var_reprs); m_rule2vars.insert(&rule, aux_vars); + TRACE("spacer", tout << rule.get_decl()->get_name() << "\n"; - for (unsigned i = 0; i < var_reprs.size(); ++i) { - tout << mk_pp(var_reprs[i].get(), m) << " "; - } - tout << "\n";); + tout << *var_reprs << "\n";); } // create constants for free variables in tail. void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, - ptr_vector& aux_vars, bool is_init) -{ + ptr_vector& aux_vars, bool is_init) { expr_free_vars fv; fv(e); - while (vars.size() < fv.size()) { - vars.push_back(nullptr); - } + while (vars.size() < fv.size()) {vars.push_back(nullptr);} + for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i] && !vars[i].get()) { - vars[i] = m.mk_fresh_const("aux", fv[i]); - vars[i] = m.mk_const (pm.get_n_pred (vars.get (i)->get_decl ())); - aux_vars.push_back(vars[i].get()); + // AG: is it useful to make names unique across rules? + app_ref v(m); + v = m.mk_fresh_const("aux", fv[i]); + v = m.mk_const (pm.get_n_pred(v->get_decl ())); + vars[i] = v; + aux_vars.push_back(v); } } } // create names for variables used in relations. -void pred_transformer::init_atom( - decl2rel const& pts, - app * atom, - app_ref_vector& var_reprs, - expr_ref_vector& conj, - unsigned tail_idx - ) -{ +void pred_transformer::init_atom(decl2rel const &pts, app *atom, + app_ref_vector &var_reprs, + expr_ref_vector &side, unsigned tail_idx) { unsigned arity = atom->get_num_args(); func_decl* head = atom->get_decl(); pred_transformer& pt = *pts.find(head); @@ -1704,13 +1686,13 @@ void pred_transformer::init_atom( } expr * repr = var_reprs[var_idx].get(); if (repr) { - conj.push_back(m.mk_eq(rep, repr)); + side.push_back(m.mk_eq(rep, repr)); } else { var_reprs[var_idx] = rep; } } else { SASSERT(is_app(arg)); - conj.push_back(m.mk_eq(rep, arg)); + side.push_back(m.mk_eq(rep, arg)); } } } @@ -2061,23 +2043,22 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) { scoped_watch _t_(m_init_rules_watch); m_context = &rules.get_context(); + // Allocate collection of predicate transformers - datalog::rule_set::decl2rules::iterator dit = rules.begin_grouped_rules(), dend = rules.end_grouped_rules(); - decl2rel::obj_map_entry* e; - for (; dit != dend; ++dit) { + for (auto dit = rules.begin_grouped_rules(), + dend = rules.end_grouped_rules(); dit != dend; ++dit) { func_decl* pred = dit->m_key; TRACE("spacer", tout << mk_pp(pred, m) << "\n";); SASSERT(!rels.contains(pred)); - e = rels.insert_if_not_there2(pred, alloc(pred_transformer, *this, - get_manager(), pred)); + auto *e = rels.insert_if_not_there2(pred, alloc(pred_transformer, *this, + get_manager(), pred)); datalog::rule_vector const& pred_rules = *dit->m_value; - for (unsigned i = 0; i < pred_rules.size(); ++i) { - e->get_data().m_value->add_rule(pred_rules[i]); - } + for (auto rule : pred_rules) {e->get_data().m_value->add_rule(rule);} } - datalog::rule_set::iterator rit = rules.begin(), rend = rules.end(); - for (; rit != rend; ++rit) { - datalog::rule* r = *rit; + + // Allocate predicate transformers for predicates that are used + // but don't have rules + for (auto *r : rules) { pred_transformer* pt; unsigned utz = r->get_uninterpreted_tail_size(); for (unsigned i = 0; i < utz; ++i) { @@ -2088,32 +2069,28 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) } } } + // Initialize use list dependencies - decl2rel::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - func_decl* pred = it->m_key; - pred_transformer* pt = it->m_value, *pt_user; - obj_hashtable const& deps = rules.get_dependencies().get_deps(pred); - obj_hashtable::iterator itf = deps.begin(), endf = deps.end(); - for (; itf != endf; ++itf) { - TRACE("spacer", tout << mk_pp(pred, m) << " " << mk_pp(*itf, m) << "\n";); - pt_user = rels.find(*itf); + // for (auto it = rels.begin(), end = rels.end(); it != end; ++it) { + for (auto &entry : rels) { + func_decl* pred = entry.m_key; + pred_transformer* pt = entry.m_value, *pt_user; + for (auto dep : rules.get_dependencies().get_deps(pred)) { + TRACE("spacer", tout << mk_pp(pred, m) << " " << mk_pp(dep, m) << "\n";); + rels.find(dep, pt_user); pt_user->add_use(pt); } } // Initialize the predicate transformers. - it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - pred_transformer& rel = *it->m_value; - rel.initialize(rels); - TRACE("spacer", rel.display(tout); ); + for (auto &entry : rels) { + pred_transformer* rel = entry.m_value; + rel->initialize(rels); + TRACE("spacer", rel->display(tout); ); } // initialize reach facts - it = rels.begin (), end = rels.end (); - for (; it != end; ++it) - { it->m_value->init_reach_facts(); } + for (auto &entry : rels) {entry.m_value->init_reach_facts();} } void context::update_rules(datalog::rule_set& rules) @@ -2861,6 +2838,7 @@ bool context::check_reachability () node = m_pob_queue.top (); m_pob_queue.pop(); unsigned old_sz = m_pob_queue.size(); + (void)old_sz; SASSERT (node->level () <= m_pob_queue.max_level ()); switch (expand_pob(*node, new_pobs)) { case l_true: diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 475a7472c..9be990d8e 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -316,7 +316,8 @@ class pred_transformer { void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition); void init_rule(decl2rel const& pts, datalog::rule const& rule, vector& is_init, ptr_vector& rules, expr_ref_vector& transition); - void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx); + void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, + expr_ref_vector& side, unsigned tail_idx); void simplify_formulas(tactic& tac, expr_ref_vector& fmls); From 7281616084e7b05f372d95136006ecffb26bdd32 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 19 May 2018 17:52:56 -0700 Subject: [PATCH 115/364] model_evaluator: optionally expand arrays as sequence of stores commit on behalf of Nikolaj --- src/model/model_evaluator.cpp | 35 ++++++++++++++-------------- src/model/model_evaluator.h | 6 +++++ src/model/model_evaluator_params.pyg | 3 ++- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 07ee125f8..eb7131406 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -53,6 +53,7 @@ struct evaluator_cfg : public default_rewriter_cfg { bool m_model_completion; bool m_cache; bool m_array_equalities; + bool m_array_as_stores; evaluator_cfg(ast_manager & m, model_core & md, params_ref const & p): m(m), @@ -87,6 +88,7 @@ struct evaluator_cfg : public default_rewriter_cfg { m_model_completion = p.completion(); m_cache = p.cache(); m_array_equalities = p.array_equalities(); + m_array_as_stores = p.array_as_stores(); } bool evaluate(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -196,20 +198,22 @@ struct evaluator_cfg : public default_rewriter_cfg { if (evaluate(a->get_decl(), a->get_num_args(), a->get_args(), result)) { return BR_REWRITE1; } + if (false && m_array_as_stores && m_ar.is_array(result)) { + expand_stores(result); + } } CTRACE("model_evaluator", st != BR_FAILED, tout << result << "\n";); return st; } - void expand_value(expr_ref& val) { + void expand_stores(expr_ref& val) { vector stores; expr_ref else_case(m); bool _unused; if (m_ar.is_array(val) && extract_array_func_interp(val, stores, else_case, _unused)) { sort* srt = m.get_sort(val); val = m_ar.mk_const_array(srt, else_case); - for (unsigned i = stores.size(); i > 0; ) { - --i; + for (unsigned i = stores.size(); i-- > 0; ) { expr_ref_vector args(m); args.push_back(val); args.append(stores[i].size(), stores[i].c_ptr()); @@ -288,7 +292,6 @@ struct evaluator_cfg : public default_rewriter_cfg { bool cache_results() const { return m_cache; } - br_status mk_array_eq(expr* a, expr* b, expr_ref& result) { if (a == b) { result = m.mk_true(); @@ -497,9 +500,6 @@ struct evaluator_cfg : public default_rewriter_cfg { TRACE("model_evaluator", tout << "else case: " << mk_pp(else_case, m) << "\n";); return true; } - - - }; template class rewriter_tpl; @@ -513,10 +513,7 @@ struct model_evaluator::imp : public rewriter_tpl { m_cfg(md.get_manager(), md, p) { set_cancel_check(false); } - - void expand_value (expr_ref &val) { - m_cfg.expand_value (val); - } + void expand_stores(expr_ref &val) {m_cfg.expand_stores(val);} }; model_evaluator::model_evaluator(model_core & md, params_ref const & p) { @@ -571,7 +568,7 @@ void model_evaluator::reset(model_core &model, params_ref const& p) { void model_evaluator::operator()(expr * t, expr_ref & result) { TRACE("model_evaluator", tout << mk_ismt2_pp(t, m()) << "\n";); m_imp->operator()(t, result); - m_imp->expand_value(result); + m_imp->expand_stores(result); } expr_ref model_evaluator::operator()(expr * t) { @@ -581,6 +578,13 @@ expr_ref model_evaluator::operator()(expr * t) { return result; } +expr_ref_vector model_evaluator::operator()(expr_ref_vector const& ts) { + expr_ref_vector rs(m()); + for (expr* t : ts) rs.push_back((*this)(t)); + return rs; +} + + bool model_evaluator::is_true(expr* t) { expr_ref tmp(m()); return eval(t, tmp, true) && m().is_true(tmp); @@ -601,12 +605,12 @@ bool model_evaluator::eval(expr* t, expr_ref& r, bool model_completion) { try { r = (*this)(t); return true; - } + } catch (model_evaluator_exception &ex) { (void)ex; TRACE("model_evaluator", tout << ex.msg () << "\n";); return false; - } + } } bool model_evaluator::eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion) { @@ -614,6 +618,3 @@ bool model_evaluator::eval(expr_ref_vector const& ts, expr_ref& r, bool model_co tmp = mk_and(ts); return eval(tmp, r, model_completion); } - - - diff --git a/src/model/model_evaluator.h b/src/model/model_evaluator.h index 6ae7d8891..d9bf3c375 100644 --- a/src/model/model_evaluator.h +++ b/src/model/model_evaluator.h @@ -44,6 +44,7 @@ public: void operator()(expr * t, expr_ref & r); expr_ref operator()(expr* t); + expr_ref_vector operator()(expr_ref_vector const& ts); // exception safe bool eval(expr* t, expr_ref& r, bool model_completion = true); @@ -53,6 +54,11 @@ public: bool is_false(expr * t); bool is_true(expr_ref_vector const& ts); + /** + * best effort evaluator of extensional array equality. + */ + expr_ref eval_array_eq(app* e, expr* arg1, expr* arg2); + void cleanup(params_ref const & p = params_ref()); void reset(params_ref const & p = params_ref()); void reset(model_core& model, params_ref const & p = params_ref()); diff --git a/src/model/model_evaluator_params.pyg b/src/model/model_evaluator_params.pyg index b6417f7fc..509b3e7c7 100644 --- a/src/model/model_evaluator_params.pyg +++ b/src/model/model_evaluator_params.pyg @@ -4,6 +4,7 @@ def_module_params('model_evaluator', max_steps_param(), ('completion', BOOL, False, 'assigns an interptetation to symbols that do not have one in the current model, when evaluating expressions in the current model'), ('cache', BOOL, True, 'cache intermediate results in the model evaluator'), - ('array_equalities', BOOL, True, 'evaluate array equalities') + ('array_equalities', BOOL, True, 'evaluate array equalities'), + ('array_as_stores', BOOL, True, 'return array as a set of stores'), )) From 988466705c47cabb216b892b72dcf60939086a90 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 19 May 2018 17:53:28 -0700 Subject: [PATCH 116/364] port array projection to qe_arrays ensure it works with multi-dimensional arrays commit on behalf of Nikolaj --- src/qe/qe_arrays.cpp | 1106 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1103 insertions(+), 3 deletions(-) diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index a227d755e..ad9d5d63f 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -18,16 +18,144 @@ Revision History: --*/ -#include "qe/qe_arrays.h" +#include "util/lbool.h" #include "ast/rewriter/rewriter_def.h" #include "ast/expr_functors.h" #include "ast/rewriter/expr_safe_replace.h" -#include "util/lbool.h" +#include "ast/rewriter/th_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" +#include "model/model_evaluator.h" +#include "qe/qe_arrays.h" + + +namespace { + bool is_partial_eq (app* a); + + /** + * \brief utility class for partial equalities + * + * A partial equality (a ==I b), for two arrays a,b and a finite set of indices I holds + * iff (Forall i. i \not\in I => a[i] == b[i]); in other words, it is a + * restricted form of the extensionality axiom + * + * using this class, we denote (a =I b) as f(a,b,i0,i1,...) + * where f is an uninterpreted predicate with name PARTIAL_EQ and + * I = {i0,i1,...} + */ + + // TBD: make work for arrays with multiple arguments. + class peq { + ast_manager& m; + expr_ref m_lhs; + expr_ref m_rhs; + vector m_diff_indices; + func_decl_ref m_decl; // the partial equality declaration + app_ref m_peq; // partial equality application + app_ref m_eq; // equivalent std equality using def. of partial eq + array_util m_arr_u; + + public: + static const char* PARTIAL_EQ; + + peq (app* p, ast_manager& m): + m (m), + m_lhs (p->get_arg (0), m), + m_rhs (p->get_arg (1), m), + m_decl (p->get_decl (), m), + m_peq (p, m), + m_eq (m), + m_arr_u (m) + { + VERIFY (is_partial_eq (p)); + SASSERT (m_arr_u.is_array (m_lhs) && + m_arr_u.is_array (m_rhs) && + m.get_sort(m_lhs) == m.get_sort(m_rhs)); + unsigned arity = get_array_arity(m.get_sort(m_lhs)); + for (unsigned i = 2; i < p->get_num_args (); i += arity) { + SASSERT(arity + i <= p->get_num_args()); + expr_ref_vector vec(m); + vec.append(arity, p->get_args() + i); + m_diff_indices.push_back (vec); + } + } + + peq (expr* lhs, expr* rhs, vector const& diff_indices, ast_manager& m): + m (m), + m_lhs (lhs, m), + m_rhs (rhs, m), + m_diff_indices (diff_indices), + m_decl (m), + m_peq (m), + m_eq (m), + m_arr_u (m) { + SASSERT (m_arr_u.is_array (lhs) && + m_arr_u.is_array (rhs) && + m.get_sort(lhs) == m.get_sort(rhs)); + ptr_vector sorts; + sorts.push_back (m.get_sort (m_lhs)); + sorts.push_back (m.get_sort (m_rhs)); + for (auto const& v : diff_indices) { + SASSERT(v.size() == get_array_arity(m.get_sort(m_lhs))); + for (expr* e : v) + sorts.push_back (m.get_sort(e)); + } + m_decl = m.mk_func_decl (symbol (PARTIAL_EQ), sorts.size (), sorts.c_ptr (), m.mk_bool_sort ()); + } + + expr_ref lhs () { return m_lhs; } + + expr_ref rhs () { return m_rhs; } + + void get_diff_indices (vector& result) { result.append(m_diff_indices); } + + app_ref mk_peq () { + if (!m_peq) { + ptr_vector args; + args.push_back (m_lhs); + args.push_back (m_rhs); + for (auto const& v : m_diff_indices) { + args.append (v.size(), v.c_ptr()); + } + m_peq = m.mk_app (m_decl, args.size (), args.c_ptr ()); + } + return m_peq; + } + + app_ref mk_eq (app_ref_vector& aux_consts, bool stores_on_rhs = true) { + if (!m_eq) { + expr_ref lhs (m_lhs, m), rhs (m_rhs, m); + if (!stores_on_rhs) { + std::swap (lhs, rhs); + } + // lhs = (...(store (store rhs i0 v0) i1 v1)...) + sort* val_sort = get_array_range (m.get_sort (lhs)); + for (expr_ref_vector const& diff : m_diff_indices) { + ptr_vector store_args; + store_args.push_back (rhs); + store_args.append (diff.size(), diff.c_ptr()); + app_ref val(m.mk_fresh_const ("diff", val_sort), m); + store_args.push_back (val); + aux_consts.push_back (val); + rhs = m_arr_u.mk_store (store_args.size (), store_args.c_ptr ()); + } + m_eq = m.mk_eq (lhs, rhs); + } + return m_eq; + } + }; + + const char* peq::PARTIAL_EQ = "!partial_eq"; + + bool is_partial_eq (app* a) { + return a->get_decl ()->get_name () == peq::PARTIAL_EQ; + } +} namespace qe { + + struct array_project_plugin::imp { // rewriter or direct procedure. @@ -187,7 +315,8 @@ namespace qe { return (*m_var)(e); } - void mk_eq(indices& x, indices y, expr_ref_vector& lits) { + void mk_eq(indices const& x, indices const& y, expr_ref_vector& lits) { + SASSERT(x.m_values.size() == y.m_values.size()); unsigned n = x.m_values.size(); for (unsigned j = 0; j < n; ++j) { lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); @@ -430,5 +559,976 @@ namespace qe { return m_imp->a.get_family_id(); } + static bool is_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { + for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; + return true; + } + + static expr_ref mk_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { + ast_manager& m = xs.get_manager(); + expr_ref_vector eqs(m); + for (unsigned i = 0; i < xs.size(); ++i) eqs.push_back(m.mk_eq(xs[i], ys[i])); + return mk_and(eqs); + } + + + class array_project_eqs_util { + ast_manager& m; + array_util m_arr_u; + model_ref M; + model_evaluator* m_mev; + app_ref m_v; // array var to eliminate + ast_mark m_has_stores_v; // has stores for m_v + expr_ref m_subst_term_v; // subst term for m_v + expr_safe_replace m_true_sub_v; // subst for true equalities + expr_safe_replace m_false_sub_v; // subst for false equalities + expr_ref_vector m_aux_lits_v; + expr_ref_vector m_idx_lits_v; + app_ref_vector m_aux_vars; + + void reset_v () { + m_v = nullptr; + m_has_stores_v.reset (); + m_subst_term_v = nullptr; + m_true_sub_v.reset (); + m_false_sub_v.reset (); + m_aux_lits_v.reset (); + m_idx_lits_v.reset (); + } + + void reset () { + M = nullptr; + m_mev = nullptr; + reset_v (); + m_aux_vars.reset (); + } + + /** + * find all array equalities on m_v or containing stores on/of m_v + * + * also mark terms containing stores on/of m_v + */ + void find_arr_eqs (expr_ref const& fml, app_ref_vector& eqs) { + if (!is_app (fml)) return; + ast_mark done; + ptr_vector todo; + todo.push_back (to_app (fml)); + while (!todo.empty ()) { + app* a = todo.back (); + if (done.is_marked (a)) { + todo.pop_back (); + continue; + } + bool all_done = true; + bool args_have_stores = false; + for (expr * arg : *a) { + if (!is_app (arg)) continue; + if (!done.is_marked (arg)) { + all_done = false; + todo.push_back (to_app (arg)); + } + else if (!args_have_stores && m_has_stores_v.is_marked (arg)) { + args_have_stores = true; + } + } + if (!all_done) continue; + todo.pop_back (); + + // mark if a has stores + if ((!m_arr_u.is_select (a) && args_have_stores) || + (m_arr_u.is_store (a) && (a->get_arg (0) == m_v))) { + m_has_stores_v.mark (a, true); + + TRACE ("qe", + tout << "has stores:\n"; + tout << mk_pp (a, m) << "\n"; + ); + } + + // check if a is a relevant array equality + expr * a0 = nullptr, *a1 = nullptr; + if (m.is_eq (a, a0, a1)) { + if (a0 == m_v || a1 == m_v || + (m_arr_u.is_array (a0) && m_has_stores_v.is_marked (a))) { + eqs.push_back (a); + } + } + // else, we can check for disequalities and handle them using extensionality, + // but it's not necessary + + done.mark (a, true); + } + } + + /** + * factor out select terms on m_v using fresh consts + */ + void factor_selects (app_ref& fml) { + expr_map sel_cache (m); + ast_mark done; + ptr_vector todo; + expr_ref_vector pinned (m); // to ensure a reference + + todo.push_back (fml); + while (!todo.empty ()) { + app* a = todo.back (); + if (done.is_marked (a)) { + todo.pop_back (); + continue; + } + expr_ref_vector args (m); + bool all_done = true; + for (expr * arg : *a) { + if (!is_app (arg)) continue; + if (!done.is_marked (arg)) { + all_done = false; + todo.push_back (to_app (arg)); + } + else if (all_done) { // all done so far.. + expr* arg_new = nullptr; proof* pr; + sel_cache.get (arg, arg_new, pr); + if (!arg_new) { + arg_new = arg; + } + args.push_back (arg_new); + } + } + if (!all_done) continue; + todo.pop_back (); + + expr_ref a_new (m.mk_app (a->get_decl (), args.size (), args.c_ptr ()), m); + + // if a_new is select on m_v, introduce new constant + if (m_arr_u.is_select (a) && + (args.get (0) == m_v || m_has_stores_v.is_marked (args.get (0)))) { + sort* val_sort = get_array_range (m.get_sort (m_v)); + app_ref val_const (m.mk_fresh_const ("sel", val_sort), m); + m_aux_vars.push_back (val_const); + // extend M to include val_const + expr_ref val = (*m_mev)(a_new); + M->register_decl (val_const->get_decl (), val); + // add equality + m_aux_lits_v.push_back (m.mk_eq (val_const, a_new)); + // replace select by const + a_new = val_const; + } + + if (a != a_new) { + sel_cache.insert (a, a_new, nullptr); + pinned.push_back (a_new); + } + done.mark (a, true); + } + expr* res = nullptr; proof* pr; + sel_cache.get (fml, res, pr); + if (res) { + fml = to_app (res); + } + } + + /** + * convert partial equality expression p_exp to an equality by + * recursively adding stores on diff indices + * + * add stores on lhs or rhs depending on whether stores_on_rhs is false/true + */ + void convert_peq_to_eq (expr* p_exp, app_ref& eq, bool stores_on_rhs = true) { + peq p (to_app (p_exp), m); + app_ref_vector diff_val_consts (m); + eq = p.mk_eq (diff_val_consts, stores_on_rhs); + m_aux_vars.append (diff_val_consts); + // extend M to include diff_val_consts + vector I; + expr_ref arr = p.lhs (); + p.get_diff_indices (I); + expr_ref val (m); + unsigned num_diff = diff_val_consts.size (); + SASSERT (num_diff == I.size ()); + for (unsigned i = 0; i < num_diff; i++) { + // mk val term + ptr_vector sel_args; + sel_args.push_back (arr); + sel_args.append(I[i].size(), I[i].c_ptr()); + expr_ref val_term (m_arr_u.mk_select (sel_args.size (), sel_args.c_ptr ()), m); + // evaluate and assign to ith diff_val_const + val = (*m_mev)(val_term); + M->register_decl (diff_val_consts.get (i)->get_decl (), val); + } + } + + /** + * mk (e0 ==indices e1) + * + * result has stores if either e0 or e1 or an index term has stores + */ + app_ref mk_peq (expr* e0, expr* e1, vector const& indices) { + peq p (e0, e1, indices, m); + return p.mk_peq (); + } + + void find_subst_term (app* eq) { + SASSERT(m.is_eq(eq)); + vector empty; + app_ref p_exp = mk_peq (eq->get_arg (0), eq->get_arg (1), empty); + bool subst_eq_found = false; + while (true) { + TRACE ("qe", tout << "processing peq:\n" << p_exp << "\n";); + + peq p (p_exp, m); + expr_ref lhs = p.lhs(), rhs = p.rhs(); + if (!m_has_stores_v.is_marked (lhs)) { + std::swap (lhs, rhs); + } + if (m_has_stores_v.is_marked (lhs)) { + /** project using the equivalence: + * + * (store(arr0,idx,x) ==I arr1) <-> + * + * (idx \in I => (arr0 ==I arr1)) /\ + * (idx \not\in I => (arr0 ==I+idx arr1) /\ (arr1[idx] == x))) + */ + vector I; + expr_ref_vector idxs (m); + p.get_diff_indices (I); + app* a_lhs = to_app (lhs); + expr* arr0 = a_lhs->get_arg (0); + idxs.append(a_lhs->get_num_args() - 2, a_lhs->get_args() + 1); + expr* x = a_lhs->get_arg (2); + expr* arr1 = rhs; + // check if (idx \in I) in M + bool idx_in_I = false; + expr_ref_vector idx_diseq (m); + if (!I.empty ()) { + expr_ref_vector vals = (*m_mev)(idxs); + for (unsigned i = 0; i < I.size () && !idx_in_I; i++) { + if (is_eq(idxs, I.get(i))) { + idx_in_I = true; + } + else { + expr_ref idx_eq = mk_eq(idxs, I[i]); + expr_ref_vector vals1 = (*m_mev)(I[i]); + if (is_eq(vals, vals1)) { + idx_in_I = true; + m_idx_lits_v.push_back (idx_eq); + } + else { + idx_diseq.push_back (m.mk_not (idx_eq)); + } + } + } + } + if (idx_in_I) { + TRACE ("qe", + tout << "store index in diff indices:\n"; + tout << mk_pp (m_idx_lits_v.back (), m) << "\n"; + ); + + // arr0 ==I arr1 + p_exp = mk_peq (arr0, arr1, I); + + TRACE ("qe", + tout << "new peq:\n"; + tout << mk_pp (p_exp, m) << "\n"; + ); + } + else { + m_idx_lits_v.append (idx_diseq); + // arr0 ==I+idx arr1 + I.push_back (idxs); + p_exp = mk_peq (arr0, arr1, I); + + TRACE ("qe", tout << "new peq:\n" << p_exp << "\n"; ); + + // arr1[idx] == x + ptr_vector sel_args; + sel_args.push_back (arr1); + sel_args.append(idxs.size(), idxs.c_ptr()); + expr_ref arr1_idx (m_arr_u.mk_select (sel_args.size (), sel_args.c_ptr ()), m); + expr_ref eq (m.mk_eq (arr1_idx, x), m); + m_aux_lits_v.push_back (eq); + + TRACE ("qe", + tout << "new eq:\n"; + tout << mk_pp (eq, m) << "\n"; + ); + } + } + else if (lhs == rhs) { // trivial peq (a ==I a) + break; + } + else if (lhs == m_v || rhs == m_v) { + subst_eq_found = true; + TRACE ("qe", + tout << "subst eq found!\n"; + ); + break; + } + else { + UNREACHABLE (); + } + } + + // factor out select terms on m_v from p_exp using fresh constants + if (subst_eq_found) { + factor_selects (p_exp); + + TRACE ("qe", + tout << "after factoring selects:\n"; + tout << mk_pp (p_exp, m) << "\n"; + for (unsigned i = m_aux_lits_v.size () - m_aux_vars.size (); i < m_aux_lits_v.size (); i++) { + tout << mk_pp (m_aux_lits_v.get (i), m) << "\n"; + } + ); + + // find subst_term + bool stores_on_rhs = true; + app* a = to_app (p_exp); + if (a->get_arg (1) == m_v) { + stores_on_rhs = false; + } + app_ref eq (m); + convert_peq_to_eq (p_exp, eq, stores_on_rhs); + m_subst_term_v = eq->get_arg (1); + + TRACE ("qe", + tout << "subst term found:\n"; + tout << mk_pp (m_subst_term_v, m) << "\n"; + ); + } + } + + /** + * compute nesting depths of stores on m_v in true_eqs, as follows: + * 0 if m_v appears on both sides of equality + * 1 if equality is (m_v=t) + * 2 if equality is (store(m_v,i,v)=t) + * ... + */ + unsigned get_nesting_depth(app* eq) { + SASSERT(m.is_eq(eq)); + expr* lhs = eq->get_arg (0); + expr* rhs = eq->get_arg (1); + bool lhs_has_v = (lhs == m_v || m_has_stores_v.is_marked (lhs)); + bool rhs_has_v = (rhs == m_v || m_has_stores_v.is_marked (rhs)); + app* store = nullptr; + + SASSERT (lhs_has_v || rhs_has_v); + + if (!lhs_has_v && is_app(rhs)) { + store = to_app (rhs); + } + else if (!rhs_has_v && is_app(lhs)) { + store = to_app (lhs); + } + else { + // v appears on both sides -- trivial equality + // put it in the beginning to simplify it away + return 0; + } + + unsigned nd = 0; // nesting depth + for (nd = 1; m_arr_u.is_store (store); nd++, store = to_app (store->get_arg (0))) + /* empty */ ; + SASSERT (store == m_v); + return nd; + } + + struct compare_nd { + bool operator()(std::pair const& x, std::pair const& y) const { + return x < y; + } + }; + + /** + * try to substitute for m_v, using array equalities + * + * compute substitution term and aux lits + */ + bool project (expr_ref const& fml) { + app_ref_vector eqs (m); + svector > true_eqs; + + find_arr_eqs (fml, eqs); + TRACE ("qe", + tout << "array equalities:\n"; + for (app * eq : eqs) tout << mk_pp(eq, m) << "\n";); + + // evaluate eqs in M + for (app * eq : eqs) { + TRACE ("qe", tout << "array equality:\n" << mk_pp (eq, m) << "\n"; ); + + if (m_mev->is_false(eq)) { + m_false_sub_v.insert (eq, m.mk_false()); + } + else { + true_eqs.push_back (std::make_pair(get_nesting_depth(eq), eq)); + } + } + std::sort(true_eqs.begin(), true_eqs.end(), compare_nd()); + DEBUG_CODE(for (unsigned i = 0; i + 1 < true_eqs.size(); ++i) SASSERT(true_eqs[i].first <= true_eqs[i+1].first);); + + // search for subst term + for (unsigned i = 0; !m_subst_term_v && i < true_eqs.size(); i++) { + app* eq = true_eqs[i].second; + m_true_sub_v.insert (eq, m.mk_true ()); + // try to find subst term + find_subst_term (eq); + } + + return true; + } + + void mk_result (expr_ref& fml) { + th_rewriter rw(m); + rw (fml); + // add in aux_lits and idx_lits + expr_ref_vector lits (m); + // TODO: eliminate possible duplicates, especially in idx_lits + // theory rewriting is a possibility, but not sure if it + // introduces unwanted terms such as ite's + lits.append (m_idx_lits_v); + lits.append (m_aux_lits_v); + lits.push_back (fml); + fml = mk_and(lits); + + if (m_subst_term_v) { + m_true_sub_v.insert (m_v, m_subst_term_v); + m_true_sub_v (fml); + } + else { + m_true_sub_v (fml); + m_false_sub_v (fml); + } + rw(fml); + SASSERT (!m.is_false (fml)); + } + + public: + + array_project_eqs_util (ast_manager& m): + m (m), + m_arr_u (m), + m_v (m), + m_subst_term_v (m), + m_true_sub_v (m), + m_false_sub_v (m), + m_aux_lits_v (m), + m_idx_lits_v (m), + m_aux_vars (m) + {} + + void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { + reset (); + model_evaluator mev(mdl); + M = &mdl; + m_mev = &mev; + + unsigned j = 0; + for (unsigned i = 0; i < arr_vars.size (); i++) { + reset_v (); + m_v = arr_vars.get (i); + if (!m_arr_u.is_array (m_v)) { + TRACE ("qe", + tout << "not an array variable: " << mk_pp (m_v, m) << "\n"; + ); + aux_vars.push_back (m_v); + continue; + } + TRACE ("qe", + tout << "projecting equalities on variable: " << mk_pp (m_v, m) << "\n"; + ); + + if (project (fml)) { + mk_result (fml); + + contains_app contains_v (m, m_v); + if (!m_subst_term_v || contains_v (m_subst_term_v)) { + arr_vars[j++] = m_v; + } + TRACE ("qe", + tout << "after projection: \n"; + tout << mk_pp (fml, m) << "\n"; + ); + } + else { + IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(m_v, m) << "\n";); + TRACE ("qe", tout << "Failed to project: " << mk_pp (m_v, m) << "\n";); + arr_vars[j++] = m_v; + } + } + arr_vars.shrink(j); + aux_vars.append (m_aux_vars); + } + }; + + + class array_select_reducer { + ast_manager& m; + array_util m_arr_u; + obj_map m_cache; + expr_ref_vector m_pinned; // to ensure a reference + expr_ref_vector m_idx_lits; + model_ref M; + model_evaluator* m_mev; + th_rewriter m_rw; + ast_mark m_arr_test; + ast_mark m_has_stores; + bool m_reduce_all_selects; + + void reset () { + m_cache.reset (); + m_pinned.reset (); + m_idx_lits.reset (); + M = nullptr; + m_mev = nullptr; + m_arr_test.reset (); + m_has_stores.reset (); + m_reduce_all_selects = false; + } + + bool is_equals (expr *e1, expr *e2) { + return e1 == e2 || (*m_mev)(e1) == (*m_mev)(e2); + } + + bool is_equals (unsigned arity, expr * const* xs, expr * const * ys) { + for (unsigned i = 0; i < arity; ++i) { + if (!is_equals(xs[i], ys[i])) return false; + } + return true; + } + + expr_ref mk_eq(unsigned arity, expr * const* xs, expr * const * ys) { + expr_ref_vector r(m); + for (unsigned i = 0; i < arity; ++i) { + r.push_back(m.mk_eq(xs[i], ys[i])); + } + return mk_and(r); + } + + void add_idx_cond (expr_ref& cond) { + m_rw (cond); + if (!m.is_true (cond)) m_idx_lits.push_back (cond); + } + + bool has_stores (expr* e) { + if (m_reduce_all_selects) return true; + return m_has_stores.is_marked (e); + } + + void mark_stores (app* a, bool args_have_stores) { + if (m_reduce_all_selects) return; + if (args_have_stores || + (m_arr_u.is_store (a) && m_arr_test.is_marked (a->get_arg (0)))) { + m_has_stores.mark (a, true); + } + } + + bool reduce (expr_ref& e) { + if (!is_app (e)) return true; + + expr *r = nullptr; + if (m_cache.find (e, r)) { + e = r; + return true; + } + + ptr_vector todo; + todo.push_back (to_app (e)); + expr_ref_vector args (m); + + while (!todo.empty ()) { + app *a = todo.back (); + unsigned sz = todo.size (); + bool dirty = false; + bool args_have_stores = false; + args.reset(); + for (expr * arg : *a) { + expr *narg = nullptr; + if (!is_app (arg)) { + args.push_back (arg); + } + else if (m_cache.find (arg, narg)) { + args.push_back (narg); + dirty |= (arg != narg); + if (!args_have_stores && has_stores (narg)) { + args_have_stores = true; + } + } + else { + todo.push_back (to_app (arg)); + } + } + + if (todo.size () > sz) continue; + todo.pop_back (); + + if (dirty) { + r = m.mk_app (a->get_decl (), args.size (), args.c_ptr ()); + m_pinned.push_back (r); + } + else { + r = a; + } + + if (m_arr_u.is_select (r) && has_stores (to_app (r)->get_arg (0))) { + r = reduce_core (to_app(r)); + } + else { + mark_stores (to_app (r), args_have_stores); + } + + m_cache.insert (a, r); + } + + SASSERT (r); + e = r; + return true; + } + + expr* reduce_core (app *a) { + if (!m_arr_u.is_store (a->get_arg (0))) return a; + unsigned arity = get_array_arity(m.get_sort(a)); + expr* array = a->get_arg (0); + expr* const* js = a->get_args() + 1; + + while (m_arr_u.is_store (array)) { + a = to_app (array); + expr* const* idxs = a->get_args() + 1; + expr_ref cond = mk_eq(arity, idxs, js); + + if (is_equals (arity, idxs, js)) { + add_idx_cond (cond); + return a->get_arg (2); + } + else { + cond = m.mk_not (cond); + add_idx_cond (cond); + array = a->get_arg (0); + } + } + ptr_vector args; + args.push_back(array); + args.append(arity, js); + expr* r = m_arr_u.mk_select (args.size(), args.c_ptr()); + m_pinned.push_back (r); + return r; + } + + void mk_result (expr_ref& fml) { + // conjoin idx lits + expr_ref_vector lits (m); + lits.append (m_idx_lits); + lits.push_back (fml); + fml = mk_and(lits); + // simplify all trivial expressions introduced + m_rw (fml); + TRACE ("qe", tout << "after reducing selects:\n" << fml << "\n";); + } + + public: + + array_select_reducer (ast_manager& m): + m (m), + m_arr_u (m), + m_pinned (m), + m_idx_lits (m), + m_rw (m), + m_reduce_all_selects (false) + {} + + void operator () (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects = false) { + if (!reduce_all_selects && arr_vars.empty ()) return; + + reset (); + model_evaluator mev(mdl); + M = &mdl; + m_mev = &mev; + m_reduce_all_selects = reduce_all_selects; + + // mark vars to eliminate + for (app* v : arr_vars) { + m_arr_test.mark (v, true); + } + + // assume all arr_vars are of array sort + // and assume no store equalities on arr_vars + if (reduce (fml)) { + mk_result (fml); + } + else { + IF_VERBOSE(2, verbose_stream() << "can't project arrays:" << "\n";); + TRACE ("qe", tout << "Failed to project arrays\n";); + } + } + }; + + + class array_project_selects_util { + typedef obj_map*> sel_map; + + struct idx_val { + expr_ref_vector idx, val; + idx_val(expr_ref_vector & idx, expr_ref_vector & val): idx(idx), val(val) {} + idx_val& operator=(idx_val const& o) { idx.reset(); val.reset(); idx.append(o.idx); val.append(o.val); return *this; } + }; + ast_manager& m; + array_util m_arr_u; + arith_util m_ari_u; + sel_map m_sel_terms; + // representative indices for eliminating selects + vector m_idxs; + app_ref_vector m_sel_consts; + expr_ref_vector m_idx_lits; + model_ref M; + model_evaluator* m_mev; + expr_safe_replace m_sub; + ast_mark m_arr_test; + + void reset () { + m_sel_terms.reset (); + m_idxs.reset(); + m_sel_consts.reset (); + m_idx_lits.reset (); + M = nullptr; + m_mev = nullptr; + m_sub.reset (); + m_arr_test.reset (); + } + + /** + * collect sel terms on array vars as given by m_arr_test + */ + void collect_selects (expr* fml) { + if (!is_app (fml)) return; + ast_mark done; + ptr_vector todo; + todo.push_back (to_app (fml)); + for (unsigned i = 0; i < todo.size(); ++i) { + app* a = todo[i]; + if (done.is_marked (a)) continue; + done.mark (a, true); + for (expr* arg : *a) { + if (!done.is_marked (arg) && is_app (arg)) { + todo.push_back (to_app (arg)); + } + } + if (m_arr_u.is_select (a)) { + expr* arr = a->get_arg (0); + if (m_arr_test.is_marked (arr)) { + ptr_vector* lst = m_sel_terms.find (to_app (arr));; + lst->push_back (a); + } + } + } + } + + struct compare_idx { + array_project_selects_util& u; + compare_idx(array_project_selects_util& u):u(u) {} + bool operator()(idx_val const& x, idx_val const& y) { + for (unsigned j = 0; j < x.val.size(); ++j) { + rational xv, yv; + VERIFY (u.m_ari_u.is_numeral(x.val[j], xv)); + VERIFY (u.m_ari_u.is_numeral(y.val[j], yv)); + if (xv < yv) return true; + if (xv > yv) return false; + } + return false; + } + }; + + expr_ref mk_lex_lt(expr_ref_vector const& xs, expr_ref_vector const& ys) { + SASSERT(xs.size() == ys.size()); + expr_ref result(m_ari_u.mk_lt(xs.back(), ys.back()), m); + for (unsigned i = xs.size()-1; i-- > 0; ) { + result = m.mk_or(m_ari_u.mk_lt(xs[i], ys[i]), + m.mk_and(m.mk_eq(xs[i], ys[i]), result)); + } + return result; + } + + /** + * model based ackermannization for sel terms of some array + * + * update sub with val consts for sel terms + */ + void ackermann (ptr_vector const& sel_terms) { + if (sel_terms.empty ()) return; + + expr* v = sel_terms.get (0)->get_arg (0); // array variable + sort* v_sort = m.get_sort (v); + sort* val_sort = get_array_range (v_sort); + unsigned arity = get_array_arity(v_sort); + + for (unsigned i = 0; i < arity; ++i) { + sort* srt = get_array_domain(v_sort, i); + if (!m_ari_u.is_real(srt) && !m_ari_u.is_int(srt)) { + TRACE("qe", tout << "unsupported index sort for Ackerman" << mk_pp(srt, m) << "\n";); + return; + } + } + + unsigned start = m_idxs.size (); // append at the end + + for (app * a : sel_terms) { + expr_ref_vector idxs(m, arity, a->get_args() + 1); + expr_ref_vector vals = (*m_mev)(idxs); + bool is_new = true; + for (unsigned j = start; j < m_idxs.size (); j++) { + if (!is_eq(m_idxs[j].val, vals)) continue; + // idx belongs to the jth equivalence class; + // substitute sel term with ith sel const + expr* c = m_sel_consts.get (j); + m_sub.insert (a, c); + // add equality (idx == repr) + m_idx_lits.push_back (mk_eq (idxs, m_idxs[j].idx)); + is_new = false; + break; + } + if (is_new) { + // new repr, val, and sel const + m_idxs.push_back(idx_val(idxs, vals)); + app_ref c (m.mk_fresh_const ("sel", val_sort), m); + m_sel_consts.push_back (c); + // substitute sel term with new const + m_sub.insert (a, c); + // extend M to include c + expr_ref val = (*m_mev)(a); + M->register_decl (c->get_decl (), val); + } + } + + // sort reprs by their value and add a chain of strict inequalities + + compare_idx cmp(*this); + std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); + + for (unsigned i = start; i < m_idxs.size()-1; i++) { + m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); + } + } + + void mk_result (expr_ref& fml) { + // conjoin idx lits + m_idx_lits.push_back(fml); + fml = mk_and (m_idx_lits); + + // substitute for sel terms + m_sub (fml); + + TRACE ("qe", tout << "after projection of selects:\n" << fml << "\n";); + } + + /** + * project selects + * populates idx lits and obtains substitution for sel terms + */ + bool project (expr* fml) { + // collect sel terms -- populate the map m_sel_terms + collect_selects (fml); + // model based ackermannization + for (auto & kv : m_sel_terms) { + TRACE ("qe",tout << "ackermann for var: " << mk_pp (kv.m_key, m) << "\n";); + ackermann (*(kv.m_value)); + } + TRACE ("qe", tout << "idx lits:\n" << m_idx_lits; ); + return true; + } + + public: + + array_project_selects_util (ast_manager& m): + m (m), + m_arr_u (m), + m_ari_u (m), + m_sel_consts (m), + m_idx_lits (m), + m_sub (m) + {} + + void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { + reset (); + model_evaluator mev(mdl); + M = &mdl; + m_mev = &mev; + + // mark vars to eliminate + // alloc empty map from array var to sel terms over it + for (app* v : arr_vars) { + m_arr_test.mark(v, true); + m_sel_terms.insert(v, alloc (ptr_vector)); + } + + // assume all arr_vars are of array sort + // and they only appear in select terms + if (project (fml)) { + mk_result (fml); + aux_vars.append (m_sel_consts); + arr_vars.reset (); + } + else { + IF_VERBOSE(2, verbose_stream() << "can't project arrays:" << "\n";); + TRACE ("qe", tout << "Failed to project arrays\n";); + } + + // dealloc + for (auto & kv : m_sel_terms) dealloc(kv.m_value); + m_sel_terms.reset (); + } + }; + + + static void array_project_eqs (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { + ast_manager& m = arr_vars.get_manager (); + array_project_eqs_util ap (m); + ap (mdl, arr_vars, fml, aux_vars); + } + + static void reduce_array_selects (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects) { + ast_manager& m = arr_vars.get_manager (); + array_select_reducer ap (m); + ap (mdl, arr_vars, fml, reduce_all_selects); + } + + static void reduce_array_selects (model& mdl, expr_ref& fml) { + ast_manager& m = fml.get_manager (); + app_ref_vector _tmp (m); + reduce_array_selects (mdl, _tmp, fml, true); + } + + static void array_project_selects (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { + ast_manager& m = arr_vars.get_manager (); + array_project_selects_util ap (m); + ap (mdl, arr_vars, fml, aux_vars); + } + + void new_array_project (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects) { + // 1. project array equalities + array_project_eqs (mdl, arr_vars, fml, aux_vars); + TRACE ("qe", + tout << "Projected array eqs:\n" << fml << "\n"; + tout << "Remaining array vars:\n" << arr_vars; + tout << "Aux vars:\n" << aux_vars; + ); + + // 2. reduce selects + if (reduce_all_selects) { + reduce_array_selects (mdl, fml); + } + else { + reduce_array_selects (mdl, arr_vars, fml, false); + } + TRACE ("qe", + tout << "Reduced selects:\n" << fml << "\n"; + ); + + // 3. project selects using model based ackermannization + array_project_selects (mdl, arr_vars, fml, aux_vars); + TRACE ("qe", + tout << "Projected array selects:\n" << fml << "\n"; + tout << "All aux vars:\n" << aux_vars; + ); + } + }; From 8ffbd5d1e54507339317d60e5146218915dbd838 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 19 May 2018 17:58:35 -0700 Subject: [PATCH 117/364] model_evaluator: respect array_as_stores option --- src/model/model_evaluator.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index eb7131406..377afda92 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -198,9 +198,6 @@ struct evaluator_cfg : public default_rewriter_cfg { if (evaluate(a->get_decl(), a->get_num_args(), a->get_args(), result)) { return BR_REWRITE1; } - if (false && m_array_as_stores && m_ar.is_array(result)) { - expand_stores(result); - } } CTRACE("model_evaluator", st != BR_FAILED, tout << result << "\n";); return st; @@ -210,7 +207,9 @@ struct evaluator_cfg : public default_rewriter_cfg { vector stores; expr_ref else_case(m); bool _unused; - if (m_ar.is_array(val) && extract_array_func_interp(val, stores, else_case, _unused)) { + if (m_array_as_stores && + m_ar.is_array(val) && + extract_array_func_interp(val, stores, else_case, _unused)) { sort* srt = m.get_sort(val); val = m_ar.mk_const_array(srt, else_case); for (unsigned i = stores.size(); i-- > 0; ) { From 23272f0d2f75dffb60a9560f98b003512617c1a0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 20 May 2018 10:38:43 -0700 Subject: [PATCH 118/364] array support for mbp Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arrays.cpp | 936 ++++++++++++++++++++++--------------------- src/qe/qe_arrays.h | 1 + src/qe/qe_mbp.cpp | 4 +- 3 files changed, 477 insertions(+), 464 deletions(-) diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index ad9d5d63f..712311f9d 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -155,415 +155,16 @@ namespace { namespace qe { - - struct array_project_plugin::imp { - - // rewriter or direct procedure. - struct rw_cfg : public default_rewriter_cfg { - ast_manager& m; - array_util& a; - expr_ref_vector m_lits; - model* m_model; - imp* m_imp; - - rw_cfg(ast_manager& m, array_util& a): - m(m), a(a), m_lits(m), m_model(nullptr) {} - - br_status reduce_app(func_decl* f, unsigned n, expr* const* args, expr_ref& result, proof_ref & pr) { - if (a.is_select(f) && a.is_store(args[0])) { - expr_ref val1(m), val2(m); - app* b = to_app(args[0]); - SASSERT(b->get_num_args() == n + 1); - for (unsigned i = 1; i < n; ++i) { - expr* arg1 = args[i]; - expr* arg2 = b->get_arg(i); - if (arg1 == arg2) { - val1 = val2 = arg1; - } - else { - VERIFY(m_model->eval(arg1, val1)); - VERIFY(m_model->eval(arg2, val2)); - } - switch(compare(val1, val2)) { - case l_true: - if (arg1 != arg2) { - m_lits.push_back(m.mk_eq(arg1, arg2)); - } - break; - case l_false: { - ptr_vector new_args; - if (i > 0) { - m_lits.resize(m_lits.size() - i); - } - m_lits.push_back(m.mk_not(m.mk_eq(arg1, arg2))); - new_args.push_back(b->get_arg(0)); - new_args.append(n-1, args+1); - result = m.mk_app(f, n, new_args.c_ptr()); - return BR_REWRITE1; - } - case l_undef: - return BR_FAILED; - } - } - result = b->get_arg(n); - return BR_DONE; - } - return BR_FAILED; - } - - lbool compare(expr* x, expr* y) { - NOT_IMPLEMENTED_YET(); - return l_undef; - } - }; - - struct indices { - expr_ref_vector m_values; - expr* const* m_vars; - - indices(ast_manager& m, model& model, unsigned n, expr* const* vars): - m_values(m), m_vars(vars) { - expr_ref val(m); - for (unsigned i = 0; i < n; ++i) { - VERIFY(model.eval(vars[i], val)); - m_values.push_back(val); - } - } - }; - - ast_manager& m; - array_util a; - scoped_ptr m_var; - - imp(ast_manager& m): m(m), a(m) {} - ~imp() {} - - bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - return false; - } - - bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - - TRACE("qe", tout << mk_pp(var, m) << "\n" << lits;); - m_var = alloc(contains_app, m, var); - - // reduce select-store redeces based on model. - // rw_cfg rw(m); - // rw(lits); - - // try first to solve for var. - if (solve_eq(model, vars, lits)) { - return true; - } - - app_ref_vector selects(m); - - // check that only non-select occurrences are in disequalities. - if (!check_diseqs(lits, selects)) { - TRACE("qe", tout << "Could not project " << mk_pp(var, m) << " for:\n" << lits << "\n";); - return false; - } - - // remove disequalities. - elim_diseqs(lits); - - // Ackerman reduction on remaining select occurrences - // either replace occurrences by model value or other node - // that is congruent to model value. - - ackermanize_select(model, selects, vars, lits); - - TRACE("qe", tout << selects << "\n" << lits << "\n";); - return true; - } - - void ackermanize_select(model& model, app_ref_vector const& selects, app_ref_vector& vars, expr_ref_vector& lits) { - expr_ref_vector vals(m), reps(m); - expr_ref val(m); - expr_safe_replace sub(m); - - if (selects.empty()) { - return; - } - - app_ref sel(m); - for (unsigned i = 0; i < selects.size(); ++i) { - sel = m.mk_fresh_const("sel", m.get_sort(selects[i])); - VERIFY (model.eval(selects[i], val)); - model.register_decl(sel->get_decl(), val); - vals.push_back(to_app(val)); - reps.push_back(val); // TODO: direct pass could handle nested selects. - vars.push_back(sel); - sub.insert(selects[i], val); - } - - sub(lits); - remove_true(lits); - project_plugin::partition_args(model, selects, lits); - project_plugin::partition_values(model, reps, lits); - } - - void remove_true(expr_ref_vector& lits) { - for (unsigned i = 0; i < lits.size(); ++i) { - if (m.is_true(lits[i].get())) { - project_plugin::erase(lits, i); - } - } - } - - bool contains_x(expr* e) { - return (*m_var)(e); - } - - void mk_eq(indices const& x, indices const& y, expr_ref_vector& lits) { - SASSERT(x.m_values.size() == y.m_values.size()); - unsigned n = x.m_values.size(); - for (unsigned j = 0; j < n; ++j) { - lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); - } - } - - // check that x occurs only under selects or in disequalities. - bool check_diseqs(expr_ref_vector const& lits, app_ref_vector& selects) { - expr_mark mark; - ptr_vector todo; - app* e; - for (unsigned i = 0; i < lits.size(); ++i) { - e = to_app(lits[i]); - if (is_diseq_x(e)) { - continue; - } - if (contains_x(e)) { - todo.push_back(e); - } - } - while (!todo.empty()) { - e = todo.back(); - todo.pop_back(); - if (mark.is_marked(e)) { - continue; - } - mark.mark(e); - if (m_var->x() == e) { - return false; - } - unsigned start = 0; - if (a.is_select(e)) { - if (e->get_arg(0) == m_var->x()) { - start = 1; - selects.push_back(e); - } - } - for (unsigned i = start; i < e->get_num_args(); ++i) { - todo.push_back(to_app(e->get_arg(i))); - } - } - return true; - } - - void elim_diseqs(expr_ref_vector& lits) { - for (unsigned i = 0; i < lits.size(); ++i) { - if (is_diseq_x(lits[i].get())) { - project_plugin::erase(lits, i); - } - } - } - - bool is_update_x(app* e) { - do { - if (m_var->x() == e) { - return true; - } - if (a.is_store(e) && contains_x(e->get_arg(0))) { - for (unsigned i = 1; i < e->get_num_args(); ++i) { - if (contains_x(e->get_arg(i))) { - return false; - } - } - e = to_app(e->get_arg(0)); - continue; - } - } - while (false); - return false; - } - - bool is_diseq_x(expr* e) { - expr *f, * s, *t; - if (m.is_not(e, f) && m.is_eq(f, s, t)) { - if (contains_x(s) && !contains_x(t) && is_update_x(to_app(s))) return true; - if (contains_x(t) && !contains_x(s) && is_update_x(to_app(t))) return true; - } - return false; - } - - bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - // find an equality to solve for. - expr* s, *t; - for (unsigned i = 0; i < lits.size(); ++i) { - if (m.is_eq(lits[i].get(), s, t)) { - vector idxs; - expr_ref save(m), back(m); - save = lits[i].get(); - back = lits.back(); - lits[i] = back; - lits.pop_back(); - unsigned sz = lits.size(); - if (contains_x(s) && !contains_x(t) && is_app(s)) { - if (solve(model, to_app(s), t, idxs, vars, lits)) { - return true; - } - } - else if (contains_x(t) && !contains_x(s) && is_app(t)) { - if (solve(model, to_app(t), s, idxs, vars, lits)) { - return true; - } - } - // put back the equality literal. - lits.resize(sz); - lits.push_back(back); - lits[i] = save; - } - // TBD: not distinct? - } - return false; - } - - bool solve(model& model, app* s, expr* t, vector& idxs, app_ref_vector& vars, expr_ref_vector& lits) { - SASSERT(contains_x(s)); - SASSERT(!contains_x(t)); - - if (s == m_var->x()) { - expr_ref result(t, m); - expr_ref_vector args(m); - sort* range = get_array_range(m.get_sort(s)); - for (unsigned i = 0; i < idxs.size(); ++i) { - app_ref var(m), sel(m); - expr_ref val(m); - var = m.mk_fresh_const("value", range); - vars.push_back(var); - args.reset(); - - args.push_back (s); - args.append(idxs[i].m_values.size(), idxs[i].m_vars); - sel = a.mk_select (args.size (), args.c_ptr ()); - VERIFY (model.eval (sel, val)); - model.register_decl (var->get_decl (), val); - - args[0] = result; - args.push_back(var); - result = a.mk_store(args.size(), args.c_ptr()); - } - expr_safe_replace sub(m); - sub.insert(s, result); - for (unsigned i = 0; i < lits.size(); ++i) { - sub(lits[i].get(), result); - lits[i] = result; - } - return true; - } - - if (a.is_store(s)) { - unsigned n = s->get_num_args()-2; - indices idx(m, model, n, s->get_args()+1); - for (unsigned i = 1; i < n; ++i) { - if (contains_x(s->get_arg(i))) { - return false; - } - } - unsigned i; - expr_ref_vector args(m); - switch (contains(idx, idxs, i)) { - case l_true: - mk_eq(idx, idxs[i], lits); - return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); - case l_false: - for (unsigned i = 0; i < idxs.size(); ++i) { - expr_ref_vector eqs(m); - mk_eq(idx, idxs[i], eqs); - lits.push_back(m.mk_not(mk_and(eqs))); // TBD: extract single index of difference based on model. - } - args.push_back(t); - args.append(n, s->get_args()+1); - lits.push_back(m.mk_eq(a.mk_select(args.size(), args.c_ptr()), s->get_arg(n+1))); - idxs.push_back(idx); - return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); - case l_undef: - return false; - } - } - return false; - } - - lbool contains(indices const& idx, vector const& idxs, unsigned& j) { - for (unsigned i = 0; i < idxs.size(); ++i) { - switch (compare(idx, idxs[i])) { - case l_true: - j = i; - return l_true; - case l_false: - break; - case l_undef: - return l_undef; - } - } - return l_false; - } - - lbool compare(indices const& idx1, indices const& idx2) { - unsigned n = idx1.m_values.size(); - for (unsigned i = 0; i < n; ++i) { - switch (compare(idx1.m_values[i], idx2.m_values[i])) { - case l_true: - break; - case l_false: - return l_false; - case l_undef: - return l_undef; - } - } - return l_true; - } - - lbool compare(expr* val1, expr* val2) { - if (m.are_equal (val1, val2)) return l_true; - if (m.are_distinct (val1, val2)) return l_false; - - if (is_uninterp(val1) || - is_uninterp(val2)) { - // TBD chase definition of nested array. - return l_undef; - } - return l_undef; - } - }; - - - array_project_plugin::array_project_plugin(ast_manager& m) { - m_imp = alloc(imp, m); - } - - array_project_plugin::~array_project_plugin() { - dealloc(m_imp); - } - - bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - return (*m_imp)(model, var, vars, lits); - } - - bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - return m_imp->solve(model, vars, lits); - } - - family_id array_project_plugin::get_family_id() { - return m_imp->a.get_family_id(); - } - static bool is_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; return true; } + static bool is_eq(vector const& xs, vector const& ys) { + for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; + return true; + } + static expr_ref mk_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { ast_manager& m = xs.get_manager(); expr_ref_vector eqs(m); @@ -571,7 +172,6 @@ namespace qe { return mk_and(eqs); } - class array_project_eqs_util { ast_manager& m; array_util m_arr_u; @@ -679,8 +279,10 @@ namespace qe { expr_ref_vector args (m); bool all_done = true; for (expr * arg : *a) { - if (!is_app (arg)) continue; - if (!done.is_marked (arg)) { + if (!is_app (arg)) { + args.push_back(arg); + } + else if (!done.is_marked (arg)) { all_done = false; todo.push_back (to_app (arg)); } @@ -1267,9 +869,15 @@ namespace qe { typedef obj_map*> sel_map; struct idx_val { - expr_ref_vector idx, val; - idx_val(expr_ref_vector & idx, expr_ref_vector & val): idx(idx), val(val) {} - idx_val& operator=(idx_val const& o) { idx.reset(); val.reset(); idx.append(o.idx); val.append(o.val); return *this; } + expr_ref_vector idx; + expr_ref_vector val; + vector rval; + idx_val(expr_ref_vector & idx, expr_ref_vector & val, vector const& rval): idx(idx), val(val), rval(rval) {} + idx_val& operator=(idx_val const& o) { + idx.reset(); val.reset(); rval.reset(); + idx.append(o.idx); val.append(o.val); rval.append(o.rval); + return *this; + } }; ast_manager& m; array_util m_arr_u; @@ -1322,14 +930,24 @@ namespace qe { } } + vector to_num(expr_ref_vector const& vals) { + vector rs; + rational r; + for (expr* v : vals) { + VERIFY (m_ari_u.is_numeral(v, r)); + rs.push_back(r); + } + return rs; + } + struct compare_idx { array_project_selects_util& u; compare_idx(array_project_selects_util& u):u(u) {} bool operator()(idx_val const& x, idx_val const& y) { - for (unsigned j = 0; j < x.val.size(); ++j) { - rational xv, yv; - VERIFY (u.m_ari_u.is_numeral(x.val[j], xv)); - VERIFY (u.m_ari_u.is_numeral(y.val[j], yv)); + SASSERT(x.rval.size() == y.rval.size()); + for (unsigned j = 0; j < x.rval.size(); ++j) { + rational const& xv = x.rval[j]; + rational const& yv = y.rval[j]; if (xv < yv) return true; if (xv > yv) return false; } @@ -1338,7 +956,7 @@ namespace qe { }; expr_ref mk_lex_lt(expr_ref_vector const& xs, expr_ref_vector const& ys) { - SASSERT(xs.size() == ys.size()); + SASSERT(xs.size() == ys.size() && !xs.empty()); expr_ref result(m_ari_u.mk_lt(xs.back(), ys.back()), m); for (unsigned i = xs.size()-1; i-- > 0; ) { result = m.mk_or(m_ari_u.mk_lt(xs[i], ys[i]), @@ -1359,17 +977,17 @@ namespace qe { sort* v_sort = m.get_sort (v); sort* val_sort = get_array_range (v_sort); unsigned arity = get_array_arity(v_sort); - - for (unsigned i = 0; i < arity; ++i) { + bool is_numeric = true; + for (unsigned i = 0; i < arity && is_numeric; ++i) { sort* srt = get_array_domain(v_sort, i); if (!m_ari_u.is_real(srt) && !m_ari_u.is_int(srt)) { - TRACE("qe", tout << "unsupported index sort for Ackerman" << mk_pp(srt, m) << "\n";); - return; - } + TRACE("qe", tout << "non-arithmetic index sort for Ackerman" << mk_pp(srt, m) << "\n";); + // TBD: generalize to also bit-vectors. + is_numeric = false; + } } - + unsigned start = m_idxs.size (); // append at the end - for (app * a : sel_terms) { expr_ref_vector idxs(m, arity, a->get_args() + 1); expr_ref_vector vals = (*m_mev)(idxs); @@ -1387,7 +1005,8 @@ namespace qe { } if (is_new) { // new repr, val, and sel const - m_idxs.push_back(idx_val(idxs, vals)); + vector rvals = to_num(vals); + m_idxs.push_back(idx_val(idxs, vals, rvals)); app_ref c (m.mk_fresh_const ("sel", val_sort), m); m_sel_consts.push_back (c); // substitute sel term with new const @@ -1398,13 +1017,25 @@ namespace qe { } } - // sort reprs by their value and add a chain of strict inequalities - - compare_idx cmp(*this); - std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); - - for (unsigned i = start; i < m_idxs.size()-1; i++) { - m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); + if (is_numeric) { + // sort reprs by their value and add a chain of strict inequalities + compare_idx cmp(*this); + std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); + for (unsigned i = start; i + 1 < m_idxs.size(); ++i) { + m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); + } + } + else if (arity == 1) { + // create distinct constraint. + expr_ref_vector xs(m); + for (unsigned i = start; i < m_idxs.size(); ++i) { + xs.append(m_idxs[i].idx); + } + m_idx_lits.push_back(m.mk_distinct(xs.size(), xs.c_ptr())); + } + else { + NOT_IMPLEMENTED_YET(); + // use a tuple. } } @@ -1447,6 +1078,7 @@ namespace qe { {} void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { + if (arr_vars.empty()) return; reset (); model_evaluator mev(mdl); M = &mdl; @@ -1478,33 +1110,414 @@ namespace qe { }; - static void array_project_eqs (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { - ast_manager& m = arr_vars.get_manager (); - array_project_eqs_util ap (m); - ap (mdl, arr_vars, fml, aux_vars); + struct array_project_plugin::imp { + + // rewriter or direct procedure. + struct rw_cfg : public default_rewriter_cfg { + ast_manager& m; + array_util& a; + expr_ref_vector m_lits; + model* m_model; + imp* m_imp; + + rw_cfg(ast_manager& m, array_util& a): + m(m), a(a), m_lits(m), m_model(nullptr) {} + + br_status reduce_app(func_decl* f, unsigned n, expr* const* args, expr_ref& result, proof_ref & pr) { + if (a.is_select(f) && a.is_store(args[0])) { + expr_ref val1(m), val2(m); + app* b = to_app(args[0]); + SASSERT(b->get_num_args() == n + 1); + for (unsigned i = 1; i < n; ++i) { + expr* arg1 = args[i]; + expr* arg2 = b->get_arg(i); + if (arg1 == arg2) { + val1 = val2 = arg1; + } + else { + VERIFY(m_model->eval(arg1, val1)); + VERIFY(m_model->eval(arg2, val2)); + } + switch(compare(val1, val2)) { + case l_true: + if (arg1 != arg2) { + m_lits.push_back(m.mk_eq(arg1, arg2)); + } + break; + case l_false: { + ptr_vector new_args; + if (i > 0) { + m_lits.resize(m_lits.size() - i); + } + m_lits.push_back(m.mk_not(m.mk_eq(arg1, arg2))); + new_args.push_back(b->get_arg(0)); + new_args.append(n-1, args+1); + result = m.mk_app(f, n, new_args.c_ptr()); + return BR_REWRITE1; + } + case l_undef: + return BR_FAILED; + } + } + result = b->get_arg(n); + return BR_DONE; + } + return BR_FAILED; + } + + lbool compare(expr* x, expr* y) { + NOT_IMPLEMENTED_YET(); + return l_undef; + } + }; + + struct indices { + expr_ref_vector m_values; + expr* const* m_vars; + + indices(ast_manager& m, model& model, unsigned n, expr* const* vars): + m_values(m), m_vars(vars) { + expr_ref val(m); + for (unsigned i = 0; i < n; ++i) { + VERIFY(model.eval(vars[i], val)); + m_values.push_back(val); + } + } + }; + + ast_manager& m; + array_util a; + scoped_ptr m_var; + + imp(ast_manager& m): m(m), a(m) {} + ~imp() {} + + bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return false; + } + + bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + + TRACE("qe", tout << mk_pp(var, m) << "\n" << lits;); + m_var = alloc(contains_app, m, var); + + // reduce select-store redeces based on model. + // rw_cfg rw(m); + // rw(lits); + + // try first to solve for var. + if (solve_eq(model, vars, lits)) { + return true; + } + + app_ref_vector selects(m); + + // check that only non-select occurrences are in disequalities. + if (!check_diseqs(lits, selects)) { + TRACE("qe", tout << "Could not project " << mk_pp(var, m) << " for:\n" << lits << "\n";); + return false; + } + + // remove disequalities. + elim_diseqs(lits); + + // Ackerman reduction on remaining select occurrences + // either replace occurrences by model value or other node + // that is congruent to model value. + + ackermanize_select(model, selects, vars, lits); + + TRACE("qe", tout << selects << "\n" << lits << "\n";); + return true; + } + + void ackermanize_select(model& model, app_ref_vector const& selects, app_ref_vector& vars, expr_ref_vector& lits) { + expr_ref_vector vals(m), reps(m); + expr_ref val(m); + expr_safe_replace sub(m); + + if (selects.empty()) { + return; + } + + app_ref sel(m); + for (unsigned i = 0; i < selects.size(); ++i) { + sel = m.mk_fresh_const("sel", m.get_sort(selects[i])); + VERIFY (model.eval(selects[i], val)); + model.register_decl(sel->get_decl(), val); + vals.push_back(to_app(val)); + reps.push_back(val); // TODO: direct pass could handle nested selects. + vars.push_back(sel); + sub.insert(selects[i], val); + } + + sub(lits); + remove_true(lits); + project_plugin::partition_args(model, selects, lits); + project_plugin::partition_values(model, reps, lits); + } + + void remove_true(expr_ref_vector& lits) { + for (unsigned i = 0; i < lits.size(); ++i) { + if (m.is_true(lits[i].get())) { + project_plugin::erase(lits, i); + } + } + } + + bool contains_x(expr* e) { + return (*m_var)(e); + } + + void mk_eq(indices const& x, indices const& y, expr_ref_vector& lits) { + SASSERT(x.m_values.size() == y.m_values.size()); + unsigned n = x.m_values.size(); + for (unsigned j = 0; j < n; ++j) { + lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); + } + } + + // check that x occurs only under selects or in disequalities. + bool check_diseqs(expr_ref_vector const& lits, app_ref_vector& selects) { + expr_mark mark; + ptr_vector todo; + app* e; + for (unsigned i = 0; i < lits.size(); ++i) { + e = to_app(lits[i]); + if (is_diseq_x(e)) { + continue; + } + if (contains_x(e)) { + todo.push_back(e); + } + } + while (!todo.empty()) { + e = todo.back(); + todo.pop_back(); + if (mark.is_marked(e)) { + continue; + } + mark.mark(e); + if (m_var->x() == e) { + return false; + } + unsigned start = 0; + if (a.is_select(e)) { + if (e->get_arg(0) == m_var->x()) { + start = 1; + selects.push_back(e); + } + } + for (unsigned i = start; i < e->get_num_args(); ++i) { + todo.push_back(to_app(e->get_arg(i))); + } + } + return true; + } + + void elim_diseqs(expr_ref_vector& lits) { + for (unsigned i = 0; i < lits.size(); ++i) { + if (is_diseq_x(lits[i].get())) { + project_plugin::erase(lits, i); + } + } + } + + bool is_update_x(app* e) { + do { + if (m_var->x() == e) { + return true; + } + if (a.is_store(e) && contains_x(e->get_arg(0))) { + for (unsigned i = 1; i < e->get_num_args(); ++i) { + if (contains_x(e->get_arg(i))) { + return false; + } + } + e = to_app(e->get_arg(0)); + continue; + } + } + while (false); + return false; + } + + bool is_diseq_x(expr* e) { + expr *f, * s, *t; + if (m.is_not(e, f) && m.is_eq(f, s, t)) { + if (contains_x(s) && !contains_x(t) && is_update_x(to_app(s))) return true; + if (contains_x(t) && !contains_x(s) && is_update_x(to_app(t))) return true; + } + return false; + } + + bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + // find an equality to solve for. + expr* s, *t; + for (unsigned i = 0; i < lits.size(); ++i) { + if (m.is_eq(lits[i].get(), s, t)) { + vector idxs; + expr_ref save(m), back(m); + save = lits[i].get(); + back = lits.back(); + lits[i] = back; + lits.pop_back(); + unsigned sz = lits.size(); + if (contains_x(s) && !contains_x(t) && is_app(s)) { + if (solve(model, to_app(s), t, idxs, vars, lits)) { + return true; + } + } + else if (contains_x(t) && !contains_x(s) && is_app(t)) { + if (solve(model, to_app(t), s, idxs, vars, lits)) { + return true; + } + } + // put back the equality literal. + lits.resize(sz); + lits.push_back(back); + lits[i] = save; + } + // TBD: not distinct? + } + return false; + } + + bool solve(model& model, app* s, expr* t, vector& idxs, app_ref_vector& vars, expr_ref_vector& lits) { + SASSERT(contains_x(s)); + SASSERT(!contains_x(t)); + + if (s == m_var->x()) { + expr_ref result(t, m); + expr_ref_vector args(m); + sort* range = get_array_range(m.get_sort(s)); + for (unsigned i = 0; i < idxs.size(); ++i) { + app_ref var(m), sel(m); + expr_ref val(m); + var = m.mk_fresh_const("value", range); + vars.push_back(var); + args.reset(); + + args.push_back (s); + args.append(idxs[i].m_values.size(), idxs[i].m_vars); + sel = a.mk_select (args.size (), args.c_ptr ()); + VERIFY (model.eval (sel, val)); + model.register_decl (var->get_decl (), val); + + args[0] = result; + args.push_back(var); + result = a.mk_store(args.size(), args.c_ptr()); + } + expr_safe_replace sub(m); + sub.insert(s, result); + for (unsigned i = 0; i < lits.size(); ++i) { + sub(lits[i].get(), result); + lits[i] = result; + } + return true; + } + + if (a.is_store(s)) { + unsigned n = s->get_num_args()-2; + indices idx(m, model, n, s->get_args()+1); + for (unsigned i = 1; i < n; ++i) { + if (contains_x(s->get_arg(i))) { + return false; + } + } + unsigned i; + expr_ref_vector args(m); + switch (contains(idx, idxs, i)) { + case l_true: + mk_eq(idx, idxs[i], lits); + return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); + case l_false: + for (unsigned i = 0; i < idxs.size(); ++i) { + expr_ref_vector eqs(m); + mk_eq(idx, idxs[i], eqs); + lits.push_back(m.mk_not(mk_and(eqs))); // TBD: extract single index of difference based on model. + } + args.push_back(t); + args.append(n, s->get_args()+1); + lits.push_back(m.mk_eq(a.mk_select(args.size(), args.c_ptr()), s->get_arg(n+1))); + idxs.push_back(idx); + return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); + case l_undef: + return false; + } + } + return false; + } + + lbool contains(indices const& idx, vector const& idxs, unsigned& j) { + for (unsigned i = 0; i < idxs.size(); ++i) { + switch (compare(idx, idxs[i])) { + case l_true: + j = i; + return l_true; + case l_false: + break; + case l_undef: + return l_undef; + } + } + return l_false; + } + + lbool compare(indices const& idx1, indices const& idx2) { + unsigned n = idx1.m_values.size(); + for (unsigned i = 0; i < n; ++i) { + switch (compare(idx1.m_values[i], idx2.m_values[i])) { + case l_true: + break; + case l_false: + return l_false; + case l_undef: + return l_undef; + } + } + return l_true; + } + + lbool compare(expr* val1, expr* val2) { + if (m.are_equal (val1, val2)) return l_true; + if (m.are_distinct (val1, val2)) return l_false; + + if (is_uninterp(val1) || + is_uninterp(val2)) { + // TBD chase definition of nested array. + return l_undef; + } + return l_undef; + } + }; + + + array_project_plugin::array_project_plugin(ast_manager& m) { + m_imp = alloc(imp, m); + } + + array_project_plugin::~array_project_plugin() { + dealloc(m_imp); + } + + bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + return (*m_imp)(model, var, vars, lits); } - static void reduce_array_selects (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects) { - ast_manager& m = arr_vars.get_manager (); - array_select_reducer ap (m); - ap (mdl, arr_vars, fml, reduce_all_selects); + bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return m_imp->solve(model, vars, lits); + } + + family_id array_project_plugin::get_family_id() { + return m_imp->a.get_family_id(); } - static void reduce_array_selects (model& mdl, expr_ref& fml) { - ast_manager& m = fml.get_manager (); - app_ref_vector _tmp (m); - reduce_array_selects (mdl, _tmp, fml, true); - } - - static void array_project_selects (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { - ast_manager& m = arr_vars.get_manager (); - array_project_selects_util ap (m); - ap (mdl, arr_vars, fml, aux_vars); - } - - void new_array_project (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects) { + void array_project_plugin::operator()(model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects) { // 1. project array equalities - array_project_eqs (mdl, arr_vars, fml, aux_vars); + ast_manager& m = fml.get_manager(); + array_project_eqs_util pe (m); + pe (mdl, arr_vars, fml, aux_vars); TRACE ("qe", tout << "Projected array eqs:\n" << fml << "\n"; tout << "Remaining array vars:\n" << arr_vars; @@ -1512,23 +1525,22 @@ namespace qe { ); // 2. reduce selects - if (reduce_all_selects) { - reduce_array_selects (mdl, fml); - } - else { - reduce_array_selects (mdl, arr_vars, fml, false); - } - TRACE ("qe", - tout << "Reduced selects:\n" << fml << "\n"; - ); + array_select_reducer rs (m); + rs (mdl, arr_vars, fml, reduce_all_selects); + + TRACE ("qe", tout << "Reduced selects:\n" << fml << "\n"; ); // 3. project selects using model based ackermannization - array_project_selects (mdl, arr_vars, fml, aux_vars); + array_project_selects_util ps (m); + ps (mdl, arr_vars, fml, aux_vars); + TRACE ("qe", tout << "Projected array selects:\n" << fml << "\n"; tout << "All aux vars:\n" << aux_vars; ); } + + }; diff --git a/src/qe/qe_arrays.h b/src/qe/qe_arrays.h index a955250a8..f2d69b564 100644 --- a/src/qe/qe_arrays.h +++ b/src/qe/qe_arrays.h @@ -34,6 +34,7 @@ namespace qe { ~array_project_plugin() override; bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; + void operator()(model& model, app_ref_vector& vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects); family_id get_family_id() override; }; diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 0b4574951..a6bdbd1dc 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -664,8 +664,8 @@ public: // project arrays if (!array_vars.empty()) { - NOT_IMPLEMENTED_YET(); - // qe::array_project (mdl, array_vars, fml, vars, m_reduce_all_selects); + qe::array_project_plugin ap(m); + ap(mdl, array_vars, fml, vars, m_reduce_all_selects); SASSERT (array_vars.empty ()); m_rw (fml); SASSERT (!m.is_false (fml)); From aeb2f3c4bbe09d61a8a063c764a426d6e70c7d91 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 09:24:57 -0700 Subject: [PATCH 119/364] factor out inherit_properties --- src/muz/spacer/spacer_context.cpp | 65 +++++++++++++++---------------- src/muz/spacer/spacer_context.h | 1 + 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index fc0246ec6..9f54ac0d8 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1749,50 +1749,51 @@ app* pred_transformer::extend_initial (expr *e) /// pred_transformer::frames -bool pred_transformer::frames::add_lemma(lemma *lem) +bool pred_transformer::frames::add_lemma(lemma *new_lemma) { - TRACE("spacer", tout << "add-lemma: " << pp_level(lem->level()) << " " + TRACE("spacer", tout << "add-lemma: " << pp_level(new_lemma->level()) << " " << m_pt.head()->get_name() << " " - << mk_pp(lem->get_expr(), m_pt.get_ast_manager()) << "\n";); + << mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";); for (unsigned i = 0, sz = m_lemmas.size(); i < sz; ++i) { - if (m_lemmas [i]->get_expr() == lem->get_expr()) { - m_pt.get_context().new_lemma_eh(m_pt, lem); + if (m_lemmas[i]->get_expr() == new_lemma->get_expr()) { + m_pt.get_context().new_lemma_eh(m_pt, new_lemma); // extend bindings if needed - if (!lem->get_bindings().empty()) { - m_lemmas [i]->add_binding(lem->get_bindings()); + if (!new_lemma->get_bindings().empty()) { + m_lemmas[i]->add_binding(new_lemma->get_bindings()); } // if the lemma is at a higher level, skip it, - // but still assert any new instances - if (m_lemmas [i]->level() >= lem->level()) { + if (m_lemmas[i]->level() >= new_lemma->level()) { TRACE("spacer", tout << "Already at a higher level: " - << pp_level(m_lemmas [i]->level()) << "\n";); - if (!lem->get_bindings().empty()) { + << pp_level(m_lemmas[i]->level()) << "\n";); + // but, since the instances might be new, assert the + // instances that have been copied into m_lemmas[i] + if (!new_lemma->get_bindings().empty()) { m_pt.add_lemma_core(m_lemmas[i], true); } + // no new lemma added return false; } // update level of the existing lemma - m_lemmas [i]->set_level(lem->level()); + m_lemmas[i]->set_level(new_lemma->level()); // assert lemma in the solver m_pt.add_lemma_core(m_lemmas[i], false); // move the lemma to its new place to maintain sortedness - for (unsigned j = i; (j + 1) < sz && m_lt(m_lemmas [j + 1], m_lemmas[j]); ++j) { + for (unsigned j = i; (j + 1) < sz && m_lt(m_lemmas[j + 1], m_lemmas[j]); ++j) { m_lemmas.swap (j, j+1); } - return true; } } - // did not find, create new lemma - m_lemmas.push_back(lem); + // new_lemma is really new + m_lemmas.push_back(new_lemma); m_sorted = false; - m_pt.add_lemma_core(lem); + m_pt.add_lemma_core(new_lemma); - if (!lem->external()) { - m_pt.get_context().new_lemma_eh(m_pt, lem); + if (!new_lemma->external()) { + m_pt.get_context().new_lemma_eh(m_pt, new_lemma); } return true; } @@ -2029,10 +2030,7 @@ void context::reset() { TRACE("spacer", tout << "\n";); m_pob_queue.reset(); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } + for (auto &entry: m_rels) {dealloc(entry.m_value);} m_rels.reset(); m_query = nullptr; m_last_result = l_undef; @@ -2093,22 +2091,23 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) for (auto &entry : rels) {entry.m_value->init_reach_facts();} } +void context::inherit_properties(const decl2rel &rels) { + for (auto &entry : rels) { + pred_transformer *pt = nullptr; + if (m_rels.find(entry.m_key, pt)) { + entry.m_value->inherit_properties(*pt); + } + } +} void context::update_rules(datalog::rule_set& rules) { decl2rel rels; init_lemma_generalizers(rules); init_rules(rules, rels); - decl2rel::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - pred_transformer* pt = nullptr; - if (m_rels.find(it->m_key, pt)) { - it->m_value->inherit_properties(*pt); - } - } + inherit_properties(rels); reset(); - it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - m_rels.insert(it->m_key, it->m_value); + for (auto &entry : rels) { + m_rels.insert(entry.m_key, entry.m_value); } } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 9be990d8e..db05df638 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -812,6 +812,7 @@ class context { // Initialization void init_lemma_generalizers(datalog::rule_set& rules); + void inherit_properties(const decl2rel& rels); bool check_invariant(unsigned lvl); bool check_invariant(unsigned lvl, func_decl* fn); From 4de58a42fe3fc6a975133947bf1f1ee7e2f334d0 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 09:46:15 -0700 Subject: [PATCH 120/364] Update initialization order --- src/muz/spacer/spacer_context.cpp | 39 ++++++++++++++++++++++--------- src/muz/spacer/spacer_context.h | 10 +++++--- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 9f54ac0d8..2baa70f46 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1720,7 +1720,7 @@ void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, datalog:: } } -void pred_transformer::inherit_properties(pred_transformer& other) +void pred_transformer::inherit_lemmas(pred_transformer& other) {m_frames.inherit_frames (other.m_frames);} app* pred_transformer::extend_initial (expr *e) @@ -2091,24 +2091,37 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) for (auto &entry : rels) {entry.m_value->init_reach_facts();} } -void context::inherit_properties(const decl2rel &rels) { +void context::inherit_lemmas(const decl2rel &rels) { for (auto &entry : rels) { pred_transformer *pt = nullptr; if (m_rels.find(entry.m_key, pt)) { - entry.m_value->inherit_properties(*pt); + entry.m_value->inherit_lemmas(*pt); } } } + void context::update_rules(datalog::rule_set& rules) { decl2rel rels; - init_lemma_generalizers(rules); + // SMT params must be set before any expression is asserted to any + // solver + init_global_smt_params(); + // constructs new pred transformers and asserts trans and init init_rules(rules, rels); - inherit_properties(rels); + // inherits lemmas from m_rels into rels + inherit_lemmas(rels); + // switch context to new rels + init(rels); + // re-initialize lemma generalizers + init_lemma_generalizers(); +} + +void context::init(const decl2rel &rels) { + // reset context. Current state is all stored in rels reset(); - for (auto &entry : rels) { - m_rels.insert(entry.m_key, entry.m_value); - } + // re-initialize context + for (auto &entry : rels) + {m_rels.insert(entry.m_key, entry.m_value);} } unsigned context::get_num_levels(func_decl* p) @@ -2250,9 +2263,8 @@ void context::reset_lemma_generalizers() m_lemma_generalizers.reset(); } -void context::init_lemma_generalizers(datalog::rule_set& rules) -{ - reset_lemma_generalizers(); +// initialize global SMT parameters shared by all solvers +void context::init_global_smt_params() { m.toggle_proof_mode(PGM_ENABLED); smt_params &fparams = m_pm.fparams (); if (!m_params.spacer_eq_prop ()) { @@ -2282,6 +2294,11 @@ void context::init_lemma_generalizers(datalog::rule_set& rules) fparams.m_ng_lift_ite = LI_FULL; } +} +void context::init_lemma_generalizers() +{ + reset_lemma_generalizers(); + if (m_params.spacer_q3_use_qgen()) { m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, true)); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index db05df638..62d90e4c0 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -428,7 +428,7 @@ public: void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r); - void inherit_properties(pred_transformer& other); + void inherit_lemmas(pred_transformer& other); void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars, bool is_init); @@ -811,8 +811,9 @@ class context { // Initialization - void init_lemma_generalizers(datalog::rule_set& rules); - void inherit_properties(const decl2rel& rels); + void init_lemma_generalizers(); + void inherit_lemmas(const decl2rel& rels); + void init_global_smt_params(); bool check_invariant(unsigned lvl); bool check_invariant(unsigned lvl, func_decl* fn); @@ -821,6 +822,9 @@ class context { void init_rules(datalog::rule_set& rules, decl2rel& transformers); + // (re)initialize context with new relations + void init(const decl2rel &rels); + void simplify_formulas(); void reset_lemma_generalizers(); From 9550fd1ec4d7fbae30e99ef18a9a4b55bcf422db Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 10:44:29 -0700 Subject: [PATCH 121/364] proof-checker: handle true-axiom --- src/ast/proofs/proof_checker.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ast/proofs/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp index 24841016c..fdbf768aa 100644 --- a/src/ast/proofs/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -211,6 +211,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { switch(k) { case PR_UNDEF: return true; + case PR_TRUE: + return true; case PR_ASSERTED: return true; case PR_GOAL: From 6514741e3fe481b1cc0f67fe41a6e463fa0a8e19 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 10:45:07 -0700 Subject: [PATCH 122/364] proof-checker: replace match_negated with ast_manager::is_complement is_complement matches true and false, while match_negated does not Necessary to handle uses of true-axiom --- src/ast/proofs/proof_checker.cpp | 149 +++++++++++++++---------------- 1 file changed, 74 insertions(+), 75 deletions(-) diff --git a/src/ast/proofs/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp index fdbf768aa..d0f8a7994 100644 --- a/src/ast/proofs/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -1,4 +1,3 @@ - /*++ Copyright (c) 2015 Microsoft Corporation @@ -16,7 +15,7 @@ Copyright (c) 2015 Microsoft Corporation #define SAME_OP(_d1_, _d2_) ((_d1_ == _d2_) || (IS_EQUIV(_d1_) && IS_EQUIV(_d2_))) -proof_checker::hyp_decl_plugin::hyp_decl_plugin() : +proof_checker::hyp_decl_plugin::hyp_decl_plugin() : m_cons(nullptr), m_atom(nullptr), m_nil(nullptr), @@ -59,13 +58,13 @@ func_decl * proof_checker::hyp_decl_plugin::mk_func_decl(decl_kind k) { } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl( - decl_kind k, unsigned num_parameters, parameter const * parameters, + decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { return mk_func_decl(k); } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl( - decl_kind k, unsigned num_parameters, parameter const * parameters, + decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { return mk_func_decl(k); } @@ -84,7 +83,7 @@ void proof_checker::hyp_decl_plugin::get_sort_names(svector & sort } } -proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m), +proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m), m_dump_lemmas(false), m_logic("AUFLIA"), m_proof_lemma_id(0) { symbol fam_name("proof_hypothesis"); if (!m.has_plugin(fam_name)) { @@ -98,16 +97,16 @@ proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pi bool proof_checker::check(proof* p, expr_ref_vector& side_conditions) { proof_ref curr(m); m_todo.push_back(p); - + bool result = true; while (result && !m_todo.empty()) { curr = m_todo.back(); m_todo.pop_back(); result = check1(curr.get(), side_conditions); if (!result) { - IF_VERBOSE(0, ast_ll_pp(verbose_stream() << "Proof check failed\n", m, curr.get());); + IF_VERBOSE(0, ast_ll_pp(verbose_stream() << "Proof check failed\n", m, curr.get());); UNREACHABLE(); - } + } } m_hypotheses.reset(); @@ -157,10 +156,10 @@ bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) { } return false; } - case PR_SPC_REWRITE: - case PR_SUPERPOSITION: + case PR_SPC_REWRITE: + case PR_SUPERPOSITION: case PR_EQUALITY_RESOLUTION: - case PR_SPC_RESOLUTION: + case PR_SPC_RESOLUTION: case PR_FACTORING: case PR_SPC_DER: { if (match_fact(p, fact)) { @@ -176,7 +175,7 @@ bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) { side_conditions.push_back(rewrite_cond.get()); return true; } - return false; + return false; } default: UNREACHABLE(); @@ -201,7 +200,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { func_decl_ref f1(m), f2(m); expr_ref_vector terms1(m), terms2(m), terms(m); sort_ref_vector decls1(m), decls2(m); - + if (match_proof(p, proofs)) { for (unsigned i = 0; i < proofs.size(); ++i) { add_premise(proofs.get(i)); @@ -231,8 +230,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return false; } case PR_REFLEXIVITY: { - if (match_fact(p, fact) && - match_proof(p) && + if (match_fact(p, fact) && + match_proof(p) && (match_equiv(fact, t1, t2) || match_oeq(fact, t1, t2)) && (t1.get() == t2.get())) { return true; @@ -245,7 +244,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { match_proof(p, p1) && match_fact(p1.get(), fml) && match_binary(fact.get(), d1, l1, r1) && - match_binary(fml.get(), d2, l2, r2) && + match_binary(fml.get(), d2, l2, r2) && SAME_OP(d1.get(), d2.get()) && l1.get() == r2.get() && r1.get() == l2.get()) { @@ -273,7 +272,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } UNREACHABLE(); return false; - } + } case PR_TRANSITIVITY_STAR: { if (match_fact(p, fact) && match_binary(fact.get(), d1, t1, t2)) { @@ -294,14 +293,14 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return false; } } - return + return vertices.size() == 2 && vertices.contains(t1->get_id()) && vertices.contains(t2->get_id()); } UNREACHABLE(); return false; - } + } case PR_MONOTONICITY: { TRACE("proof_checker", tout << mk_bounded_pp(p, m, 3) << "\n";); if (match_fact(p, fact) && @@ -317,7 +316,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { if (term1 != term2) { bool found = false; for(unsigned j = 0; j < proofs.size() && !found; ++j) { - found = + found = match_fact(proofs[j].get(), fml) && match_binary(fml.get(), d2, s1, s2) && SAME_OP(d1.get(), d2.get()) && @@ -353,7 +352,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { for (unsigned i = 0; i < q1->get_num_decls(); ++i) { if (q1->get_decl_sort(i) != q2->get_decl_sort(i)) { // term is not well-typed. - UNREACHABLE(); + UNREACHABLE(); return false; } } @@ -410,7 +409,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { side_conditions.push_back(fact.get()); return true; } - IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); + IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); return false; } case PR_REWRITE_STAR: { @@ -428,8 +427,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { side_conditions.push_back(rewrite_cond.get()); return true; } - IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); - return false; + IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); + return false; } case PR_PULL_QUANT: { if (match_proof(p) && @@ -439,7 +438,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // TBD: check the enchilada. return true; } - IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence with a quantifier:\n" << mk_bounded_pp(p, m);); + IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence with a quantifier:\n" << mk_bounded_pp(p, m);); return false; } case PR_PUSH_QUANT: { @@ -459,7 +458,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } else { return false; - } + } } } UNREACHABLE(); @@ -470,7 +469,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { match_fact(p, fact) && match_iff(fact.get(), t1, t2)) { // TBD: - // match_quantifier(t1.get(), is_forall1, decls1, body1) + // match_quantifier(t1.get(), is_forall1, decls1, body1) // filter out decls1 that occur in body1. // if list is empty, then t2 could be just body1. // otherwise t2 is also a quantifier. @@ -489,7 +488,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // TBD: check that terms are set of equalities. // t2 is an instance of a predicate in terms1 return true; - } + } IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";); return false; } @@ -511,7 +510,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { get_hypotheses(p1.get(), hypotheses); if (hypotheses.size() == 1 && match_negated(hypotheses.get(0), fact)) { // Suppose fact is (or a b c) and hypothesis is (not (or a b c)) - // That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause, + // That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause, // instead of a clause with three literals. return true; } @@ -535,7 +534,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { }); UNREACHABLE(); return false; - } + } TRACE("proof_checker", tout << "Matched:\n"; ast_ll_pp(tout, m, hypotheses[i].get()); ast_ll_pp(tout, m, ors[j-1].get());); @@ -550,7 +549,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { proofs.size() == 2 && match_fact(proofs[0].get(), fml1) && match_fact(proofs[1].get(), fml2) && - match_negated(fml1.get(), fml2.get()) && + m.is_complement(fml1.get(), fml2.get()) && m.is_false(fact.get())) { return true; } @@ -564,7 +563,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } bool found = false; for (unsigned j = 0; !found && j < terms1.size(); ++j) { - if (match_negated(terms1.get(j), fml2)) { + if (m.is_complement(terms1.get(j), fml2)) { found = true; if (j + 1 < terms1.size()) { terms1[j] = terms1.get(terms1.size()-1); @@ -573,7 +572,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } } if (!found) { - TRACE("pr_unit_bug", + TRACE("pr_unit_bug", tout << "Parents:\n"; for (unsigned i = 0; i < proofs.size(); i++) { expr_ref p(m); @@ -592,9 +591,9 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } } switch(terms1.size()) { - case 0: + case 0: return m.is_false(fact.get()); - case 1: + case 1: return fact.get() == terms1[0].get(); default: { if (match_or(fact.get(), terms2)) { @@ -617,7 +616,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return false; } - + } } UNREACHABLE(); @@ -635,7 +634,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } UNREACHABLE(); return false; - } + } case PR_IFF_FALSE: { // iff_false(?rule(?p1, (not ?fml)), (iff ?fml false)) if (match_proof(p, p1) && @@ -678,11 +677,11 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } case PR_DEF_INTRO: { // def_intro(?fml) - // + // // ?fml: forall x . ~p(x) or e(x) and forall x . ~e(x) or p(x) // : forall x . ~cond(x) or f(x) = then(x) and forall x . cond(x) or f(x) = else(x) // : forall x . f(x) = e(x) - // + // if (match_fact(p, fact) && match_proof(p) && m.is_bool(fact.get())) { @@ -712,7 +711,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return true; } UNREACHABLE(); - return false; + return false; } case PR_NNF_POS: { // TBD: @@ -735,9 +734,9 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { is_forall = true; } if (is_quantifier(e)) { - q = to_quantifier(e); + q = to_quantifier(e); // TBD check that quantifier is properly instantiated - return is_forall == q->is_forall(); + return is_forall == q->is_forall(); } } UNREACHABLE(); @@ -788,7 +787,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { vs(premise, sub.size(), sub.c_ptr(), premise); } fmls.push_back(premise.get()); - TRACE("proof_checker", + TRACE("proof_checker", tout << mk_pp(premise.get(), m) << "\n"; for (unsigned j = 0; j < sub.size(); ++j) { tout << mk_pp(sub[j], m) << " "; @@ -810,7 +809,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // ok } else { - IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" << + IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" << mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n" << mk_pp(p, m) << "\n";); } fmls[i] = premise1; @@ -825,7 +824,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { else { premise0 = m.mk_iff(premise0, conclusion); } - side_conditions.push_back(premise0); + side_conditions.push_back(premise0); return true; } default: @@ -841,7 +840,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { (=> (and ln+1 ln+2 .. ln+m) l0) or in the most general (ground) form: (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1)) - In other words we use the following (Prolog style) convention for Horn + In other words we use the following (Prolog style) convention for Horn implications: The head of a Horn implication is position 0, the first conjunct in the body of an implication is position 1 @@ -853,7 +852,7 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { app* a = to_app(e); expr* head, *body; expr_ref_vector args(m); - if (m.is_or(e)) { + if (m.is_or(e)) { SASSERT(position < a->get_num_args()); args.append(a->get_num_args(), a->get_args()); lit = args[position].get(); @@ -865,13 +864,13 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { unsigned num_heads = 1; if (m.is_or(head)) { num_heads = to_app(head)->get_num_args(); - heads = to_app(head)->get_args(); + heads = to_app(head)->get_args(); } expr*const* bodies = &body; unsigned num_bodies = 1; if (m.is_and(body)) { num_bodies = to_app(body)->get_num_args(); - bodies = to_app(body)->get_args(); + bodies = to_app(body)->get_args(); } if (position < num_heads) { args.append(num_heads, heads); @@ -884,7 +883,7 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { args.append(num_bodies, bodies); lit = m.mk_not(args[position].get()); args[position] = m.mk_true(); - e = m.mk_implies(m.mk_and(args.size(), args.c_ptr()), head); + e = m.mk_implies(m.mk_and(args.size(), args.c_ptr()), head); } } else if (position == 0) { @@ -914,7 +913,7 @@ void proof_checker::add_premise(proof* p) { } bool proof_checker::match_proof(proof const* p) const { - return + return m.is_proof(p) && m.get_num_parents(p) == 0; } @@ -927,7 +926,7 @@ bool proof_checker::match_proof(proof const* p, proof_ref& p0) const { } return false; } - + bool proof_checker::match_proof(proof const* p, proof_ref& p0, proof_ref& p1) const { if (m.is_proof(p) && m.get_num_parents(p) == 2) { @@ -1055,7 +1054,7 @@ bool proof_checker::match_oeq(expr const* e, expr_ref& t1, expr_ref& t2) const { bool proof_checker::match_negated(expr const* a, expr* b) const { expr_ref t(m); - return + return (match_not(a, t) && t.get() == b) || (match_not(b, t) && t.get() == a); } @@ -1082,7 +1081,7 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { p = stack.back(); SASSERT(m.is_proof(p)); if (m_hypotheses.contains(p)) { - stack.pop_back(); + stack.pop_back(); continue; } if (is_hypothesis(p) && match_fact(p, hyp)) { @@ -1124,13 +1123,13 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { if (!m_hypotheses.find(p, h)) { UNREACHABLE(); } - + ptr_buffer hyps; ptr_buffer todo; expr_mark mark; todo.push_back(h); expr_ref a(m), b(m); - + while (!todo.empty()) { h = todo.back(); @@ -1155,14 +1154,14 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { ast_ll_pp(tout << "Getting hypotheses from: ", m, p); tout << "Found hypotheses:\n"; for (unsigned i = 0; i < ante.size(); ++i) { - ast_ll_pp(tout, m, ante[i].get()); + ast_ll_pp(tout, m, ante[i].get()); } }); } bool proof_checker::match_nil(expr const* e) const { - return + return is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_NIL; @@ -1203,7 +1202,7 @@ expr* proof_checker::mk_nil() { } bool proof_checker::is_hypothesis(proof const* p) const { - return + return m.is_proof(p) && p->get_decl_kind() == PR_HYPOTHESIS; } @@ -1225,7 +1224,7 @@ expr* proof_checker::mk_hyp(unsigned num_hyps, expr * const * hyps) { } else { return result; - } + } } void proof_checker::dump_proof(proof const* pr) { @@ -1267,7 +1266,7 @@ void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecede bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& coeff, expr_ref& sum, bool& is_strict) { arith_util a(m); app* lit = lit0; - + if (m.is_not(lit)) { lit = to_app(lit->get_arg(0)); is_pos = !is_pos; @@ -1307,25 +1306,25 @@ bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& } // - // Multiplying by coefficients over strict + // Multiplying by coefficients over strict // and non-strict inequalities: // // (a <= b) * 2 // (a - b <= 0) * 2 // (2a - 2b <= 0) - + // (a < b) * 2 <=> // (a +1 <= b) * 2 <=> // 2a + 2 <= 2b <=> // 2a+2-2b <= 0 - + bool strict_ineq = is_pos?(a.is_gt(lit) || a.is_lt(lit)):(a.is_ge(lit) || a.is_le(lit)); - + if (is_int && strict_ineq) { sum = a.mk_add(sum, sign1); } - + term = a.mk_mul(sign1, a0); sum = a.mk_add(sum, term); term = a.mk_mul(sign2, a1); @@ -1357,7 +1356,7 @@ bool proof_checker::check_arith_proof(proof* p) { } expr_ref fact(m); proof_ref_vector proofs(m); - + if (!match_fact(p, fact)) { UNREACHABLE(); return false; @@ -1386,7 +1385,7 @@ bool proof_checker::check_arith_proof(proof* p) { coeffs[i] = lc*coeffs[i]; } } - + unsigned num_parents = m.get_num_parents(p); for (unsigned i = 0; i < num_parents; i++) { proof * a = m.get_parent(p, i); @@ -1395,11 +1394,11 @@ bool proof_checker::check_arith_proof(proof* p) { return false; } } - - if (m.is_or(fact)) { + + if (m.is_or(fact)) { app* disj = to_app(fact); unsigned num_args = disj->get_num_args(); - for (unsigned i = 0; i < num_args; ++i) { + for (unsigned i = 0; i < num_args; ++i) { app* lit = to_app(disj->get_arg(i)); if (!check_arith_literal(false, lit, coeffs[offset++], sum, is_strict)) { return false; @@ -1411,13 +1410,13 @@ bool proof_checker::check_arith_proof(proof* p) { return false; } } - + if (!sum.get()) { return false; } - + sort* s = m.get_sort(sum); - + if (is_strict) { sum = autil.mk_lt(sum, autil.mk_numeral(rational(0), s)); @@ -1435,6 +1434,6 @@ bool proof_checker::check_arith_proof(proof* p) { dump_proof(p); return false; } - + return true; } From 38a45f5482a0c2ee25e971c5afee6afb2182a100 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 10:47:24 -0700 Subject: [PATCH 123/364] Fix typo in comment --- src/tactic/core/solve_eqs_tactic.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index f579b7b4f..f665fe509 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -636,7 +636,7 @@ class solve_eqs_tactic : public tactic { TRACE("gaussian_leak", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); if (m_candidate_set.is_marked(f)) { // f may be deleted after the following update. - // so, we must remove remove the mark before doing the update + // so, we must remove the mark before doing the update m_candidate_set.mark(f, false); SASSERT(!m_candidate_set.is_marked(f)); g.update(idx, m().mk_true(), m().mk_true_proof(), nullptr); @@ -842,4 +842,3 @@ tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p, expr_replace else return clean(alloc(solve_eqs_tactic, m, p, r, false)); } - From 8be03f7c1f6d1bd1e9c27ded3c9942d109a63153 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 14:18:11 -0700 Subject: [PATCH 124/364] spacer_context: skolemize quant vars before renaming Skolemization has to be done before renaming, otherwise, can't guarantee that variable names do not clash --- src/muz/spacer/spacer_context.cpp | 73 ++++++++++++++++++------------- src/muz/spacer/spacer_context.h | 4 +- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 2baa70f46..044c0f091 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -72,34 +72,17 @@ pob::pob (pob* parent, pred_transformer& pt, void pob::set_post(expr* post) { - app_ref_vector b(get_ast_manager()); - set_post(post, b); + app_ref_vector empty_binding(get_ast_manager()); + set_post(post, empty_binding); } -void pob::set_post(expr* post, app_ref_vector const &b) { +void pob::set_post(expr* post, app_ref_vector const &binding) { normalize(post, m_post, m_pt.get_context().get_params().spacer_simplify_pob(), m_pt.get_context().get_params().spacer_use_eqclass()); m_binding.reset(); - if (b.empty()) return; - - m_binding.append(b); - - std::sort (m_binding.c_ptr(), m_binding.c_ptr() + m_binding.size(), sk_lt_proc()); - - // skolemize implicit existential quantifier - ast_manager &m = get_ast_manager(); - app_ref_vector pinned(m); - - expr_safe_replace sub(m); - for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { - expr* e; - e = m_binding.get(i); - pinned.push_back (mk_zk_const (m, i, get_sort(e))); - sub.insert (e, pinned.back()); - } - sub(m_post); + if (!binding.empty()) {m_binding.append(binding);} } void pob::inherit(pob const &p) { @@ -216,6 +199,25 @@ pob *derivation::create_first_child (model_evaluator_util &mev) return create_next_child(mev); } +void derivation::exist_skolemize(expr* fml, app_ref_vector &vars, expr_ref &res) { + ast_manager &m = get_ast_manager(); + if (vars.empty()) {res = fml; return;} + if (m.is_true(fml) || m.is_false(fml)) {res = fml; return;} + + std::sort (vars.c_ptr(), vars.c_ptr() + vars.size(), sk_lt_proc()); + + app_ref_vector pinned(m); + + expr_safe_replace sub(m); + for (unsigned i = 0, sz = vars.size(); i < sz; ++i) { + expr* e; + e = vars.get(i); + pinned.push_back (mk_zk_const (m, i, get_sort(e))); + sub.insert (e, pinned.back()); + } + sub(fml, res); +} + pob *derivation::create_next_child (model_evaluator_util &mev) { timeit _timer (is_trace_enabled("spacer_timeit"), @@ -280,6 +282,11 @@ pob *derivation::create_next_child (model_evaluator_util &mev) m_evars.append (vars); } + if (!m_evars.empty()) { + // existentially quantify out m_evars from post and skolemize the result + exist_skolemize(post.get(), m_evars, post); + } + get_manager ().formula_o2n (post.get (), post, m_premises [m_active].get_oidx (), m_evars.empty()); @@ -290,7 +297,6 @@ pob *derivation::create_next_child (model_evaluator_util &mev) pob *n = m_premises[m_active].pt().mk_pob(&m_parent, prev_level (m_parent.level ()), m_parent.depth (), post, m_evars); - IF_VERBOSE (1, verbose_stream () << "\n\tcreate_child: " << n->pt ().head ()->get_name () << " (" << n->level () << ", " << n->depth () << ") " @@ -895,9 +901,8 @@ bool pred_transformer::propagate_to_next_level (unsigned src_level) {return m_frames.propagate_to_next_level (src_level);} -/// \brief adds a lema to the solver and to child solvers -void pred_transformer::add_lemma_core(lemma* lemma, - bool ground_only) +/// \brief adds a lemma to the solver and to child solvers +void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) { unsigned lvl = lemma->level(); expr* l = lemma->get_expr(); @@ -1859,13 +1864,13 @@ void pred_transformer::frames::simplify_formulas () // ensure that the lemmas are sorted sort(); - ast_manager &m = m_pt.get_ast_manager (); + ast_manager &m = m_pt.get_ast_manager(); - tactic_ref simplifier = mk_unit_subsumption_tactic (m); + tactic_ref simplifier = mk_unit_subsumption_tactic(m); lemma_ref_vector new_lemmas; - unsigned lemmas_size = m_lemmas.size (); - goal_ref g (alloc (goal, m, false, false, false)); + unsigned lemmas_size = m_lemmas.size(); + goal_ref g(alloc (goal, m, false, false, false)); unsigned j = 0; // for every frame + infinity frame @@ -1933,6 +1938,11 @@ void pred_transformer::frames::simplify_formulas () << mk_pp(m_lemmas[n]->get_expr(), m) << "\n"; } + + verbose_stream() << "Simplified goal is:\n"; + for (unsigned k = 0; k < r->size(); ++k) + verbose_stream() << k << ": " + << mk_pp(r->form(k), m) << "\n"; } ENSURE(found); SASSERT(found); @@ -3013,7 +3023,8 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) TRACE ("spacer", tout << "expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() - << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" + << " depth: " << (n.depth () - m_pob_queue.min_depth ()) + << " fvsz: " << n.get_free_vars_size() << "\n" << mk_pp(n.post(), m) << "\n";); STRACE ("spacer.expand-add", @@ -3450,6 +3461,8 @@ bool context::create_children(pob& n, datalog::rule const& r, tout << "Implicant\n"; tout << mk_and (Phi) << "\n"; tout << "Projected Implicant\n" << mk_pp (phi1, m) << "\n"; + if (!vars.empty()) + tout << "could not eliminate: " << vars << "\n"; ); // expand literals. Ideally, we do not want to split aliasing diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 62d90e4c0..8de4a70f8 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -517,7 +517,7 @@ public: expr* post() const { return m_post.get (); } void set_post(expr *post); - void set_post(expr *post, app_ref_vector const &b); + void set_post(expr *post, app_ref_vector const &binding); /// indicate that a new post should be set for the node void new_post(expr *post) {if(post != m_post) {m_new_post = post;}} @@ -648,6 +648,8 @@ public: /// premise must be consistent with the transition relation pob *create_next_child (); + /// existentially quantify vars and skolemize the result + void exist_skolemize(expr *fml, app_ref_vector &vars, expr_ref &res); datalog::rule const& get_rule () const { return m_rule; } pob& get_parent () const { return m_parent; } ast_manager &get_ast_manager () const {return m_parent.get_ast_manager ();} From 891dcd99c2d7934e4ccf1d69c1d99b23b14a097b Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 15:22:58 -0700 Subject: [PATCH 125/364] Use fact-generating version of mk_unit_resolution() fact-using version of mk_unit_resolution() requires the fact to be a literal. Not sure why this restriction is placed there. --- src/muz/spacer/spacer_proof_utils.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 151446a93..a2a0ba6b6 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -491,9 +491,10 @@ proof* hypothesis_reducer::mk_unit_resolution_core(proof *ures, } // make unit resolution proof step - expr_ref tmp(m); - tmp = mk_or(m, pf_fact.size(), pf_fact.c_ptr()); - proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); + // expr_ref tmp(m); + // tmp = mk_or(m, pf_fact.size(), pf_fact.c_ptr()); + // proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); + proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr()); m_pinned.push_back(res); return res; From 56bce005a0dbbe368060961bff9c9c139377b169 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 15:47:28 -0700 Subject: [PATCH 126/364] virtual_solver: debug print --- src/muz/spacer/spacer_virtual_solver.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_virtual_solver.cpp b/src/muz/spacer/spacer_virtual_solver.cpp index 903f82d23..873f94160 100644 --- a/src/muz/spacer/spacer_virtual_solver.cpp +++ b/src/muz/spacer/spacer_virtual_solver.cpp @@ -150,7 +150,6 @@ lbool virtual_solver::check_sat_core(unsigned num_assumptions, st.update("time", sw.get_seconds()); st.display_smt2(out); - out.close(); if (m_factory.fparams().m_dump_recheck) { scoped_no_proof _no_proof_(m); @@ -164,7 +163,13 @@ lbool virtual_solver::check_sat_core(unsigned num_assumptions, sw2.stop(); verbose_stream() << file_name.str() << " :orig " << sw.get_seconds() << " :new " << sw2.get_seconds(); + + out << ";; :orig " << sw.get_seconds() + << " :new " << sw2.get_seconds() << "\n" + << ";; :new is time to solve with fresh smt::kernel\n"; } + + out.close(); } @@ -195,9 +200,9 @@ void virtual_solver::pop_core(unsigned n) { SASSERT(!m_in_delay_scope); m_context.pop(n); m_pushed = get_scope_level() - n > 0; - } + } else { - m_in_delay_scope = get_scope_level() - n > 0; + m_in_delay_scope = get_scope_level() - n > 0; } } From 054c6196a0fa4c1b19cfcb3ab250cdb2ea1de76d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 17:39:35 -0700 Subject: [PATCH 127/364] Move spacer qe into spacer_qe namespace Attempt to solve compilation issues with GCC and current replication of qe namespace inside and outside spacer --- src/muz/spacer/spacer_legacy_mbp.cpp | 2 +- src/muz/spacer/spacer_qe_project.cpp | 10 +-- src/muz/spacer/spacer_qe_project.h | 2 +- src/muz/spacer/spacer_util.cpp | 114 +++++++++++++-------------- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/muz/spacer/spacer_legacy_mbp.cpp b/src/muz/spacer/spacer_legacy_mbp.cpp index 9f03e6d2f..d52228d8a 100644 --- a/src/muz/spacer/spacer_legacy_mbp.cpp +++ b/src/muz/spacer/spacer_legacy_mbp.cpp @@ -99,7 +99,7 @@ void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& ); { scoped_no_proof _sp(m); - qe::arith_project(*M, arith_vars, fml, map); + spacer_qe::arith_project(*M, arith_vars, fml, map); } SASSERT(arith_vars.empty()); TRACE("spacer", diff --git a/src/muz/spacer/spacer_qe_project.cpp b/src/muz/spacer/spacer_qe_project.cpp index 753bb43b8..1bf670a7a 100644 --- a/src/muz/spacer/spacer_qe_project.cpp +++ b/src/muz/spacer/spacer_qe_project.cpp @@ -41,7 +41,7 @@ Revision History: #include "muz/spacer/spacer_mev_array.h" #include "muz/spacer/spacer_qe_project.h" -namespace +namespace spacer_qe { bool is_partial_eq (app* a); @@ -186,7 +186,7 @@ bool is_partial_eq (app* a) { } -namespace qe { +namespace spacer_qe { class is_relevant_default : public i_expr_pred { public: @@ -195,7 +195,7 @@ namespace qe { } }; - class mk_atom_default : public i_nnf_atom { + class mk_atom_default : public qe::i_nnf_atom { public: void operator()(expr* e, bool pol, expr_ref& result) override { if (pol) result = e; @@ -2254,7 +2254,7 @@ namespace qe { void arith_project(model& mdl, app_ref_vector& vars, expr_ref& fml) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); - atom_set pos_lits, neg_lits; + qe::atom_set pos_lits, neg_lits; is_relevant_default is_relevant; mk_atom_default mk_atom; get_nnf (fml, is_relevant, mk_atom, pos_lits, neg_lits); @@ -2264,7 +2264,7 @@ namespace qe { void arith_project(model& mdl, app_ref_vector& vars, expr_ref& fml, expr_map& map) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); - atom_set pos_lits, neg_lits; + qe::atom_set pos_lits, neg_lits; is_relevant_default is_relevant; mk_atom_default mk_atom; get_nnf (fml, is_relevant, mk_atom, pos_lits, neg_lits); diff --git a/src/muz/spacer/spacer_qe_project.h b/src/muz/spacer/spacer_qe_project.h index b923dc3c7..65848f8f3 100644 --- a/src/muz/spacer/spacer_qe_project.h +++ b/src/muz/spacer/spacer_qe_project.h @@ -23,7 +23,7 @@ Notes: #include "model/model.h" #include "ast/expr_map.h" -namespace qe { +namespace spacer_qe { /** Loos-Weispfenning model-based projection for a basic conjunction. Lits is a vector of literals. diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 030f2a386..7b1a2a07c 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -90,33 +90,33 @@ namespace spacer { if (!m_model) { return; } m_mev = alloc(model_evaluator, *m_model); } - + bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { m_mev->set_model_completion (model_completion); try { m_mev->operator() (e, result); return true; - } + } catch (model_evaluator_exception &ex) { (void)ex; TRACE("spacer_model_evaluator", tout << ex.msg () << "\n";); return false; } } - + bool model_evaluator_util::eval(const expr_ref_vector &v, expr_ref& res, bool model_completion) { expr_ref e(m); e = mk_and (v); return eval(e, res, model_completion); } - - + + bool model_evaluator_util::is_true(const expr_ref_vector &v) { expr_ref res(m); return eval (v, res, false) && m.is_true (res); } - + bool model_evaluator_util::is_false(expr *x) { expr_ref res(m); return eval(x, res, false) && m.is_false (res); @@ -126,7 +126,7 @@ namespace spacer { expr_ref res(m); return eval(x, res, false) && m.is_true (res); } - + void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); @@ -172,16 +172,16 @@ namespace spacer { SASSERT(orig_size <= 1 + conjs.size()); if (i + 1 == orig_size) { // no-op. - } + } else if (orig_size <= conjs.size()) { // no-op - } + } else { SASSERT(orig_size == 1 + conjs.size()); --orig_size; --i; } - } + } else { conjs[i] = tmp; } @@ -199,7 +199,7 @@ namespace spacer { ast_manager& m; public: ite_hoister(ast_manager& m): m(m) {} - + br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; @@ -234,7 +234,7 @@ namespace spacer { } ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} }; - + class ite_hoister_star : public rewriter_tpl { ite_hoister_cfg m_cfg; public: @@ -242,7 +242,7 @@ namespace spacer { rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; - + void hoist_non_bool_if(expr_ref& fml) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); @@ -274,7 +274,7 @@ namespace spacer { bool is_arith_expr(expr *e) const { return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); } - + bool is_offset(expr* e) const { if (a.is_numeral(e)) { return true; @@ -358,7 +358,7 @@ namespace spacer { !a.is_mul(lhs) && !a.is_mul(rhs); } - + bool test_term(expr* e) const { if (m.is_bool(e)) { return true; @@ -403,9 +403,9 @@ namespace spacer { public: test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - + void test_for_utvpi() { m_test_for_utvpi = true; } - + void operator()(expr* e) { if (!m_is_dl) { return; @@ -422,7 +422,7 @@ namespace spacer { m_is_dl = test_term(a->get_arg(i)); } } - + if (!m_is_dl) { char const* msg = "non-diff: "; if (m_test_for_utvpi) { @@ -431,10 +431,10 @@ namespace spacer { IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } - + bool is_dl() const { return m_is_dl; } }; - + bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); expr_fast_mark1 mark; @@ -443,7 +443,7 @@ namespace spacer { } return test.is_dl(); } - + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); test.test_for_utvpi(); @@ -455,7 +455,7 @@ namespace spacer { } - void subst_vars(ast_manager& m, + void subst_vars(ast_manager& m, app_ref_vector const& vars, model* M, expr_ref& fml) { expr_safe_replace sub (m); @@ -488,7 +488,7 @@ namespace spacer { flatten_and (fml, flat); fml = mk_and(flat); } - + app_ref_vector arith_vars (m); app_ref_vector array_vars (m); array_util arr_u (m); @@ -501,7 +501,7 @@ namespace spacer { qe_lite qe(m, p, false); qe (vars, fml); rw (fml); - + TRACE ("spacer_mbp", tout << "After qe_lite:\n"; tout << mk_pp (fml, m) << "\n"; @@ -509,7 +509,7 @@ namespace spacer { SASSERT (!m.is_false (fml)); - + // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { if (m.is_bool (v)) { @@ -523,7 +523,7 @@ namespace spacer { arith_vars.push_back (v); } } - + // substitute Booleans if (!bool_sub.empty()) { bool_sub (fml); @@ -533,58 +533,58 @@ namespace spacer { TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); bool_sub.reset (); } - + TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); - + vars.reset (); - + // project arrays { scoped_no_proof _sp (m); // -- local rewriter that is aware of current proof mode th_rewriter srw(m); - qe::array_project (*M.get (), array_vars, fml, vars, reduce_all_selects); + spacer_qe::array_project (*M.get (), array_vars, fml, vars, reduce_all_selects); SASSERT (array_vars.empty ()); srw (fml); SASSERT (!m.is_false (fml)); } - + TRACE ("spacer_mbp", tout << "extended model:\n"; model_pp (tout, *M); tout << "Auxiliary variables of index and value sorts:\n"; tout << vars; ); - + if (vars.empty()) { break; } } // project reals and ints if (!arith_vars.empty ()) { TRACE ("spacer_mbp", tout << "Arith vars:\n" << arith_vars;); - + // XXX Does not seem to have an effect // qe_lite qe(m); // qe (arith_vars, fml); // TRACE ("spacer_mbp", // tout << "After second qelite: " << // mk_pp (fml, m) << "\n";); - + if (use_native_mbp) { qe::mbp mbp (m); expr_ref_vector fmls(m); flatten_and (fml, fmls); - + mbp (true, arith_vars, *M.get (), fmls); fml = mk_and (fmls); SASSERT (arith_vars.empty ()); } else { scoped_no_proof _sp (m); - qe::arith_project (*M.get (), arith_vars, fml); + spacer_qe::arith_project (*M.get (), arith_vars, fml); } - + TRACE ("spacer_mbp", tout << "Projected arith vars:\n" << mk_pp (fml, m) << "\n"; tout << "Remaining arith vars:\n" << arith_vars << "\n";); @@ -615,8 +615,8 @@ namespace spacer { ); vars.reset (); - if (dont_sub && !arith_vars.empty ()) { - vars.append(arith_vars); + if (dont_sub && !arith_vars.empty ()) { + vars.append(arith_vars); } } @@ -701,11 +701,11 @@ namespace { model_evaluator_util &m_mev; ast_manager &m; arith_util m_arith; - + expr_ref_vector m_todo; expr_mark m_visited; - + void add_literal (expr *e, expr_ref_vector &out) { SASSERT (m.is_bool (e)); @@ -753,11 +753,11 @@ namespace { SASSERT (m.is_bool (a)); expr_ref v(m); m_mev.eval (a, v, false); - + if (!m.is_true(v) && !m.is_false(v)) { return; } - + expr *na, *f1, *f2, *f3; - + if (a->get_family_id() != m.get_basic_family_id()) { add_literal(a, out); } else if (is_uninterp_const(a)) @@ -836,13 +836,13 @@ namespace { UNREACHABLE(); } } - + void pick_literals(expr *e, expr_ref_vector &out) { SASSERT(m_todo.empty()); if (m_visited.is_marked(e)) { return; } SASSERT(is_app(e)); - + m_todo.push_back(e); do { app *a = to_app(m_todo.back()); @@ -851,27 +851,27 @@ namespace { m_visited.mark(a, true); } while (!m_todo.empty()); } - + bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) { m_visited.reset(); expr_ref e(m); e = mk_and (in); bool is_true = m_mev.is_true (e); - + for (unsigned i = 0, sz = in.size (); i < sz; ++i) { if (is_true || m_mev.is_true(in.get(i))) { pick_literals(in.get(i), out); } } - + m_visited.reset (); return is_true; } - + public: implicant_picker (model_evaluator_util &mev) : m_mev (mev), m (m_mev.get_ast_manager ()), m_arith(m), m_todo(m) {} - + void operator() (expr_ref_vector &in, expr_ref_vector& out) {pick_implicant (in, out);} }; @@ -1188,29 +1188,29 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { bool mbqi_project_var (model_evaluator_util &mev, app* var, expr_ref &fml) { ast_manager &m = fml.get_manager (); - + expr_ref val(m); mev.eval (var, val, false); - + TRACE ("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp (var, m) << "\n" << "fml: " << mk_pp (fml, m) << "\n";); expr_ref_vector terms (m); index_term_finder finder (m, var, terms); for_each_expr (finder, fml); - - + + TRACE ("mbqi_project_verbose", tout << "terms:\n"; for (unsigned i = 0, e = terms.size (); i < e; ++i) tout << i << ": " << mk_pp (terms.get (i), m) << "\n"; ); - + for (unsigned i = 0, e = terms.size(); i < e; ++i) { expr* term = terms.get (i); expr_ref tval (m); mev.eval (term, tval, false); - + TRACE ("mbqi_project_verbose", tout << "term: " << mk_pp (term, m) << " tval: " << mk_pp (tval, m) From e8cabdc6202b616062804e89a431a06ff61577f3 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 17:42:56 -0700 Subject: [PATCH 128/364] Uninitialized variable --- src/muz/spacer/spacer_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 044c0f091..df945d9bb 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2082,7 +2082,7 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) // for (auto it = rels.begin(), end = rels.end(); it != end; ++it) { for (auto &entry : rels) { func_decl* pred = entry.m_key; - pred_transformer* pt = entry.m_value, *pt_user; + pred_transformer* pt = entry.m_value, *pt_user = nullptr; for (auto dep : rules.get_dependencies().get_deps(pred)) { TRACE("spacer", tout << mk_pp(pred, m) << " " << mk_pp(dep, m) << "\n";); rels.find(dep, pt_user); From 402234757e073717d339d795205c2f8b192967d2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 May 2018 13:06:47 -0700 Subject: [PATCH 129/364] updates to mbqi Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_util.cpp | 196 ++++++++++++++++----------------- src/qe/qe_mbp.cpp | 32 +++--- 2 files changed, 114 insertions(+), 114 deletions(-) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 7b1a2a07c..90e0542fa 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -90,33 +90,33 @@ namespace spacer { if (!m_model) { return; } m_mev = alloc(model_evaluator, *m_model); } - + bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { m_mev->set_model_completion (model_completion); try { m_mev->operator() (e, result); return true; - } + } catch (model_evaluator_exception &ex) { (void)ex; TRACE("spacer_model_evaluator", tout << ex.msg () << "\n";); return false; } } - + bool model_evaluator_util::eval(const expr_ref_vector &v, expr_ref& res, bool model_completion) { expr_ref e(m); e = mk_and (v); return eval(e, res, model_completion); } - - + + bool model_evaluator_util::is_true(const expr_ref_vector &v) { expr_ref res(m); return eval (v, res, false) && m.is_true (res); } - + bool model_evaluator_util::is_false(expr *x) { expr_ref res(m); return eval(x, res, false) && m.is_false (res); @@ -126,7 +126,7 @@ namespace spacer { expr_ref res(m); return eval(x, res, false) && m.is_true (res); } - + void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); @@ -172,16 +172,16 @@ namespace spacer { SASSERT(orig_size <= 1 + conjs.size()); if (i + 1 == orig_size) { // no-op. - } + } else if (orig_size <= conjs.size()) { // no-op - } + } else { SASSERT(orig_size == 1 + conjs.size()); --orig_size; --i; } - } + } else { conjs[i] = tmp; } @@ -199,7 +199,7 @@ namespace spacer { ast_manager& m; public: ite_hoister(ast_manager& m): m(m) {} - + br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; @@ -234,7 +234,7 @@ namespace spacer { } ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} }; - + class ite_hoister_star : public rewriter_tpl { ite_hoister_cfg m_cfg; public: @@ -242,7 +242,7 @@ namespace spacer { rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; - + void hoist_non_bool_if(expr_ref& fml) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); @@ -274,7 +274,7 @@ namespace spacer { bool is_arith_expr(expr *e) const { return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); } - + bool is_offset(expr* e) const { if (a.is_numeral(e)) { return true; @@ -358,7 +358,7 @@ namespace spacer { !a.is_mul(lhs) && !a.is_mul(rhs); } - + bool test_term(expr* e) const { if (m.is_bool(e)) { return true; @@ -403,9 +403,9 @@ namespace spacer { public: test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - + void test_for_utvpi() { m_test_for_utvpi = true; } - + void operator()(expr* e) { if (!m_is_dl) { return; @@ -422,7 +422,7 @@ namespace spacer { m_is_dl = test_term(a->get_arg(i)); } } - + if (!m_is_dl) { char const* msg = "non-diff: "; if (m_test_for_utvpi) { @@ -431,10 +431,10 @@ namespace spacer { IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } - + bool is_dl() const { return m_is_dl; } }; - + bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); expr_fast_mark1 mark; @@ -443,7 +443,7 @@ namespace spacer { } return test.is_dl(); } - + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); test.test_for_utvpi(); @@ -455,7 +455,7 @@ namespace spacer { } - void subst_vars(ast_manager& m, + void subst_vars(ast_manager& m, app_ref_vector const& vars, model* M, expr_ref& fml) { expr_safe_replace sub (m); @@ -488,7 +488,7 @@ namespace spacer { flatten_and (fml, flat); fml = mk_and(flat); } - + app_ref_vector arith_vars (m); app_ref_vector array_vars (m); array_util arr_u (m); @@ -497,19 +497,19 @@ namespace spacer { expr_ref bval (m); while (true) { - params_ref p; - qe_lite qe(m, p, false); - qe (vars, fml); - rw (fml); - - TRACE ("spacer_mbp", - tout << "After qe_lite:\n"; - tout << mk_pp (fml, m) << "\n"; + params_ref p; + qe_lite qe(m, p, false); + qe (vars, fml); + rw (fml); + + TRACE ("spacer_mbp", + tout << "After qe_lite:\n"; + tout << mk_pp (fml, m) << "\n"; tout << "Vars:\n" << vars;); SASSERT (!m.is_false (fml)); - + // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { if (m.is_bool (v)) { @@ -523,7 +523,7 @@ namespace spacer { arith_vars.push_back (v); } } - + // substitute Booleans if (!bool_sub.empty()) { bool_sub (fml); @@ -533,13 +533,13 @@ namespace spacer { TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); bool_sub.reset (); } - + TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); - + vars.reset (); - + // project arrays { scoped_no_proof _sp (m); @@ -550,14 +550,14 @@ namespace spacer { srw (fml); SASSERT (!m.is_false (fml)); } - + TRACE ("spacer_mbp", tout << "extended model:\n"; model_pp (tout, *M); tout << "Auxiliary variables of index and value sorts:\n"; tout << vars; ); - + if (vars.empty()) { break; } } @@ -572,21 +572,21 @@ namespace spacer { // tout << "After second qelite: " << // mk_pp (fml, m) << "\n";); - if (use_native_mbp) { - qe::mbp mbp (m); - expr_ref_vector fmls(m); - flatten_and (fml, fmls); + if (use_native_mbp) { + qe::mbp mbp (m); + expr_ref_vector fmls(m); + flatten_and (fml, fmls); - mbp (true, arith_vars, *M.get (), fmls); - fml = mk_and (fmls); - SASSERT (arith_vars.empty ()); - } else { + mbp (true, arith_vars, *M.get (), fmls); + fml = mk_and (fmls); + SASSERT (arith_vars.empty ()); + } else { scoped_no_proof _sp (m); spacer_qe::arith_project (*M.get (), arith_vars, fml); } TRACE ("spacer_mbp", - tout << "Projected arith vars:\n" << mk_pp (fml, m) << "\n"; + tout << "Projected arith vars:\n" << mk_pp (fml, m) << "\n"; tout << "Remaining arith vars:\n" << arith_vars << "\n";); SASSERT (!m.is_false (fml)); } @@ -697,7 +697,7 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) } namespace { - class implicant_picker { +class implicant_picker { model_evaluator_util &m_mev; ast_manager &m; arith_util m_arith; @@ -747,37 +747,37 @@ namespace { out.push_back (res); } - void process_app(app *a, expr_ref_vector &out) - { - if (m_visited.is_marked(a)) { return; } + void process_app(app *a, expr_ref_vector &out) + { + if (m_visited.is_marked(a)) { return; } SASSERT (m.is_bool (a)); expr_ref v(m); m_mev.eval (a, v, false); - if (!m.is_true(v) && !m.is_false(v)) { return; } + if (!m.is_true(v) && !m.is_false(v)) { return; } expr *na, *f1, *f2, *f3; if (a->get_family_id() != m.get_basic_family_id()) - { add_literal(a, out); } + { add_literal(a, out); } else if (is_uninterp_const(a)) - { add_literal(a, out); } + { add_literal(a, out); } else if (m.is_not(a, na) && m.is_not(na, na)) - { m_todo.push_back(na); } + { m_todo.push_back(na); } else if (m.is_not(a, na)) - { m_todo.push_back(na); } + { m_todo.push_back(na); } else if (m.is_distinct(a)) { if (m.is_false(v)) m_todo.push_back (qe::project_plugin::pick_equality(m, *m_mev.get_model(), a)); else if (a->get_num_args() == 2) - { add_literal(a, out); } + { add_literal(a, out); } else m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), a->get_args())); - } else if (m.is_and(a)) { + } else if (m.is_and(a)) { if (m.is_true(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } + { m_todo.append(a->get_num_args(), a->get_args()); } else if (m.is_false(v)) { for (unsigned i = 0, sz = a->get_num_args (); i < sz; ++i) { if (m_mev.is_false(a->get_arg(i))) { @@ -786,9 +786,9 @@ namespace { } } } - } else if (m.is_or(a)) { + } else if (m.is_or(a)) { if (m.is_false(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } + { m_todo.append(a->get_num_args(), a->get_args()); } else if (m.is_true(v)) { for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { if (m_mev.is_true(a->get_arg(i))) { @@ -797,39 +797,39 @@ namespace { } } } - } else if (m.is_iff(a, f1, f2) || m.is_eq(a, f1, f2) || - (m.is_true(v) && m.is_not(a, na) && m.is_xor (na, f1, f2))) { + } else if (m.is_iff(a, f1, f2) || m.is_eq(a, f1, f2) || + (m.is_true(v) && m.is_not(a, na) && m.is_xor (na, f1, f2))) { if (!m.are_equal(f1, f2) && !m.are_distinct(f1, f2)) { if (m.is_bool(f1) && (!is_uninterp_const(f1) || !is_uninterp_const(f2))) - { m_todo.append(a->get_num_args(), a->get_args()); } - else - { add_literal(a, out); } - } - } else if (m.is_ite(a, f1, f2, f3)) { - if (m.are_equal(f2, f3)) { m_todo.push_back(f2); } - else if (m_mev.is_true (f2) && m_mev.is_true (f3)) { - m_todo.push_back(f2); - m_todo.push_back(f3); - } else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { - m_todo.push_back(f2); - m_todo.push_back(f3); - } else if (m_mev.is_true(f1)) { - m_todo.push_back(f1); - m_todo.push_back(f2); - } else if (m_mev.is_false(f1)) { - m_todo.push_back(f1); - m_todo.push_back(f3); - } - } else if (m.is_xor(a, f1, f2)) { m_todo.append(a->get_num_args(), a->get_args()); } + else + { add_literal(a, out); } + } + } else if (m.is_ite(a, f1, f2, f3)) { + if (m.are_equal(f2, f3)) { m_todo.push_back(f2); } + else if (m_mev.is_true (f2) && m_mev.is_true (f3)) { + m_todo.push_back(f2); + m_todo.push_back(f3); + } else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { + m_todo.push_back(f2); + m_todo.push_back(f3); + } else if (m_mev.is_true(f1)) { + m_todo.push_back(f1); + m_todo.push_back(f2); + } else if (m_mev.is_false(f1)) { + m_todo.push_back(f1); + m_todo.push_back(f3); + } + } else if (m.is_xor(a, f1, f2)) + { m_todo.append(a->get_num_args(), a->get_args()); } else if (m.is_implies(a, f1, f2)) { if (m.is_true (v)) { - if (m_mev.is_true(f2)) { m_todo.push_back(f2); } - else if (m_mev.is_false(f1)) { m_todo.push_back(f1); } + if (m_mev.is_true(f2)) { m_todo.push_back(f2); } + else if (m_mev.is_false(f1)) { m_todo.push_back(f1); } } else if (m.is_false(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } - } else if (m.is_true(a) || m.is_false(a)) { /* nothing */ } + { m_todo.append(a->get_num_args(), a->get_args()); } + } else if (m.is_true(a) || m.is_false(a)) { /* nothing */ } else { verbose_stream () << "Unexpected expression: " << mk_pp(a, m) << "\n"; @@ -837,10 +837,10 @@ namespace { } } - void pick_literals(expr *e, expr_ref_vector &out) - { + void pick_literals(expr *e, expr_ref_vector &out) + { SASSERT(m_todo.empty()); - if (m_visited.is_marked(e)) { return; } + if (m_visited.is_marked(e)) { return; } SASSERT(is_app(e)); m_todo.push_back(e); @@ -852,8 +852,8 @@ namespace { } while (!m_todo.empty()); } - bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) - { + bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) + { m_visited.reset(); expr_ref e(m); e = mk_and (in); @@ -861,7 +861,7 @@ namespace { for (unsigned i = 0, sz = in.size (); i < sz; ++i) { if (is_true || m_mev.is_true(in.get(i))) - { pick_literals(in.get(i), out); } + { pick_literals(in.get(i), out); } } m_visited.reset (); @@ -875,7 +875,7 @@ namespace { void operator() (expr_ref_vector &in, expr_ref_vector& out) {pick_implicant (in, out);} }; -} + } void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, expr_ref_vector &res) @@ -1178,9 +1178,9 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { expr *e1, *e2; if (m_array.is_select (n) && n->get_arg (1) != m_var) { m_res.push_back (n->get_arg (1)); - } else if (m.is_eq(n, e1, e2)) { - if (e1 == m_var) { m_res.push_back(e2); } - else if (e2 == m_var) { m_res.push_back(e1); } + } else if (m.is_eq(n, e1, e2)) { + if (e1 == m_var) { m_res.push_back(e2); } + else if (e2 == m_var) { m_res.push_back(e1); } } } }; @@ -1206,7 +1206,7 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { tout << i << ": " << mk_pp (terms.get (i), m) << "\n"; ); - for (unsigned i = 0, e = terms.size(); i < e; ++i) { + for (unsigned i = 0, e = terms.size(); i < e; ++i) { expr* term = terms.get (i); expr_ref tval (m); mev.eval (term, tval, false); diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index a6bdbd1dc..f90c32342 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -125,13 +125,13 @@ void project_plugin::push_back(expr_ref_vector& lits, expr* e) { class mbp::impl { - ast_manager& m; + ast_manager& m; params_ref m_params; - th_rewriter m_rw; + th_rewriter m_rw; ptr_vector m_plugins; - expr_mark m_visited; - expr_mark m_bool_visited; - + expr_mark m_visited; + expr_mark m_bool_visited; + // parameters bool m_reduce_all_selects; bool m_native_mbp; @@ -280,33 +280,33 @@ class mbp::impl { } else { vars[j++] = var; + } } - } if (j == vars.size()) { return; } vars.shrink(j); - j = 0; - for (unsigned i = 0; i < fmls.size(); ++i) { + j = 0; + for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(); sub(fml, val); - m_rw(val); - if (!m.is_true(val)) { + m_rw(val); + if (!m.is_true(val)) { TRACE("qe", tout << mk_pp(fml, m) << " -> " << val << "\n";); fmls[j++] = val; - } - } + } + } fmls.shrink(j); - } + } void subst_vars(model_evaluator& eval, app_ref_vector const& vars, expr_ref& fml) { expr_safe_replace sub (m); for (app * v : vars) { sub.insert(v, eval(v)); - } + } sub(fml); - } + } struct index_term_finder { ast_manager& m; @@ -731,7 +731,7 @@ mbp::mbp(ast_manager& m, params_ref const& p) { mbp::~mbp() { dealloc(m_impl); } - + void mbp::updt_params(params_ref const& p) { m_impl->updt_params(p); } From e281f855861dc9f9d653dba91cbac068181d0661 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 May 2018 14:22:00 -0700 Subject: [PATCH 130/364] add way to unit test mbp from command line Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 39 +++++ src/qe/qe_arrays.cpp | 185 ++---------------------- src/qe/qe_mbp.cpp | 2 + src/qe/qe_mbp.h | 2 +- 4 files changed, 54 insertions(+), 174 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index fa1d56fe2..1b378f430 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -30,6 +30,7 @@ Notes: #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" #include "util/gparams.h" +#include "qe/qe_mbp.h" BINARY_SYM_CMD(get_quantifier_body_cmd, @@ -352,6 +353,43 @@ public: void execute(cmd_context & ctx) override { ctx.display_dimacs(); } }; +class mbp_cmd : public cmd { + expr* m_fml; + ptr_vector m_vars; +public: + mbp_cmd():cmd("mbp") {} + char const * get_usage() const override { return ""; } + char const * get_descr(cmd_context & ctx) const override { return "perform model based projection"; } + unsigned get_arity() const override { return 2; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { + if (m_fml == nullptr) return CPK_EXPR; + return CPK_EXPR_LIST; + } + void set_next_arg(cmd_context& ctx, expr * arg) { m_fml = arg; } + void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context & ctx) override { m_fml = nullptr; } + void execute(cmd_context & ctx) override { + ast_manager& m = ctx.m(); + app_ref_vector vars(m); + model_ref mdl; + if (!ctx.is_model_available(mdl) || !ctx.get_check_sat_result()) { + throw cmd_exception("model is not available"); + } + for (expr* v : m_vars) { + if (!is_uninterp_const(v)) { + throw cmd_exception("invalid variable argument. Uninterpreted variable expected"); + } + vars.push_back(to_app(v)); + } + qe::mbp mbp(m); + expr_ref fml(m_fml, m); + mbp.spacer(vars, *mdl.get(), fml); + ctx.regular_stream() << fml << "\n"; + } +}; + void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(print_dimacs_cmd)); @@ -377,4 +415,5 @@ void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(instantiate_cmd)); ctx.insert(alloc(instantiate_nested_cmd)); ctx.insert(alloc(set_next_id)); + ctx.insert(alloc(mbp_cmd)); } diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 712311f9d..cc3952457 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -631,13 +631,13 @@ namespace qe { m_v = arr_vars.get (i); if (!m_arr_u.is_array (m_v)) { TRACE ("qe", - tout << "not an array variable: " << mk_pp (m_v, m) << "\n"; + tout << "not an array variable: " << m_v << "\n"; ); aux_vars.push_back (m_v); continue; } TRACE ("qe", - tout << "projecting equalities on variable: " << mk_pp (m_v, m) << "\n"; + tout << "projecting equalities on variable: " << m_v << "\n"; ); if (project (fml)) { @@ -653,8 +653,8 @@ namespace qe { ); } else { - IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(m_v, m) << "\n";); - TRACE ("qe", tout << "Failed to project: " << mk_pp (m_v, m) << "\n";); + IF_VERBOSE(2, verbose_stream() << "can't project:" << m_v << "\n";); + TRACE ("qe", tout << "Failed to project: " << m_v << "\n";); arr_vars[j++] = m_v; } } @@ -1196,67 +1196,6 @@ namespace qe { return false; } - bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - - TRACE("qe", tout << mk_pp(var, m) << "\n" << lits;); - m_var = alloc(contains_app, m, var); - - // reduce select-store redeces based on model. - // rw_cfg rw(m); - // rw(lits); - - // try first to solve for var. - if (solve_eq(model, vars, lits)) { - return true; - } - - app_ref_vector selects(m); - - // check that only non-select occurrences are in disequalities. - if (!check_diseqs(lits, selects)) { - TRACE("qe", tout << "Could not project " << mk_pp(var, m) << " for:\n" << lits << "\n";); - return false; - } - - // remove disequalities. - elim_diseqs(lits); - - // Ackerman reduction on remaining select occurrences - // either replace occurrences by model value or other node - // that is congruent to model value. - - ackermanize_select(model, selects, vars, lits); - - TRACE("qe", tout << selects << "\n" << lits << "\n";); - return true; - } - - void ackermanize_select(model& model, app_ref_vector const& selects, app_ref_vector& vars, expr_ref_vector& lits) { - expr_ref_vector vals(m), reps(m); - expr_ref val(m); - expr_safe_replace sub(m); - - if (selects.empty()) { - return; - } - - app_ref sel(m); - for (unsigned i = 0; i < selects.size(); ++i) { - sel = m.mk_fresh_const("sel", m.get_sort(selects[i])); - VERIFY (model.eval(selects[i], val)); - model.register_decl(sel->get_decl(), val); - vals.push_back(to_app(val)); - reps.push_back(val); // TODO: direct pass could handle nested selects. - vars.push_back(sel); - sub.insert(selects[i], val); - } - - sub(lits); - remove_true(lits); - project_plugin::partition_args(model, selects, lits); - project_plugin::partition_values(model, reps, lits); - } - void remove_true(expr_ref_vector& lits) { for (unsigned i = 0; i < lits.size(); ++i) { if (m.is_true(lits[i].get())) { @@ -1275,113 +1214,7 @@ namespace qe { for (unsigned j = 0; j < n; ++j) { lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); } - } - - // check that x occurs only under selects or in disequalities. - bool check_diseqs(expr_ref_vector const& lits, app_ref_vector& selects) { - expr_mark mark; - ptr_vector todo; - app* e; - for (unsigned i = 0; i < lits.size(); ++i) { - e = to_app(lits[i]); - if (is_diseq_x(e)) { - continue; - } - if (contains_x(e)) { - todo.push_back(e); - } - } - while (!todo.empty()) { - e = todo.back(); - todo.pop_back(); - if (mark.is_marked(e)) { - continue; - } - mark.mark(e); - if (m_var->x() == e) { - return false; - } - unsigned start = 0; - if (a.is_select(e)) { - if (e->get_arg(0) == m_var->x()) { - start = 1; - selects.push_back(e); - } - } - for (unsigned i = start; i < e->get_num_args(); ++i) { - todo.push_back(to_app(e->get_arg(i))); - } - } - return true; - } - - void elim_diseqs(expr_ref_vector& lits) { - for (unsigned i = 0; i < lits.size(); ++i) { - if (is_diseq_x(lits[i].get())) { - project_plugin::erase(lits, i); - } - } - } - - bool is_update_x(app* e) { - do { - if (m_var->x() == e) { - return true; - } - if (a.is_store(e) && contains_x(e->get_arg(0))) { - for (unsigned i = 1; i < e->get_num_args(); ++i) { - if (contains_x(e->get_arg(i))) { - return false; - } - } - e = to_app(e->get_arg(0)); - continue; - } - } - while (false); - return false; - } - - bool is_diseq_x(expr* e) { - expr *f, * s, *t; - if (m.is_not(e, f) && m.is_eq(f, s, t)) { - if (contains_x(s) && !contains_x(t) && is_update_x(to_app(s))) return true; - if (contains_x(t) && !contains_x(s) && is_update_x(to_app(t))) return true; - } - return false; - } - - bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - // find an equality to solve for. - expr* s, *t; - for (unsigned i = 0; i < lits.size(); ++i) { - if (m.is_eq(lits[i].get(), s, t)) { - vector idxs; - expr_ref save(m), back(m); - save = lits[i].get(); - back = lits.back(); - lits[i] = back; - lits.pop_back(); - unsigned sz = lits.size(); - if (contains_x(s) && !contains_x(t) && is_app(s)) { - if (solve(model, to_app(s), t, idxs, vars, lits)) { - return true; - } - } - else if (contains_x(t) && !contains_x(s) && is_app(t)) { - if (solve(model, to_app(t), s, idxs, vars, lits)) { - return true; - } - } - // put back the equality literal. - lits.resize(sz); - lits.push_back(back); - lits[i] = save; - } - // TBD: not distinct? - } - return false; - } + } bool solve(model& model, app* s, expr* t, vector& idxs, app_ref_vector& vars, expr_ref_vector& lits) { SASSERT(contains_x(s)); @@ -1502,7 +1335,13 @@ namespace qe { } bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - return (*m_imp)(model, var, vars, lits); + ast_manager& m = vars.get_manager(); + app_ref_vector vvars(m, 1, &var); + expr_ref fml = mk_and(lits); + (*this)(model, vvars, fml, vars, false); + lits.reset(); + flatten_and(fml, lits); + return true; } bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index f90c32342..bc9ac8c41 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -99,6 +99,7 @@ void project_plugin::partition_values(model& model, expr_ref_vector const& vals, } } +#if 0 void project_plugin::partition_args(model& model, app_ref_vector const& selects, expr_ref_vector& lits) { ast_manager& m = selects.get_manager(); if (selects.empty()) return; @@ -111,6 +112,7 @@ void project_plugin::partition_args(model& model, app_ref_vector const& selects, project_plugin::partition_values(model, args, lits); } } +#endif void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { lits[i] = lits.back(); diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 12b03791a..665e25d48 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -41,7 +41,7 @@ namespace qe { static expr_ref pick_equality(ast_manager& m, model& model, expr* t); static void partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits); - static void partition_args(model& model, app_ref_vector const& sels, expr_ref_vector& lits); + //static void partition_args(model& model, app_ref_vector const& sels, expr_ref_vector& lits); static void erase(expr_ref_vector& lits, unsigned& i); static void push_back(expr_ref_vector& lits, expr* lit); static void mark_rec(expr_mark& visited, expr* e); From d95e167d6181ef8eef48cba3f552dd58f2b9287c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 May 2018 16:53:33 -0700 Subject: [PATCH 131/364] updates to mbqi Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 2 +- src/qe/qe_arrays.cpp | 34 ++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 1b378f430..ceb979761 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -369,7 +369,7 @@ public: void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { m_vars.append(num, ts); } - void prepare(cmd_context & ctx) override { m_fml = nullptr; } + void prepare(cmd_context & ctx) override { m_fml = nullptr; m_vars.reset(); } void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); app_ref_vector vars(m); diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index cc3952457..f7b8898c1 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -1214,7 +1214,39 @@ namespace qe { for (unsigned j = 0; j < n; ++j) { lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); } - } + } + + bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + // find an equality to solve for. + expr* s, *t; + for (unsigned i = 0; i < lits.size(); ++i) { + if (m.is_eq(lits[i].get(), s, t)) { + vector idxs; + expr_ref save(m), back(m); + save = lits[i].get(); + back = lits.back(); + lits[i] = back; + lits.pop_back(); + unsigned sz = lits.size(); + if (contains_x(s) && !contains_x(t) && is_app(s)) { + if (solve(model, to_app(s), t, idxs, vars, lits)) { + return true; + } + } + else if (contains_x(t) && !contains_x(s) && is_app(t)) { + if (solve(model, to_app(t), s, idxs, vars, lits)) { + return true; + } + } + // put back the equality literal. + lits.resize(sz); + lits.push_back(back); + lits[i] = save; + } + // TBD: not distinct? + } + return false; + } bool solve(model& model, app* s, expr* t, vector& idxs, app_ref_vector& vars, expr_ref_vector& lits) { SASSERT(contains_x(s)); From efb1f50d008be7fbbdf1ea327913c4f0b070f7bf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 May 2018 20:11:29 -0700 Subject: [PATCH 132/364] bind nested variables Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 29 ++++++++----------- src/qe/qe_arrays.cpp | 10 +++---- src/qe/qe_mbp.cpp | 68 +++++++------------------------------------- src/qe/qe_mbp.h | 2 -- 4 files changed, 27 insertions(+), 82 deletions(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 969d22a81..f663d1b6c 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -299,20 +299,19 @@ namespace qe { obj_map tids; expr_ref_vector pinned(m); unsigned j = 0; + TRACE("qe", tout << "fmls: " << fmls << "\n";); for (unsigned i = 0; i < fmls.size(); ++i) { - expr* fml = fmls[i].get(); + expr * fml = fmls.get(i); if (!linearize(mbo, eval, fml, fmls, tids)) { - if (i != j) { - fmls[j] = fmls[i].get(); - } - ++j; + fmls[j++] = fml; } else { - TRACE("qe", tout << mk_pp(fml, m) << "\n";); + TRACE("qe", tout << "could not linearize: " << mk_pp(fml, m) << "\n";); pinned.push_back(fml); } } - fmls.resize(j); + fmls.shrink(j); + TRACE("qe", tout << "linearized: " << fmls << "\n";); // fmls holds residue, // mbo holds linear inequalities that are in scope @@ -328,13 +327,13 @@ namespace qe { if (is_arith(v) && !tids.contains(v)) { rational r; expr_ref val = eval(v); - a.is_numeral(val, r); + VERIFY(a.is_numeral(val, r)); TRACE("qe", tout << mk_pp(v, m) << " " << val << "\n";); tids.insert(v, mbo.add_var(r, a.is_int(v))); } } for (expr* fml : fmls) { - fmls_mark.mark(fml); + mark_rec(fmls_mark, fml); } ptr_vector index2expr; for (auto& kv : tids) { @@ -346,8 +345,7 @@ namespace qe { } j = 0; unsigned_vector real_vars; - for (unsigned i = 0; i < vars.size(); ++i) { - app* v = vars[i].get(); + for (app* v : vars) { if (is_arith(v) && !fmls_mark.is_marked(v)) { real_vars.push_back(tids.find(v)); } @@ -355,7 +353,7 @@ namespace qe { vars[j++] = v; } } - vars.resize(j); + vars.shrink(j); TRACE("qe", tout << "remaining vars: " << vars << "\n"; for (unsigned v : real_vars) { tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n"; @@ -391,8 +389,7 @@ namespace qe { CTRACE("qe", !m.is_true(val), tout << "Evaluated unit " << t << " to " << val << "\n";); continue; } - for (j = 0; j < r.m_vars.size(); ++j) { - var const& v = r.m_vars[j]; + for (var const& v : r.m_vars) { t = index2expr[v.m_id]; if (!v.m_coeff.is_one()) { t = a.mk_mul(a.mk_numeral(v.m_coeff, a.is_int(t)), t); @@ -418,11 +415,9 @@ namespace qe { break; } } - fmls.push_back(t); - + fmls.push_back(t); val = eval(t); CTRACE("qe", !m.is_true(val), tout << "Evaluated " << t << " to " << val << "\n";); - } } diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index f7b8898c1..305fb47b6 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -1390,9 +1390,9 @@ namespace qe { array_project_eqs_util pe (m); pe (mdl, arr_vars, fml, aux_vars); TRACE ("qe", - tout << "Projected array eqs:\n" << fml << "\n"; - tout << "Remaining array vars:\n" << arr_vars; - tout << "Aux vars:\n" << aux_vars; + tout << "Projected array eqs: " << fml << "\n"; + tout << "Remaining array vars: " << arr_vars << "\n"; + tout << "Aux vars: " << aux_vars << "\n"; ); // 2. reduce selects @@ -1406,8 +1406,8 @@ namespace qe { ps (mdl, arr_vars, fml, aux_vars); TRACE ("qe", - tout << "Projected array selects:\n" << fml << "\n"; - tout << "All aux vars:\n" << aux_vars; + tout << "Projected array selects: " << fml << "\n"; + tout << "All aux vars: " << aux_vars << "\n"; ); } diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index bc9ac8c41..b1c92cb8c 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -77,42 +77,6 @@ expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { return expr_ref(nullptr, m); } -void project_plugin::partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits) { - ast_manager& m = vals.get_manager(); - expr_ref val(m); - expr_ref_vector trail(m), reps(m); - obj_map roots; - for (unsigned i = 0; i < vals.size(); ++i) { - expr* v = vals[i], *root; - VERIFY (model.eval(v, val)); - if (roots.find(val, root)) { - lits.push_back(m.mk_eq(v, root)); - } - else { - roots.insert(val, v); - trail.push_back(val); - reps.push_back(v); - } - } - if (reps.size() > 1) { - lits.push_back(mk_distinct(reps)); - } -} - -#if 0 -void project_plugin::partition_args(model& model, app_ref_vector const& selects, expr_ref_vector& lits) { - ast_manager& m = selects.get_manager(); - if (selects.empty()) return; - unsigned num_args = selects[0]->get_decl()->get_arity(); - for (unsigned j = 1; j < num_args; ++j) { - expr_ref_vector args(m); - for (unsigned i = 0; i < selects.size(); ++i) { - args.push_back(selects[i]->get_arg(j)); - } - project_plugin::partition_values(model, args, lits); - } -} -#endif void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { lits[i] = lits.back(); @@ -136,7 +100,6 @@ class mbp::impl { // parameters bool m_reduce_all_selects; - bool m_native_mbp; bool m_dont_sub; void add_plugin(project_plugin* p) { @@ -324,8 +287,7 @@ class mbp::impl { void operator() (app *n) { expr *e1, *e2; if (m_array.is_select (n)) { - for (unsigned i = 1; i < n->get_num_args(); ++i) { - expr * arg = n->get_arg(i); + for (expr * arg : *n) { if (m.get_sort(arg) == m.get_sort(m_var) && arg != m_var) m_res.push_back (arg); } @@ -378,8 +340,7 @@ class mbp::impl { // -- evaluate to initialize eval cache (void) eval (fml); unsigned j = 0; - for (unsigned i = 0; i < vars.size (); ++i) { - app* v = vars.get(i); + for (app * v : vars) { if (!project_var (eval, v, fml)) { vars[j++] = v; } @@ -389,7 +350,6 @@ class mbp::impl { public: - opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { arith_project_plugin arith(m); return arith.maximize(fmls, mdl, t, ge, gt); @@ -530,7 +490,6 @@ public: void updt_params(params_ref const& p) { m_params.append(p); m_reduce_all_selects = m_params.get_bool("reduce_all_selects", false); - m_native_mbp = m_params.get_bool("native_mbp", false); m_dont_sub = m_params.get_bool("dont_sub", false); } @@ -549,6 +508,7 @@ public: bool validate_model(model& model, expr_ref_vector const& fmls) { expr_ref val(m); + model_evaluator eval(model); for (expr * f : fmls) { VERIFY(model.eval(f, val) && m.is_true(val)); } @@ -677,27 +637,20 @@ public: tout << "extended model:\n"; model_pp (tout, mdl); tout << "Auxiliary variables of index and value sorts:\n"; - tout << vars; + tout << vars << "\n"; ); } // project reals and ints if (!arith_vars.empty ()) { - TRACE ("qe", tout << "Arith vars:\n" << arith_vars;); + TRACE ("qe", tout << "Arith vars: " << arith_vars << "\n";); - if (m_native_mbp) { - expr_ref_vector fmls(m); - flatten_and (fml, fmls); - - (*this)(true, arith_vars, mdl, fmls); - fml = mk_and (fmls); - SASSERT (arith_vars.empty ()); - } - else { - NOT_IMPLEMENTED_YET(); - // qe::arith_project (mdl, arith_vars, fml); - } + expr_ref_vector fmls(m); + flatten_and (fml, fmls); + (*this)(true, arith_vars, mdl, fmls); + fml = mk_and (fmls); + TRACE ("qe", tout << "Projected arith vars:\n" << fml << "\n"; tout << "Remaining arith vars:\n" << arith_vars << "\n";); @@ -740,7 +693,6 @@ void mbp::updt_params(params_ref const& p) { void mbp::get_param_descrs(param_descrs & r) { r.insert("reduce_all_selects", CPK_BOOL, "(default: false) reduce selects"); - r.insert("native_mbp", CPK_BOOL, "(default: false) switch between native and spacer tailored mbp"); r.insert("dont_sub", CPK_BOOL, "(default: false) disable substitution of values for free variables"); } diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 665e25d48..25a95e885 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -40,8 +40,6 @@ namespace qe { virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { }; static expr_ref pick_equality(ast_manager& m, model& model, expr* t); - static void partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits); - //static void partition_args(model& model, app_ref_vector const& sels, expr_ref_vector& lits); static void erase(expr_ref_vector& lits, unsigned& i); static void push_back(expr_ref_vector& lits, expr* lit); static void mark_rec(expr_mark& visited, expr* e); From bf4c35982f35831e67765589d07f28d405de00eb Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 21:28:50 -0700 Subject: [PATCH 133/364] Debug print --- src/qe/qe_arrays.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 305fb47b6..4a70d1fcb 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -631,13 +631,13 @@ namespace qe { m_v = arr_vars.get (i); if (!m_arr_u.is_array (m_v)) { TRACE ("qe", - tout << "not an array variable: " << m_v << "\n"; + tout << "not an array variable: " << mk_pp (m_v, m) << "\n"; ); aux_vars.push_back (m_v); continue; } TRACE ("qe", - tout << "projecting equalities on variable: " << m_v << "\n"; + tout << "projecting equalities on variable: " << mk_pp (m_v, m) << "\n"; ); if (project (fml)) { @@ -653,8 +653,8 @@ namespace qe { ); } else { - IF_VERBOSE(2, verbose_stream() << "can't project:" << m_v << "\n";); - TRACE ("qe", tout << "Failed to project: " << m_v << "\n";); + IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(m_v, m) << "\n";); + TRACE ("qe", tout << "Failed to project: " << mk_pp (m_v, m) << "\n";); arr_vars[j++] = m_v; } } From 00f870b7ffd6bd0ea159b57c969e3a950b3cc556 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 21 May 2018 22:01:49 -0700 Subject: [PATCH 134/364] to_mbp_benchmark(): prints an mbp problem in benchmark format currently unused. See comment in spacer_util.c:qe_project for example usage --- src/muz/spacer/spacer_util.cpp | 84 +++++++++++++++++++++------------- src/muz/spacer/spacer_util.h | 3 ++ 2 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 90e0542fa..e2a1a2c3d 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -90,33 +90,33 @@ namespace spacer { if (!m_model) { return; } m_mev = alloc(model_evaluator, *m_model); } - + bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { m_mev->set_model_completion (model_completion); try { m_mev->operator() (e, result); return true; - } + } catch (model_evaluator_exception &ex) { (void)ex; TRACE("spacer_model_evaluator", tout << ex.msg () << "\n";); return false; } } - + bool model_evaluator_util::eval(const expr_ref_vector &v, expr_ref& res, bool model_completion) { expr_ref e(m); e = mk_and (v); return eval(e, res, model_completion); } - - + + bool model_evaluator_util::is_true(const expr_ref_vector &v) { expr_ref res(m); return eval (v, res, false) && m.is_true (res); } - + bool model_evaluator_util::is_false(expr *x) { expr_ref res(m); return eval(x, res, false) && m.is_false (res); @@ -126,7 +126,7 @@ namespace spacer { expr_ref res(m); return eval(x, res, false) && m.is_true (res); } - + void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); @@ -172,16 +172,16 @@ namespace spacer { SASSERT(orig_size <= 1 + conjs.size()); if (i + 1 == orig_size) { // no-op. - } + } else if (orig_size <= conjs.size()) { // no-op - } + } else { SASSERT(orig_size == 1 + conjs.size()); --orig_size; --i; } - } + } else { conjs[i] = tmp; } @@ -199,7 +199,7 @@ namespace spacer { ast_manager& m; public: ite_hoister(ast_manager& m): m(m) {} - + br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; @@ -234,7 +234,7 @@ namespace spacer { } ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} }; - + class ite_hoister_star : public rewriter_tpl { ite_hoister_cfg m_cfg; public: @@ -242,7 +242,7 @@ namespace spacer { rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; - + void hoist_non_bool_if(expr_ref& fml) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); @@ -274,7 +274,7 @@ namespace spacer { bool is_arith_expr(expr *e) const { return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); } - + bool is_offset(expr* e) const { if (a.is_numeral(e)) { return true; @@ -358,7 +358,7 @@ namespace spacer { !a.is_mul(lhs) && !a.is_mul(rhs); } - + bool test_term(expr* e) const { if (m.is_bool(e)) { return true; @@ -403,9 +403,9 @@ namespace spacer { public: test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - + void test_for_utvpi() { m_test_for_utvpi = true; } - + void operator()(expr* e) { if (!m_is_dl) { return; @@ -422,7 +422,7 @@ namespace spacer { m_is_dl = test_term(a->get_arg(i)); } } - + if (!m_is_dl) { char const* msg = "non-diff: "; if (m_test_for_utvpi) { @@ -431,10 +431,10 @@ namespace spacer { IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } - + bool is_dl() const { return m_is_dl; } }; - + bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); expr_fast_mark1 mark; @@ -443,7 +443,7 @@ namespace spacer { } return test.is_dl(); } - + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); test.test_for_utvpi(); @@ -455,7 +455,7 @@ namespace spacer { } - void subst_vars(ast_manager& m, + void subst_vars(ast_manager& m, app_ref_vector const& vars, model* M, expr_ref& fml) { expr_safe_replace sub (m); @@ -469,6 +469,24 @@ namespace spacer { sub (fml); } +void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) { + ast_manager &m = vars.m(); + ast_pp_util pp(m); + pp.collect(fml); + pp.display_decls(out); + + out << "(define-fun mbp_benchmark_fml () Bool\n "; + out << mk_pp(fml, m) << ")\n\n"; + + out << "(push)\n" + << "(assert mbp_benchmark_fml)\n" + << "(check-sat)\n" + << "(mbp mbp_benchmark_fml ("; + for (auto v : vars) {out << mk_pp(v, m) << " ";} + out << "))\n" + << "(pop)\n" + << "(exit)\n"; +} /* * eliminate simple equalities using qe_lite * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays @@ -488,7 +506,11 @@ namespace spacer { flatten_and (fml, flat); fml = mk_and(flat); } - + + + // uncomment for benchmarks + //to_mbp_benchmark(verbose_stream(), fml, vars); + app_ref_vector arith_vars (m); app_ref_vector array_vars (m); array_util arr_u (m); @@ -501,7 +523,7 @@ namespace spacer { qe_lite qe(m, p, false); qe (vars, fml); rw (fml); - + TRACE ("spacer_mbp", tout << "After qe_lite:\n"; tout << mk_pp (fml, m) << "\n"; @@ -509,7 +531,7 @@ namespace spacer { SASSERT (!m.is_false (fml)); - + // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { if (m.is_bool (v)) { @@ -523,7 +545,7 @@ namespace spacer { arith_vars.push_back (v); } } - + // substitute Booleans if (!bool_sub.empty()) { bool_sub (fml); @@ -533,13 +555,13 @@ namespace spacer { TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); bool_sub.reset (); } - + TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); - + vars.reset (); - + // project arrays { scoped_no_proof _sp (m); @@ -550,14 +572,14 @@ namespace spacer { srw (fml); SASSERT (!m.is_false (fml)); } - + TRACE ("spacer_mbp", tout << "extended model:\n"; model_pp (tout, *M); tout << "Auxiliary variables of index and value sorts:\n"; tout << vars; ); - + if (vars.empty()) { break; } } diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index dabfa494a..ffbd81de7 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -125,6 +125,9 @@ bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); +void to_mbp_benchmark(std::ostream &out, const expr* fml, + const app_ref_vector &vars); + /** * do the following in sequence * 1. use qe_lite to cheaply eliminate vars From 4e9023b8fe9411fec29939ad426be3b1aedc76a3 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 22 May 2018 08:57:17 -0700 Subject: [PATCH 135/364] Remove dead code --- src/muz/spacer/spacer_context.cpp | 29 ----------------------------- src/muz/spacer/spacer_context.h | 2 -- 2 files changed, 31 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index df945d9bb..87c9580e0 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -854,35 +854,6 @@ void pred_transformer::find_predecessors(datalog::rule const& r, ptr_vector >& preds) const -{ - preds.reset(); - obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - for (; it != end; it++) { - datalog::rule const& r = *it->m_value; - unsigned tail_sz = r.get_uninterpreted_tail_size(); - for (unsigned ti = 0; ti < tail_sz; ti++) { - preds.push_back(std::make_pair (r.get_tail(ti)->get_decl(), ti)); - } - } -} - - -void pred_transformer::remove_predecessors(expr_ref_vector& literals) -{ - // remove tags - for (unsigned i = 0; i < literals.size(); ) { - expr* l = literals[i].get(); - m.is_not(l, l); - if (m_tag2rule.contains(l)) { - literals[i] = literals.back(); - literals.pop_back(); - } else { - ++i; - } - } -} - void pred_transformer::simplify_formulas() {m_frames.simplify_formulas ();} diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 8de4a70f8..1991aa906 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -370,9 +370,7 @@ public: unsigned level, unsigned oidx, bool must, const ptr_vector **aux); - void remove_predecessors(expr_ref_vector& literals); void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; - void find_predecessors(vector >& predicates) const; datalog::rule const* find_rule(model &mev, bool& is_concrete, vector& reach_pred_used, unsigned& num_reuse_reach); From 68b7966254aeca8ed7eab70fa40026726389a700 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 22 May 2018 09:05:43 -0700 Subject: [PATCH 136/364] Use C++11 --- src/muz/spacer/spacer_context.h | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 1991aa906..0301df615 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -206,38 +206,37 @@ class pred_transformer { void get_frame_lemmas (unsigned level, expr_ref_vector &out) { - for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) - if(m_lemmas[i]->level() == level) { - out.push_back(m_lemmas[i]->get_expr()); + for (auto &lemma : m_lemmas) { + if (lemma->level() == level) { + out.push_back(lemma->get_expr()); } + } } void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) { - for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) - if(m_lemmas [i]->level() >= level) { - out.push_back(m_lemmas[i]->get_expr()); + for (auto &lemma : m_lemmas) { + if(lemma->level() >= level) { + out.push_back(lemma->get_expr()); } + } } - unsigned size () const {return m_size;} unsigned lemma_size () const {return m_lemmas.size ();} void add_frame () {m_size++;} void inherit_frames (frames &other) { - for (unsigned i = 0, sz = other.m_lemmas.size (); i < sz; ++i) { - lemma_ref lem = alloc(lemma, m_pt.get_ast_manager(), - other.m_lemmas[i]->get_expr (), - other.m_lemmas[i]->level()); - lem->add_binding(other.m_lemmas[i]->get_bindings()); - add_lemma(lem.get()); + for (auto &other_lemma : other.m_lemmas) { + lemma_ref new_lemma = alloc(lemma, m_pt.get_ast_manager(), + other_lemma->get_expr(), + other_lemma->level()); + new_lemma->add_binding(other_lemma->get_bindings()); + add_lemma(new_lemma.get()); } m_sorted = false; } - bool add_lemma (lemma *lem); + bool add_lemma (lemma *new_lemma); void propagate_to_infinity (unsigned level); bool propagate_to_next_level (unsigned level); - - }; /** From 95d820196b8b03e1bd560e8ebfaeeff49ae9d633 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 22 May 2018 09:34:01 -0700 Subject: [PATCH 137/364] Cleanup --- src/muz/spacer/spacer_context.cpp | 30 ++-- src/muz/spacer/spacer_context.h | 242 +++++++++++++----------------- 2 files changed, 118 insertions(+), 154 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 87c9580e0..c38015363 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -658,16 +658,8 @@ pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): m_extend_lit = m.mk_not (m.mk_const (pm.get_n_pred (v->get_decl ()))); } -pred_transformer::~pred_transformer() -{ - rule2inst::iterator it2 = m_rule2inst.begin(), end2 = m_rule2inst.end(); - for (; it2 != end2; ++it2) { - dealloc(it2->m_value); - } - rule2expr::iterator it3 = m_rule2transition.begin(), end3 = m_rule2transition.end(); - for (; it3 != end3; ++it3) { - m.dec_ref(it3->m_value); - } +pred_transformer::~pred_transformer() { + for (auto &entry : m_rule2transition) {m.dec_ref(entry.m_value);} } std::ostream& pred_transformer::display(std::ostream& out) const @@ -1556,20 +1548,19 @@ void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule, // Predicates that are variable representatives. Other predicates at // positions the variables occur are made equivalent with these. expr_ref_vector side(m); - app_ref_vector* var_reprs = alloc(app_ref_vector, m); - SASSERT(var_reprs); + app_ref_vector var_reprs(m); ptr_vector aux_vars; unsigned ut_size = rule.get_uninterpreted_tail_size(); unsigned t_size = rule.get_tail_size(); SASSERT(ut_size <= t_size); - init_atom(pts, rule.get_head(), *var_reprs, side, UINT_MAX); + init_atom(pts, rule.get_head(), var_reprs, side, UINT_MAX); for (unsigned i = 0; i < ut_size; ++i) { if (rule.is_neg_tail(i)) { throw default_exception("SPACER does not support " "negated predicates in rule tails"); } - init_atom(pts, rule.get_tail(i), *var_reprs, side, i); + init_atom(pts, rule.get_tail(i), var_reprs, side, i); } // -- substitute free variables expr_ref fml(m); @@ -1579,12 +1570,12 @@ void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule, {tail.push_back(rule.get_tail(i));} fml = mk_and (tail); - ground_free_vars(fml, *var_reprs, aux_vars, ut_size == 0); - SASSERT(is_all_non_null(*var_reprs)); + ground_free_vars(fml, var_reprs, aux_vars, ut_size == 0); + SASSERT(is_all_non_null(var_reprs)); expr_ref tmp(m); - var_subst (m, false)(fml, var_reprs->size (), - (expr*const*)var_reprs->c_ptr(), tmp); + var_subst(m, false)(fml, var_reprs.size (), + (expr*const*)var_reprs.c_ptr(), tmp); flatten_and (tmp, side); fml = mk_and(side); side.reset (); @@ -1607,12 +1598,11 @@ void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule, m_rule2transition.insert(&rule, fml); } // AG: shouldn't this be under the if-statement above? - m_rule2inst.insert(&rule, var_reprs); m_rule2vars.insert(&rule, aux_vars); TRACE("spacer", tout << rule.get_decl()->get_name() << "\n"; - tout << *var_reprs << "\n";); + tout << var_reprs << "\n";); } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 0301df615..f42309fac 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -266,32 +266,30 @@ class pred_transformer { typedef obj_map rule2expr; typedef obj_map > rule2apps; + typedef obj_map expr2rule; + manager& pm; // spacer::manager + ast_manager& m; // ast_manager + context& ctx; // spacer::context - manager& pm; // spacer-manager - ast_manager& m; // manager - context& ctx; - - func_decl_ref m_head; // predicate - func_decl_ref_vector m_sig; // signature - ptr_vector m_use; // places where 'this' is referenced. - ptr_vector m_rules; // rules used to derive transformer - prop_solver m_solver; // solver context - solver* m_reach_ctx; // context for reachability facts - pobs m_pobs; - frames m_frames; - reach_fact_ref_vector m_reach_facts; // reach facts - /// Number of initial reachability facts - unsigned m_rf_init_sz; - obj_map m_tag2rule; // map tag predicate to rule. + func_decl_ref m_head; // predicate + func_decl_ref_vector m_sig; // signature + ptr_vector m_use; // places where 'this' is referenced. + ptr_vector m_rules; // rules used to derive transformer + prop_solver m_solver; // solver context + solver* m_reach_ctx; // context for reachability facts + pobs m_pobs; // proof obligations created so far + frames m_frames; // frames with lemmas + reach_fact_ref_vector m_reach_facts; // reach facts + unsigned m_rf_init_sz; // number of reach fact from INIT + expr2rule m_tag2rule; // map tag predicate to rule. rule2expr m_rule2tag; // map rule to predicate tag. - rule2inst m_rule2inst; // map rules to instantiations of indices rule2expr m_rule2transition; // map rules to transition rule2apps m_rule2vars; // map rule to auxiliary variables expr_ref m_transition; // transition relation. expr_ref m_initial_state; // initial state. app_ref m_extend_lit; // literal to extend initial state bool m_all_init; // true if the pt has no uninterpreted body in any rule - ptr_vector m_predicates; + ptr_vector m_predicates; // temp vector used with find_predecessors() stats m_stats; stopwatch m_initialize_watch; stopwatch m_must_reachable_watch; @@ -320,7 +318,6 @@ class pred_transformer { void simplify_formulas(tactic& tac, expr_ref_vector& fmls); - // Debugging void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); expr* mk_fresh_reach_case_var (); @@ -330,46 +327,46 @@ public: ~pred_transformer(); inline bool use_native_mbp (); - reach_fact *get_reach_fact (expr *v) - { - for (unsigned i = 0, sz = m_reach_facts.size (); i < sz; ++i) - if(v == m_reach_facts [i]->get()) { return m_reach_facts[i]; } - return nullptr; + reach_fact *get_reach_fact (expr *v) { + for (auto *rf : m_reach_facts) { + if (v == rf->get()) {return rf;} } + return nullptr; + } + void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; - void add_rule(datalog::rule* r) { m_rules.push_back(r); } - void add_use(pred_transformer* pt) { if(!m_use.contains(pt)) { m_use.insert(pt); } } + void add_rule(datalog::rule* r) {m_rules.push_back(r);} + void add_use(pred_transformer* pt) {if(!m_use.contains(pt)) {m_use.insert(pt);}} void initialize(decl2rel const& pts); - func_decl* head() const { return m_head; } - ptr_vector const& rules() const { return m_rules; } - func_decl* sig(unsigned i) const { return m_sig[i]; } // signature - func_decl* const* sig() { return m_sig.c_ptr(); } - unsigned sig_size() const { return m_sig.size(); } - expr* transition() const { return m_transition; } - expr* initial_state() const { return m_initial_state; } - expr* rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); } - unsigned get_num_levels() { return m_frames.size (); } + func_decl* head() const {return m_head;} + ptr_vector const& rules() const {return m_rules;} + func_decl* sig(unsigned i) const {return m_sig[i];} // signature + func_decl* const* sig() {return m_sig.c_ptr();} + unsigned sig_size() const {return m_sig.size();} + expr* transition() const {return m_transition;} + expr* initial_state() const {return m_initial_state;} + expr* rule2tag(datalog::rule const* r) {return m_rule2tag.find(r);} + unsigned get_num_levels() {return m_frames.size ();} expr_ref get_cover_delta(func_decl* p_orig, int level); void add_cover(unsigned level, expr* property); - expr_ref get_reachable (); + expr_ref get_reachable(); std::ostream& display(std::ostream& strm) const; void collect_statistics(statistics& st) const; void reset_statistics(); - bool is_must_reachable (expr* state, model_ref* model = nullptr); + bool is_must_reachable(expr* state, model_ref* model = nullptr); /// \brief Returns reachability fact active in the given model /// all determines whether initial reachability facts are included as well - reach_fact *get_used_reach_fact (model_evaluator_util& mev, bool all = true); + reach_fact *get_used_reach_fact(model_evaluator_util& mev, bool all = true); /// \brief Returns reachability fact active in the origin of the given model - reach_fact* get_used_origin_reach_fact (model_evaluator_util &mev, unsigned oidx); - expr_ref get_origin_summary (model_evaluator_util &mev, - unsigned level, unsigned oidx, bool must, - const ptr_vector **aux); + reach_fact* get_used_origin_reach_fact(model_evaluator_util &mev, unsigned oidx); + expr_ref get_origin_summary(model_evaluator_util &mev, + unsigned level, unsigned oidx, bool must, + const ptr_vector **aux); - void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; datalog::rule const* find_rule(model &mev, bool& is_concrete, vector& reach_pred_used, unsigned& num_reuse_reach); @@ -409,8 +406,10 @@ public: unsigned& solver_level, expr_ref_vector* core = nullptr); bool is_invariant(unsigned level, expr* lem, - unsigned& solver_level, expr_ref_vector* core = nullptr) - { UNREACHABLE(); return false; } + unsigned& solver_level, expr_ref_vector* core = nullptr) { + // XXX only needed for legacy_frames to compile + UNREACHABLE(); return false; + } bool check_inductive(unsigned level, expr_ref_vector& state, unsigned& assumes_level, unsigned weakness = UINT_MAX); @@ -420,8 +419,8 @@ public: void simplify_formulas(); context& get_context () const {return ctx;} - manager& get_manager() const { return pm; } - ast_manager& get_ast_manager() const { return m; } + manager& get_manager() const {return pm;} + ast_manager& get_ast_manager() const {return m;} void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r); @@ -546,11 +545,10 @@ public: double get_expand_time(unsigned depth) { return m_expand_watches[depth].get_seconds();} void inc_ref () {++m_ref_count;} - void dec_ref () - { - --m_ref_count; - if(m_ref_count == 0) { dealloc(this); } - } + void dec_ref () { + --m_ref_count; + if(m_ref_count == 0) {dealloc(this);} + } class on_expand_event { @@ -600,16 +598,16 @@ class derivation { const ptr_vector *aux_vars = nullptr); premise (const premise &p); - bool is_must () {return m_must;} - expr * get_summary () {return m_summary.get ();} - app_ref_vector &get_ovars () {return m_ovars;} - unsigned get_oidx () {return m_oidx;} - pred_transformer &pt () {return m_pt;} + bool is_must() {return m_must;} + expr * get_summary() {return m_summary.get ();} + app_ref_vector &get_ovars() {return m_ovars;} + unsigned get_oidx() {return m_oidx;} + pred_transformer &pt() {return m_pt;} /// \brief Updated the summary. /// The new summary is over n-variables. - void set_summary (expr * summary, bool must, - const ptr_vector *aux_vars = nullptr); + void set_summary(expr * summary, bool must, + const ptr_vector *aux_vars = nullptr); }; @@ -630,6 +628,8 @@ class derivation { /// -- create next child using given model as the guide /// -- returns NULL if there is no next child pob* create_next_child (model_evaluator_util &mev); + /// existentially quantify vars and skolemize the result + void exist_skolemize(expr *fml, app_ref_vector &vars, expr_ref &res); public: derivation (pob& parent, datalog::rule const& rule, expr *trans, app_ref_vector const &evars); @@ -645,8 +645,6 @@ public: /// premise must be consistent with the transition relation pob *create_next_child (); - /// existentially quantify vars and skolemize the result - void exist_skolemize(expr *fml, app_ref_vector &vars, expr_ref &res); datalog::rule const& get_rule () const { return m_rule; } pob& get_parent () const { return m_parent; } ast_manager &get_ast_manager () const {return m_parent.get_ast_manager ();} @@ -672,22 +670,20 @@ public: void pop () {m_obligations.pop ();} void push (pob &n); - void inc_level () - { - SASSERT (!m_obligations.empty () || m_root); - m_max_level++; - m_min_depth++; - if(m_root && m_obligations.empty()) { m_obligations.push(m_root); } - } + void inc_level () { + SASSERT (!m_obligations.empty () || m_root); + m_max_level++; + m_min_depth++; + if(m_root && m_obligations.empty()) { m_obligations.push(m_root); } + } - pob& get_root() const { return *m_root.get (); } + pob& get_root() const {return *m_root.get ();} void set_root(pob& n); bool is_root (pob& n) const {return m_root.get () == &n;} - unsigned max_level () {return m_max_level;} - unsigned min_depth () {return m_min_depth;} - size_t size () {return m_obligations.size ();} - + unsigned max_level() {return m_max_level;} + unsigned min_depth() {return m_min_depth;} + size_t size() {return m_obligations.size();} }; @@ -788,21 +784,23 @@ class context { json_marshaller m_json_marshaller; // Functions used by search. - lbool solve_core (unsigned from_lvl = 0); + lbool solve_core(unsigned from_lvl = 0); bool is_requeue(pob &n); - bool check_reachability (); + bool check_reachability(); bool propagate(unsigned min_prop_lvl, unsigned max_prop_lvl, unsigned full_prop_lvl); bool is_reachable(pob &n); lbool expand_pob(pob &n, pob_ref_buffer &out); - reach_fact *mk_reach_fact (pob& n, model_evaluator_util &mev, - datalog::rule const& r); - bool create_children(pob& n, datalog::rule const& r, - model_evaluator_util &model, + reach_fact *mk_reach_fact(pob& n, model_evaluator_util &mev, + datalog::rule const& r); + bool create_children(pob& n, const datalog::rule &r, + model_evaluator_util &mdl, const vector& reach_pred_used, pob_ref_buffer &out); + expr_ref mk_sat_answer(); expr_ref mk_unsat_answer() const; + unsigned get_cex_depth (); // Generate inductive property void get_level_property(unsigned lvl, expr_ref_vector& res, @@ -811,27 +809,21 @@ class context { // Initialization void init_lemma_generalizers(); + void reset_lemma_generalizers(); void inherit_lemmas(const decl2rel& rels); void init_global_smt_params(); + void init_rules(datalog::rule_set& rules, decl2rel& transformers); + // (re)initialize context with new relations + void init(const decl2rel &rels); + bool validate(); bool check_invariant(unsigned lvl); bool check_invariant(unsigned lvl, func_decl* fn); void checkpoint(); - void init_rules(datalog::rule_set& rules, decl2rel& transformers); - - // (re)initialize context with new relations - void init(const decl2rel &rels); - void simplify_formulas(); - void reset_lemma_generalizers(); - - bool validate(); - - unsigned get_cex_depth (); - void dump_json(); void predecessor_eh(); @@ -839,86 +831,68 @@ class context { public: /** Initial values of predicates are stored in corresponding relations in dctx. - We check whether there is some reachable state of the relation checked_relation. */ - context( - fixedpoint_params const& params, - ast_manager& m); - + context(fixedpoint_params const& params, ast_manager& m); ~context(); - fixedpoint_params const& get_params() const { return m_params; } + const fixedpoint_params &get_params() const { return m_params; } bool use_native_mbp () {return m_use_native_mbp;} bool use_ground_cti () {return m_ground_cti;} - bool use_instantiate () { return m_instantiate; } + bool use_instantiate () {return m_instantiate;} bool weak_abs() {return m_weak_abs;} - bool use_qlemmas () {return m_use_qlemmas; } + bool use_qlemmas () {return m_use_qlemmas;} + + ast_manager& get_ast_manager() const {return m;} + manager& get_manager() {return m_pm;} + decl2rel const& get_pred_transformers() const {return m_rels;} + pred_transformer& get_pred_transformer(func_decl* p) const {return *m_rels.find(p);} + + datalog::context& get_datalog_context() const { + SASSERT(m_context); return *m_context; + } + + void update_rules(datalog::rule_set& rules); + lbool solve(unsigned from_lvl = 0); + lbool solve_from_lvl (unsigned from_lvl); + - ast_manager& get_ast_manager() const { return m; } - manager& get_manager() { return m_pm; } - decl2rel const& get_pred_transformers() const { return m_rels; } - pred_transformer& get_pred_transformer(func_decl* p) const - { return *m_rels.find(p); } - datalog::context& get_datalog_context() const - { SASSERT(m_context); return *m_context; } expr_ref get_answer(); /** * get bottom-up (from query) sequence of ground predicate instances * (for e.g. P(0,1,0,0,3)) that together form a ground derivation to query */ expr_ref get_ground_sat_answer (); + void get_rules_along_trace (datalog::rule_ref_vector& rules); void collect_statistics(statistics& st) const; void reset_statistics(); - - std::ostream& display(std::ostream& strm) const; - - void display_certificate(std::ostream& strm) const {} - - lbool solve(unsigned from_lvl = 0); - - lbool solve_from_lvl (unsigned from_lvl); - void reset(); - void set_query(func_decl* q) { m_query_pred = q; } - - void set_unsat() { m_last_result = l_false; } - - void set_model_converter(model_converter_ref& mc) { m_mc = mc; } - - void get_rules_along_trace (datalog::rule_ref_vector& rules); + std::ostream& display(std::ostream& out) const; + void display_certificate(std::ostream& out) const {NOT_IMPLEMENTED_YET();} + pob& get_root() const {return m_pob_queue.get_root();} + void set_query(func_decl* q) {m_query_pred = q;} + void set_unsat() {m_last_result = l_false;} + void set_model_converter(model_converter_ref& mc) {m_mc = mc;} model_converter_ref get_model_converter() { return m_mc; } - void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; } - - void update_rules(datalog::rule_set& rules); + scoped_ptr_vector &callbacks() {return m_callbacks;} unsigned get_num_levels(func_decl* p); expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p); - void add_cover(int level, func_decl* pred, expr* property); - expr_ref get_reachable (func_decl* p); - void add_invariant (func_decl *pred, expr* property); - model_ref get_model(); - proof_ref get_proof() const; - pob& get_root() const { return m_pob_queue.get_root(); } - expr_ref get_constraints (unsigned lvl); void add_constraint (expr *c, unsigned lvl); void new_lemma_eh(pred_transformer &pt, lemma *lem); - - scoped_ptr_vector &callbacks() {return m_callbacks;} - void new_pob_eh(pob *p); bool is_inductive(); From 55126692c90920870dc672258cb3c1cc7f36c7a6 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 22 May 2018 14:45:05 -0700 Subject: [PATCH 138/364] spacer: counterexample to pushing (ctp) Enable using fixedpoint.spacer.ctp=true For each lemma L currently at level k, keep a model M that justifies why L cannot be pushed to (k+1). L is not pushed while the model M remains valid. --- src/muz/base/fixedpoint_params.pyg | 3 +- src/muz/spacer/spacer_context.cpp | 152 +++++++++++++++++++---------- src/muz/spacer/spacer_context.h | 30 ++++-- 3 files changed, 123 insertions(+), 62 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 799e06fe6..10e319786 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -200,5 +200,6 @@ def_module_params('fixedpoint', ('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'), ('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"), ('spacer.from_level', UINT, 0, 'starting level to explore'), - ('spacer.print_json', SYMBOL, '', 'print pobs tree in JSON format to a given file') + ('spacer.print_json', SYMBOL, '', 'print pobs tree in JSON format to a given file'), + ('spacer.ctp', BOOL, False, 'enable counterexample-to-pushing technique'), )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index c38015363..7602c29b2 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -443,7 +443,7 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(lvl), - m_pob(nullptr), m_external(false) { + m_pob(nullptr), m_ctp(nullptr), m_external(false) { SASSERT(m_body); normalize(m_body, m_body); } @@ -452,7 +452,7 @@ lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(p->level()), - m_pob(p), m_external(false) { + m_pob(p), m_ctp(nullptr), m_external(false) { SASSERT(m_pob); m_pob->get_skolems(m_zks); add_binding(m_pob->get_binding()); @@ -463,7 +463,7 @@ lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(p->level()), - m_pob(p), m_external(false) + m_pob(p), m_ctp(nullptr), m_external(false) { if (m_pob) { m_pob->get_skolems(m_zks); @@ -687,11 +687,15 @@ void pred_transformer::collect_statistics(statistics& st) const // -- number of proof obligations (0 if pobs are not reused) st.update("SPACER num pobs", m_pobs.size()); + st.update("SPACER num ctp", m_stats.m_num_ctp); + st.update("SPACER num is_invariant", m_stats.m_num_is_invariant); + // -- time in rule initialization st.update ("time.spacer.init_rules.pt.init", m_initialize_watch.get_seconds ()); // -- time is must_reachable() st.update ("time.spacer.solve.pt.must_reachable", m_must_reachable_watch.get_seconds ()); + st.update("time.spacer.ctp", m_ctp_watch.get_seconds()); } void pred_transformer::reset_statistics() @@ -701,6 +705,7 @@ void pred_transformer::reset_statistics() m_stats.reset(); m_initialize_watch.reset (); m_must_reachable_watch.reset (); + m_ctp_watch.reset(); } void pred_transformer::init_sig() @@ -781,20 +786,31 @@ reach_fact *pred_transformer::get_used_origin_reach_fact (model_evaluator_util& return res; } -datalog::rule const* pred_transformer::find_rule(model &model, +const datalog::rule *pred_transformer::find_rule(model &model) { + expr_ref val(m); + + for (auto &entry : m_tag2rule) { + app *tag = to_app(entry.m_key); + if (model.eval(tag->get_decl(), val) && m.is_true(val)) { + return entry.m_value; + } + } + return nullptr; +} + +const datalog::rule *pred_transformer::find_rule(model &model, bool& is_concrete, vector& reach_pred_used, unsigned& num_reuse_reach) { - typedef obj_map tag2rule; TRACE ("spacer_verbose", datalog::rule_manager& rm = ctx.get_datalog_context().get_rule_manager(); - tag2rule::iterator it = m_tag2rule.begin(); - tag2rule::iterator end = m_tag2rule.end(); - for (; it != end; ++it) { - expr* pred = it->m_key; + for (auto &entry : m_tag2rule) { + expr* pred = entry.m_key; tout << mk_pp(pred, m) << ":\n"; - if (it->m_value) { rm.display_smt2(*(it->m_value), tout) << "\n"; } + if (entry.m_value) { + rm.display_smt2(*(entry.m_value), tout) << "\n"; + } } ); @@ -802,33 +818,32 @@ datalog::rule const* pred_transformer::find_rule(model &model, // prefer a rule where the model intersects with reach facts of all predecessors; // also find how many predecessors' reach facts are true in the model expr_ref vl(m); - datalog::rule const* r = ((datalog::rule*)nullptr); - tag2rule::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - for (; it != end; ++it) { - expr* tag = it->m_key; + const datalog::rule *r = ((datalog::rule*)nullptr); + for (auto &entry : m_tag2rule) { + expr* tag = entry.m_key; if (model.eval(to_app(tag)->get_decl(), vl) && m.is_true(vl)) { - r = it->m_value; + r = entry.m_value; is_concrete = true; num_reuse_reach = 0; - reach_pred_used.reset (); - unsigned tail_sz = r->get_uninterpreted_tail_size (); + reach_pred_used.reset(); + unsigned tail_sz = r->get_uninterpreted_tail_size(); for (unsigned i = 0; i < tail_sz; i++) { bool used = false; func_decl* d = r->get_tail(i)->get_decl(); - pred_transformer const& pt = ctx.get_pred_transformer (d); - if (!pt.has_reach_facts()) { is_concrete = false; } + const pred_transformer &pt = ctx.get_pred_transformer(d); + if (!pt.has_reach_facts()) {is_concrete = false;} else { expr_ref v(m); - pm.formula_n2o (pt.get_last_reach_case_var (), v, i); - model.eval (to_app (v.get ())->get_decl (), vl); + pm.formula_n2o(pt.get_last_reach_case_var (), v, i); + model.eval(to_app (v.get ())->get_decl (), vl); used = m.is_false (vl); is_concrete = is_concrete && used; } reach_pred_used.push_back (used); - if (used) { num_reuse_reach++; } + if (used) {num_reuse_reach++;} } - if (is_concrete) { break; } + if (is_concrete) {break;} } } // SASSERT (r); @@ -1268,30 +1283,21 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, post.push_back (n.post ()); // populate reach_assumps - - // XXX eager_reach_check must always be - // XXX enabled. Otherwise, we can get into an infinite loop in - // XXX which a model is consistent with a must-summary, but the - // XXX appropriate assumption is not set correctly by the model. - // XXX Original code handled reachability-events differently. - if (/* ctx.get_params ().eager_reach_check () && */ - n.level () > 0 && !m_all_init) { - obj_map::iterator it = m_tag2rule.begin (), - end = m_tag2rule.end (); - for (; it != end; ++it) { - datalog::rule const* r = it->m_value; - if (!r) { continue; } + if (n.level () > 0 && !m_all_init) { + for (auto &entry : m_tag2rule) { + datalog::rule const* r = entry.m_value; + if (!r) {continue;} find_predecessors(*r, m_predicates); - if (m_predicates.empty()) { continue; } + if (m_predicates.empty()) {continue;} for (unsigned i = 0; i < m_predicates.size(); i++) { const pred_transformer &pt = - ctx.get_pred_transformer (m_predicates [i]); + ctx.get_pred_transformer(m_predicates[i]); if (pt.has_reach_facts()) { expr_ref a(m); - pm.formula_n2o (pt.get_last_reach_case_var (), a, i); - reach_assumps.push_back (m.mk_not (a)); + pm.formula_n2o(pt.get_last_reach_case_var (), a, i); + reach_assumps.push_back(m.mk_not (a)); } else if (ctx.get_params().spacer_init_reach_facts()) { - reach_assumps.push_back (m.mk_not (it->m_key)); + reach_assumps.push_back(m.mk_not (entry.m_key)); break; } } @@ -1325,7 +1331,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, if (is_sat == l_true || is_sat == l_undef) { if (core) { core->reset(); } if (model) { - r = find_rule (**model, is_concrete, reach_pred_used, num_reuse_reach); + r = find_rule(**model, is_concrete, reach_pred_used, num_reuse_reach); TRACE ("spacer", tout << "reachable " << "is_concrete " << is_concrete << " rused: "; for (unsigned i = 0, sz = reach_pred_used.size (); i < sz; ++i) @@ -1352,11 +1358,47 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, return l_undef; } +/// returns true if lemma is blocked by an existing model +bool pred_transformer::is_ctp_blocked(lemma *lem) { + if (!ctx.get_params().spacer_ctp()) {return false;} + + if (!lem->has_ctp()) {return false;} + scoped_watch _t_(m_ctp_watch); + + model_ref &ctp = lem->get_ctp(); + + // -- find rule of the ctp + const datalog::rule *r; + r = find_rule(*ctp); + if (r == nullptr) {return false;} + + // -- find predicates along the rule + find_predecessors(*r, m_predicates); + + // check if any lemmas block the model + for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { + pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); + expr_ref lemmas(m), val(m); + lemmas = pt.get_formulas(lem->level(), false); + pm.formula_n2o(lemmas.get(), lemmas, i); + if (ctp->eval(lemmas, val) && m.is_false(val)) {return true;} + } + + return false; +} + bool pred_transformer::is_invariant(unsigned level, lemma* lem, - unsigned& solver_level, expr_ref_vector* core) + unsigned& solver_level, + expr_ref_vector* core) { - expr_ref lemma(m); - lemma = lem->get_expr(); + m_stats.m_num_is_invariant++; + if (is_ctp_blocked(lem)) { + m_stats.m_num_ctp++; + return false; + } + + expr_ref lemma_expr(m); + lemma_expr = lem->get_expr(); expr_ref_vector conj(m), aux(m); expr_ref gnd_lemma(m); @@ -1364,28 +1406,36 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, if (!get_context().use_qlemmas() && !lem->is_ground()) { app_ref_vector tmp(m); - ground_expr(to_quantifier(lemma)->get_expr (), gnd_lemma, tmp); - lemma = gnd_lemma.get(); + ground_expr(to_quantifier(lemma_expr)->get_expr (), gnd_lemma, tmp); + lemma_expr = gnd_lemma.get(); } - conj.push_back(mk_not(m, lemma)); + conj.push_back(mk_not(m, lemma_expr)); flatten_and (conj); prop_solver::scoped_level _sl(m_solver, level); prop_solver::scoped_subset_core _sc (m_solver, true); prop_solver::scoped_weakness _sw (m_solver, 1, ctx.weak_abs() ? lem->weakness() : UINT_MAX); + model_ref mdl; m_solver.set_core(core); - m_solver.set_model(nullptr); + m_solver.set_model(&mdl); expr * bg = m_extend_lit.get (); lbool r = m_solver.check_assumptions (conj, aux, 1, &bg, 1); if (r == l_false) { solver_level = m_solver.uses_level (); + lem->reset_ctp(); CTRACE ("spacer", level < m_solver.uses_level (), tout << "Checking at level " << level << " but only using " << m_solver.uses_level () << "\n";); SASSERT (level <= solver_level); } + else if (r == l_true) { + // optionally remove unused symbols from the model + lem->set_ctp(mdl); + } + else {lem->reset_ctp();} + return r == l_false; } @@ -1795,9 +1845,7 @@ bool pred_transformer::frames::propagate_to_next_level (unsigned level) m_pt.ensure_level (tgt_level); for (unsigned i = 0, sz = m_lemmas.size(); i < sz && m_lemmas [i]->level() <= level;) { - if (m_lemmas [i]->level () < level) - {++i; continue;} - + if (m_lemmas [i]->level () < level) {++i; continue;} unsigned solver_level; if (m_pt.is_invariant(tgt_level, m_lemmas.get(i), solver_level)) { diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index f42309fac..5cdc66ad1 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -116,6 +116,7 @@ class lemma { app_ref_vector m_bindings; unsigned m_lvl; pob_ref m_pob; + model_ref m_ctp; // counter-example to pushing bool m_external; void mk_expr_core(); @@ -127,6 +128,12 @@ public: // lemma(const lemma &other) = delete; ast_manager &get_ast_manager() {return m;} + + model_ref& get_ctp() {return m_ctp;} + bool has_ctp() {return !is_inductive() && m_ctp;} + void set_ctp(model_ref &v) {m_ctp = v;} + void reset_ctp() {m_ctp.reset();} + expr *get_expr(); bool is_false(); expr_ref_vector const &get_cube(); @@ -141,6 +148,7 @@ public: inline void set_external(bool ext){m_external = ext;} inline bool external() { return m_external;} + bool is_inductive() const {return is_infty_level(m_lvl);} unsigned level () const {return m_lvl;} void set_level (unsigned lvl); app_ref_vector& get_bindings() {return m_bindings;} @@ -151,12 +159,11 @@ public: bool is_ground () {return !is_quantifier (get_expr());} void inc_ref () {++m_ref_count;} - void dec_ref () - { - SASSERT (m_ref_count > 0); - --m_ref_count; - if(m_ref_count == 0) { dealloc(this); } - } + void dec_ref () { + SASSERT (m_ref_count > 0); + --m_ref_count; + if(m_ref_count == 0) {dealloc(this);} + } }; struct lemma_lt_proc : public std::binary_function { @@ -180,6 +187,8 @@ class pred_transformer { struct stats { unsigned m_num_propagations; unsigned m_num_invariants; + unsigned m_num_ctp; + unsigned m_num_is_invariant; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; @@ -293,7 +302,7 @@ class pred_transformer { stats m_stats; stopwatch m_initialize_watch; stopwatch m_must_reachable_watch; - + stopwatch m_ctp_watch; /// Auxiliary variables to represent different disjunctive @@ -367,7 +376,9 @@ public: unsigned level, unsigned oidx, bool must, const ptr_vector **aux); - datalog::rule const* find_rule(model &mev, bool& is_concrete, + bool is_ctp_blocked(lemma *lem); + const datalog::rule *find_rule(model &mdl); + const datalog::rule *find_rule(model &mev, bool& is_concrete, vector& reach_pred_used, unsigned& num_reuse_reach); expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); } @@ -403,7 +414,8 @@ public: vector& reach_pred_used, unsigned& num_reuse_reach); bool is_invariant(unsigned level, lemma* lem, - unsigned& solver_level, expr_ref_vector* core = nullptr); + unsigned& solver_level, + expr_ref_vector* core = nullptr); bool is_invariant(unsigned level, expr* lem, unsigned& solver_level, expr_ref_vector* core = nullptr) { From 40781c0b0c6fe265df8930ce782aaff5a33d697e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 22 May 2018 14:48:11 -0700 Subject: [PATCH 139/364] Comment on params used in spacer_context --- src/muz/spacer/spacer_context.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 7602c29b2..ed4947052 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2301,16 +2301,15 @@ void context::init_global_smt_params() { fparams.m_mbqi = m_params.spacer_mbqi(); if (!m_params.spacer_ground_cti()) { - fparams.m_pi_use_database = true; + fparams.m_pi_use_database = true; // you don't need this fparams.m_phase_selection = PS_CACHING_CONSERVATIVE2; fparams.m_restart_strategy = RS_GEOMETRIC; fparams.m_restart_factor = 1.5; fparams.m_eliminate_bounds = true; - fparams.m_qi_quick_checker = MC_UNSAT; - fparams.m_propagate_booleans = true; + fparams.m_qi_quick_checker = MC_UNSAT; // fparams.m_qi_eager_threshold = 10; fparams.m_qi_lazy_threshold = 20; - fparams.m_ng_lift_ite = LI_FULL; + fparams.m_ng_lift_ite = LI_FULL; // ? probably useless } } From 477ac4a19af43237a1c1cbb2036c97ef86d4809a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 22 May 2018 14:49:21 -0700 Subject: [PATCH 140/364] Remove dead m_propagate_booleans param --- src/smt/params/preprocessor_params.cpp | 1 - src/smt/params/preprocessor_params.h | 2 -- src/smt/smt_setup.cpp | 3 --- 3 files changed, 6 deletions(-) diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index ee4b7c2e4..3e1c6f0cd 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -46,7 +46,6 @@ void preprocessor_params::display(std::ostream & out) const { DISPLAY_PARAM(m_eliminate_term_ite); DISPLAY_PARAM(m_macro_finder); DISPLAY_PARAM(m_propagate_values); - DISPLAY_PARAM(m_propagate_booleans); DISPLAY_PARAM(m_refine_inj_axiom); DISPLAY_PARAM(m_eliminate_bounds); DISPLAY_PARAM(m_simplify_bit2int); diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h index be7fd4c01..f6724fada 100644 --- a/src/smt/params/preprocessor_params.h +++ b/src/smt/params/preprocessor_params.h @@ -37,7 +37,6 @@ struct preprocessor_params : public pattern_inference_params, bool m_eliminate_term_ite; bool m_macro_finder; bool m_propagate_values; - bool m_propagate_booleans; bool m_refine_inj_axiom; bool m_eliminate_bounds; bool m_simplify_bit2int; @@ -59,7 +58,6 @@ public: m_eliminate_term_ite(false), m_macro_finder(false), m_propagate_values(true), - m_propagate_booleans(false), // TODO << check peformance m_refine_inj_axiom(true), m_eliminate_bounds(false), m_simplify_bit2int(false), diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 55ea55663..3ab40cdc3 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -574,7 +574,6 @@ namespace smt { m_params.m_bv_cc = false; m_params.m_bb_ext_gates = true; m_params.m_nnf_cnf = false; - m_params.m_propagate_booleans = true; m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); setup_arrays(); } @@ -644,7 +643,6 @@ namespace smt { m_params.m_restart_factor = 1.5; m_params.m_eliminate_bounds = true; m_params.m_qi_quick_checker = MC_UNSAT; - m_params.m_propagate_booleans = true; m_params.m_qi_lazy_threshold = 20; // m_params.m_qi_max_eager_multipatterns = 10; /// <<< HACK m_params.m_mbqi = true; // enabling MBQI and MACRO_FINDER by default :-) @@ -672,7 +670,6 @@ namespace smt { m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_eliminate_bounds = true; m_params.m_qi_quick_checker = MC_UNSAT; - m_params.m_propagate_booleans = true; m_params.m_qi_eager_threshold = 5; // Added for MBQI release m_params.m_qi_lazy_threshold = 20; From 1da002d7b1e6ced16e9871744c29b2f934f2f9b0 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 22 May 2018 14:49:52 -0700 Subject: [PATCH 141/364] scoped params on solver solver::push_params() saves current parameters solver::pop_params() restores previously saved parameters --- src/smt/smt_solver.cpp | 14 ++++++++++++++ src/solver/solver.cpp | 6 ++++++ src/solver/solver.h | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index d282a59da..0bef90a48 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -118,6 +118,20 @@ namespace smt { m_core_extend_nonlocal_patterns = smth.core_extend_nonlocal_patterns(); } + params_ref m_params_save; + smt_params m_smt_params_save; + + void push_params() override { + m_params_save.reset(); + m_params_save.copy(solver::get_params()); + m_smt_params_save = m_smt_params; + } + + void pop_params() override { + m_smt_params = m_smt_params_save; + solver::reset_params(m_params_save); + } + void collect_param_descrs(param_descrs & r) override { m_context.collect_param_descrs(r); } diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 4b812f83b..cc1d472ca 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -206,6 +206,12 @@ void solver::collect_param_descrs(param_descrs & r) { r.insert("solver.enforce_model_conversion", CPK_BOOL, "(default: false) enforce model conversion when asserting formulas"); } +void solver::reset_params(params_ref const & p) { + m_params.reset(); + m_params.copy(p); + m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); +} + void solver::updt_params(params_ref const & p) { m_params.copy(p); m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); diff --git a/src/solver/solver.h b/src/solver/solver.h index bb330636f..4c0c361ff 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -60,6 +60,11 @@ public: */ virtual void updt_params(params_ref const & p); + /** + \brief reset parameters. + */ + virtual void reset_params(params_ref const& p); + /** \brief Retrieve set of parameters set on solver. */ @@ -70,6 +75,17 @@ public: parameters available in this solver. */ virtual void collect_param_descrs(param_descrs & r); + + /** + \brief Push a parameter state. It is restored upon pop. + Only a single scope of push is supported. + */ + virtual void push_params() {} + + /** + \brief Pop a parameter state. \sa push_params. + */ + virtual void pop_params() {} /** \brief Enable/Disable model generation for this solver object. From 9c37bef55305cd46cd619ae6976f7c75b143137c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 23 May 2018 11:04:34 -0700 Subject: [PATCH 142/364] Fix bug in ctp --- src/muz/spacer/spacer_context.cpp | 23 +++++++++++++---------- src/muz/spacer/spacer_context.h | 9 +++++---- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index ed4947052..945f6686e 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -687,8 +687,9 @@ void pred_transformer::collect_statistics(statistics& st) const // -- number of proof obligations (0 if pobs are not reused) st.update("SPACER num pobs", m_pobs.size()); - st.update("SPACER num ctp", m_stats.m_num_ctp); + st.update("SPACER num ctp blocked", m_stats.m_num_ctp_blocked); st.update("SPACER num is_invariant", m_stats.m_num_is_invariant); + st.update("SPACER num lemma jumped", m_stats.m_num_lemma_level_jump); // -- time in rule initialization st.update ("time.spacer.init_rules.pt.init", m_initialize_watch.get_seconds ()); @@ -1375,16 +1376,18 @@ bool pred_transformer::is_ctp_blocked(lemma *lem) { // -- find predicates along the rule find_predecessors(*r, m_predicates); - // check if any lemmas block the model + // check if any lemma blocks the ctp model for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); expr_ref lemmas(m), val(m); lemmas = pt.get_formulas(lem->level(), false); pm.formula_n2o(lemmas.get(), lemmas, i); - if (ctp->eval(lemmas, val) && m.is_false(val)) {return true;} + if (ctp->eval(lemmas, val) && m.is_false(val)) {return false;} } - return false; + // lem is blocked by ctp since none of the lemmas at the previous + // level block ctp + return true; } bool pred_transformer::is_invariant(unsigned level, lemma* lem, @@ -1393,7 +1396,7 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, { m_stats.m_num_is_invariant++; if (is_ctp_blocked(lem)) { - m_stats.m_num_ctp++; + m_stats.m_num_ctp_blocked++; return false; } @@ -1418,21 +1421,21 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, prop_solver::scoped_weakness _sw (m_solver, 1, ctx.weak_abs() ? lem->weakness() : UINT_MAX); model_ref mdl; + model_ref *mdl_ref_ptr = nullptr; + if (ctx.get_params().spacer_ctp()) {mdl_ref_ptr = &mdl;} m_solver.set_core(core); - m_solver.set_model(&mdl); + m_solver.set_model(mdl_ref_ptr); expr * bg = m_extend_lit.get (); lbool r = m_solver.check_assumptions (conj, aux, 1, &bg, 1); if (r == l_false) { solver_level = m_solver.uses_level (); lem->reset_ctp(); - CTRACE ("spacer", level < m_solver.uses_level (), - tout << "Checking at level " << level - << " but only using " << m_solver.uses_level () << "\n";); + if (level < m_solver.uses_level()) {m_stats.m_num_lemma_level_jump++;} SASSERT (level <= solver_level); } else if (r == l_true) { // optionally remove unused symbols from the model - lem->set_ctp(mdl); + if (mdl_ref_ptr) {lem->set_ctp(*mdl_ref_ptr);} } else {lem->reset_ctp();} diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 5cdc66ad1..c1241a1bd 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -185,10 +185,11 @@ struct lemma_lt_proc : public std::binary_function { class pred_transformer { struct stats { - unsigned m_num_propagations; - unsigned m_num_invariants; - unsigned m_num_ctp; - unsigned m_num_is_invariant; + unsigned m_num_propagations; // num of times lemma is pushed higher + unsigned m_num_invariants; // num of infty lemmas found + unsigned m_num_ctp_blocked; // num of time ctp blocked lemma pushing + unsigned m_num_is_invariant; // num of times lemmas are pushed + unsigned m_num_lemma_level_jump; // lemma learned at higher level than expected stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; From df2e9d8fe249048f75fb4c685676c2afed589136 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 23 May 2018 17:12:28 -0700 Subject: [PATCH 143/364] Make smt_solver::updt_params() commulative --- src/smt/smt_solver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 0bef90a48..01b78b80e 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -110,9 +110,9 @@ namespace smt { void updt_params(params_ref const & p) override { solver::updt_params(p); - m_smt_params.updt_params(p); - m_context.updt_params(p); - smt_params_helper smth(p); + m_smt_params.updt_params(solver::get_params()); + m_context.updt_params(solver::get_params()); + smt_params_helper smth(solver::get_params()); m_core_extend_patterns = smth.core_extend_patterns(); m_core_extend_patterns_max_distance = smth.core_extend_patterns_max_distance(); m_core_extend_nonlocal_patterns = smth.core_extend_nonlocal_patterns(); From 4b09cefb9717a40b7bc71ff3b0e3e78f3163bb23 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 23 May 2018 14:27:32 -0700 Subject: [PATCH 144/364] Replace smt::kernel with smt_solver Replace all ad-hoc uses of smt::kernel with ad-hoc uses of smt_solver --- src/muz/spacer/spacer_context.cpp | 26 ++++++++++++++------------ src/muz/spacer/spacer_generalizers.cpp | 18 +++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 945f6686e..f29f73b3a 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2256,9 +2256,10 @@ bool context::validate() fv.reverse (); tmp = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), tmp); } - smt::kernel solver(m, m_pm.fparams2()); - solver.assert_expr(tmp); - lbool res = solver.check(); + ref sol = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + sol->assert_expr(tmp); + lbool res = sol->check_sat(0, nullptr); if (res != l_false) { msg << "rule validation failed when checking: " << mk_pp(tmp, m); @@ -2648,7 +2649,8 @@ expr_ref context::get_ground_sat_answer() { cex.push_back(m.mk_const(preds[0])); } // smt context to obtain local cexes - scoped_ptr cex_ctx = alloc (smt::kernel, m, m_pm.fparams2 ()); + ref cex_ctx = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); model_evaluator_util mev (m); // preorder traversal of the query derivation tree @@ -2689,7 +2691,7 @@ expr_ref context::get_ground_sat_answer() } cex_ctx->assert_expr (pt->transition ()); cex_ctx->assert_expr (pt->rule2tag (r)); - lbool res = cex_ctx->check (); + lbool res = cex_ctx->check_sat(0, nullptr); CTRACE("cex", res == l_false, tout << "Cex fact: " << mk_pp(cex_fact, m) << "\n"; for (unsigned i = 0; i < u_tail_sz; i++) @@ -3624,10 +3626,9 @@ void context::reset_statistics() bool context::check_invariant(unsigned lvl) { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { + for (auto &entry : m_rels) { checkpoint(); - if (!check_invariant(lvl, it->m_key)) { + if (!check_invariant(lvl, entry.m_key)) { return false; } } @@ -3636,7 +3637,7 @@ bool context::check_invariant(unsigned lvl) bool context::check_invariant(unsigned lvl, func_decl* fn) { - smt::kernel ctx(m, m_pm.fparams2()); + ref ctx = mk_smt_solver(m, params_ref::get_empty(), symbol::null); pred_transformer& pt = *m_rels.find(fn); expr_ref_vector conj(m); expr_ref inv = pt.get_formulas(next_level(lvl), false); @@ -3644,9 +3645,10 @@ bool context::check_invariant(unsigned lvl, func_decl* fn) pt.add_premises(m_rels, lvl, conj); conj.push_back(m.mk_not(inv)); expr_ref fml(m.mk_and(conj.size(), conj.c_ptr()), m); - ctx.assert_expr(fml); - lbool result = ctx.check(); - TRACE("spacer", tout << "Check invariant level: " << lvl << " " << result << "\n" << mk_pp(fml, m) << "\n";); + ctx->assert_expr(fml); + lbool result = ctx->check_sat(0, nullptr); + TRACE("spacer", tout << "Check invariant level: " << lvl << " " << result + << "\n" << mk_pp(fml, m) << "\n";); return result == l_false; } diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 34f1eb730..7263f8264 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -29,7 +29,7 @@ Revision History: #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" #include "ast/expr_functors.h" - +#include "smt/smt_solver.h" namespace spacer { void lemma_sanity_checker::operator()(lemma_ref &lemma) { @@ -249,17 +249,17 @@ void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) { eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)), m.mk_const(vsymbs.get(j)))); } - smt::kernel solver(m, m_ctx.get_manager().fparams2()); + ref sol = mk_smt_solver(m, params_ref::get_empty(), symbol::null); expr_ref_vector lits(m); for (unsigned i = 0, core_sz = core.size(); i < core_sz; ++i) { SASSERT(lits.size() == i); - solver.push(); - solver.assert_expr(core.get(i)); + sol->push(); + sol->assert_expr(core.get(i)); for (unsigned j = 0, eqs_sz = eqs.size(); j < eqs_sz; ++j) { - solver.push(); - solver.assert_expr(eqs.get(j)); - lbool res = solver.check(); - solver.pop(1); + sol->push(); + sol->assert_expr(eqs.get(j)); + lbool res = sol->check_sat(0, nullptr); + sol->pop(1); if (res == l_false) { TRACE("core_array_eq", @@ -269,7 +269,7 @@ void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) break; } } - solver.pop(1); + sol->pop(1); if (lits.size() == i) { lits.push_back(core.get(i)); } } From c2b8f25cf922b3c5cb5c708aa393e1391461d8cf Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 23 May 2018 15:03:44 -0700 Subject: [PATCH 145/364] Switch to using solver instead of smt::kernel all around --- src/muz/spacer/spacer_context.cpp | 61 ++++++++---- src/muz/spacer/spacer_iuc_solver.h | 6 +- src/muz/spacer/spacer_manager.h | 8 +- src/muz/spacer/spacer_prop_solver.cpp | 18 ++-- src/muz/spacer/spacer_prop_solver.h | 29 +++--- src/muz/spacer/spacer_smt_context_manager.cpp | 23 +++-- src/muz/spacer/spacer_smt_context_manager.h | 7 +- src/muz/spacer/spacer_virtual_solver.cpp | 96 +++++++------------ src/muz/spacer/spacer_virtual_solver.h | 36 ++++--- 9 files changed, 146 insertions(+), 138 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index f29f73b3a..b1ab0082a 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2289,33 +2289,56 @@ void context::reset_lemma_generalizers() // initialize global SMT parameters shared by all solvers void context::init_global_smt_params() { m.toggle_proof_mode(PGM_ENABLED); - smt_params &fparams = m_pm.fparams (); + params_ref p; if (!m_params.spacer_eq_prop ()) { - fparams.m_arith_bound_prop = BP_NONE; - fparams.m_arith_auto_config_simplex = true; - fparams.m_arith_propagate_eqs = false; - fparams.m_arith_eager_eq_axioms = false; + // arith_propagation_mode + p.set_uint("arith.propagation_mode", 0); + // fparams.m_arith_bound_prop = BP_NONE; + // NOT AVAILABLE + // fparams.m_arith_auto_config_simplex = true; + // arith_propagate_eqs + // fparams.m_arith_propagate_eqs = false; + p.set_bool("arith.propagate_eqs", false); + // NOT AVAILABLE + // fparams.m_arith_eager_eq_axioms = false; } - fparams.m_random_seed = m_params.spacer_random_seed (); + // fparams.m_random_seed = m_params.spacer_random_seed (); + p.set_uint("random_seed", m_params.spacer_random_seed()); - fparams.m_dump_benchmarks = m_params.spacer_vs_dump_benchmarks(); - fparams.m_dump_min_time = m_params.spacer_vs_dump_min_time(); - fparams.m_dump_recheck = m_params.spacer_vs_recheck(); + // fparams.m_dump_benchmarks = m_params.spacer_vs_dump_benchmarks(); + // fparams.m_dump_min_time = m_params.spacer_vs_dump_min_time(); + // fparams.m_dump_recheck = m_params.spacer_vs_recheck(); - fparams.m_mbqi = m_params.spacer_mbqi(); + // mbqi + // fparams.m_mbqi = m_params.spacer_mbqi(); + p.set_bool("mbqi", m_params.spacer_mbqi()); if (!m_params.spacer_ground_cti()) { - fparams.m_pi_use_database = true; // you don't need this - fparams.m_phase_selection = PS_CACHING_CONSERVATIVE2; - fparams.m_restart_strategy = RS_GEOMETRIC; - fparams.m_restart_factor = 1.5; - fparams.m_eliminate_bounds = true; - fparams.m_qi_quick_checker = MC_UNSAT; // - fparams.m_qi_eager_threshold = 10; - fparams.m_qi_lazy_threshold = 20; - fparams.m_ng_lift_ite = LI_FULL; // ? probably useless + // fparams.m_pi_use_database = true; // you don't need this + // phase_selection + // fparams.m_phase_selection = PS_CACHING_CONSERVATIVE2; + p.set_uint("phase_selection", 4); + // restart_strategy + // fparams.m_restart_strategy = RS_GEOMETRIC; + p.set_uint("restart_strategy", 0); + // restart factor + // fparams.m_restart_factor = 1.5; + p.set_double("restart_factor", 1.5); + // probably not needed in our use case + // fparams.m_eliminate_bounds = true; + // NONE + // fparams.m_qi_quick_checker = MC_UNSAT; // + // qi_eager_threshold + // fparams.m_qi_eager_threshold = 10; + p.set_double("qi.eager_threshold", 10.0); + // qi_lazy_threshold + // fparams.m_qi_lazy_threshold = 20; + p.set_double("qi.lazy_threshold", 20.0); + // useless + // fparams.m_ng_lift_ite = LI_FULL; } + m_pm.updt_params(p); } void context::init_lemma_generalizers() { diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index 568124629..8bb0a8605 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -106,7 +106,11 @@ public: /* solver interface */ solver* translate(ast_manager &m, params_ref const &p) override { return m_solver.translate(m, p);} - void updt_params(params_ref const &p) override { m_solver.updt_params(p);} + void updt_params(params_ref const &p) override {m_solver.updt_params(p);} + void reset_params(params_ref const &p) override {m_solver.reset_params(p);} + const params_ref &get_params() const override {return m_solver.get_params();} + void push_params() override {m_solver.push_params();} + void pop_params() override {m_solver.pop_params();} void collect_param_descrs(param_descrs &r) override { m_solver.collect_param_descrs(r);} void set_produce_models(bool f) override { m_solver.set_produce_models(f);} void assert_expr_core(expr *t) override { m_solver.assert_expr(t);} diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index 58b5aca07..471090c30 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -155,11 +155,13 @@ public: // three different solvers with three different sets of parameters // different solvers are used for different types of queries in spacer solver* mk_fresh() {return m_contexts.mk_fresh();} - smt_params& fparams() { return m_contexts.fparams(); } + void updt_params(const params_ref &p) {m_contexts.updt_params(p);} + solver* mk_fresh2() {return m_contexts2.mk_fresh();} - smt_params &fparams2() { return m_contexts2.fparams(); } + void updt_params2(const params_ref &p) {m_contexts2.updt_params(p);} + solver* mk_fresh3() {return m_contexts3.mk_fresh();} - smt_params &fparams3() {return m_contexts3.fparams();} + void updt_params3(const params_ref &p) {m_contexts3.updt_params(p);} diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 1dd4515a7..a4ea79a25 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -56,10 +56,7 @@ prop_solver::prop_solver(spacer::manager& pm, { m_solvers[0] = pm.mk_fresh(); - m_fparams[0] = &pm.fparams(); - m_solvers[1] = pm.mk_fresh2(); - m_fparams[1] = &pm.fparams2(); m_contexts[0] = alloc(spacer::iuc_solver, *(m_solvers[0]), p.spacer_iuc(), @@ -293,8 +290,12 @@ lbool prop_solver::internal_check_assumptions( { // XXX Turn model generation if m_model != 0 SASSERT(m_ctx); - SASSERT(m_ctx_fparams); - flet _model(m_ctx_fparams->m_model, m_model != nullptr); + + params_ref p; + if (m_model != nullptr) { + p.set_bool("produce_models", true); + m_ctx->updt_params(p); + } if (m_in_level) { assert_level_atoms(m_current_level); } lbool result = maxsmt(hard_atoms, soft_atoms); @@ -333,6 +334,12 @@ lbool prop_solver::internal_check_assumptions( // manually undo proxies because maxsmt() call above manually adds proxies m_ctx->undo_proxies(*m_core); } + + if (m_model != nullptr) { + p.set_bool("produce_models", false); + m_ctx->updt_params(p); + } + return result; } @@ -350,7 +357,6 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, flatten_and(hard); m_ctx = m_contexts [solver_id == 0 ? 0 : 0 /* 1 */].get(); - m_ctx_fparams = m_fparams [solver_id == 0 ? 0 : 0 /* 1 */]; // can be disabled if use_push_bg == true // solver::scoped_push _s_(*m_ctx); diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index d10ebcfcd..27b2fd51d 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -42,11 +42,9 @@ class prop_solver { private: ast_manager& m; symbol m_name; - smt_params* m_fparams[2]; solver* m_solvers[2]; scoped_ptr m_contexts[2]; iuc_solver * m_ctx; - smt_params * m_ctx_fparams; decl_vector m_level_preds; app_ref_vector m_pos_level_atoms; // atoms used to identify level app_ref_vector m_neg_level_atoms; // @@ -138,25 +136,20 @@ public: }; class scoped_weakness { - - smt_params &m_params; - bool m_arith_ignore_int; - bool m_array_weak; public: - scoped_weakness(prop_solver &ps, unsigned solver_id, unsigned weakness) : - m_params(*ps.m_fparams[solver_id == 0 ? 0 : 0 /*1*/]) { - // save current values - m_arith_ignore_int = m_params.m_arith_ignore_int; - m_array_weak = m_params.m_array_weak; + solver *sol; + scoped_weakness(prop_solver &ps, unsigned solver_id, unsigned weakness) + : sol(nullptr) { + sol = ps.m_solvers[solver_id == 0 ? 0 : 0 /* 1 */]; + if (!sol) return; + sol->push_params(); - // set values based on weakness score - m_params.m_arith_ignore_int = weakness < 1; - m_params.m_array_weak = weakness < 2; - } - ~scoped_weakness() { - m_params.m_arith_ignore_int = m_arith_ignore_int; - m_params.m_array_weak = m_array_weak; + params_ref p; + p.set_bool("arith.ignore_int", weakness < 1); + p.set_bool("array.weak", weakness < 2); + sol->updt_params(p); } + ~scoped_weakness() {if (sol) {sol->pop_params();}} }; }; } diff --git a/src/muz/spacer/spacer_smt_context_manager.cpp b/src/muz/spacer/spacer_smt_context_manager.cpp index e26381afd..78f01b6e5 100644 --- a/src/muz/spacer/spacer_smt_context_manager.cpp +++ b/src/muz/spacer/spacer_smt_context_manager.cpp @@ -24,19 +24,17 @@ Revision History: #include "smt/smt_context.h" #include "smt/params/smt_params.h" +#include "smt/smt_solver.h" #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_smt_context_manager.h" namespace spacer { - - - smt_context_manager::smt_context_manager(ast_manager &m, unsigned max_num_contexts, const params_ref &p) : - m_fparams(p), m(m), + m_params(p), m_max_num_contexts(max_num_contexts), m_num_contexts(0) { m_stats.reset();} @@ -45,6 +43,13 @@ smt_context_manager::~smt_context_manager() { std::for_each(m_solvers.begin(), m_solvers.end(), delete_proc()); + m_solvers.reset(); + m_base_solvers.reset(); +} + +void smt_context_manager::updt_params(params_ref const &p) { + m_params.append(p); + for (auto *s : m_base_solvers) {s->updt_params(m_params);} } virtual_solver* smt_context_manager::mk_fresh() @@ -53,10 +58,14 @@ virtual_solver* smt_context_manager::mk_fresh() virtual_solver_factory *solver_factory = nullptr; if (m_max_num_contexts == 0 || m_solvers.size() < m_max_num_contexts) { - m_solvers.push_back(alloc(spacer::virtual_solver_factory, m, m_fparams)); + m_base_solvers.push_back(mk_smt_solver(m, m_params, symbol::null)); + m_solvers.push_back(alloc(spacer::virtual_solver_factory, + *m_base_solvers.back())); solver_factory = m_solvers.back(); - } else - { solver_factory = m_solvers[(m_num_contexts - 1) % m_max_num_contexts]; } + } + else { + solver_factory = m_solvers[(m_num_contexts - 1) % m_max_num_contexts]; + } return solver_factory->mk_solver(); } diff --git a/src/muz/spacer/spacer_smt_context_manager.h b/src/muz/spacer/spacer_smt_context_manager.h index 0df9bc77b..a62aba6cd 100644 --- a/src/muz/spacer/spacer_smt_context_manager.h +++ b/src/muz/spacer/spacer_smt_context_manager.h @@ -37,9 +37,10 @@ class smt_context_manager { void reset() { memset(this, 0, sizeof(*this)); } }; - smt_params m_fparams; ast_manager& m; + params_ref m_params; unsigned m_max_num_contexts; + sref_vector m_base_solvers; ptr_vector m_solvers; unsigned m_num_contexts; @@ -58,8 +59,8 @@ public: void collect_statistics(statistics& st) const; void reset_statistics(); - void updt_params(params_ref const &p) { m_fparams.updt_params(p); } - smt_params& fparams() {return m_fparams;} + void updt_params(params_ref const &p); + }; diff --git a/src/muz/spacer/spacer_virtual_solver.cpp b/src/muz/spacer/spacer_virtual_solver.cpp index 873f94160..a1a1a7eec 100644 --- a/src/muz/spacer/spacer_virtual_solver.cpp +++ b/src/muz/spacer/spacer_virtual_solver.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - multi-solver view of a single smt::kernel + multi-solver view of a single solver Author: @@ -28,13 +28,14 @@ Notes: #include "ast/scoped_proof.h" +#include "smt/smt_kernel.h" + namespace spacer { -virtual_solver::virtual_solver(virtual_solver_factory &factory, - smt::kernel &context, app* pred) : - solver_na2as(context.m()), +virtual_solver::virtual_solver(virtual_solver_factory &factory, app* pred) : + solver_na2as(factory.get_manager()), m_factory(factory), - m(context.m()), - m_context(context), + m(factory.get_manager()), + m_context(factory.get_base_solver()), m_pred(pred, m), m_virtual(!m.is_true(pred)), m_assertions(m), @@ -42,7 +43,7 @@ virtual_solver::virtual_solver(virtual_solver_factory &factory, m_flat(m), m_pushed(false), m_in_delay_scope(false), - m_dump_benchmarks(factory.fparams().m_dump_benchmarks), + m_dump_benchmarks(false /*factory.fparams().m_dump_benchmarks*/), m_dump_counter(0), m_proof(m) { @@ -114,7 +115,7 @@ lbool virtual_solver::check_sat_core(unsigned num_assumptions, out << "(exit)\n"; out.close(); } - lbool res = m_context.check(num_assumptions, assumptions); + lbool res = m_context.check_sat(num_assumptions, assumptions); sw.stop(); if (res == l_true) { m_factory.m_check_sat_watch.add(sw); @@ -125,8 +126,8 @@ lbool virtual_solver::check_sat_core(unsigned num_assumptions, } set_status(res); - if (m_dump_benchmarks && - sw.get_seconds() >= m_factory.fparams().m_dump_min_time) { + if (false /*m_dump_benchmarks && + sw.get_seconds() >= m_factory.fparams().m_dump_min_time*/) { std::stringstream file_name; file_name << "virt_solver"; if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); } @@ -151,13 +152,13 @@ lbool virtual_solver::check_sat_core(unsigned num_assumptions, st.display_smt2(out); - if (m_factory.fparams().m_dump_recheck) { + if (false /* m_factory.fparams().m_dump_recheck */) { scoped_no_proof _no_proof_(m); smt_params p; stopwatch sw2; smt::kernel kernel(m, p); - for (unsigned i = 0, sz = m_context.size(); i < sz; ++i) - { kernel.assert_expr(m_context.get_formula(i)); } + for (unsigned i = 0, sz = m_context.get_num_assertions(); i < sz; ++i) + { kernel.assert_expr(m_context.get_assertion(i)); } sw2.start(); kernel.check(num_assumptions, assumptions); sw2.stop(); @@ -208,10 +209,12 @@ void virtual_solver::pop_core(unsigned n) { void virtual_solver::get_unsat_core(ptr_vector &r) { - for (unsigned i = 0, sz = m_context.get_unsat_core_size(); i < sz; ++i) { - expr *core = m_context.get_unsat_core_expr(i); - if (is_aux_predicate(core)) { continue; } - r.push_back(core); + ptr_vector core; + m_context.get_unsat_core(core); + + for (unsigned i = 0, sz = core.size(); i < sz; ++i) { + if (is_aux_predicate(core.get(i))) { continue; } + r.push_back(core.get(i)); } } @@ -244,40 +247,25 @@ void virtual_solver::internalize_assertions() m_context.assert_expr(f); } } -void virtual_solver::refresh() -{ - SASSERT(!m_pushed); - m_head = 0; -} - -void virtual_solver::reset() -{ - SASSERT(!m_pushed); - m_head = 0; - m_assertions.reset(); - m_factory.refresh(); -} void virtual_solver::get_labels(svector &r) -{ - r.reset(); - buffer tmp; - m_context.get_relevant_labels(nullptr, tmp); - r.append(tmp.size(), tmp.c_ptr()); -} +{m_context.get_labels(r);} solver* virtual_solver::translate(ast_manager& m, params_ref const& p) { UNREACHABLE(); return nullptr; } -void virtual_solver::updt_params(params_ref const &p) { m_factory.updt_params(p); } -void virtual_solver::collect_param_descrs(param_descrs &r) { m_factory.collect_param_descrs(r); } -void virtual_solver::set_produce_models(bool f) { m_factory.set_produce_models(f); } -smt_params &virtual_solver::fparams() {return m_factory.fparams();} +void virtual_solver::updt_params(params_ref const &p) {m_context.updt_params(p);} +void virtual_solver::reset_params(params_ref const &p) {m_context.reset_params(p);} +const params_ref &virtual_solver::get_params() const {return m_context.get_params();} +void virtual_solver::push_params(){m_context.push_params();} +void virtual_solver::pop_params(){m_context.pop_params();} +void virtual_solver::collect_param_descrs(param_descrs &r) { m_context.collect_param_descrs(r); } +void virtual_solver::set_produce_models(bool f) { m_context.set_produce_models(f); } void virtual_solver::to_smt2_benchmark(std::ostream &out, - smt::kernel &context, + solver &context, unsigned num_assumptions, expr * const * assumptions, char const * name, @@ -288,11 +276,8 @@ void virtual_solver::to_smt2_benchmark(std::ostream &out, ast_pp_util pp(m); expr_ref_vector asserts(m); - - for (unsigned i = 0, sz = context.size(); i < sz; ++i) { - asserts.push_back(context.get_formula(i)); - pp.collect(asserts.back()); - } + context.get_assertions(asserts); + pp.collect(asserts); pp.collect(num_assumptions, assumptions); pp.display_decls(out); pp.display_asserts(out, asserts); @@ -303,11 +288,8 @@ void virtual_solver::to_smt2_benchmark(std::ostream &out, } -virtual_solver_factory::virtual_solver_factory(ast_manager &mgr, smt_params &fparams) : - m_fparams(fparams), m(mgr), m_context(m, m_fparams) -{ - m_stats.reset(); -} +virtual_solver_factory::virtual_solver_factory(solver &base) : + m(base.get_manager()), m_context(base) {m_stats.reset();} virtual_solver* virtual_solver_factory::mk_solver() { @@ -316,7 +298,7 @@ virtual_solver* virtual_solver_factory::mk_solver() app_ref pred(m); pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); SASSERT(m_context.get_scope_level() == 0); - m_solvers.push_back(alloc(virtual_solver, *this, m_context, pred)); + m_solvers.push_back(alloc(virtual_solver, *this, pred)); return m_solvers.back(); } @@ -333,7 +315,6 @@ void virtual_solver_factory::collect_statistics(statistics &st) const } void virtual_solver_factory::reset_statistics() { - m_context.reset_statistics(); m_stats.reset(); m_check_sat_watch.reset(); m_check_undef_watch.reset(); @@ -341,17 +322,10 @@ void virtual_solver_factory::reset_statistics() m_proof_watch.reset(); } -void virtual_solver_factory::refresh() -{ - m_context.reset(); - for (unsigned i = 0, e = m_solvers.size(); i < e; ++i) - { m_solvers [i]->refresh(); } -} - virtual_solver_factory::~virtual_solver_factory() { for (unsigned i = 0, e = m_solvers.size(); i < e; ++i) - { dealloc(m_solvers [i]); } + { dealloc(m_solvers[i]); } } diff --git a/src/muz/spacer/spacer_virtual_solver.h b/src/muz/spacer/spacer_virtual_solver.h index 162f56178..e5db5e396 100644 --- a/src/muz/spacer/spacer_virtual_solver.h +++ b/src/muz/spacer/spacer_virtual_solver.h @@ -7,7 +7,7 @@ Module Name: Abstract: - multi-solver view of a single smt::kernel + multi-solver view of a single solver Author: @@ -21,7 +21,6 @@ Notes: #include"ast/ast.h" #include"util/params.h" #include"solver/solver_na2as.h" -#include"smt/smt_kernel.h" #include"smt/params/smt_params.h" #include"util/stopwatch.h" namespace spacer { @@ -33,7 +32,7 @@ class virtual_solver : public solver_na2as { private: virtual_solver_factory &m_factory; ast_manager &m; - smt::kernel &m_context; + solver &m_context; app_ref m_pred; bool m_virtual; @@ -49,12 +48,12 @@ private: proof_ref m_proof; - virtual_solver(virtual_solver_factory &factory, smt::kernel &context, app* pred); + virtual_solver(virtual_solver_factory &factory, app* pred); bool is_aux_predicate(expr *p); void internalize_assertions(); void to_smt2_benchmark(std::ostream &out, - smt::kernel &context, + solver &context, unsigned num_assumptions, expr * const * assumptions, char const * name = "benchmarks", @@ -62,8 +61,6 @@ private: char const * status = "unknown", char const * attributes = ""); - void refresh(); - public: ~virtual_solver() override; unsigned get_num_assumptions() const override @@ -84,21 +81,24 @@ public: void get_model_core(model_ref &m) override {m_context.get_model(m);} proof* get_proof() override; std::string reason_unknown() const override - {return m_context.last_failure_as_string();} + {return m_context.reason_unknown();} void set_reason_unknown(char const *msg) override {m_context.set_reason_unknown(msg);} ast_manager& get_manager() const override {return m;} void get_labels(svector &r) override; void set_produce_models(bool f) override; smt_params &fparams(); - void reset(); expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } void set_progress_callback(progress_callback *callback) override {UNREACHABLE();} solver *translate(ast_manager &m, params_ref const &p) override; void updt_params(params_ref const &p) override; + void reset_params(params_ref const& p) override; + params_ref const& get_params() const override; void collect_param_descrs(param_descrs &r) override; + void push_params() override; + void pop_params() override; protected: @@ -107,13 +107,12 @@ protected: void pop_core(unsigned n) override; }; -/// multi-solver abstraction on top of a single smt::kernel +/// multi-solver abstraction on top of a single solver class virtual_solver_factory { friend class virtual_solver; private: - smt_params &m_fparams; ast_manager &m; - smt::kernel m_context; + solver &m_context; /// solvers managed by this factory ptr_vector m_solvers; @@ -132,20 +131,17 @@ private: stopwatch m_proof_watch; - void refresh(); - - smt_params &fparams() { return m_fparams; } + solver &get_base_solver() {return m_context;} + ast_manager &get_manager() {return m;} public: - virtual_solver_factory(ast_manager &mgr, smt_params &fparams); + virtual_solver_factory(solver &base); virtual ~virtual_solver_factory(); virtual_solver* mk_solver(); void collect_statistics(statistics &st) const; void reset_statistics(); - void updt_params(params_ref const &p) { m_fparams.updt_params(p); } - void collect_param_descrs(param_descrs &r) { /* empty */ } - void set_produce_models(bool f) { m_fparams.m_model = f; } - bool get_produce_models() { return m_fparams.m_model; } + void updt_params(params_ref const &p) {m_context.updt_params(p);} + void collect_param_descrs(param_descrs &r) {m_context.collect_param_descrs(r);} }; } From ec8a86b78ad6119029fac03da7c612a354ea2340 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 23 May 2018 21:43:07 -0700 Subject: [PATCH 146/364] Removed unused m_qi_ematching parameter from smt_params --- src/smt/params/qi_params.cpp | 1 - src/smt/params/qi_params.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/smt/params/qi_params.cpp b/src/smt/params/qi_params.cpp index a9cff6e8c..deeb2b99c 100644 --- a/src/smt/params/qi_params.cpp +++ b/src/smt/params/qi_params.cpp @@ -40,7 +40,6 @@ void qi_params::updt_params(params_ref const & _p) { #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void qi_params::display(std::ostream & out) const { - DISPLAY_PARAM(m_qi_ematching); DISPLAY_PARAM(m_qi_cost); DISPLAY_PARAM(m_qi_new_gen); DISPLAY_PARAM(m_qi_eager_threshold); diff --git a/src/smt/params/qi_params.h b/src/smt/params/qi_params.h index cc1a30673..0f6c03f5b 100644 --- a/src/smt/params/qi_params.h +++ b/src/smt/params/qi_params.h @@ -29,7 +29,6 @@ enum quick_checker_mode { }; struct qi_params { - bool m_qi_ematching; std::string m_qi_cost; std::string m_qi_new_gen; double m_qi_eager_threshold; From d06f4bd3378697d3da09993a5dfdd995b2ebc35c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 11:14:51 -0700 Subject: [PATCH 147/364] Fix reset of params_ref in solver params_ref is not a ref, and params_ref::reset is not ref::reset. params_ref::reset resets the params object being pointed to by params_ref. A proper way to reset a params_ref as a reference is to assign an empty params_ref object to it. --- src/smt/smt_solver.cpp | 2 +- src/solver/solver.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 01b78b80e..8e5c2aaa2 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -122,7 +122,7 @@ namespace smt { smt_params m_smt_params_save; void push_params() override { - m_params_save.reset(); + m_params_save = params_ref(); m_params_save.copy(solver::get_params()); m_smt_params_save = m_smt_params; } diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index cc1d472ca..84b5eb588 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -206,14 +206,13 @@ void solver::collect_param_descrs(param_descrs & r) { r.insert("solver.enforce_model_conversion", CPK_BOOL, "(default: false) enforce model conversion when asserting formulas"); } -void solver::reset_params(params_ref const & p) { - m_params.reset(); - m_params.copy(p); +void solver::reset_params(params_ref const & p) { + m_params = p; m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); } -void solver::updt_params(params_ref const & p) { - m_params.copy(p); +void solver::updt_params(params_ref const & p) { + m_params.copy(p); m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); } From 1c062297558e4d0b31104d45f0ed318ad362fd16 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 11:17:18 -0700 Subject: [PATCH 148/364] User control over qi.quick_checker smt_params option --- src/smt/params/qi_params.cpp | 1 + src/smt/params/smt_params_helper.pyg | 1 + 2 files changed, 2 insertions(+) diff --git a/src/smt/params/qi_params.cpp b/src/smt/params/qi_params.cpp index deeb2b99c..91f354eda 100644 --- a/src/smt/params/qi_params.cpp +++ b/src/smt/params/qi_params.cpp @@ -35,6 +35,7 @@ void qi_params::updt_params(params_ref const & _p) { m_qi_lazy_threshold = p.qi_lazy_threshold(); m_qi_cost = p.qi_cost(); m_qi_max_eager_multipatterns = p.qi_max_multi_patterns(); + m_qi_quick_checker = static_cast(p.qi_quick_checker()); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index a85365de0..151a42d24 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -36,6 +36,7 @@ def_module_params(module_name='smt', ('qi.lazy_threshold', DOUBLE, 20.0, 'threshold for lazy quantifier instantiation'), ('qi.cost', STRING, '(+ weight generation)', 'expression specifying what is the cost of a given quantifier instantiation'), ('qi.max_multi_patterns', UINT, 0, 'specify the number of extra multi patterns'), + ('qi.quick_checker', UINT, 0, 'specify quick checker mode, 0 - no quick checker, 1 - using unsat instances, 2 - using both unsat and no-sat instances'), ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), ('bv.enable_int2bv', BOOL, True, 'enable support for int2bv and bv2int operators'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), From c8187886cf870490004d0e9228eff90b1275e8cd Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 11:18:32 -0700 Subject: [PATCH 149/364] spacer: use same params for all solver pools --- src/muz/spacer/spacer_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index b1ab0082a..c63bb3002 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2340,6 +2340,8 @@ void context::init_global_smt_params() { m_pm.updt_params(p); } + m_pm.updt_params2(p); + m_pm.updt_params3(p); void context::init_lemma_generalizers() { reset_lemma_generalizers(); From cfeee55d4ff1c01c884216680e22af12f883145b Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 11:19:40 -0700 Subject: [PATCH 150/364] spacer: set qi.quick_checker to MC_UNSAT if quantifiers are expected --- src/muz/spacer/spacer_context.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index c63bb3002..6eb1b0d40 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2292,7 +2292,7 @@ void context::init_global_smt_params() { params_ref p; if (!m_params.spacer_eq_prop ()) { // arith_propagation_mode - p.set_uint("arith.propagation_mode", 0); + p.set_uint("arith.propagation_mode", BP_NONE); // fparams.m_arith_bound_prop = BP_NONE; // NOT AVAILABLE // fparams.m_arith_auto_config_simplex = true; @@ -2317,10 +2317,10 @@ void context::init_global_smt_params() { // fparams.m_pi_use_database = true; // you don't need this // phase_selection // fparams.m_phase_selection = PS_CACHING_CONSERVATIVE2; - p.set_uint("phase_selection", 4); + p.set_uint("phase_selection", PS_CACHING_CONSERVATIVE2); // restart_strategy // fparams.m_restart_strategy = RS_GEOMETRIC; - p.set_uint("restart_strategy", 0); + p.set_uint("restart_strategy", RS_GEOMETRIC); // restart factor // fparams.m_restart_factor = 1.5; p.set_double("restart_factor", 1.5); @@ -2328,6 +2328,7 @@ void context::init_global_smt_params() { // fparams.m_eliminate_bounds = true; // NONE // fparams.m_qi_quick_checker = MC_UNSAT; // + p.set_uint("qi.quick_checker", MC_UNSAT); // qi_eager_threshold // fparams.m_qi_eager_threshold = 10; p.set_double("qi.eager_threshold", 10.0); @@ -2339,9 +2340,9 @@ void context::init_global_smt_params() { } m_pm.updt_params(p); -} m_pm.updt_params2(p); m_pm.updt_params3(p); +} void context::init_lemma_generalizers() { reset_lemma_generalizers(); From b17be763d33fba0edf60a28b12412fca3e4d7d01 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 11:40:25 -0700 Subject: [PATCH 151/364] User control over more arith options --- src/smt/params/smt_params_helper.pyg | 2 ++ src/smt/params/theory_arith_params.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 151a42d24..816764896 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -54,6 +54,8 @@ def_module_params(module_name='smt', ('arith.ignore_int', BOOL, False, 'treat integer variables as real'), ('arith.dump_lemmas', BOOL, False, 'dump arithmetic theory lemmas to files'), ('arith.greatest_error_pivot', BOOL, False, 'Pivoting strategy'), + ('arith.eager_eq_axioms', BOOL, True, 'eager equality axioms'), + ('arith.auto_config_simplex', BOOL, False, 'force simplex solver in auto_config'), ('pb.conflict_frequency', UINT, 1000, 'conflict frequency for Pseudo-Boolean theory'), ('pb.learn_complements', BOOL, True, 'learn complement literals for Pseudo-Boolean theory'), ('pb.enable_compilation', BOOL, True, 'enable compilation into sorting circuits for Pseudo-Boolean'), diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 250848db4..0918c9423 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -37,6 +37,9 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_arith_bound_prop = static_cast(p.arith_propagation_mode()); m_arith_dump_lemmas = p.arith_dump_lemmas(); m_arith_reflect = p.arith_reflect(); + m_arith_eager_eq_axioms = p.arith_eager_eq_axioms(); + m_arith_auto_config_simplex = p.arith_auto_config_simplex(); + arith_rewriter_params ap(_p); m_arith_eq2ineq = ap.eq2ineq(); } From c2304e263619ef5a41afd6567d6d8b4859a894aa Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 11:40:38 -0700 Subject: [PATCH 152/364] spacer: Cleanup of smt parameter configuration --- src/muz/spacer/spacer_context.cpp | 35 +++++++------------------------ 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 6eb1b0d40..acc7fb268 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2290,19 +2290,12 @@ void context::reset_lemma_generalizers() void context::init_global_smt_params() { m.toggle_proof_mode(PGM_ENABLED); params_ref p; - if (!m_params.spacer_eq_prop ()) { - // arith_propagation_mode + if (!m_params.spacer_eq_prop()) { p.set_uint("arith.propagation_mode", BP_NONE); - // fparams.m_arith_bound_prop = BP_NONE; - // NOT AVAILABLE - // fparams.m_arith_auto_config_simplex = true; - // arith_propagate_eqs - // fparams.m_arith_propagate_eqs = false; + p.set_bool("arith.auto_config_simplex", true); p.set_bool("arith.propagate_eqs", false); - // NOT AVAILABLE - // fparams.m_arith_eager_eq_axioms = false; + p.set_bool("arith.eager_eq_axioms", false); } - // fparams.m_random_seed = m_params.spacer_random_seed (); p.set_uint("random_seed", m_params.spacer_random_seed()); // fparams.m_dump_benchmarks = m_params.spacer_vs_dump_benchmarks(); @@ -2310,33 +2303,21 @@ void context::init_global_smt_params() { // fparams.m_dump_recheck = m_params.spacer_vs_recheck(); // mbqi - // fparams.m_mbqi = m_params.spacer_mbqi(); p.set_bool("mbqi", m_params.spacer_mbqi()); if (!m_params.spacer_ground_cti()) { - // fparams.m_pi_use_database = true; // you don't need this - // phase_selection - // fparams.m_phase_selection = PS_CACHING_CONSERVATIVE2; p.set_uint("phase_selection", PS_CACHING_CONSERVATIVE2); - // restart_strategy - // fparams.m_restart_strategy = RS_GEOMETRIC; p.set_uint("restart_strategy", RS_GEOMETRIC); - // restart factor - // fparams.m_restart_factor = 1.5; p.set_double("restart_factor", 1.5); - // probably not needed in our use case - // fparams.m_eliminate_bounds = true; - // NONE - // fparams.m_qi_quick_checker = MC_UNSAT; // p.set_uint("qi.quick_checker", MC_UNSAT); - // qi_eager_threshold - // fparams.m_qi_eager_threshold = 10; p.set_double("qi.eager_threshold", 10.0); - // qi_lazy_threshold - // fparams.m_qi_lazy_threshold = 20; p.set_double("qi.lazy_threshold", 20.0); - // useless + + // options that we used to set, but are not user visible and + // possibly not very useful // fparams.m_ng_lift_ite = LI_FULL; + // fparams.m_eliminate_bounds = true; + // fparams.m_pi_use_database = true; } m_pm.updt_params(p); From 180d38378a2013a288d476ecc2274ed2e05a6618 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 12:12:48 -0700 Subject: [PATCH 153/364] Add additional API to solver_pool --- src/solver/solver_pool.cpp | 7 +++++++ src/solver/solver_pool.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 3cf39a64c..88838639e 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -65,6 +65,9 @@ public: solver* translate(ast_manager& m, params_ref const& p) override { UNREACHABLE(); return nullptr; } void updt_params(params_ref const& p) override { solver::updt_params(p); m_base->updt_params(p); } + void push_params() override {m_base->push_params();} + void pop_params() override {m_base->pop_params();} + void collect_param_descrs(param_descrs & r) override { m_base->collect_param_descrs(r); } void collect_statistics(statistics & st) const override { m_base->collect_statistics(st); } unsigned get_num_assertions() const override { return m_base->get_num_assertions(); } @@ -280,6 +283,10 @@ ptr_vector solver_pool::get_base_solvers() const { return solvers; } +void solver_pool::updt_params(const params_ref &p) { + ptr_vector solvers = get_base_solvers(); + for (solver *s : solvers) s->updt_params(p); +} void solver_pool::collect_statistics(statistics &st) const { ptr_vector solvers = get_base_solvers(); for (solver* s : solvers) s->collect_statistics(st); diff --git a/src/solver/solver_pool.h b/src/solver/solver_pool.h index f279dfd4b..42c13fc58 100644 --- a/src/solver/solver_pool.h +++ b/src/solver/solver_pool.h @@ -66,6 +66,8 @@ public: solver* mk_solver(); void reset_solver(solver* s); + void updt_params(const params_ref &p); + }; From 098e70a9e23fdd816184032941c93ee0b586aef2 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 15:31:31 -0700 Subject: [PATCH 154/364] spacer: switched to using solver_pool --- src/muz/spacer/spacer_context.cpp | 6 ++--- src/muz/spacer/spacer_manager.cpp | 17 +++++++----- src/muz/spacer/spacer_manager.h | 39 ++++++++++++++------------- src/muz/spacer/spacer_prop_solver.cpp | 4 +-- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index acc7fb268..3c94e5521 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -642,7 +642,7 @@ pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): pm(pm), m(pm.get_manager()), ctx(ctx), m_head(head, m), m_sig(m), m_solver(pm, ctx.get_params(), head->get_name()), - m_reach_ctx (pm.mk_fresh3 ()), + m_reach_ctx (pm.mk_solver2()), m_pobs(*this), m_frames(*this), m_reach_facts(), m_rf_init_sz(0), @@ -2320,9 +2320,9 @@ void context::init_global_smt_params() { // fparams.m_pi_use_database = true; } - m_pm.updt_params(p); + m_pm.updt_params0(p); + m_pm.updt_params1(p); m_pm.updt_params2(p); - m_pm.updt_params3(p); } void context::init_lemma_generalizers() { diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index 04d3c09d4..a281cac81 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -30,6 +30,7 @@ Revision History: #include "model/model_smt2_pp.h" #include "tactic/model_converter.h" +#include "smt/smt_solver.h" namespace spacer { class collect_decls_proc { @@ -176,12 +177,16 @@ static std::vector state_suffixes() { } manager::manager(unsigned max_num_contexts, ast_manager& manager) : - m(manager), - m_mux(m, state_suffixes()), - m_contexts(m, max_num_contexts), - m_contexts2(m, max_num_contexts), - m_contexts3(m, max_num_contexts) -{} + m(manager), m_mux(m, state_suffixes()) { + + m_pool0_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); + m_pool1_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); + m_pool2_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); + + m_pool0 = alloc(solver_pool, m_pool0_base.get(), max_num_contexts); + m_pool1 = alloc(solver_pool, m_pool1_base.get(), max_num_contexts); + m_pool2 = alloc(solver_pool, m_pool2_base.get(), max_num_contexts); +} void manager::add_new_state(func_decl * s) diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index 471090c30..f7d43ccb0 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -34,9 +34,9 @@ Revision History: #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_sym_mux.h" #include "muz/spacer/spacer_farkas_learner.h" -#include "muz/spacer/spacer_smt_context_manager.h" #include "muz/base/dl_rule.h" - +#include "solver/solver.h" +#include "solver/solver_pool.h" namespace smt {class context;} namespace spacer { @@ -78,10 +78,13 @@ class manager { // manager of multiplexed names sym_mux m_mux; + ref m_pool0_base; + ref m_pool1_base; + ref m_pool2_base; // three solver pools for different queries - spacer::smt_context_manager m_contexts; - spacer::smt_context_manager m_contexts2; - spacer::smt_context_manager m_contexts3; + scoped_ptr m_pool0; + scoped_ptr m_pool1; + scoped_ptr m_pool2; unsigned n_index() const { return 0; } unsigned o_index(unsigned i) const { return i + 1; } @@ -154,27 +157,25 @@ public: // three different solvers with three different sets of parameters // different solvers are used for different types of queries in spacer - solver* mk_fresh() {return m_contexts.mk_fresh();} - void updt_params(const params_ref &p) {m_contexts.updt_params(p);} - - solver* mk_fresh2() {return m_contexts2.mk_fresh();} - void updt_params2(const params_ref &p) {m_contexts2.updt_params(p);} - - solver* mk_fresh3() {return m_contexts3.mk_fresh();} - void updt_params3(const params_ref &p) {m_contexts3.updt_params(p);} + solver* mk_solver0() {return m_pool0->mk_solver();} + void updt_params0(const params_ref &p) {m_pool0->updt_params(p);} + solver* mk_solver1() {return m_pool1->mk_solver();} + void updt_params1(const params_ref &p) {m_pool1->updt_params(p);} + solver* mk_solver2() {return m_pool2->mk_solver();} + void updt_params2(const params_ref &p) {m_pool2->updt_params(p);} void collect_statistics(statistics& st) const { - m_contexts.collect_statistics(st); - m_contexts2.collect_statistics(st); - m_contexts3.collect_statistics(st); + m_pool0->collect_statistics(st); + m_pool1->collect_statistics(st); + m_pool2->collect_statistics(st); } void reset_statistics() { - m_contexts.reset_statistics(); - m_contexts2.reset_statistics(); - m_contexts3.reset_statistics(); + m_pool0->reset_statistics(); + m_pool1->reset_statistics(); + m_pool2->reset_statistics(); } }; diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index a4ea79a25..6b29df074 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -55,8 +55,8 @@ prop_solver::prop_solver(spacer::manager& pm, m_use_push_bg(p.spacer_keep_proxy()) { - m_solvers[0] = pm.mk_fresh(); - m_solvers[1] = pm.mk_fresh2(); + m_solvers[0] = pm.mk_solver0(); + m_solvers[1] = pm.mk_solver1(); m_contexts[0] = alloc(spacer::iuc_solver, *(m_solvers[0]), p.spacer_iuc(), From 15d0fd4b4236a66c2511048cdeae64e820d705c6 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 15:35:46 -0700 Subject: [PATCH 155/364] spacer: removed virtual_solver This commit removes virtual_solver and smt_context_manager that have been migrated into solver_pool --- src/muz/spacer/CMakeLists.txt | 2 - src/muz/spacer/spacer_manager.h | 1 + src/muz/spacer/spacer_prop_solver.h | 1 - src/muz/spacer/spacer_smt_context_manager.cpp | 88 ----- src/muz/spacer/spacer_smt_context_manager.h | 69 ---- src/muz/spacer/spacer_virtual_solver.cpp | 333 ------------------ src/muz/spacer/spacer_virtual_solver.h | 150 -------- 7 files changed, 1 insertion(+), 643 deletions(-) delete mode 100644 src/muz/spacer/spacer_smt_context_manager.cpp delete mode 100644 src/muz/spacer/spacer_smt_context_manager.h delete mode 100644 src/muz/spacer/spacer_virtual_solver.cpp delete mode 100644 src/muz/spacer/spacer_virtual_solver.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index a7bc74b88..68464a40d 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -8,11 +8,9 @@ z3_add_component(spacer spacer_generalizers.cpp spacer_manager.cpp spacer_prop_solver.cpp - spacer_smt_context_manager.cpp spacer_sym_mux.cpp spacer_util.cpp spacer_iuc_solver.cpp - spacer_virtual_solver.cpp spacer_legacy_mbp.cpp spacer_proof_utils.cpp spacer_unsat_core_learner.cpp diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index f7d43ccb0..4c3d861cb 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -13,6 +13,7 @@ Abstract: Author: Krystof Hoder (t-khoder) 2011-8-25. + Arie Gurfinkel Revision History: diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 27b2fd51d..e87732e94 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -30,7 +30,6 @@ Revision History: #include "util/util.h" #include "util/vector.h" #include "muz/spacer/spacer_manager.h" -#include "muz/spacer/spacer_smt_context_manager.h" #include "muz/spacer/spacer_iuc_solver.h" struct fixedpoint_params; diff --git a/src/muz/spacer/spacer_smt_context_manager.cpp b/src/muz/spacer/spacer_smt_context_manager.cpp deleted file mode 100644 index 78f01b6e5..000000000 --- a/src/muz/spacer/spacer_smt_context_manager.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - spacer_smt_context_manager.cpp - -Abstract: - - Manager of smt contexts - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-26. - Arie Gurfinkel -Revision History: - ---*/ - - -#include "ast/ast_pp.h" -#include "ast/ast_pp_util.h" -#include "ast/ast_smt_pp.h" - -#include "smt/smt_context.h" -#include "smt/params/smt_params.h" -#include "smt/smt_solver.h" - -#include "muz/spacer/spacer_util.h" -#include "muz/spacer/spacer_smt_context_manager.h" -namespace spacer { - -smt_context_manager::smt_context_manager(ast_manager &m, - unsigned max_num_contexts, - const params_ref &p) : - m(m), - m_params(p), - m_max_num_contexts(max_num_contexts), - m_num_contexts(0) { m_stats.reset();} - - -smt_context_manager::~smt_context_manager() -{ - std::for_each(m_solvers.begin(), m_solvers.end(), - delete_proc()); - m_solvers.reset(); - m_base_solvers.reset(); -} - -void smt_context_manager::updt_params(params_ref const &p) { - m_params.append(p); - for (auto *s : m_base_solvers) {s->updt_params(m_params);} -} - -virtual_solver* smt_context_manager::mk_fresh() -{ - ++m_num_contexts; - virtual_solver_factory *solver_factory = nullptr; - - if (m_max_num_contexts == 0 || m_solvers.size() < m_max_num_contexts) { - m_base_solvers.push_back(mk_smt_solver(m, m_params, symbol::null)); - m_solvers.push_back(alloc(spacer::virtual_solver_factory, - *m_base_solvers.back())); - solver_factory = m_solvers.back(); - } - else { - solver_factory = m_solvers[(m_num_contexts - 1) % m_max_num_contexts]; - } - - return solver_factory->mk_solver(); -} - -void smt_context_manager::collect_statistics(statistics& st) const -{ - for (unsigned i = 0; i < m_solvers.size(); ++i) { - m_solvers[i]->collect_statistics(st); - } -} - -void smt_context_manager::reset_statistics() -{ - for (unsigned i = 0; i < m_solvers.size(); ++i) { - m_solvers[i]->reset_statistics(); - } -} - - -}; diff --git a/src/muz/spacer/spacer_smt_context_manager.h b/src/muz/spacer/spacer_smt_context_manager.h deleted file mode 100644 index a62aba6cd..000000000 --- a/src/muz/spacer/spacer_smt_context_manager.h +++ /dev/null @@ -1,69 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - spacer_smt_context_manager.h - -Abstract: - - Manager of smt contexts - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-26. - Arie Gurfinkel -Revision History: - ---*/ - -#ifndef _SPACER_SMT_CONTEXT_MANAGER_H_ -#define _SPACER_SMT_CONTEXT_MANAGER_H_ - -#include "util/stopwatch.h" - -#include "smt/smt_kernel.h" -#include "muz/base/dl_util.h" -#include "muz/spacer/spacer_virtual_solver.h" - -namespace spacer { - -class smt_context_manager { - - struct stats { - unsigned m_num_smt_checks; - unsigned m_num_sat_smt_checks; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - ast_manager& m; - params_ref m_params; - unsigned m_max_num_contexts; - sref_vector m_base_solvers; - ptr_vector m_solvers; - unsigned m_num_contexts; - - - stats m_stats; - stopwatch m_check_watch; - stopwatch m_check_sat_watch; - -public: - smt_context_manager(ast_manager& m, unsigned max_num_contexts = 1, - const params_ref &p = params_ref::get_empty()); - - ~smt_context_manager(); - virtual_solver* mk_fresh(); - - void collect_statistics(statistics& st) const; - void reset_statistics(); - - void updt_params(params_ref const &p); - - -}; - -}; - -#endif diff --git a/src/muz/spacer/spacer_virtual_solver.cpp b/src/muz/spacer/spacer_virtual_solver.cpp deleted file mode 100644 index a1a1a7eec..000000000 --- a/src/muz/spacer/spacer_virtual_solver.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/** -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_virtual_solver.cpp - -Abstract: - - multi-solver view of a single solver - -Author: - - Arie Gurfinkel - -Notes: - ---*/ - -#include "muz/spacer/spacer_virtual_solver.h" -#include "ast/ast_util.h" -#include "ast/ast_pp_util.h" -#include "muz/spacer/spacer_util.h" -#include "ast/rewriter/bool_rewriter.h" - -#include "ast/proofs/proof_checker.h" -#include "ast/proofs/proof_utils.h" - -#include "ast/scoped_proof.h" - -#include "smt/smt_kernel.h" - -namespace spacer { -virtual_solver::virtual_solver(virtual_solver_factory &factory, app* pred) : - solver_na2as(factory.get_manager()), - m_factory(factory), - m(factory.get_manager()), - m_context(factory.get_base_solver()), - m_pred(pred, m), - m_virtual(!m.is_true(pred)), - m_assertions(m), - m_head(0), - m_flat(m), - m_pushed(false), - m_in_delay_scope(false), - m_dump_benchmarks(false /*factory.fparams().m_dump_benchmarks*/), - m_dump_counter(0), - m_proof(m) -{ - // -- insert m_pred->true background assumption this will not - // -- change m_context, but will add m_pred to - // -- the private field solver_na2as::m_assumptions - if (m_virtual) - { solver_na2as::assert_expr_core2(m.mk_true(), m_pred); } -} - -virtual_solver::~virtual_solver() -{ - SASSERT(!m_pushed || get_scope_level() > 0); - if (m_pushed) { pop(get_scope_level()); } - - if (m_virtual) { - m_pred = m.mk_not(m_pred); - m_context.assert_expr(m_pred); - } -} - -namespace { - - -// TBD: move to ast/proofs/elim_aux_assertions - - -} - -proof *virtual_solver::get_proof() -{ - scoped_watch _t_(m_factory.m_proof_watch); - - if (!m_proof.get()) { - elim_aux_assertions pc(m_pred); - m_proof = m_context.get_proof(); - pc(m, m_proof.get(), m_proof); - } - return m_proof.get(); -} - -bool virtual_solver::is_aux_predicate(expr *p) -{return is_app(p) && to_app(p) == m_pred.get();} - -lbool virtual_solver::check_sat_core(unsigned num_assumptions, - expr *const * assumptions) -{ - SASSERT(!m_pushed || get_scope_level() > 0); - m_proof.reset(); - scoped_watch _t_(m_factory.m_check_watch); - m_factory.m_stats.m_num_smt_checks++; - - stopwatch sw; - sw.start(); - internalize_assertions(); - if (false) { - std::stringstream file_name; - file_name << "virt_solver"; - if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); } - file_name << "_" << (m_dump_counter++) << ".smt2"; - - verbose_stream() << "Dumping SMT2 benchmark: " << file_name.str() << "\n"; - - std::ofstream out(file_name.str().c_str()); - - to_smt2_benchmark(out, m_context, num_assumptions, assumptions, - "virt_solver"); - - out << "(exit)\n"; - out.close(); - } - lbool res = m_context.check_sat(num_assumptions, assumptions); - sw.stop(); - if (res == l_true) { - m_factory.m_check_sat_watch.add(sw); - m_factory.m_stats.m_num_sat_smt_checks++; - } else if (res == l_undef) { - m_factory.m_check_undef_watch.add(sw); - m_factory.m_stats.m_num_undef_smt_checks++; - } - set_status(res); - - if (false /*m_dump_benchmarks && - sw.get_seconds() >= m_factory.fparams().m_dump_min_time*/) { - std::stringstream file_name; - file_name << "virt_solver"; - if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); } - file_name << "_" << (m_dump_counter++) << ".smt2"; - - std::ofstream out(file_name.str().c_str()); - - - out << "(set-info :status "; - if (res == l_true) { out << "sat"; } - else if (res == l_false) { out << "unsat"; } - else { out << "unknown"; } - out << ")\n"; - - to_smt2_benchmark(out, m_context, num_assumptions, assumptions, - "virt_solver"); - - out << "(exit)\n"; - ::statistics st; - m_context.collect_statistics(st); - st.update("time", sw.get_seconds()); - st.display_smt2(out); - - - if (false /* m_factory.fparams().m_dump_recheck */) { - scoped_no_proof _no_proof_(m); - smt_params p; - stopwatch sw2; - smt::kernel kernel(m, p); - for (unsigned i = 0, sz = m_context.get_num_assertions(); i < sz; ++i) - { kernel.assert_expr(m_context.get_assertion(i)); } - sw2.start(); - kernel.check(num_assumptions, assumptions); - sw2.stop(); - verbose_stream() << file_name.str() << " :orig " - << sw.get_seconds() << " :new " << sw2.get_seconds(); - - out << ";; :orig " << sw.get_seconds() - << " :new " << sw2.get_seconds() << "\n" - << ";; :new is time to solve with fresh smt::kernel\n"; - } - - out.close(); - } - - - return res; -} - -void virtual_solver::push_core() -{ - SASSERT(!m_pushed || get_scope_level() > 0); - if (m_in_delay_scope) { - // second push - internalize_assertions(); - m_context.push(); - m_pushed = true; - m_in_delay_scope = false; - } - - if (!m_pushed) { m_in_delay_scope = true; } - else { - SASSERT(m_pushed); - SASSERT(!m_in_delay_scope); - m_context.push(); - } -} -void virtual_solver::pop_core(unsigned n) { - SASSERT(!m_pushed || get_scope_level() > 0); - if (m_pushed) { - SASSERT(!m_in_delay_scope); - m_context.pop(n); - m_pushed = get_scope_level() - n > 0; - } - else { - m_in_delay_scope = get_scope_level() - n > 0; - } -} - -void virtual_solver::get_unsat_core(ptr_vector &r) -{ - ptr_vector core; - m_context.get_unsat_core(core); - - for (unsigned i = 0, sz = core.size(); i < sz; ++i) { - if (is_aux_predicate(core.get(i))) { continue; } - r.push_back(core.get(i)); - } -} - -void virtual_solver::assert_expr_core(expr *e) -{ - SASSERT(!m_pushed || get_scope_level() > 0); - if (m.is_true(e)) { return; } - if (m_in_delay_scope) { - internalize_assertions(); - m_context.push(); - m_pushed = true; - m_in_delay_scope = false; - } - - if (m_pushed) - { m_context.assert_expr(e); } - else { - m_flat.push_back(e); - flatten_and(m_flat); - m_assertions.append(m_flat); - m_flat.reset(); - } -} -void virtual_solver::internalize_assertions() -{ - SASSERT(!m_pushed || m_head == m_assertions.size()); - for (unsigned sz = m_assertions.size(); m_head < sz; ++m_head) { - expr_ref f(m); - f = m.mk_implies(m_pred, (m_assertions.get(m_head))); - m_context.assert_expr(f); - } -} - -void virtual_solver::get_labels(svector &r) -{m_context.get_labels(r);} - -solver* virtual_solver::translate(ast_manager& m, params_ref const& p) -{ - UNREACHABLE(); - return nullptr; -} -void virtual_solver::updt_params(params_ref const &p) {m_context.updt_params(p);} -void virtual_solver::reset_params(params_ref const &p) {m_context.reset_params(p);} -const params_ref &virtual_solver::get_params() const {return m_context.get_params();} -void virtual_solver::push_params(){m_context.push_params();} -void virtual_solver::pop_params(){m_context.pop_params();} -void virtual_solver::collect_param_descrs(param_descrs &r) { m_context.collect_param_descrs(r); } -void virtual_solver::set_produce_models(bool f) { m_context.set_produce_models(f); } - -void virtual_solver::to_smt2_benchmark(std::ostream &out, - solver &context, - unsigned num_assumptions, - expr * const * assumptions, - char const * name, - symbol const &logic, - char const * status, - char const * attributes) -{ - ast_pp_util pp(m); - expr_ref_vector asserts(m); - - context.get_assertions(asserts); - pp.collect(asserts); - pp.collect(num_assumptions, assumptions); - pp.display_decls(out); - pp.display_asserts(out, asserts); - out << "(check-sat "; - for (unsigned i = 0; i < num_assumptions; ++i) - { out << mk_pp(assumptions[i], m) << " "; } - out << ")\n"; -} - - -virtual_solver_factory::virtual_solver_factory(solver &base) : - m(base.get_manager()), m_context(base) {m_stats.reset();} - -virtual_solver* virtual_solver_factory::mk_solver() -{ - std::stringstream name; - name << "vsolver#" << m_solvers.size(); - app_ref pred(m); - pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); - SASSERT(m_context.get_scope_level() == 0); - m_solvers.push_back(alloc(virtual_solver, *this, pred)); - return m_solvers.back(); -} - -void virtual_solver_factory::collect_statistics(statistics &st) const -{ - m_context.collect_statistics(st); - st.update("time.virtual_solver.smt.total", m_check_watch.get_seconds()); - st.update("time.virtual_solver.smt.total.sat", m_check_sat_watch.get_seconds()); - st.update("time.virtual_solver.smt.total.undef", m_check_undef_watch.get_seconds()); - st.update("time.virtual_solver.proof", m_proof_watch.get_seconds()); - st.update("virtual_solver.checks", m_stats.m_num_smt_checks); - st.update("virtual_solver.checks.sat", m_stats.m_num_sat_smt_checks); - st.update("virtual_solver.checks.undef", m_stats.m_num_undef_smt_checks); -} -void virtual_solver_factory::reset_statistics() -{ - m_stats.reset(); - m_check_sat_watch.reset(); - m_check_undef_watch.reset(); - m_check_watch.reset(); - m_proof_watch.reset(); -} - -virtual_solver_factory::~virtual_solver_factory() -{ - for (unsigned i = 0, e = m_solvers.size(); i < e; ++i) - { dealloc(m_solvers[i]); } -} - - - -} diff --git a/src/muz/spacer/spacer_virtual_solver.h b/src/muz/spacer/spacer_virtual_solver.h deleted file mode 100644 index e5db5e396..000000000 --- a/src/muz/spacer/spacer_virtual_solver.h +++ /dev/null @@ -1,150 +0,0 @@ -/** -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_virtual_solver.h - -Abstract: - - multi-solver view of a single solver - -Author: - - Arie Gurfinkel - -Notes: - ---*/ -#ifndef SPACER_VIRTUAL_SOLVER_H_ -#define SPACER_VIRTUAL_SOLVER_H_ -#include"ast/ast.h" -#include"util/params.h" -#include"solver/solver_na2as.h" -#include"smt/params/smt_params.h" -#include"util/stopwatch.h" -namespace spacer { -class virtual_solver_factory; - -class virtual_solver : public solver_na2as { - friend class virtual_solver_factory; - -private: - virtual_solver_factory &m_factory; - ast_manager &m; - solver &m_context; - app_ref m_pred; - - bool m_virtual; - expr_ref_vector m_assertions; - unsigned m_head; - // temporary to flatten conjunction - expr_ref_vector m_flat; - - bool m_pushed; - bool m_in_delay_scope; - bool m_dump_benchmarks; - unsigned m_dump_counter; - - proof_ref m_proof; - - virtual_solver(virtual_solver_factory &factory, app* pred); - - bool is_aux_predicate(expr *p); - void internalize_assertions(); - void to_smt2_benchmark(std::ostream &out, - solver &context, - unsigned num_assumptions, - expr * const * assumptions, - char const * name = "benchmarks", - symbol const &logic = symbol::null, - char const * status = "unknown", - char const * attributes = ""); - -public: - ~virtual_solver() override; - unsigned get_num_assumptions() const override - { - unsigned sz = solver_na2as::get_num_assumptions(); - return m_virtual ? sz - 1 : sz; - } - expr* get_assumption(unsigned idx) const override - { - if(m_virtual) { idx++; } - return solver_na2as::get_assumption(idx); - } - - - void get_unsat_core(ptr_vector &r) override; - void assert_expr_core(expr *e) override; - void collect_statistics(statistics &st) const override {m_context.collect_statistics(st);} - void get_model_core(model_ref &m) override {m_context.get_model(m);} - proof* get_proof() override; - std::string reason_unknown() const override - {return m_context.reason_unknown();} - void set_reason_unknown(char const *msg) override - {m_context.set_reason_unknown(msg);} - ast_manager& get_manager() const override {return m;} - void get_labels(svector &r) override; - void set_produce_models(bool f) override; - smt_params &fparams(); - expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } - void set_progress_callback(progress_callback *callback) override {UNREACHABLE();} - - solver *translate(ast_manager &m, params_ref const &p) override; - - void updt_params(params_ref const &p) override; - void reset_params(params_ref const& p) override; - params_ref const& get_params() const override; - void collect_param_descrs(param_descrs &r) override; - void push_params() override; - void pop_params() override; - - -protected: - lbool check_sat_core(unsigned num_assumptions, expr *const * assumptions) override; - void push_core() override; - void pop_core(unsigned n) override; -}; - -/// multi-solver abstraction on top of a single solver -class virtual_solver_factory { - friend class virtual_solver; -private: - ast_manager &m; - solver &m_context; - /// solvers managed by this factory - ptr_vector m_solvers; - - struct stats { - unsigned m_num_smt_checks; - unsigned m_num_sat_smt_checks; - unsigned m_num_undef_smt_checks; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - stats m_stats; - stopwatch m_check_watch; - stopwatch m_check_sat_watch; - stopwatch m_check_undef_watch; - stopwatch m_proof_watch; - - - solver &get_base_solver() {return m_context;} - ast_manager &get_manager() {return m;} - -public: - virtual_solver_factory(solver &base); - virtual ~virtual_solver_factory(); - virtual_solver* mk_solver(); - void collect_statistics(statistics &st) const; - void reset_statistics(); - void updt_params(params_ref const &p) {m_context.updt_params(p);} - void collect_param_descrs(param_descrs &r) {m_context.collect_param_descrs(r);} -}; - -} - - -#endif From 14b9dd2cd71c9fb7f2b036fa4aa37e31a3666006 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 16:25:55 -0700 Subject: [PATCH 156/364] spacer: let pool_solver own the solver --- src/muz/spacer/spacer_manager.cpp | 15 +++++++++------ src/muz/spacer/spacer_manager.h | 3 --- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index a281cac81..d55f74b90 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -179,13 +179,16 @@ static std::vector state_suffixes() { manager::manager(unsigned max_num_contexts, ast_manager& manager) : m(manager), m_mux(m, state_suffixes()) { - m_pool0_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); - m_pool1_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); - m_pool2_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); + ref pool0_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + ref pool1_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + ref pool2_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); - m_pool0 = alloc(solver_pool, m_pool0_base.get(), max_num_contexts); - m_pool1 = alloc(solver_pool, m_pool1_base.get(), max_num_contexts); - m_pool2 = alloc(solver_pool, m_pool2_base.get(), max_num_contexts); + m_pool0 = alloc(solver_pool, pool0_base.get(), max_num_contexts); + m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); + m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); } diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index 4c3d861cb..b8783369d 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -79,9 +79,6 @@ class manager { // manager of multiplexed names sym_mux m_mux; - ref m_pool0_base; - ref m_pool1_base; - ref m_pool2_base; // three solver pools for different queries scoped_ptr m_pool0; scoped_ptr m_pool1; From 80c39eb037c90ab905006aa3ee08cb967c098dfa Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 24 May 2018 21:53:01 -0700 Subject: [PATCH 157/364] Fix solver_pool::updt_params --- src/solver/solver_pool.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 88838639e..0aee5c3dc 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -284,6 +284,7 @@ ptr_vector solver_pool::get_base_solvers() const { } void solver_pool::updt_params(const params_ref &p) { + m_base_solver->updt_params(p); ptr_vector solvers = get_base_solvers(); for (solver *s : solvers) s->updt_params(p); } From 20300bbf94b03749a68615d01f56d941e2a90583 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 May 2018 13:06:47 -0700 Subject: [PATCH 158/364] updates to mbqi Signed-off-by: Nikolaj Bjorner Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_util.cpp | 62 +++++------ src/qe/qe_arrays.cpp | 181 ++++++++++++++++++++++++++++----- src/qe/qe_mbp.cpp | 56 +++++++++- 3 files changed, 237 insertions(+), 62 deletions(-) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index e2a1a2c3d..b45e0de4a 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -90,33 +90,33 @@ namespace spacer { if (!m_model) { return; } m_mev = alloc(model_evaluator, *m_model); } - + bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { m_mev->set_model_completion (model_completion); try { m_mev->operator() (e, result); return true; - } + } catch (model_evaluator_exception &ex) { (void)ex; TRACE("spacer_model_evaluator", tout << ex.msg () << "\n";); return false; } } - + bool model_evaluator_util::eval(const expr_ref_vector &v, expr_ref& res, bool model_completion) { expr_ref e(m); e = mk_and (v); return eval(e, res, model_completion); } - - + + bool model_evaluator_util::is_true(const expr_ref_vector &v) { expr_ref res(m); return eval (v, res, false) && m.is_true (res); } - + bool model_evaluator_util::is_false(expr *x) { expr_ref res(m); return eval(x, res, false) && m.is_false (res); @@ -126,7 +126,7 @@ namespace spacer { expr_ref res(m); return eval(x, res, false) && m.is_true (res); } - + void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); @@ -172,16 +172,16 @@ namespace spacer { SASSERT(orig_size <= 1 + conjs.size()); if (i + 1 == orig_size) { // no-op. - } + } else if (orig_size <= conjs.size()) { // no-op - } + } else { SASSERT(orig_size == 1 + conjs.size()); --orig_size; --i; } - } + } else { conjs[i] = tmp; } @@ -199,7 +199,7 @@ namespace spacer { ast_manager& m; public: ite_hoister(ast_manager& m): m(m) {} - + br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; @@ -234,7 +234,7 @@ namespace spacer { } ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} }; - + class ite_hoister_star : public rewriter_tpl { ite_hoister_cfg m_cfg; public: @@ -242,7 +242,7 @@ namespace spacer { rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; - + void hoist_non_bool_if(expr_ref& fml) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); @@ -274,7 +274,7 @@ namespace spacer { bool is_arith_expr(expr *e) const { return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); } - + bool is_offset(expr* e) const { if (a.is_numeral(e)) { return true; @@ -358,7 +358,7 @@ namespace spacer { !a.is_mul(lhs) && !a.is_mul(rhs); } - + bool test_term(expr* e) const { if (m.is_bool(e)) { return true; @@ -403,9 +403,9 @@ namespace spacer { public: test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - + void test_for_utvpi() { m_test_for_utvpi = true; } - + void operator()(expr* e) { if (!m_is_dl) { return; @@ -422,7 +422,7 @@ namespace spacer { m_is_dl = test_term(a->get_arg(i)); } } - + if (!m_is_dl) { char const* msg = "non-diff: "; if (m_test_for_utvpi) { @@ -431,10 +431,10 @@ namespace spacer { IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } - + bool is_dl() const { return m_is_dl; } }; - + bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); expr_fast_mark1 mark; @@ -443,7 +443,7 @@ namespace spacer { } return test.is_dl(); } - + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); test.test_for_utvpi(); @@ -455,7 +455,7 @@ namespace spacer { } - void subst_vars(ast_manager& m, + void subst_vars(ast_manager& m, app_ref_vector const& vars, model* M, expr_ref& fml) { expr_safe_replace sub (m); @@ -506,7 +506,7 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) flatten_and (fml, flat); fml = mk_and(flat); } - + // uncomment for benchmarks //to_mbp_benchmark(verbose_stream(), fml, vars); @@ -523,7 +523,7 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) qe_lite qe(m, p, false); qe (vars, fml); rw (fml); - + TRACE ("spacer_mbp", tout << "After qe_lite:\n"; tout << mk_pp (fml, m) << "\n"; @@ -531,7 +531,7 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) SASSERT (!m.is_false (fml)); - + // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { if (m.is_bool (v)) { @@ -545,7 +545,7 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) arith_vars.push_back (v); } } - + // substitute Booleans if (!bool_sub.empty()) { bool_sub (fml); @@ -555,13 +555,13 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); bool_sub.reset (); } - + TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); - + vars.reset (); - + // project arrays { scoped_no_proof _sp (m); @@ -572,14 +572,14 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) srw (fml); SASSERT (!m.is_false (fml)); } - + TRACE ("spacer_mbp", tout << "extended model:\n"; model_pp (tout, *M); tout << "Auxiliary variables of index and value sorts:\n"; tout << vars; ); - + if (vars.empty()) { break; } } diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 4a70d1fcb..df5c7f44e 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -157,8 +157,8 @@ namespace qe { static bool is_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; - return true; - } + return true; + } static bool is_eq(vector const& xs, vector const& ys) { for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; @@ -984,9 +984,9 @@ namespace qe { TRACE("qe", tout << "non-arithmetic index sort for Ackerman" << mk_pp(srt, m) << "\n";); // TBD: generalize to also bit-vectors. is_numeric = false; - } + } } - + unsigned start = m_idxs.size (); // append at the end for (app * a : sel_terms) { expr_ref_vector idxs(m, arity, a->get_args() + 1); @@ -1018,13 +1018,13 @@ namespace qe { } if (is_numeric) { - // sort reprs by their value and add a chain of strict inequalities - compare_idx cmp(*this); - std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); + // sort reprs by their value and add a chain of strict inequalities + compare_idx cmp(*this); + std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); for (unsigned i = start; i + 1 < m_idxs.size(); ++i) { - m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); - } + m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); } + } else if (arity == 1) { // create distinct constraint. expr_ref_vector xs(m); @@ -1163,12 +1163,12 @@ namespace qe { return BR_DONE; } return BR_FAILED; - } + } lbool compare(expr* x, expr* y) { NOT_IMPLEMENTED_YET(); return l_undef; - } + } }; struct indices { @@ -1181,10 +1181,10 @@ namespace qe { for (unsigned i = 0; i < n; ++i) { VERIFY(model.eval(vars[i], val)); m_values.push_back(val); - } + } } }; - + ast_manager& m; array_util a; scoped_ptr m_var; @@ -1194,6 +1194,67 @@ namespace qe { bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; + } + + bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + + TRACE("qe", tout << mk_pp(var, m) << "\n" << lits;); + m_var = alloc(contains_app, m, var); + + // reduce select-store redeces based on model. + // rw_cfg rw(m); + // rw(lits); + + // try first to solve for var. + if (solve_eq(model, vars, lits)) { + return true; + } + + app_ref_vector selects(m); + + // check that only non-select occurrences are in disequalities. + if (!check_diseqs(lits, selects)) { + TRACE("qe", tout << "Could not project " << mk_pp(var, m) << " for:\n" << lits << "\n";); + return false; + } + + // remove disequalities. + elim_diseqs(lits); + + // Ackerman reduction on remaining select occurrences + // either replace occurrences by model value or other node + // that is congruent to model value. + + ackermanize_select(model, selects, vars, lits); + + TRACE("qe", tout << selects << "\n" << lits << "\n";); + return true; + } + + void ackermanize_select(model& model, app_ref_vector const& selects, app_ref_vector& vars, expr_ref_vector& lits) { + expr_ref_vector vals(m), reps(m); + expr_ref val(m); + expr_safe_replace sub(m); + + if (selects.empty()) { + return; + } + + app_ref sel(m); + for (unsigned i = 0; i < selects.size(); ++i) { + sel = m.mk_fresh_const("sel", m.get_sort(selects[i])); + VERIFY (model.eval(selects[i], val)); + model.register_decl(sel->get_decl(), val); + vals.push_back(to_app(val)); + reps.push_back(val); // TODO: direct pass could handle nested selects. + vars.push_back(sel); + sub.insert(selects[i], val); + } + + sub(lits); + remove_true(lits); + project_plugin::partition_args(model, selects, lits); + project_plugin::partition_values(model, reps, lits); } void remove_true(expr_ref_vector& lits) { @@ -1216,6 +1277,80 @@ namespace qe { } } + // check that x occurs only under selects or in disequalities. + bool check_diseqs(expr_ref_vector const& lits, app_ref_vector& selects) { + expr_mark mark; + ptr_vector todo; + app* e; + for (unsigned i = 0; i < lits.size(); ++i) { + e = to_app(lits[i]); + if (is_diseq_x(e)) { + continue; + } + if (contains_x(e)) { + todo.push_back(e); + } + } + while (!todo.empty()) { + e = todo.back(); + todo.pop_back(); + if (mark.is_marked(e)) { + continue; + } + mark.mark(e); + if (m_var->x() == e) { + return false; + } + unsigned start = 0; + if (a.is_select(e)) { + if (e->get_arg(0) == m_var->x()) { + start = 1; + selects.push_back(e); + } + } + for (unsigned i = start; i < e->get_num_args(); ++i) { + todo.push_back(to_app(e->get_arg(i))); + } + } + return true; + } + + void elim_diseqs(expr_ref_vector& lits) { + for (unsigned i = 0; i < lits.size(); ++i) { + if (is_diseq_x(lits[i].get())) { + project_plugin::erase(lits, i); + } + } + } + + bool is_update_x(app* e) { + do { + if (m_var->x() == e) { + return true; + } + if (a.is_store(e) && contains_x(e->get_arg(0))) { + for (unsigned i = 1; i < e->get_num_args(); ++i) { + if (contains_x(e->get_arg(i))) { + return false; + } + } + e = to_app(e->get_arg(0)); + continue; + } + } + while (false); + return false; + } + + bool is_diseq_x(expr* e) { + expr *f, * s, *t; + if (m.is_not(e, f) && m.is_eq(f, s, t)) { + if (contains_x(s) && !contains_x(t) && is_update_x(to_app(s))) return true; + if (contains_x(t) && !contains_x(s) && is_update_x(to_app(t))) return true; + } + return false; + } + bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { // find an equality to solve for. expr* s, *t; @@ -1367,13 +1502,7 @@ namespace qe { } bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - ast_manager& m = vars.get_manager(); - app_ref_vector vvars(m, 1, &var); - expr_ref fml = mk_and(lits); - (*this)(model, vvars, fml, vars, false); - lits.reset(); - flatten_and(fml, lits); - return true; + return (*m_imp)(model, var, vars, lits); } bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { @@ -1390,11 +1519,11 @@ namespace qe { array_project_eqs_util pe (m); pe (mdl, arr_vars, fml, aux_vars); TRACE ("qe", - tout << "Projected array eqs: " << fml << "\n"; - tout << "Remaining array vars: " << arr_vars << "\n"; - tout << "Aux vars: " << aux_vars << "\n"; + tout << "Projected array eqs:\n" << fml << "\n"; + tout << "Remaining array vars:\n" << arr_vars; + tout << "Aux vars:\n" << aux_vars; ); - + // 2. reduce selects array_select_reducer rs (m); rs (mdl, arr_vars, fml, reduce_all_selects); @@ -1406,8 +1535,8 @@ namespace qe { ps (mdl, arr_vars, fml, aux_vars); TRACE ("qe", - tout << "Projected array selects: " << fml << "\n"; - tout << "All aux vars: " << aux_vars << "\n"; + tout << "Projected array selects:\n" << fml << "\n"; + tout << "All aux vars:\n" << aux_vars; ); } diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index b1c92cb8c..b07e9904c 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -77,6 +77,40 @@ expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { return expr_ref(nullptr, m); } +void project_plugin::partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits) { + ast_manager& m = vals.get_manager(); + expr_ref val(m); + expr_ref_vector trail(m), reps(m); + obj_map roots; + for (unsigned i = 0; i < vals.size(); ++i) { + expr* v = vals[i], *root; + VERIFY (model.eval(v, val)); + if (roots.find(val, root)) { + lits.push_back(m.mk_eq(v, root)); + } + else { + roots.insert(val, v); + trail.push_back(val); + reps.push_back(v); + } + } + if (reps.size() > 1) { + lits.push_back(mk_distinct(reps)); + } +} + +void project_plugin::partition_args(model& model, app_ref_vector const& selects, expr_ref_vector& lits) { + ast_manager& m = selects.get_manager(); + if (selects.empty()) return; + unsigned num_args = selects[0]->get_decl()->get_arity(); + for (unsigned j = 1; j < num_args; ++j) { + expr_ref_vector args(m); + for (unsigned i = 0; i < selects.size(); ++i) { + args.push_back(selects[i]->get_arg(j)); + } + project_plugin::partition_values(model, args, lits); + } +} void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { lits[i] = lits.back(); @@ -100,6 +134,7 @@ class mbp::impl { // parameters bool m_reduce_all_selects; + bool m_native_mbp; bool m_dont_sub; void add_plugin(project_plugin* p) { @@ -287,7 +322,8 @@ class mbp::impl { void operator() (app *n) { expr *e1, *e2; if (m_array.is_select (n)) { - for (expr * arg : *n) { + for (unsigned i = 1; i < n->get_num_args(); ++i) { + expr * arg = n->get_arg(i); if (m.get_sort(arg) == m.get_sort(m_var) && arg != m_var) m_res.push_back (arg); } @@ -340,7 +376,8 @@ class mbp::impl { // -- evaluate to initialize eval cache (void) eval (fml); unsigned j = 0; - for (app * v : vars) { + for (unsigned i = 0; i < vars.size (); ++i) { + app* v = vars.get(i); if (!project_var (eval, v, fml)) { vars[j++] = v; } @@ -350,6 +387,7 @@ class mbp::impl { public: + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { arith_project_plugin arith(m); return arith.maximize(fmls, mdl, t, ge, gt); @@ -490,6 +528,7 @@ public: void updt_params(params_ref const& p) { m_params.append(p); m_reduce_all_selects = m_params.get_bool("reduce_all_selects", false); + m_native_mbp = m_params.get_bool("native_mbp", false); m_dont_sub = m_params.get_bool("dont_sub", false); } @@ -508,7 +547,6 @@ public: bool validate_model(model& model, expr_ref_vector const& fmls) { expr_ref val(m); - model_evaluator eval(model); for (expr * f : fmls) { VERIFY(model.eval(f, val) && m.is_true(val)); } @@ -637,19 +675,26 @@ public: tout << "extended model:\n"; model_pp (tout, mdl); tout << "Auxiliary variables of index and value sorts:\n"; - tout << vars << "\n"; + tout << vars; ); } // project reals and ints if (!arith_vars.empty ()) { - TRACE ("qe", tout << "Arith vars: " << arith_vars << "\n";); + TRACE ("qe", tout << "Arith vars:\n" << arith_vars;); + if (m_native_mbp) { expr_ref_vector fmls(m); flatten_and (fml, fmls); (*this)(true, arith_vars, mdl, fmls); fml = mk_and (fmls); + SASSERT (arith_vars.empty ()); + } + else { + NOT_IMPLEMENTED_YET(); + // qe::arith_project (mdl, arith_vars, fml); + } TRACE ("qe", tout << "Projected arith vars:\n" << fml << "\n"; @@ -693,6 +738,7 @@ void mbp::updt_params(params_ref const& p) { void mbp::get_param_descrs(param_descrs & r) { r.insert("reduce_all_selects", CPK_BOOL, "(default: false) reduce selects"); + r.insert("native_mbp", CPK_BOOL, "(default: false) switch between native and spacer tailored mbp"); r.insert("dont_sub", CPK_BOOL, "(default: false) disable substitution of values for free variables"); } From 7642106e73ac9c534cc3f7b1a323da1feccbf86d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 May 2018 14:22:00 -0700 Subject: [PATCH 159/364] add way to unit test mbp from command line Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 2 +- src/qe/qe_arrays.cpp | 185 ++---------------------- src/qe/qe_mbp.cpp | 2 + src/qe/qe_mbp.h | 2 + 4 files changed, 17 insertions(+), 174 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index ceb979761..1b378f430 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -369,7 +369,7 @@ public: void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { m_vars.append(num, ts); } - void prepare(cmd_context & ctx) override { m_fml = nullptr; m_vars.reset(); } + void prepare(cmd_context & ctx) override { m_fml = nullptr; } void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); app_ref_vector vars(m); diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index df5c7f44e..e582676a8 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -631,13 +631,13 @@ namespace qe { m_v = arr_vars.get (i); if (!m_arr_u.is_array (m_v)) { TRACE ("qe", - tout << "not an array variable: " << mk_pp (m_v, m) << "\n"; + tout << "not an array variable: " << m_v << "\n"; ); aux_vars.push_back (m_v); continue; } TRACE ("qe", - tout << "projecting equalities on variable: " << mk_pp (m_v, m) << "\n"; + tout << "projecting equalities on variable: " << m_v << "\n"; ); if (project (fml)) { @@ -653,8 +653,8 @@ namespace qe { ); } else { - IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(m_v, m) << "\n";); - TRACE ("qe", tout << "Failed to project: " << mk_pp (m_v, m) << "\n";); + IF_VERBOSE(2, verbose_stream() << "can't project:" << m_v << "\n";); + TRACE ("qe", tout << "Failed to project: " << m_v << "\n";); arr_vars[j++] = m_v; } } @@ -1196,67 +1196,6 @@ namespace qe { return false; } - bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - - TRACE("qe", tout << mk_pp(var, m) << "\n" << lits;); - m_var = alloc(contains_app, m, var); - - // reduce select-store redeces based on model. - // rw_cfg rw(m); - // rw(lits); - - // try first to solve for var. - if (solve_eq(model, vars, lits)) { - return true; - } - - app_ref_vector selects(m); - - // check that only non-select occurrences are in disequalities. - if (!check_diseqs(lits, selects)) { - TRACE("qe", tout << "Could not project " << mk_pp(var, m) << " for:\n" << lits << "\n";); - return false; - } - - // remove disequalities. - elim_diseqs(lits); - - // Ackerman reduction on remaining select occurrences - // either replace occurrences by model value or other node - // that is congruent to model value. - - ackermanize_select(model, selects, vars, lits); - - TRACE("qe", tout << selects << "\n" << lits << "\n";); - return true; - } - - void ackermanize_select(model& model, app_ref_vector const& selects, app_ref_vector& vars, expr_ref_vector& lits) { - expr_ref_vector vals(m), reps(m); - expr_ref val(m); - expr_safe_replace sub(m); - - if (selects.empty()) { - return; - } - - app_ref sel(m); - for (unsigned i = 0; i < selects.size(); ++i) { - sel = m.mk_fresh_const("sel", m.get_sort(selects[i])); - VERIFY (model.eval(selects[i], val)); - model.register_decl(sel->get_decl(), val); - vals.push_back(to_app(val)); - reps.push_back(val); // TODO: direct pass could handle nested selects. - vars.push_back(sel); - sub.insert(selects[i], val); - } - - sub(lits); - remove_true(lits); - project_plugin::partition_args(model, selects, lits); - project_plugin::partition_values(model, reps, lits); - } - void remove_true(expr_ref_vector& lits) { for (unsigned i = 0; i < lits.size(); ++i) { if (m.is_true(lits[i].get())) { @@ -1275,113 +1214,7 @@ namespace qe { for (unsigned j = 0; j < n; ++j) { lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); } - } - - // check that x occurs only under selects or in disequalities. - bool check_diseqs(expr_ref_vector const& lits, app_ref_vector& selects) { - expr_mark mark; - ptr_vector todo; - app* e; - for (unsigned i = 0; i < lits.size(); ++i) { - e = to_app(lits[i]); - if (is_diseq_x(e)) { - continue; - } - if (contains_x(e)) { - todo.push_back(e); - } - } - while (!todo.empty()) { - e = todo.back(); - todo.pop_back(); - if (mark.is_marked(e)) { - continue; - } - mark.mark(e); - if (m_var->x() == e) { - return false; - } - unsigned start = 0; - if (a.is_select(e)) { - if (e->get_arg(0) == m_var->x()) { - start = 1; - selects.push_back(e); - } - } - for (unsigned i = start; i < e->get_num_args(); ++i) { - todo.push_back(to_app(e->get_arg(i))); - } - } - return true; - } - - void elim_diseqs(expr_ref_vector& lits) { - for (unsigned i = 0; i < lits.size(); ++i) { - if (is_diseq_x(lits[i].get())) { - project_plugin::erase(lits, i); - } - } - } - - bool is_update_x(app* e) { - do { - if (m_var->x() == e) { - return true; - } - if (a.is_store(e) && contains_x(e->get_arg(0))) { - for (unsigned i = 1; i < e->get_num_args(); ++i) { - if (contains_x(e->get_arg(i))) { - return false; - } - } - e = to_app(e->get_arg(0)); - continue; - } - } - while (false); - return false; - } - - bool is_diseq_x(expr* e) { - expr *f, * s, *t; - if (m.is_not(e, f) && m.is_eq(f, s, t)) { - if (contains_x(s) && !contains_x(t) && is_update_x(to_app(s))) return true; - if (contains_x(t) && !contains_x(s) && is_update_x(to_app(t))) return true; - } - return false; - } - - bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - // find an equality to solve for. - expr* s, *t; - for (unsigned i = 0; i < lits.size(); ++i) { - if (m.is_eq(lits[i].get(), s, t)) { - vector idxs; - expr_ref save(m), back(m); - save = lits[i].get(); - back = lits.back(); - lits[i] = back; - lits.pop_back(); - unsigned sz = lits.size(); - if (contains_x(s) && !contains_x(t) && is_app(s)) { - if (solve(model, to_app(s), t, idxs, vars, lits)) { - return true; - } - } - else if (contains_x(t) && !contains_x(s) && is_app(t)) { - if (solve(model, to_app(t), s, idxs, vars, lits)) { - return true; - } - } - // put back the equality literal. - lits.resize(sz); - lits.push_back(back); - lits[i] = save; - } - // TBD: not distinct? - } - return false; - } + } bool solve(model& model, app* s, expr* t, vector& idxs, app_ref_vector& vars, expr_ref_vector& lits) { SASSERT(contains_x(s)); @@ -1502,7 +1335,13 @@ namespace qe { } bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - return (*m_imp)(model, var, vars, lits); + ast_manager& m = vars.get_manager(); + app_ref_vector vvars(m, 1, &var); + expr_ref fml = mk_and(lits); + (*this)(model, vvars, fml, vars, false); + lits.reset(); + flatten_and(fml, lits); + return true; } bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index b07e9904c..58eb77bb4 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -99,6 +99,7 @@ void project_plugin::partition_values(model& model, expr_ref_vector const& vals, } } +#if 0 void project_plugin::partition_args(model& model, app_ref_vector const& selects, expr_ref_vector& lits) { ast_manager& m = selects.get_manager(); if (selects.empty()) return; @@ -111,6 +112,7 @@ void project_plugin::partition_args(model& model, app_ref_vector const& selects, project_plugin::partition_values(model, args, lits); } } +#endif void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { lits[i] = lits.back(); diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 25a95e885..665e25d48 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -40,6 +40,8 @@ namespace qe { virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { }; static expr_ref pick_equality(ast_manager& m, model& model, expr* t); + static void partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits); + //static void partition_args(model& model, app_ref_vector const& sels, expr_ref_vector& lits); static void erase(expr_ref_vector& lits, unsigned& i); static void push_back(expr_ref_vector& lits, expr* lit); static void mark_rec(expr_mark& visited, expr* e); From 692a701516985b18eb6456e4742b4c993420d601 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 May 2018 16:53:33 -0700 Subject: [PATCH 160/364] updates to mbp Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 2 +- src/qe/qe_arrays.cpp | 72 ++++++++++++++++++------- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 1b378f430..ceb979761 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -369,7 +369,7 @@ public: void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { m_vars.append(num, ts); } - void prepare(cmd_context & ctx) override { m_fml = nullptr; } + void prepare(cmd_context & ctx) override { m_fml = nullptr; m_vars.reset(); } void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); app_ref_vector vars(m); diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index e582676a8..614aab35a 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -157,8 +157,8 @@ namespace qe { static bool is_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; - return true; - } + return true; + } static bool is_eq(vector const& xs, vector const& ys) { for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; @@ -631,13 +631,13 @@ namespace qe { m_v = arr_vars.get (i); if (!m_arr_u.is_array (m_v)) { TRACE ("qe", - tout << "not an array variable: " << m_v << "\n"; + tout << "not an array variable: " << mk_pp (m_v, m) << "\n"; ); aux_vars.push_back (m_v); continue; } TRACE ("qe", - tout << "projecting equalities on variable: " << m_v << "\n"; + tout << "projecting equalities on variable: " << mk_pp (m_v, m) << "\n"; ); if (project (fml)) { @@ -653,8 +653,8 @@ namespace qe { ); } else { - IF_VERBOSE(2, verbose_stream() << "can't project:" << m_v << "\n";); - TRACE ("qe", tout << "Failed to project: " << m_v << "\n";); + IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(m_v, m) << "\n";); + TRACE ("qe", tout << "Failed to project: " << mk_pp (m_v, m) << "\n";); arr_vars[j++] = m_v; } } @@ -984,9 +984,9 @@ namespace qe { TRACE("qe", tout << "non-arithmetic index sort for Ackerman" << mk_pp(srt, m) << "\n";); // TBD: generalize to also bit-vectors. is_numeric = false; - } + } } - + unsigned start = m_idxs.size (); // append at the end for (app * a : sel_terms) { expr_ref_vector idxs(m, arity, a->get_args() + 1); @@ -1018,13 +1018,13 @@ namespace qe { } if (is_numeric) { - // sort reprs by their value and add a chain of strict inequalities - compare_idx cmp(*this); - std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); + // sort reprs by their value and add a chain of strict inequalities + compare_idx cmp(*this); + std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); for (unsigned i = start; i + 1 < m_idxs.size(); ++i) { - m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); + m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); + } } - } else if (arity == 1) { // create distinct constraint. expr_ref_vector xs(m); @@ -1163,12 +1163,12 @@ namespace qe { return BR_DONE; } return BR_FAILED; - } + } lbool compare(expr* x, expr* y) { NOT_IMPLEMENTED_YET(); return l_undef; - } + } }; struct indices { @@ -1181,10 +1181,10 @@ namespace qe { for (unsigned i = 0; i < n; ++i) { VERIFY(model.eval(vars[i], val)); m_values.push_back(val); - } + } } }; - + ast_manager& m; array_util a; scoped_ptr m_var; @@ -1194,7 +1194,7 @@ namespace qe { bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; - } + } void remove_true(expr_ref_vector& lits) { for (unsigned i = 0; i < lits.size(); ++i) { @@ -1214,7 +1214,39 @@ namespace qe { for (unsigned j = 0; j < n; ++j) { lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); } - } + } + + bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + // find an equality to solve for. + expr* s, *t; + for (unsigned i = 0; i < lits.size(); ++i) { + if (m.is_eq(lits[i].get(), s, t)) { + vector idxs; + expr_ref save(m), back(m); + save = lits[i].get(); + back = lits.back(); + lits[i] = back; + lits.pop_back(); + unsigned sz = lits.size(); + if (contains_x(s) && !contains_x(t) && is_app(s)) { + if (solve(model, to_app(s), t, idxs, vars, lits)) { + return true; + } + } + else if (contains_x(t) && !contains_x(s) && is_app(t)) { + if (solve(model, to_app(t), s, idxs, vars, lits)) { + return true; + } + } + // put back the equality literal. + lits.resize(sz); + lits.push_back(back); + lits[i] = save; + } + // TBD: not distinct? + } + return false; + } bool solve(model& model, app* s, expr* t, vector& idxs, app_ref_vector& vars, expr_ref_vector& lits) { SASSERT(contains_x(s)); @@ -1362,7 +1394,7 @@ namespace qe { tout << "Remaining array vars:\n" << arr_vars; tout << "Aux vars:\n" << aux_vars; ); - + // 2. reduce selects array_select_reducer rs (m); rs (mdl, arr_vars, fml, reduce_all_selects); From 560a26127e17ce8f3c564cf884c8968ea83a8199 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 May 2018 20:11:29 -0700 Subject: [PATCH 161/364] bind nested variables Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 2 +- src/qe/qe_arrays.cpp | 10 ++--- src/qe/qe_mbp.cpp | 90 +++++++++++--------------------------------- src/qe/qe_mbp.h | 2 - 4 files changed, 27 insertions(+), 77 deletions(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index f663d1b6c..0623bc8bb 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -415,7 +415,7 @@ namespace qe { break; } } - fmls.push_back(t); + fmls.push_back(t); val = eval(t); CTRACE("qe", !m.is_true(val), tout << "Evaluated " << t << " to " << val << "\n";); } diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 614aab35a..4a70d1fcb 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -1390,9 +1390,9 @@ namespace qe { array_project_eqs_util pe (m); pe (mdl, arr_vars, fml, aux_vars); TRACE ("qe", - tout << "Projected array eqs:\n" << fml << "\n"; - tout << "Remaining array vars:\n" << arr_vars; - tout << "Aux vars:\n" << aux_vars; + tout << "Projected array eqs: " << fml << "\n"; + tout << "Remaining array vars: " << arr_vars << "\n"; + tout << "Aux vars: " << aux_vars << "\n"; ); // 2. reduce selects @@ -1406,8 +1406,8 @@ namespace qe { ps (mdl, arr_vars, fml, aux_vars); TRACE ("qe", - tout << "Projected array selects:\n" << fml << "\n"; - tout << "All aux vars:\n" << aux_vars; + tout << "Projected array selects: " << fml << "\n"; + tout << "All aux vars: " << aux_vars << "\n"; ); } diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 58eb77bb4..72348d457 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -77,42 +77,6 @@ expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { return expr_ref(nullptr, m); } -void project_plugin::partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits) { - ast_manager& m = vals.get_manager(); - expr_ref val(m); - expr_ref_vector trail(m), reps(m); - obj_map roots; - for (unsigned i = 0; i < vals.size(); ++i) { - expr* v = vals[i], *root; - VERIFY (model.eval(v, val)); - if (roots.find(val, root)) { - lits.push_back(m.mk_eq(v, root)); - } - else { - roots.insert(val, v); - trail.push_back(val); - reps.push_back(v); - } - } - if (reps.size() > 1) { - lits.push_back(mk_distinct(reps)); - } -} - -#if 0 -void project_plugin::partition_args(model& model, app_ref_vector const& selects, expr_ref_vector& lits) { - ast_manager& m = selects.get_manager(); - if (selects.empty()) return; - unsigned num_args = selects[0]->get_decl()->get_arity(); - for (unsigned j = 1; j < num_args; ++j) { - expr_ref_vector args(m); - for (unsigned i = 0; i < selects.size(); ++i) { - args.push_back(selects[i]->get_arg(j)); - } - project_plugin::partition_values(model, args, lits); - } -} -#endif void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { lits[i] = lits.back(); @@ -127,16 +91,15 @@ void project_plugin::push_back(expr_ref_vector& lits, expr* e) { class mbp::impl { - ast_manager& m; + ast_manager& m; params_ref m_params; - th_rewriter m_rw; + th_rewriter m_rw; ptr_vector m_plugins; - expr_mark m_visited; - expr_mark m_bool_visited; - + expr_mark m_visited; + expr_mark m_bool_visited; + // parameters bool m_reduce_all_selects; - bool m_native_mbp; bool m_dont_sub; void add_plugin(project_plugin* p) { @@ -282,33 +245,33 @@ class mbp::impl { } else { vars[j++] = var; - } } + } if (j == vars.size()) { return; } vars.shrink(j); - j = 0; - for (unsigned i = 0; i < fmls.size(); ++i) { + j = 0; + for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(); sub(fml, val); - m_rw(val); - if (!m.is_true(val)) { + m_rw(val); + if (!m.is_true(val)) { TRACE("qe", tout << mk_pp(fml, m) << " -> " << val << "\n";); fmls[j++] = val; - } - } - fmls.shrink(j); } + } + fmls.shrink(j); + } void subst_vars(model_evaluator& eval, app_ref_vector const& vars, expr_ref& fml) { expr_safe_replace sub (m); for (app * v : vars) { sub.insert(v, eval(v)); - } - sub(fml); } + sub(fml); + } struct index_term_finder { ast_manager& m; @@ -324,8 +287,7 @@ class mbp::impl { void operator() (app *n) { expr *e1, *e2; if (m_array.is_select (n)) { - for (unsigned i = 1; i < n->get_num_args(); ++i) { - expr * arg = n->get_arg(i); + for (expr * arg : *n) { if (m.get_sort(arg) == m.get_sort(m_var) && arg != m_var) m_res.push_back (arg); } @@ -378,8 +340,7 @@ class mbp::impl { // -- evaluate to initialize eval cache (void) eval (fml); unsigned j = 0; - for (unsigned i = 0; i < vars.size (); ++i) { - app* v = vars.get(i); + for (app * v : vars) { if (!project_var (eval, v, fml)) { vars[j++] = v; } @@ -389,7 +350,6 @@ class mbp::impl { public: - opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { arith_project_plugin arith(m); return arith.maximize(fmls, mdl, t, ge, gt); @@ -530,7 +490,6 @@ public: void updt_params(params_ref const& p) { m_params.append(p); m_reduce_all_selects = m_params.get_bool("reduce_all_selects", false); - m_native_mbp = m_params.get_bool("native_mbp", false); m_dont_sub = m_params.get_bool("dont_sub", false); } @@ -549,6 +508,7 @@ public: bool validate_model(model& model, expr_ref_vector const& fmls) { expr_ref val(m); + model_evaluator eval(model); for (expr * f : fmls) { VERIFY(model.eval(f, val) && m.is_true(val)); } @@ -677,26 +637,19 @@ public: tout << "extended model:\n"; model_pp (tout, mdl); tout << "Auxiliary variables of index and value sorts:\n"; - tout << vars; + tout << vars << "\n"; ); } // project reals and ints if (!arith_vars.empty ()) { - TRACE ("qe", tout << "Arith vars:\n" << arith_vars;); + TRACE ("qe", tout << "Arith vars: " << arith_vars << "\n";); - if (m_native_mbp) { expr_ref_vector fmls(m); flatten_and (fml, fmls); (*this)(true, arith_vars, mdl, fmls); fml = mk_and (fmls); - SASSERT (arith_vars.empty ()); - } - else { - NOT_IMPLEMENTED_YET(); - // qe::arith_project (mdl, arith_vars, fml); - } TRACE ("qe", tout << "Projected arith vars:\n" << fml << "\n"; @@ -733,14 +686,13 @@ mbp::mbp(ast_manager& m, params_ref const& p) { mbp::~mbp() { dealloc(m_impl); } - + void mbp::updt_params(params_ref const& p) { m_impl->updt_params(p); } void mbp::get_param_descrs(param_descrs & r) { r.insert("reduce_all_selects", CPK_BOOL, "(default: false) reduce selects"); - r.insert("native_mbp", CPK_BOOL, "(default: false) switch between native and spacer tailored mbp"); r.insert("dont_sub", CPK_BOOL, "(default: false) disable substitution of values for free variables"); } diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 665e25d48..25a95e885 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -40,8 +40,6 @@ namespace qe { virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { }; static expr_ref pick_equality(ast_manager& m, model& model, expr* t); - static void partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits); - //static void partition_args(model& model, app_ref_vector const& sels, expr_ref_vector& lits); static void erase(expr_ref_vector& lits, unsigned& i); static void push_back(expr_ref_vector& lits, expr* lit); static void mark_rec(expr_mark& visited, expr* e); From 5eacb8122dabf1e0f753152c311c7c08ac253ea2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 22 May 2018 08:31:19 -0700 Subject: [PATCH 162/364] add tuple features, remove dead code from mbp Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arrays.cpp | 170 ++++++++++++++++++++++--------------------- src/qe/qe_mbp.cpp | 12 +-- 2 files changed, 94 insertions(+), 88 deletions(-) diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 4a70d1fcb..1701ce742 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -172,6 +172,17 @@ namespace qe { return mk_and(eqs); } + /** + * 1. Extract all equalities (store (store .. (store x i1 v1) i2 v2) .. ) = ... + * where x appears and the equalities do not evaluate to false in the current model. + * 2. Track them as partial equivalence relations. + * 3. Sort them according to nesting depth. + * 4. Solve for x by potentially introducing fresh variables. + * Thus, (store x i v) = y, then + * x = (store y i w), (select y i) = v, where w is fresh and evaluates to (select x i). + * Generally, equalities are tracked as x =_idxs y, where x, y are equal up to idxs. + */ + class array_project_eqs_util { ast_manager& m; array_util m_arr_u; @@ -551,9 +562,7 @@ namespace qe { svector > true_eqs; find_arr_eqs (fml, eqs); - TRACE ("qe", - tout << "array equalities:\n"; - for (app * eq : eqs) tout << mk_pp(eq, m) << "\n";); + TRACE ("qe", tout << "array equalities:\n" << eqs << "\n";); // evaluate eqs in M for (app * eq : eqs) { @@ -625,20 +634,18 @@ namespace qe { M = &mdl; m_mev = &mev; - unsigned j = 0; + unsigned j = 0; for (unsigned i = 0; i < arr_vars.size (); i++) { reset_v (); m_v = arr_vars.get (i); if (!m_arr_u.is_array (m_v)) { TRACE ("qe", - tout << "not an array variable: " << mk_pp (m_v, m) << "\n"; + tout << "not an array variable: " << m_v << "\n"; ); aux_vars.push_back (m_v); continue; } - TRACE ("qe", - tout << "projecting equalities on variable: " << mk_pp (m_v, m) << "\n"; - ); + TRACE ("qe", tout << "projecting equalities on variable: " << m_v << "\n"; ); if (project (fml)) { mk_result (fml); @@ -647,14 +654,11 @@ namespace qe { if (!m_subst_term_v || contains_v (m_subst_term_v)) { arr_vars[j++] = m_v; } - TRACE ("qe", - tout << "after projection: \n"; - tout << mk_pp (fml, m) << "\n"; - ); + TRACE ("qe", tout << "after projection: \n" << fml << "\n";); } else { - IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(m_v, m) << "\n";); - TRACE ("qe", tout << "Failed to project: " << mk_pp (m_v, m) << "\n";); + IF_VERBOSE(2, verbose_stream() << "can't project:" << m_v << "\n";); + TRACE ("qe", tout << "Failed to project: " << m_v << "\n";); arr_vars[j++] = m_v; } } @@ -663,6 +667,10 @@ namespace qe { } }; + /** + * Eliminate (select (store ..) ..) redexes by evaluating indices under model M. + * This does not eliminate variables, but introduces additional constraints on index equalities. + */ class array_select_reducer { ast_manager& m; @@ -787,6 +795,11 @@ namespace qe { return true; } + /** + * \brief reduce (select (store (store x i1 v1) i2 v2) idx) under model M + * such that the result is v2 if idx = i2 under M, it is v1 if idx = i1, idx != i2 under M, + * and it is (select x idx) if idx != i1, idx !+ i2 under M. + */ expr* reduce_core (app *a) { if (!m_arr_u.is_store (a->get_arg (0))) return a; unsigned arity = get_array_arity(m.get_sort(a)); @@ -864,6 +877,17 @@ namespace qe { } }; + /** + * Perform Ackerman reduction on arrays. + * for occurrences (select a i1), (select a i2), ... assuming these are all occurrences. + * - collect i1, i2, i3, into equivalence classes according to M + * - for distinct index classes accumulate constraint i1 < i2 < i3 .. (for arithmetic) + * and generally distinct(i1, i2, i3) for arbitrary index sorts. + * - for equal indices accumulate constraint i1 = i2, i3 = i5, .. + * - introduce variables v1, v2, .., for each equivalence class. + * - replace occurrences of select by v1, v2, ... + * - update M to evaluate v1, v2, the same as (select a i1) (select a i2) + */ class array_project_selects_util { typedef obj_map*> sel_map; @@ -882,9 +906,10 @@ namespace qe { ast_manager& m; array_util m_arr_u; arith_util m_ari_u; + bv_util m_bv_u; sel_map m_sel_terms; // representative indices for eliminating selects - vector m_idxs; + vector m_idxs; app_ref_vector m_sel_consts; expr_ref_vector m_idx_lits; model_ref M; @@ -934,7 +959,15 @@ namespace qe { vector rs; rational r; for (expr* v : vals) { - VERIFY (m_ari_u.is_numeral(v, r)); + if (m_bv_u.is_bv(v)) { + VERIFY (m_bv_u.is_numeral(v, r)); + } + else if (m_ari_u.is_real(v) || m_ari_u.is_int(v)) { + VERIFY (m_ari_u.is_numeral(v, r)); + } + else { + r.reset(); + } rs.push_back(r); } return rs; @@ -955,11 +988,20 @@ namespace qe { } }; + expr* mk_lt(expr* x, expr* y) { + if (m_bv_u.is_bv(x)) { + return m.mk_not(m_bv_u.mk_ule(y, x)); + } + else { + return m_ari_u.mk_lt(x, y); + } + } + expr_ref mk_lex_lt(expr_ref_vector const& xs, expr_ref_vector const& ys) { SASSERT(xs.size() == ys.size() && !xs.empty()); - expr_ref result(m_ari_u.mk_lt(xs.back(), ys.back()), m); + expr_ref result(mk_lt(xs.back(), ys.back()), m); for (unsigned i = xs.size()-1; i-- > 0; ) { - result = m.mk_or(m_ari_u.mk_lt(xs[i], ys[i]), + result = m.mk_or(mk_lt(xs[i], ys[i]), m.mk_and(m.mk_eq(xs[i], ys[i]), result)); } return result; @@ -980,9 +1022,8 @@ namespace qe { bool is_numeric = true; for (unsigned i = 0; i < arity && is_numeric; ++i) { sort* srt = get_array_domain(v_sort, i); - if (!m_ari_u.is_real(srt) && !m_ari_u.is_int(srt)) { - TRACE("qe", tout << "non-arithmetic index sort for Ackerman" << mk_pp(srt, m) << "\n";); - // TBD: generalize to also bit-vectors. + if (!m_ari_u.is_real(srt) && !m_ari_u.is_int(srt) && !m_bv_u.is_bv_sort(srt)) { + TRACE("qe", tout << "non-numeric index sort for Ackerman" << mk_pp(srt, m) << "\n";); is_numeric = false; } } @@ -1017,7 +1058,10 @@ namespace qe { } } - if (is_numeric) { + if (start + 1 == m_idxs.size()) { + // nothing to differentiate. + } + else if (is_numeric) { // sort reprs by their value and add a chain of strict inequalities compare_idx cmp(*this); std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); @@ -1034,8 +1078,26 @@ namespace qe { m_idx_lits.push_back(m.mk_distinct(xs.size(), xs.c_ptr())); } else { - NOT_IMPLEMENTED_YET(); - // use a tuple. + datatype::util dt(m); + sort_ref_vector srts(m); + ptr_vector acc; + unsigned i = 0; + for (expr * x : m_idxs[0].idx) { + std::stringstream name; + name << "get" << (i++); + acc.push_back(mk_accessor_decl(m, symbol(name.str().c_str()), type_ref(m.get_sort(x)))); + } + constructor_decl* constrs[1] = { mk_constructor_decl(symbol("tuple"), symbol("is-tuple"), acc.size(), acc.c_ptr()) }; + datatype::def* dts = mk_datatype_decl(dt, symbol("tuple"), 0, nullptr, 1, constrs); + VERIFY(dt.get_plugin()->mk_datatypes(1, &dts, 0, nullptr, srts)); + del_datatype_decl(dts); + sort* tuple = srts.get(0); + ptr_vector const & decls = *dt.get_datatype_constructors(tuple); + expr_ref_vector xs(m); + for (unsigned i = start; i < m_idxs.size(); ++i) { + xs.push_back(m.mk_app(decls[0], m_idxs[i].idx.size(), m_idxs[i].idx.c_ptr())); + } + m_idx_lits.push_back(m.mk_distinct(xs.size(), xs.c_ptr())); } } @@ -1072,6 +1134,7 @@ namespace qe { m (m), m_arr_u (m), m_ari_u (m), + m_bv_u (m), m_sel_consts (m), m_idx_lits (m), m_sub (m) @@ -1112,65 +1175,6 @@ namespace qe { struct array_project_plugin::imp { - // rewriter or direct procedure. - struct rw_cfg : public default_rewriter_cfg { - ast_manager& m; - array_util& a; - expr_ref_vector m_lits; - model* m_model; - imp* m_imp; - - rw_cfg(ast_manager& m, array_util& a): - m(m), a(a), m_lits(m), m_model(nullptr) {} - - br_status reduce_app(func_decl* f, unsigned n, expr* const* args, expr_ref& result, proof_ref & pr) { - if (a.is_select(f) && a.is_store(args[0])) { - expr_ref val1(m), val2(m); - app* b = to_app(args[0]); - SASSERT(b->get_num_args() == n + 1); - for (unsigned i = 1; i < n; ++i) { - expr* arg1 = args[i]; - expr* arg2 = b->get_arg(i); - if (arg1 == arg2) { - val1 = val2 = arg1; - } - else { - VERIFY(m_model->eval(arg1, val1)); - VERIFY(m_model->eval(arg2, val2)); - } - switch(compare(val1, val2)) { - case l_true: - if (arg1 != arg2) { - m_lits.push_back(m.mk_eq(arg1, arg2)); - } - break; - case l_false: { - ptr_vector new_args; - if (i > 0) { - m_lits.resize(m_lits.size() - i); - } - m_lits.push_back(m.mk_not(m.mk_eq(arg1, arg2))); - new_args.push_back(b->get_arg(0)); - new_args.append(n-1, args+1); - result = m.mk_app(f, n, new_args.c_ptr()); - return BR_REWRITE1; - } - case l_undef: - return BR_FAILED; - } - } - result = b->get_arg(n); - return BR_DONE; - } - return BR_FAILED; - } - - lbool compare(expr* x, expr* y) { - NOT_IMPLEMENTED_YET(); - return l_undef; - } - }; - struct indices { expr_ref_vector m_values; expr* const* m_vars; diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 72348d457..94c2d36aa 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -578,7 +578,7 @@ public: qe_lite qe(m, m_params, false); qe (vars, fml); m_rw (fml); - TRACE ("qe", tout << "After qe_lite:\n" << fml << "\n" << "Vars:\n" << vars;); + TRACE ("qe", tout << "After qe_lite:\n" << fml << "\n" << "Vars: " << vars << "\n";); SASSERT (!m.is_false (fml)); } @@ -590,7 +590,7 @@ public: } void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) { - TRACE ("qe", tout << "Before projection:\n" << fml << "\n" << "Vars:\n" << vars;); + TRACE ("qe", tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";); model_evaluator eval(mdl, m_params); eval.set_model_completion(true); @@ -620,7 +620,7 @@ public: } } - TRACE ("qe", tout << "Array vars:\n" << array_vars;); + TRACE ("qe", tout << "Array vars: " << array_vars << "\n";); vars.reset (); @@ -636,14 +636,16 @@ public: TRACE ("qe", tout << "extended model:\n"; model_pp (tout, mdl); - tout << "Auxiliary variables of index and value sorts:\n"; + tout << "Vars: "; tout << vars << "\n"; ); } // project reals and ints if (!arith_vars.empty ()) { - TRACE ("qe", tout << "Arith vars: " << arith_vars << "\n";); + TRACE ("qe", tout << "Arith vars: " << arith_vars << "\n"; + model_pp(tout, mdl); + ); expr_ref_vector fmls(m); flatten_and (fml, fmls); From 92bac11778aa29537ccb6db4d3ff1792d4a13714 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 May 2018 15:57:18 -0700 Subject: [PATCH 163/364] update to make variables work with other theories Signed-off-by: Nikolaj Bjorner --- src/qe/qe_bool_plugin.cpp | 24 ++++------ src/qe/qe_mbp.cpp | 99 +++++++++++++++++---------------------- 2 files changed, 51 insertions(+), 72 deletions(-) diff --git a/src/qe/qe_bool_plugin.cpp b/src/qe/qe_bool_plugin.cpp index fba76a7e6..f8bac21c8 100644 --- a/src/qe/qe_bool_plugin.cpp +++ b/src/qe/qe_bool_plugin.cpp @@ -93,10 +93,8 @@ namespace qe { bool solve_units(conj_enum& conjs, expr* _fml) { expr_ref fml(_fml, m); - conj_enum::iterator it = conjs.begin(), end = conjs.end(); unsigned idx; - for (; it != end; ++it) { - expr* e = *it; + for (expr * e : conjs) { if (!is_app(e)) { continue; } @@ -138,13 +136,11 @@ namespace qe { return false; } else if (p && !n) { - atom_set::iterator it = m_ctx.pos_atoms().begin(), end = m_ctx.pos_atoms().end(); - for (; it != end; ++it) { - if (x != *it && contains_x(*it)) return false; + for (expr* y : m_ctx.pos_atoms()) { + if (x != y && contains_x(y)) return false; } - it = m_ctx.neg_atoms().begin(), end = m_ctx.neg_atoms().end(); - for (; it != end; ++it) { - if (contains_x(*it)) return false; + for (expr* y : m_ctx.neg_atoms()) { + if (contains_x(y)) return false; } // only occurrences of 'x' must be in positive atoms def = m.mk_true(); @@ -152,13 +148,11 @@ namespace qe { return true; } else if (!p && n) { - atom_set::iterator it = m_ctx.pos_atoms().begin(), end = m_ctx.pos_atoms().end(); - for (; it != end; ++it) { - if (contains_x(*it)) return false; + for (expr* y : m_ctx.pos_atoms()) { + if (contains_x(y)) return false; } - it = m_ctx.neg_atoms().begin(), end = m_ctx.neg_atoms().end(); - for (; it != end; ++it) { - if (x != *it && contains_x(*it)) return false; + for (expr* y : m_ctx.neg_atoms()) { + if (x != y && contains_x(y)) return false; } def = m.mk_false(); m_replace.apply_substitution(x, def, fml); diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 94c2d36aa..9ca4687b5 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -64,8 +64,8 @@ expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { expr_ref_vector vals(m); obj_map val2expr; app* alit = to_app(t); - for (unsigned i = 0; i < alit->get_num_args(); ++i) { - expr* e1 = alit->get_arg(i), *e2; + for (expr * e1 : *alit) { + expr *e2; VERIFY(model.eval(e1, val)); if (val2expr.find(val, e2)) { return expr_ref(m.mk_eq(e1, e2), m); @@ -91,13 +91,13 @@ void project_plugin::push_back(expr_ref_vector& lits, expr* e) { class mbp::impl { - ast_manager& m; + ast_manager& m; params_ref m_params; - th_rewriter m_rw; + th_rewriter m_rw; ptr_vector m_plugins; - expr_mark m_visited; - expr_mark m_bool_visited; - + expr_mark m_visited; + expr_mark m_bool_visited; + // parameters bool m_reduce_all_selects; bool m_dont_sub; @@ -140,12 +140,9 @@ class mbp::impl { } if (reduced) { unsigned j = 0; - for (unsigned i = 0; i < vars.size(); ++i) { - if (!is_rem.is_marked(vars[i].get())) { - if (i != j) { - vars[j] = vars[i].get(); - } - ++j; + for (app* v : vars) { + if (!is_rem.is_marked(v)) { + vars[j++] = v; } } vars.shrink(j); @@ -172,20 +169,13 @@ class mbp::impl { void filter_variables(model& model, app_ref_vector& vars, expr_ref_vector& lits, expr_ref_vector& unused_lits) { expr_mark lit_visited; project_plugin::mark_rec(lit_visited, lits); - unsigned j = 0; - for (unsigned i = 0; i < vars.size(); ++i) { - app* var = vars[i].get(); + for (app* var : vars) { if (lit_visited.is_marked(var)) { - if (i != j) { - vars[j] = vars[i].get(); + vars[j++] = var; } - ++j; } - } - if (vars.size() != j) { - vars.resize(j); - } + vars.shrink(j); } bool extract_bools(model_evaluator& eval, expr_ref_vector& fmls, expr* fml) { @@ -245,33 +235,33 @@ class mbp::impl { } else { vars[j++] = var; + } } - } if (j == vars.size()) { return; } vars.shrink(j); - j = 0; - for (unsigned i = 0; i < fmls.size(); ++i) { + j = 0; + for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(); sub(fml, val); - m_rw(val); - if (!m.is_true(val)) { + m_rw(val); + if (!m.is_true(val)) { TRACE("qe", tout << mk_pp(fml, m) << " -> " << val << "\n";); fmls[j++] = val; - } - } + } + } fmls.shrink(j); - } + } void subst_vars(model_evaluator& eval, app_ref_vector const& vars, expr_ref& fml) { expr_safe_replace sub (m); for (app * v : vars) { sub.insert(v, eval(v)); - } + } sub(fml); - } + } struct index_term_finder { ast_manager& m; @@ -594,7 +584,7 @@ public: model_evaluator eval(mdl, m_params); eval.set_model_completion(true); - app_ref_vector arith_vars (m); + app_ref_vector other_vars (m); app_ref_vector array_vars (m); array_util arr_u (m); arith_util ari_u (m); @@ -612,11 +602,8 @@ public: if (arr_u.is_array(v)) { array_vars.push_back (v); } - else if (ari_u.is_int (v) || ari_u.is_real (v)) { - arith_vars.push_back (v); - } else { - NOT_IMPLEMENTED_YET(); + other_vars.push_back (v); } } @@ -636,46 +623,44 @@ public: TRACE ("qe", tout << "extended model:\n"; model_pp (tout, mdl); - tout << "Vars: "; - tout << vars << "\n"; + tout << "Vars: " << vars << "\n"; ); } - // project reals and ints - if (!arith_vars.empty ()) { - TRACE ("qe", tout << "Arith vars: " << arith_vars << "\n"; - model_pp(tout, mdl); - ); + // project reals, ints and other variables. + if (!other_vars.empty ()) { + TRACE ("qe", tout << "Other vars: " << other_vars << "\n"; + model_pp(tout, mdl);); expr_ref_vector fmls(m); flatten_and (fml, fmls); - (*this)(true, arith_vars, mdl, fmls); + (*this)(false, other_vars, mdl, fmls); fml = mk_and (fmls); TRACE ("qe", - tout << "Projected arith vars:\n" << fml << "\n"; - tout << "Remaining arith vars:\n" << arith_vars << "\n";); + tout << "Projected other vars:\n" << fml << "\n"; + tout << "Remaining other vars:\n" << other_vars << "\n";); SASSERT (!m.is_false (fml)); } - if (!arith_vars.empty ()) { - project_vars (mdl, arith_vars, fml); + if (!other_vars.empty ()) { + project_vars (mdl, other_vars, fml); } - // substitute any remaining arith vars - if (!m_dont_sub && !arith_vars.empty ()) { - subst_vars (eval, arith_vars, fml); - TRACE ("qe", tout << "After substituting remaining arith vars:\n" << fml << "\n";); + // substitute any remaining other vars + if (!m_dont_sub && !other_vars.empty ()) { + subst_vars (eval, other_vars, fml); + TRACE ("qe", tout << "After substituting remaining other vars:\n" << fml << "\n";); // an extra round of simplification because subst_vars is not simplifying m_rw(fml); - arith_vars.reset(); + other_vars.reset(); } SASSERT(eval.is_true(fml)); vars.reset (); - vars.append(arith_vars); + vars.append(other_vars); } }; @@ -688,7 +673,7 @@ mbp::mbp(ast_manager& m, params_ref const& p) { mbp::~mbp() { dealloc(m_impl); } - + void mbp::updt_params(params_ref const& p) { m_impl->updt_params(p); } From 7e9f7d2cfe506b39633233a4c9e69259f1f78886 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 May 2018 16:02:58 -0700 Subject: [PATCH 164/364] remove removed paramter from comment Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbp.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 25a95e885..bddec8065 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -88,7 +88,6 @@ namespace qe { Apply spacer friendly MBP. Use parameters to control behavior. - reduce_all_selects (false) - - native_mbp (false) - to be deprecated - dont_sub (false) */ void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml); From a8438e081eaad149e20ee0c82f070246aee086f9 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 25 May 2018 11:18:26 -0700 Subject: [PATCH 165/364] Wired qe::mbp into spacer use option fixedpoint.spacer.native_mbp=true to use it --- src/muz/spacer/spacer_util.cpp | 87 +++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index b45e0de4a..3eaeb098e 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -90,33 +90,33 @@ namespace spacer { if (!m_model) { return; } m_mev = alloc(model_evaluator, *m_model); } - + bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { m_mev->set_model_completion (model_completion); try { m_mev->operator() (e, result); return true; - } + } catch (model_evaluator_exception &ex) { (void)ex; TRACE("spacer_model_evaluator", tout << ex.msg () << "\n";); return false; } } - + bool model_evaluator_util::eval(const expr_ref_vector &v, expr_ref& res, bool model_completion) { expr_ref e(m); e = mk_and (v); return eval(e, res, model_completion); } - - + + bool model_evaluator_util::is_true(const expr_ref_vector &v) { expr_ref res(m); return eval (v, res, false) && m.is_true (res); } - + bool model_evaluator_util::is_false(expr *x) { expr_ref res(m); return eval(x, res, false) && m.is_false (res); @@ -126,7 +126,7 @@ namespace spacer { expr_ref res(m); return eval(x, res, false) && m.is_true (res); } - + void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); @@ -172,16 +172,16 @@ namespace spacer { SASSERT(orig_size <= 1 + conjs.size()); if (i + 1 == orig_size) { // no-op. - } + } else if (orig_size <= conjs.size()) { // no-op - } + } else { SASSERT(orig_size == 1 + conjs.size()); --orig_size; --i; } - } + } else { conjs[i] = tmp; } @@ -199,7 +199,7 @@ namespace spacer { ast_manager& m; public: ite_hoister(ast_manager& m): m(m) {} - + br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; @@ -234,7 +234,7 @@ namespace spacer { } ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} }; - + class ite_hoister_star : public rewriter_tpl { ite_hoister_cfg m_cfg; public: @@ -242,7 +242,7 @@ namespace spacer { rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; - + void hoist_non_bool_if(expr_ref& fml) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); @@ -274,7 +274,7 @@ namespace spacer { bool is_arith_expr(expr *e) const { return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); } - + bool is_offset(expr* e) const { if (a.is_numeral(e)) { return true; @@ -358,7 +358,7 @@ namespace spacer { !a.is_mul(lhs) && !a.is_mul(rhs); } - + bool test_term(expr* e) const { if (m.is_bool(e)) { return true; @@ -403,9 +403,9 @@ namespace spacer { public: test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - + void test_for_utvpi() { m_test_for_utvpi = true; } - + void operator()(expr* e) { if (!m_is_dl) { return; @@ -422,7 +422,7 @@ namespace spacer { m_is_dl = test_term(a->get_arg(i)); } } - + if (!m_is_dl) { char const* msg = "non-diff: "; if (m_test_for_utvpi) { @@ -431,10 +431,10 @@ namespace spacer { IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } - + bool is_dl() const { return m_is_dl; } }; - + bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); expr_fast_mark1 mark; @@ -443,7 +443,7 @@ namespace spacer { } return test.is_dl(); } - + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); test.test_for_utvpi(); @@ -455,7 +455,7 @@ namespace spacer { } - void subst_vars(ast_manager& m, + void subst_vars(ast_manager& m, app_ref_vector const& vars, model* M, expr_ref& fml) { expr_safe_replace sub (m); @@ -487,11 +487,25 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) << "(pop)\n" << "(exit)\n"; } + +void qe_project_z3 (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + const model_ref& M, bool reduce_all_selects, bool use_native_mbp, + bool dont_sub) { + params_ref p; + p.set_bool("reduce_all_selects", reduce_all_selects); + p.set_bool("dont_sub", dont_sub); + + qe::mbp mbp(m, p); + // TODO: deal with const + model *mdl = const_cast(M.get()); + mbp.spacer(vars, *mdl, fml); +} + /* * eliminate simple equalities using qe_lite * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays */ - void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + void qe_project_spacer (ast_manager& m, app_ref_vector& vars, expr_ref& fml, const model_ref& M, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { th_rewriter rw (m); @@ -506,7 +520,7 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) flatten_and (fml, flat); fml = mk_and(flat); } - + // uncomment for benchmarks //to_mbp_benchmark(verbose_stream(), fml, vars); @@ -523,7 +537,7 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) qe_lite qe(m, p, false); qe (vars, fml); rw (fml); - + TRACE ("spacer_mbp", tout << "After qe_lite:\n"; tout << mk_pp (fml, m) << "\n"; @@ -531,7 +545,7 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) SASSERT (!m.is_false (fml)); - + // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { if (m.is_bool (v)) { @@ -545,7 +559,7 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) arith_vars.push_back (v); } } - + // substitute Booleans if (!bool_sub.empty()) { bool_sub (fml); @@ -555,13 +569,13 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); bool_sub.reset (); } - + TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); - + vars.reset (); - + // project arrays { scoped_no_proof _sp (m); @@ -572,14 +586,14 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) srw (fml); SASSERT (!m.is_false (fml)); } - + TRACE ("spacer_mbp", tout << "extended model:\n"; model_pp (tout, *M); tout << "Auxiliary variables of index and value sorts:\n"; tout << vars; ); - + if (vars.empty()) { break; } } @@ -656,6 +670,15 @@ void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) } } +void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + const model_ref& M, bool reduce_all_selects, bool use_native_mbp, + bool dont_sub) { + if (use_native_mbp) + qe_project_z3(m, vars, fml, M, reduce_all_selects, use_native_mbp, dont_sub); + else + qe_project_spacer(m, vars, fml, M, reduce_all_selects, use_native_mbp, dont_sub); +} + void expand_literals(ast_manager &m, expr_ref_vector& conjs) { if (conjs.empty()) { return; } From e6401908a529cfe1112edfb74ce8d09a103fc973 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 May 2018 13:41:57 -0700 Subject: [PATCH 166/364] fix crash Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arrays.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 1701ce742..1b3bd6c20 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -802,8 +802,9 @@ namespace qe { */ expr* reduce_core (app *a) { if (!m_arr_u.is_store (a->get_arg (0))) return a; - unsigned arity = get_array_arity(m.get_sort(a)); - expr* array = a->get_arg (0); + expr* array = a->get_arg(0); + unsigned arity = get_array_arity(m.get_sort(array)); + expr* const* js = a->get_args() + 1; while (m_arr_u.is_store (array)) { From aa8dac2583d231c1c39086232d62f8e5e72b1d0b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 May 2018 13:55:39 -0700 Subject: [PATCH 167/364] fix uninitialized variable Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 9ca4687b5..1390c0ae5 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -471,6 +471,7 @@ public: add_plugin(alloc(arith_project_plugin, m)); add_plugin(alloc(datatype_project_plugin, m)); add_plugin(alloc(array_project_plugin, m)); + updt_params(p); } ~impl() { From af57db04132dbea4fca6fd713efcaf27efcbba68 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sun, 27 May 2018 09:34:49 -0700 Subject: [PATCH 168/364] Anti-unification of two ground expressions --- src/muz/spacer/spacer_antiunify.cpp | 228 +++++++--------------------- src/muz/spacer/spacer_antiunify.h | 42 ++--- 2 files changed, 80 insertions(+), 190 deletions(-) diff --git a/src/muz/spacer/spacer_antiunify.cpp b/src/muz/spacer/spacer_antiunify.cpp index 6c38e4898..8b5e9e75b 100644 --- a/src/muz/spacer/spacer_antiunify.cpp +++ b/src/muz/spacer/spacer_antiunify.cpp @@ -103,190 +103,73 @@ struct var_abs_rewriter : public default_rewriter_cfg { }; -/* -* construct m_g, which is a generalization of t, where every constant -* is replaced by a variable for any variable in m_g, remember the -* substitution to get back t and save it in m_substitutions -*/ -anti_unifier::anti_unifier(expr* t, ast_manager& man) : m(man), m_pinned(m), m_g(m) -{ - m_pinned.push_back(t); - obj_map substitution; +anti_unifier::anti_unifier(ast_manager &manager) : m(manager), m_pinned(m) {} - var_abs_rewriter var_abs_cfg(m, substitution); - rewriter_tpl var_abs_rw (m, false, var_abs_cfg); - var_abs_rw (t, m_g); - - m_substitutions.push_back(substitution); //TODO: refactor into vector, remove k +void anti_unifier::reset() { + m_subs.reset(); + m_cache.reset(); + m_todo.reset(); + m_pinned.reset(); } -/* traverses m_g and t in parallel. if they only differ in constants - * (i.e. m_g contains a variable, where t contains a constant), then - * add the substitutions, which need to be applied to m_g to get t, to - * m_substitutions. -*/ -bool anti_unifier::add_term(expr* t) { - m_pinned.push_back(t); +void anti_unifier::operator()(expr *e1, expr *e2, expr_ref &res, + substitution &s1, substitution &s2) { - ptr_vector todo; - ptr_vector todo2; - todo.push_back(m_g); - todo2.push_back(t); + reset(); + if (e1 == e2) {res = e1; s1.reset(); s2.reset(); return;} - ast_mark visited; + m_todo.push_back(expr_pair(e1, e2)); + while (!m_todo.empty()) { + const expr_pair &p = m_todo.back(); + SASSERT(is_app(p.first)); + SASSERT(is_app(p.second)); - arith_util util(m); + app * n1 = to_app(p.first); + app * n2 = to_app(p.second); - obj_map substitution; - - while (!todo.empty()) { - expr* current = todo.back(); - todo.pop_back(); - expr* current2 = todo2.back(); - todo2.pop_back(); - - if (!visited.is_marked(current)) { - visited.mark(current, true); - - if (is_var(current)) { - // TODO: for now we don't allow variables in the terms we want to antiunify - SASSERT(m_substitutions[0].contains(current)); - if (util.is_numeral(current2)) { - substitution.insert(current, current2); - } - else {return false;} - } - else { - SASSERT(is_app(current)); - - if (is_app(current2) && - to_app(current)->get_decl() == to_app(current2)->get_decl() && - to_app(current)->get_num_args() == to_app(current2)->get_num_args()) { - // TODO: what to do for numerals here? E.g. if we - // have 1 and 2, do they have the same decl or are - // the decls already different? - SASSERT (!util.is_numeral(current) || current == current2); - for (unsigned i = 0, num_args = to_app(current)->get_num_args(); - i < num_args; ++i) { - todo.push_back(to_app(current)->get_arg(i)); - todo2.push_back(to_app(current2)->get_arg(i)); - } - } - else { - return false; - } - } - } - } - - // we now know that the terms can be anti-unified, so add the cached substitution - m_substitutions.push_back(substitution); - return true; -} - -/* -* returns m_g, where additionally any variable, which has only equal -* substitutions, is substituted with that substitution -*/ -void anti_unifier::finalize() { - ptr_vector todo; - todo.push_back(m_g); - - ast_mark visited; - - obj_map generalization; - - arith_util util(m); - - // post-order traversel which ignores constants and handles them - // directly when the enclosing term of the constant is handled - while (!todo.empty()) { - expr* current = todo.back(); - SASSERT(is_app(current)); - - // if we haven't already visited current - if (!visited.is_marked(current)) { - bool existsUnvisitedParent = false; - - for (unsigned i = 0, sz = to_app(current)->get_num_args(); i < sz; ++i) { - expr* argument = to_app(current)->get_arg(i); - - if (!is_var(argument)) { - SASSERT(is_app(argument)); - // if we haven't visited the current parent yet - if(!visited.is_marked(argument)) { - // add it to the stack - todo.push_back(argument); - existsUnvisitedParent = true; - } - } - } - - // if we already visited all parents, we can visit current too - if (!existsUnvisitedParent) { - visited.mark(current, true); - todo.pop_back(); - - ptr_buffer arg_list; - for (unsigned i = 0, num_args = to_app(current)->get_num_args(); - i < num_args; ++i) { - expr* argument = to_app(current)->get_arg(i); - - if (is_var(argument)) { - // compute whether there are different - // substitutions for argument - bool containsDifferentSubstitutions = false; - - for (unsigned i=0, sz = m_substitutions.size(); i+1 < sz; ++i) { - SASSERT(m_substitutions[i].contains(argument)); - SASSERT(m_substitutions[i+1].contains(argument)); - - // TODO: how to check equality? - if (m_substitutions[i][argument] != - m_substitutions[i+1][argument]) - { - containsDifferentSubstitutions = true; - break; - } - } - - // if yes, use the variable - if (containsDifferentSubstitutions) { - arg_list.push_back(argument); - } - // otherwise use the concrete value instead - // and remove the substitutions - else - { - arg_list.push_back(m_substitutions[0][argument]); - - for (unsigned i=0, sz = m_substitutions.size(); i < sz; ++i) { - SASSERT(m_substitutions[i].contains(argument)); - m_substitutions[i].remove(argument); - } - } - } - else { - SASSERT(generalization.contains(argument)); - arg_list.push_back(generalization[argument]); - } - } - - SASSERT(to_app(current)->get_num_args() == arg_list.size()); - expr_ref application(m.mk_app(to_app(current)->get_decl(), - to_app(current)->get_num_args(), - arg_list.c_ptr()), m); - m_pinned.push_back(application); - generalization.insert(current, application); - } + unsigned num_arg1 = n1->get_num_args(); + unsigned num_arg2 = n2->get_num_args(); + if (n1->get_decl() != n2->get_decl() || num_arg1 != num_arg2) { + expr_ref v(m); + v = m.mk_var(m_subs.size(), get_sort(n1)); + m_pinned.push_back(v); + m_subs.push_back(expr_pair(n1, n2)); + m_cache.insert(n1, n2, v); } else { - todo.pop_back(); + expr *tmp; + unsigned todo_sz = m_todo.size(); + ptr_buffer kids; + for (unsigned i = 0; i < num_arg1; ++i) { + expr *arg1 = n1->get_arg(i); + expr *arg2 = n2->get_arg(i); + if (arg1 == arg2) {kids.push_back(arg1);} + else if (m_cache.find(arg1, arg2, tmp)) {kids.push_back(tmp);} + else {m_todo.push_back(expr_pair(arg1, arg2));} + } + if (m_todo.size() > todo_sz) {continue;} + + expr_ref u(m); + u = m.mk_app(n1->get_decl(), kids.size(), kids.c_ptr()); + m_pinned.push_back(u); + m_cache.insert(n1, n2, u); } } - m_g = generalization[m_g]; + expr *r; + VERIFY(m_cache.find(e1, e2, r)); + res = r; + + // create substitutions + s1.reserve(2, m_subs.size()); + s2.reserve(2, m_subs.size()); + + for (unsigned i = 0, sz = m_subs.size(); i < sz; ++i) { + expr_pair p = m_subs.get(i); + s1.insert(i, 0, expr_offset(p.first, 1)); + s2.insert(i, 0, expr_offset(p.second, 1)); + } } @@ -319,6 +202,8 @@ public: */ bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m, expr_ref& result) { + NOT_IMPLEMENTED_YET(); +#if 0 arith_util util(m); SASSERT(au.get_num_substitutions() > 0); @@ -412,6 +297,7 @@ bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m, result = expr_ref(m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), body),m); return true; +#endif } bool naive_convex_closure::get_range(vector& v, diff --git a/src/muz/spacer/spacer_antiunify.h b/src/muz/spacer/spacer_antiunify.h index fb0933f0d..af7f6f825 100644 --- a/src/muz/spacer/spacer_antiunify.h +++ b/src/muz/spacer/spacer_antiunify.h @@ -22,32 +22,36 @@ Revision History: #define _SPACER_ANTIUNIFY_H_ #include "ast/ast.h" - +#include "ast/substitution/substitution.h" +#include "util/obj_pair_hashtable.h" namespace spacer { +/** +\brief Anti-unifier for ground expressions +*/ class anti_unifier { -public: - anti_unifier(expr* t, ast_manager& m); - ~anti_unifier() {} + typedef std::pair expr_pair; + typedef pair_hash, obj_ptr_hash > expr_pair_hash; + typedef obj_pair_map cache_ty; - bool add_term(expr* t); - void finalize(); - - expr* get_generalization() {return m_g;} - unsigned get_num_substitutions() {return m_substitutions.size();} - obj_map get_substitution(unsigned index){ - SASSERT(index < m_substitutions.size()); - return m_substitutions[index]; - } - -private: - ast_manager& m; - // tracking all created expressions + ast_manager &m; expr_ref_vector m_pinned; - expr_ref m_g; + svector m_todo; + cache_ty m_cache; + svector m_subs; - vector> m_substitutions; +public: + anti_unifier(ast_manager& m); + + void reset(); + + /** + \brief Computes anti-unifier of two ground expressions. Returns + the anti-unifier and the corresponding substitutions + */ + void operator() (expr *e1, expr *e2, expr_ref &res, + substitution &s1, substitution &s2); }; class naive_convex_closure From b73aa3642a54c68e020e7d32b1dfaef86d94a9eb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 27 May 2018 16:54:15 -0700 Subject: [PATCH 169/364] check with cube and clause Signed-off-by: Nikolaj Bjorner --- src/smt/smt_clause.h | 8 +- src/smt/smt_context.cpp | 223 +++++++++++++++++++++---------------- src/smt/smt_context.h | 16 ++- src/smt/smt_context_pp.cpp | 2 +- src/smt/smt_kernel.cpp | 9 ++ src/smt/smt_kernel.h | 2 + src/smt/smt_solver.cpp | 20 ++++ src/smt/theory_pb.cpp | 2 +- src/solver/solver.cpp | 21 ++++ src/solver/solver.h | 7 ++ 10 files changed, 202 insertions(+), 108 deletions(-) diff --git a/src/smt/smt_clause.h b/src/smt/smt_clause.h index 8e843c4cf..f0b352e05 100644 --- a/src/smt/smt_clause.h +++ b/src/smt/smt_clause.h @@ -192,13 +192,13 @@ namespace smt { return m_lits[idx]; } - literal * begin_literals() { return m_lits; } + literal * begin() { return m_lits; } - literal * end_literals() { return m_lits + m_num_literals; } + literal * end() { return m_lits + m_num_literals; } - literal const * begin_literals() const { return m_lits; } + literal const * begin() const { return m_lits; } - literal const * end_literals() const { return m_lits + m_num_literals; } + literal const * end() const { return m_lits + m_num_literals; } unsigned get_activity() const { SASSERT(is_lemma()); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 14a19f824..1e2599802 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -61,6 +61,7 @@ namespace smt { m_dyn_ack_manager(*this, p), m_is_diseq_tmp(nullptr), m_units_to_reassert(m_manager), + m_clause(nullptr), m_qhead(0), m_simp_qhead(0), m_simp_counter(0), @@ -203,8 +204,8 @@ namespace smt { literal l1 = to_literal(l_idx); literal neg_l1 = ~l1; watch_list const & wl = *it; - literal const * it2 = wl.begin_literals(); - literal const * end2 = wl.end_literals(); + literal const * it2 = wl.begin(); + literal const * end2 = wl.end(); for (; it2 != end2; ++it2) { literal l2 = *it2; if (l1.index() < l2.index()) { @@ -385,8 +386,8 @@ namespace smt { it2++; } else { - literal * it3 = cls->begin_literals() + 2; - literal * end3 = cls->end_literals(); + literal * it3 = cls->begin() + 2; + literal * end3 = cls->end(); for(; it3 != end3; ++it3) { if (get_assignment(*it3) != l_false) { // swap literal *it3 with literal at position 0 @@ -1813,6 +1814,17 @@ namespace smt { more case splits to be performed. */ bool context::decide() { + + if (m_clause && at_search_level()) { + switch (decide_clause()) { + case l_true: // already satisfied + break; + case l_undef: // made a decision + return true; + case l_false: // inconsistent + break; + } + } bool_var var; lbool phase; m_case_split_queue->next_case_split(var, phase); @@ -2152,7 +2164,7 @@ namespace smt { \brief See cache_generation(unsigned new_scope_lvl) */ void context::cache_generation(clause const * cls, unsigned new_scope_lvl) { - cache_generation(cls->get_num_literals(), cls->begin_literals(), new_scope_lvl); + cache_generation(cls->get_num_literals(), cls->begin(), new_scope_lvl); } /** @@ -2907,6 +2919,8 @@ namespace smt { del_clauses(m_aux_clauses, 0); del_clauses(m_lemmas, 0); del_justifications(m_justifications, 0); + if (m_clause) del_clause(m_clause); + m_clause = nullptr; if (m_is_diseq_tmp) { m_is_diseq_tmp->del_eh(m_manager, false); m_manager.dec_ref(m_is_diseq_tmp->get_owner()); @@ -3087,6 +3101,7 @@ namespace smt { } TRACE("internalize_assertions", tout << "after internalize_assertions()...\n"; tout << "inconsistent: " << inconsistent() << "\n";); + TRACE("after_internalize_assertions", display(tout);); } bool is_valid_assumption(ast_manager & m, expr * assumption) { @@ -3109,10 +3124,10 @@ namespace smt { /** \brief Assumptions must be uninterpreted boolean constants (aka propositional variables). */ - bool context::validate_assumptions(unsigned num_assumptions, expr * const * assumptions) { - for (unsigned i = 0; i < num_assumptions; i++) { - SASSERT(assumptions[i]); - if (!is_valid_assumption(m_manager, assumptions[i])) { + bool context::validate_assumptions(expr_ref_vector const& asms) { + for (expr* a : asms) { + SASSERT(a); + if (!is_valid_assumption(m_manager, a)) { warning_msg("an assumption must be a propositional variable or the negation of one"); return false; } @@ -3120,11 +3135,55 @@ namespace smt { return true; } - void context::init_assumptions(unsigned num_assumptions, expr * const * assumptions) { + void context::init_clause(expr_ref_vector const& clause) { + if (m_clause) del_clause(m_clause); + m_clause_lits.reset(); + for (expr* lit : clause) { + internalize_formula(lit, true); + mark_as_relevant(lit); + m_clause_lits.push_back(get_literal(lit)); + } + m_clause = mk_clause(m_clause_lits.size(), m_clause_lits.c_ptr(), nullptr); + } + + lbool context::decide_clause() { + if (!m_clause) return l_true; + shuffle(m_clause_lits.size(), m_clause_lits.c_ptr(), m_random); + for (literal l : m_clause_lits) { + switch (get_assignment(l)) { + case l_false: + break; + case l_true: + return l_true; + default: + push_scope(); + assign(l, b_justification::mk_axiom(), true); + return l_undef; + } + } + for (unsigned i = m_assigned_literals.size(); i-- > 0; ) { + literal lit = m_assigned_literals[i]; + if (m_clause_lits.contains(~lit)) { + for (unsigned j = 0, sz = m_clause->get_num_literals(); j < sz; ++j) { + if (m_clause->get_literal(j) == ~lit) { + if (j > 0) m_clause->swap_lits(j, 0); + break; + } + } + b_justification js(m_clause); + set_conflict(js, ~lit); + return l_false; + } + } + UNREACHABLE(); + return l_false; + } + + void context::init_assumptions(expr_ref_vector const& asms) { reset_assumptions(); m_literal2assumption.reset(); m_unsat_core.reset(); - if (num_assumptions > 0) { + if (!asms.empty()) { // We must give a chance to the theories to propagate before we create a new scope... propagate(); // Internal backtracking scopes (created with push_scope()) must only be created when we are @@ -3134,8 +3193,7 @@ namespace smt { if (get_cancel_flag()) return; push_scope(); - for (unsigned i = 0; i < num_assumptions; i++) { - expr * curr_assumption = assumptions[i]; + for (expr * curr_assumption : asms) { if (m_manager.is_true(curr_assumption)) continue; SASSERT(is_valid_assumption(m_manager, curr_assumption)); proof * pr = m_manager.mk_asserted(curr_assumption); @@ -3155,8 +3213,9 @@ namespace smt { } } m_search_lvl = m_scope_lvl; - SASSERT(!(num_assumptions > 0) || m_search_lvl > m_base_lvl); - SASSERT(!(num_assumptions == 0) || m_search_lvl == m_base_lvl); + SASSERT(asms.empty() || m_search_lvl > m_base_lvl); + SASSERT(!asms.empty() || m_search_lvl == m_base_lvl); + TRACE("after_internalization", display(tout);); } void context::reset_assumptions() { @@ -3165,7 +3224,8 @@ namespace smt { m_assumptions.reset(); } - lbool context::mk_unsat_core() { + lbool context::mk_unsat_core(lbool r) { + if (r != l_false) return r; SASSERT(inconsistent()); if (!tracking_assumptions()) { SASSERT(m_assumptions.empty()); @@ -3217,6 +3277,12 @@ namespace smt { m_last_search_failure = MEMOUT; return false; } + + if (m_clause) del_clause(m_clause); + m_clause = nullptr; + m_unsat_core.reset(); + m_stats.m_num_checks++; + pop_to_base_lvl(); return true; } @@ -3240,8 +3306,7 @@ namespace smt { and before internalizing any formulas. */ lbool context::setup_and_check(bool reset_cancel) { - if (!check_preamble(reset_cancel)) - return l_undef; + if (!check_preamble(reset_cancel)) return l_undef; SASSERT(m_scope_lvl == 0); SASSERT(!m_setup.already_configured()); setup_context(m_fparams.m_auto_config); @@ -3254,20 +3319,8 @@ namespace smt { } internalize_assertions(); - lbool r = l_undef; TRACE("before_search", display(tout);); - if (m_asserted_formulas.inconsistent()) { - r = l_false; - } - else { - if (inconsistent()) { - VERIFY(!resolve_conflict()); // build the proof - r = l_false; - } - else { - r = search(); - } - } + lbool r = search(); r = check_finalize(r); return r; } @@ -3281,7 +3334,7 @@ namespace smt { } void context::setup_context(bool use_static_features) { - if (m_setup.already_configured()) + if (m_setup.already_configured() || inconsistent()) return; m_setup(get_config_mode(use_static_features)); setup_components(); @@ -3316,72 +3369,40 @@ namespace smt { } } - lbool context::check(unsigned ext_num_assumptions, expr * const * ext_assumptions, bool reset_cancel, bool already_did_theory_assumptions) { - m_stats.m_num_checks++; - TRACE("check_bug", tout << "STARTING check(num_assumptions, assumptions)\n"; - tout << "inconsistent: " << inconsistent() << ", m_unsat_core.empty(): " << m_unsat_core.empty() << "\n"; - m_asserted_formulas.display(tout); - tout << "-----------------------\n"; - display(tout);); - if (!m_unsat_core.empty()) - m_unsat_core.reset(); - if (!check_preamble(reset_cancel)) - return l_undef; - - TRACE("check_bug", tout << "inconsistent: " << inconsistent() << ", m_unsat_core.empty(): " << m_unsat_core.empty() << "\n";); - pop_to_base_lvl(); + lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel, bool already_did_theory_assumptions) { + if (!check_preamble(reset_cancel)) return l_undef; TRACE("before_search", display(tout);); SASSERT(at_base_level()); - lbool r = l_undef; - if (inconsistent()) { - r = l_false; - } - else { - setup_context(false); - expr_ref_vector all_assumptions(m_manager, ext_num_assumptions, ext_assumptions); - if (!already_did_theory_assumptions) { - add_theory_assumptions(all_assumptions); - } - - unsigned num_assumptions = all_assumptions.size(); - expr * const * assumptions = all_assumptions.c_ptr(); - - if (!validate_assumptions(num_assumptions, assumptions)) - return l_undef; - TRACE("unsat_core_bug", tout << all_assumptions << "\n";); - - internalize_assertions(); - TRACE("after_internalize_assertions", display(tout);); - if (m_asserted_formulas.inconsistent()) { - r = l_false; - } - else { - init_assumptions(num_assumptions, assumptions); - TRACE("after_internalization", display(tout);); - if (inconsistent()) { - VERIFY(!resolve_conflict()); // build the proof - lbool result = mk_unsat_core(); - if (result == l_undef) { - r = l_undef; - } else { - r = l_false; - } - } - else { - r = search(); - if (r == l_false) { - lbool result = mk_unsat_core(); - if (result == l_undef) { - r = l_undef; - } - } - } - } - } + setup_context(false); + expr_ref_vector asms(m_manager, num_assumptions, assumptions); + if (!already_did_theory_assumptions) add_theory_assumptions(asms); + if (!validate_assumptions(asms)) return l_undef; + TRACE("unsat_core_bug", tout << asms << "\n";); + internalize_assertions(); + init_assumptions(asms); + lbool r = search(); + r = mk_unsat_core(r); r = check_finalize(r); return r; } + lbool context::check(expr_ref_vector const& cube, expr_ref_vector const& clause) { + if (!check_preamble(true)) return l_undef; + TRACE("before_search", display(tout);); + setup_context(false); + expr_ref_vector asms(cube); + add_theory_assumptions(asms); + if (!validate_assumptions(asms)) return l_undef; + if (!validate_assumptions(clause)) return l_undef; + internalize_assertions(); + init_assumptions(asms); + init_clause(clause); + lbool r = search(); + r = mk_unsat_core(r); + r = check_finalize(r); + return r; + } + void context::init_search() { for (theory* th : m_theory_set) { th->init_search_eh(); @@ -3450,6 +3471,12 @@ namespace smt { exit(1); } #endif + if (m_asserted_formulas.inconsistent()) + return l_false; + if (inconsistent()) { + VERIFY(!resolve_conflict()); + return l_false; + } timeit tt(get_verbosity_level() >= 100, "smt.stats"); scoped_mk_model smk(*this); SASSERT(at_search_level()); @@ -3473,24 +3500,19 @@ namespace smt { if (!restart(status, curr_lvl)) { break; - } + } } - TRACE("search_lite", tout << "status: " << status << "\n";); TRACE("guessed_literals", expr_ref_vector guessed_lits(m_manager); get_guessed_literals(guessed_lits); - unsigned sz = guessed_lits.size(); - for (unsigned i = 0; i < sz; i++) { - tout << mk_pp(guessed_lits.get(i), m_manager) << "\n"; - }); + tout << guessed_lits << "\n";); end_search(); return status; } bool context::restart(lbool& status, unsigned curr_lvl) { - if (m_last_search_failure != OK) { if (status != l_false) { // build candidate model before returning @@ -3643,6 +3665,8 @@ namespace smt { simplify_clauses(); if (!decide()) { + if (inconsistent()) + return l_false; final_check_status fcs = final_check(); TRACE("final_check_result", tout << "fcs: " << fcs << " last_search_failure: " << m_last_search_failure << "\n";); switch (fcs) { @@ -3700,6 +3724,7 @@ namespace smt { TRACE("final_check", tout << "final_check inconsistent: " << inconsistent() << "\n"; display(tout); display_normalized_enodes(tout);); CASSERT("relevancy", check_relevancy()); + if (m_fparams.m_model_on_final_check) { mk_proto_model(l_undef); model_pp(std::cout, *m_proto_model); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 32379c353..3110ea3c1 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -168,6 +168,8 @@ namespace smt { expr_ref_vector m_units_to_reassert; svector m_units_to_reassert_sign; literal_vector m_assigned_literals; + clause* m_clause; + literal_vector m_clause_lits; unsigned m_qhead; unsigned m_simp_qhead; int m_simp_counter; //!< can become negative @@ -1104,15 +1106,21 @@ namespace smt { void assert_assumption(expr * a); - bool validate_assumptions(unsigned num_assumptions, expr * const * assumptions); + bool validate_assumptions(expr_ref_vector const& asms); - void init_assumptions(unsigned num_assumptions, expr * const * assumptions); + void init_assumptions(expr_ref_vector const& asms); + + void init_clause(expr_ref_vector const& clause); + + lbool decide_clause(); void reset_assumptions(); + void reset_clause(); + void add_theory_assumptions(expr_ref_vector & theory_assumptions); - lbool mk_unsat_core(); + lbool mk_unsat_core(lbool result); void validate_unsat_core(); @@ -1497,6 +1505,8 @@ namespace smt { lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr, bool reset_cancel = true, bool already_did_theory_assumptions = false); + lbool check(expr_ref_vector const& cube, expr_ref_vector const& clause); + lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed); lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index a6b881d10..19247c1d9 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -583,7 +583,7 @@ namespace smt { case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; - if (cls) out << literal_vector(cls->get_num_literals(), cls->begin_literals()); + if (cls) out << literal_vector(cls->get_num_literals(), cls->begin()); break; } case b_justification::JUSTIFICATION: { diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index a6413aef9..1438efaf6 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -115,6 +115,10 @@ namespace smt { return m_kernel.check(num_assumptions, assumptions); } + lbool check(expr_ref_vector const& cube, expr_ref_vector const& clause) { + return m_kernel.check(cube, clause); + } + lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed) { return m_kernel.get_consequences(assumptions, vars, conseq, unfixed); } @@ -287,6 +291,11 @@ namespace smt { return r; } + lbool kernel::check(expr_ref_vector const& cube, expr_ref_vector const& clause) { + return m_imp->check(cube, clause); + } + + lbool kernel::get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed) { return m_imp->get_consequences(assumptions, vars, conseq, unfixed); } diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index 7b2f774ad..4174422f4 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -132,6 +132,8 @@ namespace smt { lbool check(app_ref_vector const& asms) { return check(asms.size(), (expr* const*)asms.c_ptr()); } + lbool check(expr_ref_vector const& cube, expr_ref_vector const& clause); + /** \brief extract consequences among variables. */ diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 8e5c2aaa2..77f3f32db 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -190,6 +190,26 @@ namespace smt { return m_context.check(num_assumptions, assumptions); } + lbool check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl, expr_ref_vector* core, proof_ref* pr) override { + lbool r = m_context.check(cube, clause); + switch (r) { + case l_false: + if (pr) *pr = get_proof(); + if (core) { + ptr_vector _core; + get_unsat_core(_core); + core->append(_core.size(), _core.c_ptr()); + } + break; + case l_true: + if (mdl) get_model(*mdl); + break; + default: + break; + } + return r; + } + struct scoped_minimize_core { smt_solver& s; expr_ref_vector m_assumptions; diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index d45590bff..953ecea2c 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -2251,7 +2251,7 @@ namespace smt { for (unsigned i = 2; i < num_lits; ++i) { process_antecedent(cls.get_literal(i), offset); } - TRACE("pb", tout << literal_vector(cls.get_num_literals(), cls.begin_literals()) << "\n";); + TRACE("pb", tout << literal_vector(cls.get_num_literals(), cls.begin()) << "\n";); break; } case b_justification::BIN_CLAUSE: diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 84b5eb588..d295427e4 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -202,6 +202,27 @@ void solver::assert_expr(expr* f, expr* t) { assert_expr_core2(fml, a); } +lbool solver::check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl, expr_ref_vector* core, proof_ref* pr) { + ast_manager& m = clause.get_manager(); + scoped_push _push(*this); + expr_ref disj = mk_or(clause); + assert_expr(disj); + lbool r = check_sat(cube); + switch (r) { + case l_false: + if (core) get_unsat_core(*core); + if (pr) *pr = get_proof(); + break; + case l_true: + if (mdl) get_model(*mdl); + break; + default: + break; + } + return r; +} + + void solver::collect_param_descrs(param_descrs & r) { r.insert("solver.enforce_model_conversion", CPK_BOOL, "(default: false) enforce model conversion when asserting formulas"); } diff --git a/src/solver/solver.h b/src/solver/solver.h index 4c0c361ff..59a61b3c7 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -146,6 +146,13 @@ public: lbool check_sat(app_ref_vector const& asms) { return check_sat(asms.size(), (expr* const*)asms.c_ptr()); } + /** + \brief Check satisfiability modulo a cube and a clause. + + The cube corresponds to auxiliary assumptions. The clause as an auxiliary disjunction that is also + assumed for the check. + */ + virtual lbool check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl = nullptr, expr_ref_vector* core = nullptr, proof_ref* pr = nullptr); /** \brief Set a progress callback procedure that is invoked by this solver during check_sat. From 4db45473597042d454d4114d493449f260702fb4 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sun, 27 May 2018 21:31:31 -0700 Subject: [PATCH 170/364] silence clang warning --- src/smt/smt_solver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 77f3f32db..0f5338ba6 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -190,6 +190,8 @@ namespace smt { return m_context.check(num_assumptions, assumptions); } + using solver_na2as::check_sat; + lbool check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl, expr_ref_vector* core, proof_ref* pr) override { lbool r = m_context.check(cube, clause); switch (r) { From 275b99e408534bfd63d7257b49f352dd5893339c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sun, 27 May 2018 21:32:15 -0700 Subject: [PATCH 171/364] Add missing override --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index ceb979761..31fc4b008 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -365,7 +365,7 @@ public: if (m_fml == nullptr) return CPK_EXPR; return CPK_EXPR_LIST; } - void set_next_arg(cmd_context& ctx, expr * arg) { m_fml = arg; } + void set_next_arg(cmd_context& ctx, expr * arg) override { m_fml = arg; } void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { m_vars.append(num, ts); } From ea032b56c078d211955adc287f6dcac717b544e6 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sun, 27 May 2018 21:37:34 -0700 Subject: [PATCH 172/364] Return false when clause cannot be decided --- src/smt/smt_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 1e2599802..4210039b0 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1822,7 +1822,7 @@ namespace smt { case l_undef: // made a decision return true; case l_false: // inconsistent - break; + return false; } } bool_var var; From 005a6d93bb84eddedf2580c9820c163757d32bc8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 May 2018 11:29:36 -0700 Subject: [PATCH 173/364] cube and clause Signed-off-by: Nikolaj Bjorner --- src/smt/smt_clause.cpp | 2 +- src/smt/smt_context.cpp | 47 +++++++++++++++++++++--------- src/solver/solver_pool.cpp | 25 +++++++++++++++- src/test/CMakeLists.txt | 1 + src/test/cube_clause.cpp | 58 ++++++++++++++++++++++++++++++++++++++ src/test/main.cpp | 1 + 6 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 src/test/cube_clause.cpp diff --git a/src/smt/smt_clause.cpp b/src/smt/smt_clause.cpp index a9365fffc..4266ab246 100644 --- a/src/smt/smt_clause.cpp +++ b/src/smt/smt_clause.cpp @@ -28,7 +28,7 @@ namespace smt { clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js, clause_del_eh * del_eh, bool save_atoms, expr * const * bool_var2expr_map) { SASSERT(k == CLS_AUX || js == 0 || !js->in_region()); - SASSERT(num_lits >= 2); + SASSERT(num_lits > 2); unsigned sz = get_obj_size(num_lits, k, save_atoms, del_eh != nullptr, js != nullptr); void * mem = m.get_allocator().allocate(sz); clause * cls = new (mem) clause(); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 4210039b0..ab7e4fa35 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1815,7 +1815,7 @@ namespace smt { */ bool context::decide() { - if (m_clause && at_search_level()) { + if (at_search_level() && !m_clause_lits.empty()) { switch (decide_clause()) { case l_true: // already satisfied break; @@ -3137,17 +3137,19 @@ namespace smt { void context::init_clause(expr_ref_vector const& clause) { if (m_clause) del_clause(m_clause); + m_clause = nullptr; m_clause_lits.reset(); for (expr* lit : clause) { internalize_formula(lit, true); mark_as_relevant(lit); m_clause_lits.push_back(get_literal(lit)); } - m_clause = mk_clause(m_clause_lits.size(), m_clause_lits.c_ptr(), nullptr); + if (m_clause_lits.size() > 2) + m_clause = clause::mk(m_manager, m_clause_lits.size(), m_clause_lits.c_ptr(), CLS_AUX); } lbool context::decide_clause() { - if (!m_clause) return l_true; + if (m_clause_lits.empty()) return l_true; shuffle(m_clause_lits.size(), m_clause_lits.c_ptr(), m_random); for (literal l : m_clause_lits) { switch (get_assignment(l)) { @@ -3162,20 +3164,38 @@ namespace smt { } } for (unsigned i = m_assigned_literals.size(); i-- > 0; ) { - literal lit = m_assigned_literals[i]; - if (m_clause_lits.contains(~lit)) { - for (unsigned j = 0, sz = m_clause->get_num_literals(); j < sz; ++j) { - if (m_clause->get_literal(j) == ~lit) { - if (j > 0) m_clause->swap_lits(j, 0); - break; + literal nlit = ~m_assigned_literals[i]; + if (m_clause_lits.contains(nlit)) { + switch (m_clause_lits.size()) { + case 1: { + b_justification js; + set_conflict(js, ~nlit); + break; + } + case 2: { + if (nlit == m_clause_lits[1]) { + std::swap(m_clause_lits[0], m_clause_lits[1]); } + b_justification js(~m_clause_lits[1]); + set_conflict(js, ~nlit); + break; } - b_justification js(m_clause); - set_conflict(js, ~lit); - return l_false; + default: { + for (unsigned j = 0, sz = m_clause->get_num_literals(); j < sz; ++j) { + if (m_clause->get_literal(j) == nlit) { + if (j > 0) m_clause->swap_lits(j, 0); + break; + } + } + b_justification js(m_clause); + set_conflict(js, ~nlit); + break; + } + } + break; } } - UNREACHABLE(); + VERIFY(!resolve_conflict()); return l_false; } @@ -3280,6 +3300,7 @@ namespace smt { if (m_clause) del_clause(m_clause); m_clause = nullptr; + m_clause_lits.reset(); m_unsat_core.reset(); m_stats.m_num_checks++; pop_to_base_lvl(); diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 0aee5c3dc..dbad0999e 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -107,12 +107,35 @@ public: } } + lbool check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl, expr_ref_vector* core, proof_ref* pr) override { + SASSERT(!m_pushed || get_scope_level() > 0); + m_proof.reset(); + internalize_assertions(); + expr_ref_vector cube1(cube); + cube1.push_back(m_pred); + lbool res = m_base->check_sat(cube1, clause, mdl, core, pr); + switch (res) { + case l_true: + m_pool.m_stats.m_num_sat_checks++; + break; + case l_undef: + m_pool.m_stats.m_num_undef_checks++; + break; + default: + break; + } + set_status(res); + + return res; + } + + // NSB: seems we would add m_pred as an assumption? lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { SASSERT(!m_pushed || get_scope_level() > 0); m_proof.reset(); scoped_watch _t_(m_pool.m_check_watch); m_pool.m_stats.m_num_checks++; - + stopwatch sw; sw.start(); internalize_assertions(); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index da4194a2c..1dc0290d8 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(test-z3 chashtable.cpp check_assumptions.cpp cnf_backbones.cpp + cube_clause.cpp datalog_parser.cpp ddnf.cpp diff_logic.cpp diff --git a/src/test/cube_clause.cpp b/src/test/cube_clause.cpp new file mode 100644 index 000000000..f625789e4 --- /dev/null +++ b/src/test/cube_clause.cpp @@ -0,0 +1,58 @@ +#include "ast/reg_decl_plugins.h" +#include "solver/solver_pool.h" +#include "smt/smt_solver.h" + + +void tst_cube_clause() { + ast_manager m; + reg_decl_plugins(m); + params_ref p; + lbool r; + ref solver = mk_smt_solver(m, p, symbol::null); + + expr_ref a(m.mk_const(symbol("a"), m.mk_bool_sort()), m); + expr_ref b(m.mk_const(symbol("b"), m.mk_bool_sort()), m); + expr_ref c(m.mk_const(symbol("c"), m.mk_bool_sort()), m); + expr_ref d(m.mk_const(symbol("d"), m.mk_bool_sort()), m); + expr_ref e(m.mk_const(symbol("e"), m.mk_bool_sort()), m); + expr_ref f(m.mk_const(symbol("f"), m.mk_bool_sort()), m); + expr_ref g(m.mk_const(symbol("g"), m.mk_bool_sort()), m); + expr_ref fml(m); + fml = m.mk_not(m.mk_and(a, b)); + solver->assert_expr(fml); + fml = m.mk_not(m.mk_and(c, d)); + solver->assert_expr(fml); + fml = m.mk_not(m.mk_and(e, f)); + solver->assert_expr(fml); + expr_ref_vector cube(m), clause(m), core(m); + r = solver->check_sat(cube); + std::cout << r << "\n"; + cube.push_back(a); + r = solver->check_sat(cube); + std::cout << r << "\n"; + cube.push_back(c); + cube.push_back(e); + r = solver->check_sat(cube); + std::cout << r << "\n"; + clause.push_back(b); + r = solver->check_sat(cube, clause); + std::cout << r << "\n"; + core.reset(); + solver->get_unsat_core(core); + std::cout << core << "\n"; + clause.push_back(d); + r = solver->check_sat(cube, clause); + std::cout << r << "\n"; + core.reset(); + solver->get_unsat_core(core); + std::cout << core << "\n"; + clause.push_back(f); + r = solver->check_sat(cube, clause); + std::cout << r << "\n"; + core.reset(); + solver->get_unsat_core(core); + std::cout << core << "\n"; + clause.push_back(g); + r = solver->check_sat(cube, clause); + std::cout << r << "\n"; +} diff --git a/src/test/main.cpp b/src/test/main.cpp index c1f169a3a..41f051f24 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -172,6 +172,7 @@ int main(int argc, char ** argv) { TST(var_subst); TST(simple_parser); TST(api); + TST(cube_clause); TST(old_interval); TST(get_implied_equalities); TST(arith_simplifier_plugin); From 56a29093d00dd70fba81e572b43607defd039202 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 28 May 2018 08:28:36 -0700 Subject: [PATCH 174/364] Cleanup transition creation in pred_transformer --- src/muz/spacer/spacer_context.cpp | 97 ++++++++++++++++++------------- src/muz/spacer/spacer_context.h | 11 ++-- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 3c94e5521..109055076 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -646,7 +646,7 @@ pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): m_pobs(*this), m_frames(*this), m_reach_facts(), m_rf_init_sz(0), - m_transition(m), m_initial_state(m), m_extend_lit(m), + m_transition_clause(m), m_transition(m), m_init(m), m_extend_lit(m), m_all_init(false), m_reach_case_vars(m) { @@ -866,12 +866,9 @@ void pred_transformer::simplify_formulas() {m_frames.simplify_formulas ();} -expr_ref pred_transformer::get_formulas(unsigned level, bool add_axioms) +expr_ref pred_transformer::get_formulas(unsigned level) { expr_ref_vector res(m); - if (add_axioms) { - res.push_back((level == 0)?initial_state():transition()); - } m_frames.get_frame_geq_lemmas (level, res); return mk_and(res); } @@ -1141,7 +1138,7 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, expr_ref v(m); if (!must) { // use may summary - summary.push_back (get_formulas (level, false)); + summary.push_back (get_formulas (level)); // -- no auxiliary variables in lemmas *aux = nullptr; } else { // find must summary to use @@ -1380,7 +1377,7 @@ bool pred_transformer::is_ctp_blocked(lemma *lem) { for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); expr_ref lemmas(m), val(m); - lemmas = pt.get_formulas(lem->level(), false); + lemmas = pt.get_formulas(lem->level()); pm.formula_n2o(lemmas.get(), lemmas, i); if (ctp->eval(lemmas, val) && m.is_false(val)) {return false;} } @@ -1498,20 +1495,20 @@ void pred_transformer::mk_assumptions(func_decl* head, expr* fml, void pred_transformer::initialize(decl2rel const& pts) { - m_initial_state = m.mk_false(); + m_init = m.mk_false(); m_transition = m.mk_true(); - init_rules(pts, m_initial_state, m_transition); + init_rules(pts); th_rewriter rw(m); rw(m_transition); - rw(m_initial_state); + rw(m_init); m_solver.assert_expr (m_transition); - m_solver.assert_expr (m_initial_state, 0); + m_solver.assert_expr (m_init, 0); TRACE("spacer", - tout << "Initial state: " << mk_pp(m_initial_state, m) << "\n"; + tout << "Initial state: " << mk_pp(m_init, m) << "\n"; tout << "Transition: " << mk_pp(m_transition, m) << "\n";); - SASSERT(is_app(m_initial_state)); - //m_reachable.add_init(to_app(m_initial_state)); + SASSERT(is_app(m_init)); + //m_reachable.add_init(to_app(m_init)); } @@ -1531,13 +1528,12 @@ void pred_transformer::init_reach_facts () } } -void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition) -{ +void pred_transformer::init_rules(decl2rel const& pts) { expr_ref_vector transitions(m); ptr_vector tr_rules; datalog::rule const* rule; - expr_ref_vector disj(m), init_conds (m); - app_ref pred(m); + expr_ref_vector init_conds (m); + app_ref tag(m); vector is_init; for (auto r : m_rules) {init_rule(pts, *r, is_init, tr_rules, transitions);} SASSERT (is_init.size () == transitions.size ()); @@ -1545,41 +1541,49 @@ void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& std::string name; switch(transitions.size()) { case 0: - transition = m.mk_false(); + m_transition = m.mk_false(); + m_transition_clause.reset(); break; case 1: // create a dummy tag. name = head()->get_name().str() + "_dummy"; - pred = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); + tag = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); rule = tr_rules[0]; - m_tag2rule.insert(pred, rule); - m_rule2tag.insert(rule, pred.get()); - transitions[0] = m.mk_implies (pred, transitions.get(0)); - transitions.push_back (m.mk_or (pred, m_extend_lit->get_arg(0))); - if (!is_init[0]) {init_conds.push_back(m.mk_not(pred));} + m_tag2rule.insert(tag, rule); + m_rule2tag.insert(rule, tag); + transitions[0] = m.mk_implies (tag, transitions.get(0)); - transition = mk_and(transitions); + m_transition_clause.push_back(m_extend_lit->get_arg(0)); + m_transition_clause.push_back(tag); + + transitions.push_back(mk_or(m_transition_clause)); + m_transition_clause.reset(); + + if (!is_init[0]) {init_conds.push_back(m.mk_not(tag));} + + m_transition = mk_and(transitions); break; default: - disj.push_back (m_extend_lit->get_arg(0)); + m_transition_clause.push_back (m_extend_lit->get_arg(0)); for (unsigned i = 0; i < transitions.size(); ++i) { name = head()->get_name().str() + "__tr" + std::to_string(i); - pred = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); + tag = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); rule = tr_rules[i]; - m_tag2rule.insert(pred, rule); - m_rule2tag.insert(rule, pred); - disj.push_back(pred); - transitions[i] = m.mk_implies(pred, transitions[i].get()); + m_tag2rule.insert(tag, rule); + m_rule2tag.insert(rule, tag); + m_transition_clause.push_back(tag); + transitions[i] = m.mk_implies(tag, transitions.get(i)); // update init conds - if (!is_init[i]) {init_conds.push_back (m.mk_not (pred));} + if (!is_init[i]) {init_conds.push_back (m.mk_not (tag));} } - transitions.push_back(m.mk_or(disj.size(), disj.c_ptr())); - transition = mk_and(transitions); + transitions.push_back(mk_or(m_transition_clause)); + m_transition_clause.reset(); + m_transition = mk_and(transitions); break; } // mk init condition - init = mk_and(init_conds); + m_init = mk_and(init_conds); // no rule has uninterpreted tail if (init_conds.empty ()) {m_all_init = true;} } @@ -1718,20 +1722,29 @@ void pred_transformer::init_atom(decl2rel const &pts, app *atom, void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r) { - r.push_back((lvl == 0)?initial_state():transition()); + if (lvl == 0) {r.push_back(m_init);} + else { + r.push_back(m_transition); + if (!m_transition_clause.empty()) { + expr_ref c(m); + c = mk_or(m_transition_clause); + r.push_back(c); + } + } for (unsigned i = 0; i < rules().size(); ++i) { add_premises(pts, lvl, *rules()[i], r); } } -void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r) +void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, + datalog::rule& rule, expr_ref_vector& r) { find_predecessors(rule, m_predicates); for (unsigned i = 0; i < m_predicates.size(); ++i) { expr_ref tmp(m); func_decl* head = m_predicates[i]; pred_transformer& pt = *pts.find(head); - expr_ref inv = pt.get_formulas(lvl, false); + expr_ref inv = pt.get_formulas(lvl); if (!m.is_true(inv)) { pm.formula_n2o(inv, tmp, i, true); r.push_back(tmp); @@ -2365,7 +2378,7 @@ void context::get_level_property(unsigned lvl, expr_ref_vector& res, if (r->head() == m_query_pred) { continue; } - expr_ref conj = r->get_formulas(lvl, false); + expr_ref conj = r->get_formulas(lvl); m_pm.formula_n2o(0, false, conj); res.push_back(conj); ptr_vector sig(r->head()->get_arity(), r->sig()); @@ -3647,7 +3660,7 @@ bool context::check_invariant(unsigned lvl, func_decl* fn) ref ctx = mk_smt_solver(m, params_ref::get_empty(), symbol::null); pred_transformer& pt = *m_rels.find(fn); expr_ref_vector conj(m); - expr_ref inv = pt.get_formulas(next_level(lvl), false); + expr_ref inv = pt.get_formulas(next_level(lvl)); if (m.is_true(inv)) { return true; } pt.add_premises(m_rels, lvl, conj); conj.push_back(m.mk_not(inv)); @@ -3667,7 +3680,7 @@ expr_ref context::get_constraints (unsigned level) decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (; it != end; ++it) { pred_transformer& r = *it->m_value; - expr_ref c = r.get_formulas(level, false); + expr_ref c = r.get_formulas(level); if (m.is_true(c)) { continue; } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index c1241a1bd..cb6d1fc23 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -295,8 +295,9 @@ class pred_transformer { rule2expr m_rule2tag; // map rule to predicate tag. rule2expr m_rule2transition; // map rules to transition rule2apps m_rule2vars; // map rule to auxiliary variables - expr_ref m_transition; // transition relation. - expr_ref m_initial_state; // initial state. + expr_ref_vector m_transition_clause; // extra clause for trans + expr_ref m_transition; // transition relation + expr_ref m_init; // initial condition app_ref m_extend_lit; // literal to extend initial state bool m_all_init; // true if the pt has no uninterpreted body in any rule ptr_vector m_predicates; // temp vector used with find_predecessors() @@ -320,7 +321,7 @@ class pred_transformer { void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result); // Initialization - void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition); + void init_rules(decl2rel const& pts); void init_rule(decl2rel const& pts, datalog::rule const& rule, vector& is_init, ptr_vector& rules, expr_ref_vector& transition); void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, @@ -355,7 +356,7 @@ public: func_decl* const* sig() {return m_sig.c_ptr();} unsigned sig_size() const {return m_sig.size();} expr* transition() const {return m_transition;} - expr* initial_state() const {return m_initial_state;} + expr* init() const {return m_init;} expr* rule2tag(datalog::rule const* r) {return m_rule2tag.find(r);} unsigned get_num_levels() {return m_frames.size ();} expr_ref get_cover_delta(func_decl* p_orig, int level); @@ -427,7 +428,7 @@ public: bool check_inductive(unsigned level, expr_ref_vector& state, unsigned& assumes_level, unsigned weakness = UINT_MAX); - expr_ref get_formulas(unsigned level, bool add_axioms); + expr_ref get_formulas(unsigned level); void simplify_formulas(); From ef58753ae7ab40fa8b783ac7dfa1f5c052c0cfd9 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 28 May 2018 18:20:57 -0700 Subject: [PATCH 175/364] Silence clang warning --- src/solver/solver_pool.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index dbad0999e..4b54639ec 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -107,6 +107,8 @@ public: } } + using solver_na2as::check_sat; + lbool check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl, expr_ref_vector* core, proof_ref* pr) override { SASSERT(!m_pushed || get_scope_level() > 0); m_proof.reset(); From c3edf8c8fad1276ee0821aaeae4b9b83bcd42db7 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 28 May 2018 18:28:38 -0700 Subject: [PATCH 176/364] Restore assertion in smt_clause --- src/smt/smt_clause.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/smt/smt_clause.cpp b/src/smt/smt_clause.cpp index 4266ab246..2b9b8dd3e 100644 --- a/src/smt/smt_clause.cpp +++ b/src/smt/smt_clause.cpp @@ -25,10 +25,10 @@ namespace smt { \brief Create a new clause. bool_var2expr_map is a mapping from bool_var -> expr, it is only used if save_atoms == true. */ - clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js, + clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js, clause_del_eh * del_eh, bool save_atoms, expr * const * bool_var2expr_map) { SASSERT(k == CLS_AUX || js == 0 || !js->in_region()); - SASSERT(num_lits > 2); + SASSERT(num_lits >= 2); unsigned sz = get_obj_size(num_lits, k, save_atoms, del_eh != nullptr, js != nullptr); void * mem = m.get_allocator().allocate(sz); clause * cls = new (mem) clause(); @@ -67,7 +67,7 @@ namespace smt { }}); return cls; } - + void clause::deallocate(ast_manager & m) { clause_del_eh * del_eh = get_del_eh(); if (del_eh) @@ -115,4 +115,3 @@ namespace smt { } }; - From 723e96175b0eb1e5cad8230e8633720549d035bd Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 28 May 2018 18:28:59 -0700 Subject: [PATCH 177/364] spacer: prepare to use incremental clause smt_solver interface --- src/muz/base/fixedpoint_params.pyg | 21 +++++++++--------- src/muz/spacer/spacer_context.cpp | 31 +++++++++++++++++++-------- src/muz/spacer/spacer_prop_solver.cpp | 12 ++++++++++- src/muz/spacer/spacer_prop_solver.h | 1 + 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 10e319786..b32f1f586 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -4,7 +4,7 @@ def_module_params('fixedpoint', params=(('timeout', UINT, UINT_MAX, 'set timeout'), ('engine', SYMBOL, 'auto-config', 'Select: auto-config, datalog, spacer, pdr, bmc'), - ('datalog.default_table', SYMBOL, 'sparse', + ('datalog.default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'), ('datalog.default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'), @@ -80,9 +80,9 @@ def_module_params('fixedpoint', "generalize lemmas using induction strengthening"), ('pdr.use_arith_inductive_generalizer', BOOL, False, "generalize lemmas using arithmetic heuristics for induction strengthening"), - ('pdr.use_convex_closure_generalizer', BOOL, False, + ('pdr.use_convex_closure_generalizer', BOOL, False, "generalize using convex closures of lemmas"), - ('pdr.use_convex_interior_generalizer', BOOL, False, + ('pdr.use_convex_interior_generalizer', BOOL, False, "generalize using convex interiors of lemmas"), ('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " + "cache (2) for model search"), @@ -92,7 +92,7 @@ def_module_params('fixedpoint', ('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"), ('pdr.try_minimize_core', BOOL, False, "try to reduce core size (before inductive minimization)"), - ('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'), + ('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'), ('print_fixedpoint_extensions', BOOL, True, "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + "when printing rules"), @@ -111,7 +111,7 @@ def_module_params('fixedpoint', ('print_statistics', BOOL, False, 'print statistics'), ('print_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), - ('tab.selection', SYMBOL, 'weight', + ('tab.selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), ('xform.bit_blast', BOOL, False, 'bit-blast bit-vectors'), @@ -128,7 +128,7 @@ def_module_params('fixedpoint', ('xform.unfold_rules', UINT, 0, "unfold rules statically using iterative squarring"), ('xform.slice', BOOL, True, "simplify clause set using slicing"), - ('xform.karr', BOOL, False, + ('xform.karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), ('spacer.use_eqclass', BOOL, False, "Generalizes equalities to equivalence classes"), ('xform.transform_arrays', BOOL, False, @@ -141,9 +141,9 @@ def_module_params('fixedpoint', "Gives the number of quantifiers per array"), ('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing", "=> GetId(i) = i, => GetId(i) = true"), - ('xform.quantify_arrays', BOOL, False, + ('xform.quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"), - ('xform.instantiate_quantifiers', BOOL, False, + ('xform.instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"), ('xform.coalesce_rules', BOOL, False, "coalesce rules"), ('xform.tail_simplifier_pve', BOOL, True, "propagate_variable_equivalences"), @@ -155,9 +155,9 @@ def_module_params('fixedpoint', ('spacer.reset_obligation_queue', BOOL, True, 'SPACER: reset obligation queue when entering a new level'), ('spacer.init_reach_facts', BOOL, True, 'SPACER: initialize reachability facts with false'), ('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'), - ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), + ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), - ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), + ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), ('spacer.skip_propagate', BOOL, False, "Skip propagate/pushing phase. Turns PDR into a BMC that returns either reachable or unknown"), ('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"), ('spacer.elim_aux', BOOL, True, "Eliminate auxiliary variables in reachability facts"), @@ -202,4 +202,5 @@ def_module_params('fixedpoint', ('spacer.from_level', UINT, 0, 'starting level to explore'), ('spacer.print_json', SYMBOL, '', 'print pobs tree in JSON format to a given file'), ('spacer.ctp', BOOL, False, 'enable counterexample-to-pushing technique'), + ('spacer.use_inc_clause', BOOL, False, 'Use incremental clause to represent trans'), )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 109055076..5e2d0f1f4 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1199,9 +1199,13 @@ bool pred_transformer::is_blocked (pob &n, unsigned &uses_level) m_solver.set_core (nullptr); m_solver.set_model (nullptr); - expr_ref_vector post(m), aux(m); + expr_ref_vector post(m), _aux(m); post.push_back (n.post ()); - lbool res = m_solver.check_assumptions (post, aux, 0, nullptr, 0); + // this only uses the lemmas at the current level + // transition relation is irrelevant + // XXX quic3: not all lemmas are asserted at the post-condition + lbool res = m_solver.check_assumptions (post, _aux, _aux, + 0, nullptr, 0); if (res == l_false) { uses_level = m_solver.uses_level(); } return res == l_false; } @@ -1315,7 +1319,8 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, // result is either sat (with some reach assumps) or // unsat (even with no reach assumps) expr *bg = m_extend_lit.get (); - lbool is_sat = m_solver.check_assumptions (post, reach_assumps, 1, &bg, 0); + lbool is_sat = m_solver.check_assumptions (post, reach_assumps, + m_transition_clause, 1, &bg, 0); TRACE ("spacer", if (!reach_assumps.empty ()) { @@ -1423,7 +1428,8 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, m_solver.set_core(core); m_solver.set_model(mdl_ref_ptr); expr * bg = m_extend_lit.get (); - lbool r = m_solver.check_assumptions (conj, aux, 1, &bg, 1); + lbool r = m_solver.check_assumptions (conj, aux, m_transition_clause, + 1, &bg, 1); if (r == l_false) { solver_level = m_solver.uses_level (); lem->reset_ctp(); @@ -1455,7 +1461,9 @@ bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, m_solver.set_model (nullptr); expr_ref_vector aux (m); conj.push_back (m_extend_lit); - lbool res = m_solver.check_assumptions (state, aux, conj.size (), conj.c_ptr (), 1); + lbool res = m_solver.check_assumptions (state, aux, + m_transition_clause, + conj.size (), conj.c_ptr (), 1); if (res == l_false) { state.reset(); state.append(core); @@ -1557,8 +1565,10 @@ void pred_transformer::init_rules(decl2rel const& pts) { m_transition_clause.push_back(m_extend_lit->get_arg(0)); m_transition_clause.push_back(tag); - transitions.push_back(mk_or(m_transition_clause)); - m_transition_clause.reset(); + if (!ctx.get_params().spacer_use_inc_clause()) { + transitions.push_back(mk_or(m_transition_clause)); + m_transition_clause.reset(); + } if (!is_init[0]) {init_conds.push_back(m.mk_not(tag));} @@ -1577,8 +1587,11 @@ void pred_transformer::init_rules(decl2rel const& pts) { // update init conds if (!is_init[i]) {init_conds.push_back (m.mk_not (tag));} } - transitions.push_back(mk_or(m_transition_clause)); - m_transition_clause.reset(); + + if (!ctx.get_params().spacer_use_inc_clause()) { + transitions.push_back(mk_or(m_transition_clause)); + m_transition_clause.reset(); + } m_transition = mk_and(transitions); break; } diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 6b29df074..dcbc4bf88 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -347,9 +347,13 @@ lbool prop_solver::internal_check_assumptions( lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, expr_ref_vector& soft, + const expr_ref_vector &clause, unsigned num_bg, expr * const * bg, unsigned solver_id) { + expr_ref cls(m); + // XXX now clause is only supported when pushing is enabled + SASSERT(clause.empty() || !m_use_push_bg); // current clients expect that flattening of HARD is // done implicitly during check_assumptions expr_ref_vector hard(m); @@ -360,7 +364,13 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, // can be disabled if use_push_bg == true // solver::scoped_push _s_(*m_ctx); - if (!m_use_push_bg) { m_ctx->push(); } + if (!m_use_push_bg) { + m_ctx->push(); + if (!clause.empty()) { + cls = mk_or(clause); + m_ctx->assert_expr(cls); + } + } iuc_solver::scoped_bg _b_(*m_ctx); for (unsigned i = 0; i < num_bg; ++i) diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index e87732e94..e1c62443e 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -94,6 +94,7 @@ public: */ lbool check_assumptions(const expr_ref_vector & hard, expr_ref_vector & soft, + const expr_ref_vector &clause, unsigned num_bg = 0, expr * const *bg = nullptr, unsigned solver_id = 0); From 4477f7d32648dc420ea11e4b4594cc429d42b685 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 28 May 2018 19:03:55 -0700 Subject: [PATCH 178/364] Fix memory leak in asserted_formulas --- src/smt/asserted_formulas.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index c7ae7605f..d364404da 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -500,6 +500,7 @@ unsigned asserted_formulas::propagate_values(unsigned i) { void asserted_formulas::update_substitution(expr* n, proof* pr) { expr* lhs, *rhs, *n1; + proof_ref pr1(m); if (is_ground(n) && m.is_eq(n, lhs, rhs)) { compute_depth(lhs); compute_depth(rhs); @@ -510,13 +511,13 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { } if (is_gt(rhs, lhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); - m_scoped_substitution.insert(rhs, lhs, m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr); + pr1 = m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr; + m_scoped_substitution.insert(rhs, lhs, pr1); return; } TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); } - proof_ref pr1(m); - if (m.is_not(n, n1)) { + if (m.is_not(n, n1)) { pr1 = m.proofs_enabled() ? m.mk_iff_false(pr) : nullptr; m_scoped_substitution.insert(n1, m.mk_false(), pr1); } @@ -638,4 +639,3 @@ void pp(asserted_formulas & f) { f.display(std::cout); } #endif - From 26339119e4dd23e4e9fbf0772b1c1422cffa75df Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 29 May 2018 13:16:30 -0700 Subject: [PATCH 179/364] solver::check_sat_cc : check_sat assuming cube and clause Extends check_sat with an ability to assume a single clause in addition to assuming a cube of assumptions --- src/smt/smt_solver.cpp | 21 ++----------- src/solver/solver.cpp | 21 ------------- src/solver/solver.h | 5 ++- src/solver/solver_na2as.cpp | 6 ++++ src/solver/solver_na2as.h | 6 ++-- src/solver/solver_pool.cpp | 61 +++++++++++++++++++++---------------- 6 files changed, 50 insertions(+), 70 deletions(-) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 0f5338ba6..d715e4879 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -190,26 +190,9 @@ namespace smt { return m_context.check(num_assumptions, assumptions); } - using solver_na2as::check_sat; - lbool check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl, expr_ref_vector* core, proof_ref* pr) override { - lbool r = m_context.check(cube, clause); - switch (r) { - case l_false: - if (pr) *pr = get_proof(); - if (core) { - ptr_vector _core; - get_unsat_core(_core); - core->append(_core.size(), _core.c_ptr()); - } - break; - case l_true: - if (mdl) get_model(*mdl); - break; - default: - break; - } - return r; + lbool check_sat_cc_core(expr_ref_vector const& cube, expr_ref_vector const& clause) override { + return m_context.check(cube, clause); } struct scoped_minimize_core { diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index d295427e4..84b5eb588 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -202,27 +202,6 @@ void solver::assert_expr(expr* f, expr* t) { assert_expr_core2(fml, a); } -lbool solver::check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl, expr_ref_vector* core, proof_ref* pr) { - ast_manager& m = clause.get_manager(); - scoped_push _push(*this); - expr_ref disj = mk_or(clause); - assert_expr(disj); - lbool r = check_sat(cube); - switch (r) { - case l_false: - if (core) get_unsat_core(*core); - if (pr) *pr = get_proof(); - break; - case l_true: - if (mdl) get_model(*mdl); - break; - default: - break; - } - return r; -} - - void solver::collect_param_descrs(param_descrs & r) { r.insert("solver.enforce_model_conversion", CPK_BOOL, "(default: false) enforce model conversion when asserting formulas"); } diff --git a/src/solver/solver.h b/src/solver/solver.h index 59a61b3c7..3d9befdbc 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -152,7 +152,10 @@ public: The cube corresponds to auxiliary assumptions. The clause as an auxiliary disjunction that is also assumed for the check. */ - virtual lbool check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl = nullptr, expr_ref_vector* core = nullptr, proof_ref* pr = nullptr); + virtual lbool check_sat_cc(expr_ref_vector const& cube, expr_ref_vector const& clause) { + if (clause.empty()) return check_sat(cube.size(), cube.c_ptr()); + NOT_IMPLEMENTED_YET(); + } /** \brief Set a progress callback procedure that is invoked by this solver during check_sat. diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index f98e08cdb..ac241b4a2 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -67,6 +67,12 @@ lbool solver_na2as::check_sat(unsigned num_assumptions, expr * const * assumptio return check_sat_core(m_assumptions.size(), m_assumptions.c_ptr()); } +lbool solver_na2as::check_sat_cc(const expr_ref_vector &assumptions, const expr_ref_vector &clause) { + if (clause.empty()) return check_sat(assumptions.size(), assumptions.c_ptr()); + append_assumptions app(m_assumptions, assumptions.size(), assumptions.c_ptr()); + return check_sat_cc_core(m_assumptions, clause); +} + lbool solver_na2as::get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { append_assumptions app(m_assumptions, asms.size(), asms.c_ptr()); return get_consequences_core(m_assumptions, vars, consequences); diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index 75fb27189..a3ed85a9a 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -36,19 +36,21 @@ public: void assert_expr_core2(expr * t, expr * a) override; // virtual void assert_expr_core(expr * t) = 0; - + // Subclasses of solver_na2as should redefine the following *_core methods instead of these ones. lbool check_sat(unsigned num_assumptions, expr * const * assumptions) override; + lbool check_sat_cc(const expr_ref_vector &assumptions, const expr_ref_vector &clause) override; void push() override; void pop(unsigned n) override; unsigned get_scope_level() const override; - + unsigned get_num_assumptions() const override { return m_assumptions.size(); } expr * get_assumption(unsigned idx) const override { return m_assumptions[idx]; } lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override; lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override; protected: virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) = 0; + virtual lbool check_sat_cc_core(const expr_ref_vector &assumptions, const expr_ref_vector &clause) {NOT_IMPLEMENTED_YET();} virtual void push_core() = 0; virtual void pop_core(unsigned n) = 0; }; diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 4b54639ec..8012dd129 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -107,37 +107,12 @@ public: } } - using solver_na2as::check_sat; - - lbool check_sat(expr_ref_vector const& cube, expr_ref_vector const& clause, model_ref* mdl, expr_ref_vector* core, proof_ref* pr) override { - SASSERT(!m_pushed || get_scope_level() > 0); - m_proof.reset(); - internalize_assertions(); - expr_ref_vector cube1(cube); - cube1.push_back(m_pred); - lbool res = m_base->check_sat(cube1, clause, mdl, core, pr); - switch (res) { - case l_true: - m_pool.m_stats.m_num_sat_checks++; - break; - case l_undef: - m_pool.m_stats.m_num_undef_checks++; - break; - default: - break; - } - set_status(res); - - return res; - } - - // NSB: seems we would add m_pred as an assumption? lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { SASSERT(!m_pushed || get_scope_level() > 0); m_proof.reset(); scoped_watch _t_(m_pool.m_check_watch); m_pool.m_stats.m_num_checks++; - + stopwatch sw; sw.start(); internalize_assertions(); @@ -156,13 +131,45 @@ public: break; } set_status(res); - + if (false /*m_dump_benchmarks && sw.get_seconds() >= m_pool.fparams().m_dump_min_time*/) { dump_benchmark(num_assumptions, assumptions, res, sw); } return res; } + lbool check_sat_cc_core(const expr_ref_vector &cube, + const expr_ref_vector &clause) override { + SASSERT(!m_pushed || get_scope_level() > 0); + m_proof.reset(); + scoped_watch _t_(m_pool.m_check_watch); + m_pool.m_stats.m_num_checks++; + + stopwatch sw; + sw.start(); + internalize_assertions(); + lbool res = m_base->check_sat_cc(cube, clause); + sw.stop(); + switch (res) { + case l_true: + m_pool.m_check_sat_watch.add(sw); + m_pool.m_stats.m_num_sat_checks++; + break; + case l_undef: + m_pool.m_check_undef_watch.add(sw); + m_pool.m_stats.m_num_undef_checks++; + break; + default: + break; + } + set_status(res); + + // if (false /*m_dump_benchmarks && sw.get_seconds() >= m_pool.fparams().m_dump_min_time*/) { + // dump_benchmark(num_assumptions, assumptions, res, sw); + // } + return res; + } + void push_core() override { SASSERT(!m_pushed || get_scope_level() > 0); if (m_in_delayed_scope) { From 1343b272e738df0ea8c757a2998f8f92f2291205 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 29 May 2018 13:17:42 -0700 Subject: [PATCH 180/364] Implement iuc_solver::check_sat_cc --- src/muz/spacer/spacer_iuc_solver.cpp | 22 ++++++++++++++++++++++ src/muz/spacer/spacer_iuc_solver.h | 1 + 2 files changed, 23 insertions(+) diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index d924f36d7..2814fc249 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -127,6 +127,28 @@ lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions return res; } +lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, + const expr_ref_vector &clause) { + if (clause.empty()) {return check_sat(cube.size(), cube.c_ptr());} + + // -- remove any old assumptions + if (m_assumptions.size() > m_first_assumption) + { m_assumptions.shrink(m_first_assumption); } + + // -- replace theory literals in background assumptions with proxies + mk_proxies(m_assumptions); + // -- in case mk_proxies added new literals, they are all background + m_first_assumption = m_assumptions.size(); + + m_assumptions.append(cube); + m_is_proxied = mk_proxies(m_assumptions, m_first_assumption); + + lbool res; + res = m_solver.check_sat_cc(m_assumptions, clause); + set_status (res); + return res; +} + app* iuc_solver::def_manager::mk_proxy (expr *v) { diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index 8bb0a8605..72d995208 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -123,6 +123,7 @@ public: {return m_solver.get_scope_level();} lbool check_sat(unsigned num_assumptions, expr * const *assumptions) override; + lbool check_sat_cc(const expr_ref_vector &cube, const expr_ref_vector &clause) override; void set_progress_callback(progress_callback *callback) override {m_solver.set_progress_callback(callback);} unsigned get_num_assertions() const override From f7d015de8dd44f96a1b49e09fdb60562720310a8 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 29 May 2018 13:18:02 -0700 Subject: [PATCH 181/364] Switch spacer_prop_solver to check_sat_cc --- src/muz/spacer/spacer_prop_solver.cpp | 27 ++++++++++----------------- src/muz/spacer/spacer_prop_solver.h | 6 ++++-- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index dcbc4bf88..7be4443e3 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -224,7 +224,8 @@ lbool prop_solver::mss(expr_ref_vector &hard, expr_ref_vector &soft) { /// Poor man's maxsat. No guarantees of maximum solution /// Runs maxsat loop on m_ctx Returns l_false if hard is unsat, /// otherwise reduces soft such that hard & soft is sat. -lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) +lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, + const expr_ref_vector &clause) { // replace expressions by assumption literals iuc_solver::scoped_mk_proxy _p_(*m_ctx, hard); @@ -232,7 +233,7 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) // assume soft constraints are propositional literals (no need to proxy) hard.append(soft); - lbool res = m_ctx->check_sat(hard.size(), hard.c_ptr()); + lbool res = m_ctx->check_sat_cc(hard, clause); // if hard constraints alone are unsat or there are no soft // constraints, we are done if (res != l_false || soft.empty()) { return res; } @@ -266,7 +267,7 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) } // check that the NEW constraints became sat - res = m_ctx->check_sat(hard.size(), hard.c_ptr()); + res = m_ctx->check_sat_cc(hard, clause); if (res != l_false) { break; } // still unsat, update the core and repeat core.reset(); @@ -284,9 +285,9 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) return res; } -lbool prop_solver::internal_check_assumptions( - expr_ref_vector& hard_atoms, - expr_ref_vector& soft_atoms) +lbool prop_solver::internal_check_assumptions(expr_ref_vector &hard_atoms, + expr_ref_vector &soft_atoms, + const expr_ref_vector &clause) { // XXX Turn model generation if m_model != 0 SASSERT(m_ctx); @@ -298,7 +299,7 @@ lbool prop_solver::internal_check_assumptions( } if (m_in_level) { assert_level_atoms(m_current_level); } - lbool result = maxsmt(hard_atoms, soft_atoms); + lbool result = maxsmt(hard_atoms, soft_atoms, clause); if (result != l_false && m_model) { m_ctx->get_model(*m_model); } SASSERT(result != l_false || soft_atoms.empty()); @@ -352,8 +353,6 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, unsigned solver_id) { expr_ref cls(m); - // XXX now clause is only supported when pushing is enabled - SASSERT(clause.empty() || !m_use_push_bg); // current clients expect that flattening of HARD is // done implicitly during check_assumptions expr_ref_vector hard(m); @@ -364,13 +363,7 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, // can be disabled if use_push_bg == true // solver::scoped_push _s_(*m_ctx); - if (!m_use_push_bg) { - m_ctx->push(); - if (!clause.empty()) { - cls = mk_or(clause); - m_ctx->assert_expr(cls); - } - } + if (!m_use_push_bg) {m_ctx->push();} iuc_solver::scoped_bg _b_(*m_ctx); for (unsigned i = 0; i < num_bg; ++i) @@ -379,7 +372,7 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, unsigned soft_sz = soft.size(); (void) soft_sz; - lbool res = internal_check_assumptions(hard, soft); + lbool res = internal_check_assumptions(hard, soft, clause); if (!m_use_push_bg) { m_ctx->pop(1); } TRACE("psolve_verbose", diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index e1c62443e..337f24825 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -64,9 +64,11 @@ private: void ensure_level(unsigned lvl); lbool internal_check_assumptions(expr_ref_vector &hard, - expr_ref_vector &soft); + expr_ref_vector &soft, + const expr_ref_vector &clause); - lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft); + lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, + const expr_ref_vector &clause); lbool mss(expr_ref_vector &hard, expr_ref_vector &soft); From 0c2e3c0894b8bf3b59fb97c42c71dd1f90a0f5f1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 29 May 2018 15:40:39 -0700 Subject: [PATCH 182/364] fixes to clause proof tracking Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_antiunify.cpp | 4 +-- src/smt/smt_conflict_resolution.cpp | 1 + src/smt/smt_conflict_resolution.h | 2 +- src/smt/smt_context.cpp | 46 ++++++++--------------------- src/smt/smt_setup.cpp | 3 +- 5 files changed, 19 insertions(+), 37 deletions(-) diff --git a/src/muz/spacer/spacer_antiunify.cpp b/src/muz/spacer/spacer_antiunify.cpp index 8b5e9e75b..0edfb6598 100644 --- a/src/muz/spacer/spacer_antiunify.cpp +++ b/src/muz/spacer/spacer_antiunify.cpp @@ -57,8 +57,8 @@ struct var_abs_rewriter : public default_rewriter_cfg { { bool contains_const_child = false; app* a = to_app(t); - for (unsigned i=0, sz = a->get_num_args(); i < sz; ++i) { - if (m_util.is_numeral(a->get_arg(i))) { + for (expr * arg : *a) { + if (m_util.is_numeral(arg)) { contains_const_child = true; } } diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 7984fd5f0..f7bc60051 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -1033,6 +1033,7 @@ namespace smt { return pr; } SASSERT(js != 0); + TRACE("proof_gen_bug", tout << js << "\n";); m_todo_pr.push_back(tp_elem(js)); return nullptr; } diff --git a/src/smt/smt_conflict_resolution.h b/src/smt/smt_conflict_resolution.h index b5b857184..90390ae7e 100644 --- a/src/smt/smt_conflict_resolution.h +++ b/src/smt/smt_conflict_resolution.h @@ -96,7 +96,7 @@ namespace smt { }; tp_elem(literal l):m_kind(LITERAL), m_lidx(l.index()) {} tp_elem(enode * lhs, enode * rhs):m_kind(EQUALITY), m_lhs(lhs), m_rhs(rhs) {} - tp_elem(justification * js):m_kind(JUSTIFICATION), m_js(js) {} + tp_elem(justification * js):m_kind(JUSTIFICATION), m_js(js) { SASSERT(js);} }; svector m_todo_pr; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index ab7e4fa35..c6f77cf1f 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3144,8 +3144,14 @@ namespace smt { mark_as_relevant(lit); m_clause_lits.push_back(get_literal(lit)); } - if (m_clause_lits.size() > 2) - m_clause = clause::mk(m_manager, m_clause_lits.size(), m_clause_lits.c_ptr(), CLS_AUX); + if (m_clause_lits.size() >= 2) { + justification* js = nullptr; + if (m_manager.proofs_enabled()) { + proof * pr = mk_clause_def_axiom(m_clause_lits.size(), m_clause_lits.c_ptr(), nullptr); + js = mk_justification(justification_proof_wrapper(*this, pr)); + } + m_clause = clause::mk(m_manager, m_clause_lits.size(), m_clause_lits.c_ptr(), CLS_AUX, js); + } } lbool context::decide_clause() { @@ -3163,38 +3169,12 @@ namespace smt { return l_undef; } } - for (unsigned i = m_assigned_literals.size(); i-- > 0; ) { - literal nlit = ~m_assigned_literals[i]; - if (m_clause_lits.contains(nlit)) { - switch (m_clause_lits.size()) { - case 1: { - b_justification js; - set_conflict(js, ~nlit); - break; - } - case 2: { - if (nlit == m_clause_lits[1]) { - std::swap(m_clause_lits[0], m_clause_lits[1]); - } - b_justification js(~m_clause_lits[1]); - set_conflict(js, ~nlit); - break; - } - default: { - for (unsigned j = 0, sz = m_clause->get_num_literals(); j < sz; ++j) { - if (m_clause->get_literal(j) == nlit) { - if (j > 0) m_clause->swap_lits(j, 0); - break; - } - } - b_justification js(m_clause); - set_conflict(js, ~nlit); - break; - } - } - break; - } + if (m_clause_lits.size() == 1) { + set_conflict(b_justification(), ~m_clause_lits[0]); } + else { + set_conflict(b_justification(m_clause), null_literal); + } VERIFY(!resolve_conflict()); return l_false; } diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 3ab40cdc3..97f5c433c 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -730,7 +730,8 @@ namespace smt { } void setup::setup_i_arith() { - m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); + m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); + // m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); } void setup::setup_r_arith() { From ebfb2a4a1e1c52da405673791ef5f0eea95d38e2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 29 May 2018 18:27:45 -0700 Subject: [PATCH 183/364] Fix mbp to respect reduce_all_selects option Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbp.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 1390c0ae5..438f1c061 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -592,10 +592,11 @@ public: flatten_and(fml); - do_qe_lite(vars, fml); while (!vars.empty()) { + do_qe_lite(vars, fml); + do_qe_bool(mdl, vars, fml); // sort out vars into bools, arith (int/real), and arrays @@ -613,21 +614,19 @@ public: vars.reset (); // project arrays - if (!array_vars.empty()) { - qe::array_project_plugin ap(m); - ap(mdl, array_vars, fml, vars, m_reduce_all_selects); - SASSERT (array_vars.empty ()); - m_rw (fml); - SASSERT (!m.is_false (fml)); - } - + qe::array_project_plugin ap(m); + ap(mdl, array_vars, fml, vars, m_reduce_all_selects); + SASSERT (array_vars.empty ()); + m_rw (fml); + SASSERT (!m.is_false (fml)); + TRACE ("qe", tout << "extended model:\n"; model_pp (tout, mdl); tout << "Vars: " << vars << "\n"; ); - } + // project reals, ints and other variables. if (!other_vars.empty ()) { TRACE ("qe", tout << "Other vars: " << other_vars << "\n"; From 61cd74818f3fb22c5b835d732922d18b75eb3df1 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 29 May 2018 21:22:32 -0700 Subject: [PATCH 184/364] Pin lemmas so that they don't disappear --- src/muz/spacer/spacer_context.cpp | 3 +++ src/muz/spacer/spacer_context.h | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 5e2d0f1f4..e826e19aa 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1834,6 +1834,9 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) // new_lemma is really new m_lemmas.push_back(new_lemma); + // XXX because m_lemmas is reduced, keep secondary vector of all lemmas + // XXX so that pob can refer to its lemmas without creating reference cycles + m_pinned_lemmas.push_back(new_lemma); m_sorted = false; m_pt.add_lemma_core(new_lemma); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index cb6d1fc23..f1b852cc3 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -198,12 +198,13 @@ class pred_transformer { #include "muz/spacer/spacer_legacy_frames.h" class frames { private: - pred_transformer &m_pt; - lemma_ref_vector m_lemmas; - unsigned m_size; + pred_transformer &m_pt; // parent pred_transformer + lemma_ref_vector m_pinned_lemmas; // all created lemmas + lemma_ref_vector m_lemmas; // active lemmas + unsigned m_size; // num of frames - bool m_sorted; - lemma_lt_proc m_lt; + bool m_sorted; // true if m_lemmas is sorted by m_lt + lemma_lt_proc m_lt; // sort order for m_lemmas void sort (); From 5072a2a869f2b312649a815a8b495009840ea92f Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 29 May 2018 21:23:32 -0700 Subject: [PATCH 185/364] spacer: pobs keep track of their lemmas --- src/muz/spacer/spacer_context.cpp | 33 +++++++++++++++++++++---------- src/muz/spacer/spacer_context.h | 8 ++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index e826e19aa..3d7e9a921 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -70,7 +70,6 @@ pob::pob (pob* parent, pred_transformer& pt, } } - void pob::set_post(expr* post) { app_ref_vector empty_binding(get_ast_manager()); set_post(post, empty_binding); @@ -1800,36 +1799,48 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) << m_pt.head()->get_name() << " " << mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";); - for (unsigned i = 0, sz = m_lemmas.size(); i < sz; ++i) { - if (m_lemmas[i]->get_expr() == new_lemma->get_expr()) { + unsigned i = 0; + for (auto *old_lemma : m_lemmas) { + if (old_lemma->get_expr() == new_lemma->get_expr()) { m_pt.get_context().new_lemma_eh(m_pt, new_lemma); + + // register existing lemma with the pob + if (new_lemma->has_pob()) { + pob_ref &pob = new_lemma->get_pob(); + if (!pob->lemmas().contains(old_lemma)) + pob->add_lemma(old_lemma); + } + // extend bindings if needed if (!new_lemma->get_bindings().empty()) { - m_lemmas[i]->add_binding(new_lemma->get_bindings()); + old_lemma->add_binding(new_lemma->get_bindings()); } // if the lemma is at a higher level, skip it, - if (m_lemmas[i]->level() >= new_lemma->level()) { + if (old_lemma->level() >= new_lemma->level()) { TRACE("spacer", tout << "Already at a higher level: " - << pp_level(m_lemmas[i]->level()) << "\n";); + << pp_level(old_lemma->level()) << "\n";); // but, since the instances might be new, assert the // instances that have been copied into m_lemmas[i] if (!new_lemma->get_bindings().empty()) { - m_pt.add_lemma_core(m_lemmas[i], true); + m_pt.add_lemma_core(old_lemma, true); } // no new lemma added return false; } // update level of the existing lemma - m_lemmas[i]->set_level(new_lemma->level()); + old_lemma->set_level(new_lemma->level()); // assert lemma in the solver - m_pt.add_lemma_core(m_lemmas[i], false); + m_pt.add_lemma_core(old_lemma, false); // move the lemma to its new place to maintain sortedness - for (unsigned j = i; (j + 1) < sz && m_lt(m_lemmas[j + 1], m_lemmas[j]); ++j) { + unsigned sz = m_lemmas.size(); + for (unsigned j = i; + (j + 1) < sz && m_lt(m_lemmas[j + 1], m_lemmas[j]); ++j) { m_lemmas.swap (j, j+1); } return true; } + i++; } // new_lemma is really new @@ -1840,6 +1851,8 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) m_sorted = false; m_pt.add_lemma_core(new_lemma); + if (new_lemma->has_pob()) {new_lemma->get_pob()->add_lemma(new_lemma);} + if (!new_lemma->external()) { m_pt.get_context().new_lemma_eh(m_pt, new_lemma); } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index f1b852cc3..2adf23007 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -486,7 +486,11 @@ class pob { /// derivation representing the position of this node in the parent's rule scoped_ptr m_derivation; + /// pobs created as children of this pob (at any time, not + /// necessarily currently active) ptr_vector m_kids; + // lemmas created to block this pob (at any time, not necessarily active) + ptr_vector m_lemmas; // depth -> watch std::map m_expand_watches; @@ -542,9 +546,13 @@ public: bool is_closed () const { return !m_open; } void close(); + const ptr_vector &children() {return m_kids;} void add_child (pob &v) {m_kids.push_back (&v);} void erase_child (pob &v) {m_kids.erase (&v);} + const ptr_vector &lemmas() {return m_lemmas;} + void add_lemma(lemma* new_lemma) {m_lemmas.push_back(new_lemma);} + bool is_ground () { return m_binding.empty (); } unsigned get_free_vars_size() { return m_binding.size(); } app_ref_vector const &get_binding() const {return m_binding;} From bfa472faece8e9ca356848adc99db2d5cc98f96f Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 29 May 2018 22:11:52 -0700 Subject: [PATCH 186/364] New style of json dump based on lemmas at pob --- src/muz/spacer/spacer_context.cpp | 6 +- src/muz/spacer/spacer_context.h | 4 +- src/muz/spacer/spacer_json.cpp | 234 +++++++++++++++++------------- src/muz/spacer/spacer_json.h | 39 +++-- 4 files changed, 156 insertions(+), 127 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 3d7e9a921..491349a4e 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -441,7 +441,7 @@ void derivation::premise::set_summary (expr * summary, bool must, lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(lvl), + m_zks(m), m_bindings(m), m_lvl(lvl), m_init_lvl(m_lvl), m_pob(nullptr), m_ctp(nullptr), m_external(false) { SASSERT(m_body); normalize(m_body, m_body); @@ -450,7 +450,7 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(p->level()), + m_zks(m), m_bindings(m), m_lvl(p->level()), m_init_lvl(m_lvl), m_pob(p), m_ctp(nullptr), m_external(false) { SASSERT(m_pob); m_pob->get_skolems(m_zks); @@ -461,7 +461,7 @@ lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(p->level()), + m_zks(m), m_bindings(m), m_lvl(p->level()), m_init_lvl(m_lvl), m_pob(p), m_ctp(nullptr), m_external(false) { if (m_pob) { diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 2adf23007..c28f69d8b 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -114,7 +114,8 @@ class lemma { expr_ref_vector m_cube; app_ref_vector m_zks; app_ref_vector m_bindings; - unsigned m_lvl; + unsigned m_lvl; // current level of the lemma + unsigned m_init_lvl; // level at which lemma was created pob_ref m_pob; model_ref m_ctp; // counter-example to pushing bool m_external; @@ -150,6 +151,7 @@ public: bool is_inductive() const {return is_infty_level(m_lvl);} unsigned level () const {return m_lvl;} + unsigned init_level() const {return m_init_lvl;} void set_level (unsigned lvl); app_ref_vector& get_bindings() {return m_bindings;} bool has_binding(app_ref_vector const &binding); diff --git a/src/muz/spacer/spacer_json.cpp b/src/muz/spacer/spacer_json.cpp index c1094f192..cf36eb899 100644 --- a/src/muz/spacer/spacer_json.cpp +++ b/src/muz/spacer/spacer_json.cpp @@ -24,56 +24,59 @@ Notes: namespace spacer { - std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m) { +static std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m) { - mk_epp pp = mk_epp(t, m); - std::ostringstream ss; - ss << pp; - out << "\""; - for (auto &c:ss.str()) { - switch (c) { - case '"': - out << "\\\""; - break; - case '\\': - out << "\\\\"; - break; - case '\b': - out << "\\b"; - break; - case '\f': - out << "\\f"; - break; - case '\n': - out << "\\n"; - break; - case '\r': - out << "\\r"; - break; - case '\t': - out << "\\t"; - break; - default: - if ('\x00' <= c && c <= '\x1f') { - out << "\\u" - << std::hex << std::setw(4) << std::setfill('0') << (int) c; - } else { - out << c; - } + mk_epp pp = mk_epp(t, m); + std::ostringstream ss; + ss << pp; + out << "\""; + for (auto &c:ss.str()) { + switch (c) { + case '"': + out << "\\\""; + break; + case '\\': + out << "\\\\"; + break; + case '\b': + out << "\\b"; + break; + case '\f': + out << "\\f"; + break; + case '\n': + out << "\\n"; + break; + case '\r': + out << "\\r"; + break; + case '\t': + out << "\\t"; + break; + default: + if ('\x00' <= c && c <= '\x1f') { + out << "\\u" + << std::hex << std::setw(4) << std::setfill('0') << (int) c; + } else { + out << c; } } - out << "\""; - return out; } + out << "\""; + return out; +} - std::ostream &json_marshal(std::ostream &out, lemma *l) { - out << R"({"level":")" << l->level() << R"(", "expr":)"; - json_marshal(out, l->get_expr(), l->get_ast_manager()); - out << "}"; - return out; - } +static std::ostream &json_marshal(std::ostream &out, lemma *l) { + out << "{" + << R"("init_level":")" << l->init_level() + << R"(", "level":")" << l->level() + << R"(", "expr":)"; + json_marshal(out, l->get_expr(), l->get_ast_manager()); + out << "}"; + return out; +} - std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector &lemmas) { +static std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector &lemmas) { std::ostringstream ls; for (auto l:lemmas) { @@ -85,74 +88,103 @@ namespace spacer { } - void json_marshaller::register_lemma(lemma *l) { - if (l->has_pob()) { - m_relations[&*l->get_pob()][l->get_pob()->depth()].push_back(l); +void json_marshaller::register_lemma(lemma *l) { + if (l->has_pob()) { + m_relations[&*l->get_pob()][l->get_pob()->depth()].push_back(l); + } +} + +void json_marshaller::register_pob(pob *p) { + m_relations[p]; +} + +void json_marshaller::marshal_lemmas_old(std::ostream &out) const { + unsigned pob_id = 0; + for (auto &pob_map:m_relations) { + std::ostringstream pob_lemmas; + for (auto &depth_lemmas : pob_map.second) { + pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") + << "\"" << depth_lemmas.first << "\":"; + json_marshal(pob_lemmas, depth_lemmas.second); } + if (pob_lemmas.tellp()) { + out << ((unsigned)out.tellp() == 0 ? "" : ",\n"); + out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; + } + pob_id++; } +} +void json_marshaller::marshal_lemmas_new(std::ostream &out) const { + unsigned pob_id = 0; + for (auto &pob_map:m_relations) { + std::ostringstream pob_lemmas; + pob *n = pob_map.first; + for (auto *l : n->lemmas()) { + pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") + << "\"0\":"; + lemma_ref_vector lemmas_vec; + lemmas_vec.push_back(l); + json_marshal(pob_lemmas, lemmas_vec); + } - void json_marshaller::register_pob(pob *p) { - m_relations[p]; + if (pob_lemmas.tellp()) { + out << ((unsigned)out.tellp() == 0 ? "" : ",\n"); + out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; + } + pob_id++; } +} - std::ostream &spacer::json_marshaller::marshal(std::ostream &out) const { - std::ostringstream nodes; - std::ostringstream edges; - std::ostringstream lemmas; +std::ostream &json_marshaller::marshal(std::ostream &out) const { + std::ostringstream nodes; + std::ostringstream edges; + std::ostringstream lemmas; - unsigned pob_id = 0; + if (m_old_style) + marshal_lemmas_old(lemmas); + else + marshal_lemmas_new(lemmas); + + unsigned pob_id = 0; + unsigned depth = 0; + while (true) { + double root_expand_time = m_ctx->get_root().get_expand_time(depth); + bool a = false; + pob_id = 0; for (auto &pob_map:m_relations) { - std::ostringstream pob_lemmas; - for (auto &depth_lemmas : pob_map.second) { - pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") << "\"" << depth_lemmas.first << "\":"; - json_marshal(pob_lemmas, depth_lemmas.second); - } - if (pob_lemmas.tellp()) { - lemmas << ((unsigned)lemmas.tellp() == 0 ? "" : ",\n"); - lemmas << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; + pob *n = pob_map.first; + double expand_time = n->get_expand_time(depth); + if (expand_time > 0) { + a = true; + std::ostringstream pob_expr; + json_marshal(pob_expr, n->post(), n->get_ast_manager()); + + nodes << ((unsigned)nodes.tellp() == 0 ? "" : ",\n") << + "{\"id\":\"" << depth << n << + "\",\"relative_time\":\"" << expand_time / root_expand_time << + "\",\"absolute_time\":\"" << std::setprecision(2) << expand_time << + "\",\"predicate\":\"" << n->pt().head()->get_name() << + "\",\"expr_id\":\"" << n->post()->get_id() << + "\",\"pob_id\":\"" << pob_id << + "\",\"depth\":\"" << depth << + "\",\"expr\":" << pob_expr.str() << "}"; + if (n->parent()) { + edges << ((unsigned)edges.tellp() == 0 ? "" : ",\n") << + "{\"from\":\"" << depth << n->parent() << + "\",\"to\":\"" << depth << n << "\"}"; + } } pob_id++; } - - unsigned depth = 0; - while (true) { - double root_expand_time = m_ctx->get_root().get_expand_time(depth); - bool a = false; - pob_id = 0; - for (auto &pob_map:m_relations) { - pob *n = pob_map.first; - double expand_time = n->get_expand_time(depth); - if (expand_time > 0) { - a = true; - std::ostringstream pob_expr; - json_marshal(pob_expr, n->post(), n->get_ast_manager()); - - nodes << ((unsigned)nodes.tellp() == 0 ? "" : ",\n") << - "{\"id\":\"" << depth << n << - "\",\"relative_time\":\"" << expand_time / root_expand_time << - "\",\"absolute_time\":\"" << std::setprecision(2) << expand_time << - "\",\"predicate\":\"" << n->pt().head()->get_name() << - "\",\"expr_id\":\"" << n->post()->get_id() << - "\",\"pob_id\":\"" << pob_id << - "\",\"depth\":\"" << depth << - "\",\"expr\":" << pob_expr.str() << "}"; - if (n->parent()) { - edges << ((unsigned)edges.tellp() == 0 ? "" : ",\n") << - "{\"from\":\"" << depth << n->parent() << - "\",\"to\":\"" << depth << n << "\"}"; - } - } - pob_id++; - } - if (!a) { - break; - } - depth++; + if (!a) { + break; } - out << "{\n\"nodes\":[\n" << nodes.str() << "\n],\n"; - out << "\"edges\":[\n" << edges.str() << "\n],\n"; - out << "\"lemmas\":{\n" << lemmas.str() << "\n}\n}\n"; - return out; + depth++; } + out << "{\n\"nodes\":[\n" << nodes.str() << "\n],\n"; + out << "\"edges\":[\n" << edges.str() << "\n],\n"; + out << "\"lemmas\":{\n" << lemmas.str() << "\n}\n}\n"; + return out; +} } diff --git a/src/muz/spacer/spacer_json.h b/src/muz/spacer/spacer_json.h index b100a87dc..131e72678 100644 --- a/src/muz/spacer/spacer_json.h +++ b/src/muz/spacer/spacer_json.h @@ -31,34 +31,29 @@ class ast_manager; namespace spacer { - class lemma; - - typedef sref_vector lemma_ref_vector; - - class context; - - class pob; - - std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m); - - std::ostream &json_marshal(std::ostream &out, lemma *l); - - std::ostream &json_marshal(std::ostream &out, lemma_ref_vector &lemmas); +class lemma; +typedef sref_vector lemma_ref_vector; +class context; +class pob; - class json_marshaller { - context *m_ctx; - std::map> m_relations; +class json_marshaller { + context *m_ctx; + bool m_old_style; + std::map> m_relations; - public: - json_marshaller(context *ctx) : m_ctx(ctx) {} + void marshal_lemmas_old(std::ostream &out) const; + void marshal_lemmas_new(std::ostream &out) const; +public: + json_marshaller(context *ctx, bool old_style = false) : + m_ctx(ctx), m_old_style(old_style) {} - void register_lemma(lemma *l); + void register_lemma(lemma *l); - void register_pob(pob *p); + void register_pob(pob *p); - std::ostream &marshal(std::ostream &out) const; - }; + std::ostream &marshal(std::ostream &out) const; +}; } From fce68536d3e9b09bc621af0705a318ae030e0639 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 30 May 2018 09:04:39 -0700 Subject: [PATCH 187/364] spacer: print all lemmas in json --- src/muz/spacer/spacer_json.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_json.cpp b/src/muz/spacer/spacer_json.cpp index cf36eb899..a99a8f298 100644 --- a/src/muz/spacer/spacer_json.cpp +++ b/src/muz/spacer/spacer_json.cpp @@ -119,9 +119,10 @@ void json_marshaller::marshal_lemmas_new(std::ostream &out) const { for (auto &pob_map:m_relations) { std::ostringstream pob_lemmas; pob *n = pob_map.first; + unsigned i = 0; for (auto *l : n->lemmas()) { pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") - << "\"0\":"; + << "\"" << i++ << "\":"; lemma_ref_vector lemmas_vec; lemmas_vec.push_back(l); json_marshal(pob_lemmas, lemmas_vec); From 3a97451f8cffbd1e7310913b0c7db19566dbd5b4 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 30 May 2018 09:05:05 -0700 Subject: [PATCH 188/364] spacer: normalize the cube before creating a lemma --- src/muz/spacer/spacer_context.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 491349a4e..5f8951ecb 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -487,8 +487,10 @@ void lemma::mk_expr_core() { if (m_pob) {mk_cube_core();} SASSERT(!m_cube.empty()); - m_body = ::push_not(::mk_and(m_cube)); + m_body = ::mk_and(m_cube); + // normalize works better with a cube normalize(m_body, m_body); + m_body = ::push_not(m_body); if (!m_zks.empty() && has_zk_const(m_body)) { app_ref_vector zks(m); From fb52c362107a1f41c0b1e5214a2f2a461d8cabf8 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 30 May 2018 09:11:16 -0700 Subject: [PATCH 189/364] spacer: switch to new IUC as default --- src/muz/base/fixedpoint_params.pyg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index b32f1f586..7b8f8fb72 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -181,11 +181,11 @@ def_module_params('fixedpoint', ('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'), ('spacer.q3.instantiate', BOOL, True, 'instantiate quantified lemmas'), ('spacer.q3', BOOL, True, 'allow quantified lemmas in frames'), - ('spacer.iuc', UINT, 0, + ('spacer.iuc', UINT, 1, '0 = use old implementation of unsat-core-generation, ' + '1 = use new implementation of IUC generation, ' + '2 = use new implementation of IUC + min-cut optimization'), - ('spacer.iuc.arith', UINT, 0, + ('spacer.iuc.arith', UINT, 1, '0 = use simple Farkas plugin, ' + '1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation),' + '2 = use Gaussian elimination optimization (broken), 3 = use additive IUC plugin'), From b120923dd5dec11618da1f48b9a2936c8f567422 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 30 May 2018 11:54:57 -0700 Subject: [PATCH 190/364] qe_lite: bug fix in der::der_sort_vars() The case of VAR 1 = (+ (:var 2) 10) VAR 2 = (+ 0 foo) was not properly handled whenever VAR2 has only one reference. In that case, VAR2 is not marked as done when VAR1 is processed, causing VAR2 to be duplicated in elimination order --- src/qe/qe_lite.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 2a14aa6f2..296cb2ad2 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -142,10 +142,10 @@ namespace eq { } else { SASSERT(fr.second == 1); - visiting.reset_mark(t); + visiting.reset_mark(t); if (!done.is_marked(t)) { if (definitions.get(vidx, nullptr) != nullptr) - order.push_back(vidx); + order.push_back(vidx); done.mark(t); } } @@ -444,7 +444,7 @@ namespace eq { expr_ref r(m); m_subst(cur, m_subst_map.size(), m_subst_map.c_ptr(), r); unsigned inx = sz - m_order[i]- 1; - SASSERT(m_subst_map[inx]==0); + SASSERT(m_subst_map[inx]==nullptr); m_subst_map[inx] = r; } } From 0452bc3d43936922de98af70f3a5f34778cd17ba Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 30 May 2018 11:59:57 -0700 Subject: [PATCH 191/364] qe_lite: simplify definitions before deciding on elimination order --- src/qe/qe_lite.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 296cb2ad2..715848f89 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -142,10 +142,10 @@ namespace eq { } else { SASSERT(fr.second == 1); - visiting.reset_mark(t); + visiting.reset_mark(t); if (!done.is_marked(t)) { if (definitions.get(vidx, nullptr) != nullptr) - order.push_back(vidx); + order.push_back(vidx); done.mark(t); } } From b50da20531c83f848db7ac086120150bc623fd1a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 30 May 2018 13:45:48 -0700 Subject: [PATCH 192/364] array_mbp: turn on model completion --- src/qe/qe_arrays.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 1b3bd6c20..bcf642fb7 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -631,6 +631,7 @@ namespace qe { void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { reset (); model_evaluator mev(mdl); + mev.set_model_completion(true); M = &mdl; m_mev = &mev; @@ -857,6 +858,7 @@ namespace qe { reset (); model_evaluator mev(mdl); + mev.set_model_completion(true); M = &mdl; m_mev = &mev; m_reduce_all_selects = reduce_all_selects; @@ -1167,6 +1169,7 @@ namespace qe { TRACE ("qe", tout << "Failed to project arrays\n";); } + mev.set_model_completion(true); // dealloc for (auto & kv : m_sel_terms) dealloc(kv.m_value); m_sel_terms.reset (); From bebfac047e54043f5e2acf682cd577d0ad7ea986 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 10:40:42 -0700 Subject: [PATCH 193/364] Dump benchmarks in pool_solver --- src/solver/solver_pool.cpp | 100 ++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 41 deletions(-) diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 8012dd129..a015c5ea2 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -32,8 +32,11 @@ class pool_solver : public solver_na2as { expr_ref_vector m_flat; bool m_pushed; bool m_in_delayed_scope; + bool m_dump_benchmarks; + double m_dump_threshold; unsigned m_dump_counter; + bool is_virtual() const { return !m.is_true(m_pred); } public: pool_solver(solver* b, solver_pool& pool, app_ref& pred): @@ -47,10 +50,13 @@ public: m_flat(m), m_pushed(false), m_in_delayed_scope(false), + m_dump_benchmarks(false), + m_dump_threshold(5.0), m_dump_counter(0) { if (is_virtual()) { solver_na2as::assert_expr_core2(m.mk_true(), pred); } + updt_params(m_base->get_params()); } ~pool_solver() override { @@ -64,7 +70,11 @@ public: solver* base_solver() { return m_base.get(); } solver* translate(ast_manager& m, params_ref const& p) override { UNREACHABLE(); return nullptr; } - void updt_params(params_ref const& p) override { solver::updt_params(p); m_base->updt_params(p); } + void updt_params(params_ref const& p) override { + solver::updt_params(p); m_base->updt_params(p); + m_dump_benchmarks = solver::get_params().get_bool("dump_benchmarks", false); + m_dump_threshold = solver::get_params().get_double("dump_threshold", 5.0); + } void push_params() override {m_base->push_params();} void pop_params() override {m_base->pop_params();} @@ -76,8 +86,8 @@ public: void get_unsat_core(ptr_vector & r) override { m_base->get_unsat_core(r); unsigned j = 0; - for (unsigned i = 0; i < r.size(); ++i) - if (m_pred != r[i]) + for (unsigned i = 0; i < r.size(); ++i) + if (m_pred != r[i]) r[j++] = r[i]; r.shrink(j); } @@ -132,8 +142,10 @@ public: } set_status(res); - if (false /*m_dump_benchmarks && sw.get_seconds() >= m_pool.fparams().m_dump_min_time*/) { - dump_benchmark(num_assumptions, assumptions, res, sw); + if (m_dump_benchmarks && sw.get_seconds() >= m_dump_threshold) { + expr_ref_vector cube(m, num_assumptions, assumptions); + expr_ref_vector clause(m); + dump_benchmark(cube, clause, res, sw.get_seconds()); } return res; } @@ -164,9 +176,9 @@ public: } set_status(res); - // if (false /*m_dump_benchmarks && sw.get_seconds() >= m_pool.fparams().m_dump_min_time*/) { - // dump_benchmark(num_assumptions, assumptions, res, sw); - // } + if (m_dump_benchmarks && sw.get_seconds() >= m_dump_threshold) { + dump_benchmark(cube, clause, res, sw.get_seconds()); + } return res; } @@ -179,9 +191,9 @@ public: m_pushed = true; m_in_delayed_scope = false; } - - if (!m_pushed) { - m_in_delayed_scope = true; + + if (!m_pushed) { + m_in_delayed_scope = true; } else { SASSERT(!m_in_delayed_scope); @@ -196,24 +208,24 @@ public: SASSERT(!m_in_delayed_scope); m_base->pop(n); m_pushed = lvl - n > 0; - } - else { - m_in_delayed_scope = lvl - n > 0; + } + else { + m_in_delayed_scope = lvl - n > 0; } } - + void assert_expr_core(expr * e) override { SASSERT(!m_pushed || get_scope_level() > 0); - if (m.is_true(e)) return; + if (m.is_true(e)) return; if (m_in_delayed_scope) { internalize_assertions(); m_base->push(); m_pushed = true; m_in_delayed_scope = false; } - + if (m_pushed) { - m_base->assert_expr(e); + m_base->assert_expr(e); } else { m_flat.push_back(e); @@ -221,7 +233,7 @@ public: m_assertions.append(m_flat); m_flat.reset(); } - } + } void get_model_core(model_ref & _m) override { m_base->get_model_core(_m); } @@ -235,7 +247,7 @@ public: void set_progress_callback(progress_callback * callback) override { m_base->set_progress_callback(callback); } expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { return expr_ref_vector(m); } - + ast_manager& get_manager() const override { return m_base->get_manager(); } void refresh(solver* new_base) { @@ -253,42 +265,49 @@ public: private: - void dump_benchmark(unsigned num_assumptions, expr * const * assumptions, lbool res, stopwatch& sw) { - std::string file_name = mk_file_name(); + void dump_benchmark(const expr_ref_vector &cube, const expr_ref_vector &clause, + lbool last_status, double last_time) { + std::string file_name = mk_file_name(); std::ofstream out(file_name); - if (!out) { + if (!out) { IF_VERBOSE(0, verbose_stream() << "could not open file " << file_name << " for output\n"); return; } - out << "(set-info :status " << lbool2status(res) << ")\n"; - m_base->display(out, num_assumptions, assumptions); - out << "(check-sat"; - for (unsigned i = 0; i < num_assumptions; ++i) { - out << " " << mk_pp(assumptions[i], m); + out << "(set-info :status " << lbool2status(last_status) << ")\n"; + m_base->display(out, cube.size(), cube.c_ptr()); + if (!clause.empty()) { + out << ";; extra clause\n"; + out << "(assert (or "; + for (auto *lit : clause) out << mk_pp(lit, m) << " "; + out << "))\n"; } - out << ")"; + + out << "(check-sat"; + for (auto * lit : cube) out << " " << mk_pp(lit, m); + out << ")\n"; + out << "(exit)\n"; ::statistics st; m_base->collect_statistics(st); - st.update("time", sw.get_seconds()); - st.display_smt2(out); - out.close(); + st.update("time", last_time); + st.display_smt2(out); + out.close(); } char const* lbool2status(lbool r) const { switch (r) { case l_true: return "sat"; case l_false: return "unsat"; - case l_undef: return "unknown"; + case l_undef: return "unknown"; } return "?"; } std::string mk_file_name() { std::stringstream file_name; - file_name << "virt_solver"; - if (is_virtual()) file_name << "_" << m_pred->get_decl()->get_name(); + file_name << "pool_solver"; + if (is_virtual()) file_name << "_" << m_pred->get_decl()->get_name(); file_name << "_" << (m_dump_counter++) << ".smt2"; return file_name.str(); } @@ -317,12 +336,11 @@ ptr_vector solver_pool::get_base_solvers() const { void solver_pool::updt_params(const params_ref &p) { m_base_solver->updt_params(p); - ptr_vector solvers = get_base_solvers(); - for (solver *s : solvers) s->updt_params(p); + for (solver *s : m_solvers) s->updt_params(p); } void solver_pool::collect_statistics(statistics &st) const { ptr_vector solvers = get_base_solvers(); - for (solver* s : solvers) s->collect_statistics(st); + for (solver* s : solvers) s->collect_statistics(st); st.update("time.pool_solver.smt.total", m_check_watch.get_seconds()); st.update("time.pool_solver.smt.total.sat", m_check_sat_watch.get_seconds()); st.update("time.pool_solver.smt.total.undef", m_check_undef_watch.get_seconds()); @@ -336,7 +354,7 @@ void solver_pool::reset_statistics() { #if 0 ptr_vector solvers = get_base_solvers(); for (solver* s : solvers) { - s->reset_statistics(); + s->reset_statistics(); } #endif m_stats.reset(); @@ -348,7 +366,7 @@ void solver_pool::reset_statistics() { /** \brief Create a fresh solver instance. - The first num_pools solvers are independent and + The first num_pools solvers are independent and use a fresh instance of the base solver. Subsequent solvers reuse the first num_polls base solvers, rotating among the first num_pools. @@ -374,7 +392,7 @@ solver* solver_pool::mk_solver() { void solver_pool::reset_solver(solver* s) { pool_solver* ps = dynamic_cast(s); SASSERT(ps); - if (ps) ps->reset(); + if (ps) ps->reset(); } void solver_pool::refresh(solver* base_solver) { From e2e1411707e4a7968caffca511b6289b73a18939 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 10:42:25 -0700 Subject: [PATCH 194/364] Option to dump SMT queries as benchmarks during Spacer run --- src/muz/base/fixedpoint_params.pyg | 2 ++ src/muz/spacer/spacer_context.cpp | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 7b8f8fb72..9d3ee864d 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -203,4 +203,6 @@ def_module_params('fixedpoint', ('spacer.print_json', SYMBOL, '', 'print pobs tree in JSON format to a given file'), ('spacer.ctp', BOOL, False, 'enable counterexample-to-pushing technique'), ('spacer.use_inc_clause', BOOL, False, 'Use incremental clause to represent trans'), + ('spacer.dump_benchmarks', BOOL, False, 'Dump SMT queries as benchmarks'), + ('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'), )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 5f8951ecb..c9fa7f72b 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2342,13 +2342,12 @@ void context::init_global_smt_params() { } p.set_uint("random_seed", m_params.spacer_random_seed()); - // fparams.m_dump_benchmarks = m_params.spacer_vs_dump_benchmarks(); - // fparams.m_dump_min_time = m_params.spacer_vs_dump_min_time(); - // fparams.m_dump_recheck = m_params.spacer_vs_recheck(); + p.set_bool("dump_benchmarks", m_params.spacer_dump_benchmarks()); + p.set_double("dump_threshold", m_params.spacer_dump_threshold()); + // mbqi p.set_bool("mbqi", m_params.spacer_mbqi()); - if (!m_params.spacer_ground_cti()) { p.set_uint("phase_selection", PS_CACHING_CONSERVATIVE2); p.set_uint("restart_strategy", RS_GEOMETRIC); From 2a2b21326b5b29de8aea9671a873f0fda77bd16b Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 10:42:49 -0700 Subject: [PATCH 195/364] Stats on num_proxies in iuc_solver --- src/muz/spacer/spacer_iuc_solver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 2814fc249..1043dd939 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -209,6 +209,7 @@ void iuc_solver::collect_statistics (statistics &st) const { m_solver.collect_statistics (st); st.update ("time.iuc_solver.iuc_core", m_iuc_watch.get_seconds ()); + st.update("iuc_solver.num_proxies", m_proxies.size()); } void iuc_solver::reset_statistics () From fde58664f639ca2662cfb9b46ed9757efe14d0de Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 10:43:07 -0700 Subject: [PATCH 196/364] Moved mk_reach_fact to pred_transformer --- src/muz/spacer/spacer_context.cpp | 48 ++++++++++++------------------- src/muz/spacer/spacer_context.h | 7 +++-- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index c9fa7f72b..9d8e3d6af 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -688,6 +688,9 @@ void pred_transformer::collect_statistics(statistics& st) const // -- number of proof obligations (0 if pobs are not reused) st.update("SPACER num pobs", m_pobs.size()); + // -- number of reach facts created + st.update("SPACER num reach queries", m_stats.m_num_reach_queries); + st.update("SPACER num ctp blocked", m_stats.m_num_ctp_blocked); st.update("SPACER num is_invariant", m_stats.m_num_is_invariant); st.update("SPACER num lemma jumped", m_stats.m_num_lemma_level_jump); @@ -2345,9 +2348,9 @@ void context::init_global_smt_params() { p.set_bool("dump_benchmarks", m_params.spacer_dump_benchmarks()); p.set_double("dump_threshold", m_params.spacer_dump_threshold()); - // mbqi p.set_bool("mbqi", m_params.spacer_mbqi()); + if (!m_params.spacer_ground_cti()) { p.set_uint("phase_selection", PS_CACHING_CONSERVATIVE2); p.set_uint("restart_strategy", RS_GEOMETRIC); @@ -3018,7 +3021,7 @@ bool context::is_reachable(pob &n) mev.set_model(*model); // -- update must summary if (r && r->get_uninterpreted_tail_size () > 0) { - reach_fact_ref rf = mk_reach_fact (n, mev, *r); + reach_fact_ref rf = n.pt().mk_reach_fact (n, mev, *r); n.pt ().add_reach_fact (rf.get ()); } @@ -3158,7 +3161,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) if (is_concrete) { // -- update must summary if (r && r->get_uninterpreted_tail_size() > 0) { - reach_fact_ref rf = mk_reach_fact (n, mev, *r); + reach_fact_ref rf = n.pt().mk_reach_fact (n, mev, *r); checkpoint (); n.pt ().add_reach_fact (rf.get ()); checkpoint (); @@ -3373,50 +3376,49 @@ bool context::propagate(unsigned min_prop_lvl, return false; } -reach_fact *context::mk_reach_fact (pob& n, model_evaluator_util &mev, - const datalog::rule& r) +reach_fact *pred_transformer::mk_reach_fact (pob& n, model_evaluator_util &mev, + const datalog::rule& r) { + SASSERT(&n.pt() == this); timeit _timer1 (is_trace_enabled("spacer_timeit"), "mk_reach_fact", verbose_stream ()); expr_ref res(m); reach_fact_ref_vector child_reach_facts; - pred_transformer& pt = n.pt (); - ptr_vector preds; - pt.find_predecessors (r, preds); + find_predecessors (r, preds); expr_ref_vector path_cons (m); - path_cons.push_back (pt.get_transition (r)); + path_cons.push_back (get_transition (r)); app_ref_vector vars (m); for (unsigned i = 0; i < preds.size (); i++) { func_decl* pred = preds[i]; - pred_transformer& ch_pt = get_pred_transformer (pred); + pred_transformer& ch_pt = ctx.get_pred_transformer (pred); // get a reach fact of body preds used in the model expr_ref o_ch_reach (m); reach_fact *kid = ch_pt.get_used_origin_reach_fact (mev, i); child_reach_facts.push_back (kid); - m_pm.formula_n2o (kid->get (), o_ch_reach, i); + pm.formula_n2o (kid->get (), o_ch_reach, i); path_cons.push_back (o_ch_reach); // collect o-vars to eliminate for (unsigned j = 0; j < pred->get_arity (); j++) - { vars.push_back(m.mk_const(m_pm.o2o(ch_pt.sig(j), 0, i))); } + { vars.push_back(m.mk_const(pm.o2o(ch_pt.sig(j), 0, i))); } const ptr_vector &v = kid->aux_vars (); for (unsigned j = 0, sz = v.size (); j < sz; ++j) - { vars.push_back(m.mk_const(m_pm.n2o(v [j]->get_decl(), i))); } + { vars.push_back(m.mk_const(pm.n2o(v [j]->get_decl(), i))); } } // collect aux vars to eliminate - ptr_vector& aux_vars = pt.get_aux_vars (r); - bool elim_aux = get_params ().spacer_elim_aux (); + ptr_vector& aux_vars = get_aux_vars (r); + bool elim_aux = ctx.get_params().spacer_elim_aux(); if (elim_aux) { vars.append(aux_vars.size(), aux_vars.c_ptr()); } res = mk_and (path_cons); // -- pick an implicant from the path condition - if (get_params().spacer_reach_dnf()) { + if (ctx.get_params().spacer_reach_dnf()) { expr_ref_vector u(m), lits(m); u.push_back (res); compute_implicant_literals (mev, u, lits); @@ -3437,7 +3439,7 @@ reach_fact *context::mk_reach_fact (pob& n, model_evaluator_util &mev, timeit _timer1 (is_trace_enabled("spacer_timeit"), "mk_reach_fact::qe_project", verbose_stream ()); - qe_project (m, vars, res, mev.get_model (), false, m_use_native_mbp); + qe_project (m, vars, res, mev.get_model (), false, ctx.use_native_mbp()); } @@ -3601,8 +3603,6 @@ void context::collect_statistics(statistics& st) const // -- number of times a pob for some predicate transformer has // -- been created st.update("SPACER num queries", m_stats.m_num_queries); - // -- number of reach facts created - st.update("SPACER num reach queries", m_stats.m_num_reach_queries); // -- number of times a reach fact was true in some model st.update("SPACER num reuse reach facts", m_stats.m_num_reuse_reach); // -- maximum level at which any query was asked @@ -3641,16 +3641,6 @@ void context::collect_statistics(statistics& st) const for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) { m_lemma_generalizers[i]->collect_statistics(st); } - - // brunch out - verbose_stream () << "BRUNCH_STAT max_query_lvl " << m_stats.m_max_query_lvl << "\n"; - verbose_stream () << "BRUNCH_STAT num_queries " << m_stats.m_num_queries << "\n"; - verbose_stream () << "BRUNCH_STAT num_lemmas " << m_stats.m_num_lemmas << "\n"; - verbose_stream () << "BRUNCH_STAT num_reach_queries " << m_stats.m_num_reach_queries << "\n"; - verbose_stream () << "BRUNCH_STAT num_reach_reuse " << m_stats.m_num_reuse_reach << "\n"; - verbose_stream () << "BRUNCH_STAT inductive_lvl " << m_inductive_lvl << "\n"; - verbose_stream () << "BRUNCH_STAT max_depth " << m_stats.m_max_depth << "\n"; - verbose_stream () << "BRUNCH_STAT cex_depth " << m_stats.m_cex_depth << "\n"; } void context::reset_statistics() diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index c28f69d8b..9675fad09 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -192,6 +192,8 @@ class pred_transformer { unsigned m_num_ctp_blocked; // num of time ctp blocked lemma pushing unsigned m_num_is_invariant; // num of times lemmas are pushed unsigned m_num_lemma_level_jump; // lemma learned at higher level than expected + unsigned m_num_reach_queries; + stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; @@ -399,6 +401,8 @@ public: /// initialize reachability facts using initial rules void init_reach_facts (); + reach_fact *mk_reach_fact(pob &n, model_evaluator_util &mev, + const datalog::rule &r); void add_reach_fact (reach_fact *fact); // add reachability fact reach_fact* get_last_reach_fact () const { return m_reach_facts.back (); } expr* get_last_reach_case_var () const; @@ -761,7 +765,6 @@ class context { struct stats { unsigned m_num_queries; - unsigned m_num_reach_queries; unsigned m_num_reuse_reach; unsigned m_max_query_lvl; unsigned m_max_depth; @@ -816,8 +819,6 @@ class context { unsigned full_prop_lvl); bool is_reachable(pob &n); lbool expand_pob(pob &n, pob_ref_buffer &out); - reach_fact *mk_reach_fact(pob& n, model_evaluator_util &mev, - datalog::rule const& r); bool create_children(pob& n, const datalog::rule &r, model_evaluator_util &mdl, const vector& reach_pred_used, From 16fefe850a51de6d4007cc504a95fc05c5f7e353 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 11:16:05 -0700 Subject: [PATCH 197/364] Factored mbp into pred_transformer and added stats --- src/muz/spacer/spacer_context.cpp | 27 +++++++++++++++------------ src/muz/spacer/spacer_context.h | 6 ++++++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 9d8e3d6af..ba7a2cb9b 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -227,8 +227,6 @@ pob *derivation::create_next_child (model_evaluator_util &mev) expr_ref_vector summaries (m); app_ref_vector vars (m); - bool use_native_mbp = get_context ().use_native_mbp (); - bool ground = get_context ().use_ground_cti (); // -- find first may premise while (m_active < m_premises.size() && m_premises[m_active].is_must()) { summaries.push_back (m_premises[m_active].get_summary ()); @@ -246,7 +244,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) timeit _timer1 (is_trace_enabled("spacer_timeit"), "create_next_child::qproject1", verbose_stream ()); - qe_project (m, vars, m_trans, mev.get_model (), true, use_native_mbp, !ground); + pt().mbp(vars, m_trans, mev.get_model()); //qe::reduce_array_selects (*mev.get_model (), m_trans); // remember variables that need to be existentially quantified m_evars.append (vars); @@ -274,7 +272,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) timeit _timer2 (is_trace_enabled("spacer_timeit"), "create_next_child::qproject2", verbose_stream ()); - qe_project (m, vars, post, mev.get_model (), true, use_native_mbp, !ground); + pt().mbp(vars, post, mev.get_model()); //qe::reduce_array_selects (*mev.get_model (), post); // remember variables that need to be existentially quantified @@ -309,9 +307,6 @@ pob *derivation::create_next_child () { if (m_active + 1 >= m_premises.size()) { return nullptr; } - bool use_native_mbp = get_context ().use_native_mbp (); - bool ground = get_context ().use_ground_cti (); - // update the summary of the active node to some must summary // construct a new model consistent with the must summary of m_active premise @@ -375,8 +370,7 @@ pob *derivation::create_next_child () { vars.push_back(m.mk_const(pm.o2n(pt.sig(i), 0))); } if (!vars.empty ()) { - qe_project (m, vars, m_trans, mev.get_model (), true, use_native_mbp, - !ground); + this->pt().mbp(vars, m_trans, mev.get_model()); // keep track of implicitly quantified variables m_evars.append (vars); } @@ -701,6 +695,7 @@ void pred_transformer::collect_statistics(statistics& st) const st.update ("time.spacer.solve.pt.must_reachable", m_must_reachable_watch.get_seconds ()); st.update("time.spacer.ctp", m_ctp_watch.get_seconds()); + st.update("time.spacer.mbp", m_mbp_watch.get_seconds()); } void pred_transformer::reset_statistics() @@ -711,6 +706,7 @@ void pred_transformer::reset_statistics() m_initialize_watch.reset (); m_must_reachable_watch.reset (); m_ctp_watch.reset(); + m_mbp_watch.reset(); } void pred_transformer::init_sig() @@ -1255,6 +1251,14 @@ bool pred_transformer::is_qblocked (pob &n) { return res == l_false; } + +void pred_transformer::mbp(app_ref_vector &vars, expr_ref &fml, + const model_ref &mdl, bool reduce_all_selects) { + scoped_watch _t_(m_mbp_watch); + qe_project(m, vars, fml, mdl, reduce_all_selects, + use_native_mbp(), !ctx.use_ground_cti()); +} + // // check if predicate transformer has a satisfiable predecessor state. // returns either a satisfiable predecessor state or @@ -3439,7 +3443,7 @@ reach_fact *pred_transformer::mk_reach_fact (pob& n, model_evaluator_util &mev, timeit _timer1 (is_trace_enabled("spacer_timeit"), "mk_reach_fact::qe_project", verbose_stream ()); - qe_project (m, vars, res, mev.get_model (), false, ctx.use_native_mbp()); + mbp(vars, res, mev.get_model(), false /*, false */); } @@ -3517,8 +3521,7 @@ bool context::create_children(pob& n, datalog::rule const& r, n.get_skolems(vars); expr_ref phi1 = mk_and (Phi); - qe_project (m, vars, phi1, mev.get_model (), true, - m_use_native_mbp, !m_ground_cti); + n.pt().mbp(vars, phi1, mev.get_model ()); //qe::reduce_array_selects (*mev.get_model (), phi1); SASSERT (!m_ground_cti || vars.empty ()); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 9675fad09..4ece95489 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -310,6 +310,7 @@ class pred_transformer { stopwatch m_initialize_watch; stopwatch m_must_reachable_watch; stopwatch m_ctp_watch; + stopwatch m_mbp_watch; /// Auxiliary variables to represent different disjunctive @@ -458,6 +459,10 @@ public: /// \brief Returns true if the obligation is already blocked by current quantified lemmas bool is_qblocked (pob &n); + /// \brief interface to Model Based Projection + void mbp(app_ref_vector &vars, expr_ref &fml, const model_ref &mdl, + bool reduce_all_selects = true); + }; @@ -679,6 +684,7 @@ public: ast_manager &get_ast_manager () const {return m_parent.get_ast_manager ();} manager &get_manager () const {return m_parent.get_manager ();} context &get_context() const {return m_parent.get_context();} + pred_transformer &pt() const {return m_parent.pt();} }; From cdba0721e76c7f34216b428f456646d8ad3c80a2 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 11:27:52 -0700 Subject: [PATCH 198/364] Extra stats in iuc_solver --- src/muz/spacer/spacer_iuc_solver.cpp | 39 +++++++++++++++++++++------- src/muz/spacer/spacer_iuc_solver.h | 5 +++- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 1043dd939..d9caefb33 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -208,13 +208,20 @@ bool iuc_solver::is_proxy(expr *e, app_ref &def) void iuc_solver::collect_statistics (statistics &st) const { m_solver.collect_statistics (st); - st.update ("time.iuc_solver.iuc_core", m_iuc_watch.get_seconds ()); + st.update ("time.iuc_solver.get_iuc", m_iuc_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.hyp_reduce1", m_hyp_reduce1_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.hyp_reduce2", m_hyp_reduce2_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.learn_core", m_learn_core_sw.get_seconds()); + st.update("iuc_solver.num_proxies", m_proxies.size()); } void iuc_solver::reset_statistics () { - m_iuc_watch.reset (); + m_iuc_sw.reset(); + m_hyp_reduce1_sw.reset(); + m_hyp_reduce2_sw.reset(); + m_learn_core_sw.reset(); } void iuc_solver::get_unsat_core (ptr_vector &core) @@ -275,7 +282,7 @@ void iuc_solver::elim_proxies (expr_ref_vector &v) void iuc_solver::get_iuc(expr_ref_vector &core) { - scoped_watch _t_ (m_iuc_watch); + scoped_watch _t_ (m_iuc_sw); typedef obj_hashtable expr_set; expr_set core_lits; @@ -305,6 +312,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core) // -- old hypothesis reducer while the new one is broken if (m_old_hyp_reducer) { + scoped_watch _t_ (m_hyp_reduce1_sw); // AG: deprecated // pre-process proof in order to get a proof which is // better suited for unsat-core-extraction @@ -326,6 +334,8 @@ void iuc_solver::get_iuc(expr_ref_vector &core) // -- new hypothesis reducer else { + scoped_watch _t_ (m_hyp_reduce2_sw); + // pre-process proof for better iuc extraction if (m_print_farkas_stats) { iuc_proof iuc_before(m, res.get(), core_lits); @@ -333,11 +343,19 @@ void iuc_solver::get_iuc(expr_ref_vector &core) iuc_before.dump_farkas_stats(); } - theory_axiom_reducer ta_reducer(m); - proof_ref pr1(ta_reducer.reduce (res.get()), m); + proof_ref pr1(m); + { + scoped_watch _t_ (m_hyp_reduce1_sw); + theory_axiom_reducer ta_reducer(m); + pr1 = ta_reducer.reduce (res.get()); + } - hypothesis_reducer hyp_reducer(m); - proof_ref pr2(hyp_reducer.reduce(pr1), m); + proof_ref pr2(m); + { + scoped_watch _t_ (m_hyp_reduce2_sw); + hypothesis_reducer hyp_reducer(m); + pr2 = hyp_reducer.reduce(pr1); + } res = pr2; @@ -391,8 +409,11 @@ void iuc_solver::get_iuc(expr_ref_vector &core) UNREACHABLE(); } - // compute interpolating unsat core - learner.compute_unsat_core(core); + { + scoped_watch _t_ (m_learn_core_sw); + // compute interpolating unsat core + learner.compute_unsat_core(core); + } elim_proxies (core); // AG: this should be taken care of by minimizing the iuc cut diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index 72d995208..c3b16b2dd 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -54,7 +54,10 @@ private: unsigned m_first_assumption; bool m_is_proxied; - stopwatch m_iuc_watch; + stopwatch m_iuc_sw; + stopwatch m_hyp_reduce1_sw; + stopwatch m_hyp_reduce2_sw; + stopwatch m_learn_core_sw; expr_substitution m_elim_proxies_sub; bool m_split_literals; From 1e5423788054f77a5c91b05d77c0de6ab623a98f Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 11:29:39 -0700 Subject: [PATCH 199/364] mbp_array: Fix set_model_completion bug --- src/qe/qe_arrays.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index bcf642fb7..651c167b6 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -1147,6 +1147,7 @@ namespace qe { if (arr_vars.empty()) return; reset (); model_evaluator mev(mdl); + mev.set_model_completion(true); M = &mdl; m_mev = &mev; @@ -1169,7 +1170,6 @@ namespace qe { TRACE ("qe", tout << "Failed to project arrays\n";); } - mev.set_model_completion(true); // dealloc for (auto & kv : m_sel_terms) dealloc(kv.m_value); m_sel_terms.reset (); From 451d42319b80bf855c648813a7226356f9512243 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 14:26:21 -0700 Subject: [PATCH 200/364] Rename m_reach_ctx into m_reach_solver --- src/muz/spacer/spacer_context.cpp | 18 +++++++++--------- src/muz/spacer/spacer_context.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index ba7a2cb9b..0a5be7893 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -637,7 +637,7 @@ pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): pm(pm), m(pm.get_manager()), ctx(ctx), m_head(head, m), m_sig(m), m_solver(pm, ctx.get_params(), head->get_name()), - m_reach_ctx (pm.mk_solver2()), + m_reach_solver (pm.mk_solver2()), m_pobs(*this), m_frames(*this), m_reach_facts(), m_rf_init_sz(0), @@ -739,12 +739,12 @@ bool pred_transformer::is_must_reachable(expr* state, model_ref* model) // reachable using the init rule of the current transformer if (m_reach_facts.empty()) { return false; } - m_reach_ctx->push (); - m_reach_ctx->assert_expr (state); - m_reach_ctx->assert_expr (m.mk_not (m_reach_case_vars.back ())); - lbool res = m_reach_ctx->check_sat (0, nullptr); - if (model) { m_reach_ctx->get_model(*model); } - m_reach_ctx->pop (1); + m_reach_solver->push (); + m_reach_solver->assert_expr (state); + m_reach_solver->assert_expr (m.mk_not (m_reach_case_vars.back ())); + lbool res = m_reach_solver->check_sat (0, nullptr); + if (model) { m_reach_solver->get_model(*model); } + m_reach_solver->pop (1); return (res == l_true); } @@ -1011,7 +1011,7 @@ void pred_transformer::add_reach_fact (reach_fact *fact) if (fact->is_init()) {m_rf_init_sz++;} - // update m_reach_ctx + // update m_reach_solver expr_ref last_var (m); expr_ref new_var (m); expr_ref fml (m); @@ -1029,7 +1029,7 @@ void pred_transformer::add_reach_fact (reach_fact *fact) if (last_var) {fml = m.mk_or(m.mk_not(last_var), fact->get(), new_var);} else {fml = m.mk_or(fact->get(), new_var);} - m_reach_ctx->assert_expr (fml); + m_reach_solver->assert_expr (fml); TRACE ("spacer", tout << "updating reach ctx: " << mk_pp(fml, m) << "\n";); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 4ece95489..77836a389 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -291,7 +291,7 @@ class pred_transformer { ptr_vector m_use; // places where 'this' is referenced. ptr_vector m_rules; // rules used to derive transformer prop_solver m_solver; // solver context - solver* m_reach_ctx; // context for reachability facts + ref m_reach_solver; // context for reachability facts pobs m_pobs; // proof obligations created so far frames m_frames; // frames with lemmas reach_fact_ref_vector m_reach_facts; // reach facts From 0b387cd7ebc40490500aa94ef48a6363c9da0059 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 14:48:01 -0700 Subject: [PATCH 201/364] Moved pool_solvers from spacer::manager into spacer::context --- src/muz/spacer/spacer_context.cpp | 113 +++++++++++++++----------- src/muz/spacer/spacer_context.h | 16 +++- src/muz/spacer/spacer_manager.cpp | 13 +-- src/muz/spacer/spacer_manager.h | 29 +------ src/muz/spacer/spacer_prop_solver.cpp | 9 +- src/muz/spacer/spacer_prop_solver.h | 10 ++- 6 files changed, 94 insertions(+), 96 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 0a5be7893..b27bd724e 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -636,8 +636,8 @@ void lemma::mk_insts(expr_ref_vector &out, expr* e) pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): pm(pm), m(pm.get_manager()), ctx(ctx), m_head(head, m), - m_sig(m), m_solver(pm, ctx.get_params(), head->get_name()), - m_reach_solver (pm.mk_solver2()), + m_sig(m), + m_reach_solver (ctx.mk_solver2()), m_pobs(*this), m_frames(*this), m_reach_facts(), m_rf_init_sz(0), @@ -645,6 +645,8 @@ pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): m_all_init(false), m_reach_case_vars(m) { + m_solver = alloc(prop_solver, m, ctx.mk_solver0(), ctx.mk_solver1(), + ctx.get_params(), head->get_name()); init_sig (); app_ref v(m); std::stringstream name; @@ -670,7 +672,7 @@ std::ostream& pred_transformer::display(std::ostream& out) const void pred_transformer::collect_statistics(statistics& st) const { - m_solver.collect_statistics(st); + m_solver->collect_statistics(st); // -- number of times a lemma has been propagated to a higher level // -- during push @@ -700,7 +702,7 @@ void pred_transformer::collect_statistics(statistics& st) const void pred_transformer::reset_statistics() { - m_solver.reset_statistics(); + m_solver->reset_statistics(); //m_reachable.reset_statistics(); m_stats.reset(); m_initialize_watch.reset (); @@ -727,7 +729,7 @@ void pred_transformer::ensure_level(unsigned level) while (m_frames.size() <= level) { m_frames.add_frame (); - m_solver.add_level (); + m_solver->add_level (); } } @@ -913,10 +915,10 @@ void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) if (is_infty_level(lvl)) { m_stats.m_num_invariants++; } if (lemma->is_ground()) { - if (is_infty_level(lvl)) { m_solver.assert_expr(l); } + if (is_infty_level(lvl)) { m_solver->assert_expr(l); } else { ensure_level (lvl); - m_solver.assert_expr (l, lvl); + m_solver->assert_expr (l, lvl); } } @@ -963,10 +965,10 @@ void pred_transformer::add_lemma_from_child (pred_transformer& child, TRACE("spacer_detail", tout << "child property: " << mk_pp(inst.get (j), m) << "\n";); if (is_infty_level(lvl)) { - m_solver.assert_expr(inst.get(j)); + m_solver->assert_expr(inst.get(j)); } else { - m_solver.assert_expr(inst.get(j), lvl); + m_solver->assert_expr(inst.get(j), lvl); } } } @@ -1195,18 +1197,18 @@ void pred_transformer::propagate_to_infinity (unsigned level) bool pred_transformer::is_blocked (pob &n, unsigned &uses_level) { ensure_level (n.level ()); - prop_solver::scoped_level _sl (m_solver, n.level ()); - m_solver.set_core (nullptr); - m_solver.set_model (nullptr); + prop_solver::scoped_level _sl (*m_solver, n.level ()); + m_solver->set_core (nullptr); + m_solver->set_model (nullptr); expr_ref_vector post(m), _aux(m); post.push_back (n.post ()); // this only uses the lemmas at the current level // transition relation is irrelevant // XXX quic3: not all lemmas are asserted at the post-condition - lbool res = m_solver.check_assumptions (post, _aux, _aux, + lbool res = m_solver->check_assumptions (post, _aux, _aux, 0, nullptr, 0); - if (res == l_false) { uses_level = m_solver.uses_level(); } + if (res == l_false) { uses_level = m_solver->uses_level(); } return res == l_false; } @@ -1282,12 +1284,12 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, ensure_level(n.level()); // prepare the solver - prop_solver::scoped_level _sl(m_solver, n.level()); - prop_solver::scoped_subset_core _sc (m_solver, !n.use_farkas_generalizer ()); - prop_solver::scoped_weakness _sw(m_solver, 0, + prop_solver::scoped_level _sl(*m_solver, n.level()); + prop_solver::scoped_subset_core _sc (*m_solver, !n.use_farkas_generalizer ()); + prop_solver::scoped_weakness _sw(*m_solver, 0, ctx.weak_abs() ? n.weakness() : UINT_MAX); - m_solver.set_core(core); - m_solver.set_model(model); + m_solver->set_core(core); + m_solver->set_model(model); expr_ref_vector post (m), reach_assumps (m); post.push_back (n.post ()); @@ -1327,7 +1329,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, // result is either sat (with some reach assumps) or // unsat (even with no reach assumps) expr *bg = m_extend_lit.get (); - lbool is_sat = m_solver.check_assumptions (post, reach_assumps, + lbool is_sat = m_solver->check_assumptions (post, reach_assumps, m_transition_clause, 1, &bg, 0); TRACE ("spacer", @@ -1362,7 +1364,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, } } ); - uses_level = m_solver.uses_level(); + uses_level = m_solver->uses_level(); return l_false; } UNREACHABLE(); @@ -1426,22 +1428,22 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, conj.push_back(mk_not(m, lemma_expr)); flatten_and (conj); - prop_solver::scoped_level _sl(m_solver, level); - prop_solver::scoped_subset_core _sc (m_solver, true); - prop_solver::scoped_weakness _sw (m_solver, 1, + prop_solver::scoped_level _sl(*m_solver, level); + prop_solver::scoped_subset_core _sc (*m_solver, true); + prop_solver::scoped_weakness _sw (*m_solver, 1, ctx.weak_abs() ? lem->weakness() : UINT_MAX); model_ref mdl; model_ref *mdl_ref_ptr = nullptr; if (ctx.get_params().spacer_ctp()) {mdl_ref_ptr = &mdl;} - m_solver.set_core(core); - m_solver.set_model(mdl_ref_ptr); + m_solver->set_core(core); + m_solver->set_model(mdl_ref_ptr); expr * bg = m_extend_lit.get (); - lbool r = m_solver.check_assumptions (conj, aux, m_transition_clause, + lbool r = m_solver->check_assumptions (conj, aux, m_transition_clause, 1, &bg, 1); if (r == l_false) { - solver_level = m_solver.uses_level (); + solver_level = m_solver->uses_level (); lem->reset_ctp(); - if (level < m_solver.uses_level()) {m_stats.m_num_lemma_level_jump++;} + if (level < m_solver->uses_level()) {m_stats.m_num_lemma_level_jump++;} SASSERT (level <= solver_level); } else if (r == l_true) { @@ -1461,21 +1463,21 @@ bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, states = mk_and(state); states = m.mk_not(states); mk_assumptions(head(), states, conj); - prop_solver::scoped_level _sl(m_solver, level); - prop_solver::scoped_subset_core _sc (m_solver, true); - prop_solver::scoped_weakness _sw (m_solver, 1, + prop_solver::scoped_level _sl(*m_solver, level); + prop_solver::scoped_subset_core _sc (*m_solver, true); + prop_solver::scoped_weakness _sw (*m_solver, 1, ctx.weak_abs() ? weakness : UINT_MAX); - m_solver.set_core(&core); - m_solver.set_model (nullptr); + m_solver->set_core(&core); + m_solver->set_model (nullptr); expr_ref_vector aux (m); conj.push_back (m_extend_lit); - lbool res = m_solver.check_assumptions (state, aux, + lbool res = m_solver->check_assumptions (state, aux, m_transition_clause, conj.size (), conj.c_ptr (), 1); if (res == l_false) { state.reset(); state.append(core); - uses_level = m_solver.uses_level(); + uses_level = m_solver->uses_level(); } TRACE ("core_array_eq", tout << "check_inductive: " @@ -1518,8 +1520,8 @@ void pred_transformer::initialize(decl2rel const& pts) rw(m_transition); rw(m_init); - m_solver.assert_expr (m_transition); - m_solver.assert_expr (m_init, 0); + m_solver->assert_expr (m_transition); + m_solver->assert_expr (m_init, 0); TRACE("spacer", tout << "Initial state: " << mk_pp(m_init, m) << "\n"; tout << "Transition: " << mk_pp(m_transition, m) << "\n";); @@ -1790,7 +1792,7 @@ app* pred_transformer::extend_initial (expr *e) // -- extend the initial condition ic = m.mk_or (m_extend_lit, e, v); - m_solver.assert_expr (ic); + m_solver->assert_expr (ic); // -- remember the new extend literal m_extend_lit = m.mk_not (v); @@ -2076,7 +2078,7 @@ context::context(fixedpoint_params const& params, m_params(params), m(m), m_context(nullptr), - m_pm(params.pdr_max_num_contexts(), m), + m_pm(m), m_query_pred(m), m_query(nullptr), m_pob_queue(), @@ -2090,8 +2092,19 @@ context::context(fixedpoint_params const& params, m_weak_abs(params.spacer_weak_abs()), m_use_restarts(params.spacer_restarts()), m_restart_initial_threshold(params.spacer_restart_initial_threshold()), - m_json_marshaller(this) -{} + m_json_marshaller(this) { + ref pool0_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + ref pool1_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + ref pool2_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + + unsigned max_num_contexts = params.pdr_max_num_contexts(); + m_pool0 = alloc(solver_pool, pool0_base.get(), max_num_contexts); + m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); + m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); +} context::~context() { @@ -2370,9 +2383,9 @@ void context::init_global_smt_params() { // fparams.m_pi_use_database = true; } - m_pm.updt_params0(p); - m_pm.updt_params1(p); - m_pm.updt_params2(p); + m_pool0->updt_params(p); + m_pool1->updt_params(p); + m_pool2->updt_params(p); } void context::init_lemma_generalizers() { @@ -3598,6 +3611,10 @@ bool context::create_children(pob& n, datalog::rule const& r, void context::collect_statistics(statistics& st) const { + m_pool0->collect_statistics(st); + m_pool1->collect_statistics(st); + m_pool2->collect_statistics(st); + decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (it = m_rels.begin(); it != end; ++it) { it->m_value->collect_statistics(st); @@ -3639,7 +3656,6 @@ void context::collect_statistics(statistics& st) const st.update("spacer.random_seed", m_params.spacer_random_seed()); st.update("spacer.lemmas_imported", m_stats.m_num_lemmas_imported); st.update("spacer.lemmas_discarded", m_stats.m_num_lemmas_discarded); - m_pm.collect_statistics(st); for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) { m_lemma_generalizers[i]->collect_statistics(st); @@ -3648,12 +3664,15 @@ void context::collect_statistics(statistics& st) const void context::reset_statistics() { + m_pool0->reset_statistics(); + m_pool1->reset_statistics(); + m_pool2->reset_statistics(); + decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (it = m_rels.begin(); it != end; ++it) { it->m_value->reset_statistics(); } m_stats.reset(); - m_pm.reset_statistics(); for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) { m_lemma_generalizers[i]->reset_statistics(); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 77836a389..55e8cbafe 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -290,7 +290,7 @@ class pred_transformer { func_decl_ref_vector m_sig; // signature ptr_vector m_use; // places where 'this' is referenced. ptr_vector m_rules; // rules used to derive transformer - prop_solver m_solver; // solver context + scoped_ptr m_solver; // solver context ref m_reach_solver; // context for reachability facts pobs m_pobs; // proof obligations created so far frames m_frames; // frames with lemmas @@ -796,6 +796,13 @@ class context { ast_manager& m; datalog::context* m_context; manager m_pm; + + // three solver pools for different queries + scoped_ptr m_pool0; + scoped_ptr m_pool1; + scoped_ptr m_pool2; + + decl2rel m_rels; // Map from relation predicate to fp-operator. func_decl_ref m_query_pred; pred_transformer* m_query; @@ -928,6 +935,13 @@ public: void new_pob_eh(pob *p); bool is_inductive(); + + + // three different solvers with three different sets of parameters + // different solvers are used for different types of queries in spacer + solver* mk_solver0() {return m_pool0->mk_solver();} + solver* mk_solver1() {return m_pool1->mk_solver();} + solver* mk_solver2() {return m_pool2->mk_solver();} }; inline bool pred_transformer::use_native_mbp () {return ctx.use_native_mbp ();} diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index d55f74b90..719bf7862 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -176,19 +176,8 @@ static std::vector state_suffixes() { return res; } -manager::manager(unsigned max_num_contexts, ast_manager& manager) : +manager::manager(ast_manager& manager) : m(manager), m_mux(m, state_suffixes()) { - - ref pool0_base = - mk_smt_solver(m, params_ref::get_empty(), symbol::null); - ref pool1_base = - mk_smt_solver(m, params_ref::get_empty(), symbol::null); - ref pool2_base = - mk_smt_solver(m, params_ref::get_empty(), symbol::null); - - m_pool0 = alloc(solver_pool, pool0_base.get(), max_num_contexts); - m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); - m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); } diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index b8783369d..2149395ef 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -79,10 +79,6 @@ class manager { // manager of multiplexed names sym_mux m_mux; - // three solver pools for different queries - scoped_ptr m_pool0; - scoped_ptr m_pool1; - scoped_ptr m_pool2; unsigned n_index() const { return 0; } unsigned o_index(unsigned i) const { return i + 1; } @@ -90,7 +86,7 @@ class manager { void add_new_state(func_decl * s); public: - manager(unsigned max_num_contexts, ast_manager & manager); + manager(ast_manager & manager); ast_manager& get_manager() const { return m; } @@ -152,29 +148,6 @@ public: unsigned tgt_idx, bool homogenous = true) const {m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous);} - - // three different solvers with three different sets of parameters - // different solvers are used for different types of queries in spacer - solver* mk_solver0() {return m_pool0->mk_solver();} - void updt_params0(const params_ref &p) {m_pool0->updt_params(p);} - - solver* mk_solver1() {return m_pool1->mk_solver();} - void updt_params1(const params_ref &p) {m_pool1->updt_params(p);} - - solver* mk_solver2() {return m_pool2->mk_solver();} - void updt_params2(const params_ref &p) {m_pool2->updt_params(p);} - - void collect_statistics(statistics& st) const { - m_pool0->collect_statistics(st); - m_pool1->collect_statistics(st); - m_pool2->collect_statistics(st); - } - - void reset_statistics() { - m_pool0->reset_statistics(); - m_pool1->reset_statistics(); - m_pool2->reset_statistics(); - } }; /** Skolem constants for quantified spacer */ diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 7be4443e3..d8d7fec97 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -40,9 +40,10 @@ Revision History: namespace spacer { -prop_solver::prop_solver(spacer::manager& pm, +prop_solver::prop_solver(ast_manager &m, + solver *solver0, solver *solver1, fixedpoint_params const& p, symbol const& name) : - m(pm.get_manager()), + m(m), m_name(name), m_ctx(nullptr), m_pos_level_atoms(m), @@ -55,8 +56,8 @@ prop_solver::prop_solver(spacer::manager& pm, m_use_push_bg(p.spacer_keep_proxy()) { - m_solvers[0] = pm.mk_solver0(); - m_solvers[1] = pm.mk_solver1(); + m_solvers[0] = solver0; + m_solvers[1] = solver1; m_contexts[0] = alloc(spacer::iuc_solver, *(m_solvers[0]), p.spacer_iuc(), diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 337f24825..ecc838d1a 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -29,19 +29,21 @@ Revision History: #include "smt/smt_kernel.h" #include "util/util.h" #include "util/vector.h" -#include "muz/spacer/spacer_manager.h" +#include "solver/solver.h" #include "muz/spacer/spacer_iuc_solver.h" +#include "muz/spacer/spacer_util.h" struct fixedpoint_params; namespace spacer { +typedef ptr_vector decl_vector; class prop_solver { private: ast_manager& m; symbol m_name; - solver* m_solvers[2]; + ref m_solvers[2]; scoped_ptr m_contexts[2]; iuc_solver * m_ctx; decl_vector m_level_preds; @@ -73,7 +75,7 @@ private: public: - prop_solver(spacer::manager &manager, + prop_solver(ast_manager &m, solver *solver0, solver* solver1, fixedpoint_params const& p, symbol const& name); @@ -142,7 +144,7 @@ public: solver *sol; scoped_weakness(prop_solver &ps, unsigned solver_id, unsigned weakness) : sol(nullptr) { - sol = ps.m_solvers[solver_id == 0 ? 0 : 0 /* 1 */]; + sol = ps.m_solvers[solver_id == 0 ? 0 : 0 /* 1 */].get(); if (!sol) return; sol->push_params(); From ada548b5ae6aef150dc22d5a55a8d9baa8eaa4fe Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 15:18:36 -0700 Subject: [PATCH 202/364] Removed unused options --- src/muz/base/fixedpoint_params.pyg | 2 -- src/muz/spacer/spacer_context.cpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 9d3ee864d..69050ed71 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -153,7 +153,6 @@ def_module_params('fixedpoint', ('spacer.eager_reach_check', BOOL, True, 'SPACER: eagerly check if a query is reachable using reachability facts of predecessors'), ('spacer.use_lemma_as_cti', BOOL, False, 'SPACER: use a lemma instead of a CTI in flexible_trace'), ('spacer.reset_obligation_queue', BOOL, True, 'SPACER: reset obligation queue when entering a new level'), - ('spacer.init_reach_facts', BOOL, True, 'SPACER: initialize reachability facts with false'), ('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'), ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), @@ -161,7 +160,6 @@ def_module_params('fixedpoint', ('spacer.skip_propagate', BOOL, False, "Skip propagate/pushing phase. Turns PDR into a BMC that returns either reachable or unknown"), ('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"), ('spacer.elim_aux', BOOL, True, "Eliminate auxiliary variables in reachability facts"), - ('spacer.reach_as_init', BOOL, True, "Extend initial rules with computed reachability facts"), ('spacer.blast_term_ite', BOOL, True, "Expand non-Boolean ite-terms"), ('spacer.nondet_tie_break', BOOL, False, "Break ties in obligation queue non-deterministically"), ('spacer.reach_dnf', BOOL, True, "Restrict reachability facts to DNF"), diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index b27bd724e..2349ea03a 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1019,7 +1019,7 @@ void pred_transformer::add_reach_fact (reach_fact *fact) expr_ref fml (m); if (!m_reach_case_vars.empty()) {last_var = m_reach_case_vars.back();} - if (fact->is_init () || !ctx.get_params ().spacer_reach_as_init ()) + if (fact->is_init ()) {new_var = mk_fresh_reach_case_var();} else { new_var = extend_initial (fact->get ())->get_arg (0); @@ -1308,7 +1308,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, expr_ref a(m); pm.formula_n2o(pt.get_last_reach_case_var (), a, i); reach_assumps.push_back(m.mk_not (a)); - } else if (ctx.get_params().spacer_init_reach_facts()) { + } else { reach_assumps.push_back(m.mk_not (entry.m_key)); break; } From 7a8563a34cc8d0b500e7ac9c35ec5915d93acd6a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 17:06:01 -0700 Subject: [PATCH 203/364] spacer: cleaner management of rf tags --- src/muz/spacer/spacer_context.cpp | 124 ++++++++++++++---------------- src/muz/spacer/spacer_context.h | 24 +++--- 2 files changed, 70 insertions(+), 78 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 2349ea03a..5a6e1eec7 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -641,18 +641,24 @@ pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): m_pobs(*this), m_frames(*this), m_reach_facts(), m_rf_init_sz(0), - m_transition_clause(m), m_transition(m), m_init(m), m_extend_lit(m), - m_all_init(false), - m_reach_case_vars(m) + m_transition_clause(m), m_transition(m), m_init(m), + m_extend_lit0(m), m_extend_lit(m), + m_all_init(false) { m_solver = alloc(prop_solver, m, ctx.mk_solver0(), ctx.mk_solver1(), ctx.get_params(), head->get_name()); init_sig (); + + m_extend_lit = mk_extend_lit(); + m_extend_lit0 = m_extend_lit; +} + +app_ref pred_transformer::mk_extend_lit() { app_ref v(m); std::stringstream name; name << m_head->get_name () << "_ext0"; v = m.mk_const (symbol(name.str().c_str()), m.mk_bool_sort()); - m_extend_lit = m.mk_not (m.mk_const (pm.get_n_pred (v->get_decl ()))); + return app_ref(m.mk_not (m.mk_const (pm.get_n_pred (v->get_decl ()))), m); } pred_transformer::~pred_transformer() { @@ -743,7 +749,7 @@ bool pred_transformer::is_must_reachable(expr* state, model_ref* model) m_reach_solver->push (); m_reach_solver->assert_expr (state); - m_reach_solver->assert_expr (m.mk_not (m_reach_case_vars.back ())); + m_reach_solver->assert_expr (m.mk_not (m_reach_facts.back()->tag())); lbool res = m_reach_solver->check_sat (0, nullptr); if (model) { m_reach_solver->get_model(*model); } m_reach_solver->pop (1); @@ -754,39 +760,29 @@ bool pred_transformer::is_must_reachable(expr* state, model_ref* model) reach_fact* pred_transformer::get_used_reach_fact (model_evaluator_util& mev, - bool all) -{ + bool all) { expr_ref v (m); - for (unsigned i = all ? 0 : m_rf_init_sz, sz = m_reach_case_vars.size (); - i < sz; i++) { - VERIFY (mev.eval (m_reach_case_vars.get (i), v, false)); - if (m.is_false (v)) { - return m_reach_facts.get (i); - } + for (auto *rf : m_reach_facts) { + if (!all && rf->is_init()) continue; + VERIFY(mev.eval (rf->tag(), v, false)); + if (m.is_false(v)) return rf; } - - UNREACHABLE (); + UNREACHABLE(); return nullptr; } reach_fact *pred_transformer::get_used_origin_reach_fact (model_evaluator_util& mev, - unsigned oidx) -{ + unsigned oidx) { expr_ref b(m), v(m); - reach_fact *res = nullptr; - for (unsigned i = 0, sz = m_reach_case_vars.size (); i < sz; i++) { - pm.formula_n2o (m_reach_case_vars.get (i), v, oidx); + for (auto *rf : m_reach_facts) { + pm.formula_n2o (rf->tag(), v, oidx); VERIFY(mev.eval (v, b, false)); - - if (m.is_false (b)) { - res = m_reach_facts.get (i); - break; - } + if (m.is_false (b)) return rf; } - SASSERT (res); - return res; + UNREACHABLE(); + return nullptr; } const datalog::rule *pred_transformer::find_rule(model &model) { @@ -837,7 +833,7 @@ const datalog::rule *pred_transformer::find_rule(model &model, if (!pt.has_reach_facts()) {is_concrete = false;} else { expr_ref v(m); - pm.formula_n2o(pt.get_last_reach_case_var (), v, i); + pm.formula_n2o(pt.get_last_reach_tag (), v, i); model.eval(to_app (v.get ())->get_decl (), vl); used = m.is_false (vl); is_concrete = is_concrete && used; @@ -975,23 +971,18 @@ void pred_transformer::add_lemma_from_child (pred_transformer& child, } -expr* pred_transformer::mk_fresh_reach_case_var () +app_ref pred_transformer::mk_fresh_reach_tag () { std::stringstream name; func_decl_ref decl(m); - name << head ()->get_name () << "#reach_case_" << m_reach_case_vars.size (); + name << head ()->get_name () << "#reach_tag_" << m_reach_facts.size (); decl = m.mk_func_decl (symbol (name.str ().c_str ()), 0, (sort*const*)nullptr, m.mk_bool_sort ()); - m_reach_case_vars.push_back (m.mk_const (pm.get_n_pred (decl))); - return m_reach_case_vars.back (); + return app_ref(m.mk_const (pm.get_n_pred (decl)), m); } -expr* pred_transformer::get_reach_case_var (unsigned idx) const -{return m_reach_case_vars.get (idx);} - - -void pred_transformer::add_reach_fact (reach_fact *fact) +void pred_transformer::add_reach_fact (reach_fact *rf) { timeit _timer (is_trace_enabled("spacer_timeit"), "spacer::pred_transformer::add_reach_fact", @@ -999,46 +990,45 @@ void pred_transformer::add_reach_fact (reach_fact *fact) TRACE ("spacer", tout << "add_reach_fact: " << head()->get_name() << " " - << (fact->is_init () ? "INIT " : "") - << mk_pp(fact->get (), m) << "\n";); + << (rf->is_init () ? "INIT " : "") + << mk_pp(rf->get (), m) << "\n";); // -- avoid duplicates - if (fact == nullptr || get_reach_fact(fact->get())) {return;} + if (!rf || get_reach_fact(rf->get())) {return;} // all initial facts are grouped together - SASSERT (!fact->is_init () || m_reach_facts.empty () || + SASSERT (!rf->is_init () || m_reach_facts.empty () || m_reach_facts.back ()->is_init ()); - m_reach_facts.push_back (fact); - if (fact->is_init()) {m_rf_init_sz++;} + // create tags + app_ref last_tag(m); + app_ref new_tag(m); + expr_ref fml(m); + if (!m_reach_facts.empty()) {last_tag = m_reach_facts.back()->tag();} + if (rf->is_init ()) + new_tag = mk_fresh_reach_tag(); + else + // side-effect: updates m_solver with rf + new_tag = to_app(extend_initial(rf->get())->get_arg(0)); + rf->set_tag(new_tag); + + // add to m_reach_facts + m_reach_facts.push_back (rf); + if (rf->is_init()) {m_rf_init_sz++;} // update m_reach_solver - expr_ref last_var (m); - expr_ref new_var (m); - expr_ref fml (m); - - if (!m_reach_case_vars.empty()) {last_var = m_reach_case_vars.back();} - if (fact->is_init ()) - {new_var = mk_fresh_reach_case_var();} - else { - new_var = extend_initial (fact->get ())->get_arg (0); - m_reach_case_vars.push_back (new_var); - } - - SASSERT (m_reach_facts.size () == m_reach_case_vars.size ()); - - if (last_var) {fml = m.mk_or(m.mk_not(last_var), fact->get(), new_var);} - else {fml = m.mk_or(fact->get(), new_var);} - + if (last_tag) {fml = m.mk_or(m.mk_not(last_tag), rf->get(), rf->tag());} + else {fml = m.mk_or(rf->get(), rf->tag());} m_reach_solver->assert_expr (fml); - TRACE ("spacer", - tout << "updating reach ctx: " << mk_pp(fml, m) << "\n";); + TRACE ("spacer", tout << "updating reach ctx: " << fml << "\n";); - lemma lem(m, fml, infty_level()); + // update solvers of other pred_transformers + // XXX wrap rf into a lemma to fit the API + lemma fake_lemma(m, fml, infty_level()); // update users; reach facts are independent of levels for (auto use : m_use) - {use->add_lemma_from_child (*this, &lem, infty_level());} + use->add_lemma_from_child (*this, &fake_lemma, infty_level()); } expr_ref pred_transformer::get_reachable() @@ -1080,8 +1070,8 @@ expr_ref pred_transformer::get_reachable() return res; } -expr* pred_transformer::get_last_reach_case_var () const -{return m_reach_case_vars.empty () ? nullptr : m_reach_case_vars.back ();} +expr* pred_transformer::get_last_reach_tag () const +{return m_reach_facts.empty() ? nullptr : m_reach_facts.back()->tag();} expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) { @@ -1306,7 +1296,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, ctx.get_pred_transformer(m_predicates[i]); if (pt.has_reach_facts()) { expr_ref a(m); - pm.formula_n2o(pt.get_last_reach_case_var (), a, i); + pm.formula_n2o(pt.get_last_reach_tag(), a, i); reach_assumps.push_back(m.mk_not (a)); } else { reach_assumps.push_back(m.mk_not (entry.m_key)); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 55e8cbafe..154f8ea07 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -68,6 +68,9 @@ class reach_fact { const datalog::rule &m_rule; reach_fact_ref_vector m_justification; + // variable used to tag this reach fact in an incremental disjunction + app_ref m_tag; + bool m_init; public: @@ -75,10 +78,10 @@ public: expr* fact, const ptr_vector &aux_vars, bool init = false) : m_ref_count (0), m_fact (fact, m), m_aux_vars (aux_vars), - m_rule(rule), m_init (init) {} + m_rule(rule), m_tag(m), m_init (init) {} reach_fact (ast_manager &m, const datalog::rule &rule, expr* fact, bool init = false) : - m_ref_count (0), m_fact (fact, m), m_rule(rule), m_init (init) {} + m_ref_count (0), m_fact (fact, m), m_rule(rule), m_tag(m), m_init (init) {} bool is_init () {return m_init;} const datalog::rule& get_rule () {return m_rule;} @@ -89,6 +92,9 @@ public: expr *get () {return m_fact.get ();} const ptr_vector &aux_vars () {return m_aux_vars;} + app* tag() const {SASSERT(m_tag); return m_tag;} + void set_tag(app* tag) {m_tag = tag;} + void inc_ref () {++m_ref_count;} void dec_ref () { @@ -303,7 +309,8 @@ class pred_transformer { expr_ref_vector m_transition_clause; // extra clause for trans expr_ref m_transition; // transition relation expr_ref m_init; // initial condition - app_ref m_extend_lit; // literal to extend initial state + app_ref m_extend_lit0; // first literal used to extend initial state + app_ref m_extend_lit; // current literal to extend initial state bool m_all_init; // true if the pt has no uninterpreted body in any rule ptr_vector m_predicates; // temp vector used with find_predecessors() stats m_stats; @@ -312,13 +319,8 @@ class pred_transformer { stopwatch m_ctp_watch; stopwatch m_mbp_watch; - - /// Auxiliary variables to represent different disjunctive - /// cases of must summaries. Stored over 'n' (a.k.a. new) - /// versions of the variables - expr_ref_vector m_reach_case_vars; - void init_sig(); + app_ref mk_extend_lit(); void ensure_level(unsigned level); void add_lemma_core (lemma *lemma, bool ground_only = false); void add_lemma_from_child (pred_transformer &child, lemma *lemma, @@ -337,7 +339,7 @@ class pred_transformer { void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); - expr* mk_fresh_reach_case_var (); + app_ref mk_fresh_reach_tag (); public: pred_transformer(context& ctx, manager& pm, func_decl* head); @@ -406,7 +408,7 @@ public: const datalog::rule &r); void add_reach_fact (reach_fact *fact); // add reachability fact reach_fact* get_last_reach_fact () const { return m_reach_facts.back (); } - expr* get_last_reach_case_var () const; + expr* get_last_reach_tag () const; pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b){ From cfcc0846885abd832178c36a9d0cfe61e52f27b0 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 17:12:13 -0700 Subject: [PATCH 204/364] reach_fact --> rf --- src/muz/spacer/spacer_context.cpp | 58 +++++++++++++++---------------- src/muz/spacer/spacer_context.h | 20 +++++------ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 5a6e1eec7..c621757f3 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -335,7 +335,7 @@ pob *derivation::create_next_child () mev.set_model (*model); // find must summary used - reach_fact *rf = pt.get_used_reach_fact (mev, true); + reach_fact *rf = pt.get_used_rf (mev, true); // get an implicant of the summary expr_ref_vector u(m), lits (m); @@ -759,7 +759,7 @@ bool pred_transformer::is_must_reachable(expr* state, model_ref* model) -reach_fact* pred_transformer::get_used_reach_fact (model_evaluator_util& mev, +reach_fact* pred_transformer::get_used_rf (model_evaluator_util& mev, bool all) { expr_ref v (m); @@ -772,7 +772,7 @@ reach_fact* pred_transformer::get_used_reach_fact (model_evaluator_util& mev, return nullptr; } -reach_fact *pred_transformer::get_used_origin_reach_fact (model_evaluator_util& mev, +reach_fact *pred_transformer::get_used_origin_rf (model_evaluator_util& mev, unsigned oidx) { expr_ref b(m), v(m); @@ -830,10 +830,10 @@ const datalog::rule *pred_transformer::find_rule(model &model, bool used = false; func_decl* d = r->get_tail(i)->get_decl(); const pred_transformer &pt = ctx.get_pred_transformer(d); - if (!pt.has_reach_facts()) {is_concrete = false;} + if (!pt.has_rfs()) {is_concrete = false;} else { expr_ref v(m); - pm.formula_n2o(pt.get_last_reach_tag (), v, i); + pm.formula_n2o(pt.get_last_rf_tag (), v, i); model.eval(to_app (v.get ())->get_decl (), vl); used = m.is_false (vl); is_concrete = is_concrete && used; @@ -971,7 +971,7 @@ void pred_transformer::add_lemma_from_child (pred_transformer& child, } -app_ref pred_transformer::mk_fresh_reach_tag () +app_ref pred_transformer::mk_fresh_rf_tag () { std::stringstream name; func_decl_ref decl(m); @@ -982,19 +982,19 @@ app_ref pred_transformer::mk_fresh_reach_tag () return app_ref(m.mk_const (pm.get_n_pred (decl)), m); } -void pred_transformer::add_reach_fact (reach_fact *rf) +void pred_transformer::add_rf (reach_fact *rf) { timeit _timer (is_trace_enabled("spacer_timeit"), - "spacer::pred_transformer::add_reach_fact", + "spacer::pred_transformer::add_rf", verbose_stream ()); TRACE ("spacer", - tout << "add_reach_fact: " << head()->get_name() << " " + tout << "add_rf: " << head()->get_name() << " " << (rf->is_init () ? "INIT " : "") << mk_pp(rf->get (), m) << "\n";); // -- avoid duplicates - if (!rf || get_reach_fact(rf->get())) {return;} + if (!rf || get_rf(rf->get())) {return;} // all initial facts are grouped together SASSERT (!rf->is_init () || m_reach_facts.empty () || @@ -1007,7 +1007,7 @@ void pred_transformer::add_reach_fact (reach_fact *rf) if (!m_reach_facts.empty()) {last_tag = m_reach_facts.back()->tag();} if (rf->is_init ()) - new_tag = mk_fresh_reach_tag(); + new_tag = mk_fresh_rf_tag(); else // side-effect: updates m_solver with rf new_tag = to_app(extend_initial(rf->get())->get_arg(0)); @@ -1070,7 +1070,7 @@ expr_ref pred_transformer::get_reachable() return res; } -expr* pred_transformer::get_last_reach_tag () const +expr* pred_transformer::get_last_rf_tag () const {return m_reach_facts.empty() ? nullptr : m_reach_facts.back()->tag();} expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) @@ -1134,7 +1134,7 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, // -- no auxiliary variables in lemmas *aux = nullptr; } else { // find must summary to use - reach_fact *f = get_used_origin_reach_fact (mev, oidx); + reach_fact *f = get_used_origin_rf (mev, oidx); summary.push_back (f->get ()); *aux = &f->aux_vars (); } @@ -1294,9 +1294,9 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, for (unsigned i = 0; i < m_predicates.size(); i++) { const pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); - if (pt.has_reach_facts()) { + if (pt.has_rfs()) { expr_ref a(m); - pm.formula_n2o(pt.get_last_reach_tag(), a, i); + pm.formula_n2o(pt.get_last_rf_tag(), a, i); reach_assumps.push_back(m.mk_not (a)); } else { reach_assumps.push_back(m.mk_not (entry.m_key)); @@ -1521,7 +1521,7 @@ void pred_transformer::initialize(decl2rel const& pts) } -void pred_transformer::init_reach_facts () +void pred_transformer::init_rfs () { expr_ref_vector v(m); reach_fact_ref fact; @@ -1531,7 +1531,7 @@ void pred_transformer::init_reach_facts () if (r->get_uninterpreted_tail_size() == 0) { fact = alloc (reach_fact, m, *r, m_rule2transition.find(r), get_aux_vars(*r), true); - add_reach_fact(fact.get ()); + add_rf(fact.get ()); } } } @@ -2164,7 +2164,7 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) } // initialize reach facts - for (auto &entry : rels) {entry.m_value->init_reach_facts();} + for (auto &entry : rels) {entry.m_value->init_rfs();} } void context::inherit_lemmas(const decl2rel &rels) { @@ -2505,7 +2505,7 @@ unsigned context::get_cex_depth() pred_transformer* pt; // get and discard query rule - fact = m_query->get_last_reach_fact (); + fact = m_query->get_last_rf (); r = &fact->get_rule (); unsigned cex_depth = 0; @@ -2580,7 +2580,7 @@ void context::get_rules_along_trace(datalog::rule_ref_vector& rules) pred_transformer* pt; // get query rule - fact = m_query->get_last_reach_fact (); + fact = m_query->get_last_rf (); r = &fact->get_rule (); rules.push_back (const_cast (r)); TRACE ("spacer", @@ -2687,7 +2687,7 @@ expr_ref context::get_ground_sat_answer() datalog::rule const* r; // get and discard query rule - reach_fact = m_query->get_last_reach_fact (); + reach_fact = m_query->get_last_rf (); r = &reach_fact->get_rule (); // initialize queues @@ -3028,8 +3028,8 @@ bool context::is_reachable(pob &n) mev.set_model(*model); // -- update must summary if (r && r->get_uninterpreted_tail_size () > 0) { - reach_fact_ref rf = n.pt().mk_reach_fact (n, mev, *r); - n.pt ().add_reach_fact (rf.get ()); + reach_fact_ref rf = n.pt().mk_rf (n, mev, *r); + n.pt ().add_rf (rf.get ()); } // if n has a derivation, create a new child and report l_undef @@ -3168,9 +3168,9 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) if (is_concrete) { // -- update must summary if (r && r->get_uninterpreted_tail_size() > 0) { - reach_fact_ref rf = n.pt().mk_reach_fact (n, mev, *r); + reach_fact_ref rf = n.pt().mk_rf (n, mev, *r); checkpoint (); - n.pt ().add_reach_fact (rf.get ()); + n.pt ().add_rf (rf.get ()); checkpoint (); } @@ -3383,12 +3383,12 @@ bool context::propagate(unsigned min_prop_lvl, return false; } -reach_fact *pred_transformer::mk_reach_fact (pob& n, model_evaluator_util &mev, +reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, const datalog::rule& r) { SASSERT(&n.pt() == this); timeit _timer1 (is_trace_enabled("spacer_timeit"), - "mk_reach_fact", + "mk_rf", verbose_stream ()); expr_ref res(m); reach_fact_ref_vector child_reach_facts; @@ -3405,7 +3405,7 @@ reach_fact *pred_transformer::mk_reach_fact (pob& n, model_evaluator_util &mev, pred_transformer& ch_pt = ctx.get_pred_transformer (pred); // get a reach fact of body preds used in the model expr_ref o_ch_reach (m); - reach_fact *kid = ch_pt.get_used_origin_reach_fact (mev, i); + reach_fact *kid = ch_pt.get_used_origin_rf (mev, i); child_reach_facts.push_back (kid); pm.formula_n2o (kid->get (), o_ch_reach, i); path_cons.push_back (o_ch_reach); @@ -3444,7 +3444,7 @@ reach_fact *pred_transformer::mk_reach_fact (pob& n, model_evaluator_util &mev, { timeit _timer1 (is_trace_enabled("spacer_timeit"), - "mk_reach_fact::qe_project", + "mk_rf::qe_project", verbose_stream ()); mbp(vars, res, mev.get_model(), false /*, false */); } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 154f8ea07..924c5f9cc 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -339,14 +339,14 @@ class pred_transformer { void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); - app_ref mk_fresh_reach_tag (); + app_ref mk_fresh_rf_tag (); public: pred_transformer(context& ctx, manager& pm, func_decl* head); ~pred_transformer(); inline bool use_native_mbp (); - reach_fact *get_reach_fact (expr *v) { + reach_fact *get_rf (expr *v) { for (auto *rf : m_reach_facts) { if (v == rf->get()) {return rf;} } @@ -379,9 +379,9 @@ public: bool is_must_reachable(expr* state, model_ref* model = nullptr); /// \brief Returns reachability fact active in the given model /// all determines whether initial reachability facts are included as well - reach_fact *get_used_reach_fact(model_evaluator_util& mev, bool all = true); + reach_fact *get_used_rf(model_evaluator_util& mev, bool all = true); /// \brief Returns reachability fact active in the origin of the given model - reach_fact* get_used_origin_reach_fact(model_evaluator_util &mev, unsigned oidx); + reach_fact* get_used_origin_rf(model_evaluator_util &mev, unsigned oidx); expr_ref get_origin_summary(model_evaluator_util &mev, unsigned level, unsigned oidx, bool must, const ptr_vector **aux); @@ -400,15 +400,15 @@ public: bool add_lemma(expr * lemma, unsigned lvl); bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);} expr* get_reach_case_var (unsigned idx) const; - bool has_reach_facts () const { return !m_reach_facts.empty () ;} + bool has_rfs () const { return !m_reach_facts.empty () ;} /// initialize reachability facts using initial rules - void init_reach_facts (); - reach_fact *mk_reach_fact(pob &n, model_evaluator_util &mev, + void init_rfs (); + reach_fact *mk_rf(pob &n, model_evaluator_util &mev, const datalog::rule &r); - void add_reach_fact (reach_fact *fact); // add reachability fact - reach_fact* get_last_reach_fact () const { return m_reach_facts.back (); } - expr* get_last_reach_tag () const; + void add_rf (reach_fact *fact); // add reachability fact + reach_fact* get_last_rf () const { return m_reach_facts.back (); } + expr* get_last_rf_tag () const; pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b){ From 70f4674b3a6788acb5baf17a972b0888100f6617 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 18:25:18 -0700 Subject: [PATCH 205/364] Code to update solver with all constraints of a pred_transformer --- src/muz/spacer/spacer_context.cpp | 79 ++++++++++++++++++++++++++++++- src/muz/spacer/spacer_context.h | 27 +++++++---- 2 files changed, 96 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index c621757f3..55f5fa2a6 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -864,7 +864,7 @@ void pred_transformer::simplify_formulas() {m_frames.simplify_formulas ();} -expr_ref pred_transformer::get_formulas(unsigned level) +expr_ref pred_transformer::get_formulas(unsigned level) const { expr_ref_vector res(m); m_frames.get_frame_geq_lemmas (level, res); @@ -1791,6 +1791,83 @@ app* pred_transformer::extend_initial (expr *e) } +/// \brief Update a given solver with all constraints representing +/// this pred_transformer +void pred_transformer::updt_solver(prop_solver *solver) { + + m_solver->assert_expr(m_transition); + m_solver->assert_expr(m_init, 0); + + // -- facts derivable at the head + expr_ref last_tag(m); + last_tag = m_extend_lit0; + for (auto *rf : m_reach_facts) { + if (rf->is_init()) continue; + m_solver->assert_expr(m.mk_or(last_tag, rf->get(), rf->tag())); + last_tag = m.mk_not(rf->tag()); + } + + + for (auto &entry : m_tag2rule) { + const datalog::rule *r = entry.m_value; + if (!r) continue; + find_predecessors(*r, m_predicates); + if (m_predicates.empty()) continue; + + for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { + const pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); + // assert lemmas of pt + updt_solver_with_lemmas(solver, pt, to_app(entry.m_key), i); + // assert rfs of pt + update_solver_with_rfs(solver, pt, to_app(entry.m_key), i); + } + } +} + +void pred_transformer::updt_solver_with_lemmas(prop_solver *solver, + const pred_transformer &pt, + app* rule_tag, unsigned pos) { + expr_ref not_rule_tag(m); + not_rule_tag = m.mk_not(rule_tag); + + // XXX deal with quantified instantiations + // XXX perhaps expose lemmas, which gives better idea of what is active + expr_ref_vector fmls(m); + for (unsigned i = 0, sz = pt.get_num_levels(); i <= sz; ++i) { + expr_ref tmp(m); + if (i == sz) i = infty_level(); + tmp = pt.get_formulas(i); + flatten_and(tmp, fmls); + + for (expr *f : fmls) { + tmp = m.mk_or(not_rule_tag, f); + pm.formula_n2o(tmp, tmp, pos, !is_quantifier(f)); + m_solver->assert_expr(tmp, i); + } + } +} + +void pred_transformer::update_solver_with_rfs(prop_solver *solver, + const pred_transformer &pt, + app *rule_tag, unsigned pos) { + expr_ref last_tag(m); + expr_ref not_rule_tag(m); + not_rule_tag = m.mk_not(rule_tag); + + for (auto *rf : pt.m_reach_facts) { + expr_ref e(m); + if (last_tag) { + expr *args[4] = { m.mk_not(rule_tag), last_tag, rf->get(), rf->tag() }; + e = m.mk_or(4, args); + } + else + e = m.mk_or(m.mk_not(rule_tag), rf->get(), rf->tag()); + last_tag = m.mk_not(rf->tag()); + pm.formula_n2o(e.get(), e, pos); + solver->assert_expr(e); + } +} + /// pred_transformer::frames diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 924c5f9cc..d95f1185d 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -226,14 +226,14 @@ class pred_transformer { pred_transformer& pt () {return m_pt;} - void get_frame_lemmas (unsigned level, expr_ref_vector &out) { + void get_frame_lemmas (unsigned level, expr_ref_vector &out) const { for (auto &lemma : m_lemmas) { if (lemma->level() == level) { out.push_back(lemma->get_expr()); } } } - void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) { + void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) const { for (auto &lemma : m_lemmas) { if(lemma->level() >= level) { out.push_back(lemma->get_expr()); @@ -366,7 +366,7 @@ public: expr* transition() const {return m_transition;} expr* init() const {return m_init;} expr* rule2tag(datalog::rule const* r) {return m_rule2tag.find(r);} - unsigned get_num_levels() {return m_frames.size ();} + unsigned get_num_levels() const {return m_frames.size ();} expr_ref get_cover_delta(func_decl* p_orig, int level); void add_cover(unsigned level, expr* property); expr_ref get_reachable(); @@ -438,7 +438,7 @@ public: bool check_inductive(unsigned level, expr_ref_vector& state, unsigned& assumes_level, unsigned weakness = UINT_MAX); - expr_ref get_formulas(unsigned level); + expr_ref get_formulas(unsigned level) const; void simplify_formulas(); @@ -465,6 +465,15 @@ public: void mbp(app_ref_vector &vars, expr_ref &fml, const model_ref &mdl, bool reduce_all_selects = true); + void updt_solver(prop_solver *solver); + + void updt_solver_with_lemmas(prop_solver *solver, + const pred_transformer &pt, + app *rule_tag, unsigned pos); + void update_solver_with_rfs(prop_solver *solver, + const pred_transformer &pt, + app *rule_tag, unsigned pos); + }; @@ -566,8 +575,8 @@ public: const ptr_vector &lemmas() {return m_lemmas;} void add_lemma(lemma* new_lemma) {m_lemmas.push_back(new_lemma);} - bool is_ground () { return m_binding.empty (); } - unsigned get_free_vars_size() { return m_binding.size(); } + bool is_ground () const { return m_binding.empty (); } + unsigned get_free_vars_size() const { return m_binding.size(); } app_ref_vector const &get_binding() const {return m_binding;} /* * Returns a map from variable id to skolems that implicitly @@ -718,9 +727,9 @@ public: void set_root(pob& n); bool is_root (pob& n) const {return m_root.get () == &n;} - unsigned max_level() {return m_max_level;} - unsigned min_depth() {return m_min_depth;} - size_t size() {return m_obligations.size();} + unsigned max_level() const {return m_max_level;} + unsigned min_depth() const {return m_min_depth;} + size_t size() const {return m_obligations.size();} }; From 862eef5ec0dd3c3869564c1dbab06389c360ce6a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 18:33:20 -0700 Subject: [PATCH 206/364] Eliminate all existential variables from reach facts --- src/muz/spacer/spacer_context.cpp | 20 +++++++++++--------- src/muz/spacer/spacer_context.h | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 55f5fa2a6..a70c860f1 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -244,7 +244,8 @@ pob *derivation::create_next_child (model_evaluator_util &mev) timeit _timer1 (is_trace_enabled("spacer_timeit"), "create_next_child::qproject1", verbose_stream ()); - pt().mbp(vars, m_trans, mev.get_model()); + pt().mbp(vars, m_trans, mev.get_model(), + true, pt().get_context().use_ground_cti()); //qe::reduce_array_selects (*mev.get_model (), m_trans); // remember variables that need to be existentially quantified m_evars.append (vars); @@ -272,7 +273,8 @@ pob *derivation::create_next_child (model_evaluator_util &mev) timeit _timer2 (is_trace_enabled("spacer_timeit"), "create_next_child::qproject2", verbose_stream ()); - pt().mbp(vars, post, mev.get_model()); + pt().mbp(vars, post, mev.get_model(), + true, pt().get_context().use_ground_cti()); //qe::reduce_array_selects (*mev.get_model (), post); // remember variables that need to be existentially quantified @@ -370,7 +372,8 @@ pob *derivation::create_next_child () { vars.push_back(m.mk_const(pm.o2n(pt.sig(i), 0))); } if (!vars.empty ()) { - this->pt().mbp(vars, m_trans, mev.get_model()); + this->pt().mbp(vars, m_trans, mev.get_model(), + true, this->pt().get_context().use_ground_cti()); // keep track of implicitly quantified variables m_evars.append (vars); } @@ -1244,11 +1247,10 @@ bool pred_transformer::is_qblocked (pob &n) { } -void pred_transformer::mbp(app_ref_vector &vars, expr_ref &fml, - const model_ref &mdl, bool reduce_all_selects) { +void pred_transformer::mbp(app_ref_vector &vars, expr_ref &fml, const model_ref &mdl, + bool reduce_all_selects, bool force) { scoped_watch _t_(m_mbp_watch); - qe_project(m, vars, fml, mdl, reduce_all_selects, - use_native_mbp(), !ctx.use_ground_cti()); + qe_project(m, vars, fml, mdl, reduce_all_selects, use_native_mbp(), !force); } // @@ -3523,7 +3525,7 @@ reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, timeit _timer1 (is_trace_enabled("spacer_timeit"), "mk_rf::qe_project", verbose_stream ()); - mbp(vars, res, mev.get_model(), false /*, false */); + mbp(vars, res, mev.get_model(), false, true /* force or skolemize */); } @@ -3601,7 +3603,7 @@ bool context::create_children(pob& n, datalog::rule const& r, n.get_skolems(vars); expr_ref phi1 = mk_and (Phi); - n.pt().mbp(vars, phi1, mev.get_model ()); + n.pt().mbp(vars, phi1, mev.get_model (), true, use_ground_cti()); //qe::reduce_array_selects (*mev.get_model (), phi1); SASSERT (!m_ground_cti || vars.empty ()); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index d95f1185d..d546c3948 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -463,7 +463,7 @@ public: /// \brief interface to Model Based Projection void mbp(app_ref_vector &vars, expr_ref &fml, const model_ref &mdl, - bool reduce_all_selects = true); + bool reduce_all_selects, bool force = false); void updt_solver(prop_solver *solver); From 502e323678b27e0a00101837243f8eb00447580a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 31 May 2018 20:30:35 -0700 Subject: [PATCH 207/364] Fixes to pred_tranformer::updt_solver --- src/muz/spacer/spacer_context.cpp | 93 ++++++++++++++++++++------- src/muz/spacer/spacer_context.h | 3 +- src/muz/spacer/spacer_prop_solver.cpp | 2 + src/muz/spacer/spacer_prop_solver.h | 7 ++ 4 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index a70c860f1..bbedbc391 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1483,8 +1483,6 @@ void pred_transformer::mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result) { expr_ref tmp1(m), tmp2(m); - expr_substitution sub (m); - proof_ref pr (m.mk_asserted (m.mk_true ()), m); obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); for (; it != end; ++it) { @@ -1797,19 +1795,49 @@ app* pred_transformer::extend_initial (expr *e) /// this pred_transformer void pred_transformer::updt_solver(prop_solver *solver) { - m_solver->assert_expr(m_transition); - m_solver->assert_expr(m_init, 0); + solver->assert_expr(m_transition); + solver->assert_expr(m_init, 0); // -- facts derivable at the head expr_ref last_tag(m); last_tag = m_extend_lit0; for (auto *rf : m_reach_facts) { - if (rf->is_init()) continue; - m_solver->assert_expr(m.mk_or(last_tag, rf->get(), rf->tag())); + if (rf->is_init()) continue; // already in m_init + solver->assert_expr(m.mk_or(last_tag, rf->get(), rf->tag())); last_tag = m.mk_not(rf->tag()); } + SASSERT(last_tag == m_extend_lit); + // -- lemmas + app_ref_vector _unused(m); + expr_ref_vector fmls(m); + // -- assert lemmas + for (auto *u : m_frames.lemmas()) { + // instances + u->mk_insts(fmls); + // extra ground instance + if (!u->is_ground()) { + expr_ref gnd(m); + ground_expr(u->get_expr(), gnd, _unused); + fmls.push_back(gnd); + } + + // (quantified) lemma + if (u->is_ground() || get_context().use_qlemmas()) + fmls.push_back(u->get_expr()); + + // send to solver + if (is_infty_level(u->level())) + solver->assert_exprs(fmls); + else { + for (unsigned i = 0; i <= u->level(); ++i) + solver->assert_exprs(fmls, i); + } + fmls.reset(); + } + + // -- lemmas and rfs from other predicates for (auto &entry : m_tag2rule) { const datalog::rule *r = entry.m_value; if (!r) continue; @@ -1829,41 +1857,56 @@ void pred_transformer::updt_solver(prop_solver *solver) { void pred_transformer::updt_solver_with_lemmas(prop_solver *solver, const pred_transformer &pt, app* rule_tag, unsigned pos) { - expr_ref not_rule_tag(m); - not_rule_tag = m.mk_not(rule_tag); - - // XXX deal with quantified instantiations - // XXX perhaps expose lemmas, which gives better idea of what is active + app_ref_vector _unused(m); expr_ref_vector fmls(m); - for (unsigned i = 0, sz = pt.get_num_levels(); i <= sz; ++i) { - expr_ref tmp(m); - if (i == sz) i = infty_level(); - tmp = pt.get_formulas(i); - flatten_and(tmp, fmls); + for (auto *u : pt.m_frames.lemmas()) { + expr_ref e(m), gnd(m); + e = u->get_expr(); + pm.formula_n2o(e, e, pos); + u->mk_insts(fmls, e); - for (expr *f : fmls) { - tmp = m.mk_or(not_rule_tag, f); - pm.formula_n2o(tmp, tmp, pos, !is_quantifier(f)); - m_solver->assert_expr(tmp, i); + if (!u->is_ground()) { + // special ground instance + ground_expr(u->get_expr(), gnd, _unused); + pm.formula_n2o(gnd, gnd, pos); + fmls.push_back(gnd); } + + // quantified formula + if (u->is_ground() || get_context().use_qlemmas()) + fmls.push_back(e); + + // add tag + for (unsigned i = 0, sz = fmls.size(); i < sz; ++i) + fmls.set(i, m.mk_implies(rule_tag, fmls.get(i))); + + // send to solver + if (is_infty_level(u->level())) + solver->assert_exprs(fmls); + else { + for (unsigned i = 1, end = next_level(u->level()); i <= end; ++i) + solver->assert_exprs(fmls, i); + } + fmls.reset(); } } void pred_transformer::update_solver_with_rfs(prop_solver *solver, const pred_transformer &pt, app *rule_tag, unsigned pos) { - expr_ref last_tag(m); expr_ref not_rule_tag(m); not_rule_tag = m.mk_not(rule_tag); + expr_ref last_tag(m); for (auto *rf : pt.m_reach_facts) { expr_ref e(m); - if (last_tag) { - expr *args[4] = { m.mk_not(rule_tag), last_tag, rf->get(), rf->tag() }; + if (!last_tag) { + e = m.mk_or(m.mk_not(rule_tag), rf->get(), rf->tag()); + } + else { + expr *args[4] = { not_rule_tag, last_tag, rf->get(), rf->tag() }; e = m.mk_or(4, args); } - else - e = m.mk_or(m.mk_not(rule_tag), rf->get(), rf->tag()); last_tag = m.mk_not(rf->tag()); pm.formula_n2o(e.get(), e, pos); solver->assert_expr(e); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index d546c3948..120953e97 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -223,7 +223,8 @@ class pred_transformer { ~frames() {} void simplify_formulas (); - pred_transformer& pt () {return m_pt;} + pred_transformer& pt() const {return m_pt;} + const lemma_ref_vector &lemmas() const {return m_lemmas;} void get_frame_lemmas (unsigned level, expr_ref_vector &out) const { diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index d8d7fec97..1f33e2f12 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -125,6 +125,8 @@ void prop_solver::assert_expr(expr * form) void prop_solver::assert_expr(expr * form, unsigned level) { + if (is_infty_level(level)) {assert_expr(form);return;} + ensure_level(level); app * lev_atom = m_pos_level_atoms[level].get(); app_ref lform(m.mk_or(form, lev_atom), m); diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index ecc838d1a..c87277e16 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -93,6 +93,13 @@ public: void assert_expr(expr * form); void assert_expr(expr * form, unsigned level); + void assert_exprs(const expr_ref_vector &fmls) { + for (auto *f : fmls) assert_expr(f); + } + void assert_exprs(const expr_ref_vector &fmls, unsigned level) { + for (auto *f : fmls) assert_expr(f, level); + } + /** * check assumptions with a background formula */ From bfeb15b87689ede173d69fea5df3429cecb9bdf1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 1 Jun 2018 08:09:33 -0700 Subject: [PATCH 208/364] move to list of clauses Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_iuc_solver.cpp | 6 +- src/muz/spacer/spacer_iuc_solver.h | 2 +- src/muz/spacer/spacer_prop_solver.cpp | 18 +++--- src/muz/spacer/spacer_prop_solver.h | 4 +- src/smt/smt_context.cpp | 91 +++++++++++++++------------ src/smt/smt_context.h | 8 ++- src/smt/smt_kernel.cpp | 6 +- src/smt/smt_kernel.h | 2 +- src/smt/smt_solver.cpp | 4 +- src/solver/solver.h | 4 +- src/solver/solver_na2as.cpp | 6 +- src/solver/solver_na2as.h | 4 +- src/solver/solver_pool.cpp | 16 ++--- src/test/cube_clause.cpp | 16 +++-- 14 files changed, 104 insertions(+), 83 deletions(-) diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index d9caefb33..35932b2ba 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -128,8 +128,8 @@ lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions } lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, - const expr_ref_vector &clause) { - if (clause.empty()) {return check_sat(cube.size(), cube.c_ptr());} + vector const & clauses) { + if (clauses.empty()) {return check_sat(cube.size(), cube.c_ptr());} // -- remove any old assumptions if (m_assumptions.size() > m_first_assumption) @@ -144,7 +144,7 @@ lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, m_is_proxied = mk_proxies(m_assumptions, m_first_assumption); lbool res; - res = m_solver.check_sat_cc(m_assumptions, clause); + res = m_solver.check_sat_cc(m_assumptions, clauses); set_status (res); return res; } diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index c3b16b2dd..dcee54612 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -126,7 +126,7 @@ public: {return m_solver.get_scope_level();} lbool check_sat(unsigned num_assumptions, expr * const *assumptions) override; - lbool check_sat_cc(const expr_ref_vector &cube, const expr_ref_vector &clause) override; + lbool check_sat_cc(const expr_ref_vector &cube, vector const & clauses) override; void set_progress_callback(progress_callback *callback) override {m_solver.set_progress_callback(callback);} unsigned get_num_assertions() const override diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 1f33e2f12..4613cd089 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -202,7 +202,7 @@ lbool prop_solver::mss(expr_ref_vector &hard, expr_ref_vector &soft) { res = m_ctx->check_sat(j+1, hard.c_ptr()); if (res == l_false) { // -- flip non-true literal to be false - hard[j] = m.mk_not(hard.get(j)); + hard[j] = mk_not(m, hard.get(j)); } else if (res == l_true) { // -- get the model for the next iteration of the outer loop @@ -218,7 +218,7 @@ lbool prop_solver::mss(expr_ref_vector &hard, expr_ref_vector &soft) { } // move sat soft constraints to the output vector - for (unsigned k = i; k < j; ++k) {soft.push_back(hard.get(k));} + for (unsigned k = i; k < j; ++k) { soft.push_back(hard.get(k)); } // cleanup hard constraints hard.resize(hard_sz); return l_true; @@ -228,7 +228,7 @@ lbool prop_solver::mss(expr_ref_vector &hard, expr_ref_vector &soft) { /// Runs maxsat loop on m_ctx Returns l_false if hard is unsat, /// otherwise reduces soft such that hard & soft is sat. lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, - const expr_ref_vector &clause) + vector const & clauses) { // replace expressions by assumption literals iuc_solver::scoped_mk_proxy _p_(*m_ctx, hard); @@ -236,7 +236,7 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, // assume soft constraints are propositional literals (no need to proxy) hard.append(soft); - lbool res = m_ctx->check_sat_cc(hard, clause); + lbool res = m_ctx->check_sat_cc(hard, clauses); // if hard constraints alone are unsat or there are no soft // constraints, we are done if (res != l_false || soft.empty()) { return res; } @@ -270,7 +270,7 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, } // check that the NEW constraints became sat - res = m_ctx->check_sat_cc(hard, clause); + res = m_ctx->check_sat_cc(hard, clauses); if (res != l_false) { break; } // still unsat, update the core and repeat core.reset(); @@ -290,7 +290,7 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, lbool prop_solver::internal_check_assumptions(expr_ref_vector &hard_atoms, expr_ref_vector &soft_atoms, - const expr_ref_vector &clause) + vector const & clauses) { // XXX Turn model generation if m_model != 0 SASSERT(m_ctx); @@ -302,7 +302,7 @@ lbool prop_solver::internal_check_assumptions(expr_ref_vector &hard_atoms, } if (m_in_level) { assert_level_atoms(m_current_level); } - lbool result = maxsmt(hard_atoms, soft_atoms, clause); + lbool result = maxsmt(hard_atoms, soft_atoms, clauses); if (result != l_false && m_model) { m_ctx->get_model(*m_model); } SASSERT(result != l_false || soft_atoms.empty()); @@ -375,7 +375,9 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, unsigned soft_sz = soft.size(); (void) soft_sz; - lbool res = internal_check_assumptions(hard, soft, clause); + vector clauses; + clauses.push_back(clause); + lbool res = internal_check_assumptions(hard, soft, clauses); if (!m_use_push_bg) { m_ctx->pop(1); } TRACE("psolve_verbose", diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index c87277e16..042215eff 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -67,10 +67,10 @@ private: lbool internal_check_assumptions(expr_ref_vector &hard, expr_ref_vector &soft, - const expr_ref_vector &clause); + vector const & clause); lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, - const expr_ref_vector &clause); + vector const & clauses); lbool mss(expr_ref_vector &hard, expr_ref_vector &soft); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index c6f77cf1f..0680d7bcf 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -61,7 +61,6 @@ namespace smt { m_dyn_ack_manager(*this, p), m_is_diseq_tmp(nullptr), m_units_to_reassert(m_manager), - m_clause(nullptr), m_qhead(0), m_simp_qhead(0), m_simp_counter(0), @@ -1815,7 +1814,7 @@ namespace smt { */ bool context::decide() { - if (at_search_level() && !m_clause_lits.empty()) { + if (at_search_level() && !m_tmp_clauses.empty()) { switch (decide_clause()) { case l_true: // already satisfied break; @@ -2919,8 +2918,7 @@ namespace smt { del_clauses(m_aux_clauses, 0); del_clauses(m_lemmas, 0); del_justifications(m_justifications, 0); - if (m_clause) del_clause(m_clause); - m_clause = nullptr; + reset_tmp_clauses(); if (m_is_diseq_tmp) { m_is_diseq_tmp->del_eh(m_manager, false); m_manager.dec_ref(m_is_diseq_tmp->get_owner()); @@ -3135,48 +3133,62 @@ namespace smt { return true; } - void context::init_clause(expr_ref_vector const& clause) { - if (m_clause) del_clause(m_clause); - m_clause = nullptr; - m_clause_lits.reset(); - for (expr* lit : clause) { + void context::init_clause(expr_ref_vector const& _clause) { + literal_vector lits; + for (expr* lit : _clause) { internalize_formula(lit, true); mark_as_relevant(lit); - m_clause_lits.push_back(get_literal(lit)); + lits.push_back(get_literal(lit)); } - if (m_clause_lits.size() >= 2) { + clause* clausep = nullptr; + if (lits.size() >= 2) { justification* js = nullptr; if (m_manager.proofs_enabled()) { - proof * pr = mk_clause_def_axiom(m_clause_lits.size(), m_clause_lits.c_ptr(), nullptr); + proof * pr = mk_clause_def_axiom(lits.size(), lits.c_ptr(), nullptr); js = mk_justification(justification_proof_wrapper(*this, pr)); } - m_clause = clause::mk(m_manager, m_clause_lits.size(), m_clause_lits.c_ptr(), CLS_AUX, js); + clausep = clause::mk(m_manager, lits.size(), lits.c_ptr(), CLS_AUX, js); } + m_tmp_clauses.push_back(std::make_pair(clausep, lits)); + } + + void context::reset_tmp_clauses() { + for (auto& p : m_tmp_clauses) { + if (p.first) del_clause(p.first); + } + m_tmp_clauses.reset(); } lbool context::decide_clause() { - if (m_clause_lits.empty()) return l_true; - shuffle(m_clause_lits.size(), m_clause_lits.c_ptr(), m_random); - for (literal l : m_clause_lits) { - switch (get_assignment(l)) { - case l_false: - break; - case l_true: - return l_true; - default: - push_scope(); - assign(l, b_justification::mk_axiom(), true); - return l_undef; - } + if (m_tmp_clauses.empty()) return l_true; + for (auto & tmp_clause : m_tmp_clauses) { + literal_vector& lits = tmp_clause.second; + for (literal l : lits) { + switch (get_assignment(l)) { + case l_false: + break; + case l_true: + goto next_clause; + default: + shuffle(lits.size(), lits.c_ptr(), m_random); + push_scope(); + assign(l, b_justification::mk_axiom(), true); + return l_undef; + } + } + + if (lits.size() == 1) { + set_conflict(b_justification(), ~lits[0]); + } + else { + set_conflict(b_justification(tmp_clause.first), null_literal); + } + VERIFY(!resolve_conflict()); + return l_false; + next_clause: + ; } - if (m_clause_lits.size() == 1) { - set_conflict(b_justification(), ~m_clause_lits[0]); - } - else { - set_conflict(b_justification(m_clause), null_literal); - } - VERIFY(!resolve_conflict()); - return l_false; + return l_true; } void context::init_assumptions(expr_ref_vector const& asms) { @@ -3277,10 +3289,7 @@ namespace smt { m_last_search_failure = MEMOUT; return false; } - - if (m_clause) del_clause(m_clause); - m_clause = nullptr; - m_clause_lits.reset(); + reset_tmp_clauses(); m_unsat_core.reset(); m_stats.m_num_checks++; pop_to_base_lvl(); @@ -3387,17 +3396,17 @@ namespace smt { return r; } - lbool context::check(expr_ref_vector const& cube, expr_ref_vector const& clause) { + lbool context::check(expr_ref_vector const& cube, vector const& clauses) { if (!check_preamble(true)) return l_undef; TRACE("before_search", display(tout);); setup_context(false); expr_ref_vector asms(cube); add_theory_assumptions(asms); if (!validate_assumptions(asms)) return l_undef; - if (!validate_assumptions(clause)) return l_undef; + for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; internalize_assertions(); init_assumptions(asms); - init_clause(clause); + for (auto const& clause : clauses) init_clause(clause); lbool r = search(); r = mk_unsat_core(r); r = check_finalize(r); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 3110ea3c1..16c1c7ac3 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -168,8 +168,8 @@ namespace smt { expr_ref_vector m_units_to_reassert; svector m_units_to_reassert_sign; literal_vector m_assigned_literals; - clause* m_clause; - literal_vector m_clause_lits; + typedef std::pair tmp_clause; + vector m_tmp_clauses; unsigned m_qhead; unsigned m_simp_qhead; int m_simp_counter; //!< can become negative @@ -1114,6 +1114,8 @@ namespace smt { lbool decide_clause(); + void reset_tmp_clauses(); + void reset_assumptions(); void reset_clause(); @@ -1505,7 +1507,7 @@ namespace smt { lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr, bool reset_cancel = true, bool already_did_theory_assumptions = false); - lbool check(expr_ref_vector const& cube, expr_ref_vector const& clause); + lbool check(expr_ref_vector const& cube, vector const& clauses); lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed); diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index 1438efaf6..b03604b5b 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -115,7 +115,7 @@ namespace smt { return m_kernel.check(num_assumptions, assumptions); } - lbool check(expr_ref_vector const& cube, expr_ref_vector const& clause) { + lbool check(expr_ref_vector const& cube, vector const& clause) { return m_kernel.check(cube, clause); } @@ -291,8 +291,8 @@ namespace smt { return r; } - lbool kernel::check(expr_ref_vector const& cube, expr_ref_vector const& clause) { - return m_imp->check(cube, clause); + lbool kernel::check(expr_ref_vector const& cube, vector const& clauses) { + return m_imp->check(cube, clauses); } diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index 4174422f4..d78f71e20 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -132,7 +132,7 @@ namespace smt { lbool check(app_ref_vector const& asms) { return check(asms.size(), (expr* const*)asms.c_ptr()); } - lbool check(expr_ref_vector const& cube, expr_ref_vector const& clause); + lbool check(expr_ref_vector const& cube, vector const& clauses); /** \brief extract consequences among variables. diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index d715e4879..1e49f7f87 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -191,8 +191,8 @@ namespace smt { } - lbool check_sat_cc_core(expr_ref_vector const& cube, expr_ref_vector const& clause) override { - return m_context.check(cube, clause); + lbool check_sat_cc_core(expr_ref_vector const& cube, vector const& clauses) override { + return m_context.check(cube, clauses); } struct scoped_minimize_core { diff --git a/src/solver/solver.h b/src/solver/solver.h index 3d9befdbc..c4df362e4 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -152,8 +152,8 @@ public: The cube corresponds to auxiliary assumptions. The clause as an auxiliary disjunction that is also assumed for the check. */ - virtual lbool check_sat_cc(expr_ref_vector const& cube, expr_ref_vector const& clause) { - if (clause.empty()) return check_sat(cube.size(), cube.c_ptr()); + virtual lbool check_sat_cc(expr_ref_vector const& cube, vector const& clauses) { + if (clauses.empty()) return check_sat(cube.size(), cube.c_ptr()); NOT_IMPLEMENTED_YET(); } diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index ac241b4a2..db745597c 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -67,10 +67,10 @@ lbool solver_na2as::check_sat(unsigned num_assumptions, expr * const * assumptio return check_sat_core(m_assumptions.size(), m_assumptions.c_ptr()); } -lbool solver_na2as::check_sat_cc(const expr_ref_vector &assumptions, const expr_ref_vector &clause) { - if (clause.empty()) return check_sat(assumptions.size(), assumptions.c_ptr()); +lbool solver_na2as::check_sat_cc(const expr_ref_vector &assumptions, vector const &clauses) { + if (clauses.empty()) return check_sat(assumptions.size(), assumptions.c_ptr()); append_assumptions app(m_assumptions, assumptions.size(), assumptions.c_ptr()); - return check_sat_cc_core(m_assumptions, clause); + return check_sat_cc_core(m_assumptions, clauses); } lbool solver_na2as::get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index a3ed85a9a..d1515a206 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -39,7 +39,7 @@ public: // Subclasses of solver_na2as should redefine the following *_core methods instead of these ones. lbool check_sat(unsigned num_assumptions, expr * const * assumptions) override; - lbool check_sat_cc(const expr_ref_vector &assumptions, const expr_ref_vector &clause) override; + lbool check_sat_cc(const expr_ref_vector &assumptions, vector const &clauses) override; void push() override; void pop(unsigned n) override; unsigned get_scope_level() const override; @@ -50,7 +50,7 @@ public: lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override; protected: virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) = 0; - virtual lbool check_sat_cc_core(const expr_ref_vector &assumptions, const expr_ref_vector &clause) {NOT_IMPLEMENTED_YET();} + virtual lbool check_sat_cc_core(const expr_ref_vector &assumptions, vector const &clauses) { NOT_IMPLEMENTED_YET(); } virtual void push_core() = 0; virtual void pop_core(unsigned n) = 0; }; diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index a015c5ea2..1d0fd0c11 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -144,14 +144,14 @@ public: if (m_dump_benchmarks && sw.get_seconds() >= m_dump_threshold) { expr_ref_vector cube(m, num_assumptions, assumptions); - expr_ref_vector clause(m); - dump_benchmark(cube, clause, res, sw.get_seconds()); + vector clauses; + dump_benchmark(cube, clauses, res, sw.get_seconds()); } return res; } - lbool check_sat_cc_core(const expr_ref_vector &cube, - const expr_ref_vector &clause) override { + lbool check_sat_cc_core(expr_ref_vector const & cube, + vector const & clauses) override { SASSERT(!m_pushed || get_scope_level() > 0); m_proof.reset(); scoped_watch _t_(m_pool.m_check_watch); @@ -160,7 +160,7 @@ public: stopwatch sw; sw.start(); internalize_assertions(); - lbool res = m_base->check_sat_cc(cube, clause); + lbool res = m_base->check_sat_cc(cube, clauses); sw.stop(); switch (res) { case l_true: @@ -177,7 +177,7 @@ public: set_status(res); if (m_dump_benchmarks && sw.get_seconds() >= m_dump_threshold) { - dump_benchmark(cube, clause, res, sw.get_seconds()); + dump_benchmark(cube, clauses, res, sw.get_seconds()); } return res; } @@ -265,7 +265,7 @@ public: private: - void dump_benchmark(const expr_ref_vector &cube, const expr_ref_vector &clause, + void dump_benchmark(const expr_ref_vector &cube, vector const & clauses, lbool last_status, double last_time) { std::string file_name = mk_file_name(); std::ofstream out(file_name); @@ -276,7 +276,7 @@ private: out << "(set-info :status " << lbool2status(last_status) << ")\n"; m_base->display(out, cube.size(), cube.c_ptr()); - if (!clause.empty()) { + for (auto const& clause : clauses) { out << ";; extra clause\n"; out << "(assert (or "; for (auto *lit : clause) out << mk_pp(lit, m) << " "; diff --git a/src/test/cube_clause.cpp b/src/test/cube_clause.cpp index f625789e4..0c783277b 100644 --- a/src/test/cube_clause.cpp +++ b/src/test/cube_clause.cpp @@ -35,24 +35,32 @@ void tst_cube_clause() { r = solver->check_sat(cube); std::cout << r << "\n"; clause.push_back(b); - r = solver->check_sat(cube, clause); + vector clauses; + clauses.push_back(clause); + r = solver->check_sat_cc(cube, clauses); std::cout << r << "\n"; core.reset(); solver->get_unsat_core(core); std::cout << core << "\n"; clause.push_back(d); - r = solver->check_sat(cube, clause); + clauses.reset(); + clauses.push_back(clause); + r = solver->check_sat_cc(cube, clauses); std::cout << r << "\n"; core.reset(); solver->get_unsat_core(core); std::cout << core << "\n"; clause.push_back(f); - r = solver->check_sat(cube, clause); + clauses.reset(); + clauses.push_back(clause); + r = solver->check_sat_cc(cube, clauses); std::cout << r << "\n"; core.reset(); solver->get_unsat_core(core); std::cout << core << "\n"; clause.push_back(g); - r = solver->check_sat(cube, clause); + clauses.reset(); + clauses.push_back(clause); + r = solver->check_sat_cc(cube, clauses); std::cout << r << "\n"; } From aa77a918cd49e7d332cfa1cc33040e2fc42aba38 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 1 Jun 2018 10:48:47 -0700 Subject: [PATCH 209/364] Optimizing qe_lite --- src/ast/rewriter/rewriter.h | 2 ++ src/ast/rewriter/rewriter_def.h | 11 ++++++++ src/ast/rewriter/var_subst.cpp | 4 +++ src/qe/qe_lite.cpp | 45 ++++++++++++++++++--------------- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index 8c00f541b..c761b5ca3 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -346,6 +346,8 @@ public: void set_bindings(unsigned num_bindings, expr * const * bindings); void set_inv_bindings(unsigned num_bindings, expr * const * bindings); + void update_binding_at(unsigned i, expr* binding); + void update_inv_binding_at(unsigned i, expr* binding); void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * t, expr_ref & result) { operator()(t, result, m_pr); } void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) { diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index 4d4c4f708..e8a14b953 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -639,6 +639,17 @@ void rewriter_tpl::set_inv_bindings(unsigned num_bindings, expr * const TRACE("rewriter", display_bindings(tout);); } +template +void rewriter_tpl::update_inv_binding_at(unsigned i, expr* binding) { + m_bindings[i] = binding; +} + +template +void rewriter_tpl::update_binding_at(unsigned i, expr* binding) { + m_bindings[m_bindings.size() - i - 1] = binding; +} + + template template void rewriter_tpl::main_loop(expr * t, expr_ref & result, proof_ref & result_pr) { diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index 97e0ddb19..7877cf1d2 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -24,6 +24,10 @@ Notes: #include "ast/for_each_expr.h" void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result) { + if (is_ground(n)) { + result = n; + return; + } SASSERT(is_well_sorted(result.m(), n)); m_reducer.reset(); if (m_std_order) diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 715848f89..c027d2d07 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -40,14 +40,16 @@ Revision History: namespace eq { bool occurs_var(unsigned idx, expr* e) { + if (is_ground(e)) return false; ptr_buffer todo; - todo.push_back(e); + todo.push_back(e); ast_mark mark; while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) continue; mark.mark(e, true); + if (is_ground(e)) continue; if (is_var(e)) { if (to_var(e)->get_idx() == idx) return true; } @@ -67,7 +69,8 @@ namespace eq { arith_util a; datatype_util dt; is_variable_proc* m_is_variable; - var_subst m_subst; + beta_reducer m_subst; + expr_ref_vector m_subst_map; expr_ref_vector m_new_exprs; ptr_vector m_map; @@ -75,7 +78,6 @@ namespace eq { int_vector m_var2pos; ptr_vector m_inx2var; unsigned_vector m_order; - expr_ref_vector m_subst_map; expr_ref_buffer m_new_args; th_rewriter m_rewriter; params_ref m_params; @@ -149,7 +151,7 @@ namespace eq { done.mark(t); } } - done.mark(t); + done.mark(t); todo.pop_back(); break; case AST_QUANTIFIER: @@ -426,26 +428,29 @@ namespace eq { TRACE("qe_lite", tout << "Elimination m_order:" << std::endl; - for(unsigned i=0; iget_num_patterns(); j++) { expr_ref new_pat(m); - m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat); + m_subst(q->get_pattern(j), new_pat); new_patterns.push_back(new_pat); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { expr_ref new_nopat(m); - m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat); + m_subst(q->get_no_pattern(j), new_nopat); new_no_patterns.push_back(new_nopat); } @@ -748,7 +753,7 @@ namespace eq { expr_ref r(m), new_r(m); r = m.mk_and(conjs.size(), conjs.c_ptr()); create_substitution(largest_vinx + 1); - m_subst(r, m_subst_map.size(), m_subst_map.c_ptr(), new_r); + m_subst(r, new_r); m_rewriter(new_r); conjs.reset(); flatten_and(new_r, conjs); @@ -776,8 +781,8 @@ namespace eq { dt(m), m_is_variable(nullptr), m_subst(m), - m_new_exprs(m), m_subst_map(m), + m_new_exprs(m), m_new_args(m), m_rewriter(m), m_params(p) {} From 6464468cd89d9ebedfbf147f768d7a393c4d97d7 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 2 Jun 2018 22:57:22 -0700 Subject: [PATCH 210/364] Remove dead code --- src/muz/spacer/spacer_manager.h | 41 +--- src/muz/spacer/spacer_sym_mux.cpp | 391 +----------------------------- src/muz/spacer/spacer_sym_mux.h | 107 +------- 3 files changed, 21 insertions(+), 518 deletions(-) diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index 2149395ef..a73a9a63b 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -96,35 +96,6 @@ public: func_decl * get_o_pred(func_decl * s, unsigned idx); func_decl * get_n_pred(func_decl * s); - void get_o_index(func_decl* p, unsigned& idx) const { - m_mux.try_get_index(p, idx); - SASSERT(idx != n_index()); - idx--; // m_mux has indices starting at 1 - } - - bool is_o(func_decl * p, unsigned idx) const - {return m_mux.has_index(p, o_index(idx));} - bool is_o(func_decl * p) const { - unsigned idx; - return m_mux.try_get_index(p, idx) && idx != n_index(); - } - bool is_o(expr* e) const - {return is_app(e) && is_o(to_app(e)->get_decl());} - bool is_o(expr* e, unsigned idx) const - {return is_app(e) && is_o(to_app(e)->get_decl(), idx);} - bool is_n(func_decl * p) const - {return m_mux.has_index(p, n_index());} - bool is_n(expr* e) const - {return is_app(e) && is_n(to_app(e)->get_decl());} - - - /** true if f doesn't contain any n predicates */ - bool is_o_formula(expr * f) const - {return !m_mux.contains(f, n_index());} - /** true if f contains only o state preds of index o_idx */ - bool is_o_formula(expr * f, unsigned o_idx) const - {return m_mux.is_homogenous_formula(f, o_index(o_idx));} - /** true if f doesn't contain any o predicates */ bool is_n_formula(expr * f) const {return m_mux.is_homogenous_formula(f, n_index());} @@ -135,18 +106,22 @@ public: func_decl * n2o(func_decl * p, unsigned o_idx) const {return m_mux.conv(p, n_index(), o_index(o_idx));} - void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const + void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, + bool homogenous = true) const {m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous);} - void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const + void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, + bool homogenous = true) const {m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous);} void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const - {m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous);} + {m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), + result, homogenous);} void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous = true) const - {m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous);} + {m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), + tgt, homogenous);} }; diff --git a/src/muz/spacer/spacer_sym_mux.cpp b/src/muz/spacer/spacer_sym_mux.cpp index ee1d3726d..9426ddcf7 100644 --- a/src/muz/spacer/spacer_sym_mux.cpp +++ b/src/muz/spacer/spacer_sym_mux.cpp @@ -82,7 +82,6 @@ void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * dom m_prim2all.insert(tuple[0], tuple); m_prefix2prim.insert(prefix, tuple[0]); m_prim2prefix.insert(tuple[0], prefix); - m_prim_preds.push_back(tuple[0]); m_ref_holder.push_back(prefix); } @@ -122,32 +121,7 @@ func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) c } -func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, - unsigned arity, sort * const * domain, sort * range) -{ - func_decl * prim = try_get_primary_by_prefix(prefix); - if (prim) { - SASSERT(prim->get_arity() == arity); - SASSERT(prim->get_range() == range); - //domain should match as well, but we won't bother checking an array equality - return conv(prim, 0, idx); - } - - decl_vector syms; - create_tuple(prefix, arity, domain, range, idx + 1, syms); - return syms[idx]; -} - -bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const -{ - if (!is_app(e)) { return false; } - app * a = to_app(e); - if (m.is_not(a) && is_app(a->get_arg(0))) { - a = to_app(a->get_arg(0)); - } - return is_muxed(a->get_decl()); -} struct sym_mux::formula_checker { @@ -198,155 +172,15 @@ private: bool m_found_what_needed; }; -bool sym_mux::contains(expr * e, unsigned idx) const -{ - formula_checker chck(*this, false, idx); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return chck.some_with_idx(); -} bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const { + expr_mark visited; formula_checker chck(*this, true, idx); - for_each_expr(chck, m_visited, e); - m_visited.reset(); + for_each_expr(chck, visited, e); return chck.all_have_idx(); } -bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const -{ - expr * const * begin = vect.c_ptr(); - expr * const * end = begin + vect.size(); - for (expr * const * it = begin; it != end; it++) { - if (!is_homogenous_formula(*it, idx)) { - return false; - } - } - return true; -} - -class sym_mux::index_collector { - sym_mux const& m_parent; - svector m_indices; -public: - index_collector(sym_mux const& s): - m_parent(s) {} - - void operator()(expr * e) - { - if (is_app(e)) { - func_decl * sym = to_app(e)->get_decl(); - unsigned idx; - if (m_parent.try_get_index(sym, idx)) { - SASSERT(idx > 0); - --idx; - if (m_indices.size() <= idx) { - m_indices.resize(idx + 1, false); - } - m_indices[idx] = true; - } - } - } - - void extract(unsigned_vector& indices) - { - for (unsigned i = 0; i < m_indices.size(); ++i) { - if (m_indices[i]) { - indices.push_back(i); - } - } - } -}; - - - -void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const -{ - indices.reset(); - index_collector collector(*this); - for_each_expr(collector, m_visited, e); - m_visited.reset(); - collector.extract(indices); -} - -class sym_mux::variable_collector { - sym_mux const& m_parent; - vector >& m_vars; -public: - variable_collector(sym_mux const& s, vector >& vars): - m_parent(s), m_vars(vars) {} - - void operator()(expr * e) - { - if (is_app(e)) { - func_decl * sym = to_app(e)->get_decl(); - unsigned idx; - if (m_parent.try_get_index(sym, idx)) { - SASSERT(idx > 0); - --idx; - if (m_vars.size() <= idx) { - m_vars.resize(idx + 1, ptr_vector()); - } - m_vars[idx].push_back(to_app(e)); - } - } - } -}; - -void sym_mux::collect_variables(expr* e, vector >& vars) const -{ - vars.reset(); - variable_collector collector(*this, vars); - for_each_expr(collector, m_visited, e); - m_visited.reset(); -} - -class sym_mux::hmg_checker { - const sym_mux & m_parent; - - bool m_found_idx; - unsigned m_idx; - bool m_multiple_indexes; - -public: - hmg_checker(const sym_mux & parent) : - m_parent(parent), m_found_idx(false), m_multiple_indexes(false) - { - } - - void operator()(expr * e) - { - if (m_multiple_indexes || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - unsigned sym_idx; - if (!m_parent.try_get_index(sym, sym_idx)) { return; } - - if (!m_found_idx) { - m_found_idx = true; - m_idx = sym_idx; - return; - } - if (m_idx == sym_idx) { return; } - m_multiple_indexes = true; - } - - bool has_multiple_indexes() const - { - return m_multiple_indexes; - } -}; - -bool sym_mux::is_homogenous_formula(expr * e) const -{ - hmg_checker chck(*this); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return !chck.has_multiple_indexes(); -} - - struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg { private: ast_manager & m; @@ -379,8 +213,8 @@ public: } }; -void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) const -{ +void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, + expr_ref & res, bool homogenous) const { if (src_idx == tgt_idx) { res = f; return; @@ -389,220 +223,3 @@ void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_re rewriter_tpl rwr(m, false, r_cfg); rwr(f, res); } - -struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg { -private: - ast_manager & m; - const sym_mux & m_parent; - int m_shift; -public: - shifting_rewriter_cfg(const sym_mux & parent, int shift) - : m(parent.get_manager()), - m_parent(parent), - m_shift(shift) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) - { - if (!is_app(s)) { return false; } - app * a = to_app(s); - func_decl * sym = a->get_decl(); - - unsigned idx; - if (!m_parent.try_get_index(sym, idx)) { - return false; - } - SASSERT(static_cast(idx) + m_shift >= 0); - func_decl * tgt = m_parent.conv(sym, idx, idx + m_shift); - t = m.mk_app(tgt, a->get_args()); - return true; - } -}; - -void sym_mux::shift_formula(expr * f, int dist, expr_ref & res) const -{ - if (dist == 0) { - res = f; - return; - } - shifting_rewriter_cfg r_cfg(*this, dist); - rewriter_tpl rwr(m, false, r_cfg); - rwr(f, res); -} - -void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, - expr_ref_vector & res) const -{ - res.reset(); - expr * const * begin = vect.c_ptr(); - expr * const * end = begin + vect.size(); - for (expr * const * it = begin; it != end; it++) { - expr_ref converted(m); - conv_formula(*it, src_idx, tgt_idx, converted); - res.push_back(converted); - } -} - -void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const -{ - unsigned i = 0; - while (i < vect.size()) { - expr* e = vect[i].get(); - if (contains(e, idx) && is_homogenous_formula(e, idx)) { - i++; - } else { - //we don't allow mixing states inside vector elements - SASSERT(!contains(e, idx)); - vect[i] = vect.back(); - vect.pop_back(); - } - } -} - -void sym_mux::partition_o_idx( - expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, unsigned idx) const -{ - - for (unsigned i = 0; i < lits.size(); ++i) { - if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) { - o_lits.push_back(lits[i]); - } else { - other.push_back(lits[i]); - } - } -} - - - -class sym_mux::nonmodel_sym_checker { - const sym_mux & m_parent; - - bool m_found; -public: - nonmodel_sym_checker(const sym_mux & parent) : - m_parent(parent), m_found(false) - { - } - - void operator()(expr * e) - { - if (m_found || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - - if (m_parent.is_non_model_sym(sym)) { - m_found = true; - } - } - - bool found() const - { - return m_found; - } -}; - -bool sym_mux::has_nonmodel_symbol(expr * e) const -{ - nonmodel_sym_checker chck(*this); - for_each_expr(chck, e); - return chck.found(); -} - -void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const -{ - unsigned i = 0; - while (i < vect.size()) { - if (!has_nonmodel_symbol(vect[i].get())) { - i++; - continue; - } - vect[i] = vect.back(); - vect.pop_back(); - } -} - -class sym_mux::decl_idx_comparator { - const sym_mux & m_parent; -public: - decl_idx_comparator(const sym_mux & parent) - : m_parent(parent) - { } - - bool operator()(func_decl * sym1, func_decl * sym2) - { - unsigned idx1, idx2; - if (!m_parent.try_get_index(sym1, idx1)) { idx1 = UINT_MAX; } - if (!m_parent.try_get_index(sym2, idx2)) { idx2 = UINT_MAX; } - - if (idx1 != idx2) { return idx1 < idx2; } - return lt(sym1->get_name(), sym2->get_name()); - } -}; - -std::string sym_mux::pp_model(const model_core & mdl) const -{ - decl_vector consts; - unsigned sz = mdl.get_num_constants(); - for (unsigned i = 0; i < sz; i++) { - func_decl * d = mdl.get_constant(i); - consts.push_back(d); - } - - std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this)); - - std::stringstream res; - - decl_vector::iterator end = consts.end(); - for (decl_vector::iterator it = consts.begin(); it != end; it++) { - func_decl * d = *it; - std::string name = d->get_name().str(); - const char * arrow = " -> "; - res << name << arrow; - unsigned indent = static_cast(name.length() + strlen(arrow)); - res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n"; - - if (it + 1 != end) { - unsigned idx1, idx2; - if (!try_get_index(*it, idx1)) { idx1 = UINT_MAX; } - if (!try_get_index(*(it + 1), idx2)) { idx2 = UINT_MAX; } - if (idx1 != idx2) { res << "\n"; } - } - } - return res.str(); -} - - -#if 0 - -class sym_mux::index_renamer_cfg : public default_rewriter_cfg { - const sym_mux & m_parent; - unsigned m_idx; - -public: - index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) - { - if (!is_app(s)) { return false; } - app * a = to_app(s); - if (a->get_family_id() != null_family_id) { - return false; - } - func_decl * sym = a->get_decl(); - unsigned idx; - if (!m_parent.try_get_index(sym, idx)) { - return false; - } - if (m_idx == idx) { - return false; - } - ast_manager& m = m_parent.get_manager(); - symbol name = symbol((sym->get_name().str() + "!").c_str()); - func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range()); - t = m.mk_app(tgt, a->get_num_args(), a->get_args()); - return true; - } -}; - -#endif diff --git a/src/muz/spacer/spacer_sym_mux.h b/src/muz/spacer/spacer_sym_mux.h index a4d10971a..97e3459d7 100644 --- a/src/muz/spacer/spacer_sym_mux.h +++ b/src/muz/spacer/spacer_sym_mux.h @@ -7,7 +7,8 @@ Module Name: Abstract: - A symbol multiplexer that helps with having multiple versions of each of a set of symbols. + A symbol multiplexer that helps with having multiple versions of + each of a set of symbols. Author: @@ -20,18 +21,17 @@ Revision History: #ifndef _SYM_MUX_H_ #define _SYM_MUX_H_ +#include +#include + #include "ast/ast.h" #include "util/map.h" #include "util/vector.h" -#include class model_core; namespace spacer { class sym_mux { -public: - typedef ptr_vector app_vector; - typedef ptr_vector decl_vector; private: typedef obj_map sym2u; typedef obj_map sym2dv; @@ -41,7 +41,6 @@ private: ast_manager & m; mutable ast_ref_vector m_ref_holder; - mutable expr_mark m_visited; mutable unsigned m_next_sym_suffix_idx; mutable symbols m_used_suffixes; @@ -75,24 +74,15 @@ private: */ sym2sym m_prim2prefix; - decl_vector m_prim_preds; - obj_hashtable m_non_model_syms; struct formula_checker; struct conv_rewriter_cfg; - struct shifting_rewriter_cfg; - class decl_idx_comparator; - class hmg_checker; - class nonmodel_sym_checker; - class index_renamer_cfg; - class index_collector; - class variable_collector; std::string get_suffix(unsigned i) const; void ensure_tuple_size(func_decl * prim, unsigned sz) const; - expr_ref isolate_o_idx(expr* e, unsigned idx) const; + // expr_ref isolate_o_idx(expr* e, unsigned idx) const; public: sym_mux(ast_manager & m, const std::vector & suffixes); @@ -101,9 +91,7 @@ public: bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); } bool try_get_index(func_decl * sym, unsigned & idx) const - { - return m_sym2idx.find(sym, idx); - } + {return m_sym2idx.find(sym, idx);} bool has_index(func_decl * sym, unsigned idx) const { @@ -143,30 +131,6 @@ public: return conv(prim, 0, idx); } - /** - Marks symbol as non-model which means it will not appear in models collected by - get_muxed_cube_from_model function. - This is to take care of auxiliary symbols introduced by the disjunction relations - to relativize lemmas coming from disjuncts. - */ - void mark_as_non_model(func_decl * sym) - { - SASSERT(is_muxed(sym)); - m_non_model_syms.insert(get_primary(sym)); - } - - func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, - unsigned arity, sort * const * domain, sort * range); - - - - bool is_muxed_lit(expr * e, unsigned idx) const; - - bool is_non_model_sym(func_decl * s) const - { - return is_muxed(s) && m_non_model_syms.contains(get_primary(s)); - } - /** Create a multiplexed tuple of propositional constants. Symbols may be suplied in the tuple vector, @@ -181,27 +145,7 @@ public: Return true if the only multiplexed symbols which e contains are of index idx. */ bool is_homogenous_formula(expr * e, unsigned idx) const; - bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const; - /** - Return true if all multiplexed symbols which e contains are of one index. - */ - bool is_homogenous_formula(expr * e) const; - - /** - Return true if expression e contains a muxed symbol of index idx. - */ - bool contains(expr * e, unsigned idx) const; - - /** - Collect indices used in expression. - */ - void collect_indices(expr* e, unsigned_vector& indices) const; - - /** - Collect used variables of each index. - */ - void collect_variables(expr* e, vector >& vars) const; /** Convert symbol sym which has to be of src_idx variant into variant tgt_idx. @@ -213,43 +157,10 @@ public: Convert src_idx symbols in formula f variant into tgt_idx. If homogenous is true, formula cannot contain symbols of other variants. */ - void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous = true) const; - void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, - expr_ref_vector & res) const; + void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, + expr_ref & res, bool homogenous = true) const; - /** - Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift - symbol index to a negative value. - */ - void shift_formula(expr * f, int dist, expr_ref & res) const; - /** - Remove from vect literals (atoms or negations of atoms) of symbols - that contain multiplexed symbols with indexes other than idx. - - Each of the literals can contain only symbols multiplexed with one index - (this trivially holds if the literals are propositional). - - Order of elements in vect may be modified by this function - */ - void filter_idx(expr_ref_vector & vect, unsigned idx) const; - - /** - Partition literals into o_literals and others. - */ - void partition_o_idx(expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, unsigned idx) const; - - bool has_nonmodel_symbol(expr * e) const; - void filter_non_model_lits(expr_ref_vector & vect) const; - - func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); } - func_decl * const * end_prim_preds() const { return m_prim_preds.end(); } - - void get_muxed_cube_from_model(const model_core & model, expr_ref_vector & res) const; - - std::string pp_model(const model_core & mdl) const; }; } From e0e435582a95a08238e6680ae3d4b9244205871e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 2 Jun 2018 23:23:56 -0700 Subject: [PATCH 211/364] Minor code cleanup --- src/muz/spacer/spacer_sym_mux.cpp | 63 ++++++++++++++----------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/src/muz/spacer/spacer_sym_mux.cpp b/src/muz/spacer/spacer_sym_mux.cpp index 9426ddcf7..776776188 100644 --- a/src/muz/spacer/spacer_sym_mux.cpp +++ b/src/muz/spacer/spacer_sym_mux.cpp @@ -55,7 +55,12 @@ std::string sym_mux::get_suffix(unsigned i) const return m_suffixes[i]; } -void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, +/** + populates a vector called tuple with func_decl's + tuple[0] is called primary and is used as key in various maps + */ +void sym_mux::create_tuple(func_decl* prefix, unsigned arity, + sort * const * domain, sort * range, unsigned tuple_length, decl_vector & tuple) { SASSERT(tuple_length > 0); @@ -63,16 +68,18 @@ void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * dom tuple.push_back(0); } SASSERT(tuple.size() == tuple_length); - std::string pre = prefix->get_name().str(); for (unsigned i = 0; i < tuple_length; i++) { if (tuple[i] != 0) { SASSERT(tuple[i]->get_arity() == arity); SASSERT(tuple[i]->get_range() == range); - //domain should match as well, but we won't bother checking an array equality + //domain should match as well, but we won't bother + //checking an array equality } else { - std::string name = pre + get_suffix(i); - tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range); + std::string name = prefix->get_name().str(); + name += get_suffix(i); + tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, + domain, range); } m_ref_holder.push_back(tuple[i]); m_sym2idx.insert(tuple[i], i); @@ -85,6 +92,9 @@ void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * dom m_ref_holder.push_back(prefix); } +/** + extends a tuple in which prim is primary to the given size +*/ void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const { SASSERT(m_prim2all.contains(prim)); @@ -98,8 +108,9 @@ void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const std::string prefix_name = prefix->get_name().bare_str(); for (unsigned i = tuple.size(); i < sz; ++i) { std::string name = prefix_name + get_suffix(i); - func_decl * new_sym = m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(), - prefix->get_domain(), prefix->get_range()); + func_decl * new_sym = + m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(), + prefix->get_domain(), prefix->get_range()); tuple.push_back(new_sym); m_ref_holder.push_back(new_sym); @@ -108,8 +119,9 @@ void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const } } -func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const -{ +/** given a func_decl with src_idx returns its version with tgt_idx */ +func_decl * sym_mux::conv(func_decl * sym, + unsigned src_idx, unsigned tgt_idx) const { if (src_idx == tgt_idx) { return sym; } func_decl * prim = (src_idx == 0) ? sym : get_primary(sym); if (tgt_idx > src_idx) { @@ -121,15 +133,10 @@ func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) c } - - - struct sym_mux::formula_checker { - formula_checker(const sym_mux & parent, bool all, unsigned idx) : - m_parent(parent), m_all(all), m_idx(idx), - m_found_what_needed(false) - { - } + formula_checker(const sym_mux & parent, unsigned idx) : + m_parent(parent), m_idx(idx), + m_found_what_needed(false) {} void operator()(expr * e) { @@ -140,28 +147,15 @@ struct sym_mux::formula_checker { if (!m_parent.try_get_index(sym, sym_idx)) { return; } bool have_idx = sym_idx == m_idx; - - if (m_all ? (!have_idx) : have_idx) { - m_found_what_needed = true; - } + m_found_what_needed = !have_idx; } - bool all_have_idx() const - { - SASSERT(m_all); //we were looking for the queried property - return !m_found_what_needed; - } + bool all_have_idx() const {return !m_found_what_needed;} - bool some_with_idx() const - { - SASSERT(!m_all); //we were looking for the queried property - return m_found_what_needed; - } private: const sym_mux & m_parent; - bool m_all; unsigned m_idx; /** @@ -176,7 +170,7 @@ private: bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const { expr_mark visited; - formula_checker chck(*this, true, idx); + formula_checker chck(*this, idx); for_each_expr(chck, visited, e); return chck.all_have_idx(); } @@ -189,7 +183,8 @@ private: unsigned m_to_idx; bool m_homogenous; public: - conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous) + conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, + unsigned to_idx, bool homogenous) : m(parent.get_manager()), m_parent(parent), m_from_idx(from_idx), From 268274911a1bb91695464cc4b7f16d41b229bf2e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sun, 3 Jun 2018 09:13:38 -0700 Subject: [PATCH 212/364] Fix to cube-and-clause interface in prop_solver --- src/muz/spacer/spacer_prop_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 4613cd089..64f1bfc9a 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -376,7 +376,7 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, unsigned soft_sz = soft.size(); (void) soft_sz; vector clauses; - clauses.push_back(clause); + if (!clause.empty()) clauses.push_back(clause); lbool res = internal_check_assumptions(hard, soft, clauses); if (!m_use_push_bg) { m_ctx->pop(1); } From 38c2b56f0e6a0625666e5f6d67669680dc3e182c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sun, 3 Jun 2018 16:05:29 -0700 Subject: [PATCH 213/364] Rewrite spacer::sym_mux Simpler implementation that only provides functionality actually used by spacer --- src/muz/spacer/spacer_manager.cpp | 43 ++---- src/muz/spacer/spacer_manager.h | 16 +-- src/muz/spacer/spacer_sym_mux.cpp | 221 ++++++++++++------------------ src/muz/spacer/spacer_sym_mux.h | 140 +++++-------------- 4 files changed, 138 insertions(+), 282 deletions(-) diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index 719bf7862..856207463 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -170,42 +170,25 @@ void inductive_property::display(datalog::rule_manager& rm, ptr_vector state_suffixes() { - std::vector res; - res.push_back("_n"); - return res; -} -manager::manager(ast_manager& manager) : - m(manager), m_mux(m, state_suffixes()) { -} +manager::manager(ast_manager& manager) : m(manager), m_mux(m) {} - -void manager::add_new_state(func_decl * s) -{ - SASSERT(s->get_arity() == 0); //we currently don't support non-constant states - decl_vector vect; - - SASSERT(o_index(0) == 1); //we assume this in the number of retrieved symbols - m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect); -} - -func_decl * manager::get_o_pred(func_decl* s, unsigned idx) -{ - func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx)); - if (res) { return res; } - add_new_state(s); - res = m_mux.try_get_by_prefix(s, o_index(idx)); +func_decl * manager::get_o_pred(func_decl* s, unsigned idx) { + func_decl * res = m_mux.find_by_decl(s, o_index(idx)); + if (!res) { + m_mux.register_decl(s); + res = m_mux.find_by_decl(s, o_index(idx)); + } SASSERT(res); return res; } -func_decl * manager::get_n_pred(func_decl* s) -{ - func_decl * res = m_mux.try_get_by_prefix(s, n_index()); - if (res) { return res; } - add_new_state(s); - res = m_mux.try_get_by_prefix(s, n_index()); +func_decl * manager::get_n_pred(func_decl* s) { + func_decl * res = m_mux.find_by_decl(s, n_index()); + if (!res) { + m_mux.register_decl(s); + res = m_mux.find_by_decl(s, n_index()); + } SASSERT(res); return res; } diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index a73a9a63b..d592a30a8 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -83,8 +83,6 @@ class manager { unsigned n_index() const { return 0; } unsigned o_index(unsigned i) const { return i + 1; } - void add_new_state(func_decl * s); - public: manager(ast_manager & manager); @@ -100,27 +98,27 @@ public: {return m_mux.is_homogenous_formula(f, n_index());} func_decl * o2n(func_decl * p, unsigned o_idx) const - {return m_mux.conv(p, o_index(o_idx), n_index());} + {return m_mux.shift_decl(p, o_index(o_idx), n_index());} func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) const - {return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx));} + {return m_mux.shift_decl(p, o_index(src_idx), o_index(tgt_idx));} func_decl * n2o(func_decl * p, unsigned o_idx) const - {return m_mux.conv(p, n_index(), o_index(o_idx));} + {return m_mux.shift_decl(p, n_index(), o_index(o_idx));} void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const - {m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous);} + {m_mux.shift_expr(f, o_index(o_idx), n_index(), result, homogenous);} void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const - {m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous);} + {m_mux.shift_expr(f, n_index(), o_index(o_idx), result, homogenous);} void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const - {m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), + {m_mux.shift_expr(result.get(), n_index(), o_index(o_idx), result, homogenous);} void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous = true) const - {m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), + {m_mux.shift_expr(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous);} }; diff --git a/src/muz/spacer/spacer_sym_mux.cpp b/src/muz/spacer/spacer_sym_mux.cpp index 776776188..5dfb3a531 100644 --- a/src/muz/spacer/spacer_sym_mux.cpp +++ b/src/muz/spacer/spacer_sym_mux.cpp @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2011 Microsoft Corporation +Copyright (c) 2018 Arie Gurfinkel and Microsoft Corporation Module Name: @@ -7,10 +7,12 @@ Module Name: Abstract: - A symbol multiplexer that helps with having multiple versions of each of a set of symbols. + A symbol multiplexer that helps with having multiple versions of + each of a set of symbols. Author: + Arie Gurfinkel Krystof Hoder (t-khoder) 2011-9-8. Revision History: @@ -22,166 +24,114 @@ Revision History: #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" -#include "model/model.h" - #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_sym_mux.h" using namespace spacer; -sym_mux::sym_mux(ast_manager & m, const std::vector & suffixes) - : m(m), m_ref_holder(m), m_next_sym_suffix_idx(0), m_suffixes(suffixes) -{ - for (std::string const& s : m_suffixes) { - m_used_suffixes.insert(symbol(s.c_str())); +sym_mux::sym_mux(ast_manager & m) : m(m) {} +sym_mux::~sym_mux() { + for (auto &entry : m_entries) { + dealloc(entry.m_value); } } -std::string sym_mux::get_suffix(unsigned i) const -{ - while (m_suffixes.size() <= i) { - std::string new_suffix; - symbol new_syffix_sym; - do { - std::stringstream stm; - stm << '_' << m_next_sym_suffix_idx; - m_next_sym_suffix_idx++; - new_suffix = stm.str(); - new_syffix_sym = symbol(new_suffix.c_str()); - } while (m_used_suffixes.contains(new_syffix_sym)); - m_used_suffixes.insert(new_syffix_sym); - m_suffixes.push_back(new_suffix); - } - return m_suffixes[i]; +func_decl_ref sym_mux::mk_variant(func_decl *fdecl, unsigned i) const { + func_decl_ref v(m); + std::string name = fdecl->get_name().str(); + std::string suffix = "_"; + suffix += i == 0 ? "n" : std::to_string(i - 1); + name += suffix; + v = m.mk_func_decl(symbol(name.c_str()), fdecl->get_arity(), + fdecl->get_domain(), fdecl->get_range()); + return v; } -/** - populates a vector called tuple with func_decl's - tuple[0] is called primary and is used as key in various maps - */ -void sym_mux::create_tuple(func_decl* prefix, unsigned arity, - sort * const * domain, sort * range, - unsigned tuple_length, decl_vector & tuple) -{ - SASSERT(tuple_length > 0); - while (tuple.size() < tuple_length) { - tuple.push_back(0); - } - SASSERT(tuple.size() == tuple_length); - for (unsigned i = 0; i < tuple_length; i++) { +void sym_mux::register_decl(func_decl *fdecl) { + sym_mux_entry *entry = alloc(sym_mux_entry, m); + entry->m_main = fdecl; + entry->m_variants.push_back(mk_variant(fdecl, 0)); + entry->m_variants.push_back(mk_variant(fdecl, 1)); - if (tuple[i] != 0) { - SASSERT(tuple[i]->get_arity() == arity); - SASSERT(tuple[i]->get_range() == range); - //domain should match as well, but we won't bother - //checking an array equality - } else { - std::string name = prefix->get_name().str(); - name += get_suffix(i); - tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, - domain, range); - } - m_ref_holder.push_back(tuple[i]); - m_sym2idx.insert(tuple[i], i); - m_sym2prim.insert(tuple[i], tuple[0]); - } - - m_prim2all.insert(tuple[0], tuple); - m_prefix2prim.insert(prefix, tuple[0]); - m_prim2prefix.insert(tuple[0], prefix); - m_ref_holder.push_back(prefix); + m_entries.insert(fdecl, entry); + m_muxes.insert(entry->m_variants.get(0), std::make_pair(entry, 0)); + m_muxes.insert(entry->m_variants.get(1), std::make_pair(entry, 1)); } - -/** - extends a tuple in which prim is primary to the given size -*/ -void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const -{ - SASSERT(m_prim2all.contains(prim)); - decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value; - SASSERT(tuple[0] == prim); - - if (sz <= tuple.size()) { return; } - - func_decl * prefix; - TRUSTME(m_prim2prefix.find(prim, prefix)); - std::string prefix_name = prefix->get_name().bare_str(); - for (unsigned i = tuple.size(); i < sz; ++i) { - std::string name = prefix_name + get_suffix(i); - func_decl * new_sym = - m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(), - prefix->get_domain(), prefix->get_range()); - - tuple.push_back(new_sym); - m_ref_holder.push_back(new_sym); - m_sym2idx.insert(new_sym, i); - m_sym2prim.insert(new_sym, prim); +void sym_mux::ensure_capacity(sym_mux_entry &entry, unsigned sz) { + while (entry.m_variants.size() < sz) { + unsigned idx = entry.m_variants.size(); + entry.m_variants.push_back (mk_variant(entry.m_main, idx)); + m_muxes.insert(entry.m_variants.back(), std::make_pair(&entry, idx)); } } -/** given a func_decl with src_idx returns its version with tgt_idx */ -func_decl * sym_mux::conv(func_decl * sym, - unsigned src_idx, unsigned tgt_idx) const { - if (src_idx == tgt_idx) { return sym; } - func_decl * prim = (src_idx == 0) ? sym : get_primary(sym); - if (tgt_idx > src_idx) { - ensure_tuple_size(prim, tgt_idx + 1); - } - decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value; - SASSERT(sym_vect[src_idx] == sym); - return sym_vect[tgt_idx]; +bool sym_mux::find_idx(func_decl * sym, unsigned & idx) const { + std::pair entry; + if (m_muxes.find(sym, entry)) {idx = entry.second; return true;} + return false; } +func_decl * sym_mux::find_by_decl(func_decl* fdecl, unsigned idx) { + sym_mux_entry *entry = nullptr; + if (m_entries.find(fdecl, entry)) { + ensure_capacity(*entry, idx+1); + return entry->m_variants.get(idx); + } + return nullptr; +} -struct sym_mux::formula_checker { +func_decl * sym_mux::shift_decl(func_decl * decl, + unsigned src_idx, unsigned tgt_idx) const { + std::pair entry; + if (m_muxes.find(decl, entry)) { + SASSERT(entry.second == src_idx); + return entry.first->m_variants.get(tgt_idx); + } + UNREACHABLE(); + return nullptr; +} + +namespace { +struct formula_checker { formula_checker(const sym_mux & parent, unsigned idx) : - m_parent(parent), m_idx(idx), - m_found_what_needed(false) {} + m_parent(parent), m_idx(idx), m_found(false) {} - void operator()(expr * e) - { - if (m_found_what_needed || !is_app(e)) { return; } + void operator()(expr * e) { + if (m_found || !is_app(e)) { return; } func_decl * sym = to_app(e)->get_decl(); unsigned sym_idx; - if (!m_parent.try_get_index(sym, sym_idx)) { return; } + if (!m_parent.find_idx(sym, sym_idx)) { return; } bool have_idx = sym_idx == m_idx; - m_found_what_needed = !have_idx; - + m_found = !have_idx; } - bool all_have_idx() const {return !m_found_what_needed;} - + bool all_have_idx() const {return !m_found;} private: const sym_mux & m_parent; unsigned m_idx; - - /** - If we check whether all muxed symbols are of given index, we look for - counter-examples, checking whether form contains a muxed symbol of an index, - we look for symbol of index m_idx. - */ - bool m_found_what_needed; + bool m_found; }; - - -bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const -{ - expr_mark visited; - formula_checker chck(*this, idx); - for_each_expr(chck, visited, e); - return chck.all_have_idx(); } -struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg { +bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const { + expr_mark visited; + formula_checker fck(*this, idx); + for_each_expr(fck, visited, e); + return fck.all_have_idx(); +} + +namespace { +struct conv_rewriter_cfg : public default_rewriter_cfg { private: ast_manager & m; const sym_mux & m_parent; unsigned m_from_idx; unsigned m_to_idx; bool m_homogenous; + expr_ref_vector m_pinned; public: conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous) @@ -189,7 +139,7 @@ public: m_parent(parent), m_from_idx(from_idx), m_to_idx(to_idx), - m_homogenous(homogenous) {} + m_homogenous(homogenous), m_pinned(m) {(void) m_homogenous;} bool get_subst(expr * s, expr * & t, proof * & t_pr) { @@ -197,24 +147,23 @@ public: app * a = to_app(s); func_decl * sym = a->get_decl(); if (!m_parent.has_index(sym, m_from_idx)) { - (void) m_homogenous; SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); return false; } - func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx); - + func_decl * tgt = m_parent.shift_decl(sym, m_from_idx, m_to_idx); t = m.mk_app(tgt, a->get_args()); + m_pinned.push_back(t); return true; } }; - -void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, - expr_ref & res, bool homogenous) const { - if (src_idx == tgt_idx) { - res = f; - return; - } - conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); - rewriter_tpl rwr(m, false, r_cfg); - rwr(f, res); +} + +void sym_mux::shift_expr(expr * f, unsigned src_idx, unsigned tgt_idx, + expr_ref & res, bool homogenous) const { + if (src_idx == tgt_idx) {res = f;} + else { + conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); + rewriter_tpl rwr(m, false, r_cfg); + rwr(f, res); + } } diff --git a/src/muz/spacer/spacer_sym_mux.h b/src/muz/spacer/spacer_sym_mux.h index 97e3459d7..9657b47d9 100644 --- a/src/muz/spacer/spacer_sym_mux.h +++ b/src/muz/spacer/spacer_sym_mux.h @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2011 Microsoft Corporation +Copyright (c) 2018 Arie Gurfinkel and Microsoft Corporation Module Name: @@ -12,6 +12,7 @@ Abstract: Author: + Arie Gurfinkel Krystof Hoder (t-khoder) 2011-9-8. Revision History: @@ -21,144 +22,69 @@ Revision History: #ifndef _SYM_MUX_H_ #define _SYM_MUX_H_ -#include #include #include "ast/ast.h" #include "util/map.h" #include "util/vector.h" -class model_core; - namespace spacer { class sym_mux { private: - typedef obj_map sym2u; - typedef obj_map sym2dv; - typedef obj_map sym2sym; - typedef obj_map sym2pred; - typedef hashtable symbols; + class sym_mux_entry { + public: + func_decl_ref m_main; + func_decl_ref_vector m_variants; + sym_mux_entry(ast_manager &m) : m_main(m), m_variants(m) {}; + }; - ast_manager & m; - mutable ast_ref_vector m_ref_holder; + typedef obj_map decl2entry_map; + typedef obj_map > mux2entry_map; - mutable unsigned m_next_sym_suffix_idx; - mutable symbols m_used_suffixes; - /** Here we have default suffixes for each of the variants */ - mutable std::vector m_suffixes; + ast_manager &m; + decl2entry_map m_entries; + mux2entry_map m_muxes; + func_decl_ref mk_variant(func_decl *fdecl, unsigned i) const; + void ensure_capacity(sym_mux_entry &entry, unsigned sz) ; - /** - Primary symbol is the 0-th variant. This member maps from primary symbol - to vector of all its variants (including the primary variant). - */ - sym2dv m_prim2all; - - /** - For each symbol contains its variant index - */ - mutable sym2u m_sym2idx; - /** - For each symbol contains its primary variant - */ - mutable sym2sym m_sym2prim; - - /** - Maps prefixes passed to the create_tuple to - the primary symbol created from it. - */ - sym2pred m_prefix2prim; - - /** - Maps pripary symbols to prefixes that were used to create them. - */ - sym2sym m_prim2prefix; - - - - struct formula_checker; - struct conv_rewriter_cfg; - - std::string get_suffix(unsigned i) const; - void ensure_tuple_size(func_decl * prim, unsigned sz) const; - - // expr_ref isolate_o_idx(expr* e, unsigned idx) const; public: - sym_mux(ast_manager & m, const std::vector & suffixes); - + sym_mux(ast_manager & m); + ~sym_mux(); ast_manager & get_manager() const { return m; } - bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); } - - bool try_get_index(func_decl * sym, unsigned & idx) const - {return m_sym2idx.find(sym, idx);} - + void register_decl(func_decl *fdecl); + bool find_idx(func_decl * sym, unsigned & idx) const; bool has_index(func_decl * sym, unsigned idx) const - { - unsigned actual_idx; - return try_get_index(sym, actual_idx) && idx == actual_idx; - } + {unsigned v; return find_idx(sym, v) && idx == v;} - /** Return primary symbol. sym must be muxed. */ - func_decl * get_primary(func_decl * sym) const - { - func_decl * prim; - TRUSTME(m_sym2prim.find(sym, prim)); - return prim; - } /** - Return primary symbol created from prefix, or 0 if the prefix was never used. + \brief Return symbol created from prefix, or 0 if the prefix + was never used. */ - func_decl * try_get_primary_by_prefix(func_decl* prefix) const - { - func_decl * res; - if(!m_prefix2prim.find(prefix, res)) { - return nullptr; - } - return res; - } + func_decl * find_by_decl(func_decl* fdecl, unsigned idx); /** - Return symbol created from prefix, or 0 if the prefix was never used. - */ - func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) const - { - func_decl * prim = try_get_primary_by_prefix(prefix); - if(!prim) { - return nullptr; - } - return conv(prim, 0, idx); - } - - /** - Create a multiplexed tuple of propositional constants. - Symbols may be suplied in the tuple vector, - those beyond the size of the array and those with corresponding positions - assigned to zero will be created using prefix. - Tuple length must be at least one. - */ - void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, - unsigned tuple_length, decl_vector & tuple); - - /** - Return true if the only multiplexed symbols which e contains are of index idx. + \brief Return true if the only multiplexed symbols which e contains are + of index idx. */ bool is_homogenous_formula(expr * e, unsigned idx) const; /** - Convert symbol sym which has to be of src_idx variant into variant tgt_idx. + \brief Convert symbol sym which has to be of src_idx variant + into variant tgt_idx. */ - func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const; - + func_decl * shift_decl(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const; /** - Convert src_idx symbols in formula f variant into tgt_idx. - If homogenous is true, formula cannot contain symbols of other variants. + \brief Convert src_idx symbols in formula f variant into + tgt_idx. If homogenous is true, formula cannot contain symbols + of other variants. */ - void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, - expr_ref & res, bool homogenous = true) const; + void shift_expr(expr * f, unsigned src_idx, unsigned tgt_idx, + expr_ref & res, bool homogenous = true) const; }; From dd064bd8f9f7aa656a24f6f25f213c7b5c492e9c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sun, 3 Jun 2018 16:32:06 -0700 Subject: [PATCH 214/364] Bug fix to spacer::sym_mux --- src/muz/spacer/spacer_sym_mux.cpp | 5 +++-- src/muz/spacer/spacer_sym_mux.h | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/muz/spacer/spacer_sym_mux.cpp b/src/muz/spacer/spacer_sym_mux.cpp index 5dfb3a531..cfe0908d6 100644 --- a/src/muz/spacer/spacer_sym_mux.cpp +++ b/src/muz/spacer/spacer_sym_mux.cpp @@ -57,7 +57,7 @@ void sym_mux::register_decl(func_decl *fdecl) { m_muxes.insert(entry->m_variants.get(0), std::make_pair(entry, 0)); m_muxes.insert(entry->m_variants.get(1), std::make_pair(entry, 1)); } -void sym_mux::ensure_capacity(sym_mux_entry &entry, unsigned sz) { +void sym_mux::ensure_capacity(sym_mux_entry &entry, unsigned sz) const { while (entry.m_variants.size() < sz) { unsigned idx = entry.m_variants.size(); entry.m_variants.push_back (mk_variant(entry.m_main, idx)); @@ -71,7 +71,7 @@ bool sym_mux::find_idx(func_decl * sym, unsigned & idx) const { return false; } -func_decl * sym_mux::find_by_decl(func_decl* fdecl, unsigned idx) { +func_decl * sym_mux::find_by_decl(func_decl* fdecl, unsigned idx) const { sym_mux_entry *entry = nullptr; if (m_entries.find(fdecl, entry)) { ensure_capacity(*entry, idx+1); @@ -85,6 +85,7 @@ func_decl * sym_mux::shift_decl(func_decl * decl, std::pair entry; if (m_muxes.find(decl, entry)) { SASSERT(entry.second == src_idx); + ensure_capacity(*entry.first, tgt_idx + 1); return entry.first->m_variants.get(tgt_idx); } UNREACHABLE(); diff --git a/src/muz/spacer/spacer_sym_mux.h b/src/muz/spacer/spacer_sym_mux.h index 9657b47d9..5690370bd 100644 --- a/src/muz/spacer/spacer_sym_mux.h +++ b/src/muz/spacer/spacer_sym_mux.h @@ -42,11 +42,11 @@ private: typedef obj_map > mux2entry_map; ast_manager &m; - decl2entry_map m_entries; - mux2entry_map m_muxes; + mutable decl2entry_map m_entries; + mutable mux2entry_map m_muxes; func_decl_ref mk_variant(func_decl *fdecl, unsigned i) const; - void ensure_capacity(sym_mux_entry &entry, unsigned sz) ; + void ensure_capacity(sym_mux_entry &entry, unsigned sz) const; public: sym_mux(ast_manager & m); @@ -58,12 +58,13 @@ public: bool has_index(func_decl * sym, unsigned idx) const {unsigned v; return find_idx(sym, v) && idx == v;} + bool is_muxed(func_decl *fdecl) const {return m_muxes.contains(fdecl);} /** \brief Return symbol created from prefix, or 0 if the prefix was never used. */ - func_decl * find_by_decl(func_decl* fdecl, unsigned idx); + func_decl * find_by_decl(func_decl* fdecl, unsigned idx) const; /** \brief Return true if the only multiplexed symbols which e contains are From 4ca734528e91bae092675f50fd87d788331661db Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:24:47 -0700 Subject: [PATCH 215/364] Formatting --- src/muz/spacer/spacer_util.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 3eaeb098e..ebcbb9145 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -923,11 +923,10 @@ class implicant_picker { } void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, - expr_ref_vector &res) - { + expr_ref_vector &res) { // XXX what is the point of flattening? flatten_and (formula); - if (formula.empty()) { return; } + if (formula.empty()) { return; } implicant_picker ipick (mev); ipick (formula, res); From 6b82068d8deaf560354c7419a21ed9c3133be3c7 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:26:03 -0700 Subject: [PATCH 216/364] Bug fix in spacer::derivation::exist_skolemize --- src/muz/spacer/spacer_context.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index bbedbc391..511df2383 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -203,7 +203,23 @@ void derivation::exist_skolemize(expr* fml, app_ref_vector &vars, expr_ref &res) if (vars.empty()) {res = fml; return;} if (m.is_true(fml) || m.is_false(fml)) {res = fml; return;} - std::sort (vars.c_ptr(), vars.c_ptr() + vars.size(), sk_lt_proc()); + { + std::stable_sort (vars.c_ptr(), vars.c_ptr() + vars.size(), sk_lt_proc()); + unsigned i, j, end; + app_ref v(m); + for (i = 1, j = 1, end = vars.size(); i < end; ++i) { + if (vars.get(j-1) != vars.get(i)) { + v = vars.get(i); // keep ref + vars.set(j++, v); + } + } + vars.shrink(j); + } + + TRACE("spacer", tout << "Skolemizing: "; + for (auto v : vars) tout << " " << mk_pp(v, m) << " "; + tout << "\nfrom " << mk_pp(fml, m) << "\n"; + ); app_ref_vector pinned(m); From b61da6fcc041cce5eff4fe40a0bcae5a9f0c10bd Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:29:18 -0700 Subject: [PATCH 217/364] Debug print in org-mode format --- src/muz/spacer/spacer_context.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 511df2383..1268dbede 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -911,17 +911,12 @@ void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) << " " << mk_pp (l, m) << "\n";); STRACE ("spacer.expand-add", - tout << "add-lemma: " << pp_level (lvl) << " " + tout << "** add-lemma: " << pp_level (lvl) << " " << head ()->get_name () << " " << mk_epp (l, m) << "\n"; if (!lemma->is_ground()) { - expr_ref_vector inst(m); - lemma->mk_insts(inst); - for (unsigned i = 0, sz = inst.size(); i < sz; ++i) { - tout << mk_epp(inst.get(i), m) << "\n"; - } - + tout << "Bindings: " << lemma->get_bindings() << "\n"; } tout << "\n"; ); @@ -965,8 +960,6 @@ void pred_transformer::add_lemma_from_child (pred_transformer& child, lemma->mk_insts(inst, l); // -- take ground instance of the current lemma ground_expr(to_quantifier(l)->get_expr(), grnd_lemma, tmp); - STRACE("spacer.expand-add", - tout << "Adding instance: " << mk_epp(grnd_lemma, m) << "\n";); inst.push_back(grnd_lemma); } for (unsigned j=0; j < inst.size(); j++) { @@ -3239,7 +3232,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) << mk_pp(n.post(), m) << "\n";); STRACE ("spacer.expand-add", - tout << "expand-pob: " << n.pt().head()->get_name() + tout << "** expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_epp(n.post(), m) << "\n\n";); From d7dc10212e8532c69c783a5138420e250f6f3a3e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:32:59 -0700 Subject: [PATCH 218/364] Clean up spacer::context::create_children --- src/muz/spacer/spacer_context.cpp | 83 ++++++++++++------------------- 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 1268dbede..aaa379771 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -3612,93 +3612,71 @@ bool context::create_children(pob& n, datalog::rule const& r, scoped_watch _w_ (m_create_children_watch); pred_transformer& pt = n.pt(); - expr* const T = pt.get_transition(r); - expr* const phi = n.post(); TRACE("spacer", tout << "Model:\n"; model_smt2_pp(tout, m, *mev.get_model (), 0); tout << "\n"; - tout << "Transition:\n" << mk_pp(T, m) << "\n"; - tout << "Phi:\n" << mk_pp(phi, m) << "\n";); + tout << "Transition:\n" << mk_pp(pt.get_transition(r), m) << "\n"; + tout << "Pob:\n" << mk_pp(n.post(), m) << "\n";); SASSERT (r.get_uninterpreted_tail_size () > 0); ptr_vector preds; pt.find_predecessors(r, preds); - ptr_vector pred_pts; - - for (ptr_vector::iterator it = preds.begin (); - it != preds.end (); it++) { - pred_pts.push_back (&get_pred_transformer (*it)); - } - - expr_ref_vector forms(m), Phi(m); // obtain all formulas to consider for model generalization - forms.push_back(T); - forms.push_back(phi); + expr_ref_vector forms(m), lits(m); + forms.push_back(pt.get_transition(r)); + forms.push_back(n.post()); - compute_implicant_literals (mev, forms, Phi); - - //pt.remove_predecessors (Phi); + compute_implicant_literals (mev, forms, lits); + expr_ref phi = mk_and (lits); + // primed variables of the head app_ref_vector vars(m); - unsigned sig_size = pt.head()->get_arity(); - for (unsigned i = 0; i < sig_size; ++i) { + for (unsigned i = 0, sz = pt.head()->get_arity(); i < sz; ++i) { vars.push_back(m.mk_const(m_pm.o2n(pt.sig(i), 0))); } + // local variables of the rule ptr_vector& aux_vars = pt.get_aux_vars(r); vars.append(aux_vars.size(), aux_vars.c_ptr()); n.get_skolems(vars); + n.get_skolems(vars); + // skolems of the pob - expr_ref phi1 = mk_and (Phi); - n.pt().mbp(vars, phi1, mev.get_model (), true, use_ground_cti()); + n.pt().mbp(vars, phi, mev.get_model (), true, use_ground_cti()); //qe::reduce_array_selects (*mev.get_model (), phi1); SASSERT (!m_ground_cti || vars.empty ()); TRACE ("spacer", - tout << "Implicant\n"; - tout << mk_and (Phi) << "\n"; - tout << "Projected Implicant\n" << mk_pp (phi1, m) << "\n"; + tout << "Implicant:\n"; + tout << lits << "\n"; + tout << "After MBP:\n" << phi << "\n"; if (!vars.empty()) - tout << "could not eliminate: " << vars << "\n"; + tout << "Failed to eliminate: " << vars << "\n"; ); - // expand literals. Ideally, we do not want to split aliasing - // equalities. Unfortunately, the interface does not allow for - // that yet. - // XXX This mixes up with derivation. Needs more thought. - // Phi.reset (); - // flatten_and (phi1, Phi); - // if (!Phi.empty ()) - // { - // expand_literals (m, Phi); - // phi1 = m_pm.mk_and (Phi); - // } + derivation *deriv = alloc(derivation, n, r, phi, vars); - - derivation *deriv = alloc (derivation, n, r, phi1, vars); + // pick an order to process children + unsigned_vector kid_order; + kid_order.resize(preds.size(), 0); + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) kid_order[i] = i; + if (get_params().spacer_order_children() == 1) kid_order.reverse(); for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { - unsigned j; - if (get_params ().spacer_order_children () == 1) - // -- reverse order - { j = sz - i - 1; } - else - // -- default order - { j = i; } + unsigned j = kid_order[i]; - pred_transformer &pt = get_pred_transformer (preds [j]); + + pred_transformer &pt = get_pred_transformer(preds.get(j)); const ptr_vector *aux = nullptr; expr_ref sum(m); - // XXX This is a bit confusing. The summary is returned over - // XXX o-variables. But it is simpler if it is returned over n-variables instead. - sum = pt.get_origin_summary (mev, prev_level (n.level ()), - j, reach_pred_used [j], &aux); - deriv->add_premise (pt, j, sum, reach_pred_used [j], aux); + sum = pt.get_origin_summary (mev, prev_level(n.level()), + j, reach_pred_used[j], &aux); + deriv->add_premise (pt, j, sum, reach_pred_used[j], aux); } // create post for the first child and add to queue @@ -3719,7 +3697,8 @@ bool context::create_children(pob& n, datalog::rule const& r, // -- not satisfy 'T && phi'. It is possible to recover from // -- that more gracefully. For now, we just remove the // -- derivation completely forcing it to be recomputed - if (m_weak_abs && (!mev.is_true(T) || !mev.is_true(phi))) + if (m_weak_abs && (!mev.is_true(pt.get_transition(r)) || + !mev.is_true(n.post()))) { kid->reset_derivation(); } out.push_back(kid); From c5ff5ac2a13ae58eaedae6732e9902be7130ebb8 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:33:36 -0700 Subject: [PATCH 219/364] Clen up spacer::pred_transformer::get_origin_summary --- src/muz/spacer/spacer_context.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index aaa379771..adcb8c8ae 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1142,7 +1142,7 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, expr_ref v(m); if (!must) { // use may summary - summary.push_back (get_formulas (level)); + summary.push_back (get_formulas(level)); // -- no auxiliary variables in lemmas *aux = nullptr; } else { // find must summary to use @@ -1160,10 +1160,10 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, } // -- pick an implicant - expr_ref_vector literals (m); - compute_implicant_literals (mev, summary, literals); + expr_ref_vector lits(m); + compute_implicant_literals (mev, summary, lits); - return mk_and(literals); + return mk_and(lits); } From da66ad6f80c6726e05e9060e0bc6fd33fdca1af8 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:34:14 -0700 Subject: [PATCH 220/364] Cleanup derivation::create_next_child --- src/muz/spacer/spacer_context.cpp | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index adcb8c8ae..5512f14f0 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -191,8 +191,7 @@ void derivation::add_premise (pred_transformer &pt, -pob *derivation::create_first_child (model_evaluator_util &mev) -{ +pob *derivation::create_first_child (model_evaluator_util &mev) { if (m_premises.empty()) { return nullptr; } m_active = 0; return create_next_child(mev); @@ -262,9 +261,8 @@ pob *derivation::create_next_child (model_evaluator_util &mev) verbose_stream ()); pt().mbp(vars, m_trans, mev.get_model(), true, pt().get_context().use_ground_cti()); - //qe::reduce_array_selects (*mev.get_model (), m_trans); - // remember variables that need to be existentially quantified m_evars.append (vars); + vars.reset(); } if (!mev.is_true (m_premises[m_active].get_summary())) { @@ -273,28 +271,28 @@ pob *derivation::create_next_child (model_evaluator_util &mev) } - // create the post condition by compute post-image over summaries + // create the post-condition by computing a post-image over summaries // that precede currently active premise - vars.reset (); for (unsigned i = m_active + 1; i < m_premises.size(); ++i) { summaries.push_back (m_premises [i].get_summary ()); vars.append (m_premises [i].get_ovars ()); } summaries.push_back (m_trans); - expr_ref post(m); - post = mk_and (summaries); + post = mk_and(summaries); summaries.reset (); + if (!vars.empty()) { - timeit _timer2 (is_trace_enabled("spacer_timeit"), - "create_next_child::qproject2", - verbose_stream ()); + timeit _timer2(is_trace_enabled("spacer_timeit"), + "create_next_child::qproject2", + verbose_stream ()); pt().mbp(vars, post, mev.get_model(), true, pt().get_context().use_ground_cti()); //qe::reduce_array_selects (*mev.get_model (), post); // remember variables that need to be existentially quantified - m_evars.append (vars); + m_evars.append(vars); + vars.reset(); } if (!m_evars.empty()) { @@ -649,6 +647,7 @@ void lemma::mk_insts(expr_ref_vector &out, expr* e) } + // ---------------- // pred_tansformer @@ -3633,8 +3632,8 @@ bool context::create_children(pob& n, datalog::rule const& r, compute_implicant_literals (mev, forms, lits); expr_ref phi = mk_and (lits); - // primed variables of the head + // primed variables of the head app_ref_vector vars(m); for (unsigned i = 0, sz = pt.head()->get_arity(); i < sz; ++i) { vars.push_back(m.mk_const(m_pm.o2n(pt.sig(i), 0))); @@ -3643,9 +3642,8 @@ bool context::create_children(pob& n, datalog::rule const& r, ptr_vector& aux_vars = pt.get_aux_vars(r); vars.append(aux_vars.size(), aux_vars.c_ptr()); - n.get_skolems(vars); - n.get_skolems(vars); // skolems of the pob + n.get_skolems(vars); n.pt().mbp(vars, phi, mev.get_model (), true, use_ground_cti()); //qe::reduce_array_selects (*mev.get_model (), phi1); @@ -3666,10 +3664,10 @@ bool context::create_children(pob& n, datalog::rule const& r, kid_order.resize(preds.size(), 0); for (unsigned i = 0, sz = preds.size(); i < sz; ++i) kid_order[i] = i; if (get_params().spacer_order_children() == 1) kid_order.reverse(); + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { unsigned j = kid_order[i]; - pred_transformer &pt = get_pred_transformer(preds.get(j)); const ptr_vector *aux = nullptr; From 6fb6279f07488b2b77bf77d68bc4a76dbbd6d0db Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 11:56:45 -0700 Subject: [PATCH 221/364] Cleanup array_eq_generalizer --- src/muz/spacer/spacer_generalizers.cpp | 118 +++++++++++++------------ src/muz/spacer/spacer_generalizers.h | 2 + 2 files changed, 64 insertions(+), 56 deletions(-) diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 7263f8264..d7c7429ee 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -21,6 +21,7 @@ Revision History: #include "muz/spacer/spacer_context.h" #include "muz/spacer/spacer_generalizers.h" +#include "ast/ast_util.h" #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" @@ -205,103 +206,108 @@ public: }; } +bool lemma_array_eq_generalizer::is_array_eq (ast_manager &m, expr* e) { + + expr *e1 = nullptr, *e2 = nullptr; + if (m.is_eq(e, e1, e2) && is_app(e1) && is_app(e2)) { + app *a1 = to_app(e1); + app *a2 = to_app(e2); + array_util au(m); + if (a1->get_family_id() == null_family_id && + a2->get_family_id() == null_family_id && + au.is_array(a1) && au.is_array(a2)) + return true; + } + return false; +} + void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) { - TRACE("core_array_eq", tout << "Looking for equalities\n";); - // -- find array constants ast_manager &m = lemma->get_ast_manager(); - manager &pm = m_ctx.get_manager(); - (void)pm; - unsigned weakness = lemma->weakness(); expr_ref_vector core(m); expr_ref v(m); func_decl_set symb; collect_array_proc cap(m, symb); + + // -- find array constants core.append (lemma->get_cube()); v = mk_and(core); for_each_expr(cap, v); - TRACE("core_array_eq", + CTRACE("core_array_eq", symb.size() > 1 && symb.size() <= 8, tout << "found " << symb.size() << " array variables in: \n" - << mk_pp(v, m) << "\n";); + << v << "\n";); - // too few constants - if (symb.size() <= 1) { return; } - // too many constants, skip this - if (symb.size() >= 8) { return; } + // too few constants or too many constants + if (symb.size() <= 1 || symb.size() > 8) { return; } - // -- for every pair of variables, try an equality - typedef func_decl_set::iterator iterator; + // -- for every pair of constants (A, B), check whether the + // -- equality (A=B) generalizes a literal in the lemma + ptr_vector vsymbs; - for (iterator it = symb.begin(), end = symb.end(); - it != end; ++it) - { vsymbs.push_back(*it); } + for (auto * fdecl : symb) {vsymbs.push_back(fdecl);} + // create all equalities expr_ref_vector eqs(m); + for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i) { + for (unsigned j = i + 1; j < sz; ++j) { + eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)), + m.mk_const(vsymbs.get(j)))); + } + } - for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i) - for (unsigned j = i + 1; j < sz; ++j) - { eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)), - m.mk_const(vsymbs.get(j)))); } - + // smt-solver to check whether a literal is generalized. using + // default params. There has to be a simpler way to approximate + // this check ref sol = mk_smt_solver(m, params_ref::get_empty(), symbol::null); + // literals of the new lemma expr_ref_vector lits(m); - for (unsigned i = 0, core_sz = core.size(); i < core_sz; ++i) { - SASSERT(lits.size() == i); - sol->push(); - sol->assert_expr(core.get(i)); - for (unsigned j = 0, eqs_sz = eqs.size(); j < eqs_sz; ++j) { - sol->push(); - sol->assert_expr(eqs.get(j)); + lits.append(core); + expr *t = nullptr; + bool dirty = false; + for (unsigned i = 0, sz = core.size(); i < sz; ++i) { + // skip a literal is it is already an array equality + if (m.is_not(lits.get(i), t) && is_array_eq(m, t)) continue; + solver::scoped_push _pp_(*sol); + sol->assert_expr(lits.get(i)); + for (auto *e : eqs) { + solver::scoped_push _p_(*sol); + sol->assert_expr(e); lbool res = sol->check_sat(0, nullptr); - sol->pop(1); if (res == l_false) { TRACE("core_array_eq", - tout << "strengthened " << mk_pp(core.get(i), m) - << " with " << mk_pp(m.mk_not(eqs.get(j)), m) << "\n";); - lits.push_back(m.mk_not(eqs.get(j))); + tout << "strengthened " << mk_pp(lits.get(i), m) + << " with " << mk_pp(mk_not(m, e), m) << "\n";); + lits[i] = mk_not(m, e); + dirty = true; break; } } - sol->pop(1); - if (lits.size() == i) { lits.push_back(core.get(i)); } } - /** - HACK: if the first 3 arguments of pt are boolean, assume - they correspond to SeaHorn encoding and condition the equality on them. - */ - // pred_transformer &pt = n.pt (); - // if (pt.sig_size () >= 3 && - // m.is_bool (pt.sig (0)->get_range ()) && - // m.is_bool (pt.sig (1)->get_range ()) && - // m.is_bool (pt.sig (2)->get_range ())) - // { - // lits.push_back (m.mk_const (pm.o2n(pt.sig (0), 0))); - // lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (1), 0)))); - // lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (2), 0)))); - // } + // nothing changed + if (!dirty) return; - TRACE("core_array_eq", tout << "new possible core " - << mk_and(lits) << "\n";); + TRACE("core_array_eq", + tout << "new possible core " << mk_and(lits) << "\n";); pred_transformer &pt = lemma->get_pob()->pt(); - // -- check if it is consistent with the transition relation + // -- check if the generalized result is consistent with trans unsigned uses_level1; - if (pt.check_inductive(lemma->level(), lits, uses_level1, weakness)) { + if (pt.check_inductive(lemma->level(), lits, uses_level1, lemma->weakness())) { TRACE("core_array_eq", tout << "Inductive!\n";); - lemma->update_cube(lemma->get_pob(),lits); + lemma->update_cube(lemma->get_pob(), lits); lemma->set_level(uses_level1); - return; - } else - { TRACE("core_array_eq", tout << "Not-Inductive!\n";);} + } + else + {TRACE("core_array_eq", tout << "Not-Inductive!\n";);} } void lemma_eq_generalizer::operator() (lemma_ref &lemma) diff --git a/src/muz/spacer/spacer_generalizers.h b/src/muz/spacer/spacer_generalizers.h index 518b09f6c..45ae472bd 100644 --- a/src/muz/spacer/spacer_generalizers.h +++ b/src/muz/spacer/spacer_generalizers.h @@ -83,6 +83,8 @@ public: }; class lemma_array_eq_generalizer : public lemma_generalizer { +private: + bool is_array_eq(ast_manager &m, expr *e); public: lemma_array_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {} ~lemma_array_eq_generalizer() override {} From 7396ad72abd2fc70e1200b6e7c6e442b4c5efe75 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:21:03 -0700 Subject: [PATCH 222/364] Give up when a lemma is re-discovered too many times --- src/muz/spacer/spacer_context.cpp | 30 ++++++++++++++++++++---------- src/muz/spacer/spacer_context.h | 4 ++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 5512f14f0..a36f2476f 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -453,7 +453,7 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(lvl), m_init_lvl(m_lvl), - m_pob(nullptr), m_ctp(nullptr), m_external(false) { + m_pob(nullptr), m_ctp(nullptr), m_external(false), m_bumped(0) { SASSERT(m_body); normalize(m_body, m_body); } @@ -462,7 +462,7 @@ lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(p->level()), m_init_lvl(m_lvl), - m_pob(p), m_ctp(nullptr), m_external(false) { + m_pob(p), m_ctp(nullptr), m_external(false), m_bumped(0) { SASSERT(m_pob); m_pob->get_skolems(m_zks); add_binding(m_pob->get_binding()); @@ -473,7 +473,7 @@ lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_lvl(p->level()), m_init_lvl(m_lvl), - m_pob(p), m_ctp(nullptr), m_external(false) + m_pob(p), m_ctp(nullptr), m_external(false), m_bumped(0) { if (m_pob) { m_pob->get_skolems(m_zks); @@ -1955,6 +1955,16 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) if (!new_lemma->get_bindings().empty()) { m_pt.add_lemma_core(old_lemma, true); } + if(is_infty_level(old_lemma->level())) { + old_lemma->bump(); + if (old_lemma->get_bumped() >= 100) { + IF_VERBOSE(1, verbose_stream() << "Adding lemma to oo " + << old_lemma->get_bumped() << " " + << mk_pp(old_lemma->get_expr(), + m_pt.get_ast_manager()) << "\n";); + throw default_exception("Stuck on a lemma"); + } + } // no new lemma added return false; } @@ -1962,13 +1972,6 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) // update level of the existing lemma old_lemma->set_level(new_lemma->level()); // assert lemma in the solver - m_pt.add_lemma_core(old_lemma, false); - // move the lemma to its new place to maintain sortedness - unsigned sz = m_lemmas.size(); - for (unsigned j = i; - (j + 1) < sz && m_lt(m_lemmas[j + 1], m_lemmas[j]); ++j) { - m_lemmas.swap (j, j+1); - } return true; } i++; @@ -1976,6 +1979,13 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) // new_lemma is really new m_lemmas.push_back(new_lemma); + m_pt.add_lemma_core(old_lemma, false); + // move the lemma to its new place to maintain sortedness + unsigned sz = m_lemmas.size(); + for (unsigned j = i; + (j + 1) < sz && m_lt(m_lemmas[j + 1], m_lemmas[j]); ++j) { + m_lemmas.swap (j, j+1); + } // XXX because m_lemmas is reduced, keep secondary vector of all lemmas // XXX so that pob can refer to its lemmas without creating reference cycles m_pinned_lemmas.push_back(new_lemma); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 120953e97..e9881b3ff 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -125,6 +125,7 @@ class lemma { pob_ref m_pob; model_ref m_ctp; // counter-example to pushing bool m_external; + unsigned m_bumped; void mk_expr_core(); void mk_cube_core(); @@ -141,6 +142,9 @@ public: void set_ctp(model_ref &v) {m_ctp = v;} void reset_ctp() {m_ctp.reset();} + void bump() {m_bumped++;} + unsigned get_bumped() {return m_bumped;} + expr *get_expr(); bool is_false(); expr_ref_vector const &get_cube(); From 5756871738485a70d689effd898ae2e4a442ee93 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:22:51 -0700 Subject: [PATCH 223/364] Always attempt to eliminate all existential variables Sometimes variables that cannot be eliminated in one context, can be eliminated in the other. Pass all available variables to MBP to be eliminated if possible --- src/muz/spacer/spacer_context.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index a36f2476f..e32c065aa 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -240,7 +240,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) ast_manager &m = get_ast_manager (); expr_ref_vector summaries (m); - app_ref_vector vars (m); + app_ref_vector vars(m); // -- find first may premise while (m_active < m_premises.size() && m_premises[m_active].is_must()) { @@ -259,6 +259,8 @@ pob *derivation::create_next_child (model_evaluator_util &mev) timeit _timer1 (is_trace_enabled("spacer_timeit"), "create_next_child::qproject1", verbose_stream ()); + vars.append(m_evars); + m_evars.reset(); pt().mbp(vars, m_trans, mev.get_model(), true, pt().get_context().use_ground_cti()); m_evars.append (vars); @@ -286,6 +288,8 @@ pob *derivation::create_next_child (model_evaluator_util &mev) timeit _timer2(is_trace_enabled("spacer_timeit"), "create_next_child::qproject2", verbose_stream ()); + vars.append(m_evars); + m_evars.reset(); pt().mbp(vars, post, mev.get_model(), true, pt().get_context().use_ground_cti()); //qe::reduce_array_selects (*mev.get_model (), post); @@ -386,10 +390,13 @@ pob *derivation::create_next_child () { vars.push_back(m.mk_const(pm.o2n(pt.sig(i), 0))); } if (!vars.empty ()) { + vars.append(m_evars); + m_evars.reset(); this->pt().mbp(vars, m_trans, mev.get_model(), true, this->pt().get_context().use_ground_cti()); // keep track of implicitly quantified variables m_evars.append (vars); + vars.reset(); } } @@ -1972,13 +1979,6 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) // update level of the existing lemma old_lemma->set_level(new_lemma->level()); // assert lemma in the solver - return true; - } - i++; - } - - // new_lemma is really new - m_lemmas.push_back(new_lemma); m_pt.add_lemma_core(old_lemma, false); // move the lemma to its new place to maintain sortedness unsigned sz = m_lemmas.size(); @@ -1986,6 +1986,13 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) (j + 1) < sz && m_lt(m_lemmas[j + 1], m_lemmas[j]); ++j) { m_lemmas.swap (j, j+1); } + return true; + } + i++; + } + + // new_lemma is really new + m_lemmas.push_back(new_lemma); // XXX because m_lemmas is reduced, keep secondary vector of all lemmas // XXX so that pob can refer to its lemmas without creating reference cycles m_pinned_lemmas.push_back(new_lemma); From 3178f7f86d0f7ebb75a4badc797aa554199e7da0 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 12:53:45 -0700 Subject: [PATCH 224/364] Add random order of children in spacer --- src/muz/spacer/spacer_context.cpp | 10 +++++++++- src/muz/spacer/spacer_context.h | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index e32c065aa..7d9e6e5be 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2241,6 +2241,9 @@ context::context(fixedpoint_params const& params, m_pool0 = alloc(solver_pool, pool0_base.get(), max_num_contexts); m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); + + m_random.set_seed(m_params.spacer_random_seed()); + m_children_order = static_cast(m_params.spacer_order_children()); } context::~context() @@ -3680,7 +3683,12 @@ bool context::create_children(pob& n, datalog::rule const& r, unsigned_vector kid_order; kid_order.resize(preds.size(), 0); for (unsigned i = 0, sz = preds.size(); i < sz; ++i) kid_order[i] = i; - if (get_params().spacer_order_children() == 1) kid_order.reverse(); + if (m_children_order == CO_REV_RULE) { + kid_order.reverse(); + } + else if (m_children_order == CO_RANDOM) { + shuffle(kid_order.size(), kid_order.c_ptr(), m_random); + } for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { unsigned j = kid_order[i]; diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index e9881b3ff..ccdac2c48 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -782,6 +782,12 @@ public: }; +// order in which children are processed +enum spacer_children_order { + CO_RULE, // same order as in the rule + CO_REV_RULE, // reverse order of the rule + CO_RANDOM // random shuffle +}; class context { @@ -819,6 +825,8 @@ class context { scoped_ptr m_pool2; + random_gen m_random; + spacer_children_order m_children_order; decl2rel m_rels; // Map from relation predicate to fp-operator. func_decl_ref m_query_pred; pred_transformer* m_query; From fee7828b511722e8d7a7c8748e8a74add1fa8530 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Jun 2018 14:23:46 -0700 Subject: [PATCH 225/364] fix solve bug Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 100 +++++++++++++++++---------- src/math/simplex/model_based_opt.h | 4 ++ 2 files changed, 68 insertions(+), 36 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index f12918bfe..86b3cfa0c 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -175,8 +175,7 @@ namespace opt { rational new_x_val; rational x_coeff, eps(0); vector const& vars = r.m_vars; - for (unsigned j = 0; j < vars.size(); ++j) { - var const& v = vars[j]; + for (var const& v : vars) { if (x == v.m_id) { x_coeff = v.m_coeff; } @@ -227,8 +226,7 @@ namespace opt { --i; unsigned x = bound_vars[i]; unsigned_vector const& row_ids = m_var2row_ids[x]; - for (unsigned j = 0; j < row_ids.size(); ++j) { - unsigned row_id = row_ids[j]; + for (unsigned row_id : row_ids) { row & r = m_rows[row_id]; r.m_value = get_row_value(r); SASSERT(invariant(row_id, r)); @@ -368,7 +366,7 @@ namespace opt { tout << a1 << " " << a2 << ": "; display(tout, m_rows[row_dst]); display(tout, m_rows[row_src]);); - if (a1.is_pos() != a2.is_pos()) { + if (a1.is_pos() != a2.is_pos() || m_rows[row_src].m_type == opt::t_eq) { mul_add(x, a1, row_src, a2, row_dst); } else { @@ -384,6 +382,18 @@ namespace opt { } } + void model_based_opt::solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { + SASSERT(a1 == get_coefficient(row_src, x)); + SASSERT(a1.is_pos()); + SASSERT(row_src != row_dst); + SASSERT(m_rows[row_src].m_type == t_eq); + if (!m_rows[row_dst].m_alive) return; + rational a2 = get_coefficient(row_dst, x); + mul(row_dst, a1); + mul_add(false, row_dst, -a2, row_src); + SASSERT(get_coefficient(row_dst, x).is_zero()); + } + // resolution for integer rows. void model_based_opt::mul_add( unsigned x, rational const& src_c, unsigned row_src, rational const& dst_c, unsigned row_dst) { @@ -457,8 +467,8 @@ namespace opt { } void model_based_opt::mk_coeffs_without(vector& dst, vector const src, unsigned x) { - for (unsigned i = 0; i < src.size(); ++i) { - if (src[i].m_id != x) dst.push_back(src[i]); + for (var const & v : src) { + if (v.m_id != x) dst.push_back(v); } } @@ -469,8 +479,8 @@ namespace opt { void model_based_opt::mul(unsigned dst, rational const& c) { if (c.is_one()) return; row& r = m_rows[dst]; - for (unsigned i = 0; i < r.m_vars.size(); ++i) { - r.m_vars[i].m_coeff *= c; + for (auto & v : r.m_vars) { + v.m_coeff *= c; } r.m_coeff *= c; r.m_value *= c; @@ -674,8 +684,8 @@ namespace opt { unsigned dst = new_row(); row const& r = m_rows[src]; set_row(dst, r.m_vars, r.m_coeff, r.m_mod, r.m_type); - for (unsigned i = 0; i < r.m_vars.size(); ++i) { - m_var2row_ids[r.m_vars[i].m_id].push_back(dst); + for (auto const& v : r.m_vars) { + m_var2row_ids[v.m_id].push_back(dst); } SASSERT(invariant(dst, m_rows[dst])); return dst; @@ -692,8 +702,8 @@ namespace opt { void model_based_opt::add_constraint(vector const& coeffs, rational const& c, rational const& m, ineq_type rel) { unsigned row_id = new_row(); set_row(row_id, coeffs, c, m, rel); - for (unsigned i = 0; i < coeffs.size(); ++i) { - m_var2row_ids[coeffs[i].m_id].push_back(row_id); + for (var const& coeff : coeffs) { + m_var2row_ids[coeff.m_id].push_back(row_id); } SASSERT(invariant(row_id, m_rows[row_id])); } @@ -703,9 +713,9 @@ namespace opt { } void model_based_opt::get_live_rows(vector& rows) { - for (unsigned i = 0; i < m_rows.size(); ++i) { - if (m_rows[i].m_alive) { - rows.push_back(m_rows[i]); + for (row const& r : m_rows) { + if (r.m_alive) { + rows.push_back(r); } } } @@ -742,9 +752,9 @@ namespace opt { glb_rows.reset(); mod_rows.reset(); bool lub_is_unit = false, glb_is_unit = false; + unsigned eq_row = UINT_MAX; // select the lub and glb. - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id = row_ids[i]; + for (unsigned row_id : row_ids) { if (visited.contains(row_id)) { continue; } @@ -758,8 +768,8 @@ namespace opt { continue; } if (r.m_type == t_eq) { - solve_for(row_id, x); - return; + eq_row = row_id; + continue; } if (r.m_type == t_mod) { mod_rows.push_back(row_id); @@ -795,6 +805,11 @@ namespace opt { solve_mod(x, mod_rows); return; } + + if (eq_row != UINT_MAX) { + solve_for(eq_row, x); + return; + } unsigned lub_size = lub_rows.size(); unsigned glb_size = glb_rows.size(); @@ -803,8 +818,7 @@ namespace opt { // There are only upper or only lower bounds. if (row_index == UINT_MAX) { - for (unsigned i = 0; i < glb_rows.size(); ++i) { - unsigned row_id = glb_rows[i]; + for (unsigned row_id : glb_rows) { SASSERT(m_rows[row_id].m_alive); SASSERT(!get_coefficient(row_id, x).is_zero()); retire_row(row_id); @@ -839,8 +853,7 @@ namespace opt { // General case. rational coeff = get_coefficient(row_index, x); - for (unsigned i = 0; i < glb_rows.size(); ++i) { - unsigned row_id = glb_rows[i]; + for (unsigned row_id : glb_rows) { if (row_id != row_index) { resolve(row_index, coeff, row_id, x); } @@ -866,8 +879,8 @@ namespace opt { void model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows) { SASSERT(!mod_rows.empty()); rational D(1); - for (unsigned i = 0; i < mod_rows.size(); ++i) { - D = lcm(D, m_rows[mod_rows[i]].m_mod); + for (unsigned idx : mod_rows) { + D = lcm(D, m_rows[idx].m_mod); } if (D.is_zero()) { throw default_exception("modulo 0 is not defined"); @@ -876,9 +889,9 @@ namespace opt { rational val_x = m_var2value[x]; rational u = mod(val_x, D); SASSERT(u.is_nonneg() && u < D); - for (unsigned i = 0; i < mod_rows.size(); ++i) { - replace_var(mod_rows[i], x, u); - SASSERT(invariant(mod_rows[i], m_rows[mod_rows[i]])); + for (unsigned idx : mod_rows) { + replace_var(idx, x, u); + SASSERT(invariant(idx, m_rows[idx])); } // // update inequalities such that u is added to t and @@ -894,8 +907,7 @@ namespace opt { unsigned y = add_var(new_val, true); unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id = row_ids[i]; + for (unsigned row_id : row_ids) { if (!visited.contains(row_id)) { // x |-> D*y + u replace_var(row_id, x, D, y, u); @@ -954,23 +966,39 @@ namespace opt { SASSERT(!a.is_zero()); SASSERT(m_rows[row_id1].m_type == t_eq); SASSERT(m_rows[row_id1].m_alive); - if (m_var2is_int[x] && !abs(a).is_one()) { + if (a.is_neg()) { + a.neg(); + m_rows[row_id1].neg(); + } + SASSERT(a.is_pos()); + if (m_var2is_int[x] && !a.is_one()) { row& r1 = m_rows[row_id1]; vector coeffs; mk_coeffs_without(coeffs, r1.m_vars, x); rational c = r1.m_coeff; - add_divides(coeffs, c, abs(a)); + add_divides(coeffs, c, a); } unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; visited.insert(row_id1); - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id2 = row_ids[i]; + for (unsigned row_id2 : row_ids) { if (!visited.contains(row_id2)) { visited.insert(row_id2); + b = get_coefficient(row_id2, x); if (!b.is_zero()) { - resolve(row_id1, a, row_id2, x); + row& dst = m_rows[row_id2]; + switch (dst.m_type) { + case t_eq: + case t_lt: + case t_le: + solve(row_id1, a, row_id2, x); + break; + case t_mod: + // mod reduction already done. + UNREACHABLE(); + break; + } } } } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 54360d0ac..9546529f2 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -58,6 +58,8 @@ namespace opt { rational m_value; // value of m_vars + m_coeff under interpretation of m_var2value. bool m_alive; // rows can be marked dead if they have been processed. void reset() { m_vars.reset(); m_coeff.reset(); m_value.reset(); } + + void neg() { for (var & v : m_vars) v.m_coeff.neg(); m_coeff.neg(); m_value.neg(); } }; private: @@ -85,6 +87,8 @@ namespace opt { void resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); + void solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); + void mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2); void mul_add(unsigned x, rational const& a1, unsigned row_src, rational const& a2, unsigned row_dst); From 2a243d38d10e721aca05f1a329572785208f04a5 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 15:10:57 -0700 Subject: [PATCH 226/364] Model based Cartesian decomposition --- src/muz/spacer/CMakeLists.txt | 1 + src/muz/spacer/spacer_mbc.cpp | 101 ++++++++++++++++++++++++++++++++++ src/muz/spacer/spacer_mbc.h | 45 +++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 src/muz/spacer/spacer_mbc.cpp create mode 100644 src/muz/spacer/spacer_mbc.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 68464a40d..162216055 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -25,6 +25,7 @@ z3_add_component(spacer spacer_callback.cpp spacer_json.cpp spacer_iuc_proof.cpp + spacer_mbc.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_mbc.cpp b/src/muz/spacer/spacer_mbc.cpp new file mode 100644 index 000000000..d002998b3 --- /dev/null +++ b/src/muz/spacer/spacer_mbc.cpp @@ -0,0 +1,101 @@ +#include + +#include "muz/spacer/spacer_mbc.h" +#include "ast/rewriter/rewriter.h" +#include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/scoped_proof.h" +#include "model/model_evaluator.h" + + +namespace spacer { + +mbc::mbc(ast_manager &m) : m(m) {} + +namespace { +class mbc_rewriter_cfg : public default_rewriter_cfg { + + ast_manager &m; + const mbc::partition_map &m_pmap; + model &m_mdl; + model_evaluator m_mev; + vector &m_parts; + unsigned m_current_part; + + obj_map m_subs; +public: + mbc_rewriter_cfg(ast_manager &m, const mbc::partition_map &pmap, + model &mdl, vector &parts) : + m(m), m_pmap(pmap), m_mdl(mdl), m_mev(m_mdl), + m_parts(parts), m_current_part(UINT_MAX) + {m_mev.set_model_completion(true);} + + br_status reduce_app(func_decl *f, unsigned num, expr * const * args, + expr_ref &result, proof_ref & result_pr) { + unsigned part = UINT_MAX; + // not a constant + if (num != 0) return BR_FAILED; + // not in partition map + if (!m_pmap.find(f, part)) return BR_FAILED; + + // first part element, remember it + if (!found_partition()) { + set_partition(part); + return BR_FAILED; + } + + // decide value based on model + expr_ref e(m), val(m); + e = m.mk_app(f, num, args); + + // already in our substitution map + expr *t = nullptr; + if (m_subs.find(e, t)) { + result = t; + return BR_DONE; + } + + // eval in the model + m_mev.eval(e, val, true); + + // store decided equality + m_parts[part].push_back(m.mk_eq(e, val)); + // store substitution + m_subs.insert(e, val); + + result = val; + return BR_DONE; + } + + bool get_subst(expr * s, expr * & t, proof * & t_pr) { + return m_subs.find(s, t); + } + + void reset_partition() {m_current_part = UINT_MAX;} + unsigned partition() {return m_current_part;} + bool found_partition() {return m_current_part < UINT_MAX;} + void set_partition(unsigned v) {m_current_part = v;} +}; +} + +void mbc::operator()(const partition_map &pmap, expr_ref_vector &lits, + model &mdl, vector &res) { + scoped_no_proof _sp (m); + + mbc_rewriter_cfg cfg(m, pmap, mdl, res); + rewriter_tpl rw(m, false, cfg); + th_rewriter thrw(m); + + for (auto *lit : lits) { + expr_ref new_lit(m); + cfg.reset_partition(); + rw(lit, new_lit); + thrw(new_lit); + if (cfg.found_partition()) { + SASSERT(cfg.partition() < res.size()); + res[cfg.partition()].push_back(new_lit); + } + } +} + +} diff --git a/src/muz/spacer/spacer_mbc.h b/src/muz/spacer/spacer_mbc.h new file mode 100644 index 000000000..5dbf50f6d --- /dev/null +++ b/src/muz/spacer/spacer_mbc.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + spacer_mbc.h + +Abstract: + + Model-Based Cartesian Decomposition + +Author: + + Arie Gurfinkel + +Revision History: + +--*/ + +#ifndef _SPACER_MBC_H_ +#define _SPACER_MBC_H_ + +#include "ast/ast.h" +#include "util/obj_hashtable.h" +#include "model/model.h" + +namespace spacer { + +class mbc { + ast_manager &m; +public: + mbc(ast_manager &m); + + + typedef obj_map partition_map; + + /** + \Brief Model Based Cartesian projection of lits + */ + void operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl, + vector &res); +}; + +} +#endif From e860e4d0454874844c0ca4734c785bc661cd7039 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 15:54:05 -0700 Subject: [PATCH 227/364] Bug fix for quantified pob generation --- src/muz/spacer/spacer_context.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 7d9e6e5be..6e9f0db39 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -288,24 +288,26 @@ pob *derivation::create_next_child (model_evaluator_util &mev) timeit _timer2(is_trace_enabled("spacer_timeit"), "create_next_child::qproject2", verbose_stream ()); + // include m_evars in case they can eliminated now as well vars.append(m_evars); - m_evars.reset(); pt().mbp(vars, post, mev.get_model(), true, pt().get_context().use_ground_cti()); //qe::reduce_array_selects (*mev.get_model (), post); - - // remember variables that need to be existentially quantified - m_evars.append(vars); - vars.reset(); + } + else { + // if no variables to eliminate, don't forget about m_evars + // that occur in m_trans + vars.append(m_evars); } - if (!m_evars.empty()) { - // existentially quantify out m_evars from post and skolemize the result - exist_skolemize(post.get(), m_evars, post); + if (!vars.empty()) { + // existentially quantify out vars from post and skolemize the result + exist_skolemize(post.get(), vars, post); } get_manager ().formula_o2n (post.get (), post, - m_premises [m_active].get_oidx (), m_evars.empty()); + m_premises [m_active].get_oidx (), + vars.empty()); /* The level and depth are taken from the parent, not the sibling. @@ -313,7 +315,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) and lower level is a better starting point. */ pob *n = m_premises[m_active].pt().mk_pob(&m_parent, prev_level (m_parent.level ()), - m_parent.depth (), post, m_evars); + m_parent.depth (), post, vars); IF_VERBOSE (1, verbose_stream () << "\n\tcreate_child: " << n->pt ().head ()->get_name () << " (" << n->level () << ", " << n->depth () << ") " From 478d7c790e333b2aed5e2a264d2e4b5075a10baa Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 4 Jun 2018 16:11:27 -0700 Subject: [PATCH 228/364] mbc: moved code under get_subst() --- src/muz/spacer/spacer_mbc.cpp | 45 +++++++++++++++-------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/src/muz/spacer/spacer_mbc.cpp b/src/muz/spacer/spacer_mbc.cpp index d002998b3..4708a6a4e 100644 --- a/src/muz/spacer/spacer_mbc.cpp +++ b/src/muz/spacer/spacer_mbc.cpp @@ -30,45 +30,38 @@ public: m_parts(parts), m_current_part(UINT_MAX) {m_mev.set_model_completion(true);} - br_status reduce_app(func_decl *f, unsigned num, expr * const * args, - expr_ref &result, proof_ref & result_pr) { + bool get_subst(expr *s, expr * & t, proof * & t_pr) { + if (!is_app(s)) return false; unsigned part = UINT_MAX; - // not a constant - if (num != 0) return BR_FAILED; + // not in partition map - if (!m_pmap.find(f, part)) return BR_FAILED; + if (!m_pmap.find (to_app(s)->get_decl(), part)) return false; // first part element, remember it if (!found_partition()) { set_partition(part); - return BR_FAILED; + return false; + } + + // already in our substitution map + expr *tmp = nullptr; + if (m_subs.find(s, tmp)) { + t = tmp; + return true; } // decide value based on model - expr_ref e(m), val(m); - e = m.mk_app(f, num, args); - - // already in our substitution map - expr *t = nullptr; - if (m_subs.find(e, t)) { - result = t; - return BR_DONE; - } + expr_ref val(m); // eval in the model - m_mev.eval(e, val, true); + m_mev.eval(s, val, true); - // store decided equality - m_parts[part].push_back(m.mk_eq(e, val)); + // store decided equality (also keeps ref to s and val + m_parts[part].push_back(m.mk_eq(s, val)); // store substitution - m_subs.insert(e, val); - - result = val; - return BR_DONE; - } - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - return m_subs.find(s, t); + m_subs.insert(s, val); + t = val; + return true; } void reset_partition() {m_current_part = UINT_MAX;} From ece2e53c98c9434bbfd6a892db0b537f49e26443 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 12:00:13 -0700 Subject: [PATCH 229/364] Ported model_search and model_node from pdr into spacer --- src/muz/spacer/CMakeLists.txt | 1 + src/muz/spacer/spacer_pdr.cpp | 221 ++++++++++++++++++++++++++++++++++ src/muz/spacer/spacer_pdr.h | 107 ++++++++++++++++ 3 files changed, 329 insertions(+) create mode 100644 src/muz/spacer/spacer_pdr.cpp create mode 100644 src/muz/spacer/spacer_pdr.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 162216055..8e41d5ae7 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -26,6 +26,7 @@ z3_add_component(spacer spacer_json.cpp spacer_iuc_proof.cpp spacer_mbc.cpp + spacer_pdr.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp new file mode 100644 index 000000000..ce3f53963 --- /dev/null +++ b/src/muz/spacer/spacer_pdr.cpp @@ -0,0 +1,221 @@ +/**++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + spacer_pdr.h + +Abstract: + + SPACER gPDR strategy implementation + +Author: + + Arie Gurfinkel + + Based on muz/pdr + +Notes: + +--*/ +#include "muz/spacer/spacer_pdr.h" + +namespace spacer { +model_node::model_node(model_node* parent, pob_ref &pob): + m_pob(pob), m_parent(parent), m_next(nullptr), m_prev(nullptr), + m_orig_level(m_pob->level()), m_depth(0), + m_closed(false) { + SASSERT(m_pob); + if (m_parent) m_parent->add_child(*this); +} + +void model_node::add_child(model_node &kid) { + m_children.push_back(this); + SASSERT(level() == kid.level() + 1); + SASSERT(level() > 0); + kid.m_depth = m_depth + 1; + if (is_closed()) set_open(); +} + +unsigned model_node::index_in_parent() const { + if (!m_parent) return 0; + for (unsigned i = 0, sz = m_parent->children().size(); i < sz; ++i) { + if (this == m_parent->children().get(i)) return i; + } + UNREACHABLE(); + return 0; +} + +void model_node::check_pre_closed() { + for (auto *kid : m_children) {if (kid->is_open()) return;} + + set_pre_closed(); + model_node* p = m_parent; + while (p && p->is_1closed()) { + p->set_pre_closed(); + p = p->parent(); + } +} +void model_node::set_open() { + SASSERT(m_closed); + m_closed = false; + model_node* p = parent(); + while (p && p->is_closed()) { + p->m_closed = false; + p = p->parent(); + } +} + +void model_node::detach(model_node*& qhead) { + if (!in_queue()) return; + SASSERT(children().empty()); + if (this == m_next) { + SASSERT(m_prev == this); + SASSERT(this == qhead); + qhead = nullptr; + } + else { + m_next->m_prev = m_prev; + m_prev->m_next = m_next; + if (this == qhead) qhead = m_next; + } + + // detach + m_prev = nullptr; + m_next = nullptr; +} + + +// insert node n after this in the queue +// requires: this is in a queue or this == n +void model_node::insert_after(model_node* n) { + SASSERT(!in_queue()); + if (this == n) { + m_next = n; + m_prev = n; + } + else { + n->m_next = m_next; + m_next->m_prev = n; + m_next = n; + n->m_prev = this; + } +} + +void model_search::reset() { + m_cache.reset(); + if (m_root) { + erase_children(*m_root, false); + remove_node(*m_root, false); + dealloc(m_root); + m_root = nullptr; + } +} + +model_node* model_search::pop_front() { + if (!m_qhead) return nullptr; + model_node *res = m_qhead; + res->detach(m_qhead); + return res; +} + +void model_search::add_leaf(model_node& n) { + SASSERT(n.children().empty()); + model_nodes ns; + model_nodes& nodes = cache(n).insert_if_not_there2(n.post(), ns)->get_data().m_value; + if (nodes.contains(&n)) return; + + nodes.push_back(&n); + if (nodes.size() == 1) { + SASSERT(n.is_open()); + enqueue_leaf(n); + } + else n.set_pre_closed(); +} + +void model_search::enqueue_leaf(model_node& n) { + SASSERT(n.is_open()); + // queue is empty, initialize it with n + if (!m_qhead) { + m_qhead = &n; + m_qhead->insert_after(m_qhead); + } + // insert n after m_qhead + else if (m_bfs) { + m_qhead->insert_after(&n); + } + // insert n after m_qhead()->next() + else { + m_qhead->next()->insert_after(&n); + } +} + + + +void model_search::set_root(model_node* root) { + reset(); + m_root = root; + SASSERT(m_root); + SASSERT(m_root->children().empty()); + SASSERT(cache(*root).empty()); + // XXX Don't get why 1 is legal here + cache(*root).insert(root->post(), 1); + enqueue_leaf(*root); +} + +void model_search::backtrack_level(bool uses_level, model_node& n) { + SASSERT(m_root); + if (uses_level) {NOT_IMPLEMENTED_YET();} + if (uses_level && m_root->level() > n.level()) { + n.increase_level(); + enqueue_leaf(n); + } + else { + model_node* p = n.parent(); + if (p) { + erase_children(*p, true); + enqueue_leaf(*p); + } + } +} + +obj_map >& model_search::cache(model_node const& n) { + unsigned l = n.orig_level(); + if (l >= m_cache.size()) m_cache.resize(l + 1); + return m_cache[l]; +} + +void model_search::erase_children(model_node& n, bool backtrack) { + ptr_vector todo, nodes; + todo.append(n.children()); + // detach n from queue + n.detach(m_qhead); + n.reset(); + while (!todo.empty()) { + model_node* m = todo.back(); + todo.pop_back(); + nodes.push_back(m); + todo.append(m->children()); + remove_node(*m, backtrack); + } + std::for_each(nodes.begin(), nodes.end(), delete_proc()); +} + +// removes node from the search tree and from the cache +void model_search::remove_node(model_node& n, bool backtrack) { + model_nodes& nodes = cache(n).find(n.post()); + nodes.erase(&n); + n.detach(m_qhead); + // TBD: siblings would also fail if n is not a goal. + if (!nodes.empty() && backtrack && + nodes[0]->children().empty() && nodes[0]->is_closed()) { + model_node* n1 = nodes[0]; + n1->set_open(); + enqueue_leaf(*n1); + } + + if (nodes.empty()) cache(n).remove(n.post()); +} + + +} diff --git a/src/muz/spacer/spacer_pdr.h b/src/muz/spacer/spacer_pdr.h new file mode 100644 index 000000000..dd62230bd --- /dev/null +++ b/src/muz/spacer/spacer_pdr.h @@ -0,0 +1,107 @@ +/**++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + spacer_pdr.h + +Abstract: + + SPACER gPDR strategy implementation + +Author: + + Arie Gurfinkel + + Based on muz/pdr + +Notes: + +--*/ +#ifndef _SPACER_PDR_H_ +#define _SPACER_PDR_H_ + +#include "muz/spacer/spacer_context.h" + +namespace spacer { +// structure for counter-example search. +class model_node { + pob_ref m_pob; // proof obligation + model_node* m_parent; // parent in the search tree + ptr_vector m_children; // children in the search tree + model_node* m_next; // next element of an in-place circular queue + model_node* m_prev; // prev element of an in-place circular queue + unsigned m_orig_level; // level at which this search node was created + unsigned m_depth; // + bool m_closed; // whether the pob is derivable +public: + model_node(model_node* parent, pob_ref &pob); + void add_child(model_node &kid); + + expr *post() const {return m_pob->post();} + unsigned level() const { return m_pob->level(); } + unsigned orig_level() const { return m_orig_level; } + unsigned depth() const { return m_depth; } + void increase_level() { m_pob->inc_level(); } + const pob_ref &pob() const { return m_pob; } + ptr_vector const& children() { return m_children; } + pred_transformer& pt() const { return m_pob->pt(); } + model_node* parent() const { return m_parent; } + // order in children of the parent + unsigned index_in_parent() const; + + bool is_closed() const { return m_closed; } + bool is_open() const { return !is_closed(); } + + // closed or has children and they are all closed + bool is_1closed() { + if (is_closed()) return true; + if (m_children.empty()) return false; + for (auto kid : m_children) {if (kid->is_open()) return false;} + return true; + } + + void check_pre_closed(); + void set_pre_closed() {m_closed = true;} + + void set_closed() {m_closed = true;} + void set_open(); + void reset() {m_children.reset();} + + /// queue + + // remove this node from the given queue + void detach(model_node*& qhead); + void insert_after(model_node* n); + model_node* next() const {return m_next;} + bool in_queue() {return m_next && m_prev;} +}; + +class model_search { + typedef ptr_vector model_nodes; + bool m_bfs; + model_node* m_root; + model_node* m_qhead; + vector > m_cache; + obj_map& cache(model_node const& n); + void erase_children(model_node& n, bool backtrack); + void remove_node(model_node& n, bool backtrack); + void add_leaf(model_node* n); // add leaf to priority queue. + +public: + model_search(bool bfs): m_bfs(bfs), m_root(nullptr), m_qhead(nullptr) {} + ~model_search() {reset();} + + void set_root(model_node* n); + + void reset(); + model_node* pop_front(); + void add_leaf(model_node& n); // add fresh node. + model_node& get_root() const { return *m_root; } + void backtrack_level(bool uses_level, model_node& n); + void remove_goal(model_node& n); + + void enqueue_leaf(model_node &n); +}; +} +#endif From ab5f579d0b41c9192208d5a8f89c8c2efd31e017 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 16:12:12 -0700 Subject: [PATCH 230/364] Comments in pdr_context.cpp --- src/muz/pdr/pdr_context.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index c2cd94ee0..1b1350617 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -210,7 +210,7 @@ namespace pdr { void pred_transformer::simplify_formulas() { tactic_ref us = mk_unit_subsumption_tactic(m); simplify_formulas(*us, m_invariants); - for (auto & fmls : m_levels) + for (auto & fmls : m_levels) simplify_formulas(*us, fmls); } @@ -876,6 +876,7 @@ namespace pdr { return out; } + // return order of this node in the children of its parent unsigned model_node::index() const { model_node* p = parent(); if (!p) return 0; @@ -886,7 +887,8 @@ namespace pdr { return 0; } - + // detach this node from a queue with the head root + // requires: root is a head of a queue void model_node::dequeue(model_node*& root) { TRACE("pdr", tout << this << " root: " << root << " " << state() << "\n";); if (!m_next && !m_prev) return; @@ -912,6 +914,8 @@ namespace pdr { } + // insert node n after this in the queue + // requires: this is in a queue or this == n void model_node::enqueue(model_node* n) { TRACE("pdr", tout << n << " " << n->state() << "\n";); SASSERT(!n->m_next); @@ -963,6 +967,7 @@ namespace pdr { } void model_search::set_leaf(model_node& n) { + // remove all children that n might have erase_children(n, true); SASSERT(n.is_open()); enqueue_leaf(&n); @@ -971,13 +976,16 @@ namespace pdr { void model_search::enqueue_leaf(model_node* n) { TRACE("pdr_verbose", tout << "node: " << n << " " << n->state() << " goal: " << m_goal << "\n";); SASSERT(n->is_open()); + // queue is empty, initialize it with n if (!m_goal) { m_goal = n; m_goal->enqueue(n); } + // insert n after m_goal else if (m_bfs) { m_goal->enqueue(n); } + // insert n after m_goal()->next() else { m_goal->next()->enqueue(n); } @@ -1002,7 +1010,9 @@ namespace pdr { void model_search::erase_children(model_node& n, bool backtrack) { ptr_vector todo, nodes; todo.append(n.children()); + // detach n from queue remove_goal(n); + // removes children n.reset(); while (!todo.empty()) { model_node* m = todo.back(); @@ -1014,10 +1024,12 @@ namespace pdr { std::for_each(nodes.begin(), nodes.end(), delete_proc()); } + // removes node from the search tree and from the cache void model_search::remove_node(model_node& n, bool backtrack) { TRACE("pdr_verbose", tout << "remove: " << n.level() << ": " << &n << " " << n.state() << "\n";); model_nodes& nodes = cache(n).find(n.state()); nodes.erase(&n); + // detach n from m_goals remove_goal(n); // TBD: siblings would also fail if n is not a goal. if (!nodes.empty() && backtrack && nodes[0]->children().empty() && nodes[0]->is_closed()) { @@ -1036,6 +1048,7 @@ namespace pdr { } } + // detach node n from the queue m_goal void model_search::remove_goal(model_node& n) { n.dequeue(m_goal); } @@ -1913,7 +1926,7 @@ namespace pdr { verbose_stream() << ex.to_string(); }); - // upgrade invariants that are known to be inductive. + // upgrade invariants that are known to be inductive. decl2rel::iterator it = m_rels.begin (), end = m_rels.end (); for (; m_inductive_lvl > 0 && it != end; ++it) { if (it->m_value->head() != m_query_pred) { From 521392a8f15ec4b19f273d63fb3a9ca09d9841e1 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 16:12:45 -0700 Subject: [PATCH 231/364] First partially working pdr strategy in spacer --- src/muz/base/fixedpoint_params.pyg | 1 + src/muz/spacer/spacer_context.cpp | 13 +++-- src/muz/spacer/spacer_context.h | 6 +++ src/muz/spacer/spacer_pdr.cpp | 86 ++++++++++++++++++++++++++---- src/muz/spacer/spacer_pdr.h | 6 +-- 5 files changed, 95 insertions(+), 17 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 69050ed71..b5231c27b 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -203,4 +203,5 @@ def_module_params('fixedpoint', ('spacer.use_inc_clause', BOOL, False, 'Use incremental clause to represent trans'), ('spacer.dump_benchmarks', BOOL, False, 'Dump SMT queries as benchmarks'), ('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'), + ('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'), )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 6e9f0db39..3f3433cc6 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2591,7 +2591,14 @@ lbool context::solve(unsigned from_lvl) { m_last_result = l_undef; try { - m_last_result = solve_core (from_lvl); + if (m_params.spacer_gpdr()) { + SASSERT(from_lvl == 0); + m_last_result = gpdr_solve_core(); + } + else { + m_last_result = solve_core (from_lvl); + } + if (m_last_result == l_false) { simplify_formulas(); m_last_result = l_false; @@ -2967,7 +2974,7 @@ lbool context::solve_core (unsigned from_lvl) unsigned max_level = get_params ().spacer_max_level (); - for (unsigned i = 0; i < max_level; ++i) { + for (unsigned i = from_lvl; i < max_level; ++i) { checkpoint(); m_expanded_lvl = infty_level (); m_stats.m_max_query_lvl = lvl; @@ -3244,6 +3251,7 @@ void context::predecessor_eh() /// out contains new pobs to add to the queue in case the result is l_undef lbool context::expand_pob(pob& n, pob_ref_buffer &out) { + SASSERT(out.empty()); pob::on_expand_event _evt(n); TRACE ("spacer", tout << "expand-pob: " << n.pt().head()->get_name() @@ -3630,7 +3638,6 @@ bool context::create_children(pob& n, datalog::rule const& r, const vector &reach_pred_used, pob_ref_buffer &out) { - scoped_watch _w_ (m_create_children_watch); pred_transformer& pt = n.pt(); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index ccdac2c48..bb3ad007d 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -42,6 +42,8 @@ namespace datalog { namespace spacer { +class model_search; + class pred_transformer; class derivation; class pob_queue; @@ -848,6 +850,10 @@ class context { scoped_ptr_vector m_callbacks; json_marshaller m_json_marshaller; + // Solve using gpdr strategy + lbool gpdr_solve_core(); + bool gpdr_check_reachability(unsigned lvl, model_search &ms); + // Functions used by search. lbool solve_core(unsigned from_lvl = 0); bool is_requeue(pob &n); diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index ce3f53963..e46907493 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -19,9 +19,10 @@ Notes: --*/ #include "muz/spacer/spacer_pdr.h" +#include "muz/base/dl_context.h" namespace spacer { -model_node::model_node(model_node* parent, pob_ref &pob): +model_node::model_node(model_node* parent, class pob *pob): m_pob(pob), m_parent(parent), m_next(nullptr), m_prev(nullptr), m_orig_level(m_pob->level()), m_depth(0), m_closed(false) { @@ -30,7 +31,7 @@ model_node::model_node(model_node* parent, pob_ref &pob): } void model_node::add_child(model_node &kid) { - m_children.push_back(this); + m_children.push_back(&kid); SASSERT(level() == kid.level() + 1); SASSERT(level() > 0); kid.m_depth = m_depth + 1; @@ -67,7 +68,7 @@ void model_node::set_open() { } void model_node::detach(model_node*& qhead) { - if (!in_queue()) return; + SASSERT(in_queue()); SASSERT(children().empty()); if (this == m_next) { SASSERT(m_prev == this); @@ -103,13 +104,13 @@ void model_node::insert_after(model_node* n) { } void model_search::reset() { - m_cache.reset(); if (m_root) { erase_children(*m_root, false); remove_node(*m_root, false); dealloc(m_root); m_root = nullptr; } + m_cache.reset(); } model_node* model_search::pop_front() { @@ -135,6 +136,7 @@ void model_search::add_leaf(model_node& n) { void model_search::enqueue_leaf(model_node& n) { SASSERT(n.is_open()); + SASSERT(!n.in_queue()); // queue is empty, initialize it with n if (!m_qhead) { m_qhead = &n; @@ -157,10 +159,7 @@ void model_search::set_root(model_node* root) { m_root = root; SASSERT(m_root); SASSERT(m_root->children().empty()); - SASSERT(cache(*root).empty()); - // XXX Don't get why 1 is legal here - cache(*root).insert(root->post(), 1); - enqueue_leaf(*root); + add_leaf(*root); } void model_search::backtrack_level(bool uses_level, model_node& n) { @@ -189,8 +188,8 @@ void model_search::erase_children(model_node& n, bool backtrack) { ptr_vector todo, nodes; todo.append(n.children()); // detach n from queue - n.detach(m_qhead); - n.reset(); + if (n.in_queue()) n.detach(m_qhead); + n.reset_children(); while (!todo.empty()) { model_node* m = todo.back(); todo.pop_back(); @@ -205,7 +204,7 @@ void model_search::erase_children(model_node& n, bool backtrack) { void model_search::remove_node(model_node& n, bool backtrack) { model_nodes& nodes = cache(n).find(n.post()); nodes.erase(&n); - n.detach(m_qhead); + if (n.in_queue()) n.detach(m_qhead); // TBD: siblings would also fail if n is not a goal. if (!nodes.empty() && backtrack && nodes[0]->children().empty() && nodes[0]->is_closed()) { @@ -218,4 +217,69 @@ void model_search::remove_node(model_node& n, bool backtrack) { } +lbool context::gpdr_solve_core() { + scoped_watch _w_(m_solve_watch); + //if there is no query predicate, abort + if (!m_rels.find(m_query_pred, m_query)) { return l_false; } + + model_search ms(true); + unsigned lvl = 0; + unsigned max_level = get_params ().spacer_max_level (); + for (lvl = 0; lvl < max_level; ++lvl) { + checkpoint(); + IF_VERBOSE(1,verbose_stream() << "GPDR Entering level "<< lvl << "\n";); + STRACE("spacer.expand-add", tout << "\n* LEVEL " << lvl << "\n";); + m_expanded_lvl = infty_level(); + m_stats.m_max_query_lvl = lvl; + if (gpdr_check_reachability(lvl, ms)) {return l_true;} + if (lvl > 0) { + if (propagate(m_expanded_lvl, lvl, UINT_MAX)) {return l_false;} + } + } + + // communicate failure to datalog::context + if (m_context) { m_context->set_status(datalog::BOUNDED); } + return l_undef; + } + +bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { + pob_ref root_pob = m_query->mk_pob(nullptr, lvl, 0, m.mk_true()); + model_node *root_node = alloc(model_node, nullptr, root_pob.get()); + + ms.set_root(root_node); + pob_ref_buffer new_pobs; + + while (model_node *node = ms.pop_front()) { + IF_VERBOSE(2, verbose_stream() << "Expand node: " + << node->level() << "\n";); + new_pobs.reset(); + checkpoint(); + switch (expand_pob(*node->pob(), new_pobs)){ + case l_true: + node->set_closed(); + if (node == root_node) return true; + break; + case l_false: + ms.backtrack_level(false, *node); + if (node == root_node) return false; + break; + case l_undef: + SASSERT(!new_pobs.empty()); + for (auto pob : new_pobs) { + TRACE("spacer_pdr", + tout << "looking at pob at level " << pob->level() << " " + << mk_pp(pob->post(), m) << "\n";); + if (pob == node->pob()) {continue;} + model_node *kid = alloc(model_node, node, pob); + ms.add_leaf(*kid); + } + node->check_pre_closed(); + break; + } + } + + return root_node->is_closed(); +} + +} // spacer diff --git a/src/muz/spacer/spacer_pdr.h b/src/muz/spacer/spacer_pdr.h index dd62230bd..56900a1e8 100644 --- a/src/muz/spacer/spacer_pdr.h +++ b/src/muz/spacer/spacer_pdr.h @@ -35,7 +35,7 @@ class model_node { unsigned m_depth; // bool m_closed; // whether the pob is derivable public: - model_node(model_node* parent, pob_ref &pob); + model_node(model_node* parent, pob* pob); void add_child(model_node &kid); expr *post() const {return m_pob->post();} @@ -43,7 +43,7 @@ public: unsigned orig_level() const { return m_orig_level; } unsigned depth() const { return m_depth; } void increase_level() { m_pob->inc_level(); } - const pob_ref &pob() const { return m_pob; } + pob_ref &pob() { return m_pob; } ptr_vector const& children() { return m_children; } pred_transformer& pt() const { return m_pob->pt(); } model_node* parent() const { return m_parent; } @@ -66,7 +66,7 @@ public: void set_closed() {m_closed = true;} void set_open(); - void reset() {m_children.reset();} + void reset_children() {m_children.reset();} /// queue From cb683389f66852dabd449a234d49b67de7023d6a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 16:20:55 -0700 Subject: [PATCH 232/364] spacer::context: Factor params into udpt_params --- src/muz/spacer/spacer_context.cpp | 8 ++++++-- src/muz/spacer/spacer_context.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 3f3433cc6..8806afb6a 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2244,8 +2244,7 @@ context::context(fixedpoint_params const& params, m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); - m_random.set_seed(m_params.spacer_random_seed()); - m_children_order = static_cast(m_params.spacer_order_children()); + updt_params() } context::~context() @@ -2254,6 +2253,11 @@ context::~context() reset(); } +void context::updt_params() { + m_random.set_seed(m_params.spacer_random_seed()); + m_children_order = static_cast(m_params.spacer_order_children()); +} + void context::reset() { TRACE("spacer", tout << "\n";); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index bb3ad007d..e00d479f7 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -897,6 +897,7 @@ class context { void predecessor_eh(); + void updt_params(); public: /** Initial values of predicates are stored in corresponding relations in dctx. @@ -905,6 +906,7 @@ public: context(fixedpoint_params const& params, ast_manager& m); ~context(); + const fixedpoint_params &get_params() const { return m_params; } bool use_native_mbp () {return m_use_native_mbp;} bool use_ground_cti () {return m_ground_cti;} From 8b689ae27fd90afebc123a9d3abf71724b60fdbc Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 17:11:19 -0700 Subject: [PATCH 233/364] Moved is_int_expr into arith_recognizers --- src/ast/arith_decl_plugin.cpp | 31 ++- src/ast/arith_decl_plugin.h | 3 +- src/smt/theory_arith_core.h | 469 ++++++++++++++++------------------ 3 files changed, 253 insertions(+), 250 deletions(-) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index 688edbcd5..fe5bc3af5 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -81,7 +81,7 @@ app * arith_decl_plugin::mk_numeral(algebraic_numbers::anum const & val, bool is return mk_numeral(rval, is_int); } else { - if (is_int) { + if (is_int) { m_manager->raise_exception("invalid irrational value passed as an integer"); } unsigned idx = aw().mk_id(val); @@ -638,6 +638,35 @@ bool arith_recognizers::is_numeral(expr const * n, rational & val, bool & is_int return true; } +#define IS_INT_EXPR_DEPTH_LIMIT 100 +bool arith_recognizers::is_int_expr(expr const *e) const { + if (is_int(e)) return true; + if (is_uninterp(e)) return false; + ptr_buffer todo; + todo.push_back(e); + rational r; + unsigned i = 0; + while (!todo.empty()) { + ++i; + if (i > IS_INT_EXPR_DEPTH_LIMIT) {return false;} + e = todo.back(); + todo.pop_back(); + if (is_to_real(e)) { + // pass + } + else if (is_numeral(e, r) && r.is_int()) { + // pass + } + else if (is_add(e) || is_mul(e)) { + todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + } + else { + return false; + } + } + return true; +} + arith_util::arith_util(ast_manager & m): arith_recognizers(m.mk_family_id("arith")), m_manager(m), diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 6cebdaded..d7340297b 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -244,6 +244,8 @@ public: return false; } + bool is_int_expr(expr const * e) const; + bool is_le(expr const * n) const { return is_app_of(n, m_afid, OP_LE); } bool is_ge(expr const * n) const { return is_app_of(n, m_afid, OP_GE); } bool is_lt(expr const * n) const { return is_app_of(n, m_afid, OP_LT); } @@ -533,4 +535,3 @@ inline app_ref operator>(app_ref const& x, app_ref const& y) { } #endif /* ARITH_DECL_PLUGIN_H_ */ - diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 46388fcf4..af761eecf 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -27,7 +27,7 @@ Revision History: #include "ast/ast_smt2_pp.h" namespace smt { - + template void theory_arith::found_unsupported_op(app * n) { if (!m_found_unsupported_op) { @@ -69,33 +69,7 @@ namespace smt { #if 0 return m_util.is_int(e); #else - if (m_util.is_int(e)) return true; - if (is_uninterp(e)) return false; - m_todo.reset(); - m_todo.push_back(e); - rational r; - unsigned i = 0; - while (!m_todo.empty()) { - ++i; - if (i > 100) { - return false; - } - e = m_todo.back(); - m_todo.pop_back(); - if (m_util.is_to_real(e)) { - // pass - } - else if (m_util.is_numeral(e, r) && r.is_int()) { - // pass - } - else if (m_util.is_add(e) || m_util.is_mul(e)) { - m_todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - } - else { - return false; - } - } - return true; + return m_util.is_int_expr(e); #endif } @@ -133,7 +107,7 @@ namespace smt { m_nl_monomials.push_back(r); SASSERT(check_vector_sizes()); SASSERT(m_var_occs[r].empty()); - TRACE("mk_arith_var", + TRACE("mk_arith_var", tout << "#" << n->get_owner_id() << " :=\n" << mk_ll_pp(n->get_owner(), get_manager()) << "\n"; tout << "is_attached_to_var: " << is_attached_to_var(n) << ", var: " << n->get_th_var(get_id()) << "\n";); get_context().attach_th_var(n, this, r); @@ -175,15 +149,15 @@ namespace smt { } /** - \brief Create an enode for n. + \brief Create an enode for n. */ template enode * theory_arith::mk_enode(app * n) { context & ctx = get_context(); - if (ctx.e_internalized(n)) + if (ctx.e_internalized(n)) return ctx.get_enode(n); else - return ctx.mk_enode(n, !reflect(n), false, enable_cgc_for(n)); + return ctx.mk_enode(n, !reflect(n), false, enable_cgc_for(n)); } /** @@ -239,13 +213,13 @@ namespace smt { row_entry & r_entry = r.add_row_entry(r_idx); int c_idx; col_entry & c_entry = c.add_col_entry(c_idx); - + r_entry.m_var = v; r_entry.m_coeff = coeff; if (invert) r_entry.m_coeff .neg(); r_entry.m_col_idx = c_idx; - + c_entry.m_row_id = r_id; c_entry.m_row_idx = r_idx; } @@ -266,7 +240,7 @@ namespace smt { return; } } - rational _val; + rational _val; expr* arg1, *arg2; if (m_util.is_mul(m, arg1, arg2) && m_util.is_numeral(arg1, _val) && is_app(arg1) && is_app(arg2)) { SASSERT(m->get_num_args() == 2); @@ -315,7 +289,7 @@ namespace smt { // HACK: n was already internalized by the internalize_internal_monomial or internalize_internal_add call above. // This can happen when one of calls invoke (indirectly) mk_axiom. // For example, they contain a nested to_int(t) term. - // TODO: reimplement mk_axiom. The current implementation is flaky. + // TODO: reimplement mk_axiom. The current implementation is flaky. // I should cache the axioms that need to be created. They should only be internalized after we finished internalizing the // current atom. Several other theories have similar problems. del_row(r_id); @@ -348,7 +322,7 @@ namespace smt { } /** - \brief Internalize the terms of the form (* c (* t1 ... tn)) and (* t1 ... tn). + \brief Internalize the terms of the form (* c (* t1 ... tn)) and (* t1 ... tn). Return an alias for the term. */ template @@ -389,7 +363,7 @@ namespace smt { return expr2var(n); ctx.internalize(n->get_arg(0), false); ctx.internalize(n->get_arg(1), false); - enode * e = mk_enode(n); + enode * e = mk_enode(n); return mk_var(e); } @@ -479,7 +453,7 @@ namespace smt { ctx.mark_as_relevant(l_conseq); } else { - // We must mark the antecedent as relevant, otherwise the + // We must mark the antecedent as relevant, otherwise the // core will not propagate it to the theory of arithmetic. // In a previous version, we were not doing that. // The core was assigning it to true, this assignment was inconsistent with @@ -496,7 +470,7 @@ namespace smt { if (!m_util.is_zero(q)) { ast_manager & m = get_manager(); expr_ref div(m), zero(m), eqz(m), eq(m); - TRACE("div_axiom_bug", tout << "expanding div_axiom for: " << mk_pp(p, m) << " / " << mk_pp(q, m) << "\n";); + TRACE("div_axiom_bug", tout << "expanding div_axiom for: " << mk_pp(p, m) << " / " << mk_pp(q, m) << "\n";); div = m_util.mk_div(p, q); zero = m_util.mk_numeral(rational(0), false); eqz = m.mk_eq(q, zero); @@ -525,7 +499,7 @@ namespace smt { eq = m.mk_eq(m_util.mk_add(m_util.mk_mul(divisor, div), mod), dividend); lower = m_util.mk_ge(mod, zero); upper = m_util.mk_le(mod, abs_divisor); - TRACE("div_axiom_bug", + TRACE("div_axiom_bug", tout << "eqz: " << eqz << " neq: " << eq << "\n"; tout << "lower: " << lower << "\n"; tout << "upper: " << upper << "\n";); @@ -534,7 +508,7 @@ namespace smt { mk_axiom(eqz, lower, !is_numeral); mk_axiom(eqz, upper, !is_numeral); rational k; - if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && + if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && k.is_pos() && k < rational(8)) { rational j(0); #if 1 @@ -550,7 +524,7 @@ namespace smt { j += rational(1); } ctx.mk_th_axiom(get_id(), lits.size(), lits.begin()); - + #else // performs slightly worse. literal_buffer lits; @@ -567,7 +541,7 @@ namespace smt { j += rational(1); } #endif - } + } } } @@ -586,12 +560,12 @@ namespace smt { mk_axiom(dltz, eq1); dltz = m.mk_not(dltz); // !n < 0 || rem(a,n) = -mod(a, n) - mk_axiom(dltz, eq2); + mk_axiom(dltz, eq2); } // // create the term: s := x - to_real(to_int(x)) - // add the bounds 0 <= s < 1 + // add the bounds 0 <= s < 1 // template void theory_arith::mk_to_int_axiom(app * n) { @@ -606,7 +580,7 @@ namespace smt { } expr_ref to_r(m_util.mk_to_real(n), m); expr_ref diff(m_util.mk_add(x, m_util.mk_mul(m_util.mk_real(-1), to_r)), m); - + expr_ref lo(m_util.mk_ge(diff, m_util.mk_real(0)), m); expr_ref hi(m_util.mk_ge(diff, m_util.mk_real(1)), m); hi = m.mk_not(hi); @@ -631,7 +605,7 @@ namespace smt { // // Create the axiom (iff (is_int x) (= x (to_real (to_int x)))) - // + // template void theory_arith::mk_is_int_axiom(app * n) { @@ -718,7 +692,7 @@ namespace smt { ++m_top; } ~scoped_row_vars() { - --m_top; + --m_top; } }; @@ -738,7 +712,7 @@ namespace smt { SASSERT(!m_util.is_sub(n)); SASSERT(!m_util.is_uminus(n)); - + if (m_util.is_add(n)) return internalize_add(n); else if (m_util.is_mul(n)) @@ -747,9 +721,9 @@ namespace smt { return internalize_div(n); else if (m_util.is_idiv(n)) return internalize_idiv(n); - else if (m_util.is_mod(n)) + else if (m_util.is_mod(n)) return internalize_mod(n); - else if (m_util.is_rem(n)) + else if (m_util.is_rem(n)) return internalize_rem(n); else if (m_util.is_to_real(n)) return internalize_to_real(n); @@ -844,7 +818,7 @@ namespace smt { /** \brief Collect variables in the given row that have the given kind, but a different from the row main var (i.e., var that owns the row). - + The inv of the coefficients is also stored in result */ template @@ -864,7 +838,7 @@ namespace smt { } /** - \brief Normalize row as a quasi base row, it does not contain quasi-base + \brief Normalize row as a quasi base row, it does not contain quasi-base variables different from r.m_base_var. */ template @@ -910,7 +884,7 @@ namespace smt { // For example, consider the following scenario: // // 1) s is a quasi-base var, s depends on x, and value of x is v0 - // + // // 2) x is updated to v1, but the update does not affect s (s is a quasi-base var). // // 3) quasi_base_row2base_row is executed, and we compute the value of s using @@ -920,7 +894,7 @@ namespace smt { // // 5) if this branch is deleted, the row owned by s will not satisfy // valid_row_assignment. - // + // m_value[s] = tmp; SASSERT(!m_in_update_trail_stack.contains(s)); save_value(s); @@ -962,8 +936,8 @@ namespace smt { TRACE("mk_bound_axioms", tout << "add bound axioms for v" << v << " " << a1 << "\n";); if (!get_context().is_searching()) { // - // NB. We make an assumption that user push calls propagation - // before internal scopes are pushed. This flushes all newly + // NB. We make an assumption that user push calls propagation + // before internal scopes are pushed. This flushes all newly // asserted atoms into the right context. // m_new_atoms.push_back(a1); @@ -978,7 +952,7 @@ namespace smt { typename atoms::iterator lo_inf = end, lo_sup = end; typename atoms::iterator hi_inf = end, hi_sup = end; for (; it != end; ++it) { - atom * a2 = *it; + atom * a2 = *it; inf_numeral const & k2(a2->get_k()); atom_kind kind2 = a2->get_atom_kind(); TRACE("mk_bound_axioms", display_atom(tout << "compare " << a2 << " ", a2, true); tout << "\n";); @@ -1006,7 +980,7 @@ namespace smt { else if (hi_sup == end || k2 < (*hi_sup)->get_k()) { hi_sup = it; } - } + } if (lo_inf != end) mk_bound_axiom(a1, *lo_inf); if (lo_sup != end) mk_bound_axiom(a1, *lo_sup); if (hi_inf != end) mk_bound_axiom(a1, *hi_inf); @@ -1017,8 +991,8 @@ namespace smt { void theory_arith::mk_bound_axiom(atom* a1, atom* a2) { TRACE("mk_bound_axioms", tout << a1 << " " << a2 << "\n";); theory_var v = a1->get_var(); - literal l1(a1->get_bool_var()); - literal l2(a2->get_bool_var()); + literal l1(a1->get_bool_var()); + literal l2(a2->get_bool_var()); inf_numeral const & k1(a1->get_k()); inf_numeral const & k2(a2->get_k()); atom_kind kind1 = a1->get_atom_kind(); @@ -1027,7 +1001,7 @@ namespace smt { SASSERT(v == a2->get_var()); if (k1 == k2 && kind1 == kind2) return; SASSERT(k1 != k2 || kind1 != kind2); - parameter coeffs[3] = { parameter(symbol("farkas")), + parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) }; if (kind1 == A_LOWER) { @@ -1059,7 +1033,7 @@ namespace smt { } else { // k1 < k2, k2 <= x => ~(x <= k1) - mk_clause(~l1, ~l2, 3, coeffs); + mk_clause(~l1, ~l2, 3, coeffs); if (v_is_int && k1 == k2 - inf_numeral(1)) { // x <= k1 or k1+l <= x mk_clause(l1, l2, 3, coeffs); @@ -1077,7 +1051,7 @@ namespace smt { // k1 <= hi_sup , x <= k1 => x <= hi_sup mk_clause(~l1, l2, 3, coeffs); } - } + } } template @@ -1085,7 +1059,7 @@ namespace smt { CTRACE("arith_verbose", !m_new_atoms.empty(), tout << "flush bound axioms\n";); while (!m_new_atoms.empty()) { - ptr_vector atoms; + ptr_vector atoms; atoms.push_back(m_new_atoms.back()); m_new_atoms.pop_back(); theory_var v = atoms.back()->get_var(); @@ -1096,8 +1070,8 @@ namespace smt { m_new_atoms.pop_back(); --i; } - } - CTRACE("arith", atoms.size() > 1, + } + CTRACE("arith", atoms.size() > 1, for (unsigned i = 0; i < atoms.size(); ++i) { atoms[i]->display(*this, tout); tout << "\n"; }); @@ -1124,10 +1098,10 @@ namespace smt { hi_inf1 = next_inf(a1, A_UPPER, hi_inf, end, fhi_inf); lo_sup1 = next_sup(a1, A_LOWER, lo_sup, end, flo_sup); hi_sup1 = next_sup(a1, A_UPPER, hi_sup, end, fhi_sup); - if (lo_inf1 != end) lo_inf = lo_inf1; - if (lo_sup1 != end) lo_sup = lo_sup1; - if (hi_inf1 != end) hi_inf = hi_inf1; - if (hi_sup1 != end) hi_sup = hi_sup1; + if (lo_inf1 != end) lo_inf = lo_inf1; + if (lo_sup1 != end) lo_sup = lo_sup1; + if (hi_inf1 != end) hi_inf = hi_inf1; + if (hi_sup1 != end) hi_sup = hi_sup1; if (!flo_inf) lo_inf = end; if (!fhi_inf) hi_inf = end; if (!flo_sup) lo_sup = end; @@ -1137,15 +1111,15 @@ namespace smt { if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(a1, *lo_sup); if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(a1, *hi_inf); if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(a1, *hi_sup); - } + } } } template - typename theory_arith::atoms::iterator + typename theory_arith::atoms::iterator theory_arith::first( - atom_kind kind, - typename atoms::iterator it, + atom_kind kind, + typename atoms::iterator it, typename atoms::iterator end) { for (; it != end; ++it) { atom* a = *it; @@ -1155,18 +1129,18 @@ namespace smt { } template - typename theory_arith::atoms::iterator + typename theory_arith::atoms::iterator theory_arith::next_inf( - atom* a1, - atom_kind kind, - typename atoms::iterator it, + atom* a1, + atom_kind kind, + typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { inf_numeral const & k1(a1->get_k()); typename atoms::iterator result = end; found_compatible = false; for (; it != end; ++it) { - atom * a2 = *it; + atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; inf_numeral const & k2(a2->get_k()); @@ -1182,17 +1156,17 @@ namespace smt { } template - typename theory_arith::atoms::iterator + typename theory_arith::atoms::iterator theory_arith::next_sup( - atom* a1, - atom_kind kind, - typename atoms::iterator it, + atom* a1, + atom_kind kind, + typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { inf_numeral const & k1(a1->get_k()); found_compatible = false; for (; it != end; ++it) { - atom * a2 = *it; + atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; inf_numeral const & k2(a2->get_k()); @@ -1234,7 +1208,7 @@ namespace smt { app * rhs = to_app(n->get_arg(1)); expr * rhs2; if (m_util.is_to_real(rhs, rhs2) && is_app(rhs2)) { rhs = to_app(rhs2); } - if (!m_util.is_numeral(rhs)) { + if (!m_util.is_numeral(rhs)) { UNREACHABLE(); throw default_exception("malformed atomic constraint"); } @@ -1290,7 +1264,7 @@ namespace smt { enode * n1 = ctx.get_enode(lhs); enode * n2 = ctx.get_enode(rhs); // The expression atom may be a theory axiom. In this case, it may not be in simplified form. - // So, an atom such as (= a a) may occur. The procedure mk_axioms, expects n1 != n2. + // So, an atom such as (= a a) may occur. The procedure mk_axioms, expects n1 != n2. // So, we should check it. It doesn't make sense to create an axiom for (= a a) in the arith_eq_adapter. if (n1->get_th_var(get_id()) != null_theory_var && n2->get_th_var(get_id()) != null_theory_var && @@ -1305,7 +1279,7 @@ namespace smt { void theory_arith::apply_sort_cnstr(enode * n, sort * s) { // do nothing... } - + template void theory_arith::assign_eh(bool_var v, bool is_true) { TRACE("arith_verbose", tout << "p" << v << " := " << (is_true?"true":"false") << "\n";); @@ -1320,13 +1294,13 @@ namespace smt { template void theory_arith::relevant_eh(app * n) { TRACE("arith_relevant_eh", tout << "relevant_eh: " << mk_pp(n, get_manager()) << "\n";); - if (m_util.is_mod(n)) + if (m_util.is_mod(n)) mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1)); else if (m_util.is_rem(n)) mk_rem_axiom(n->get_arg(0), n->get_arg(1)); - else if (m_util.is_div(n)) + else if (m_util.is_div(n)) mk_div_axiom(n->get_arg(0), n->get_arg(1)); - else if (m_util.is_to_int(n)) + else if (m_util.is_to_int(n)) mk_to_int_axiom(n); else if (m_util.is_is_int(n)) mk_is_int_axiom(n); @@ -1335,16 +1309,16 @@ namespace smt { template void theory_arith::new_eq_eh(theory_var v1, theory_var v2) { TRACE("arith_new_eq_eh", tout << "#" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); - TRACE("arith_new_eq_eh_detail", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << + TRACE("arith_new_eq_eh_detail", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); enode * n1 = get_enode(v1); - - if (!m_util.is_int(n1->get_owner()) && + + if (!m_util.is_int(n1->get_owner()) && !m_util.is_real(n1->get_owner())) { return; } - if (m_params.m_arith_eq_bounds) { + if (m_params.m_arith_eq_bounds) { enode * n2 = get_enode(v2); SASSERT(n1->get_root() == n2->get_root()); if (m_util.is_numeral(n1->get_owner())) { @@ -1391,7 +1365,7 @@ namespace smt { template void theory_arith::new_diseq_eh(theory_var v1, theory_var v2) { - TRACE("arith_new_diseq_eh", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << + TRACE("arith_new_diseq_eh", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); m_stats.m_assert_diseq++; m_arith_eq_adapter.new_diseq_eh(v1, v2); @@ -1456,8 +1430,8 @@ namespace smt { result = FC_GIVEUP; break; case FC_CONTINUE: - TRACE("arith", - tout << "continue arith..." + TRACE("arith", + tout << "continue arith..." << (get_context().inconsistent()?"inconsistent\n":"\n");); return FC_CONTINUE; } @@ -1475,8 +1449,8 @@ namespace smt { TRACE("arith_eq_adapter_info", m_arith_eq_adapter.display_already_processed(tout);); TRACE("arith", display(tout);); - if (!propagate_core()) - return FC_CONTINUE; + if (!propagate_core()) + return FC_CONTINUE; if (delayed_assume_eqs()) return FC_CONTINUE; get_context().push_trail(value_trail(m_final_check_idx)); @@ -1493,12 +1467,12 @@ namespace smt { TRACE("arith", tout << "result: " << result << "\n";); return result; } - + template bool theory_arith::can_propagate() { return process_atoms() && m_asserted_qhead < m_asserted_bounds.size(); } - + template void theory_arith::propagate() { TRACE("arith_propagate", tout << "propagate\n"; display(tout);); @@ -1512,7 +1486,7 @@ namespace smt { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); - + flush_bound_axioms(); propagate_linear_monomials(); while (m_asserted_qhead < m_asserted_bounds.size()) { @@ -1520,7 +1494,7 @@ namespace smt { m_asserted_qhead++; if (!assert_bound(b)) { failed(); - return false; + return false; } } if (!make_feasible()) { @@ -1545,15 +1519,15 @@ namespace smt { CASSERT("arith", satisfy_bounds()); return true; } - + template void theory_arith::failed() { restore_assignment(); m_to_patch.reset(); m_to_check.reset(); m_in_to_check.reset(); - } - + } + template void theory_arith::flush_eh() { std::for_each(m_atoms.begin(), m_atoms.end(), delete_proc()); @@ -1561,7 +1535,7 @@ namespace smt { std::for_each(m_bounds_to_delete.begin(), m_bounds_to_delete.end(), delete_proc()); m_bounds_to_delete.reset(); } - + template void theory_arith::reset_eh() { m_stats.reset(); @@ -1666,7 +1640,7 @@ namespace smt { result.neg(); return is_diff; } - + template theory_arith::theory_arith(ast_manager & m, theory_arith_params & params): theory(m.mk_family_id("arith")), @@ -1702,8 +1676,8 @@ namespace smt { } template - theory* theory_arith::mk_fresh(context* new_ctx) { - return alloc(theory_arith, new_ctx->get_manager(), m_params); + theory* theory_arith::mk_fresh(context* new_ctx) { + return alloc(theory_arith, new_ctx->get_manager(), m_params); } template @@ -1717,7 +1691,7 @@ namespace smt { // Add Row // // ----------------------------------- - + /** \brief Set: row1 <- row1 + coeff * row2 */ @@ -1735,8 +1709,8 @@ namespace smt { CASSERT("arith", check_null_var_pos()); r1.save_var_pos(m_var_pos); - - // + + // // loop over variables in row2, // add terms in row2 to row1. // @@ -1785,7 +1759,7 @@ namespace smt { r_entry.m_coeff -= it->m_coeff); } else { - ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff, + ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff, r_entry.m_coeff += it->m_coeff * coeff); } @@ -1797,17 +1771,17 @@ namespace smt { theory_var v = r1.get_base_var(); if (is_int(v) && !get_value(v).is_int()) gcd_test(r1); - } + } } /** \brief Set r1 <- r1 + a_xs[0].m_coeff * get_var_row(a_xs[0].m_var) + ... + a_xs[0].m_coeff * get_var_row(a_xs[sz-1].m_var) - + \pre For all i in [0..sz-1]. not is_non_base(a_xs[i]) */ template void theory_arith::add_rows(unsigned r1, unsigned sz, linear_monomial * a_xs) { - if (sz == 0) + if (sz == 0) return; for (unsigned i = 0; i < sz; i++) { linear_monomial & m = a_xs[i]; @@ -1837,7 +1811,7 @@ namespace smt { } m_changed_assignment = true; } - + template void theory_arith::discard_update_trail() { m_in_update_trail_stack.reset(); @@ -1876,15 +1850,15 @@ namespace smt { } /** - \brief m_value[v] += delta, and update dependent (non-base) variables. + \brief m_value[v] += delta, and update dependent (non-base) variables. */ template void theory_arith::update_value(theory_var v, inf_numeral const & delta) { update_value_core(v, delta); - + column & c = m_columns[v]; c.compress_if_needed(m_rows); - + inf_numeral delta2; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); @@ -1929,7 +1903,7 @@ namespace smt { int r_id = get_var_row(x_i); row & r = m_rows[r_id]; - + SASSERT(r.is_coeff_of(x_j, a_ij)); #define DIVIDE_ROW(_ADJUST_COEFF_) \ @@ -1953,14 +1927,14 @@ namespace smt { set_var_row(x_i, -1); set_var_row(x_j, r_id); - + SASSERT(r.m_base_var == x_i); r.m_base_var = x_j; set_var_kind(x_i, NON_BASE); set_var_kind(x_j, BASE); - - eliminate(x_j, apply_gcd_test); + + eliminate(x_j, apply_gcd_test); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); @@ -1979,7 +1953,7 @@ namespace smt { /** \brief Eliminate x_i from the rows different from get_var_row(x_i) - + If Lazy = true, then x_i is only eliminated from base rows. */ template @@ -1998,7 +1972,7 @@ namespace smt { unsigned r1_sz = m_rows[r_id].size(); if (it->m_row_id != static_cast(r_id)) { row & r2 = m_rows[it->m_row_id]; - theory_var s2 = r2.m_base_var; + theory_var s2 = r2.m_base_var; if (s2 != null_theory_var && (!Lazy || is_base(s2))) { a_kj = r2[it->m_row_idx].m_coeff; a_kj.neg(); @@ -2006,12 +1980,12 @@ namespace smt { get_manager().limit().inc((r1_sz + r2.size()) * (a_kj.storage_size())); } } - else { + else { s_pos = i; } } - } - CTRACE("eliminate", !Lazy && c.size() != 1, + } + CTRACE("eliminate", !Lazy && c.size() != 1, tout << "eliminating v" << x_i << ", Lazy: " << Lazy << ", c.size: " << c.size() << "\n"; display(tout);); SASSERT(Lazy || c.size() == 1); @@ -2051,7 +2025,7 @@ namespace smt { pivot(x_i, x_j, a_ij, m_eager_gcd); CASSERT("arith", valid_row_assignment()); } - + /** \brief Return the number of base variables that are non free and are v dependent. The function adds 1 to the result if v is non free. @@ -2077,9 +2051,9 @@ namespace smt { } return result; } - + /** - \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, + \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. @@ -2090,29 +2064,29 @@ namespace smt { theory_var max = get_num_vars(); theory_var result = max; row const & r = m_rows[get_var_row(x_i)]; - + typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead()) { - theory_var x_j = it->m_var; - numeral const & a_ij = it->m_coeff; + for (; it != end; ++it) { + if (!it->is_dead()) { + theory_var x_j = it->m_var; + numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos(); - bool is_pos = !is_neg; + bool is_pos = !is_neg; if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { SASSERT(is_non_base(x_j)); - if (x_j < result) { - result = x_j; - out_a_ij = a_ij; + if (x_j < result) { + result = x_j; + out_a_ij = a_ij; } } } } return result < max ? result : null_theory_var; } - + /** - \brief Select a variable x_j in the row r defining the base var x_i, + \brief Select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. @@ -2135,13 +2109,13 @@ namespace smt { for (; it != end; ++it) { - if (!it->is_dead()) { - theory_var x_j = it->m_var; - numeral const & a_ij = it->m_coeff; - + if (!it->is_dead()) { + theory_var x_j = it->m_var; + numeral const & a_ij = it->m_coeff; + bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos(); - bool is_pos = !is_neg; - if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { + bool is_pos = !is_neg; + if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { int num = get_num_non_free_dep_vars(x_j, best_so_far); int col_sz = m_columns[x_j].size(); if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) { @@ -2157,13 +2131,13 @@ namespace smt { result = x_j; out_a_ij = a_ij; } - } + } } } } return result < max ? result : null_theory_var; } - + /** \brief Wrapper for select_blands_pivot_core and select_pivot_core */ @@ -2184,7 +2158,7 @@ namespace smt { // Make feasible // // ----------------------------------- - + /** \brief Make the given variable feasible. This method assumes that x_i is a base var. Return false if it was not possible to @@ -2192,8 +2166,8 @@ namespace smt { */ template bool theory_arith::make_var_feasible(theory_var x_i) { - CTRACE("arith_bug", !is_base(x_i), - tout << "x_i: " << x_i << ", below_lower(x_i): " << below_lower(x_i) << + CTRACE("arith_bug", !is_base(x_i), + tout << "x_i: " << x_i << ", below_lower(x_i): " << below_lower(x_i) << ", above_upper(x_i): " << above_upper(x_i) << "\n"; display(tout);); SASSERT(is_base(x_i)); @@ -2245,7 +2219,7 @@ namespace smt { continue; SASSERT(curr_error > inf_numeral(0)); if (best == null_theory_var || (!least && curr_error > best_error) || (least && curr_error < best_error)) { - TRACE("select_pivot", tout << "best: " << best << " v" << v + TRACE("select_pivot", tout << "best: " << best << " v" << v << ", best_error: " << best_error << ", curr_error: " << curr_error << "\n";); best = v; best_error = curr_error; @@ -2266,7 +2240,7 @@ namespace smt { m_to_patch.erase(best); return best; } - + template theory_var theory_arith::select_smallest_var() { return m_to_patch.erase_min(); @@ -2277,7 +2251,7 @@ namespace smt { if (m_blands_rule) return select_smallest_var(); switch (m_params.m_arith_pivot_strategy) { - case ARITH_PIVOT_GREATEST_ERROR: + case ARITH_PIVOT_GREATEST_ERROR: return select_greatest_error_var(); case ARITH_PIVOT_LEAST_ERROR: return select_least_error_var(); @@ -2319,12 +2293,12 @@ namespace smt { m_left_basis.insert(v); } } - if (!make_var_feasible(v)) { + if (!make_var_feasible(v)) { TRACE("arith_make_feasible", tout << "make_feasible: unsat\n"; display(tout);); return false; } TRACE("arith_make_feasible_detail", display(tout);); - if (get_context().get_cancel_flag()) { + if (get_context().get_cancel_flag()) { return true; } } @@ -2339,7 +2313,7 @@ namespace smt { /** \brief A row is in a sign inconsistency when it is implying a lower (upper) bound on x_i, which is above (below) its known - upper (lower) bound. + upper (lower) bound. */ template void theory_arith::sign_row_conflict(theory_var x_i, bool is_below) { @@ -2353,7 +2327,7 @@ namespace smt { // if x_i is an integer variable, then delta can be negative: // // Example: x_i <= 0 get_value(x_i) = 1/4 - // + // // The value is above the upper bound. // Since x_i is an integer, get_epsilon(x_i) = 1, and delta = -3/4 @@ -2383,7 +2357,7 @@ namespace smt { antecedents ante(*this); explain_bound(r, idx, !is_below, delta, ante); b->push_justification(ante, numeral(1), coeffs_enabled()); - + TRACE("sign_row_conflict", tout << "v" << x_i << " is_below: " << is_below << " delta: " << delta << "\n"; display_var(tout, x_i); tout << "is_below_lower: " << below_lower(x_i) << ", is_above_upper: " << above_upper(x_i) << "\n"; ante.display(tout);); @@ -2397,7 +2371,7 @@ namespace smt { // Assert bound // // ----------------------------------- - + /** \brief Assert x >= k, return false if a conflict is detected. */ @@ -2409,7 +2383,7 @@ namespace smt { bound * u = upper(v); bound * l = lower(v); - + if (u && k > u->get_value()) { sign_bound_conflict(u, b); return false; @@ -2419,7 +2393,7 @@ namespace smt { // redundant return true; } - + switch (get_var_kind(v)) { case QUASI_BASE: quasi_base_row2base_row(get_var_row(v)); @@ -2438,10 +2412,10 @@ namespace smt { push_bound_trail(v, l, false); set_bound(b, false); - + if (propagation_mode() != BP_NONE) mark_rows_for_bound_prop(v); - + return true; } @@ -2457,12 +2431,12 @@ namespace smt { TRACE("arith", display_bound(tout, b); tout << "v" << v << " <= " << k << "\n";); bound * u = upper(v); bound * l = lower(v); - + if (l && k < l->get_value()) { sign_bound_conflict(l, b); return false; } - + if (u && k >= u->get_value()) { // redundant return true; @@ -2474,7 +2448,7 @@ namespace smt { SASSERT(get_var_kind(v) == BASE); case BASE: if (!m_to_patch.contains(v) && get_value(v) > k) { - TRACE("to_patch_bug", tout << "need to be patched (assert upper): "; display_var(tout, v);); + TRACE("to_patch_bug", tout << "need to be patched (assert upper): "; display_var(tout, v);); m_to_patch.insert(v); } break; @@ -2486,12 +2460,12 @@ namespace smt { push_bound_trail(v, u, true); set_bound(b, true); - + if (propagation_mode() != BP_NONE) mark_rows_for_bound_prop(v); return true; - } + } template bool theory_arith::assert_bound(bound * b) { @@ -2507,7 +2481,7 @@ namespace smt { bool result = true; switch (b->get_bound_kind()) { - case B_LOWER: + case B_LOWER: m_stats.m_assert_lower++; result = assert_lower(b); break; @@ -2516,7 +2490,7 @@ namespace smt { result = assert_upper(b); break; } - + TRACE("arith_bound", tout << "result: " << result << "\n"; display(tout);); return result; } @@ -2541,7 +2515,7 @@ namespace smt { // Bound propagation // // ----------------------------------- - + /** \brief Mark the row r1 for bound propagation. */ @@ -2554,7 +2528,7 @@ namespace smt { } /** - \brief Mark all rows that contain v for bound propagation. + \brief Mark all rows that contain v for bound propagation. */ template void theory_arith::mark_rows_for_bound_prop(theory_var v) { @@ -2600,7 +2574,7 @@ namespace smt { - lower_idx >= 0 : row can imply a lower bound for the monomial at 'lower_idx' - lower_idx == -1 : row can imply a lower bound for every monomial in the row. - lower_idx == -2 : row cannot be used to imply a lower bound. - + - upper_idx >= 0 : row can imply a upper bound for the monomial at 'upper_idx' - upper_idx == -1 : row can imply a upper bound for every monomial in the row. - upper_idx == -2 : row cannot be used to imply a upper bound. @@ -2608,7 +2582,7 @@ namespace smt { template void theory_arith::is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const { lower_idx = -1; - upper_idx = -1; + upper_idx = -1; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (int i = 0; it != end; ++it, ++i) { @@ -2667,7 +2641,7 @@ namespace smt { // implied_k is a lower bound for entry.m_var bound * curr = lower(entry.m_var); if (curr == nullptr || implied_k > curr->get_value()) { - TRACE("arith_imply_bound", + TRACE("arith_imply_bound", tout << "implying lower bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); @@ -2675,21 +2649,21 @@ namespace smt { } } else { - // implied_k is an upper bound for it->m_var + // implied_k is an upper bound for it->m_var bound * curr = upper(entry.m_var); if (curr == nullptr || implied_k < curr->get_value()) { - TRACE("arith_imply_bound", + TRACE("arith_imply_bound", tout << "implying upper bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); } } - } + } } /** - \brief Auxiliary method. See is_row_useful_for_bound_prop + \brief Auxiliary method. See is_row_useful_for_bound_prop If is_lower = true (false), then imply a lower (upper) bound for all monomials in the row. The monomial bounds are used to compute bounds @@ -2697,7 +2671,7 @@ namespace smt { */ template void theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { - // Traverse the row once and compute + // Traverse the row once and compute // bb = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If is_lower = true // bb = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_j < 0} -a_j * upper(x_j)) If is_lower = false inf_numeral bb; @@ -2710,7 +2684,7 @@ namespace smt { bb.submul(it->m_coeff, b); } } - + inf_numeral implied_k; it = r.begin_entries(); for (int idx = 0; it != end; ++it, ++idx) { @@ -2721,7 +2695,7 @@ namespace smt { implied_k.addmul(it->m_coeff, b); // implied_k is a bound for the monomial in position it implied_k /= it->m_coeff; - TRACE("arith_imply_bound", + TRACE("arith_imply_bound", display_var(tout, it->m_var); tout << "implied bound: " << (it->m_coeff.is_pos() ? ">=" : "<=") << implied_k << "\n";); if (it->m_coeff.is_pos() == is_lower) { @@ -2737,7 +2711,7 @@ namespace smt { } } else { - // implied_k is an upper bound for it->m_var + // implied_k is an upper bound for it->m_var bound * curr = upper(it->m_var); if (curr == nullptr || implied_k < curr->get_value()) { // improved upper bound @@ -2754,13 +2728,13 @@ namespace smt { /** \brief Create an explanation for the lower/upper bound of the variable at position idx. - + \remark delta is used for relaxing the explanation. That is, the implied bound can be delta weaker the the computed value. - \remark the is_lower parameter is a little bit counterintuitive. It says if the other monomials + \remark the is_lower parameter is a little bit counterintuitive. It says if the other monomials imply a lower (upper) bound for the monomial at position idx. - + Store the result in 'antecedent' */ template @@ -2770,7 +2744,7 @@ namespace smt { return; context & ctx = get_context(); row_entry const & entry = r[idx]; - numeral coeff = entry.m_coeff; + numeral coeff = entry.m_coeff; if (relax_bounds()) { // if the variable v at position idx can have a delta increase (decrease) of 'delta', then // the monomial (coeff * v) at position idx can have a delta increase (decrease) of '|coeff| * delta' @@ -2811,7 +2785,7 @@ namespace smt { // limit_k1 += delta * coeff; limit_k1.addmul(inv_coeff, delta); } - TRACE("propagate_bounds_bug", tout << "is_b_lower: " << is_b_lower << " k1: " << k_1 << " limit_k1: " + TRACE("propagate_bounds_bug", tout << "is_b_lower: " << is_b_lower << " k1: " << k_1 << " limit_k1: " << limit_k1 << " delta: " << delta << " coeff: " << coeff << "\n";); inf_numeral k_2 = k_1; atom * new_atom = nullptr; @@ -2891,7 +2865,7 @@ namespace smt { delta = k; delta -= k2; } - TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; + TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); } @@ -2901,13 +2875,13 @@ namespace smt { // example: // k = -1/5*epsilon // k2 = 0 - // Thus, v <= -1/5*epsilon + // Thus, v <= -1/5*epsilon // (not v >= 0) which is equivalent to v <= -epsilon. delta = k2; delta -= k; delta -= epsilon; if (delta.is_nonneg()) { - TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; + TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); } @@ -2922,18 +2896,18 @@ namespace smt { delta -= k2; delta -= epsilon; if (delta.is_nonneg()) { - TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; + TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); } } - // v <= k k <= k2 |- v <= k2 + // v <= k k <= k2 |- v <= k2 if (kind == B_UPPER && k <= k2) { if (relax_bounds()) { delta = k2; delta -= k; } - TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; + TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); } @@ -2947,7 +2921,7 @@ namespace smt { context & ctx = get_context(); if (dump_lemmas()) { TRACE("arith", ante.display(tout) << " --> "; ctx.display_detailed_literal(tout, l); tout << "\n";); - ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), + ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l); } } @@ -2956,7 +2930,7 @@ namespace smt { void theory_arith::dump_lemmas(literal l, derived_bound const& ante) { context & ctx = get_context(); if (dump_lemmas()) { - ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), + ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l); } } @@ -2968,10 +2942,10 @@ namespace smt { antecedents ante(*this); explain_bound(r, idx, is_lower, delta, ante); dump_lemmas(l, ante); - - TRACE("propagate_bounds", + + TRACE("propagate_bounds", ante.display(tout) << " --> "; - ctx.display_detailed_literal(tout, l); + ctx.display_detailed_literal(tout, l); tout << "\n";); if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) { literal_vector & lits = m_tmp_literal_vector2; @@ -2992,8 +2966,8 @@ namespace smt { region & r = ctx.get_region(); ctx.assign(l, ctx.mk_justification( ext_theory_propagation_justification( - get_id(), r, ante.lits().size(), ante.lits().c_ptr(), - ante.eqs().size(), ante.eqs().c_ptr(), l, + get_id(), r, ante.lits().size(), ante.lits().c_ptr(), + ante.eqs().size(), ante.eqs().c_ptr(), l, ante.num_params(), ante.params("assign-bounds")))); } } @@ -3014,29 +2988,29 @@ namespace smt { int lower_idx; int upper_idx; is_row_useful_for_bound_prop(r, lower_idx, upper_idx); - + if (lower_idx >= 0) { imply_bound_for_monomial(r, lower_idx, true); } else if (lower_idx == -1) { imply_bound_for_all_monomials(r, true); } - + if (upper_idx >= 0) { imply_bound_for_monomial(r, upper_idx, false); } else if (upper_idx == -1) { imply_bound_for_all_monomials(r, false); } - - // sneaking cheap eq detection in this loop + + // sneaking cheap eq detection in this loop propagate_cheap_eq(*it); } - + #if 0 theory_var v = r.get_base_var(); if (!is_int(v) || get_value(v).is_int()) { - // If an integer value is not assigned to an integer value, then + // If an integer value is not assigned to an integer value, then // bound propagation can diverge. m_in_to_check.remove(v); } @@ -3064,15 +3038,15 @@ namespace smt { dump_lemmas(false_literal, ante); set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), bounds, proof_rule); } - + template - void theory_arith::set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, + void theory_arith::set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& bounds, char const* proof_rule) { SASSERT(num_literals != 0 || num_eqs != 0); context & ctx = get_context(); m_stats.m_conflicts++; m_num_conflicts++; - TRACE("arith_conflict", + TRACE("arith_conflict", tout << "scope: " << ctx.get_scope_level() << "\n"; for (unsigned i = 0; i < num_literals; i++) { ctx.display_detailed_literal(tout, lits[i]); @@ -3095,13 +3069,13 @@ namespace smt { record_conflict(num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)); ctx.set_conflict( ctx.mk_justification( - ext_theory_conflict_justification(get_id(), ctx.get_region(), num_literals, lits, num_eqs, eqs, + ext_theory_conflict_justification(get_id(), ctx.get_region(), num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)))); } /** \brief Collect the proofs for the fixed variables in the given row. Store - the proofs in result. + the proofs in result. */ template void theory_arith::collect_fixed_var_justifications(row const & r, antecedents& antecedents) const { @@ -3110,7 +3084,7 @@ namespace smt { for (; it != end; ++it) { if (!it->is_dead() && is_fixed(it->m_var)) { lower(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); - upper(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); + upper(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); } } } @@ -3124,33 +3098,33 @@ namespace smt { // // The arithmetic module uses infinitesimals. So, // an inf_numeral (n,k) represents n + k*epsilon - // where epsilon is a very small number. + // where epsilon is a very small number. // In order to generate a model, we need to compute // a value for epsilon in a way all bounds remain // satisfied. // // 1) Handling inequalities: (n1, k1) <= (n2, k2) - // - // The only intersting case is n1 < n2 and k1 > k2. + // + // The only intersting case is n1 < n2 and k1 > k2. // Using the definition of infinitesimal numbers // we have: // n1 + k1 * epsilon <= n2 + k2 - epsilon // Therefore: // epsilon <= (n2 - n1) / (k1 - k2) - // + // // Starting at Z3 V2.0, we split disequalities. // So, we do not need to handle them. If we decide // to support them again in the future: // // 2) Handling disequalities: (n1, k1) /= n2 - // + // // case a) k1 is positive and n1 < n2 // Thus, epsilon < (n2 - n1) / k1 // => epsilon <= (n2 - n1) / 2*k1 // // case b) k1 is negative and n1 > n2 - // Similarly, epsilon <= (n2 - n1) / 2*k1 - // + // Similarly, epsilon <= (n2 - n1) / 2*k1 + // /** \brief Update the value of epsilon using the inequality l <= u @@ -3166,7 +3140,7 @@ namespace smt { } SASSERT(m_epsilon.is_pos()); } - + template void theory_arith::compute_epsilon() { m_epsilon = numeral(1); @@ -3184,21 +3158,21 @@ namespace smt { /** The epsilon computed by compute_epsilon may accidentally make two shared - variables to have the same assignment. This method keeps dividing + variables to have the same assignment. This method keeps dividing epsilon by 2 until this "clash" does not occur. Here is an example of the problem - + Assignment: x -> 9.5 - y -> 10 - epsilon - + y -> 10 - epsilon + x and y have different assignments. However, if compute_epsilon sets epsilon to 0.5, then x and y become 9.5. However, an equality is not propagated to the core since in the assignment above they are assigned to distinct values. - - This bug was reported by Marcello Bersani. + + This bug was reported by Marcello Bersani. Remark: this is not really a soundness bug. The result sat/unsat produced by Z3 was still correct. - However, the model construction was incorrect. Perhaps, this explains why this bug was not + However, the model construction was incorrect. Perhaps, this explains why this bug was not detected before. */ template @@ -3221,7 +3195,7 @@ namespace smt { if (mapping.find(value, v2)) { SASSERT(!is_int_src(v2)); if (get_value(v) != get_value(v2)) { - // v and v2 are not known to be equal. + // v and v2 are not known to be equal. // The choice of m_epsilon is making them equal. TRACE("refine_epsilon", tout << "v" << v << " v" << v2 << " " << get_value(v) << " " << get_value(v2) << " " << value << std::endl; @@ -3274,7 +3248,7 @@ namespace smt { template bool theory_arith::to_expr(inf_numeral const& val, bool is_int, expr_ref & r) { - if (val.get_infinitesimal().is_zero()) { + if (val.get_infinitesimal().is_zero()) { numeral _val = val.get_rational(); r = m_util.mk_numeral(_val.to_rational(), is_int); return true; @@ -3292,25 +3266,25 @@ namespace smt { } template - bool theory_arith::get_lower(enode * n, expr_ref & r) { + bool theory_arith::get_lower(enode * n, expr_ref & r) { theory_var v = n->get_th_var(get_id()); bound* b = (v == null_theory_var) ? nullptr : lower(v); return b && to_expr(b->get_value(), is_int(v), r); } - + template - bool theory_arith::get_upper(enode * n, expr_ref & r) { + bool theory_arith::get_upper(enode * n, expr_ref & r) { theory_var v = n->get_th_var(get_id()); bound* b = (v == null_theory_var) ? nullptr : upper(v); return b && to_expr(b->get_value(), is_int(v), r); } - + // ----------------------------------- // // Backtracking // // ----------------------------------- - + template void theory_arith::push_scope_eh() { theory::push_scope_eh(); @@ -3415,7 +3389,7 @@ namespace smt { --it; m_unassigned_atoms[*it]++; } - + m_unassigned_atoms_trail.shrink(old_trail_size); } @@ -3432,7 +3406,7 @@ namespace smt { SASSERT(m_var_occs[v].back() == a); m_var_occs[v].pop_back(); dealloc(a); - } + } m_atoms.shrink(old_size); } @@ -3444,7 +3418,7 @@ namespace smt { --it; bound * b = *it; dealloc(b); - } + } m_bounds_to_delete.shrink(old_size); } @@ -3461,7 +3435,7 @@ namespace smt { SASSERT(m_columns[v].size() == 1); del_row(get_var_row(v)); TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); - break; + break; case BASE: SASSERT(lazy_pivoting_lvl() != 0 || m_columns[v].size() == 1); if (lazy_pivoting_lvl() > 0) @@ -3519,7 +3493,7 @@ namespace smt { r.reset(); m_dead_rows.push_back(r_id); } - + /** \brief reset and retrieve built-in explanation hints for arithmetic lemmmas. */ @@ -3542,4 +3516,3 @@ namespace smt { }; #endif /* THEORY_ARITH_CORE_H_ */ - From fca0442487d1e884c443126e319fb4f00df99c3f Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 17:11:46 -0700 Subject: [PATCH 234/364] Fix proof_checker to use is_int_expr --- src/ast/proofs/proof_checker.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ast/proofs/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp index d0f8a7994..e148299be 100644 --- a/src/ast/proofs/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -84,7 +84,7 @@ void proof_checker::hyp_decl_plugin::get_sort_names(svector & sort } proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m), - m_dump_lemmas(false), m_logic("AUFLIA"), m_proof_lemma_id(0) { + m_dump_lemmas(false), m_logic("AUFLIRA"), m_proof_lemma_id(0) { symbol fam_name("proof_hypothesis"); if (!m.has_plugin(fam_name)) { m.register_plugin(fam_name, alloc(hyp_decl_plugin)); @@ -1245,9 +1245,9 @@ void proof_checker::dump_proof(proof const* pr) { void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent) { char buffer[128]; #ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "proof_lemma_%d.smt", m_proof_lemma_id); + sprintf_s(buffer, ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id); #else - sprintf(buffer, "proof_lemma_%d.smt", m_proof_lemma_id); + sprintf(buffer, "proof_lemma_%d.smt2", m_proof_lemma_id); #endif std::ofstream out(buffer); ast_smt_pp pp(m); @@ -1278,6 +1278,10 @@ bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& SASSERT(lit->get_num_args() == 2); sort* s = m.get_sort(lit->get_arg(0)); bool is_int = a.is_int(s); + if (!is_int && a.is_int_expr(lit->get_arg(0))) { + is_int = true; + s = a.mk_int(); + } if (!is_int && is_pos && (a.is_gt(lit) || a.is_lt(lit))) { is_strict = true; @@ -1394,7 +1398,6 @@ bool proof_checker::check_arith_proof(proof* p) { return false; } } - if (m.is_or(fact)) { app* disj = to_app(fact); unsigned num_args = disj->get_num_args(); From 1994f1d7e4eb3f952a062e477792cf33aaee4d65 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 17:46:23 -0700 Subject: [PATCH 235/364] Cleanup of spacer options --- src/muz/spacer/spacer_context.cpp | 98 +++++++++++++++++++++---------- src/muz/spacer/spacer_context.h | 34 +++++++++++ src/muz/spacer/spacer_pdr.cpp | 2 +- 3 files changed, 103 insertions(+), 31 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 8806afb6a..118a99dd3 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -77,8 +77,8 @@ void pob::set_post(expr* post) { void pob::set_post(expr* post, app_ref_vector const &binding) { normalize(post, m_post, - m_pt.get_context().get_params().spacer_simplify_pob(), - m_pt.get_context().get_params().spacer_use_eqclass()); + m_pt.get_context().simplify_pob(), + m_pt.get_context().use_eqclass()); m_binding.reset(); if (!binding.empty()) {m_binding.append(binding);} @@ -1382,7 +1382,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, /// returns true if lemma is blocked by an existing model bool pred_transformer::is_ctp_blocked(lemma *lem) { - if (!ctx.get_params().spacer_ctp()) {return false;} + if (!ctx.use_ctp()) {return false;} if (!lem->has_ctp()) {return false;} scoped_watch _t_(m_ctp_watch); @@ -1443,7 +1443,7 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, ctx.weak_abs() ? lem->weakness() : UINT_MAX); model_ref mdl; model_ref *mdl_ref_ptr = nullptr; - if (ctx.get_params().spacer_ctp()) {mdl_ref_ptr = &mdl;} + if (ctx.use_ctp()) {mdl_ref_ptr = &mdl;} m_solver->set_core(core); m_solver->set_model(mdl_ref_ptr); expr * bg = m_extend_lit.get (); @@ -1582,7 +1582,7 @@ void pred_transformer::init_rules(decl2rel const& pts) { m_transition_clause.push_back(m_extend_lit->get_arg(0)); m_transition_clause.push_back(tag); - if (!ctx.get_params().spacer_use_inc_clause()) { + if (!ctx.use_inc_clause()) { transitions.push_back(mk_or(m_transition_clause)); m_transition_clause.reset(); } @@ -1605,7 +1605,7 @@ void pred_transformer::init_rules(decl2rel const& pts) { if (!is_init[i]) {init_conds.push_back (m.mk_not (tag));} } - if (!ctx.get_params().spacer_use_inc_clause()) { + if (!ctx.use_inc_clause()) { transitions.push_back(mk_or(m_transition_clause)); m_transition_clause.reset(); } @@ -1671,7 +1671,7 @@ void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule, // rewrite and simplify th_rewriter rw(m); rw(fml); - if (ctx.get_params().spacer_blast_term_ite()) {blast_term_ite(fml); rw(fml);} + if (ctx.blast_term_ite()) {blast_term_ite(fml); rw(fml);} TRACE("spacer", tout << mk_pp(fml, m) << "\n";); // allow quantifiers in init rule @@ -2170,7 +2170,7 @@ pob* pred_transformer::pobs::mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b) { - if (!m_pt.ctx.get_params().spacer_reuse_pobs()) { + if (!m_pt.ctx.reuse_pobs()) { pob* n = alloc(pob, parent, m_pt, level, depth); n->set_post(post, b); return n; @@ -2244,7 +2244,7 @@ context::context(fixedpoint_params const& params, m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); - updt_params() + updt_params(); } context::~context() @@ -2256,8 +2256,46 @@ context::~context() void context::updt_params() { m_random.set_seed(m_params.spacer_random_seed()); m_children_order = static_cast(m_params.spacer_order_children()); + m_simplify_pob = m_params.spacer_simplify_pob(); + m_use_eqclass = m_params.spacer_use_eqclass(); + m_use_ctp = m_params.spacer_ctp(); + m_use_inc_clause = m_params.spacer_use_inc_clause(); + m_blast_term_ite = m_params.spacer_blast_term_ite(); + m_reuse_pobs = m_params.spacer_reuse_pobs(); + m_use_ind_gen = m_params.pdr_use_inductive_generalizer(); + m_use_array_eq_gen = m_params.spacer_use_array_eq_generalizer(); + m_check_lemmas = m_params.spacer_lemma_sanity_check(); + m_max_level = m_params.spacer_max_level (); + m_skip_propagate = m_params.spacer_skip_propagate (); + m_reset_obligation_queue = m_params.spacer_reset_obligation_queue(); + m_flexible_trace = m_params.pdr_flexible_trace(); + m_flexible_trace_depth = m_params.pdr_flexible_trace_depth(); + m_use_lemma_as_pob = m_params.spacer_use_lemma_as_cti(); + m_elim_aux = m_params.spacer_elim_aux(); + m_reach_dnf = m_params.spacer_reach_dnf(); + m_use_derivations = m_params.spacer_use_derivations(); + m_validate_result = m_params.pdr_validate_result(); + m_use_eq_prop = m_params.spacer_eq_prop(); + m_ground_pob = m_params.spacer_ground_cti(); + m_q3_qgen = m_params.spacer_q3_use_qgen(); + m_use_gpdr = m_params.spacer_gpdr(); + m_simplify_formulas_pre = m_params.pdr_simplify_formulas_pre(); + m_simplify_formulas_post = m_params.pdr_simplify_formulas_post(); + + + if (m_use_gpdr) { + // set options to be compatible with GPDR + m_weak_abs = false; + m_flexible_trace = false; + m_use_qlemmas = false; + m_ground_pob = true; + m_reset_obligation_queue = false; + m_use_derivations = false; + m_use_lemma_as_pob = false; + } } + void context::reset() { TRACE("spacer", tout << "\n";); @@ -2403,7 +2441,7 @@ expr_ref context::get_reachable(func_decl *p) bool context::validate() { - if (!m_params.pdr_validate_result()) { return true; } + if (!m_validate_result) { return true; } std::stringstream msg; @@ -2500,7 +2538,7 @@ void context::reset_lemma_generalizers() void context::init_global_smt_params() { m.toggle_proof_mode(PGM_ENABLED); params_ref p; - if (!m_params.spacer_eq_prop()) { + if (!m_use_eq_prop) { p.set_uint("arith.propagation_mode", BP_NONE); p.set_bool("arith.auto_config_simplex", true); p.set_bool("arith.propagate_eqs", false); @@ -2514,7 +2552,7 @@ void context::init_global_smt_params() { // mbqi p.set_bool("mbqi", m_params.spacer_mbqi()); - if (!m_params.spacer_ground_cti()) { + if (!m_ground_pob) { p.set_uint("phase_selection", PS_CACHING_CONSERVATIVE2); p.set_uint("restart_strategy", RS_GEOMETRIC); p.set_double("restart_factor", 1.5); @@ -2537,29 +2575,29 @@ void context::init_lemma_generalizers() { reset_lemma_generalizers(); - if (m_params.spacer_q3_use_qgen()) { + if (m_q3_qgen) { m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, true)); m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this, m_params.spacer_q3_qgen_normalize())); } - if (get_params().spacer_use_eqclass()) { + if (use_eqclass()) { m_lemma_generalizers.push_back (alloc(lemma_eq_generalizer, *this)); } // -- AG: commented out because it is causing performance issues at the moment //m_lemma_generalizers.push_back (alloc (unsat_core_generalizer, *this)); - if (m_params.pdr_use_inductive_generalizer()) { + if (m_use_ind_gen) { m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0)); } - if (m_params.spacer_use_array_eq_generalizer()) { + if (m_use_array_eq_gen) { m_lemma_generalizers.push_back(alloc(lemma_array_eq_generalizer, *this)); } - if (get_params().spacer_lemma_sanity_check()) { + if (m_check_lemmas) { m_lemma_generalizers.push_back(alloc(lemma_sanity_checker, *this)); } @@ -2595,7 +2633,7 @@ lbool context::solve(unsigned from_lvl) { m_last_result = l_undef; try { - if (m_params.spacer_gpdr()) { + if (m_use_gpdr) { SASSERT(from_lvl == 0); m_last_result = gpdr_solve_core(); } @@ -2976,7 +3014,7 @@ lbool context::solve_core (unsigned from_lvl) pob *root = m_query->mk_pob(nullptr,from_lvl,0,m.mk_true()); m_pob_queue.set_root (*root); - unsigned max_level = get_params ().spacer_max_level (); + unsigned max_level = m_max_level; for (unsigned i = from_lvl; i < max_level; ++i) { checkpoint(); @@ -2985,7 +3023,7 @@ lbool context::solve_core (unsigned from_lvl) if (check_reachability()) { return l_true; } - if (lvl > 0 && !get_params ().spacer_skip_propagate ()) + if (lvl > 0 && !m_skip_propagate) if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { dump_json(); return l_false; } dump_json(); @@ -3032,7 +3070,7 @@ bool context::check_reachability () pob_ref_buffer new_pobs; - if (get_params().spacer_reset_obligation_queue()) { m_pob_queue.reset(); } + if (m_reset_obligation_queue) { m_pob_queue.reset(); } unsigned initial_size = m_stats.m_num_lemmas; unsigned threshold = m_restart_initial_threshold; @@ -3132,8 +3170,8 @@ bool context::check_reachability () /// returns true if the given pob can be re-scheduled bool context::is_requeue(pob &n) { - if (!get_params().pdr_flexible_trace()) {return false;} - unsigned max_depth = get_params().pdr_flexible_trace_depth(); + if (!m_flexible_trace) {return false;} + unsigned max_depth = m_flexible_trace_depth; return (n.level() >= m_pob_queue.max_level() || m_pob_queue.max_level() - n.level() <= max_depth); } @@ -3299,7 +3337,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) unsigned num_reuse_reach = 0; - if (get_params().pdr_flexible_trace() && n.pt().is_blocked(n, uses_level)) { + if (m_flexible_trace && n.pt().is_blocked(n, uses_level)) { // if (!m_pob_queue.is_root (n)) n.close (); IF_VERBOSE (1, verbose_stream () << " K " << std::fixed << std::setprecision(2) @@ -3418,7 +3456,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) if (v) { m_stats.m_num_lemmas++; } // Optionally update the node to be the negation of the lemma - if (v && get_params().spacer_use_lemma_as_cti()) { + if (v && m_use_lemma_as_pob) { n.new_post (mk_and(lemma->get_cube())); n.set_farkas_generalizer (false); // XXX hack while refactoring is in progress @@ -3494,7 +3532,7 @@ bool context::propagate(unsigned min_prop_lvl, if (full_prop_lvl < max_prop_lvl) { full_prop_lvl = max_prop_lvl; } - if (m_params.pdr_simplify_formulas_pre()) { + if (m_simplify_formulas_pre) { simplify_formulas(); } STRACE ("spacer.expand-add", tout << "Propagating\n";); @@ -3539,7 +3577,7 @@ bool context::propagate(unsigned min_prop_lvl, return true; } else if (all_propagated && lvl > max_prop_lvl) { break; } } - if (m_params.pdr_simplify_formulas_post()) { + if (m_simplify_formulas_post) { simplify_formulas(); } @@ -3583,13 +3621,13 @@ reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, } // collect aux vars to eliminate ptr_vector& aux_vars = get_aux_vars (r); - bool elim_aux = ctx.get_params().spacer_elim_aux(); + bool elim_aux = ctx.elim_aux(); if (elim_aux) { vars.append(aux_vars.size(), aux_vars.c_ptr()); } res = mk_and (path_cons); // -- pick an implicant from the path condition - if (ctx.get_params().spacer_reach_dnf()) { + if (ctx.reach_dnf()) { expr_ref_vector u(m), lits(m); u.push_back (res); compute_implicant_literals (mev, u, lits); @@ -3727,7 +3765,7 @@ bool context::create_children(pob& n, datalog::rule const& r, kid->set_derivation (deriv); // Optionally disable derivation optimization - if (!get_params().spacer_use_derivations()) { kid->reset_derivation(); } + if (!m_use_derivations) { kid->reset_derivation(); } // -- deriviation is abstract if the current weak model does // -- not satisfy 'T && phi'. It is possible to recover from diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index e00d479f7..8bc7f36e6 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -846,6 +846,32 @@ class context { bool m_use_qlemmas; bool m_weak_abs; bool m_use_restarts; + bool m_simplify_pob; + bool m_use_eqclass; + bool m_use_ctp; + bool m_use_inc_clause; + bool m_blast_term_ite; + bool m_reuse_pobs; + bool m_use_ind_gen; + bool m_use_array_eq_gen; + bool m_check_lemmas; + bool m_skip_propagate; + bool m_reset_obligation_queue; + bool m_flexible_trace; + bool m_use_lemma_as_pob; + bool m_elim_aux; + bool m_reach_dnf; + bool m_use_derivations; + bool m_validate_result; + bool m_use_eq_prop; + bool m_ground_pob; + bool m_q3_qgen; + bool m_use_gpdr; + bool m_simplify_formulas_pre; + bool m_simplify_formulas_post; + + unsigned m_flexible_trace_depth; + unsigned m_max_level; unsigned m_restart_initial_threshold; scoped_ptr_vector m_callbacks; json_marshaller m_json_marshaller; @@ -913,6 +939,14 @@ public: bool use_instantiate () {return m_instantiate;} bool weak_abs() {return m_weak_abs;} bool use_qlemmas () {return m_use_qlemmas;} + bool use_eqclass() { return m_use_eqclass;} + bool simplify_pob() {return m_simplify_pob;} + bool use_ctp() {return m_use_ctp;} + bool use_inc_clause() {return m_use_inc_clause;} + bool blast_term_ite() {return m_blast_term_ite;} + bool reuse_pobs() {return m_reuse_pobs;} + bool elim_aux() {return m_elim_aux;} + bool reach_dnf() {return m_reach_dnf;} ast_manager& get_ast_manager() const {return m;} manager& get_manager() {return m_pm;} diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index e46907493..6c7da20a7 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -224,7 +224,7 @@ lbool context::gpdr_solve_core() { model_search ms(true); unsigned lvl = 0; - unsigned max_level = get_params ().spacer_max_level (); + unsigned max_level = m_max_level; for (lvl = 0; lvl < max_level; ++lvl) { checkpoint(); IF_VERBOSE(1,verbose_stream() << "GPDR Entering level "<< lvl << "\n";); From e1a45671b354f0ce3d9a3b8a37cdbba3774d8c91 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 18:43:10 -0700 Subject: [PATCH 236/364] Cleanup spacer options --- src/muz/spacer/spacer_context.cpp | 23 +++++++++++------------ src/muz/spacer/spacer_context.h | 3 +-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 118a99dd3..9e70fa3c6 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -262,7 +262,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) vars.append(m_evars); m_evars.reset(); pt().mbp(vars, m_trans, mev.get_model(), - true, pt().get_context().use_ground_cti()); + true, pt().get_context().use_ground_pob()); m_evars.append (vars); vars.reset(); } @@ -291,7 +291,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) // include m_evars in case they can eliminated now as well vars.append(m_evars); pt().mbp(vars, post, mev.get_model(), - true, pt().get_context().use_ground_cti()); + true, pt().get_context().use_ground_pob()); //qe::reduce_array_selects (*mev.get_model (), post); } else { @@ -395,7 +395,7 @@ pob *derivation::create_next_child () vars.append(m_evars); m_evars.reset(); this->pt().mbp(vars, m_trans, mev.get_model(), - true, this->pt().get_context().use_ground_cti()); + true, this->pt().get_context().use_ground_pob()); // keep track of implicitly quantified variables m_evars.append (vars); vars.reset(); @@ -2224,13 +2224,6 @@ context::context(fixedpoint_params const& params, m_last_result(l_undef), m_inductive_lvl(0), m_expanded_lvl(0), - m_use_native_mbp(params.spacer_native_mbp ()), - m_ground_cti (params.spacer_ground_cti ()), - m_instantiate (params.spacer_q3_instantiate ()), - m_use_qlemmas (params.spacer_q3()), - m_weak_abs(params.spacer_weak_abs()), - m_use_restarts(params.spacer_restarts()), - m_restart_initial_threshold(params.spacer_restart_initial_threshold()), m_json_marshaller(this) { ref pool0_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); @@ -2281,6 +2274,12 @@ void context::updt_params() { m_use_gpdr = m_params.spacer_gpdr(); m_simplify_formulas_pre = m_params.pdr_simplify_formulas_pre(); m_simplify_formulas_post = m_params.pdr_simplify_formulas_post(); + m_use_native_mbp = m_params.spacer_native_mbp (); + m_instantiate = m_params.spacer_q3_instantiate (); + m_use_qlemmas = m_params.spacer_q3(); + m_weak_abs = m_params.spacer_weak_abs(); + m_use_restarts = m_params.spacer_restarts(); + m_restart_initial_threshold = m_params.spacer_restart_initial_threshold(); if (m_use_gpdr) { @@ -3716,9 +3715,9 @@ bool context::create_children(pob& n, datalog::rule const& r, // skolems of the pob n.get_skolems(vars); - n.pt().mbp(vars, phi, mev.get_model (), true, use_ground_cti()); + n.pt().mbp(vars, phi, mev.get_model (), true, use_ground_pob()); //qe::reduce_array_selects (*mev.get_model (), phi1); - SASSERT (!m_ground_cti || vars.empty ()); + SASSERT (!m_ground_pob || vars.empty ()); TRACE ("spacer", tout << "Implicant:\n"; diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 8bc7f36e6..115dded55 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -841,7 +841,6 @@ class context { model_converter_ref m_mc; proof_converter_ref m_pc; bool m_use_native_mbp; - bool m_ground_cti; bool m_instantiate; bool m_use_qlemmas; bool m_weak_abs; @@ -935,7 +934,7 @@ public: const fixedpoint_params &get_params() const { return m_params; } bool use_native_mbp () {return m_use_native_mbp;} - bool use_ground_cti () {return m_ground_cti;} + bool use_ground_pob () {return m_ground_pob;} bool use_instantiate () {return m_instantiate;} bool weak_abs() {return m_weak_abs;} bool use_qlemmas () {return m_use_qlemmas;} From d2ae3b4025565948b300a4fba582278bae11f4df Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 5 Jun 2018 22:26:26 -0700 Subject: [PATCH 237/364] Create children for pdr in spacer This is first working version of gpdr strategy. Passes one test. --- src/muz/spacer/spacer_context.cpp | 5 +++ src/muz/spacer/spacer_context.h | 4 +++ src/muz/spacer/spacer_pdr.cpp | 54 ++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 9e70fa3c6..5165940d8 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -3727,6 +3727,11 @@ bool context::create_children(pob& n, datalog::rule const& r, tout << "Failed to eliminate: " << vars << "\n"; ); + if (m_use_gpdr && preds.size() > 1) { + SASSERT(vars.empty()); + return gpdr_create_split_children(n, r, phi, mev.get_model(), out); + } + derivation *deriv = alloc(derivation, n, r, phi, vars); // pick an order to process children diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 115dded55..3c6c35742 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -878,6 +878,10 @@ class context { // Solve using gpdr strategy lbool gpdr_solve_core(); bool gpdr_check_reachability(unsigned lvl, model_search &ms); + bool gpdr_create_split_children(pob &n, const datalog::rule &r, + expr *trans, + model_ref &mdl, + pob_ref_buffer &out); // Functions used by search. lbool solve_core(unsigned from_lvl = 0); diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index 6c7da20a7..e1a268c22 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -20,6 +20,7 @@ Notes: --*/ #include "muz/spacer/spacer_pdr.h" #include "muz/base/dl_context.h" +#include "muz/spacer/spacer_mbc.h" namespace spacer { model_node::model_node(model_node* parent, class pob *pob): @@ -90,7 +91,9 @@ void model_node::detach(model_node*& qhead) { // insert node n after this in the queue // requires: this is in a queue or this == n void model_node::insert_after(model_node* n) { - SASSERT(!in_queue()); + SASSERT(this == n || in_queue()); + SASSERT(n); + SASSERT(!n->in_queue()); if (this == n) { m_next = n; m_prev = n; @@ -282,4 +285,53 @@ bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { return root_node->is_closed(); } +bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, + expr *trans, + model_ref &mdl, + pob_ref_buffer &out) { + pred_transformer &pt = n.pt(); + ptr_vector preds; + pt.find_predecessors(r, preds); + SASSERT(preds.size() > 1); + + ptr_vector ppts; + for (auto *p : preds) ppts.push_back(&get_pred_transformer(p)); + + mbc::partition_map pmap; + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { + func_decl *p = preds.get(i); + pred_transformer &ppt = *ppts.get(i); + for (unsigned j = 0, jsz = p->get_arity(); j < jsz; ++j) { + pmap.insert(m_pm.o2o(ppt.sig(j), 0, i), i); + } + } + + + spacer::mbc _mbc(m); + expr_ref_vector lits(m); + flatten_and(trans, lits); + vector res(preds.size(), expr_ref_vector(m)); + _mbc(pmap, lits, *mdl.get(), res); + + + for (unsigned i = 0, sz = res.size(); i < sz; ++i) { + expr_ref post(m); + pred_transformer &ppt = *ppts.get(i); + post = mk_and(res.get(i)); + m_pm.formula_o2n(post.get(), post, i, true); + pob * k = ppt.mk_pob(&n, prev_level(n.level()), n.depth(), post); + out.push_back(k); + IF_VERBOSE (1, verbose_stream() + << "\n\tcreate_child: " << k->pt().head()->get_name() + << " (" << k->level() << ", " << k->depth() << ") " + << (k->use_farkas_generalizer() ? "FAR " : "SUB ") + << k->post()->get_id(); + verbose_stream().flush();); + + } + + return true; +} + + } // spacer From c3fb863ad17d34b274d1b9b8db5c9dab3ad5a5b0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Jun 2018 22:37:35 -0700 Subject: [PATCH 238/364] formatting/reviewing Signed-off-by: Nikolaj Bjorner --- src/ast/ast.h | 33 + src/muz/spacer/spacer_context.cpp | 61 +- src/muz/spacer/spacer_context.h | 18 +- src/muz/spacer/spacer_iuc_solver.cpp | 132 ++-- src/muz/spacer/spacer_iuc_solver.h | 101 ++- src/muz/spacer/spacer_pdr.cpp | 45 +- src/muz/spacer/spacer_pdr.h | 22 +- src/muz/spacer/spacer_quant_generalizer.cpp | 97 ++- src/muz/spacer/spacer_unsat_core_learner.cpp | 73 +-- src/muz/spacer/spacer_unsat_core_learner.h | 19 +- src/muz/spacer/spacer_unsat_core_plugin.cpp | 627 ++++++++----------- src/muz/spacer/spacer_unsat_core_plugin.h | 16 +- src/solver/check_sat_result.h | 2 +- 13 files changed, 570 insertions(+), 676 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index ce193e8b1..b6b71c6a3 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1744,6 +1744,14 @@ public: arity, domain); } + func_decl * mk_const_decl(const char* name, sort * s) { + return mk_func_decl(symbol(name), static_cast(0), nullptr, s); + } + + func_decl * mk_const_decl(std::string const& name, sort * s) { + return mk_func_decl(symbol(name.c_str()), static_cast(0), nullptr, s); + } + func_decl * mk_const_decl(symbol const & name, sort * s) { return mk_func_decl(name, static_cast(0), nullptr, s); } @@ -1810,6 +1818,14 @@ public: return mk_const(mk_const_decl(name, s)); } + app * mk_const(std::string const & name, sort * s) { + return mk_const(mk_const_decl(name, s)); + } + + app * mk_const(char const* name, sort * s) { + return mk_const(mk_const_decl(name, s)); + } + func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range); @@ -2150,6 +2166,23 @@ public: return n > 0 && get_sort(p->get_arg(n - 1)) != m_proof_sort; } expr * get_fact(proof const * p) const { SASSERT(is_proof(p)); SASSERT(has_fact(p)); return p->get_arg(p->get_num_args() - 1); } + + class proof_parents { + ast_manager& m; + proof * m_proof; + public: + proof_parents(ast_manager& m, proof * p): m(m), m_proof(p) {} + proof * const * begin() const { return (proof* const*)(m_proof->begin()); } + proof * const * end() const { + unsigned n = m_proof->get_num_args(); + return (proof* const*)(begin() + (m.has_fact(m_proof) ? n - 1 : n)); + } + }; + + proof_parents get_parents(proof* p) { + return proof_parents(*this, p); + } + unsigned get_num_parents(proof const * p) const { SASSERT(is_proof(p)); unsigned n = p->get_num_args(); diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 5165940d8..6726bb15d 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1500,11 +1500,9 @@ void pred_transformer::mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result) { expr_ref tmp1(m), tmp2(m); - obj_map::iterator it = m_tag2rule.begin(), - end = m_tag2rule.end(); - for (; it != end; ++it) { - expr* tag = it->m_key; - datalog::rule const* r = it->m_value; + for (auto& kv : m_tag2rule) { + expr* tag = kv.m_key; + datalog::rule const* r = kv.m_value; if (!r) { continue; } find_predecessors(*r, m_predicates); for (unsigned i = 0; i < m_predicates.size(); i++) { @@ -2338,7 +2336,6 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) } // Initialize use list dependencies - // for (auto it = rels.begin(), end = rels.end(); it != end; ++it) { for (auto &entry : rels) { func_decl* pred = entry.m_key; pred_transformer* pt = entry.m_value, *pt_user = nullptr; @@ -2464,14 +2461,13 @@ bool context::validate() get_level_property(m_inductive_lvl, refs, rs); inductive_property ex(m, mc, rs); ex.to_model(model); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); var_subst vs(m, false); - for (; it != end; ++it) { - ptr_vector const& rules = it->m_value->rules(); - TRACE ("spacer", tout << "PT: " << it->m_value->head ()->get_name ().str () + for (auto& kv : m_rels) { + ptr_vector const& rules = kv.m_value->rules(); + TRACE ("spacer", tout << "PT: " << kv.m_value->head ()->get_name ().str () << "\n";); - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule& r = *rules[i]; + for (auto* rp : rules) { + datalog::rule& r = *rp; TRACE ("spacer", get_datalog_context (). @@ -2603,11 +2599,9 @@ void context::init_lemma_generalizers() } void context::get_level_property(unsigned lvl, expr_ref_vector& res, - vector& rs) const -{ - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer* r = it->m_value; + vector& rs) const { + for (auto const& kv : m_rels) { + pred_transformer* r = kv.m_value; if (r->head() == m_query_pred) { continue; } @@ -2619,12 +2613,9 @@ void context::get_level_property(unsigned lvl, expr_ref_vector& res, } } -void context::simplify_formulas() -{ - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer* r = it->m_value; - r->simplify_formulas(); +void context::simplify_formulas() { + for (auto& kv : m_rels) { + kv.m_value->simplify_formulas(); } } @@ -3549,18 +3540,17 @@ bool context::propagate(unsigned min_prop_lvl, tout << "In full propagation\n";); bool all_propagated = true; - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { + for (auto & kv : m_rels) { checkpoint(); - pred_transformer& r = *it->m_value; + pred_transformer& r = *kv.m_value; all_propagated = r.propagate_to_next_level(lvl) && all_propagated; } //CASSERT("spacer", check_invariant(lvl)); if (all_propagated) { - for (it = m_rels.begin(); it != end; ++it) { + for (auto& kv : m_rels) { checkpoint (); - pred_transformer& r = *it->m_value; + pred_transformer& r = *kv.m_value; r.propagate_to_infinity (lvl); } if (lvl <= max_prop_lvl) { @@ -3793,9 +3783,8 @@ void context::collect_statistics(statistics& st) const m_pool1->collect_statistics(st); m_pool2->collect_statistics(st); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (it = m_rels.begin(); it != end; ++it) { - it->m_value->collect_statistics(st); + for (auto const& kv : m_rels) { + kv.m_value->collect_statistics(st); } // -- number of times a pob for some predicate transformer has @@ -3846,9 +3835,8 @@ void context::reset_statistics() m_pool1->reset_statistics(); m_pool2->reset_statistics(); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (it = m_rels.begin(); it != end; ++it) { - it->m_value->reset_statistics(); + for (auto & kv : m_rels) { + kv.m_value->reset_statistics(); } m_stats.reset(); @@ -3897,9 +3885,8 @@ expr_ref context::get_constraints (unsigned level) expr_ref res(m); expr_ref_vector constraints(m); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer& r = *it->m_value; + for (auto & kv : m_rels) { + pred_transformer& r = *kv.m_value; expr_ref c = r.get_formulas(level); if (m.is_true(c)) { continue; } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 3c6c35742..34c04d596 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -176,7 +176,7 @@ public: void dec_ref () { SASSERT (m_ref_count > 0); --m_ref_count; - if(m_ref_count == 0) {dealloc(this);} + if (m_ref_count == 0) {dealloc(this);} } }; @@ -242,7 +242,7 @@ class pred_transformer { } void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) const { for (auto &lemma : m_lemmas) { - if(lemma->level() >= level) { + if (lemma->level() >= level) { out.push_back(lemma->get_expr()); } } @@ -362,7 +362,7 @@ public: void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; void add_rule(datalog::rule* r) {m_rules.push_back(r);} - void add_use(pred_transformer* pt) {if(!m_use.contains(pt)) {m_use.insert(pt);}} + void add_use(pred_transformer* pt) {if (!m_use.contains(pt)) {m_use.insert(pt);}} void initialize(decl2rel const& pts); func_decl* head() const {return m_head;} @@ -528,7 +528,7 @@ public: pob (pob* parent, pred_transformer& pt, unsigned level, unsigned depth=0, bool add_to_parent=true); - ~pob() {if(m_parent) { m_parent->erase_child(*this); }} + ~pob() {if (m_parent) { m_parent->erase_child(*this); }} unsigned weakness() {return m_weakness;} void bump_weakness() {m_weakness++;} @@ -564,7 +564,7 @@ public: void set_post(expr *post, app_ref_vector const &binding); /// indicate that a new post should be set for the node - void new_post(expr *post) {if(post != m_post) {m_new_post = post;}} + void new_post(expr *post) {if (post != m_post) {m_new_post = post;}} /// true if the node needs to be updated outside of the priority queue bool is_dirty () {return m_new_post;} /// clean a dirty node @@ -592,14 +592,14 @@ public: */ void get_skolems(app_ref_vector& v); - void on_expand() { m_expand_watches[m_depth].start(); if(m_parent.get()){m_parent.get()->on_expand();} } - void off_expand() { m_expand_watches[m_depth].stop(); if(m_parent.get()){m_parent.get()->off_expand();} }; + void on_expand() { m_expand_watches[m_depth].start(); if (m_parent.get()){m_parent.get()->on_expand();} } + void off_expand() { m_expand_watches[m_depth].stop(); if (m_parent.get()){m_parent.get()->off_expand();} }; double get_expand_time(unsigned depth) { return m_expand_watches[depth].get_seconds();} void inc_ref () {++m_ref_count;} void dec_ref () { --m_ref_count; - if(m_ref_count == 0) {dealloc(this);} + if (m_ref_count == 0) {dealloc(this);} } class on_expand_event @@ -727,7 +727,7 @@ public: SASSERT (!m_obligations.empty () || m_root); m_max_level++; m_min_depth++; - if(m_root && m_obligations.empty()) { m_obligations.push(m_root); } + if (m_root && m_obligations.empty()) { m_obligations.push(m_root); } } pob& get_root() const {return *m_root.get ();} diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 35932b2ba..a0bc2a627 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -99,8 +99,9 @@ void iuc_solver::pop_bg (unsigned n) { if (n == 0) { return; } - if (m_assumptions.size () > m_first_assumption) - { m_assumptions.shrink(m_first_assumption); } + if (m_assumptions.size () > m_first_assumption) { + m_assumptions.shrink(m_first_assumption); + } m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0; m_assumptions.shrink (m_first_assumption); } @@ -110,9 +111,8 @@ unsigned iuc_solver::get_num_bg () {return m_first_assumption;} lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions) { // -- remove any old assumptions - if (m_assumptions.size () > m_first_assumption) - { m_assumptions.shrink(m_first_assumption); } - + m_assumptions.shrink(m_first_assumption); + // -- replace theory literals in background assumptions with proxies mk_proxies (m_assumptions); // -- in case mk_proxies added new literals, they are all background @@ -121,20 +121,17 @@ lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions m_assumptions.append (num_assumptions, assumptions); m_is_proxied = mk_proxies (m_assumptions, m_first_assumption); - lbool res; - res = m_solver.check_sat (m_assumptions.size (), m_assumptions.c_ptr ()); - set_status (res); - return res; + return set_status (m_solver.check_sat (m_assumptions)); } lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, vector const & clauses) { - if (clauses.empty()) {return check_sat(cube.size(), cube.c_ptr());} - + if (clauses.empty()) + return check_sat(cube.size(), cube.c_ptr()); + // -- remove any old assumptions - if (m_assumptions.size() > m_first_assumption) - { m_assumptions.shrink(m_first_assumption); } - + m_assumptions.shrink(m_first_assumption); + // -- replace theory literals in background assumptions with proxies mk_proxies(m_assumptions); // -- in case mk_proxies added new literals, they are all background @@ -143,28 +140,24 @@ lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, m_assumptions.append(cube); m_is_proxied = mk_proxies(m_assumptions, m_first_assumption); - lbool res; - res = m_solver.check_sat_cc(m_assumptions, clauses); - set_status (res); - return res; + return set_status (m_solver.check_sat_cc(m_assumptions, clauses)); } app* iuc_solver::def_manager::mk_proxy (expr *v) { app* r; - if (m_expr2proxy.find(v, r)) { return r; } + if (m_expr2proxy.find(v, r)) + return r; ast_manager &m = m_parent.m; - app_ref proxy(m); - app_ref def(m); - proxy = m_parent.fresh_proxy (); - def = m.mk_or (m.mk_not (proxy), v); + app* proxy = m_parent.fresh_proxy (); + app* def = m.mk_or (m.mk_not (proxy), v); m_defs.push_back (def); m_expr2proxy.insert (v, proxy); m_proxy2def.insert (proxy, def); - m_parent.assert_expr (def.get ()); + m_parent.assert_expr (def); return proxy; } @@ -191,18 +184,16 @@ bool iuc_solver::def_manager::is_proxy_def (expr *v) bool iuc_solver::is_proxy(expr *e, app_ref &def) { - if (!is_uninterp_const(e)) { return false; } + if (!is_uninterp_const(e)) + return false; - app *a = to_app (e); + app* a = to_app (e); - for (int i = m_defs.size (); i > 0; --i) - if (m_defs[i-1].is_proxy (a, def)) - { return true; } + for (int i = m_defs.size (); i-- > 0; ) + if (m_defs[i].is_proxy (a, def)) + return true; - if (m_base_defs.is_proxy (a, def)) - { return true; } - - return false; + return m_base_defs.is_proxy (a, def); } void iuc_solver::collect_statistics (statistics &st) const @@ -233,21 +224,25 @@ void iuc_solver::undo_proxies_in_core (ptr_vector &r) { app_ref e(m); expr_fast_mark1 bg; - for (unsigned i = 0; i < m_first_assumption; ++i) - { bg.mark(m_assumptions.get(i)); } + for (unsigned i = 0; i < m_first_assumption; ++i) { + bg.mark(m_assumptions.get(i)); + } // expand proxies unsigned j = 0; - for (unsigned i = 0, sz = r.size(); i < sz; ++i) { + for (expr* rr : r) { // skip background assumptions - if (bg.is_marked(r[i])) { continue; } + if (bg.is_marked(rr)) + continue; // -- undo proxies, but only if they were introduced in check_sat - if (m_is_proxied && is_proxy(r[i], e)) { + if (m_is_proxied && is_proxy(rr, e)) { SASSERT (m.is_or (e)); - r[j] = e->get_arg (1); - } else if (i != j) { r[j] = r[i]; } - j++; + r[j++] = e->get_arg (1); + } + else { + r[j++] = rr; + } } r.shrink (j); } @@ -370,65 +365,66 @@ void iuc_solver::get_iuc(expr_ref_vector &core) unsat_core_learner learner(m, iuc_pf); + unsat_core_plugin* plugin; // -- register iuc plugins - if (m_iuc_arith == 0 || m_iuc_arith == 1) { - unsat_core_plugin_farkas_lemma* plugin = + switch (m_iuc_arith) { + case 0: + case 1: + plugin = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, (m_iuc_arith == 1) /* use constants from A */); learner.register_plugin(plugin); - } - else if (m_iuc_arith == 2) { + break; + case 2: SASSERT(false && "Broken"); - unsat_core_plugin_farkas_lemma_optimized* plugin = - alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m); + plugin = alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m); learner.register_plugin(plugin); - } - else if(m_iuc_arith == 3) { - unsat_core_plugin_farkas_lemma_bounded* plugin = - alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m); + break; + case 3: + plugin = alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m); learner.register_plugin(plugin); - } - else { + break; + default: UNREACHABLE(); + break; } - if (m_iuc == 1) { + switch (m_iuc) { + case 1: // -- iuc based on the lowest cut in the proof - unsat_core_plugin_lemma* plugin = - alloc(unsat_core_plugin_lemma, learner); + plugin = alloc(unsat_core_plugin_lemma, learner); learner.register_plugin(plugin); - } - else if (m_iuc == 2) { + break; + case 2: // -- iuc based on the smallest cut in the proof - unsat_core_plugin_min_cut* plugin = - alloc(unsat_core_plugin_min_cut, learner, m); + plugin = alloc(unsat_core_plugin_min_cut, learner, m); learner.register_plugin(plugin); - } - else { + break; + default: UNREACHABLE(); + break; } - + { scoped_watch _t_ (m_learn_core_sw); // compute interpolating unsat core learner.compute_unsat_core(core); } - + elim_proxies (core); // AG: this should be taken care of by minimizing the iuc cut simplify_bounds (core); } - + IF_VERBOSE(2, - verbose_stream () << "IUC Core:\n" - << mk_and(core) << "\n";); + verbose_stream () << "IUC Core:\n" << core << "\n";); } void iuc_solver::refresh () { // only refresh in non-pushed state - SASSERT (m_defs.size () == 0); + SASSERT (m_defs.empty()); expr_ref_vector assertions (m); for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) { expr* a = m_solver.get_assertion (i); diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index dcee54612..0195ea134 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -26,11 +26,10 @@ namespace spacer { class iuc_solver : public solver { private: struct def_manager { - iuc_solver &m_parent; + iuc_solver & m_parent; + expr_ref_vector m_defs; obj_map m_expr2proxy; - obj_map m_proxy2def; - - expr_ref_vector m_defs; + obj_map m_proxy2def; def_manager(iuc_solver &parent) : m_parent(parent), m_defs(m_parent.m) @@ -44,15 +43,15 @@ private: }; friend struct def_manager; - ast_manager &m; - solver &m_solver; - app_ref_vector m_proxies; - unsigned m_num_proxies; + ast_manager& m; + solver& m_solver; + app_ref_vector m_proxies; + unsigned m_num_proxies; vector m_defs; - def_manager m_base_defs; - expr_ref_vector m_assumptions; - unsigned m_first_assumption; - bool m_is_proxied; + def_manager m_base_defs; + expr_ref_vector m_assumptions; + unsigned m_first_assumption; + bool m_is_proxied; stopwatch m_iuc_sw; stopwatch m_hyp_reduce1_sw; @@ -95,7 +94,7 @@ public: /* iuc solver specific */ void get_unsat_core(expr_ref_vector &core) override; virtual void get_iuc(expr_ref_vector &core); - void set_split_literals(bool v) {m_split_literals = v;} + void set_split_literals(bool v) { m_split_literals = v; } bool mk_proxies(expr_ref_vector &v, unsigned from = 0); void undo_proxies(expr_ref_vector &v); @@ -103,42 +102,40 @@ public: void pop_bg(unsigned n); unsigned get_num_bg(); - void get_full_unsat_core(ptr_vector &core) - {m_solver.get_unsat_core(core);} + void get_full_unsat_core(ptr_vector &core) { m_solver.get_unsat_core(core); } /* solver interface */ - solver* translate(ast_manager &m, params_ref const &p) override { return m_solver.translate(m, p);} - void updt_params(params_ref const &p) override {m_solver.updt_params(p);} - void reset_params(params_ref const &p) override {m_solver.reset_params(p);} - const params_ref &get_params() const override {return m_solver.get_params();} - void push_params() override {m_solver.push_params();} - void pop_params() override {m_solver.pop_params();} - void collect_param_descrs(param_descrs &r) override { m_solver.collect_param_descrs(r);} - void set_produce_models(bool f) override { m_solver.set_produce_models(f);} - void assert_expr_core(expr *t) override { m_solver.assert_expr(t);} - void assert_expr_core2(expr *t, expr *a) override { NOT_IMPLEMENTED_YET();} + solver* translate(ast_manager &m, params_ref const &p) override { + return m_solver.translate(m, p); + } + void updt_params(params_ref const &p) override { m_solver.updt_params(p); } + void reset_params(params_ref const &p) override { m_solver.reset_params(p); } + const params_ref &get_params() const override { return m_solver.get_params(); } + void push_params() override { m_solver.push_params(); } + void pop_params() override { m_solver.pop_params(); } + void collect_param_descrs(param_descrs &r) override { m_solver.collect_param_descrs(r); } + void set_produce_models(bool f) override { m_solver.set_produce_models(f); } + void assert_expr_core(expr *t) override { m_solver.assert_expr(t); } + void assert_expr_core2(expr *t, expr *a) override { NOT_IMPLEMENTED_YET(); } expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } void push() override; void pop(unsigned n) override; - unsigned get_scope_level() const override - {return m_solver.get_scope_level();} + unsigned get_scope_level() const override { return m_solver.get_scope_level(); } lbool check_sat(unsigned num_assumptions, expr * const *assumptions) override; lbool check_sat_cc(const expr_ref_vector &cube, vector const & clauses) override; - void set_progress_callback(progress_callback *callback) override - {m_solver.set_progress_callback(callback);} - unsigned get_num_assertions() const override - {return m_solver.get_num_assertions();} - expr * get_assertion(unsigned idx) const override - {return m_solver.get_assertion(idx);} - unsigned get_num_assumptions() const override - {return m_solver.get_num_assumptions();} - expr * get_assumption(unsigned idx) const override - {return m_solver.get_assumption(idx);} - std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const override - { return m_solver.display(out, n, es); } + void set_progress_callback(progress_callback *callback) override { + m_solver.set_progress_callback(callback); + } + unsigned get_num_assertions() const override { return m_solver.get_num_assertions(); } + expr * get_assertion(unsigned idx) const override { return m_solver.get_assertion(idx); } + unsigned get_num_assumptions() const override { return m_solver.get_num_assumptions(); } + expr * get_assumption(unsigned idx) const override { return m_solver.get_assumption(idx); } + std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const override { + return m_solver.display(out, n, es); + } /* check_sat_result interface */ @@ -148,13 +145,10 @@ public: void get_unsat_core(ptr_vector &r) override; void get_model_core(model_ref &m) override {m_solver.get_model(m);} proof *get_proof() override {return m_solver.get_proof();} - std::string reason_unknown() const override - {return m_solver.reason_unknown();} - void set_reason_unknown(char const* msg) override - {m_solver.set_reason_unknown(msg);} - void get_labels(svector &r) override - {m_solver.get_labels(r);} - ast_manager &get_manager() const override {return m;} + std::string reason_unknown() const override { return m_solver.reason_unknown(); } + void set_reason_unknown(char const* msg) override { m_solver.set_reason_unknown(msg); } + void get_labels(svector &r) override { m_solver.get_labels(r); } + ast_manager& get_manager() const override { return m; } virtual void refresh(); @@ -162,10 +156,10 @@ public: iuc_solver &m_s; expr_ref_vector &m_v; public: - scoped_mk_proxy(iuc_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) - {m_s.mk_proxies(m_v);} - ~scoped_mk_proxy() - {m_s.undo_proxies(m_v);} + scoped_mk_proxy(iuc_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) { + m_s.mk_proxies(m_v); + } + ~scoped_mk_proxy() { m_s.undo_proxies(m_v); } }; class scoped_bg { @@ -173,8 +167,11 @@ public: unsigned m_bg_sz; public: scoped_bg(iuc_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} - ~scoped_bg() - {if(m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }} + ~scoped_bg() { + if (m_s.get_num_bg() > m_bg_sz) { + m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); + } + } }; }; } diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index e1a268c22..89647ed02 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -28,14 +28,14 @@ model_node::model_node(model_node* parent, class pob *pob): m_orig_level(m_pob->level()), m_depth(0), m_closed(false) { SASSERT(m_pob); - if (m_parent) m_parent->add_child(*this); + if (m_parent) m_parent->add_child(this); } -void model_node::add_child(model_node &kid) { - m_children.push_back(&kid); - SASSERT(level() == kid.level() + 1); +void model_node::add_child(model_node* kid) { + m_children.push_back(kid); + SASSERT(level() == kid->level() + 1); SASSERT(level() > 0); - kid.m_depth = m_depth + 1; + kid->m_depth = m_depth + 1; if (is_closed()) set_open(); } @@ -109,7 +109,7 @@ void model_node::insert_after(model_node* n) { void model_search::reset() { if (m_root) { erase_children(*m_root, false); - remove_node(*m_root, false); + remove_node(m_root, false); dealloc(m_root); m_root = nullptr; } @@ -117,24 +117,28 @@ void model_search::reset() { } model_node* model_search::pop_front() { - if (!m_qhead) return nullptr; model_node *res = m_qhead; - res->detach(m_qhead); + if (res) { + res->detach(m_qhead); + } return res; } -void model_search::add_leaf(model_node& n) { +void model_search::add_leaf(model_node* _n) { + model_node& n = *_n; SASSERT(n.children().empty()); model_nodes ns; model_nodes& nodes = cache(n).insert_if_not_there2(n.post(), ns)->get_data().m_value; if (nodes.contains(&n)) return; - nodes.push_back(&n); + nodes.push_back(_n); if (nodes.size() == 1) { SASSERT(n.is_open()); enqueue_leaf(n); } - else n.set_pre_closed(); + else { + n.set_pre_closed(); + } } void model_search::enqueue_leaf(model_node& n) { @@ -162,7 +166,7 @@ void model_search::set_root(model_node* root) { m_root = root; SASSERT(m_root); SASSERT(m_root->children().empty()); - add_leaf(*root); + add_leaf(root); } void model_search::backtrack_level(bool uses_level, model_node& n) { @@ -198,15 +202,16 @@ void model_search::erase_children(model_node& n, bool backtrack) { todo.pop_back(); nodes.push_back(m); todo.append(m->children()); - remove_node(*m, backtrack); + remove_node(m, backtrack); } std::for_each(nodes.begin(), nodes.end(), delete_proc()); } // removes node from the search tree and from the cache -void model_search::remove_node(model_node& n, bool backtrack) { +void model_search::remove_node(model_node* _n, bool backtrack) { + model_node& n = *_n; model_nodes& nodes = cache(n).find(n.post()); - nodes.erase(&n); + nodes.erase(_n); if (n.in_queue()) n.detach(m_qhead); // TBD: siblings would also fail if n is not a goal. if (!nodes.empty() && backtrack && @@ -241,9 +246,10 @@ lbool context::gpdr_solve_core() { } // communicate failure to datalog::context - if (m_context) { m_context->set_status(datalog::BOUNDED); } + if (m_context) { + m_context->set_status(datalog::BOUNDED); + } return l_undef; - } bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { @@ -273,9 +279,8 @@ bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { TRACE("spacer_pdr", tout << "looking at pob at level " << pob->level() << " " << mk_pp(pob->post(), m) << "\n";); - if (pob == node->pob()) {continue;} - model_node *kid = alloc(model_node, node, pob); - ms.add_leaf(*kid); + if (pob != node->pob()) + ms.add_leaf(alloc(model_node, node, pob)); } node->check_pre_closed(); break; diff --git a/src/muz/spacer/spacer_pdr.h b/src/muz/spacer/spacer_pdr.h index 56900a1e8..36abb0bc9 100644 --- a/src/muz/spacer/spacer_pdr.h +++ b/src/muz/spacer/spacer_pdr.h @@ -36,9 +36,9 @@ class model_node { bool m_closed; // whether the pob is derivable public: model_node(model_node* parent, pob* pob); - void add_child(model_node &kid); + void add_child(model_node* kid); - expr *post() const {return m_pob->post();} + expr *post() const { return m_pob->post(); } unsigned level() const { return m_pob->level(); } unsigned orig_level() const { return m_orig_level; } unsigned depth() const { return m_depth; } @@ -57,24 +57,25 @@ public: bool is_1closed() { if (is_closed()) return true; if (m_children.empty()) return false; - for (auto kid : m_children) {if (kid->is_open()) return false;} + for (auto kid : m_children) + if (kid->is_open()) return false; return true; } void check_pre_closed(); - void set_pre_closed() {m_closed = true;} + void set_pre_closed() { m_closed = true; } - void set_closed() {m_closed = true;} + void set_closed() { m_closed = true; } void set_open(); - void reset_children() {m_children.reset();} + void reset_children() { m_children.reset(); } /// queue // remove this node from the given queue void detach(model_node*& qhead); void insert_after(model_node* n); - model_node* next() const {return m_next;} - bool in_queue() {return m_next && m_prev;} + model_node* next() const { return m_next; } + bool in_queue() { return m_next && m_prev; } }; class model_search { @@ -85,8 +86,7 @@ class model_search { vector > m_cache; obj_map& cache(model_node const& n); void erase_children(model_node& n, bool backtrack); - void remove_node(model_node& n, bool backtrack); - void add_leaf(model_node* n); // add leaf to priority queue. + void remove_node(model_node* _n, bool backtrack); public: model_search(bool bfs): m_bfs(bfs), m_root(nullptr), m_qhead(nullptr) {} @@ -96,7 +96,7 @@ public: void reset(); model_node* pop_front(); - void add_leaf(model_node& n); // add fresh node. + void add_leaf(model_node* n); // add fresh node. model_node& get_root() const { return *m_root; } void backtrack_level(bool uses_level, model_node& n); void remove_goal(model_node& n); diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index 3dace0cdd..f9525b2f6 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -132,14 +132,13 @@ void lemma_quantifier_generalizer::find_candidates(expr *e, << " in " << mk_pp(e, m) << "\n";); extra.push_back(index); if (m_arith.is_add(index)) { - for (unsigned j = 0, asz = index->get_num_args(); j < asz; j++) { - expr *arg = index->get_arg(j); - if (!is_app(arg) || marked_args.is_marked(arg)) {continue;} - marked_args.mark(arg); - candidates.push_back (to_app(arg)); + for (expr * arg : *index) { + if (!is_app(arg) || marked_args.is_marked(arg)) {continue;} + marked_args.mark(arg); + candidates.push_back (to_app(arg)); + } } } - } std::sort(candidates.c_ptr(), candidates.c_ptr() + candidates.size(), index_lt_proc(m)); @@ -214,15 +213,15 @@ void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector bool found = false; expr_ref_vector kids(m); expr_ref_vector kids_bind(m); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - if (a->get_arg(i) == sk) { + for (expr* arg : *a) { + if (arg == sk) { found = true; - kids.push_back(a->get_arg(i)); + kids.push_back(arg); kids_bind.push_back(bind); } else { - kids.push_back (times_minus_one(a->get_arg(i), arith)); - kids_bind.push_back (times_minus_one(a->get_arg(i), arith)); + kids.push_back (times_minus_one(arg, arith)); + kids_bind.push_back (times_minus_one(arg, arith)); } } if (!found) continue; @@ -292,7 +291,7 @@ void lemma_quantifier_generalizer::mk_abs_cube(lemma_ref &lemma, app *term, var gnd_cube.push_back(lit); } else { - expr *e1, *e2; + expr *e1, *e2; // generalize v=num into v>=num if (m.is_eq(abs_lit, e1, e2) && (e1 == var || e2 == var)) { if (m_arith.is_numeral(e1)) { @@ -310,13 +309,13 @@ void lemma_quantifier_generalizer::mk_abs_cube(lemma_ref &lemma, app *term, var if (!lb && is_lb(var, abs_lit)) { lb = abs_lit; - } + } else if (!ub && is_ub(var, abs_lit)) { ub = abs_lit; + } } } } -} // -- returns true if e is an upper bound for var bool lemma_quantifier_generalizer::is_ub(var *var, expr *e) { @@ -348,38 +347,34 @@ bool lemma_quantifier_generalizer::is_ub(var *var, expr *e) { if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_add(e1)) { app *a = to_app(e1); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - expr *arg = a->get_arg(i); - if (arg == var) {return true;} + for (expr* arg : *a) { + if (arg == var) return true; } } // t1 <= t2 + -1*var if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_add(e2)) { app *a = to_app(e2); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - expr *arg = a->get_arg(i); + for (expr* arg : *a) { if (m_arith.is_times_minus_one(arg, arg) && arg == var) - {return true;} + return true; } } // t1 >= t2 + var if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_add(e2)) { app *a = to_app(e2); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - expr *arg = a->get_arg(i); - if (arg == var) {return true;} + for (expr * arg : *a) { + if (arg == var) return true; } } // -1*var + t1 >= t2 if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_add(e1)) { app *a = to_app(e1); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - expr *arg = a->get_arg(i); + for (expr * arg : *a) { if (m_arith.is_times_minus_one(arg, arg) && arg == var) - {return true;} + return true; } } return false; @@ -414,38 +409,34 @@ bool lemma_quantifier_generalizer::is_lb(var *var, expr *e) { if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_add(e1)) { app *a = to_app(e1); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - expr *arg = a->get_arg(i); - if (arg == var) {return true;} + for (expr * arg : *a) { + if (arg == var) return true; } } // t1 >= t2 + -1*var if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_add(e2)) { app *a = to_app(e2); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - expr *arg = a->get_arg(i); + for (expr * arg : *a) { if (m_arith.is_times_minus_one(arg, arg) && arg == var) - {return true;} + return true; } } // t1 <= t2 + var if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_add(e2)) { app *a = to_app(e2); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - expr *arg = a->get_arg(i); - if (arg == var) {return true;} + for (expr * arg : *a) { + if (arg == var) return true; } } // -1*var + t1 <= t2 if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_add(e1)) { app *a = to_app(e1); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - expr *arg = a->get_arg(i); + for (expr * arg : *a) { if (m_arith.is_times_minus_one(arg, arg) && arg == var) - {return true;} + return true; } } @@ -470,22 +461,22 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { tout << "lb = "; if (lb) tout << mk_pp(lb, m); else tout << "none"; tout << "\n"; - tout << "ub = "; if (ub) tout << mk_pp(ub, m); else tout << "none"; tout << "\n";); - if (!lb && !ub) {return false;} + if (!lb && !ub) + return false; // -- guess lower or upper bound if missing if (!lb) { abs_cube.push_back (m_arith.mk_ge (var, term)); lb = abs_cube.back(); - } + } if (!ub) { abs_cube.push_back (m_arith.mk_lt(var, term)); ub = abs_cube.back(); - } + } rational init; expr_ref constant(m); @@ -511,7 +502,7 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { flatten_and(gnd, gnd_cube); TRACE("spacer_qgen", - tout << "New CUBE is: " << mk_pp(mk_and(gnd_cube),m) << "\n";); + tout << "New CUBE is: " << gnd_cube << "\n";); // check if the result is a true lemma unsigned uses_level = 0; @@ -519,8 +510,8 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { if (pt.check_inductive(lemma->level(), gnd_cube, uses_level, lemma->weakness())) { TRACE("spacer_qgen", tout << "Quantifier Generalization Succeeded!\n" - << "New CUBE is: " << mk_pp(mk_and(gnd_cube),m) << "\n";); - SASSERT(zks.size() >= m_offset); + << "New CUBE is: " << gnd_cube << "\n";); + SASSERT(zks.size() >= static_cast(m_offset)); // lift quantified variables to top of select expr_ref ext_bind(m); @@ -541,7 +532,7 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { } lemma->update_cube(lemma->get_pob(), gnd_cube); - lemma->set_level(uses_level); + lemma->set_level(uses_level); SASSERT(var->get_idx() < zks.size()); SASSERT(is_app(ext_bind)); @@ -570,8 +561,7 @@ bool lemma_quantifier_generalizer::find_stride(expr_ref_vector &c, expr_ref &pat if (is_var(p_index)) return false; std::vector instances; - for (unsigned i=0; i < c.size(); i++) { - expr *lit = c.get(i); + for (expr* lit : c) { if (!contains_selects(lit, m)) continue; @@ -589,16 +579,17 @@ bool lemma_quantifier_generalizer::find_stride(expr_ref_vector &c, expr_ref &pat unsigned matched = 0; for (unsigned p=0; p < size; p++) { expr *arg = p_index->get_arg(p); - if (is_var(arg)) - { + if (is_var(arg)) { rational val; - if (p < candidate->get_num_args() && m_arith.is_numeral(candidate->get_arg(p), val)) { + if (p < candidate->get_num_args() && + m_arith.is_numeral(candidate->get_arg(p), val) && + val.is_unsigned()) { instances.push_back(val.get_unsigned()); } } else { - for (unsigned j=0; j < candidate->get_num_args(); j++) { - if (candidate->get_arg(j) == arg) { + for (expr* cand : *candidate) { + if (cand == arg) { matched++; break; } diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index a6ab01870..5b3420800 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -17,46 +17,36 @@ Revision History: --*/ #include +#include "ast/for_each_expr.h" +#include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_unsat_core_learner.h" #include "muz/spacer/spacer_unsat_core_plugin.h" #include "muz/spacer/spacer_iuc_proof.h" -#include "ast/for_each_expr.h" - -#include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_util.h" -namespace spacer -{ +namespace spacer { -unsat_core_learner::~unsat_core_learner() -{ +unsat_core_learner::~unsat_core_learner() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); } -void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) -{ +void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) { m_plugins.push_back(plugin); } -void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) -{ +void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { // traverse proof proof_post_order it(m_pr.get(), m); - while (it.hasNext()) - { + while (it.hasNext()) { proof* currentNode = it.next(); - if (m.get_num_parents(currentNode) > 0) - { + if (m.get_num_parents(currentNode) > 0) { bool need_to_mark_closed = true; - for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) - { - SASSERT(m.is_proof(currentNode->get_arg(i))); - proof* premise = to_app(currentNode->get_arg(i)); - - need_to_mark_closed = need_to_mark_closed && (!m_pr.is_b_marked(premise) || m_closed.is_marked(premise)); + for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { + proof* premise = m.get_parent(currentNode, i); + need_to_mark_closed &= (!m_pr.is_b_marked(premise) || m_closed.is_marked(premise)); } // save result @@ -65,8 +55,9 @@ void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) // we have now collected all necessary information, so we can visit the node // if the node mixes A-reasoning and B-reasoning and contains non-closed premises - if (m_pr.is_a_marked(currentNode) && m_pr.is_b_marked(currentNode) && !m_closed.is_marked(currentNode)) - { + if (m_pr.is_a_marked(currentNode) && + m_pr.is_b_marked(currentNode) && + !m_closed.is_marked(currentNode)) { compute_partial_core(currentNode); // then we need to compute a partial core } } @@ -77,46 +68,38 @@ void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) // TODO: remove duplicates from unsat core? // move all lemmas into vector - for (expr* const* it = m_unsat_core.begin(); it != m_unsat_core.end(); ++it) - { - unsat_core.push_back(*it); + for (expr* e : m_unsat_core) { + unsat_core.push_back(e); } } -void unsat_core_learner::compute_partial_core(proof* step) -{ - for (unsat_core_plugin** it=m_plugins.begin(), **end = m_plugins.end (); it != end && !m_closed.is_marked(step); ++it) - { - unsat_core_plugin* plugin = *it; +void unsat_core_learner::compute_partial_core(proof* step) { + for (unsat_core_plugin* plugin : m_plugins) { + if (m_closed.is_marked(step)) break; plugin->compute_partial_core(step); } } -void unsat_core_learner::finalize() -{ - for (unsat_core_plugin** it=m_plugins.begin(); it != m_plugins.end(); ++it) - { - unsat_core_plugin* plugin = *it; +void unsat_core_learner::finalize() { + for (unsat_core_plugin* plugin : m_plugins) { plugin->finalize(); } } -bool unsat_core_learner::is_closed(proof*p) -{ +bool unsat_core_learner::is_closed(proof* p) { return m_closed.is_marked(p); } -void unsat_core_learner::set_closed(proof* p, bool value) -{ + +void unsat_core_learner::set_closed(proof* p, bool value) { m_closed.mark(p, value); } -bool unsat_core_learner::is_b_open(proof *p) -{ - return m_pr.is_b_marked(p) && !is_closed (p); +bool unsat_core_learner::is_b_open(proof *p) { + return m_pr.is_b_marked(p) && !is_closed (p); } -void unsat_core_learner::add_lemma_to_core(expr* lemma) -{ +void unsat_core_learner::add_lemma_to_core(expr* lemma) { m_unsat_core.push_back(lemma); } + } diff --git a/src/muz/spacer/spacer_unsat_core_learner.h b/src/muz/spacer/spacer_unsat_core_learner.h index a8b5965fc..327b12eb6 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.h +++ b/src/muz/spacer/spacer_unsat_core_learner.h @@ -6,7 +6,8 @@ Module Name: spacer_unsat_core_learner.h Abstract: - itp cores + + itp cores Author: Bernhard Gleiss @@ -27,8 +28,7 @@ namespace spacer { class unsat_core_plugin; class iuc_proof; - class unsat_core_learner - { + class unsat_core_learner { typedef obj_hashtable expr_set; public: @@ -37,7 +37,7 @@ namespace spacer { virtual ~unsat_core_learner(); ast_manager& m; - iuc_proof& m_pr; + iuc_proof& m_pr; /* * register a plugin for computation of partial unsat cores @@ -56,7 +56,6 @@ namespace spacer { * - a node is closed, iff it has already been interpolated, i.e. its contribution is * already covered by the unsat-core. */ - bool is_closed(proof* p); void set_closed(proof* p, bool value); @@ -67,14 +66,14 @@ namespace spacer { */ void add_lemma_to_core(expr* lemma); - - private: ptr_vector m_plugins; ast_mark m_closed; - // collects the lemmas of the unsat-core - // will at the end be inserted into unsat_core. + /* + * collects the lemmas of the unsat-core + * will at the end be inserted into unsat_core. + */ expr_ref_vector m_unsat_core; /* @@ -86,9 +85,7 @@ namespace spacer { * finalize computation of unsat-core */ void finalize(); - }; - } #endif diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index 557983628..25b12cf25 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -6,7 +6,7 @@ Module Name: spacer_unsat_core_plugin.cpp Abstract: - plugin for itp cores + plugin for itp cores Author: Bernhard Gleiss @@ -32,259 +32,213 @@ Revision History: #include "muz/spacer/spacer_unsat_core_learner.h" #include "muz/spacer/spacer_iuc_proof.h" -namespace spacer -{ +namespace spacer { + + unsat_core_plugin::unsat_core_plugin(unsat_core_learner& learner): + m(learner.m), m_learner(learner) {}; + -void unsat_core_plugin_lemma::compute_partial_core(proof* step) -{ - SASSERT(m_learner.m_pr.is_a_marked(step)); - SASSERT(m_learner.m_pr.is_b_marked(step)); - - for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof* premise = to_app(step->get_arg(i)); - - if (m_learner.is_b_open (premise)) - { - // by IH, premises that are AB marked are already closed - SASSERT(!m_learner.m_pr.is_a_marked(premise)); - add_lowest_split_to_core(premise); - } - } - m_learner.set_closed(step, true); -} - -void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const -{ - SASSERT(m_learner.is_b_open(step)); - ast_manager &m = m_learner.m; - - ptr_vector todo; - todo.push_back(step); - - while (!todo.empty()) - { - proof* pf = todo.back(); - todo.pop_back(); - - // if current step hasn't been processed, - if (!m_learner.is_closed(pf)) - { - m_learner.set_closed(pf, true); - // the step is b-marked and not closed. - // by I.H. the step must be already visited - // so if it is also a-marked, it must be closed - SASSERT(m_learner.m_pr.is_b_marked(pf)); - SASSERT(!m_learner.m_pr.is_a_marked(pf)); - - // the current step needs to be interpolated: - expr* fact = m_learner.m.get_fact(pf); - // if we trust the current step and we are able to use it - if (m_learner.m_pr.is_b_pure (pf) && - (m.is_asserted(pf) || is_literal(m, fact))) - { - // just add it to the core - m_learner.add_lemma_to_core(fact); - } - // otherwise recurse on premises - else - { - for (unsigned i = 0, sz = m_learner.m.get_num_parents(pf); - i < sz; ++i) - { - SASSERT(m_learner.m.is_proof(pf->get_arg(i))); - proof* premise = m.get_parent (pf, i); - if (m_learner.is_b_open(premise)) { - todo.push_back(premise); - } - } - } - - } - } -} - - -void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) -{ - ast_manager &m = m_learner.m; - SASSERT(m_learner.m_pr.is_a_marked(step)); - SASSERT(m_learner.m_pr.is_b_marked(step)); - // XXX this assertion should be true so there is no need to check for it - SASSERT (!m_learner.is_closed (step)); - func_decl* d = step->get_decl(); - symbol sym; - if(!m_learner.is_closed(step) && // if step is not already interpolated - step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma - d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas" && - d->get_num_parameters() >= m_learner.m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients - { - SASSERT(m_learner.m.has_fact(step)); - - ptr_vector literals; - vector coefficients; - - /* The farkas lemma represents a subproof starting from premise(-set)s A, BNP and BP(ure) and - * ending in a disjunction D. We need to compute the contribution of BP, i.e. a formula, which - * is entailed by BP and together with A and BNP entails D. - * - * Let Fark(F) be the farkas coefficient for F. We can use the fact that - * (A*Fark(A) + BNP*Fark(BNP) + BP*Fark(BP) + (neg D)*Fark(D)) => false. (E1) - * We further have that A+B => C implies (A \land B) => C. (E2) - * - * Alternative 1: - * From (E1) immediately get that BP*Fark(BP) is a solution. - * - * Alternative 2: - * We can rewrite (E2) to rewrite (E1) to - * (BP*Fark(BP)) => (neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D))) (E3) - * and since we can derive (A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) from - * A, BNP and D, we also know that it is inconsisent. Therefore - * neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) is a solution. - * - * Finally we also need the following workaround: - * 1) Although we know from theory, that the Farkas coefficients are always nonnegative, - * the Farkas coefficients provided by arith_core are sometimes negative (must be a bug) - * as workaround we take the absolute value of the provided coefficients. - */ - parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient - - STRACE("spacer.farkas", - verbose_stream() << "Farkas input: "<< "\n"; - for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof *prem = m.get_parent (step, i); - - rational coef; - VERIFY(params[i].is_rational(coef)); - - bool b_pure = m_learner.m_pr.is_b_pure (prem); - verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n"; - } - ); - - bool can_be_closed = true; - - for(unsigned i = 0; i < m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof * premise = m.get_parent (step, i); - - if (m_learner.is_b_open (premise)) - { + void unsat_core_plugin_lemma::compute_partial_core(proof* step) { + SASSERT(m_learner.m_pr.is_a_marked(step)); + SASSERT(m_learner.m_pr.is_b_marked(step)); + + for (proof* premise : m.get_parents(step)) { + + if (m_learner.is_b_open (premise)) { + // by IH, premises that are AB marked are already closed SASSERT(!m_learner.m_pr.is_a_marked(premise)); + add_lowest_split_to_core(premise); + } + } + m_learner.set_closed(step, true); + } + + void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const + { + SASSERT(m_learner.is_b_open(step)); + + ptr_buffer todo; + todo.push_back(step); + + while (!todo.empty()) { + proof* pf = todo.back(); + todo.pop_back(); + + // if current step hasn't been processed, + if (!m_learner.is_closed(pf)) { + m_learner.set_closed(pf, true); + // the step is b-marked and not closed. + // by I.H. the step must be already visited + // so if it is also a-marked, it must be closed + SASSERT(m_learner.m_pr.is_b_marked(pf)); + SASSERT(!m_learner.m_pr.is_a_marked(pf)); + + // the current step needs to be interpolated: + expr* fact = m.get_fact(pf); + // if we trust the current step and we are able to use it + if (m_learner.m_pr.is_b_pure (pf) && + (m.is_asserted(pf) || is_literal(m, fact))) { + // just add it to the core + m_learner.add_lemma_to_core(fact); + } + // otherwise recurse on premises + else { + for (proof* premise : m.get_parents(pf)) + if (m_learner.is_b_open(premise)) + todo.push_back(premise); + } + + } + } + } - if (m_learner.m_pr.is_b_pure (step)) - { - if (!m_use_constant_from_a) - { - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - literals.push_back(to_app(m_learner.m.get_fact(premise))); - coefficients.push_back(abs(coefficient)); + + void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) + { + SASSERT(m_learner.m_pr.is_a_marked(step)); + SASSERT(m_learner.m_pr.is_b_marked(step)); + // XXX this assertion should be true so there is no need to check for it + SASSERT (!m_learner.is_closed (step)); + func_decl* d = step->get_decl(); + symbol sym; + if (!m_learner.is_closed(step) && // if step is not already interpolated + step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma + d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step + d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", + d->get_parameter(1).is_symbol(sym) && sym == "farkas" && + d->get_num_parameters() >= m.get_num_parents(step) + 2) { // the following parameters are the Farkas coefficients + + SASSERT(m.has_fact(step)); + + coeff_lits_t coeff_lits; + expr_ref_vector pinned(m); + + /* The farkas lemma represents a subproof starting from premise(-set)s A, BNP and BP(ure) and + * ending in a disjunction D. We need to compute the contribution of BP, i.e. a formula, which + * is entailed by BP and together with A and BNP entails D. + * + * Let Fark(F) be the farkas coefficient for F. We can use the fact that + * (A*Fark(A) + BNP*Fark(BNP) + BP*Fark(BP) + (neg D)*Fark(D)) => false. (E1) + * We further have that A+B => C implies (A \land B) => C. (E2) + * + * Alternative 1: + * From (E1) immediately get that BP*Fark(BP) is a solution. + * + * Alternative 2: + * We can rewrite (E2) to rewrite (E1) to + * (BP*Fark(BP)) => (neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D))) (E3) + * and since we can derive (A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) from + * A, BNP and D, we also know that it is inconsisent. Therefore + * neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) is a solution. + * + * Finally we also need the following workaround: + * 1) Although we know from theory, that the Farkas coefficients are always nonnegative, + * the Farkas coefficients provided by arith_core are sometimes negative (must be a bug) + * as workaround we take the absolute value of the provided coefficients. + */ + parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient + + STRACE("spacer.farkas", + verbose_stream() << "Farkas input: "<< "\n"; + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * prem = m.get_parent(step, i); + + rational coef = params[i].get_rational(); + + bool b_pure = m_learner.m_pr.is_b_pure (prem); + verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m_learner.m) << "\n"; + } + ); + + bool can_be_closed = true; + + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * premise = m.get_parent(step, i); + + if (m_learner.is_b_open (premise)) { + SASSERT(!m_learner.m_pr.is_a_marked(premise)); + + if (m_learner.m_pr.is_b_pure (step)) { + if (!m_use_constant_from_a) { + rational coefficient = params[i].get_rational(); + coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); + } + } + else { + can_be_closed = false; + + if (m_use_constant_from_a) { + rational coefficient = params[i].get_rational(); + coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); + } } } - else - { - can_be_closed = false; - - if (m_use_constant_from_a) - { - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - literals.push_back(to_app(m_learner.m.get_fact(premise))); - coefficients.push_back(abs(coefficient)); + else { + if (m_use_constant_from_a) { + rational coefficient = params[i].get_rational(); + coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); } } } - else - { - if (m_use_constant_from_a) - { - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - literals.push_back(to_app(m_learner.m.get_fact(premise))); - coefficients.push_back(abs(coefficient)); + + if (m_use_constant_from_a) { + params += m.get_num_parents(step); // point to the first Farkas coefficient, which corresponds to a formula in the conclusion + + // the conclusion can either be a single formula or a disjunction of several formulas, we have to deal with both situations + if (m.get_num_parents(step) + 2 < d->get_num_parameters()) { + unsigned num_args = 1; + expr* conclusion = m.get_fact(step); + expr* const* args = &conclusion; + if (m.is_or(conclusion)) { + app* _or = to_app(conclusion); + num_args = _or->get_num_args(); + args = _or->get_args(); + } + SASSERT(m.get_num_parents(step) + 2 + num_args == d->get_num_parameters()); + + bool_rewriter brw(m_learner.m); + for (unsigned i = 0; i < num_args; ++i) { + expr* premise = args[i]; + + expr_ref negatedPremise(m_learner.m); + brw.mk_not(premise, negatedPremise); + pinned.push_back(negatedPremise); + rational coefficient = params[i].get_rational(); + coeff_lits.push_back(std::make_pair(abs(coefficient), to_app(negatedPremise))); + } } } - } - if (m_use_constant_from_a) - { - params += m_learner.m.get_num_parents(step); // point to the first Farkas coefficient, which corresponds to a formula in the conclusion - - // the conclusion can either be a single formula or a disjunction of several formulas, we have to deal with both situations - if (m_learner.m.get_num_parents(step) + 2 < d->get_num_parameters()) - { - unsigned num_args = 1; - expr* conclusion = m_learner.m.get_fact(step); - expr* const* args = &conclusion; - if (m_learner.m.is_or(conclusion)) - { - app* _or = to_app(conclusion); - num_args = _or->get_num_args(); - args = _or->get_args(); - } - SASSERT(m_learner.m.get_num_parents(step) + 2 + num_args == d->get_num_parameters()); - - bool_rewriter brw(m_learner.m); - for (unsigned i = 0; i < num_args; ++i) - { - expr* premise = args[i]; - - expr_ref negatedPremise(m_learner.m); - brw.mk_not(premise, negatedPremise); - literals.push_back(to_app(negatedPremise)); - - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - coefficients.push_back(abs(coefficient)); - } + // only if all b-premises can be used directly, add the farkas core and close the step + if (can_be_closed) { + m_learner.set_closed(step, true); + + expr_ref res = compute_linear_combination(coeff_lits); + + m_learner.add_lemma_to_core(res); } } + } - // only if all b-premises can be used directly, add the farkas core and close the step - if (can_be_closed) - { - m_learner.set_closed(step, true); + expr_ref unsat_core_plugin_farkas_lemma::compute_linear_combination(const coeff_lits_t& coeff_lits) + { - expr_ref res(m_learner.m); - compute_linear_combination(coefficients, literals, res); - - m_learner.add_lemma_to_core(res); + smt::farkas_util util(m); + if (m_use_constant_from_a) { + util.set_split_literals (m_split_literals); // small optimization: if flag m_split_literals is set, then preserve diff constraints + } + for (auto& p : coeff_lits) { + util.add(p.first, p.second); + } + if (m_use_constant_from_a) { + return util.get(); + } + else { + expr_ref negated_linear_combination = util.get(); + return expr_ref(mk_not(m, negated_linear_combination), m); } } -} - -void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector& coefficients, const ptr_vector& literals, expr_ref& res) -{ - SASSERT(literals.size() == coefficients.size()); - - ast_manager& m = res.get_manager(); - smt::farkas_util util(m); - if (m_use_constant_from_a) - { - util.set_split_literals (m_split_literals); // small optimization: if flag m_split_literals is set, then preserve diff constraints - } - for(unsigned i = 0; i < literals.size(); ++i) - { - util.add(coefficients[i], literals[i]); - } - if (m_use_constant_from_a) - { - res = util.get(); - } - else - { - expr_ref negated_linear_combination = util.get(); - res = mk_not(m, negated_linear_combination); - } -} void unsat_core_plugin_farkas_lemma_optimized::compute_partial_core(proof* step) { @@ -298,34 +252,29 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vectorget_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", d->get_parameter(1).is_symbol(sym) && sym == "farkas" && - d->get_num_parameters() >= m_learner.m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients + d->get_num_parameters() >= m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients { - SASSERT(m_learner.m.has_fact(step)); + SASSERT(m.has_fact(step)); vector > linear_combination; // collects all summands of the linear combination parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient STRACE("spacer.farkas", - verbose_stream() << "Farkas input: "<< "\n"; - for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof *prem = m.get_parent (step, i); + verbose_stream() << "Farkas input: "<< "\n"; + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * prem = m.get_parent(step, i); - rational coef; - VERIFY(params[i].is_rational(coef)); - - bool b_pure = m_learner.m_pr.is_b_pure (prem); - verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n"; - } - ); + rational coef = params[i].get_rational(); + + bool b_pure = m_learner.m_pr.is_b_pure (prem); + verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m_learner.m) << "\n"; + } + ); bool can_be_closed = true; - for(unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof * premise = m.get_parent (step, i); + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * premise = m.get_parent(step, i); if (m_learner.m_pr.is_b_marked(premise) && !m_learner.is_closed(premise)) { @@ -333,10 +282,9 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector 0); + SASSERT(!linear_combination.empty()); }); // 1. construct ordered basis ptr_vector ordered_basis; obj_map map; unsigned counter = 0; - for (const auto& linear_combination : m_linear_combinations) - { - for (const auto& pair : linear_combination) - { - if (!map.contains(pair.first)) - { + for (const auto& linear_combination : m_linear_combinations) { + for (const auto& pair : linear_combination) { + if (!map.contains(pair.first)) { ordered_basis.push_back(pair.first); map.insert(pair.first, counter++); } @@ -396,11 +341,9 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector literals; - vector coefficients; - for (unsigned l=0; l < matrix.num_cols(); ++l) - { - if (!matrix.get(k,l).is_zero()) - { - literals.push_back(ordered_basis[l]); - coefficients.push_back(matrix.get(k,l)); + coeff_lits_t coeff_lits; + for (unsigned l = 0; l < matrix.num_cols(); ++l) { + if (!matrix.get(k,l).is_zero()) { + coeff_lits.push_back(std::make_pair(matrix.get(k, l), ordered_basis[l])); + } } - } - SASSERT(literals.size() > 0); - expr_ref linear_combination(m); - compute_linear_combination(coefficients, literals, linear_combination); + SASSERT(!coeff_lits.empty()); + expr_ref linear_combination = compute_linear_combination(coeff_lits); m_learner.add_lemma_to_core(linear_combination); } } - void unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const vector& coefficients, const ptr_vector& literals, expr_ref& res) - { - SASSERT(literals.size() == coefficients.size()); + expr_ref unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const coeff_lits_t& coeff_lits) + { + + smt::farkas_util util(m); + for (auto const & p : coeff_lits) { + util.add(p.first, p.second); + } + expr_ref negated_linear_combination = util.get(); + SASSERT(m.is_not(negated_linear_combination)); + return expr_ref(mk_not(m, negated_linear_combination), m); + //TODO: rewrite the get-method to return nonnegated stuff? + } - ast_manager& m = res.get_manager(); - smt::farkas_util util(m); - for(unsigned i = 0; i < literals.size(); ++i) - { - util.add(coefficients[i], literals[i]); - } - expr_ref negated_linear_combination = util.get(); - SASSERT(m.is_not(negated_linear_combination)); - res = mk_not(m, negated_linear_combination); //TODO: rewrite the get-method to return nonnegated stuff? - } - - - void unsat_core_plugin_farkas_lemma_bounded::finalize() { + void unsat_core_plugin_farkas_lemma_bounded::finalize() { if (m_linear_combinations.empty()) { return; } @@ -486,13 +421,12 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector coeffs; - for (unsigned i=0; i < matrix.num_rows(); ++i) { + for (unsigned i = 0; i < matrix.num_rows(); ++i) { coeffs.push_back(expr_ref_vector(m)); } vector bounded_vectors; - for (unsigned j=0; j < matrix.num_cols(); ++j) - { + for (unsigned j = 0; j < matrix.num_cols(); ++j) { bounded_vectors.push_back(expr_ref_vector(m)); } @@ -501,37 +435,24 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector s = mk_smt_solver(m, p, symbol::null); // TODO: incremental version? + solver_ref s = mk_smt_solver(m, p, symbol::null); // TODO: incremental version? // add new variables w_in, - for (unsigned i=0; i < matrix.num_rows(); ++i) - { + for (unsigned i = 0; i < matrix.num_rows(); ++i) { std::string name = "w_" + std::to_string(i) + std::to_string(n); - - func_decl_ref decl(m); - decl = m.mk_func_decl(symbol(name.c_str()), 0, (sort*const*)nullptr, util.mk_int()); - coeffs[i].push_back(m.mk_const(decl)); + coeffs[i].push_back(m.mk_const(name, util.mk_int())); } // we need s_jn - for (unsigned j=0; j < matrix.num_cols(); ++j) - { + for (unsigned j = 0; j < matrix.num_cols(); ++j) { std::string name = "s_" + std::to_string(j) + std::to_string(n); - - func_decl_ref decl(m); - decl = m.mk_func_decl(symbol(name.c_str()), 0, (sort*const*)nullptr, util.mk_int()); - - expr_ref s_jn(m); - s_jn = m.mk_const(decl); - - bounded_vectors[j].push_back(s_jn); + bounded_vectors[j].push_back(m.mk_const(name, util.mk_int())); } // assert bounds for all s_jn - for (unsigned l=0; l < n; ++l) { - for (unsigned j=0; j < matrix.num_cols(); ++j) { + for (unsigned l = 0; l < n; ++l) { + for (unsigned j = 0; j < matrix.num_cols(); ++j) { expr* s_jn = bounded_vectors[j][l].get(); - expr_ref lb(util.mk_le(util.mk_int(0), s_jn), m); expr_ref ub(util.mk_le(s_jn, util.mk_int(1)), m); s->assert_expr(lb); @@ -540,47 +461,40 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vectorassert_expr(eq); } + expr_ref eq(m.mk_eq(a_ij, sum), m); + s->assert_expr(eq); } - + } + // check result - lbool res = s->check_sat(0,nullptr); + lbool res = s->check_sat(0, nullptr); // if sat extract model and add corresponding linear combinations to core if (res == lbool::l_true) { model_ref model; s->get_model(model); - for (unsigned k=0; k < n; ++k) { - ptr_vector literals; - vector coefficients; - for (unsigned j=0; j < matrix.num_cols(); ++j) { + for (unsigned k = 0; k < n; ++k) { + coeff_lits_t coeff_lits; + for (unsigned j = 0; j < matrix.num_cols(); ++j) { expr_ref evaluation(m); model.get()->eval(bounded_vectors[j][k].get(), evaluation, false); if (!util.is_zero(evaluation)) { - literals.push_back(ordered_basis[j]); - coefficients.push_back(rational(1)); + coeff_lits.push_back(std::make_pair(rational(1), ordered_basis[j])); } } - SASSERT(!literals.empty()); // since then previous outer loop would have found solution already - expr_ref linear_combination(m); - compute_linear_combination(coefficients, literals, linear_combination); + SASSERT(!coeff_lits.empty()); // since then previous outer loop would have found solution already + expr_ref linear_combination = compute_linear_combination(coeff_lits); m_learner.add_lemma_to_core(linear_combination); } @@ -589,7 +503,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector todo_subproof; + ptr_buffer todo_subproof; - for (unsigned i = 0, sz = m.get_num_parents(step); i < sz; ++i) - { - proof* premise = m.get_parent (step, i); - { - if (m_learner.m_pr.is_b_marked(premise)) - { - todo_subproof.push_back(premise); - } + for (proof* premise : m.get_parents(step)) { + if (m_learner.m_pr.is_b_marked(premise)) { + todo_subproof.push_back(premise); } } while (!todo_subproof.empty()) @@ -685,10 +593,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vectorget_arg(i))); - proof* premise = m.get_parent (current, i); + for (proof* premise : m.get_parents(current)) { todo_subproof.push_back(premise); } } @@ -790,8 +695,8 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector> coeff_lits_t; + ast_manager& m; public: - unsat_core_plugin(unsat_core_learner& learner) : m_learner(learner){}; - virtual ~unsat_core_plugin(){}; + unsat_core_plugin(unsat_core_learner& learner); + virtual ~unsat_core_plugin() {}; virtual void compute_partial_core(proof* step) = 0; virtual void finalize(){}; @@ -68,24 +70,23 @@ private: /* * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res */ - void compute_linear_combination(const vector& coefficients, const ptr_vector& literals, expr_ref& res); + expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); }; class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { public: - unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner), m(m) {}; + unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner) {}; void compute_partial_core(proof* step) override; void finalize() override; protected: vector > > m_linear_combinations; - ast_manager& m; /* * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res */ - void compute_linear_combination(const vector& coefficients, const ptr_vector& literals, expr_ref& res); + expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); }; class unsat_core_plugin_farkas_lemma_bounded : public unsat_core_plugin_farkas_lemma_optimized { @@ -104,7 +105,6 @@ private: void compute_partial_core(proof* step) override; void finalize() override; private: - ast_manager& m; ast_mark m_visited; // saves for each node i whether the subproof with root i has already been added to the min-cut-problem obj_map m_proof_to_node_minus; // maps proof-steps to the corresponding minus-nodes (the ones which are closer to source) diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index 716c68b00..762735b8d 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -47,7 +47,7 @@ public: virtual ~check_sat_result() {} void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } - void set_status(lbool r) { m_status = r; } + lbool set_status(lbool r) { return m_status = r; } lbool status() const { return m_status; } virtual void collect_statistics(statistics & st) const = 0; virtual void get_unsat_core(ptr_vector & r) = 0; From 54add824e900983b09d327f4a1745bec2b91f3ad Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 6 Jun 2018 09:34:03 -0700 Subject: [PATCH 239/364] Debug print --- src/muz/spacer/spacer_pdr.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index 89647ed02..7ead97628 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -246,8 +246,8 @@ lbool context::gpdr_solve_core() { } // communicate failure to datalog::context - if (m_context) { - m_context->set_status(datalog::BOUNDED); + if (m_context) { + m_context->set_status(datalog::BOUNDED); } return l_undef; } @@ -279,7 +279,7 @@ bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { TRACE("spacer_pdr", tout << "looking at pob at level " << pob->level() << " " << mk_pp(pob->post(), m) << "\n";); - if (pob != node->pob()) + if (pob != node->pob()) ms.add_leaf(alloc(model_node, node, pob)); } node->check_pre_closed(); @@ -332,6 +332,13 @@ bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, << (k->use_farkas_generalizer() ? "FAR " : "SUB ") << k->post()->get_id(); verbose_stream().flush();); + TRACE ("spacer", + tout << "create-pob: " << k->pt().head()->get_name() + << " level: " << k->level() + << " depth: " << k->depth () + << " fvsz: " << k->get_free_vars_size() << "\n" + << mk_pp(k->post(), m) << "\n";); + } From f74ca2f0c085a3a9406eeea1d046488a29b3bfdb Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 6 Jun 2018 09:40:59 -0700 Subject: [PATCH 240/364] Fix caching bug in mbc --- src/muz/spacer/spacer_mbc.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/muz/spacer/spacer_mbc.cpp b/src/muz/spacer/spacer_mbc.cpp index 4708a6a4e..df0ce1cb4 100644 --- a/src/muz/spacer/spacer_mbc.cpp +++ b/src/muz/spacer/spacer_mbc.cpp @@ -17,16 +17,17 @@ class mbc_rewriter_cfg : public default_rewriter_cfg { ast_manager &m; const mbc::partition_map &m_pmap; + obj_map &m_subs; model &m_mdl; model_evaluator m_mev; vector &m_parts; unsigned m_current_part; - obj_map m_subs; public: mbc_rewriter_cfg(ast_manager &m, const mbc::partition_map &pmap, + obj_map &subs, model &mdl, vector &parts) : - m(m), m_pmap(pmap), m_mdl(mdl), m_mev(m_mdl), + m(m), m_pmap(pmap), m_subs(subs), m_mdl(mdl), m_mev(m_mdl), m_parts(parts), m_current_part(UINT_MAX) {m_mev.set_model_completion(true);} @@ -56,7 +57,7 @@ public: // eval in the model m_mev.eval(s, val, true); - // store decided equality (also keeps ref to s and val + // store decided equality (also keeps ref to s and val) m_parts[part].push_back(m.mk_eq(s, val)); // store substitution m_subs.insert(s, val); @@ -64,6 +65,8 @@ public: return true; } + + void reset() {reset_partition();}; void reset_partition() {m_current_part = UINT_MAX;} unsigned partition() {return m_current_part;} bool found_partition() {return m_current_part < UINT_MAX;} @@ -75,13 +78,14 @@ void mbc::operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl, vector &res) { scoped_no_proof _sp (m); - mbc_rewriter_cfg cfg(m, pmap, mdl, res); + obj_map subs; + mbc_rewriter_cfg cfg(m, pmap, subs, mdl, res); rewriter_tpl rw(m, false, cfg); th_rewriter thrw(m); for (auto *lit : lits) { expr_ref new_lit(m); - cfg.reset_partition(); + rw.reset(); rw(lit, new_lit); thrw(new_lit); if (cfg.found_partition()) { @@ -89,6 +93,10 @@ void mbc::operator()(const partition_map &pmap, expr_ref_vector &lits, res[cfg.partition()].push_back(new_lit); } } + + TRACE("mbc", tout << "Input: " << lits << "\n" + << "Output: \n"; + for (auto &vec : res) tout << vec << "\n==================\n";); } } From 9fef466c634f71fca813575d4ff9b972a72728f9 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 6 Jun 2018 10:10:00 -0700 Subject: [PATCH 241/364] Respect children order in spacer/pdr --- src/muz/spacer/spacer_pdr.cpp | 38 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index 7ead97628..d1b634350 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -318,20 +318,17 @@ bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, vector res(preds.size(), expr_ref_vector(m)); _mbc(pmap, lits, *mdl.get(), res); + // pick an order to process children + unsigned_vector kid_order; + kid_order.resize(preds.size(), 0); + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) kid_order[i] = i; + if (m_children_order == CO_REV_RULE) { + kid_order.reverse(); + } + else if (m_children_order == CO_RANDOM) { + shuffle(kid_order.size(), kid_order.c_ptr(), m_random); + } - for (unsigned i = 0, sz = res.size(); i < sz; ++i) { - expr_ref post(m); - pred_transformer &ppt = *ppts.get(i); - post = mk_and(res.get(i)); - m_pm.formula_o2n(post.get(), post, i, true); - pob * k = ppt.mk_pob(&n, prev_level(n.level()), n.depth(), post); - out.push_back(k); - IF_VERBOSE (1, verbose_stream() - << "\n\tcreate_child: " << k->pt().head()->get_name() - << " (" << k->level() << ", " << k->depth() << ") " - << (k->use_farkas_generalizer() ? "FAR " : "SUB ") - << k->post()->get_id(); - verbose_stream().flush();); TRACE ("spacer", tout << "create-pob: " << k->pt().head()->get_name() << " level: " << k->level() @@ -346,4 +343,19 @@ bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, } + + for (unsigned i = 0, sz = res.size(); i < sz; ++i) { + unsigned j = kid_order[i]; + expr_ref post(m); + pred_transformer &ppt = *ppts.get(j); + post = mk_and(res.get(j)); + m_pm.formula_o2n(post.get(), post, j, true); + pob * k = ppt.mk_pob(&n, prev_level(n.level()), n.depth(), post); + out.push_back(k); + IF_VERBOSE (1, verbose_stream() + << "\n\tcreate_child: " << k->pt().head()->get_name() + << " (" << k->level() << ", " << k->depth() << ") " + << (k->use_farkas_generalizer() ? "FAR " : "SUB ") + << k->post()->get_id(); + verbose_stream().flush();); } // spacer From cefdb8c01dbeb59cfdf1a8fc21c4620d27d63baf Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 6 Jun 2018 10:10:35 -0700 Subject: [PATCH 242/364] Use reachable cache --- src/muz/spacer/spacer_pdr.cpp | 42 +++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index d1b634350..6b13e82f8 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -264,6 +264,20 @@ bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { << node->level() << "\n";); new_pobs.reset(); checkpoint(); + pred_transformer &pt = node->pt(); + + // check reachable cache + if (pt.is_must_reachable(node->pob()->post(), nullptr)) { + TRACE("spacer", + tout << "must-reachable: " << pt.head()->get_name() << " level: " + << node->level() << " depth: " << node->depth () << "\n"; + tout << mk_pp(node->pob()->post(), m) << "\n";); + + node->set_closed(); + if (node == root_node) return true; + continue; + } + switch (expand_pob(*node->pob(), new_pobs)){ case l_true: node->set_closed(); @@ -329,20 +343,6 @@ bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, shuffle(kid_order.size(), kid_order.c_ptr(), m_random); } - TRACE ("spacer", - tout << "create-pob: " << k->pt().head()->get_name() - << " level: " << k->level() - << " depth: " << k->depth () - << " fvsz: " << k->get_free_vars_size() << "\n" - << mk_pp(k->post(), m) << "\n";); - - - } - - return true; -} - - for (unsigned i = 0, sz = res.size(); i < sz; ++i) { unsigned j = kid_order[i]; @@ -358,4 +358,18 @@ bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, << (k->use_farkas_generalizer() ? "FAR " : "SUB ") << k->post()->get_id(); verbose_stream().flush();); + TRACE ("spacer", + tout << "create-pob: " << k->pt().head()->get_name() + << " level: " << k->level() + << " depth: " << k->depth () + << " fvsz: " << k->get_free_vars_size() << "\n" + << mk_pp(k->post(), m) << "\n";); + + + } + + return true; +} + + } // spacer From 6adaed718f52a5f8a8d819453f6dcff91402e43d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 13:11:48 -0700 Subject: [PATCH 243/364] remove pdr Signed-off-by: Nikolaj Bjorner --- src/CMakeLists.txt | 1 - src/muz/base/dl_context.cpp | 31 +- src/muz/base/dl_engine_base.h | 2 - src/muz/base/fixedpoint_params.pyg | 132 +- src/muz/fp/CMakeLists.txt | 1 - src/muz/fp/dl_register_engine.cpp | 4 - src/muz/pdr/CMakeLists.txt | 20 - src/muz/pdr/pdr_closure.cpp | 177 -- src/muz/pdr/pdr_closure.h | 67 - src/muz/pdr/pdr_context.cpp | 2431 ----------------------- src/muz/pdr/pdr_context.h | 448 ----- src/muz/pdr/pdr_dl_interface.cpp | 225 --- src/muz/pdr/pdr_dl_interface.h | 78 - src/muz/pdr/pdr_farkas_learner.cpp | 1012 ---------- src/muz/pdr/pdr_farkas_learner.h | 128 -- src/muz/pdr/pdr_generalizers.cpp | 777 -------- src/muz/pdr/pdr_generalizers.h | 110 - src/muz/pdr/pdr_manager.cpp | 321 --- src/muz/pdr/pdr_manager.h | 304 --- src/muz/pdr/pdr_prop_solver.cpp | 459 ----- src/muz/pdr/pdr_prop_solver.h | 139 -- src/muz/pdr/pdr_reachable_cache.cpp | 132 -- src/muz/pdr/pdr_reachable_cache.h | 66 - src/muz/pdr/pdr_smt_context_manager.cpp | 167 -- src/muz/pdr/pdr_smt_context_manager.h | 92 - src/muz/pdr/pdr_sym_mux.cpp | 601 ------ src/muz/pdr/pdr_sym_mux.h | 247 --- src/muz/pdr/pdr_util.cpp | 508 ----- src/muz/pdr/pdr_util.h | 81 - 29 files changed, 70 insertions(+), 8691 deletions(-) delete mode 100644 src/muz/pdr/CMakeLists.txt delete mode 100644 src/muz/pdr/pdr_closure.cpp delete mode 100644 src/muz/pdr/pdr_closure.h delete mode 100644 src/muz/pdr/pdr_context.cpp delete mode 100644 src/muz/pdr/pdr_context.h delete mode 100644 src/muz/pdr/pdr_dl_interface.cpp delete mode 100644 src/muz/pdr/pdr_dl_interface.h delete mode 100644 src/muz/pdr/pdr_farkas_learner.cpp delete mode 100644 src/muz/pdr/pdr_farkas_learner.h delete mode 100644 src/muz/pdr/pdr_generalizers.cpp delete mode 100644 src/muz/pdr/pdr_generalizers.h delete mode 100644 src/muz/pdr/pdr_manager.cpp delete mode 100644 src/muz/pdr/pdr_manager.h delete mode 100644 src/muz/pdr/pdr_prop_solver.cpp delete mode 100644 src/muz/pdr/pdr_prop_solver.h delete mode 100644 src/muz/pdr/pdr_reachable_cache.cpp delete mode 100644 src/muz/pdr/pdr_reachable_cache.h delete mode 100644 src/muz/pdr/pdr_smt_context_manager.cpp delete mode 100644 src/muz/pdr/pdr_smt_context_manager.h delete mode 100644 src/muz/pdr/pdr_sym_mux.cpp delete mode 100644 src/muz/pdr/pdr_sym_mux.h delete mode 100644 src/muz/pdr/pdr_util.cpp delete mode 100644 src/muz/pdr/pdr_util.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9020c9e4b..7dee4039a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -81,7 +81,6 @@ add_subdirectory(muz/base) add_subdirectory(muz/dataflow) add_subdirectory(muz/transforms) add_subdirectory(muz/rel) -add_subdirectory(muz/pdr) add_subdirectory(muz/clp) add_subdirectory(muz/tab) add_subdirectory(muz/bmc) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index cc14de5ce..2b0987be7 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -188,9 +188,7 @@ namespace datalog { if (m_trail.get_num_scopes() == 0) { throw default_exception("there are no backtracking points to pop to"); } - if (m_engine.get()) { - throw default_exception("pop operation is only supported by duality engine"); - } + throw default_exception("pop operation is not supported"); m_trail.pop_scope(1); } @@ -576,17 +574,11 @@ namespace datalog { m_rule_properties.check_infinite_sorts(); break; case SPACER_ENGINE: - case PDR_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); m_rule_properties.check_uninterpreted_free(); break; - case QPDR_ENGINE: - m_rule_properties.collect(r); - m_rule_properties.check_for_negated_predicates(); - m_rule_properties.check_uninterpreted_free(); - break; case BMC_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_for_negated_predicates(); @@ -776,19 +768,14 @@ namespace datalog { DL_ENGINE get_engine() const { return m_engine_type; } void operator()(expr* e) { - if (is_quantifier(e)) { - m_engine_type = QPDR_ENGINE; - } - else if (m_engine_type != QPDR_ENGINE) { if (a.is_int_real(e)) { - m_engine_type = PDR_ENGINE; + m_engine_type = SPACER_ENGINE; } else if (is_var(e) && m.is_bool(e)) { - m_engine_type = PDR_ENGINE; + m_engine_type = SPACER_ENGINE; } else if (dt.is_datatype(m.get_sort(e))) { - m_engine_type = PDR_ENGINE; - } + m_engine_type = SPACER_ENGINE; } } }; @@ -805,12 +792,6 @@ namespace datalog { else if (e == symbol("spacer")) { m_engine_type = SPACER_ENGINE; } - else if (e == symbol("pdr")) { - m_engine_type = PDR_ENGINE; - } - else if (e == symbol("qpdr")) { - m_engine_type = QPDR_ENGINE; - } else if (e == symbol("bmc")) { m_engine_type = BMC_ENGINE; } @@ -858,8 +839,6 @@ namespace datalog { switch (get_engine()) { case DATALOG_ENGINE: case SPACER_ENGINE: - case PDR_ENGINE: - case QPDR_ENGINE: case BMC_ENGINE: case QBMC_ENGINE: case TAB_ENGINE: @@ -882,8 +861,6 @@ namespace datalog { switch (get_engine()) { case DATALOG_ENGINE: case SPACER_ENGINE: - case PDR_ENGINE: - case QPDR_ENGINE: case BMC_ENGINE: case QBMC_ENGINE: case TAB_ENGINE: diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index 9fc90ab1d..d6099e04c 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.h @@ -25,9 +25,7 @@ Revision History: namespace datalog { enum DL_ENGINE { DATALOG_ENGINE, - PDR_ENGINE, SPACER_ENGINE, - QPDR_ENGINE, BMC_ENGINE, QBMC_ENGINE, TAB_ENGINE, diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index b5231c27b..f11e2faf9 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -1,37 +1,37 @@ -def_module_params('fixedpoint', +def_module_params('fixedpoint', description='fixedpoint parameters', export=True, params=(('timeout', UINT, UINT_MAX, 'set timeout'), - ('engine', SYMBOL, 'auto-config', - 'Select: auto-config, datalog, spacer, pdr, bmc'), - ('datalog.default_table', SYMBOL, 'sparse', + ('engine', SYMBOL, 'auto-config', + 'Select: auto-config, datalog, bmc, spacer'), + ('datalog.default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'), - ('datalog.default_relation', SYMBOL, 'pentagon', + ('datalog.default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'), - ('datalog.generate_explanations', BOOL, False, + ('datalog.generate_explanations', BOOL, False, 'produce explanations for produced facts when using the datalog engine'), - ('datalog.use_map_names', BOOL, True, + ('datalog.use_map_names', BOOL, True, "use names from map files when displaying tuples"), - ('datalog.magic_sets_for_queries', BOOL, False, + ('datalog.magic_sets_for_queries', BOOL, False, "magic set transformation will be used for queries"), - ('datalog.explanations_on_relation_level', BOOL, False, - 'if true, explanations are generated as history of each relation, ' + - 'rather than per fact (generate_explanations must be set to true for ' + + ('datalog.explanations_on_relation_level', BOOL, False, + 'if true, explanations are generated as history of each relation, ' + + 'rather than per fact (generate_explanations must be set to true for ' + 'this option to have any effect)'), - ('datalog.unbound_compressor', BOOL, True, - "auxiliary relations will be introduced to avoid unbound variables " + + ('datalog.unbound_compressor', BOOL, True, + "auxiliary relations will be introduced to avoid unbound variables " + "in rule heads"), - ('datalog.similarity_compressor', BOOL, True, - "rules that differ only in values of constants will be merged into " + + ('datalog.similarity_compressor', BOOL, True, + "rules that differ only in values of constants will be merged into " + "a single rule"), - ('datalog.similarity_compressor_threshold', UINT, 11, - "if similarity_compressor is on, this value determines how many " + + ('datalog.similarity_compressor_threshold', UINT, 11, + "if similarity_compressor is on, this value determines how many " + "similar rules there must be in order for them to be merged"), - ('datalog.all_or_nothing_deltas', BOOL, False, + ('datalog.all_or_nothing_deltas', BOOL, False, "compile rules so that it is enough for the delta relation in " + - "union and widening operations to determine only whether the " + + "union and widening operations to determine only whether the " + "updated relation was modified or not"), - ('datalog.compile_with_widening', BOOL, False, + ('datalog.compile_with_widening', BOOL, False, "widening will be used to compile recursive rules"), ('datalog.default_table_checked', BOOL, False, "if true, the default " + 'table will be default_table inside a wrapper that checks that its results ' + @@ -39,15 +39,15 @@ def_module_params('fixedpoint', ('datalog.default_table_checker', SYMBOL, 'null', "see default_table_checked"), ('datalog.check_relation',SYMBOL,'null', "name of default relation to check. " + "operations on the default relation will be verified using SMT solving"), - ('datalog.initial_restart_timeout', UINT, 0, - "length of saturation run before the first restart (in ms), " + + ('datalog.initial_restart_timeout', UINT, 0, + "length of saturation run before the first restart (in ms), " + "zero means no restarts"), - ('datalog.output_profile', BOOL, False, - "determines whether profile information should be " + + ('datalog.output_profile', BOOL, False, + "determines whether profile information should be " + "output when outputting Datalog rules or instructions"), - ('datalog.print.tuples', BOOL, True, + ('datalog.print.tuples', BOOL, True, "determines whether tuples for output predicates should be output"), - ('datalog.profile_timeout_milliseconds', UINT, 0, + ('datalog.profile_timeout_milliseconds', UINT, 0, "instructions and rules that took less than the threshold " + "will not be printed when printed the instruction/rule list"), ('datalog.dbg_fpr_nonempty_relation_signature', BOOL, False, @@ -56,94 +56,94 @@ def_module_params('fixedpoint', "table columns, if it would have been empty otherwise"), ('datalog.subsumption', BOOL, True, "if true, removes/filters predicates with total transitions"), - ('pdr.bfs_model_search', BOOL, True, - "use BFS strategy for expanding model search"), - ('pdr.farkas', BOOL, True, + ('pdr.bfs_model_search', BOOL, True, + "use BFS strategy for expanding model search"), + ('pdr.farkas', BOOL, True, "use lemma generator based on Farkas (for linear real arithmetic)"), ('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"), - ('pdr.flexible_trace', BOOL, False, + ('pdr.flexible_trace', BOOL, False, "allow PDR generate long counter-examples " + "by extending candidate trace within search area"), - ('pdr.flexible_trace_depth', UINT, UINT_MAX, + ('pdr.flexible_trace_depth', UINT, UINT_MAX, 'Controls the depth (below the current level) at which flexible trace can be applied'), - ('pdr.use_model_generalizer', BOOL, False, + ('pdr.use_model_generalizer', BOOL, False, "use model for backwards propagation (instead of symbolic simulation)"), - ('pdr.validate_result', BOOL, False, + ('pdr.validate_result', BOOL, False, "validate result (by proof checking or model checking)"), - ('pdr.simplify_formulas_pre', BOOL, False, + ('pdr.simplify_formulas_pre', BOOL, False, "simplify derived formulas before inductive propagation"), - ('pdr.simplify_formulas_post', BOOL, False, + ('pdr.simplify_formulas_post', BOOL, False, "simplify derived formulas after inductive propagation"), - ('pdr.use_multicore_generalizer', BOOL, False, + ('pdr.use_multicore_generalizer', BOOL, False, "extract multiple cores for blocking states"), - ('pdr.use_inductive_generalizer', BOOL, True, + ('pdr.use_inductive_generalizer', BOOL, True, "generalize lemmas using induction strengthening"), - ('pdr.use_arith_inductive_generalizer', BOOL, False, + ('pdr.use_arith_inductive_generalizer', BOOL, False, "generalize lemmas using arithmetic heuristics for induction strengthening"), - ('pdr.use_convex_closure_generalizer', BOOL, False, + ('pdr.use_convex_closure_generalizer', BOOL, False, "generalize using convex closures of lemmas"), - ('pdr.use_convex_interior_generalizer', BOOL, False, + ('pdr.use_convex_interior_generalizer', BOOL, False, "generalize using convex interiors of lemmas"), - ('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " + + ('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " + "cache (2) for model search"), - ('pdr.inductive_reachability_check', BOOL, False, + ('pdr.inductive_reachability_check', BOOL, False, "assume negation of the cube on the previous level when " + "checking for reachability (not only during cube weakening)"), ('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"), - ('pdr.try_minimize_core', BOOL, False, + ('pdr.try_minimize_core', BOOL, False, "try to reduce core size (before inductive minimization)"), ('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'), - ('print_fixedpoint_extensions', BOOL, True, - "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + + ('print_fixedpoint_extensions', BOOL, True, + "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + "when printing rules"), - ('print_low_level_smt2', BOOL, False, - "use (faster) low-level SMT2 printer (the printer is scalable " + + ('print_low_level_smt2', BOOL, False, + "use (faster) low-level SMT2 printer (the printer is scalable " + "but the result may not be as readable)"), - ('print_with_variable_declarations', BOOL, True, + ('print_with_variable_declarations', BOOL, True, "use variable declarations when displaying rules " + "(instead of attempting to use original names)"), ('print_answer', BOOL, False, 'print answer instance(s) to query'), - ('print_certificate', BOOL, False, + ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), - ('print_boogie_certificate', BOOL, False, + ('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a ' + 'format understood by Boogie'), ('print_statistics', BOOL, False, 'print statistics'), - ('print_aig', SYMBOL, '', + ('print_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), ('tab.selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), - ('xform.bit_blast', BOOL, False, + ('xform.bit_blast', BOOL, False, 'bit-blast bit-vectors'), - ('xform.magic', BOOL, False, + ('xform.magic', BOOL, False, "perform symbolic magic set transformation"), - ('xform.scale', BOOL, False, + ('xform.scale', BOOL, False, "add scaling variable to linear real arithmetic clauses"), ('xform.inline_linear', BOOL, True, "try linear inlining method"), ('xform.inline_eager', BOOL, True, "try eager inlining of rules"), - ('xform.inline_linear_branch', BOOL, False, + ('xform.inline_linear_branch', BOOL, False, "try linear inlining method with potential expansion"), ('xform.compress_unbound', BOOL, True, "compress tails with unbound variables"), ('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"), - ('xform.unfold_rules', UINT, 0, + ('xform.unfold_rules', UINT, 0, "unfold rules statically using iterative squarring"), ('xform.slice', BOOL, True, "simplify clause set using slicing"), - ('xform.karr', BOOL, False, + ('xform.karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), ('spacer.use_eqclass', BOOL, False, "Generalizes equalities to equivalence classes"), - ('xform.transform_arrays', BOOL, False, + ('xform.transform_arrays', BOOL, False, "Rewrites arrays equalities and applies select over store"), - ('xform.instantiate_arrays', BOOL, False, + ('xform.instantiate_arrays', BOOL, False, "Transforms P(a) into P(i, a[i] a)"), - ('xform.instantiate_arrays.enforce', BOOL, False, + ('xform.instantiate_arrays.enforce', BOOL, False, "Transforms P(a) into P(i, a[i]), discards a from predicate"), - ('xform.instantiate_arrays.nb_quantifier', UINT, 1, + ('xform.instantiate_arrays.nb_quantifier', UINT, 1, "Gives the number of quantifiers per array"), - ('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing", + ('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing", "=> GetId(i) = i, => GetId(i) = true"), - ('xform.quantify_arrays', BOOL, False, + ('xform.quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"), - ('xform.instantiate_quantifiers', BOOL, False, + ('xform.instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"), ('xform.coalesce_rules', BOOL, False, "coalesce rules"), ('xform.tail_simplifier_pve', BOOL, True, "propagate_variable_equivalences"), @@ -154,8 +154,8 @@ def_module_params('fixedpoint', ('spacer.use_lemma_as_cti', BOOL, False, 'SPACER: use a lemma instead of a CTI in flexible_trace'), ('spacer.reset_obligation_queue', BOOL, True, 'SPACER: reset obligation queue when entering a new level'), ('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'), - ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), - ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), + ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), + ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), ('spacer.skip_propagate', BOOL, False, "Skip propagate/pushing phase. Turns PDR into a BMC that returns either reachable or unknown"), ('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"), diff --git a/src/muz/fp/CMakeLists.txt b/src/muz/fp/CMakeLists.txt index 41262813a..4837df81b 100644 --- a/src/muz/fp/CMakeLists.txt +++ b/src/muz/fp/CMakeLists.txt @@ -9,7 +9,6 @@ z3_add_component(fp clp ddnf muz - pdr rel spacer tab diff --git a/src/muz/fp/dl_register_engine.cpp b/src/muz/fp/dl_register_engine.cpp index a2270d774..28a2a1c5e 100644 --- a/src/muz/fp/dl_register_engine.cpp +++ b/src/muz/fp/dl_register_engine.cpp @@ -21,7 +21,6 @@ Revision History: #include "muz/clp/clp_context.h" #include "muz/tab/tab_context.h" #include "muz/rel/rel_context.h" -#include "muz/pdr/pdr_dl_interface.h" #include "muz/ddnf/ddnf.h" #include "muz/spacer/spacer_dl_interface.h" @@ -30,9 +29,6 @@ namespace datalog { engine_base* register_engine::mk_engine(DL_ENGINE engine_type) { switch(engine_type) { - case PDR_ENGINE: - case QPDR_ENGINE: - return alloc(pdr::dl_interface, *m_ctx); case SPACER_ENGINE: return alloc(spacer::dl_interface, *m_ctx); case DATALOG_ENGINE: diff --git a/src/muz/pdr/CMakeLists.txt b/src/muz/pdr/CMakeLists.txt deleted file mode 100644 index ca2992b97..000000000 --- a/src/muz/pdr/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -z3_add_component(pdr - SOURCES - pdr_closure.cpp - pdr_context.cpp - pdr_dl_interface.cpp - pdr_farkas_learner.cpp - pdr_generalizers.cpp - pdr_manager.cpp - pdr_prop_solver.cpp - pdr_reachable_cache.cpp - pdr_smt_context_manager.cpp - pdr_sym_mux.cpp - pdr_util.cpp - COMPONENT_DEPENDENCIES - arith_tactics - core_tactics - muz - smt_tactic - transforms -) diff --git a/src/muz/pdr/pdr_closure.cpp b/src/muz/pdr/pdr_closure.cpp deleted file mode 100644 index 82caf285b..000000000 --- a/src/muz/pdr/pdr_closure.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - pdr_closure.cpp - -Abstract: - - Utility functions for computing closures. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-9-1. - -Revision History: - ---*/ - -#include "muz/pdr/pdr_closure.h" -#include "muz/pdr/pdr_context.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/ast_util.h" - -namespace pdr { - - expr_ref scaler::operator()(expr* e, expr* k, obj_map* translate) { - m_cache[0].reset(); - m_cache[1].reset(); - m_translate = translate; - m_k = k; - return scale(e, false); - } - - expr_ref scaler::scale(expr* e, bool is_mul) { - expr* r; - if (m_cache[is_mul].find(e, r)) { - return expr_ref(r, m); - } - if (!is_app(e)) { - return expr_ref(e, m); - } - app* ap = to_app(e); - if (m_translate && m_translate->find(ap->get_decl(), r)) { - return expr_ref(r, m); - } - if (!is_mul && a.is_numeral(e)) { - return expr_ref(a.mk_mul(m_k, e), m); - } - expr_ref_vector args(m); - bool is_mul_rec = is_mul || a.is_mul(e); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - args.push_back(scale(ap->get_arg(i), is_mul_rec)); - } - expr_ref result(m); - result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); - m_cache[is_mul].insert(e, result); - return result; - } - - expr_ref scaler::undo_k(expr* e, expr* k) { - expr_safe_replace sub(m); - th_rewriter rw(m); - expr_ref result(e, m); - sub.insert(k, a.mk_numeral(rational(1), false)); - sub(result); - rw(result); - return result; - } - - - closure::closure(pred_transformer& p, bool is_closure): - m(p.get_manager()), m_pt(p), a(m), - m_is_closure(is_closure), m_sigma(m), m_trail(m) {} - - - void closure::add_variables(unsigned num_vars, expr_ref_vector& fmls) { - manager& pm = m_pt.get_pdr_manager(); - SASSERT(num_vars > 0); - while (m_vars.size() < num_vars) { - m_vars.resize(m_vars.size()+1); - m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); - } - - unsigned sz = m_pt.sig_size(); - - for (unsigned i = 0; i < sz; ++i) { - expr* var; - ptr_vector vars; - func_decl* fn0 = m_pt.sig(i); - func_decl* fn1 = pm.o2n(fn0, 0); - sort* srt = fn0->get_range(); - if (a.is_int_real(srt)) { - for (unsigned j = 0; j < num_vars; ++j) { - if (!m_vars[j].find(fn1, var)) { - var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); - m_trail.push_back(var); - m_vars[j].insert(fn1, var); - } - vars.push_back(var); - } - fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); - } - } - if (m_is_closure) { - for (unsigned i = 0; i < num_vars; ++i) { - fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - } - else { - // is interior: - for (unsigned i = 0; i < num_vars; ++i) { - fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - } - fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr()))); - } - - expr_ref closure::close_fml(expr* e) { - expr* e0, *e1, *e2; - expr_ref result(m); - if (a.is_lt(e, e1, e2)) { - result = a.mk_le(e1, e2); - } - else if (a.is_gt(e, e1, e2)) { - result = a.mk_ge(e1, e2); - } - else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) { - result = a.mk_le(e1, e2); - } - else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) { - result = a.mk_ge(e1, e2); - } - else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) || - (m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) { - result = e; - } - else { - IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";); - result = m.mk_true(); - } - return result; - } - - expr_ref closure::close_conjunction(expr* fml) { - expr_ref_vector fmls(m); - flatten_and(fml, fmls); - for (unsigned i = 0; i < fmls.size(); ++i) { - fmls[i] = close_fml(fmls[i].get()); - } - return expr_ref(mk_and(fmls), m); - } - - expr_ref closure::relax(unsigned i, expr* fml) { - scaler sc(m); - expr_ref result = sc(fml, m_sigma[i].get(), &m_vars[i]); - return close_conjunction(result); - } - - expr_ref closure::operator()(expr_ref_vector const& As) { - if (As.empty()) { - return expr_ref(m.mk_false(), m); - } - if (As.size() == 1) { - return expr_ref(As[0], m); - } - expr_ref_vector fmls(m); - expr_ref B(m); - add_variables(As.size(), fmls); - for (unsigned i = 0; i < As.size(); ++i) { - fmls.push_back(relax(i, As[i])); - } - B = mk_and(fmls); - return B; - } - -} diff --git a/src/muz/pdr/pdr_closure.h b/src/muz/pdr/pdr_closure.h deleted file mode 100644 index c41e83389..000000000 --- a/src/muz/pdr/pdr_closure.h +++ /dev/null @@ -1,67 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - pdr_closure.h - -Abstract: - - Utility functions for computing closures. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-9-1. - -Revision History: - ---*/ - -#ifndef PDR_CLOSURE_H_ -#define PDR_CLOSURE_H_ - -#include "ast/arith_decl_plugin.h" - -namespace pdr { - - // Arithmetic scaling functor. - // Variables are replaced using - // m_translate. Constants are replaced by - // multiplication with a variable 'k' (scale factor). - class scaler { - ast_manager& m; - arith_util a; - obj_map m_cache[2]; - expr* m_k; - obj_map* m_translate; - public: - scaler(ast_manager& m): m(m), a(m), m_translate(nullptr) {} - expr_ref operator()(expr* e, expr* k, obj_map* translate = nullptr); - expr_ref undo_k(expr* e, expr* k); - private: - expr_ref scale(expr* e, bool is_mul); - }; - - class pred_transformer; - - class closure { - ast_manager& m; - pred_transformer& m_pt; - arith_util a; - bool m_is_closure; - expr_ref_vector m_sigma; - expr_ref_vector m_trail; - vector > m_vars; - - expr_ref relax(unsigned i, expr* fml); - expr_ref close_conjunction(expr* fml); - expr_ref close_fml(expr* fml); - void add_variables(unsigned num_vars, expr_ref_vector& fmls); - public: - closure(pred_transformer& pt, bool is_closure); - expr_ref operator()(expr_ref_vector const& As); - - }; -} - -#endif diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp deleted file mode 100644 index 1b1350617..000000000 --- a/src/muz/pdr/pdr_context.cpp +++ /dev/null @@ -1,2431 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_context.cpp - -Abstract: - - PDR predicate transformers and search context. - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-20. - -Revision History: - - Based on pdr_dl.cpp by - Krystof Hoder (t-khoder) 2011-9-19. - -Notes: - - ---*/ - - -#include -#include "muz/base/dl_util.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "ast/rewriter/var_subst.h" -#include "util/util.h" -#include "muz/pdr/pdr_prop_solver.h" -#include "muz/pdr/pdr_context.h" -#include "muz/pdr/pdr_generalizers.h" -#include "ast/for_each_expr.h" -#include "muz/base/dl_rule_set.h" -#include "smt/tactic/unit_subsumption_tactic.h" -#include "model/model_smt2_pp.h" -#include "muz/transforms/dl_mk_rule_inliner.h" -#include "ast/ast_smt2_pp.h" -#include "qe/qe_lite.h" -#include "ast/ast_ll_pp.h" -#include "ast/proofs/proof_checker.h" -#include "smt/smt_value_sort.h" -#include "muz/base/dl_boogie_proof.h" -#include "ast/scoped_proof.h" -#include "tactic/core/blast_term_ite_tactic.h" -#include "model/model_implicant.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/ast_util.h" - -namespace pdr { - - - static const unsigned infty_level = UINT_MAX; - - static bool is_infty_level(unsigned lvl) { return lvl == infty_level; } - - static unsigned next_level(unsigned lvl) { return is_infty_level(lvl)?lvl:(lvl+1); } - - struct pp_level { - unsigned m_level; - pp_level(unsigned l): m_level(l) {} - }; - - static std::ostream& operator<<(std::ostream& out, pp_level const& p) { - if (is_infty_level(p.m_level)) { - return out << "oo"; - } - else { - return out << p.m_level; - } - } - - // ---------------- - // pred_tansformer - - pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): - pm(pm), m(pm.get_manager()), - ctx(ctx), m_head(head, m), - m_sig(m), m_solver(pm, head->get_name()), - m_invariants(m), m_transition(m), m_initial_state(m), - m_reachable(pm, (datalog::PDR_CACHE_MODE)ctx.get_params().pdr_cache_mode()) {} - - pred_transformer::~pred_transformer() { - rule2inst::iterator it2 = m_rule2inst.begin(), end2 = m_rule2inst.end(); - for (; it2 != end2; ++it2) { - dealloc(it2->m_value); - } - rule2expr::iterator it3 = m_rule2transition.begin(), end3 = m_rule2transition.end(); - for (; it3 != end3; ++it3) { - m.dec_ref(it3->m_value); - } - } - - std::ostream& pred_transformer::display(std::ostream& out) const { - if (!rules().empty()) out << "rules\n"; - datalog::rule_manager& rm = ctx.get_context().get_rule_manager(); - for (unsigned i = 0; i < rules().size(); ++i) { - rm.display_smt2(*rules()[i], out) << "\n"; - } - out << "transition\n" << mk_pp(transition(), m) << "\n"; - return out; - } - - void pred_transformer::collect_statistics(statistics& st) const { - m_solver.collect_statistics(st); - m_reachable.collect_statistics(st); - st.update("PDR num propagations", m_stats.m_num_propagations); - unsigned np = m_invariants.size(); - for (unsigned i = 0; i < m_levels.size(); ++i) { - np += m_levels[i].size(); - } - st.update("PDR num properties", np); - } - - void pred_transformer::reset_statistics() { - m_solver.reset_statistics(); - m_reachable.reset_statistics(); - m_stats.reset(); - } - - void pred_transformer::init_sig() { - if (m_sig.empty()) { - for (unsigned i = 0; i < m_head->get_arity(); ++i) { - sort * arg_sort = m_head->get_domain(i); - std::stringstream name_stm; - name_stm << m_head->get_name() << '_' << i; - func_decl_ref stm(m); - stm = m.mk_func_decl(symbol(name_stm.str().c_str()), 0, (sort*const*)nullptr, arg_sort); - m_sig.push_back(pm.get_o_pred(stm, 0)); - } - } - } - - void pred_transformer::ensure_level(unsigned level) { - if (is_infty_level(level)) { - return; - } - while (m_levels.size() <= level) { - m_solver.add_level(); - m_levels.push_back(expr_ref_vector(m)); - } - } - - bool pred_transformer::is_reachable(expr* state) { - return m_reachable.is_reachable(state); - } - - datalog::rule const& pred_transformer::find_rule(model_core const& model) const { - TRACE("pdr_verbose", - datalog::rule_manager& rm = ctx.get_context().get_rule_manager(); - for (auto const& kv : m_tag2rule) { - expr* pred = kv.m_key; - tout << mk_pp(pred, m) << ":\n"; - if (kv.m_value) rm.display_smt2(*kv.m_value, tout) << "\n"; - } - ); - - if (m_tag2rule.size() == 1) { - return *m_tag2rule.begin()->m_value; - } - - expr_ref vl(m); - for (auto const& kv : m_tag2rule) { - expr* pred = kv.m_key; - if (model.eval(to_app(pred)->get_decl(), vl) && m.is_true(vl)) { - return *kv.m_value; - } - } - throw default_exception("could not find rule"); - } - - void pred_transformer::find_predecessors(datalog::rule const& r, ptr_vector& preds) const { - preds.reset(); - unsigned tail_sz = r.get_uninterpreted_tail_size(); - for (unsigned ti = 0; ti < tail_sz; ti++) { - preds.push_back(r.get_tail(ti)->get_decl()); - } - } - - - void pred_transformer::remove_predecessors(expr_ref_vector& literals) { - // remove tags - for (unsigned i = 0; i < literals.size(); ) { - expr* l = literals[i].get(); - m.is_not(l, l); - if (m_tag2rule.contains(l)) { - literals[i] = literals.back(); - literals.pop_back(); - } - else { - ++i; - } - } - } - - void pred_transformer::simplify_formulas(tactic& tac, expr_ref_vector& v) { - goal_ref g(alloc(goal, m, false, false, false)); - for (expr* e : v) g->assert_expr(e); - goal_ref_buffer result; - tac(g, result); - SASSERT(result.size() == 1); - goal* r = result[0]; - v.reset(); - for (unsigned j = 0; j < r->size(); ++j) v.push_back(r->form(j)); - } - - void pred_transformer::simplify_formulas() { - tactic_ref us = mk_unit_subsumption_tactic(m); - simplify_formulas(*us, m_invariants); - for (auto & fmls : m_levels) - simplify_formulas(*us, fmls); - } - - expr_ref pred_transformer::get_formulas(unsigned level, bool add_axioms) { - expr_ref_vector res(m); - if (add_axioms) { - res.push_back(pm.get_background()); - res.push_back((level == 0)?initial_state():transition()); - } - res.append(m_invariants); - for (unsigned i = level; i < m_levels.size(); ++i) { - res.append(m_levels[i]); - } - return pm.mk_and(res); - } - - expr_ref pred_transformer::get_propagation_formula(decl2rel const& pts, unsigned level) { - expr_ref result(m), tmp1(m), tmp2(m); - expr_ref_vector conj(m); - if (level == 0) { - conj.push_back(initial_state()); - } - else { - conj.push_back(transition()); - } - conj.push_back(get_formulas(level, true)); - obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - for (; level > 0 && it != end; ++it) { - expr* tag = it->m_key; - datalog::rule const* r = it->m_value; - if (!r) continue; - find_predecessors(*r, m_predicates); - for (unsigned i = 0; i < m_predicates.size(); ++i) { - func_decl* d = m_predicates[i]; - pred_transformer & pt = *pts.find(d); - tmp1 = pt.get_formulas(level-1, false); - TRACE("pdr", tout << mk_pp(tmp1, m) << "\n";); - pm.formula_n2o(tmp1, tmp2, i, false); - conj.push_back(m.mk_implies(tag, tmp2)); - } - } - return pm.mk_and(conj); - } - - bool pred_transformer::propagate_to_next_level(unsigned src_level) { - unsigned tgt_level = next_level(src_level); - ensure_level(next_level(tgt_level)); - expr_ref_vector& src = m_levels[src_level]; - - CTRACE("pdr", !src.empty(), - tout << "propagating " << src_level << " to " << tgt_level; - tout << " for relation " << head()->get_name() << "\n";); - - for (unsigned i = 0; i < src.size(); ) { - expr * curr = src[i].get(); - unsigned stored_lvl = 0; - VERIFY(m_prop2level.find(curr, stored_lvl)); - SASSERT(stored_lvl >= src_level); - bool assumes_level; - if (stored_lvl > src_level) { - TRACE("pdr", tout << "at level: "<< stored_lvl << " " << mk_pp(curr, m) << "\n";); - src[i] = src.back(); - src.pop_back(); - } - else if (is_invariant(tgt_level, curr, false, assumes_level)) { - - add_property(curr, assumes_level?tgt_level:infty_level); - TRACE("pdr", tout << "is invariant: "<< pp_level(tgt_level) << " " << mk_pp(curr, m) << "\n";); - src[i] = src.back(); - src.pop_back(); - ++m_stats.m_num_propagations; - } - else { - TRACE("pdr", tout << "not propagated: " << mk_pp(curr, m) << "\n";); - ++i; - } - } - IF_VERBOSE(3, verbose_stream() << "propagate: " << pp_level(src_level) << "\n"; - for (unsigned i = 0; i < src.size(); ++i) { - verbose_stream() << mk_pp(src[i].get(), m) << "\n"; - }); - return src.empty(); - } - - bool pred_transformer::add_property1(expr * lemma, unsigned lvl) { - if (is_infty_level(lvl)) { - if (!m_invariants.contains(lemma)) { - TRACE("pdr", tout << "property1: " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); - m_invariants.push_back(lemma); - m_prop2level.insert(lemma, lvl); - m_solver.add_formula(lemma); - return true; - } - else { - TRACE("pdr", tout << "already contained: " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); - return false; - } - } - ensure_level(lvl); - unsigned old_level; - if (!m_prop2level.find(lemma, old_level) || old_level < lvl) { - TRACE("pdr", tout << "property1: " << pp_level(lvl) << " " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); - m_levels[lvl].push_back(lemma); - m_prop2level.insert(lemma, lvl); - m_solver.add_level_formula(lemma, lvl); - return true; - } - else { - TRACE("pdr", tout << "old-level: " << pp_level(old_level) << " " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); - return false; - } - } - - void pred_transformer::add_child_property(pred_transformer& child, expr* lemma, unsigned lvl) { - ensure_level(lvl); - expr_ref_vector fmls(m); - mk_assumptions(child.head(), lemma, fmls); - for (unsigned i = 0; i < fmls.size(); ++i) { - TRACE("pdr", tout << "child property: " << mk_pp(fmls[i].get(), m) << "\n";); - if (is_infty_level(lvl)) { - m_solver.add_formula(fmls[i].get()); - } - else { - m_solver.add_level_formula(fmls[i].get(), lvl); - } - } - } - - void pred_transformer::add_property(expr* lemma, unsigned lvl) { - expr_ref_vector lemmas(m); - flatten_and(lemma, lemmas); - for (unsigned i = 0; i < lemmas.size(); ++i) { - expr* lemma_i = lemmas[i].get(); - if (add_property1(lemma_i, lvl)) { - IF_VERBOSE(2, verbose_stream() << pp_level(lvl) << " " << mk_pp(lemma_i, m) << "\n";); - for (unsigned j = 0; j < m_use.size(); ++j) { - m_use[j]->add_child_property(*this, lemma_i, next_level(lvl)); - } - } - } - } - - expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) { - expr_ref result(m.mk_true(), m), v(m), c(m); - if (level == -1) { - result = pm.mk_and(m_invariants); - } - else if ((unsigned)level < m_levels.size()) { - result = pm.mk_and(m_levels[level]); - } - // replace local constants by bound variables. - expr_substitution sub(m); - for (unsigned i = 0; i < sig_size(); ++i) { - c = m.mk_const(pm.o2n(sig(i), 0)); - v = m.mk_var(i, sig(i)->get_range()); - sub.insert(c, v); - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(result); - - // adjust result according to model converter. - unsigned arity = m_head->get_arity(); - model_ref md = alloc(model, m); - if (arity == 0) { - md->register_decl(m_head, result); - } - else { - func_interp* fi = alloc(func_interp, m, arity); - fi->set_else(result); - md->register_decl(m_head, fi); - } - model_converter_ref mc = ctx.get_model_converter(); - apply(mc, md); - if (p_orig->get_arity() == 0) { - result = md->get_const_interp(p_orig); - } - else { - result = md->get_func_interp(p_orig)->get_interp(); - } - return result; - } - - void pred_transformer::add_cover(unsigned level, expr* property) { - // replace bound variables by local constants. - expr_ref result(property, m), v(m), c(m); - expr_substitution sub(m); - for (unsigned i = 0; i < sig_size(); ++i) { - c = m.mk_const(pm.o2n(sig(i), 0)); - v = m.mk_var(i, sig(i)->get_range()); - sub.insert(v, c); - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(result); - TRACE("pdr", tout << "cover:\n" << mk_pp(result, m) << "\n";); - // add the property. - add_property(result, level); - } - - void pred_transformer::propagate_to_infinity(unsigned invariant_level) { - expr_ref inv = get_formulas(invariant_level, false); - add_property(inv, infty_level); - // cleanup - for (unsigned i = invariant_level; i < m_levels.size(); ++i) { - m_levels[i].reset(); - } - } - - lbool pred_transformer::is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level) { - TRACE("pdr", - tout << "is-reachable: " << head()->get_name() << " level: " << n.level() << "\n"; - tout << mk_pp(n.state(), m) << "\n";); - ensure_level(n.level()); - model_ref model; - prop_solver::scoped_level _sl(m_solver, n.level()); - m_solver.set_core(core); - m_solver.set_model(&model); - lbool is_sat = m_solver.check_conjunction_as_assumptions(n.state()); - if (is_sat == l_true && core) { - core->reset(); - TRACE("pdr", tout << "updating model\n"; - model_smt2_pp(tout, m, *model, 0); - tout << mk_pp(n.state(), m) << "\n";); - n.set_model(model); - } - else if (is_sat == l_false) { - uses_level = m_solver.assumes_level(); - } - m_solver.set_model(nullptr); - return is_sat; - } - - bool pred_transformer::is_invariant(unsigned level, expr* states, bool inductive, bool& assumes_level, expr_ref_vector* core) { - expr_ref_vector conj(m); - expr_ref tmp(m); - - conj.push_back(m.mk_not(states)); - - if (inductive) { - mk_assumptions(head(), states, conj); - } - tmp = pm.mk_and(conj); - prop_solver::scoped_level _sl(m_solver, level); - m_solver.set_core(core); - m_solver.set_model(nullptr); - lbool r = m_solver.check_conjunction_as_assumptions(tmp); - if (r == l_false) { - assumes_level = m_solver.assumes_level(); - } - return r == l_false; - } - - bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& lits, bool& assumes_level) { - manager& pm = get_pdr_manager(); - expr_ref_vector conj(m), core(m); - expr_ref fml(m), states(m); - states = m.mk_not(pm.mk_and(lits)); - mk_assumptions(head(), states, conj); - fml = pm.mk_and(conj); - prop_solver::scoped_level _sl(m_solver, level); - m_solver.set_core(&core); - m_solver.set_subset_based_core(true); - m_solver.set_model(nullptr); - lbool res = m_solver.check_assumptions_and_formula(lits, fml); - if (res == l_false) { - lits.reset(); - lits.append(core); - assumes_level = m_solver.assumes_level(); - } - return res == l_false; - } - - void pred_transformer::mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result) { - expr_ref tmp1(m), tmp2(m); - obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - for (; it != end; ++it) { - expr* pred = it->m_key; - datalog::rule const* r = it->m_value; - if (!r) continue; - find_predecessors(*r, m_predicates); - for (unsigned i = 0; i < m_predicates.size(); i++) { - func_decl* d = m_predicates[i]; - if (d == head) { - tmp1 = m.mk_implies(pred, fml); - pm.formula_n2o(tmp1, tmp2, i); - result.push_back(tmp2); - } - } - } - } - - void pred_transformer::initialize(decl2rel const& pts) { - m_initial_state = m.mk_false(); - m_transition = m.mk_true(); - init_rules(pts, m_initial_state, m_transition); - th_rewriter rw(m); - rw(m_transition); - rw(m_initial_state); - - m_solver.add_formula(m_transition); - m_solver.add_level_formula(m_initial_state, 0); - TRACE("pdr", - tout << "Initial state: " << mk_pp(m_initial_state, m) << "\n"; - tout << "Transition: " << mk_pp(m_transition, m) << "\n";); - SASSERT(is_app(m_initial_state)); - m_reachable.add_init(to_app(m_initial_state)); - } - - void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition) { - expr_ref_vector transitions(m); - ptr_vector tr_rules; - datalog::rule const* rule; - expr_ref_vector disj(m); - app_ref pred(m); - for (unsigned i = 0; i < rules().size(); ++i) { - init_rule(pts, *rules()[i], init, tr_rules, transitions); - } - switch(transitions.size()) { - case 0: - transition = m.mk_false(); - break; - case 1: - // create a dummy tag. - pred = m.mk_fresh_const(head()->get_name().str().c_str(), m.mk_bool_sort()); - rule = tr_rules[0]; - m_tag2rule.insert(pred, rule); - m_rule2tag.insert(rule, pred.get()); - transitions.push_back(pred); - transition = pm.mk_and(transitions); - break; - default: - for (unsigned i = 0; i < transitions.size(); ++i) { - pred = m.mk_fresh_const(head()->get_name().str().c_str(), m.mk_bool_sort()); - rule = tr_rules[i]; - m_tag2rule.insert(pred, rule); - m_rule2tag.insert(rule, pred); - disj.push_back(pred); - transitions[i] = m.mk_implies(pred, transitions[i].get()); - } - transitions.push_back(m.mk_or(disj.size(), disj.c_ptr())); - transition = pm.mk_and(transitions); - break; - } - } - - void pred_transformer::init_rule( - decl2rel const& pts, - datalog::rule const& rule, - expr_ref& init, - ptr_vector& rules, - expr_ref_vector& transitions) - { - // Predicates that are variable representatives. Other predicates at - // positions the variables occur are made equivalent with these. - expr_ref_vector conj(m); - app_ref_vector var_reprs(m); - ptr_vector aux_vars; - - unsigned ut_size = rule.get_uninterpreted_tail_size(); - unsigned t_size = rule.get_tail_size(); - SASSERT(ut_size <= t_size); - init_atom(pts, rule.get_head(), var_reprs, conj, UINT_MAX); - for (unsigned i = 0; i < ut_size; ++i) { - if (rule.is_neg_tail(i)) { - char const* msg = "PDR does not supported negated predicates in rule tails"; - IF_VERBOSE(0, verbose_stream() << msg << "\n";); - throw default_exception(msg); - } - init_atom(pts, rule.get_tail(i), var_reprs, conj, i); - } - for (unsigned i = ut_size; i < t_size; ++i) { - ground_free_vars(rule.get_tail(i), var_reprs, aux_vars); - } - SASSERT(check_filled(var_reprs)); - expr_ref_vector tail(m); - for (unsigned i = ut_size; i < t_size; ++i) { - tail.push_back(rule.get_tail(i)); - } - flatten_and(tail); - for (unsigned i = 0; i < tail.size(); ++i) { - expr_ref tmp(m); - var_subst vs(m, false); - vs(tail[i].get(), var_reprs.size(), (expr*const*)var_reprs.c_ptr(), tmp); - conj.push_back(tmp); - TRACE("pdr", tout << mk_pp(tail[i].get(), m) << "\n" << mk_pp(tmp, m) << "\n";); - if (!is_ground(tmp)) { - std::stringstream msg; - msg << "PDR cannot solve non-ground tails: " << tmp; - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - } - expr_ref fml = pm.mk_and(conj); - th_rewriter rw(m); - rw(fml); - if (ctx.is_dl() || ctx.is_utvpi()) { - blast_term_ite(fml); - } - TRACE("pdr", tout << mk_pp(fml, m) << "\n";); - SASSERT(is_ground(fml)); - if (m.is_false(fml)) { - // no-op. - } - else { - if (ut_size == 0) { - init = m.mk_or(init, fml); - } - transitions.push_back(fml); - m.inc_ref(fml); - m_rule2transition.insert(&rule, fml.get()); - rules.push_back(&rule); - } - m_rule2inst.insert(&rule, alloc(app_ref_vector, var_reprs)); - m_rule2vars.insert(&rule, aux_vars); - TRACE("pdr", - tout << rule.get_decl()->get_name() << "\n"; - for (unsigned i = 0; i < var_reprs.size(); ++i) { - tout << mk_pp(var_reprs[i].get(), m) << " "; - } - if (!var_reprs.empty()) tout << "\n";); - } - - bool pred_transformer::check_filled(app_ref_vector const& v) const { - for (unsigned i = 0; i < v.size(); ++i) { - if (!v[i]) return false; - } - return true; - } - - // create constants for free variables in tail. - void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars) { - expr_free_vars fv; - fv(e); - while (vars.size() < fv.size()) { - vars.push_back(nullptr); - } - for (unsigned i = 0; i < fv.size(); ++i) { - if (fv[i] && !vars[i].get()) { - vars[i] = m.mk_fresh_const("aux", fv[i]); - aux_vars.push_back(vars[i].get()); - } - } - } - - // create names for variables used in relations. - void pred_transformer::init_atom( - decl2rel const& pts, - app * atom, - app_ref_vector& var_reprs, - expr_ref_vector& conj, - unsigned tail_idx - ) - { - unsigned arity = atom->get_num_args(); - func_decl* head = atom->get_decl(); - pred_transformer& pt = *pts.find(head); - for (unsigned i = 0; i < arity; i++) { - app_ref rep(m); - - if (tail_idx == UINT_MAX) { - rep = m.mk_const(pm.o2n(pt.sig(i), 0)); - } - else { - rep = m.mk_const(pm.o2o(pt.sig(i), 0, tail_idx)); - } - - expr * arg = atom->get_arg(i); - if (is_var(arg)) { - var * v = to_var(arg); - unsigned var_idx = v->get_idx(); - if (var_idx >= var_reprs.size()) { - var_reprs.resize(var_idx+1); - } - expr * repr = var_reprs[var_idx].get(); - if (repr) { - conj.push_back(m.mk_eq(rep, repr)); - } - else { - var_reprs[var_idx] = rep; - } - } - else { - SASSERT(is_app(arg)); - conj.push_back(m.mk_eq(rep, arg)); - } - } - } - - void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r) { - r.push_back(pm.get_background()); - r.push_back((lvl == 0)?initial_state():transition()); - for (unsigned i = 0; i < rules().size(); ++i) { - add_premises(pts, lvl, *rules()[i], r); - } - } - - void pred_transformer::close(expr* e) { - m_reachable.add_reachable(e); - } - - void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r) { - find_predecessors(rule, m_predicates); - for (unsigned i = 0; i < m_predicates.size(); ++i) { - expr_ref tmp(m); - func_decl* head = m_predicates[i]; - pred_transformer& pt = *pts.find(head); - expr_ref inv = pt.get_formulas(lvl, false); - if (!m.is_true(inv)) { - pm.formula_n2o(inv, tmp, i, true); - r.push_back(tmp); - } - } - } - - void pred_transformer::inherit_properties(pred_transformer& other) { - SASSERT(m_head == other.m_head); - obj_map::iterator it = other.m_prop2level.begin(); - obj_map::iterator end = other.m_prop2level.end(); - for (; it != end; ++it) { - IF_VERBOSE(2, verbose_stream() << "(pdr-inherit: " << mk_pp(it->m_key, m) << ")\n";); - add_property(it->m_key, it->m_value); - } - } - - // ---------------- - // model_node - - void model_node::set_closed() { - TRACE("pdr", tout << state() << "\n";); - pt().close(state()); - m_closed = true; - } - - void model_node::set_open() { - SASSERT(m_closed); - m_closed = false; - model_node* p = parent(); - while (p && p->is_closed()) { - p->m_closed = false; - p = p->parent(); - } - } - - void model_node::check_pre_closed() { - for (unsigned i = 0; i < children().size(); ++i) { - if (children()[i]->is_open()) return; - } - set_pre_closed(); - model_node* p = parent(); - while (p && p->is_1closed()) { - p->set_pre_closed(); - p = p->parent(); - } - } - - static bool is_ini(datalog::rule const& r) { - return r.get_uninterpreted_tail_size() == 0; - } - - datalog::rule* model_node::get_rule() { - if (m_rule) { - return const_cast(m_rule); - } - // only initial states are not set by the PDR search. - SASSERT(m_model.get()); - if (!m_model.get()) { - std::stringstream msg; - msg << "no model for node " << state(); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - - datalog::rule const& rl1 = pt().find_rule(*m_model); - if (is_ini(rl1)) { - set_rule(&rl1); - return const_cast(m_rule); - } - ast_manager& m = pt().get_manager(); - // otherwise, the initial state is reachable. - ptr_vector const& rules = pt().rules(); - ptr_vector ini_rules; - expr_ref_vector tags(m); - expr_ref ini_tags(m), ini_state(m); - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule* rl = rules[i]; - if (is_ini(*rl)) { - tags.push_back(pt().rule2tag(rl)); - } - } - SASSERT(!tags.empty()); - ini_tags = ::mk_or(tags); - ini_state = m.mk_and(ini_tags, pt().initial_state(), state()); - model_ref mdl; - pt().get_solver().set_model(&mdl); - TRACE("pdr", tout << ini_state << "\n";); - if (l_true != pt().get_solver().check_conjunction_as_assumptions(ini_state)) { - std::stringstream msg; - msg << "Unsatisfiable initial state: " << ini_state << "\n"; - display(msg, 2); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - SASSERT(mdl.get()); - datalog::rule const& rl2 = pt().find_rule(*mdl); - SASSERT(is_ini(rl2)); - set_rule(&rl2); - pt().get_solver().set_model(nullptr); - return const_cast(m_rule); - } - - - void model_node::mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding) { - ast_manager& m = pt().get_manager(); - expr_ref_vector conjs(m); - obj_map model; - flatten_and(state(), conjs); - for (unsigned i = 0; i < conjs.size(); ++i) { - expr* e = conjs[i].get(), *e1, *e2; - if (m.is_eq(e, e1, e2)) { - if (m.is_value(e2)) { - model.insert(e1, e2); - } - else if (m.is_value(e1)) { - model.insert(e2, e1); - } - } - else if (m.is_not(e, e1)) { - model.insert(e1, m.mk_false()); - } - else { - model.insert(e, m.mk_true()); - } - } - r0 = get_rule(); - app_ref_vector& inst = pt().get_inst(r0); - TRACE("pdr", tout << state() << " instance: " << inst.size() << "\n";); - for (unsigned i = 0; i < inst.size(); ++i) { - expr* v; - if (model.find(inst[i].get(), v)) { - binding.push_back(v); - } - else { - binding.push_back(m.mk_var(i, m.get_sort(inst[i].get()))); - } - } - r1 = r0; - if (!inst.empty()) { - r1.get_manager().substitute(r1, binding.size(), binding.c_ptr()); - } - } - - - - std::ostream& model_node::display(std::ostream& out, unsigned indent) { - for (unsigned i = 0; i < indent; ++i) out << " "; - out << m_level << " " << m_pt.head()->get_name() << " " << (m_closed?"closed":"open") << "\n"; - for (unsigned i = 0; i < indent; ++i) out << " "; - out << " " << mk_pp(m_state, m_state.get_manager(), indent) << " " << m_state->get_id() << "\n"; - for (unsigned i = 0; i < children().size(); ++i) { - children()[i]->display(out, indent + 1); - } - return out; - } - - // return order of this node in the children of its parent - unsigned model_node::index() const { - model_node* p = parent(); - if (!p) return 0; - for (unsigned i = 0; i < p->children().size(); ++i) { - if (this == p->children()[i]) return i; - } - UNREACHABLE(); - return 0; - } - - // detach this node from a queue with the head root - // requires: root is a head of a queue - void model_node::dequeue(model_node*& root) { - TRACE("pdr", tout << this << " root: " << root << " " << state() << "\n";); - if (!m_next && !m_prev) return; - SASSERT(m_next); - SASSERT(m_prev); - SASSERT(children().empty()); - if (this == m_next) { - SASSERT(m_prev == this); - if (root == this) { - root = nullptr; - } - } - else { - m_next->m_prev = m_prev; - m_prev->m_next = m_next; - if (this == root) { - root = m_next; - } - } - TRACE("pdr", tout << "new root: " << root << "\n";); - m_prev = nullptr; - m_next = nullptr; - } - - - // insert node n after this in the queue - // requires: this is in a queue or this == n - void model_node::enqueue(model_node* n) { - TRACE("pdr", tout << n << " " << n->state() << "\n";); - SASSERT(!n->m_next); - SASSERT(!n->m_prev); - if (this == n) { - m_next = n; - m_prev = n; - } - else { - n->m_next = m_next; - m_next->m_prev = n; - m_next = n; - n->m_prev = this; - } - } - // ---------------- - // model_search - - /** - \brief Dequeue the next goal. - */ - model_node* model_search::next() { - if (!m_goal) { - return nullptr; - } - else { - model_node* result = m_goal; - result->dequeue(m_goal); - return result; - } - } - - - void model_search::add_leaf(model_node& n) { - SASSERT(n.children().empty()); - model_nodes ns; - model_nodes& nodes = cache(n).insert_if_not_there2(n.state(), ns)->get_data().m_value; - if (nodes.contains(&n)) { - return; - } - nodes.push_back(&n); - TRACE("pdr_verbose", tout << "add: " << n.level() << ": " << &n << " " << n.state() << "\n";); - if (nodes.size() == 1) { - set_leaf(n); - } - else { - n.set_pre_closed(); - } - } - - void model_search::set_leaf(model_node& n) { - // remove all children that n might have - erase_children(n, true); - SASSERT(n.is_open()); - enqueue_leaf(&n); - } - - void model_search::enqueue_leaf(model_node* n) { - TRACE("pdr_verbose", tout << "node: " << n << " " << n->state() << " goal: " << m_goal << "\n";); - SASSERT(n->is_open()); - // queue is empty, initialize it with n - if (!m_goal) { - m_goal = n; - m_goal->enqueue(n); - } - // insert n after m_goal - else if (m_bfs) { - m_goal->enqueue(n); - } - // insert n after m_goal()->next() - else { - m_goal->next()->enqueue(n); - } - } - - void model_search::set_root(model_node* root) { - reset(); - m_root = root; - SASSERT(cache(*root).empty()); - cache(*root).insert(root->state(), 1); - set_leaf(*root); - } - - obj_map >& model_search::cache(model_node const& n) { - unsigned l = n.orig_level(); - if (l >= m_cache.size()) { - m_cache.resize(l + 1); - } - return m_cache[l]; - } - - void model_search::erase_children(model_node& n, bool backtrack) { - ptr_vector todo, nodes; - todo.append(n.children()); - // detach n from queue - remove_goal(n); - // removes children - n.reset(); - while (!todo.empty()) { - model_node* m = todo.back(); - todo.pop_back(); - nodes.push_back(m); - todo.append(m->children()); - remove_node(*m, backtrack); - } - std::for_each(nodes.begin(), nodes.end(), delete_proc()); - } - - // removes node from the search tree and from the cache - void model_search::remove_node(model_node& n, bool backtrack) { - TRACE("pdr_verbose", tout << "remove: " << n.level() << ": " << &n << " " << n.state() << "\n";); - model_nodes& nodes = cache(n).find(n.state()); - nodes.erase(&n); - // detach n from m_goals - remove_goal(n); - // TBD: siblings would also fail if n is not a goal. - if (!nodes.empty() && backtrack && nodes[0]->children().empty() && nodes[0]->is_closed()) { - TRACE("pdr_verbose", for (unsigned i = 0; i < nodes.size(); ++i) n.display(tout << &n << "\n", 2);); - model_node* n1 = nodes[0]; - n1->set_open(); - enqueue_leaf(n1); - } - - if (!nodes.empty() && n.get_model_ptr() && backtrack) { - model_ref mr(n.get_model_ptr()); - nodes[0]->set_model(mr); - } - if (nodes.empty()) { - cache(n).remove(n.state()); - } - } - - // detach node n from the queue m_goal - void model_search::remove_goal(model_node& n) { - n.dequeue(m_goal); - } - - void model_search::well_formed() { - // each open leaf is in the set of m_goal. - ptr_vector nodes; - nodes.push_back(&get_root()); - for (unsigned i = 0; i < nodes.size(); ++i) { - model_node* n = nodes[i]; - if (!n->children().empty()) { - nodes.append(n->children()); - } - else if (n->is_open() && !n->is_goal() && n->parent()) { - TRACE("pdr", n->display(tout << "node " << n << " not found among leaves\n", 0); display(tout);); - UNREACHABLE(); - return; - } - } - if (m_goal) { - model_node* n = m_goal; - do { - if (!n->is_open() || !n->children().empty()) { - TRACE("pdr", n->display(tout << "invalid leaf\n", 0); - display(tout);); - UNREACHABLE(); - return; - } - n = n->next(); - } - while (m_goal != n); - } - - // each state appears in at most one goal per level. - bool found = true; - for (unsigned l = 0; m_goal && found; ++l) { - found = false; - obj_hashtable open_states; - model_node* n = m_goal; - do { - if (n->level() == l) { - found = true; - if (n->is_open()) { - if (open_states.contains(n->state())) { - TRACE("pdr", n->display(tout << "repeated leaf\n", 0); display(tout);); - UNREACHABLE(); - } - open_states.insert(n->state()); - } - } - n = n->next(); - } - while (m_goal != n); - } - // a node is open if and only if it contains an - // open child which is a goal. - for (unsigned i = 0; i < nodes.size(); ++i) { - model_node* n = nodes[i]; - if (!n->children().empty() && n->parent()) { - found = false; - for (unsigned j = 0; !found && j < n->children().size(); ++j) { - found = n->children()[j]->is_open(); - } - if (n->is_open() != found) { - TRACE("pdr", n->display(tout << "node in inconsistent state\n", 0); display(tout);); - UNREACHABLE(); - } - } - } - } - - unsigned model_search::num_goals() const { - model_node* n = m_goal; - unsigned num = 0; - if (n) { - do { - ++num; - n = n->next(); - } - while (n != m_goal); - } - return num; - } - - std::ostream& model_search::display(std::ostream& out) const { - if (m_root) { - m_root->display(out, 0); - } - out << "goals " << num_goals() << "\n"; - model_node* n = m_goal; - if (n) { - do { - n->display(out, 1); - n = n->next(); - } - while (n != m_goal); - } - return out; - } - - /** - \brief Ensure that all nodes in the tree have associated models. - get_trace and get_proof_trace rely on models to extract rules. - */ - void model_search::update_models() { - obj_map models; - obj_map rules; - ptr_vector todo; - todo.push_back(m_root); - while (!todo.empty()) { - model_node* n = todo.back(); - if (n->get_model_ptr()) { - models.insert(n->state(), n->get_model_ptr()); - rules.insert(n->state(), n->get_rule()); - } - todo.pop_back(); - todo.append(n->children().size(), n->children().c_ptr()); - } - - todo.push_back(m_root); - while (!todo.empty()) { - model_node* n = todo.back(); - model* md = nullptr; - if (!n->get_model_ptr()) { - if (models.find(n->state(), md)) { - TRACE("pdr", tout << n->state() << "\n";); - model_ref mr(md); - n->set_model(mr); - datalog::rule const* rule = rules.find(n->state()); - n->set_rule(rule); - } - else { - TRACE("pdr", tout << "no model for " << n->state() << "\n";); - IF_VERBOSE(1, n->display(verbose_stream() << "no model:\n", 0); - verbose_stream() << n->state() << "\n";); - } - } - else { - TRACE("pdr", tout << n->state() << "\n";); - } - todo.pop_back(); - todo.append(n->children().size(), n->children().c_ptr()); - } - } - - /** - Extract trace comprising of constraints - and predicates that are satisfied from facts to the query. - The resulting trace - */ - expr_ref model_search::get_trace(context const& ctx) { - pred_transformer& pt = get_root().pt(); - ast_manager& m = pt.get_manager(); - manager& pm = pt.get_pdr_manager(); - datalog::context& dctx = ctx.get_context(); - datalog::rule_manager& rm = dctx.get_rule_manager(); - expr_ref_vector constraints(m), predicates(m); - expr_ref tmp(m); - ptr_vector children; - unsigned deltas[2]; - datalog::rule_ref rule(rm), r0(rm); - model_node* n = m_root; - datalog::rule_counter& vc = rm.get_counter(); - substitution subst(m); - unifier unif(m); - rule = n->get_rule(); - unsigned max_var = vc.get_max_rule_var(*rule); - predicates.push_back(rule->get_head()); - children.push_back(n); - bool first = true; - update_models(); - while (!children.empty()) { - SASSERT(children.size() == predicates.size()); - expr_ref_vector binding(m); - n = children.back(); - children.pop_back(); - TRACE("pdr", n->display(tout, 0);); - n->mk_instantiate(r0, rule, binding); - - max_var = std::max(max_var, vc.get_max_rule_var(*rule)); - subst.reset(); - subst.reserve(2, max_var+1); - deltas[0] = 0; - deltas[1] = max_var+1; - - VERIFY(unif(predicates.back(), rule->get_head(), subst)); - for (unsigned i = 0; i < constraints.size(); ++i) { - subst.apply(2, deltas, expr_offset(constraints[i].get(), 0), tmp); - dctx.get_rewriter()(tmp); - constraints[i] = tmp; - } - for (unsigned i = 0; i < predicates.size(); ++i) { - subst.apply(2, deltas, expr_offset(predicates[i].get(), 0), tmp); - predicates[i] = tmp; - } - if (!first) { - constraints.push_back(predicates.back()); - } - first = false; - predicates.pop_back(); - for (unsigned i = rule->get_uninterpreted_tail_size(); i < rule->get_tail_size(); ++i) { - subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); - constraints.push_back(tmp); - } - for (unsigned i = 0; i < constraints.size(); ++i) { - max_var = std::max(vc.get_max_var(constraints[i].get()), max_var); - } - if (n->children().empty()) { - // nodes whose states are repeated - // in the search tree do not have children. - continue; - } - - SASSERT(n->children().size() == rule->get_uninterpreted_tail_size()); - - for (unsigned i = 0; i < rule->get_uninterpreted_tail_size(); ++i) { - subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); - predicates.push_back(tmp); - } - for (unsigned i = 0; i < predicates.size(); ++i) { - max_var = std::max(vc.get_max_var(predicates[i].get()), max_var); - } - - children.append(n->children()); - } - expr_safe_replace repl(m); - for (unsigned i = 0; i < constraints.size(); ++i) { - expr* e = constraints[i].get(), *e1, *e2; - if (m.is_eq(e, e1, e2) && is_var(e1) && is_ground(e2)) { - repl.insert(e1, e2); - } - else if (m.is_eq(e, e1, e2) && is_var(e2) && is_ground(e1)) { - repl.insert(e2, e1); - } - } - expr_ref_vector result(m); - for (unsigned i = 0; i < constraints.size(); ++i) { - expr_ref tmp(m); - tmp = constraints[i].get(); - repl(tmp); - dctx.get_rewriter()(tmp); - if (!m.is_true(tmp)) { - result.push_back(tmp); - } - } - return pm.mk_and(result); - } - - proof_ref model_search::get_proof_trace(context const& ctx) { - pred_transformer& pt = get_root().pt(); - ast_manager& m = pt.get_manager(); - datalog::context& dctx = ctx.get_context(); - datalog::rule_manager& rm = dctx.get_rule_manager(); - datalog::rule_unifier unif(dctx); - datalog::dl_decl_util util(m); - datalog::rule_ref r0(rm), r1(rm); - obj_map cache; - obj_map rules; - ptr_vector todo; - proof_ref_vector trail(m); - datalog::rule_ref_vector rules_trail(rm); - proof* pr = nullptr; - unif.set_normalize(true); - todo.push_back(m_root); - update_models(); - while (!todo.empty()) { - model_node* n = todo.back(); - TRACE("pdr", n->display(tout, 0);); - if (cache.find(n->state(), pr)) { - todo.pop_back(); - continue; - } - ptr_vector pfs; - ptr_vector rls; - ptr_vector const& chs = n->children(); - pfs.push_back(0); - rls.push_back(0); - for (unsigned i = 0; i < chs.size(); ++i) { - if (cache.find(chs[i]->state(), pr)) { - pfs.push_back(pr); - rls.push_back(rules.find(chs[i]->state())); - } - else { - todo.push_back(chs[i]); - } - } - if (pfs.size() != 1 + chs.size()) { - continue; - } - proof_ref rl(m); - expr_ref_vector binding(m); - n->mk_instantiate(r0, r1, binding); - proof_ref p1(m), p2(m); - p1 = r0->get_proof(); - IF_VERBOSE(0, if (!p1) r0->display(dctx, verbose_stream());); - SASSERT(p1); - pfs[0] = p1; - rls[0] = r1; - TRACE("pdr", - tout << "root: " << mk_pp(p1.get(), m) << "\n"; - for (unsigned i = 0; i < binding.size(); ++i) { - tout << mk_pp(binding[i].get(), m) << "\n"; - } - for (unsigned i = 1; i < pfs.size(); ++i) { - tout << mk_pp(pfs[i], m) << "\n"; - } - ); - datalog::rule_ref reduced_rule(rm), r3(rm); - reduced_rule = rls[0]; - // check if binding is identity. - bool binding_is_id = true; - for (unsigned i = 0; binding_is_id && i < binding.size(); ++i) { - expr* v = binding[i].get(); - binding_is_id = is_var(v) && to_var(v)->get_idx() == i; - } - if (rls.size() > 1 || !binding_is_id) { - expr_ref tmp(m); - vector substs; - svector > positions; - substs.push_back(binding); // TODO base substitution. - for (unsigned i = 1; i < rls.size(); ++i) { - datalog::rule& src = *rls[i]; - bool unified = unif.unify_rules(*reduced_rule, 0, src); - if (!unified) { - IF_VERBOSE(0, - verbose_stream() << "Could not unify rules: "; - reduced_rule->display(dctx, verbose_stream()); - src.display(dctx, verbose_stream());); - } - expr_ref_vector sub1 = unif.get_rule_subst(*reduced_rule, true); - TRACE("pdr", - for (unsigned k = 0; k < sub1.size(); ++k) { - tout << mk_pp(sub1[k].get(), m) << " "; - } - tout << "\n"; - ); - - for (unsigned j = 0; j < substs.size(); ++j) { - for (unsigned k = 0; k < substs[j].size(); ++k) { - var_subst(m, false)(substs[j][k].get(), sub1.size(), sub1.c_ptr(), tmp); - substs[j][k] = tmp; - } - while (substs[j].size() < sub1.size()) { - substs[j].push_back(sub1[substs[j].size()].get()); - } - } - - positions.push_back(std::make_pair(i,0)); - substs.push_back(unif.get_rule_subst(src, false)); - VERIFY(unif.apply(*reduced_rule.get(), 0, src, r3)); - reduced_rule = r3; - } - - expr_ref fml_concl(m); - rm.to_formula(*reduced_rule.get(), fml_concl); - p1 = m.mk_hyper_resolve(pfs.size(), pfs.c_ptr(), fml_concl, positions, substs); - - } - cache.insert(n->state(), p1); - rules.insert(n->state(), reduced_rule); - trail.push_back(p1); - rules_trail.push_back(reduced_rule); - todo.pop_back(); - } - return proof_ref(cache.find(m_root->state()), m); - } - - model_search::~model_search() { - TRACE("pdr", tout << "\n";); - reset(); - } - - void model_search::reset() { - if (m_root) { - erase_children(*m_root, false); - remove_node(*m_root, false); - dealloc(m_root); - m_root = nullptr; - } - m_cache.reset(); - } - - void model_search::backtrack_level(bool uses_level, model_node& n) { - SASSERT(m_root); - if (uses_level && m_root->level() > n.level()) { - IF_VERBOSE(2, verbose_stream() << "Increase level " << n.level() << "\n";); - n.increase_level(); - enqueue_leaf(&n); - } - else { - model_node* p = n.parent(); - if (p) { - set_leaf(*p); - } - } - DEBUG_CODE(well_formed();); - } - - // ---------------- - // context - - context::context( - smt_params& fparams, - fixedpoint_params const& params, - ast_manager& m - ) - : m_fparams(fparams), - m_params(params), - m(m), - m_context(nullptr), - m_pm(m_fparams, params.pdr_max_num_contexts(), m), - m_query_pred(m), - m_query(nullptr), - m_search(m_params.pdr_bfs_model_search()), - m_last_result(l_undef), - m_inductive_lvl(0), - m_expanded_lvl(0) - { - } - - context::~context() { - reset_core_generalizers(); - reset(); - } - - void context::reset(decl2rel& rels) { - decl2rel::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - rels.reset(); - } - - void context::reset(bool full) { - TRACE("pdr", tout << "reset\n";); - reset(m_rels); - if (full) { - reset(m_rels_tmp); - } - m_search.reset(); - m_query = nullptr; - m_last_result = l_undef; - m_inductive_lvl = 0; - } - - void context::init_rules(datalog::rule_set& rules, decl2rel& rels) { - m_context = &rules.get_context(); - // Allocate collection of predicate transformers - datalog::rule_set::decl2rules::iterator dit = rules.begin_grouped_rules(), dend = rules.end_grouped_rules(); - decl2rel::obj_map_entry* e; - for (; dit != dend; ++dit) { - func_decl* pred = dit->m_key; - TRACE("pdr", tout << mk_pp(pred, m) << "\n";); - SASSERT(!rels.contains(pred)); - e = rels.insert_if_not_there2(pred, alloc(pred_transformer, *this, get_pdr_manager(), pred)); - datalog::rule_vector const& pred_rules = *dit->m_value; - for (unsigned i = 0; i < pred_rules.size(); ++i) { - e->get_data().m_value->add_rule(pred_rules[i]); - } - } - TRACE("pdr", tout << "adding rules\n";); - datalog::rule_set::iterator rit = rules.begin(), rend = rules.end(); - for (; rit != rend; ++rit) { - datalog::rule* r = *rit; - pred_transformer* pt; - unsigned utz = r->get_uninterpreted_tail_size(); - for (unsigned i = 0; i < utz; ++i) { - func_decl* pred = r->get_decl(i); - if (!rels.find(pred, pt)) { - pt = alloc(pred_transformer, *this, get_pdr_manager(), pred); - rels.insert(pred, pt); - } - } - } - // Initialize use list dependencies - TRACE("pdr", tout << "initialize use list dependencies\n";); - decl2rel::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - func_decl* pred = it->m_key; - pred_transformer* pt = it->m_value, *pt_user; - obj_hashtable const& deps = rules.get_dependencies().get_deps(pred); - obj_hashtable::iterator itf = deps.begin(), endf = deps.end(); - for (; itf != endf; ++itf) { - TRACE("pdr", tout << mk_pp(pred, m) << " " << mk_pp(*itf, m) << "\n";); - pt_user = rels.find(*itf); - pt_user->add_use(pt); - } - } - - TRACE("pdr", tout << "initialize predicate transformers\n";); - // Initialize the predicate transformers. - it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - SASSERT(it->m_value); - pred_transformer& rel = *it->m_value; - rel.initialize(rels); - TRACE("pdr", rel.display(tout); ); - } - } - - void context::update_rules(datalog::rule_set& rules) { - TRACE("pdr", tout << "update rules\n";); - reset(m_rels_tmp); - init_core_generalizers(rules); - init_rules(rules, m_rels_tmp); - decl2rel::iterator it = m_rels_tmp.begin(), end = m_rels_tmp.end(); - for (; it != end; ++it) { - pred_transformer* pt = nullptr; - if (m_rels.find(it->m_key, pt)) { - it->m_value->inherit_properties(*pt); - } - } - reset(false); - it = m_rels_tmp.begin(), end = m_rels_tmp.end(); - for (; it != end; ++it) { - m_rels.insert(it->m_key, it->m_value); - } - m_rels_tmp.reset(); - TRACE("pdr", tout << "done update rules\n";); - } - - unsigned context::get_num_levels(func_decl* p) { - pred_transformer* pt = nullptr; - if (m_rels.find(p, pt)) { - return pt->get_num_levels(); - } - else { - IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); - return 0; - } - } - - expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p) { - pred_transformer* pt = nullptr; - if (m_rels.find(p, pt)) { - return pt->get_cover_delta(p_orig, level); - } - else { - IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); - return expr_ref(m.mk_true(), m); - } - } - - void context::add_cover(int level, func_decl* p, expr* property) { - pred_transformer* pt = nullptr; - if (!m_rels.find(p, pt)) { - pt = alloc(pred_transformer, *this, get_pdr_manager(), p); - m_rels.insert(p, pt); - IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); - } - unsigned lvl = (level == -1)?infty_level:((unsigned)level); - pt->add_cover(lvl, property); - } - - class context::classifier_proc { - ast_manager& m; - arith_util a; - bool m_is_bool; - bool m_is_bool_arith; - bool m_has_arith; - bool m_is_dl; - bool m_is_utvpi; - public: - classifier_proc(ast_manager& m, datalog::rule_set& rules): - m(m), a(m), m_is_bool(true), m_is_bool_arith(true), m_has_arith(false), m_is_dl(false), m_is_utvpi(false) { - classify(rules); - } - void operator()(expr* e) { - if (m_is_bool) { - if (!m.is_bool(e)) { - m_is_bool = false; - } - else if (is_var(e)) { - // no-op. - } - else if (!is_app(e)) { - m_is_bool = false; - } - else if (to_app(e)->get_num_args() > 0 && - to_app(e)->get_family_id() != m.get_basic_family_id()) { - m_is_bool = false; - } - } - - m_has_arith = m_has_arith || a.is_int_real(e); - - if (m_is_bool_arith) { - if (!m.is_bool(e) && !a.is_int_real(e)) { - m_is_bool_arith = false; - } - else if (is_var(e)) { - // no-op - } - else if (!is_app(e)) { - m_is_bool_arith = false; - } - else if (to_app(e)->get_num_args() > 0 && - to_app(e)->get_family_id() != m.get_basic_family_id() && - to_app(e)->get_family_id() != a.get_family_id()) { - m_is_bool_arith = false; - } - } - } - - bool is_bool() const { return m_is_bool; } - - bool is_bool_arith() const { return m_is_bool_arith; } - - bool is_dl() const { return m_is_dl; } - - bool is_utvpi() const { return m_is_utvpi; } - - private: - - void classify(datalog::rule_set& rules) { - expr_fast_mark1 mark; - datalog::rule_set::iterator it = rules.begin(), end = rules.end(); - for (; it != end; ++it) { - datalog::rule& r = *(*it); - classify_pred(mark, r.get_head()); - unsigned utsz = r.get_uninterpreted_tail_size(); - for (unsigned i = 0; i < utsz; ++i) { - classify_pred(mark, r.get_tail(i)); - } - for (unsigned i = utsz; i < r.get_tail_size(); ++i) { - quick_for_each_expr(*this, mark, r.get_tail(i)); - } - } - mark.reset(); - - m_is_dl = false; - m_is_utvpi = false; - if (m_has_arith) { - ptr_vector forms; - for (it = rules.begin(); it != end; ++it) { - datalog::rule& r = *(*it); - unsigned utsz = r.get_uninterpreted_tail_size(); - forms.push_back(r.get_head()); - for (unsigned i = utsz; i < r.get_tail_size(); ++i) { - forms.push_back(r.get_tail(i)); - } - } - m_is_dl = is_difference_logic(m, forms.size(), forms.c_ptr()); - m_is_utvpi = m_is_dl || is_utvpi_logic(m, forms.size(), forms.c_ptr()); - } - } - - void classify_pred(expr_fast_mark1& mark, app* pred) { - for (unsigned i = 0; i < pred->get_num_args(); ++i) { - quick_for_each_expr(*this, mark, pred->get_arg(i)); - } - } - }; - - void context::validate_proof() { - std::stringstream msg; - proof_ref pr = get_proof(); - proof_checker checker(m); - expr_ref_vector side_conditions(m); - bool ok = checker.check(pr, side_conditions); - if (!ok) { - msg << "proof validation failed"; - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - for (unsigned i = 0; i < side_conditions.size(); ++i) { - expr* cond = side_conditions[i].get(); - expr_ref tmp(m); - - tmp = m.mk_not(cond); - IF_VERBOSE(2, verbose_stream() << "checking side-condition:\n" << mk_pp(cond, m) << "\n";); - smt::kernel solver(m, get_fparams()); - solver.assert_expr(tmp); - lbool res = solver.check(); - if (res != l_false) { - msg << "rule validation failed when checking: " << mk_pp(cond, m); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - } - } - - void context::validate_search() { - expr_ref tr = m_search.get_trace(*this); - TRACE("pdr", tout << tr << "\n";); - smt::kernel solver(m, get_fparams()); - solver.assert_expr(tr); - lbool res = solver.check(); - if (res != l_true) { - std::stringstream msg; - msg << "rule validation failed when checking: " << tr; - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - } - - void context::validate_model() { - IF_VERBOSE(1, verbose_stream() << "(pdr.validate_model)\n";); - std::stringstream msg; - expr_ref_vector refs(m); - expr_ref tmp(m); - model_ref model; - vector rs; - model_converter_ref mc; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, mc, rs); - ex.to_model(model); - var_subst vs(m, false); - expr_free_vars fv; - for (auto const& kv : m_rels) { - ptr_vector const& rules = kv.m_value->rules(); - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule& r = *rules[i]; - model->eval(r.get_head(), tmp); - expr_ref_vector fmls(m); - fmls.push_back(m.mk_not(tmp)); - unsigned utsz = r.get_uninterpreted_tail_size(); - unsigned tsz = r.get_tail_size(); - for (unsigned j = 0; j < utsz; ++j) { - model->eval(r.get_tail(j), tmp); - fmls.push_back(tmp); - } - for (unsigned j = utsz; j < tsz; ++j) { - fmls.push_back(r.get_tail(j)); - } - tmp = m.mk_and(fmls.size(), fmls.c_ptr()); - svector names; - fv(tmp); - fv.set_default_sort(m.mk_bool_sort()); - for (unsigned i = 0; i < fv.size(); ++i) { - names.push_back(symbol(i)); - } - fv.reverse(); - if (!fv.empty()) { - tmp = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), tmp); - } - smt::kernel solver(m, get_fparams()); - solver.assert_expr(tmp); - lbool res = solver.check(); - TRACE("pdr", tout << tmp << " " << res << "\n";); - if (res != l_false) { - msg << "rule validation failed when checking: " << mk_pp(tmp, m); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - } - } - } - - void context::validate() { - if (!m_params.pdr_validate_result()) { - return; - } - switch(m_last_result) { - case l_true: - if (m_params.generate_proof_trace()) { - validate_proof(); - } - validate_search(); - break; - case l_false: - validate_model(); - break; - default: - break; - } - } - - void context::reset_core_generalizers() { - std::for_each(m_core_generalizers.begin(), m_core_generalizers.end(), delete_proc()); - m_core_generalizers.reset(); - } - - void context::init_core_generalizers(datalog::rule_set& rules) { - reset_core_generalizers(); - classifier_proc classify(m, rules); - bool use_mc = m_params.pdr_use_multicore_generalizer(); - if (use_mc) { - m_core_generalizers.push_back(alloc(core_multi_generalizer, *this, 0)); - } - if (!classify.is_bool()) { - m.toggle_proof_mode(PGM_ENABLED); - m_fparams.m_arith_bound_prop = BP_NONE; - m_fparams.m_arith_auto_config_simplex = true; - m_fparams.m_arith_propagate_eqs = false; - m_fparams.m_arith_eager_eq_axioms = false; - if (m_params.pdr_utvpi() && - !m_params.pdr_use_convex_closure_generalizer() && - !m_params.pdr_use_convex_interior_generalizer()) { - if (classify.is_dl()) { - m_fparams.m_arith_mode = AS_DIFF_LOGIC; - m_fparams.m_arith_eq2ineq = true; - } - else if (classify.is_utvpi()) { - IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); - m_fparams.m_arith_mode = AS_UTVPI; - m_fparams.m_arith_eq2ineq = true; - } - else { - m_fparams.m_arith_mode = AS_ARITH; - m_fparams.m_arith_eq2ineq = false; - } - } - } - if (m_params.pdr_use_convex_closure_generalizer()) { - m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, true)); - } - if (m_params.pdr_use_convex_interior_generalizer()) { - m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, false)); - } - if (!use_mc && m_params.pdr_use_inductive_generalizer()) { - m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); - } - if (m_params.pdr_inductive_reachability_check()) { - m_core_generalizers.push_back(alloc(core_induction_generalizer, *this)); - } - if (m_params.pdr_use_arith_inductive_generalizer()) { - m_core_generalizers.push_back(alloc(core_arith_inductive_generalizer, *this)); - } - - } - - void context::get_level_property(unsigned lvl, expr_ref_vector& res, vector& rs) { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer* r = it->m_value; - if (r->head() == m_query_pred) { - continue; - } - expr_ref conj = r->get_formulas(lvl, false); - m_pm.formula_n2o(0, false, conj); - res.push_back(conj); - ptr_vector sig(r->head()->get_arity(), r->sig()); - rs.push_back(relation_info(m, r->head(), sig, conj)); - } - } - - void context::simplify_formulas() { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer* r = it->m_value; - r->simplify_formulas(); - } - } - - lbool context::solve() { - TRACE("pdr", tout << "solve\n";); - m_last_result = l_undef; - try { - solve_impl(); - UNREACHABLE(); - } - catch (model_exception) { - IF_VERBOSE(1, verbose_stream() << "\n"; m_search.display(verbose_stream());); - m_last_result = l_true; - validate(); - - IF_VERBOSE(1, - if (m_params.print_boogie_certificate()) { - display_certificate(verbose_stream()); - }); - - return l_true; - } - catch (inductive_exception) { - simplify_formulas(); - m_last_result = l_false; - TRACE("pdr", display_certificate(tout);); - IF_VERBOSE(1, { - expr_ref_vector refs(m); - vector rs; - get_level_property(m_inductive_lvl, refs, rs); - model_converter_ref mc; - inductive_property ex(m, mc, rs); - verbose_stream() << ex.to_string(); - }); - - // upgrade invariants that are known to be inductive. - decl2rel::iterator it = m_rels.begin (), end = m_rels.end (); - for (; m_inductive_lvl > 0 && it != end; ++it) { - if (it->m_value->head() != m_query_pred) { - it->m_value->propagate_to_infinity (m_inductive_lvl); - } - } - validate(); - return l_false; - } - catch (unknown_exception) { - return l_undef; - } - UNREACHABLE(); - return l_undef; - } - - void context::checkpoint() { - if (m.canceled()) { - throw default_exception(Z3_CANCELED_MSG); - } - } - - /** - \brief retrieve answer. - */ - expr_ref context::get_answer() { - switch(m_last_result) { - case l_true: return mk_sat_answer(); - case l_false: return mk_unsat_answer(); - default: return expr_ref(m.mk_true(), m); - } - } - - model_ref context::get_model() { - SASSERT(m_last_result == l_false); - expr_ref_vector refs(m); - vector rs; - model_ref md; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, m_mc, rs); - ex.to_model(md); - return md; - } - - proof_ref context::get_proof() const { - scoped_proof _sc(m); - proof_ref proof(m); - SASSERT(m_last_result == l_true); - proof = m_search.get_proof_trace(*this); - TRACE("pdr", tout << "PDR trace: " << proof << "\n";); - apply(m, m_pc.get(), proof); - TRACE("pdr", tout << "PDR trace: " << proof << "\n";); - // proof_utils::push_instantiations_up(proof); - // TRACE("pdr", tout << "PDR up: " << proof << "\n";); - return proof; - } - - - /** - \brief Retrieve satisfying assignment with explanation. - */ - expr_ref context::mk_sat_answer() const { - if (m_params.generate_proof_trace()) { - proof_ref pr = get_proof(); - return expr_ref(pr.get(), m); - } - return m_search.get_trace(*this); - } - - expr_ref context::mk_unsat_answer() { - expr_ref_vector refs(m); - vector rs; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, const_cast(m_mc), rs); - return ex.to_expr(); - } - - void context::solve_impl() { - if (!m_rels.find(m_query_pred, m_query)) { - throw inductive_exception(); - } - unsigned lvl = 0; - bool reachable; - while (true) { - checkpoint(); - m_expanded_lvl = lvl; - reachable = check_reachability(lvl); - if (reachable) { - throw model_exception(); - } - if (lvl != 0) { - propagate(lvl); - } - lvl++; - m_stats.m_max_depth = std::max(m_stats.m_max_depth, lvl); - IF_VERBOSE(1,verbose_stream() << "Entering level "<level() << "\n";); - checkpoint(); - expand_node(*node); - } - return root->is_closed(); - } - - void context::close_node(model_node& n) { - n.set_closed(); - model_node* p = n.parent(); - while (p && p->is_1closed()) { - p->set_closed(); - p = p->parent(); - } - } - - - void context::expand_node(model_node& n) { - SASSERT(n.is_open()); - expr_ref_vector cube(m); - - if (n.level() < m_expanded_lvl) { - m_expanded_lvl = n.level(); - } - - pred_transformer::scoped_farkas sf (n.pt(), m_params.pdr_farkas()); - if (n.pt().is_reachable(n.state())) { - TRACE("pdr", tout << "reachable\n";); - close_node(n); - } - else { - bool uses_level = true; - switch (expand_state(n, cube, uses_level)) { - case l_true: - if (n.level() == 0) { - TRACE("pdr", n.display(tout << "reachable at level 0\n", 0);); - close_node(n); - } - else { - TRACE("pdr", n.display(tout, 0);); - create_children(n); - } - break; - case l_false: { - core_generalizer::cores cores; - cores.push_back(std::make_pair(cube, uses_level)); - TRACE("pdr", tout << "cube:\n"; - for (unsigned j = 0; j < cube.size(); ++j) tout << mk_pp(cube[j].get(), m) << "\n";); - for (unsigned i = 0; !cores.empty() && i < m_core_generalizers.size(); ++i) { - core_generalizer::cores new_cores; - for (unsigned j = 0; j < cores.size(); ++j) { - (*m_core_generalizers[i])(n, cores[j].first, cores[j].second, new_cores); - } - cores.reset(); - cores.append(new_cores); - } - bool found_invariant = false; - for (unsigned i = 0; i < cores.size(); ++i) { - expr_ref_vector const& core = cores[i].first; - uses_level = cores[i].second; - found_invariant = !uses_level || found_invariant; - expr_ref ncore(m_pm.mk_not_and(core), m); - TRACE("pdr", tout << "invariant state: " << (uses_level?"":"(inductive) ") << mk_pp(ncore, m) << "\n";); - n.pt().add_property(ncore, uses_level?n.level():infty_level); - } - CASSERT("pdr",n.level() == 0 || check_invariant(n.level()-1)); - m_search.backtrack_level(!found_invariant && m_params.pdr_flexible_trace(), n); - break; - } - case l_undef: { - TRACE("pdr", tout << "unknown state: " << mk_pp(m_pm.mk_and(cube), m) << "\n";); - IF_VERBOSE(1, verbose_stream() << "unknown state\n";); - throw unknown_exception(); - } - } - } - } - - // - // check if predicate transformer has a satisfiable predecessor state. - // returns either a satisfiable predecessor state or - // return a property that blocks state and is implied by the - // predicate transformer (or some unfolding of it). - // - lbool context::expand_state(model_node& n, expr_ref_vector& result, bool& uses_level) { - TRACE("pdr", - tout << "expand_state: " << n.pt().head()->get_name(); - tout << " level: " << n.level() << "\n"; - tout << mk_pp(n.state(), m) << "\n";); - - return n.pt().is_reachable(n, &result, uses_level); - } - - void context::propagate(unsigned max_prop_lvl) { - if (m_params.pdr_simplify_formulas_pre()) { - simplify_formulas(); - } - for (unsigned lvl = m_expanded_lvl; lvl <= max_prop_lvl; lvl++) { - checkpoint(); - bool all_propagated = true; - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - checkpoint(); - pred_transformer& r = *it->m_value; - all_propagated = r.propagate_to_next_level(lvl) && all_propagated; - } - CASSERT("pdr", check_invariant(lvl)); - - if (all_propagated && lvl == max_prop_lvl) { - m_inductive_lvl = lvl; - throw inductive_exception(); - } - } - if (m_params.pdr_simplify_formulas_post()) { - simplify_formulas(); - } - } - - - /** - \brief create children states from model cube. - - Introduce the shorthands: - - - T(x0,x1,x) for transition - - phi(x) for n.state() - - M(x0,x1,x) for n.model() - - Assumptions: - M => phi & T - - In other words, - 1. phi & T is implied by M - - Goal is to find phi0(x0), phi1(x1) such that: - - phi(x) & phi0(x0) & phi1(x1) => T(x0, x1, x) - - Strategy: - - - Extract literals from T & phi using ternary simulation with M. - - resulting formula is Phi. - - - perform cheap existential quantifier elimination on - Phi <- exists x . Phi(x0,x1,x) - (e.g., destructive equality resolution) - - - Sub-strategy 1: rename remaining x to fresh variables. - - Sub-strategy 2: replace remaining x to M(x). - - - For each literal L in result: - - - if L is x0 pure, add L to L0 - - if L is x1 pure, add L to L1 - - if L mixes x0, x1, add x1 = M(x1) to L1, add L(x1 |-> M(x1)) to L0 - - - Create sub-goals for L0 and L1. - - */ - void context::create_children(model_node& n) { - SASSERT(n.level() > 0); - bool use_model_generalizer = m_params.pdr_use_model_generalizer(); - scoped_no_proof _sc(m); - - pred_transformer& pt = n.pt(); - model_ref M = n.get_model_ptr(); - SASSERT(M.get()); - datalog::rule const& r = pt.find_rule(*M); - expr* T = pt.get_transition(r); - expr* phi = n.state(); - - n.set_rule(&r); - - - TRACE("pdr", - tout << "Model:\n"; - model_smt2_pp(tout, m, *M, 0); - tout << "\n"; - tout << "Transition:\n" << mk_pp(T, m) << "\n"; - tout << "Phi:\n" << mk_pp(phi, m) << "\n";); - - model_implicant mev(m); - expr_ref_vector mdl(m), forms(m), Phi(m); - forms.push_back(T); - forms.push_back(phi); - flatten_and(forms); - ptr_vector forms1(forms.size(), forms.c_ptr()); - if (use_model_generalizer) { - Phi.append(mev.minimize_model(forms1, M)); - } - else { - Phi.append(mev.minimize_literals(forms1, M)); - } - ptr_vector preds; - pt.find_predecessors(r, preds); - pt.remove_predecessors(Phi); - - app_ref_vector vars(m); - unsigned sig_size = pt.head()->get_arity(); - for (unsigned i = 0; i < sig_size; ++i) { - vars.push_back(m.mk_const(m_pm.o2n(pt.sig(i), 0))); - } - ptr_vector& aux_vars = pt.get_aux_vars(r); - vars.append(aux_vars.size(), aux_vars.c_ptr()); - - scoped_ptr rep; - qe_lite qe(m, m_params.p); - expr_ref phi1 = m_pm.mk_and(Phi); - qe(vars, phi1); - TRACE("pdr", tout << "Eliminated\n" << mk_pp(phi1, m) << "\n";); - if (!use_model_generalizer) { - reduce_disequalities(*M, 3, phi1); - TRACE("pdr", tout << "Reduced-eq\n" << mk_pp(phi1, m) << "\n";); - } - get_context().get_rewriter()(phi1); - - TRACE("pdr", - tout << "Vars:\n"; - for (unsigned i = 0; i < vars.size(); ++i) { - tout << mk_pp(vars[i].get(), m) << "\n"; - } - tout << "Literals\n"; - tout << mk_pp(m_pm.mk_and(Phi), m) << "\n"; - tout << "Reduced\n" << mk_pp(phi1, m) << "\n";); - - if (!vars.empty()) { - // also fresh names for auxiliary variables in body? - expr_substitution sub(m); - expr_ref tmp(m); - proof_ref pr(m); - pr = m.mk_asserted(m.mk_true()); - for (unsigned i = 0; i < vars.size(); ++i) { - tmp = mev.eval(M, vars[i].get()); - sub.insert(vars[i].get(), tmp, pr); - } - if (!rep) rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - (*rep)(phi1); - TRACE("pdr", tout << "Projected:\n" << mk_pp(phi1, m) << "\n";); - } - Phi.reset(); - flatten_and(phi1, Phi); - unsigned_vector indices; - vector child_states; - child_states.resize(preds.size(), expr_ref_vector(m)); - for (unsigned i = 0; i < Phi.size(); ++i) { - m_pm.collect_indices(Phi[i].get(), indices); - if (indices.size() == 0) { - IF_VERBOSE(3, verbose_stream() << "Skipping " << mk_pp(Phi[i].get(), m) << "\n";); - } - else if (indices.size() == 1) { - child_states[indices.back()].push_back(Phi[i].get()); - } - else { - expr_substitution sub(m); - expr_ref tmp(m); - proof_ref pr(m); - pr = m.mk_asserted(m.mk_true()); - vector > vars; - m_pm.collect_variables(Phi[i].get(), vars); - SASSERT(vars.size() == indices.back()+1); - for (unsigned j = 1; j < indices.size(); ++j) { - ptr_vector const& vs = vars[indices[j]]; - for (unsigned k = 0; k < vs.size(); ++k) { - tmp = mev.eval(M, vs[k]); - sub.insert(vs[k], tmp, pr); - child_states[indices[j]].push_back(m.mk_eq(vs[k], tmp)); - } - } - tmp = Phi[i].get(); - if (!rep) rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - (*rep)(tmp); - child_states[indices[0]].push_back(tmp); - } - - } - - expr_ref n_cube(m); - for (unsigned i = 0; i < preds.size(); ++i) { - pred_transformer& pt = *m_rels.find(preds[i]); - SASSERT(pt.head() == preds[i]); - expr_ref o_cube = m_pm.mk_and(child_states[i]); - m_pm.formula_o2n(o_cube, n_cube, i); - model_node* child = alloc(model_node, &n, n_cube, pt, n.level()-1); - ++m_stats.m_num_nodes; - m_search.add_leaf(*child); - IF_VERBOSE(2, verbose_stream() << "Predecessor: " << mk_pp(n_cube, m) << " " << (child->is_closed()?"closed":"open") << "\n";); - m_stats.m_max_depth = std::max(m_stats.m_max_depth, child->depth()); - } - n.check_pre_closed(); - TRACE("pdr", m_search.display(tout);); - } - - void context::collect_statistics(statistics& st) const { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (it = m_rels.begin(); it != end; ++it) { - it->m_value->collect_statistics(st); - } - st.update("PDR num unfoldings", m_stats.m_num_nodes); - st.update("PDR max depth", m_stats.m_max_depth); - st.update("PDR inductive level", m_inductive_lvl); - m_pm.collect_statistics(st); - - for (unsigned i = 0; i < m_core_generalizers.size(); ++i) { - m_core_generalizers[i]->collect_statistics(st); - } - } - - void context::reset_statistics() { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (it = m_rels.begin(); it != end; ++it) { - it->m_value->reset_statistics(); - } - m_stats.reset(); - m_pm.reset_statistics(); - - for (unsigned i = 0; i < m_core_generalizers.size(); ++i) { - m_core_generalizers[i]->reset_statistics(); - } - - } - - - std::ostream& context::display(std::ostream& out) const { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - it->m_value->display(out); - } - m_search.display(out); - return out; - } - - bool context::check_invariant(unsigned lvl) { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - checkpoint(); - if (!check_invariant(lvl, it->m_key)) { - return false; - } - } - return true; - } - - bool context::check_invariant(unsigned lvl, func_decl* fn) { - smt::kernel ctx(m, m_fparams); - pred_transformer& pt = *m_rels.find(fn); - expr_ref_vector conj(m); - expr_ref inv = pt.get_formulas(next_level(lvl), false); - if (m.is_true(inv)) return true; - pt.add_premises(m_rels, lvl, conj); - conj.push_back(m.mk_not(inv)); - expr_ref fml(m.mk_and(conj.size(), conj.c_ptr()), m); - ctx.assert_expr(fml); - lbool result = ctx.check(); - TRACE("pdr", tout << "Check invariant level: " << lvl << " " << result << "\n" << mk_pp(fml, m) << "\n";); - return result == l_false; - } - - void context::display_certificate(std::ostream& strm) { - switch(m_last_result) { - case l_false: { - expr_ref_vector refs(m); - vector rs; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, const_cast(m_mc), rs); - strm << ex.to_string(); - break; - } - case l_true: { - if (m_params.print_boogie_certificate()) { - datalog::boogie_proof bp(m); - bp.set_proof(get_proof()); - bp.set_model(nullptr); - bp.pp(strm); - } - else { - strm << mk_pp(mk_sat_answer(), m); - } - break; - } - case l_undef: { - strm << "unknown"; - break; - } - } - } - -} diff --git a/src/muz/pdr/pdr_context.h b/src/muz/pdr/pdr_context.h deleted file mode 100644 index ebaa3f257..000000000 --- a/src/muz/pdr/pdr_context.h +++ /dev/null @@ -1,448 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_context.h - -Abstract: - - PDR for datalog - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-20. - -Revision History: - ---*/ - -#ifndef PDR_CONTEXT_H_ -#define PDR_CONTEXT_H_ - -#ifdef _CYGWIN -#undef min -#undef max -#endif -#include -#include "muz/pdr/pdr_manager.h" -#include "muz/pdr/pdr_prop_solver.h" -#include "muz/pdr/pdr_reachable_cache.h" -#include "muz/base/fixedpoint_params.hpp" - - -namespace datalog { - class rule_set; - class context; -}; - -namespace pdr { - - class pred_transformer; - class model_node; - class context; - - typedef obj_map rule2inst; - typedef obj_map decl2rel; - - - // - // Predicate transformer state. - // A predicate transformer corresponds to the - // set of rules that have the same head predicates. - // - - class pred_transformer { - - struct stats { - unsigned m_num_propagations; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - typedef obj_map rule2expr; - typedef obj_map > rule2apps; - - manager& pm; // pdr-manager - ast_manager& m; // manager - context& ctx; - - func_decl_ref m_head; // predicate - func_decl_ref_vector m_sig; // signature - ptr_vector m_use; // places where 'this' is referenced. - ptr_vector m_rules; // rules used to derive transformer - prop_solver m_solver; // solver context - vector m_levels; // level formulas - expr_ref_vector m_invariants; // properties that are invariant. - obj_map m_prop2level; // map property to level where it occurs. - obj_map m_tag2rule; // map tag predicate to rule. - rule2expr m_rule2tag; // map rule to predicate tag. - rule2inst m_rule2inst; // map rules to instantiations of indices - rule2expr m_rule2transition; // map rules to transition - rule2apps m_rule2vars; // map rule to auxiliary variables - expr_ref m_transition; // transition relation. - expr_ref m_initial_state; // initial state. - reachable_cache m_reachable; - ptr_vector m_predicates; - stats m_stats; - - void init_sig(); - void ensure_level(unsigned level); - bool add_property1(expr * lemma, unsigned lvl); // add property 'p' to state at level lvl. - void add_child_property(pred_transformer& child, expr* lemma, unsigned lvl); - void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result); - - // Initialization - void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition); - void init_rule(decl2rel const& pts, datalog::rule const& rule, expr_ref& init, - ptr_vector& rules, expr_ref_vector& transition); - void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx); - - void simplify_formulas(tactic& tac, expr_ref_vector& fmls); - - // Debugging - bool check_filled(app_ref_vector const& v) const; - - void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); - - public: - pred_transformer(context& ctx, manager& pm, func_decl* head); - ~pred_transformer(); - - void add_rule(datalog::rule* r) { m_rules.push_back(r); } - void add_use(pred_transformer* pt) { if (!m_use.contains(pt)) m_use.insert(pt); } - void initialize(decl2rel const& pts); - - func_decl* head() const { return m_head; } - ptr_vector const& rules() const { return m_rules; } - func_decl* sig(unsigned i) { init_sig(); return m_sig[i].get(); } // signature - func_decl* const* sig() { init_sig(); return m_sig.c_ptr(); } - unsigned sig_size() { init_sig(); return m_sig.size(); } - expr* transition() const { return m_transition; } - expr* initial_state() const { return m_initial_state; } - expr* rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); } - unsigned get_num_levels() { return m_levels.size(); } - expr_ref get_cover_delta(func_decl* p_orig, int level); - void add_cover(unsigned level, expr* property); - context& get_context() { return ctx; } - - std::ostream& display(std::ostream& strm) const; - - void collect_statistics(statistics& st) const; - void reset_statistics(); - - bool is_reachable(expr* state); - void remove_predecessors(expr_ref_vector& literals); - void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; - datalog::rule const& find_rule(model_core const& model) const; - expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); } - ptr_vector& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); } - - bool propagate_to_next_level(unsigned level); - void propagate_to_infinity(unsigned level); - void add_property(expr * lemma, unsigned lvl); // add property 'p' to state at level. - - lbool is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level); - bool is_invariant(unsigned level, expr* co_state, bool inductive, bool& assumes_level, expr_ref_vector* core = nullptr); - bool check_inductive(unsigned level, expr_ref_vector& state, bool& assumes_level); - - expr_ref get_formulas(unsigned level, bool add_axioms); - - void simplify_formulas(); - - expr_ref get_propagation_formula(decl2rel const& pts, unsigned level); - - manager& get_pdr_manager() const { return pm; } - ast_manager& get_manager() const { return m; } - - void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r); - - void close(expr* e); - - app_ref_vector& get_inst(datalog::rule const* r) { return *m_rule2inst.find(r);} - - void inherit_properties(pred_transformer& other); - - void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars); - - prop_solver& get_solver() { return m_solver; } - prop_solver const& get_solver() const { return m_solver; } - - void set_use_farkas(bool f) { get_solver().set_use_farkas(f); } - bool get_use_farkas() const { return get_solver().get_use_farkas(); } - class scoped_farkas { - bool m_old; - pred_transformer& m_p; - public: - scoped_farkas(pred_transformer& p, bool v): m_old(p.get_use_farkas()), m_p(p) { - p.set_use_farkas(v); - } - ~scoped_farkas() { m_p.set_use_farkas(m_old); } - }; - - }; - - - // structure for counter-example search. - class model_node { - model_node* m_parent; - model_node* m_next; - model_node* m_prev; - pred_transformer& m_pt; - expr_ref m_state; - model_ref m_model; - ptr_vector m_children; - unsigned m_level; - unsigned m_orig_level; - unsigned m_depth; - bool m_closed; - datalog::rule const* m_rule; - public: - model_node(model_node* parent, expr_ref& state, pred_transformer& pt, unsigned level): - m_parent(parent), m_next(nullptr), m_prev(nullptr), m_pt(pt), m_state(state), m_model(nullptr), - m_level(level), m_orig_level(level), m_depth(0), m_closed(false), m_rule(nullptr) { - model_node* p = m_parent; - if (p) { - p->m_children.push_back(this); - SASSERT(p->m_level == level+1); - SASSERT(p->m_level > 0); - m_depth = p->m_depth+1; - if (p && p->is_closed()) { - p->set_open(); - } - } - } - void set_model(model_ref& m) { m_model = m; } - unsigned level() const { return m_level; } - unsigned orig_level() const { return m_orig_level; } - unsigned depth() const { return m_depth; } - void increase_level() { ++m_level; } - expr_ref const& state() const { return m_state; } - ptr_vector const& children() { return m_children; } - pred_transformer& pt() const { return m_pt; } - model_node* parent() const { return m_parent; } - model* get_model_ptr() const { return m_model.get(); } - model const& get_model() const { return *m_model; } - unsigned index() const; - - bool is_closed() const { return m_closed; } - bool is_open() const { return !is_closed(); } - - bool is_1closed() { - if (is_closed()) return true; - if (m_children.empty()) return false; - for (unsigned i = 0; i < m_children.size(); ++i) { - if (m_children[i]->is_open()) return false; - } - return true; - } - - void check_pre_closed(); - void set_closed(); - void set_open(); - void set_pre_closed() { TRACE("pdr", tout << state() << "\n";); m_closed = true; } - void reset() { m_children.reset(); } - - void set_rule(datalog::rule const* r) { m_rule = r; } - datalog::rule* get_rule(); - - void mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding); - - std::ostream& display(std::ostream& out, unsigned indent); - - void dequeue(model_node*& root); - void enqueue(model_node* n); - model_node* next() const { return m_next; } - bool is_goal() const { return nullptr != next(); } - }; - - class model_search { - typedef ptr_vector model_nodes; - bool m_bfs; - model_node* m_root; - model_node* m_goal; - vector > m_cache; - obj_map& cache(model_node const& n); - void erase_children(model_node& n, bool backtrack); - void remove_node(model_node& n, bool backtrack); - void enqueue_leaf(model_node* n); // add leaf to priority queue. - void update_models(); - void set_leaf(model_node& n); // Set node as leaf, remove children. - unsigned num_goals() const; - - public: - model_search(bool bfs): m_bfs(bfs), m_root(nullptr), m_goal(nullptr) {} - ~model_search(); - - void reset(); - model_node* next(); - void add_leaf(model_node& n); // add fresh node. - - void set_root(model_node* n); - model_node& get_root() const { return *m_root; } - std::ostream& display(std::ostream& out) const; - expr_ref get_trace(context const& ctx); - proof_ref get_proof_trace(context const& ctx); - void backtrack_level(bool uses_level, model_node& n); - void remove_goal(model_node& n); - - void well_formed(); - }; - - struct model_exception { }; - struct inductive_exception {}; - - - // 'state' is unsatisfiable at 'level' with 'core'. - // Minimize or weaken core. - class core_generalizer { - protected: - context& m_ctx; - public: - typedef vector > cores; - core_generalizer(context& ctx): m_ctx(ctx) {} - virtual ~core_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) = 0; - virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - new_cores.push_back(std::make_pair(core, uses_level)); - if (!core.empty()) { - (*this)(n, new_cores.back().first, new_cores.back().second); - } - } - virtual void collect_statistics(statistics& st) const {} - virtual void reset_statistics() {} - }; - - class context { - - struct stats { - unsigned m_num_nodes; - unsigned m_max_depth; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - smt_params& m_fparams; - fixedpoint_params const& m_params; - ast_manager& m; - datalog::context* m_context; - manager m_pm; - decl2rel m_rels; // Map from relation predicate to fp-operator. - decl2rel m_rels_tmp; - func_decl_ref m_query_pred; - pred_transformer* m_query; - mutable model_search m_search; - lbool m_last_result; - unsigned m_inductive_lvl; - unsigned m_expanded_lvl; - ptr_vector m_core_generalizers; - stats m_stats; - model_converter_ref m_mc; - proof_converter_ref m_pc; - - // Functions used by search. - void solve_impl(); - bool check_reachability(unsigned level); - void propagate(unsigned max_prop_lvl); - void close_node(model_node& n); - void check_pre_closed(model_node& n); - void expand_node(model_node& n); - lbool expand_state(model_node& n, expr_ref_vector& cube, bool& uses_level); - void create_children(model_node& n); - expr_ref mk_sat_answer() const; - expr_ref mk_unsat_answer(); - - // Generate inductive property - void get_level_property(unsigned lvl, expr_ref_vector& res, vector & rs); - - - // Initialization - class classifier_proc; - void init_core_generalizers(datalog::rule_set& rules); - - bool check_invariant(unsigned lvl); - bool check_invariant(unsigned lvl, func_decl* fn); - - void checkpoint(); - - void init_rules(datalog::rule_set& rules, decl2rel& transformers); - - void simplify_formulas(); - - void reset_core_generalizers(); - - void reset(decl2rel& rels); - - void validate(); - void validate_proof(); - void validate_search(); - void validate_model(); - - public: - - /** - Initial values of predicates are stored in corresponding relations in dctx. - - We check whether there is some reachable state of the relation checked_relation. - */ - context( - smt_params& fparams, - fixedpoint_params const& params, - ast_manager& m); - - ~context(); - - smt_params& get_fparams() const { return m_fparams; } - fixedpoint_params const& get_params() const { return m_params; } - ast_manager& get_manager() const { return m; } - manager& get_pdr_manager() { return m_pm; } - decl2rel const& get_pred_transformers() const { return m_rels; } - pred_transformer& get_pred_transformer(func_decl* p) const { return *m_rels.find(p); } - datalog::context& get_context() const { SASSERT(m_context); return *m_context; } - expr_ref get_answer(); - - bool is_dl() const { return m_fparams.m_arith_mode == AS_DIFF_LOGIC; } - bool is_utvpi() const { return m_fparams.m_arith_mode == AS_UTVPI; } - - void collect_statistics(statistics& st) const; - void reset_statistics(); - - std::ostream& display(std::ostream& strm) const; - - void display_certificate(std::ostream& strm); - - lbool solve(); - - void reset(bool full = true); - - void set_query(func_decl* q) { m_query_pred = q; } - - void set_unsat() { m_last_result = l_false; } - - void set_model_converter(model_converter_ref& mc) { m_mc = mc; } - - model_converter_ref get_model_converter() { return m_mc; } - - void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; } - - void update_rules(datalog::rule_set& rules); - - void set_axioms(expr* axioms) { m_pm.set_background(axioms); } - - unsigned get_num_levels(func_decl* p); - - expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p); - - void add_cover(int level, func_decl* pred, expr* property); - - model_ref get_model(); - - proof_ref get_proof() const; - - model_node& get_root() const { return m_search.get_root(); } - - }; - -}; - -#endif diff --git a/src/muz/pdr/pdr_dl_interface.cpp b/src/muz/pdr/pdr_dl_interface.cpp deleted file mode 100644 index 27ce0500c..000000000 --- a/src/muz/pdr/pdr_dl_interface.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_dl.cpp - -Abstract: - - SMT2 interface for the datalog PDR - -Author: - - Krystof Hoder (t-khoder) 2011-9-22. - -Revision History: - ---*/ - -#include "muz/base/dl_context.h" -#include "muz/transforms/dl_mk_coi_filter.h" -#include "muz/base/dl_rule.h" -#include "muz/base/dl_rule_transformer.h" -#include "muz/pdr/pdr_context.h" -#include "muz/pdr/pdr_dl_interface.h" -#include "muz/base/dl_rule_set.h" -#include "muz/transforms/dl_mk_slice.h" -#include "muz/transforms/dl_mk_unfold.h" -#include "muz/transforms/dl_mk_coalesce.h" -#include "muz/transforms/dl_transforms.h" -#include "ast/scoped_proof.h" -#include "model/model_smt2_pp.h" - -using namespace pdr; - -dl_interface::dl_interface(datalog::context& ctx) : - engine_base(ctx.get_manager(), "pdr"), - m_ctx(ctx), - m_pdr_rules(ctx), - m_old_rules(ctx), - m_context(nullptr), - m_refs(ctx.get_manager()) { - m_context = alloc(pdr::context, ctx.get_fparams(), ctx.get_params(), ctx.get_manager()); -} - - -dl_interface::~dl_interface() { - dealloc(m_context); -} - - -// -// Check if the new rules are weaker so that we can -// re-use existing context. -// -void dl_interface::check_reset() { - datalog::rule_set const& new_rules = m_ctx.get_rules(); - datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); - bool is_subsumed = !old_rules.empty(); - for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) { - is_subsumed = false; - for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { - if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) { - is_subsumed = true; - } - } - if (!is_subsumed) { - TRACE("pdr", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule ");); - m_context->reset(); - } - } - m_old_rules.replace_rules(new_rules); -} - - -lbool dl_interface::query(expr * query) { - //we restore the initial state in the datalog context - m_ctx.ensure_opened(); - m_refs.reset(); - m_pred2slice.reset(); - ast_manager& m = m_ctx.get_manager(); - datalog::rule_manager& rm = m_ctx.get_rule_manager(); - datalog::rule_set& rules0 = m_ctx.get_rules(); - - datalog::rule_set old_rules(rules0); - func_decl_ref query_pred(m); - rm.mk_query(query, rules0); - expr_ref bg_assertion = m_ctx.get_background_assertion(); - - check_reset(); - - TRACE("pdr", - if (!m.is_true(bg_assertion)) { - tout << "axioms:\n"; - tout << mk_pp(bg_assertion, m) << "\n"; - } - tout << "query: " << mk_pp(query, m) << "\n"; - tout << "rules:\n"; - m_ctx.display_rules(tout); - ); - - - apply_default_transformation(m_ctx); - - if (m_ctx.get_params().xform_slice()) { - datalog::rule_transformer transformer(m_ctx); - datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); - transformer.register_plugin(slice); - m_ctx.transform_rules(transformer); - - // track sliced predicates. - obj_map const& preds = slice->get_predicates(); - obj_map::iterator it = preds.begin(); - obj_map::iterator end = preds.end(); - for (; it != end; ++it) { - m_pred2slice.insert(it->m_key, it->m_value); - m_refs.push_back(it->m_key); - m_refs.push_back(it->m_value); - } - } - - if (m_ctx.get_params().xform_unfold_rules() > 0) { - unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules(); - datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); - transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); - transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); - if (m_ctx.get_params().xform_coalesce_rules()) { - m_ctx.transform_rules(transf1); - } - while (num_unfolds > 0) { - m_ctx.transform_rules(transf2); - --num_unfolds; - } - } - - const datalog::rule_set& rules = m_ctx.get_rules(); - if (rules.get_output_predicates().empty()) { - m_context->set_unsat(); - return l_false; - } - - query_pred = rules.get_output_predicate(); - - TRACE("pdr", - tout << "rules:\n"; - m_ctx.display_rules(tout); - m_ctx.display_smt2(0, 0, tout); - ); - - IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); - m_pdr_rules.replace_rules(rules); - m_pdr_rules.close(); - m_ctx.record_transformed_rules(); - m_ctx.reopen(); - m_ctx.replace_rules(old_rules); - - - scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. - - m_context->set_proof_converter(m_ctx.get_proof_converter()); - m_context->set_model_converter(m_ctx.get_model_converter()); - m_context->set_query(query_pred); - m_context->set_axioms(bg_assertion); - m_context->update_rules(m_pdr_rules); - - if (m_pdr_rules.get_rules().empty()) { - m_context->set_unsat(); - IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(),0);); - return l_false; - } - - return m_context->solve(); - -} - -expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig) { - func_decl* pred = pred_orig; - m_pred2slice.find(pred_orig, pred); - SASSERT(pred); - return m_context->get_cover_delta(level, pred_orig, pred); -} - -void dl_interface::add_cover(int level, func_decl* pred, expr* property) { - if (m_ctx.get_params().xform_slice()) { - throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers"); - } - m_context->add_cover(level, pred, property); -} - -unsigned dl_interface::get_num_levels(func_decl* pred) { - m_pred2slice.find(pred, pred); - SASSERT(pred); - return m_context->get_num_levels(pred); -} - -void dl_interface::collect_statistics(statistics& st) const { - m_context->collect_statistics(st); -} - -void dl_interface::reset_statistics() { - m_context->reset_statistics(); -} - -void dl_interface::display_certificate(std::ostream& out) const { - m_context->display_certificate(out); -} - -expr_ref dl_interface::get_answer() { - return m_context->get_answer(); -} - - - -void dl_interface::updt_params() { - dealloc(m_context); - m_context = alloc(pdr::context, m_ctx.get_fparams(), m_ctx.get_params(), m_ctx.get_manager()); -} - -model_ref dl_interface::get_model() { - return m_context->get_model(); -} - -proof_ref dl_interface::get_proof() { - return m_context->get_proof(); -} diff --git a/src/muz/pdr/pdr_dl_interface.h b/src/muz/pdr/pdr_dl_interface.h deleted file mode 100644 index 1a0b04635..000000000 --- a/src/muz/pdr/pdr_dl_interface.h +++ /dev/null @@ -1,78 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_dl_interface.h - -Abstract: - - SMT2 interface for the datalog PDR - -Author: - - Krystof Hoder (t-khoder) 2011-9-22. - -Revision History: - ---*/ - -#ifndef PDR_DL_INTERFACE_H_ -#define PDR_DL_INTERFACE_H_ - -#include "util/lbool.h" -#include "muz/base/dl_rule.h" -#include "muz/base/dl_rule_set.h" -#include "muz/base/dl_util.h" -#include "muz/base/dl_engine_base.h" -#include "util/statistics.h" - -namespace datalog { - class context; -} - -namespace pdr { - - class context; - - class dl_interface : public datalog::engine_base { - datalog::context& m_ctx; - datalog::rule_set m_pdr_rules; - datalog::rule_set m_old_rules; - context* m_context; - obj_map m_pred2slice; - ast_ref_vector m_refs; - - void check_reset(); - - public: - dl_interface(datalog::context& ctx); - ~dl_interface() override; - - lbool query(expr* query) override; - - void display_certificate(std::ostream& out) const override; - - void collect_statistics(statistics& st) const override; - - void reset_statistics() override; - - expr_ref get_answer() override; - - unsigned get_num_levels(func_decl* pred) override; - - expr_ref get_cover_delta(int level, func_decl* pred) override; - - void add_cover(int level, func_decl* pred, expr* property) override; - - void updt_params() override; - - model_ref get_model() override; - - proof_ref get_proof() override; - - }; -} - - -#endif diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp deleted file mode 100644 index 6695788c2..000000000 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ /dev/null @@ -1,1012 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_farkas_learner.cpp - -Abstract: - - Proviced abstract interface and some inplementations of algorithms - for strenghtning lemmas - -Author: - - Krystof Hoder (t-khoder) 2011-11-1. - -Revision History: - ---*/ - -#include "ast/ast_smt2_pp.h" -#include "ast/array_decl_plugin.h" -#include "ast/rewriter/bool_rewriter.h" -#include "ast/dl_decl_plugin.h" -#include "ast/for_each_expr.h" -#include "muz/base/dl_util.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "muz/pdr/pdr_util.h" -#include "muz/pdr/pdr_farkas_learner.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/ast_ll_pp.h" -#include "tactic/arith/arith_bounds_tactic.h" -#include "ast/proofs/proof_utils.h" -#include "ast/reg_decl_plugins.h" - - -namespace pdr { - - class farkas_learner::constr { - - ast_manager& m; - arith_util a; - app_ref_vector m_ineqs; - vector m_coeffs; - - unsigned m_time; - unsigned_vector m_roots, m_size, m_his, m_reps, m_ts; - - void mk_coerce(expr*& e1, expr*& e2) { - if (a.is_int(e1) && a.is_real(e2)) { - e1 = a.mk_to_real(e1); - } - else if (a.is_int(e2) && a.is_real(e1)) { - e2 = a.mk_to_real(e2); - } - } - - app* mk_add(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_add(e1, e2); - } - - app* mk_mul(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_mul(e1, e2); - } - - app* mk_le(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_le(e1, e2); - } - - app* mk_ge(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_ge(e1, e2); - } - - app* mk_gt(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_gt(e1, e2); - } - - app* mk_lt(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_lt(e1, e2); - } - - void mul(rational const& c, expr* e, expr_ref& res) { - expr_ref tmp(m); - if (c.is_one()) { - tmp = e; - } - else { - tmp = mk_mul(a.mk_numeral(c, c.is_int() && a.is_int(e)), e); - } - res = mk_add(res, tmp); - } - - bool is_int_sort(app* c) { - SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); - SASSERT(a.is_int(c->get_arg(0)) || a.is_real(c->get_arg(0))); - return a.is_int(c->get_arg(0)); - } - - bool is_int_sort() { - SASSERT(!m_ineqs.empty()); - return is_int_sort(m_ineqs[0].get()); - } - - void normalize_coeffs() { - rational l(1); - for (unsigned i = 0; i < m_coeffs.size(); ++i) { - l = lcm(l, denominator(m_coeffs[i])); - } - if (!l.is_one()) { - for (unsigned i = 0; i < m_coeffs.size(); ++i) { - m_coeffs[i] *= l; - } - } - } - - app* mk_one() { - return a.mk_numeral(rational(1), true); - } - - app* fix_sign(bool is_pos, app* c) { - expr* x, *y; - SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); - bool is_int = is_int_sort(c); - if (is_int && is_pos && (a.is_lt(c, x, y) || a.is_gt(c, y, x))) { - return mk_le(mk_add(x, mk_one()), y); - } - if (is_int && !is_pos && (a.is_le(c, x, y) || a.is_ge(c, y, x))) { - // !(x <= y) <=> x > y <=> x >= y + 1 - return mk_ge(x, mk_add(y, mk_one())); - } - if (is_pos) { - return c; - } - if (a.is_le(c, x, y)) return mk_gt(x, y); - if (a.is_lt(c, x, y)) return mk_ge(x, y); - if (a.is_ge(c, x, y)) return mk_lt(x, y); - if (a.is_gt(c, x, y)) return mk_le(x, y); - UNREACHABLE(); - return c; - } - - public: - constr(ast_manager& m) : m(m), a(m), m_ineqs(m), m_time(0) {} - - void reset() { - m_ineqs.reset(); - m_coeffs.reset(); - } - - /** add a multiple of constraint c to the current constr */ - void add(rational const & coef, app * c) { - bool is_pos = true; - expr* e; - while (m.is_not(c, e)) { - is_pos = !is_pos; - c = to_app(e); - } - - if (!coef.is_zero() && !m.is_true(c)) { - m_coeffs.push_back(coef); - m_ineqs.push_back(fix_sign(is_pos, c)); - } - } - - // - // Extract the complement of premises multiplied by Farkas coefficients. - // - void get(expr_ref& res) { - if (m_coeffs.empty()) { - res = m.mk_false(); - return; - } - bool is_int = is_int_sort(); - if (is_int) { - normalize_coeffs(); - } - TRACE("pdr", - for (unsigned i = 0; i < m_coeffs.size(); ++i) { - tout << m_coeffs[i] << ": " << mk_pp(m_ineqs[i].get(), m) << "\n"; - } - ); - - res = extract_consequence(0, m_coeffs.size()); - -#if 1 - // partition equalities into variable disjoint sets. - // take the conjunction of these instead of the - // linear combination. - partition_ineqs(); - expr_ref_vector lits(m); - unsigned lo = 0; - for (unsigned i = 0; i < m_his.size(); ++i) { - unsigned hi = m_his[i]; - lits.push_back(extract_consequence(lo, hi)); - lo = hi; - } - res = mk_or(lits); - IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } }); -#endif - } - - private: - - // partition inequalities into variable disjoint sets. - void partition_ineqs() { - m_reps.reset(); - m_his.reset(); - ++m_time; - for (unsigned i = 0; i < m_ineqs.size(); ++i) { - m_reps.push_back(process_term(m_ineqs[i].get())); - } - unsigned head = 0; - while (head < m_ineqs.size()) { - unsigned r = find(m_reps[head]); - unsigned tail = head; - for (unsigned i = head+1; i < m_ineqs.size(); ++i) { - if (find(m_reps[i]) == r) { - ++tail; - if (tail != i) { - SASSERT(tail < i); - std::swap(m_reps[tail], m_reps[i]); - app_ref tmp(m); - tmp = m_ineqs[i].get(); - m_ineqs[i] = m_ineqs[tail].get(); - m_ineqs[tail] = tmp; - std::swap(m_coeffs[tail], m_coeffs[i]); - } - } - } - head = tail + 1; - m_his.push_back(head); - } - } - - unsigned find(unsigned idx) { - if (m_ts.size() <= idx) { - m_roots.resize(idx+1); - m_size.resize(idx+1); - m_ts.resize(idx+1); - m_roots[idx] = idx; - m_ts[idx] = m_time; - m_size[idx] = 1; - return idx; - } - if (m_ts[idx] != m_time) { - m_size[idx] = 1; - m_ts[idx] = m_time; - m_roots[idx] = idx; - return idx; - } - while (true) { - if (m_roots[idx] == idx) { - return idx; - } - idx = m_roots[idx]; - } - } - - void merge(unsigned i, unsigned j) { - i = find(i); - j = find(j); - if (i == j) { - return; - } - if (m_size[i] > m_size[j]) { - std::swap(i, j); - } - m_roots[i] = j; - m_size[j] += m_size[i]; - } - - unsigned process_term(expr* e) { - unsigned r = e->get_id(); - ptr_vector todo; - ast_mark mark; - todo.push_back(e); - while (!todo.empty()) { - e = todo.back(); - todo.pop_back(); - if (mark.is_marked(e)) { - continue; - } - mark.mark(e, true); - if (is_uninterp(e)) { - merge(r, e->get_id()); - } - if (is_app(e)) { - app* a = to_app(e); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - todo.push_back(a->get_arg(i)); - } - } - } - return r; - } - - expr_ref extract_consequence(unsigned lo, unsigned hi) { - bool is_int = is_int_sort(); - app_ref zero(a.mk_numeral(rational::zero(), is_int), m); - expr_ref res(m); - res = zero; - bool is_strict = false; - bool is_eq = true; - expr* x, *y; - for (unsigned i = lo; i < hi; ++i) { - app* c = m_ineqs[i].get(); - if (m.is_eq(c, x, y)) { - mul(m_coeffs[i], x, res); - mul(-m_coeffs[i], y, res); - } - if (a.is_lt(c, x, y) || a.is_gt(c, y, x)) { - mul(m_coeffs[i], x, res); - mul(-m_coeffs[i], y, res); - is_strict = true; - is_eq = false; - } - if (a.is_le(c, x, y) || a.is_ge(c, y, x)) { - mul(m_coeffs[i], x, res); - mul(-m_coeffs[i], y, res); - is_eq = false; - } - } - - zero = a.mk_numeral(rational::zero(), a.is_int(res)); - if (is_eq) { - res = m.mk_eq(res, zero); - } - else if (is_strict) { - res = mk_lt(res, zero); - } - else { - res = mk_le(res, zero); - } - res = m.mk_not(res); - th_rewriter rw(m); - params_ref params; - params.set_bool("gcd_rounding", true); - rw.updt_params(params); - proof_ref pr(m); - expr_ref result(m); - rw(res, result, pr); - fix_dl(result); - return result; - } - - // patch: swap addends to make static - // features recognize difference constraint. - void fix_dl(expr_ref& r) { - expr* e; - if (m.is_not(r, e)) { - r = e; - fix_dl(r); - r = m.mk_not(r); - return; - } - expr* e1, *e2, *e3, *e4; - if ((m.is_eq(r, e1, e2) || a.is_lt(r, e1, e2) || a.is_gt(r, e1, e2) || - a.is_le(r, e1, e2) || a.is_ge(r, e1, e2))) { - if (a.is_add(e1, e3, e4) && a.is_mul(e3)) { - r = m.mk_app(to_app(r)->get_decl(), a.mk_add(e4,e3), e2); - } - } - } - }; - - farkas_learner::farkas_learner(smt_params& params, ast_manager& outer_mgr) - : m_proof_params(get_proof_params(params)), - m_pr(PGM_ENABLED), - m_constr(nullptr), - m_combine_farkas_coefficients(true), - p2o(m_pr, outer_mgr), - o2p(outer_mgr, m_pr) - { - reg_decl_plugins(m_pr); - m_ctx = alloc(smt::kernel, m_pr, m_proof_params); - } - - farkas_learner::~farkas_learner() { - dealloc(m_constr); - } - - smt_params farkas_learner::get_proof_params(smt_params& orig_params) { - smt_params res(orig_params); - res.m_arith_bound_prop = BP_NONE; - // temp hack to fix the build - // res.m_conflict_resolution_strategy = CR_ALL_DECIDED; - res.m_arith_auto_config_simplex = true; - res.m_arith_propagate_eqs = false; - res.m_arith_eager_eq_axioms = false; - res.m_arith_eq_bounds = false; - return res; - } - - class farkas_learner::equality_expander_cfg : public default_rewriter_cfg - { - ast_manager& m; - arith_util m_ar; - public: - equality_expander_cfg(ast_manager& m) : m(m), m_ar(m) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - expr * x, *y; - if (m.is_eq(s, x, y) && (m_ar.is_int(x) || m_ar.is_real(x))) { - t = m.mk_and(m_ar.mk_ge(x, y), m_ar.mk_le(x, y)); - return true; - } - else { - return false; - } - } - - }; - - class collect_pure_proc { - func_decl_set& m_symbs; - public: - collect_pure_proc(func_decl_set& s):m_symbs(s) {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - m_symbs.insert(a->get_decl()); - } - } - void operator()(var*) {} - void operator()(quantifier*) {} - }; - - - bool farkas_learner::get_lemma_guesses(expr * A_ext, expr * B_ext, expr_ref_vector& lemmas) - { - expr_ref A(o2p(A_ext), m_pr); - expr_ref B(o2p(B_ext), m_pr); - proof_ref pr(m_pr); - expr_ref tmp(m_pr); - expr_ref_vector ilemmas(m_pr); - equality_expander_cfg ee_rwr_cfg(m_pr); - rewriter_tpl ee_rwr(m_pr, false, ee_rwr_cfg); - - lemmas.reset(); - - ee_rwr(A, A); - ee_rwr(B, B); - - expr_set bs; - expr_ref_vector blist(m_pr); - flatten_and(B, blist); - for (unsigned i = 0; i < blist.size(); ++i) { - bs.insert(blist[i].get()); - } - - - if (!m_ctx) { - m_ctx = alloc(smt::kernel, m_pr, m_proof_params); - } - - m_ctx->push(); - m_ctx->assert_expr(A); - expr_set::iterator it = bs.begin(), end = bs.end(); - for (; it != end; ++it) { - m_ctx->assert_expr(*it); - } - lbool res = m_ctx->check(); - bool is_unsat = res == l_false; - if (is_unsat) { - pr = m_ctx->get_proof(); - get_lemmas(m_ctx->get_proof(), bs, ilemmas); - for (unsigned i = 0; i < ilemmas.size(); ++i) { - lemmas.push_back(p2o(ilemmas[i].get())); - } - } - m_ctx->pop(1); - - IF_VERBOSE(3, { - for (unsigned i = 0; i < ilemmas.size(); ++i) { - verbose_stream() << "B': " << mk_pp(ilemmas[i].get(), m_pr) << "\n"; - } - }); - - TRACE("farkas_learner", - tout << (is_unsat?"unsat":"sat") << "\n"; - tout << "A: " << mk_pp(A_ext, m_ctx->m()) << "\n"; - tout << "B: " << mk_pp(B_ext, m_ctx->m()) << "\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - tout << "B': " << mk_pp(ilemmas[i].get(), m_pr) << "\n"; - }); - DEBUG_CODE( - if (is_unsat) { - m_ctx->push(); - m_ctx->assert_expr(A); - for (unsigned i = 0; i < ilemmas.size(); ++i) { - m_ctx->assert_expr(ilemmas[i].get()); - } - lbool res2 = m_ctx->check(); - SASSERT(l_false == res2); - m_ctx->pop(1); - } - ); - return is_unsat; - } - - // - // Perform simple subsumption check of lemmas. - // - void farkas_learner::simplify_lemmas(expr_ref_vector& lemmas) { - ast_manager& m = lemmas.get_manager(); - goal_ref g(alloc(goal, m, false, false, false)); - TRACE("farkas_simplify_lemmas", - for (unsigned i = 0; i < lemmas.size(); ++i) { - tout << mk_pp(lemmas[i].get(), m) << "\n"; - }); - - for (unsigned i = 0; i < lemmas.size(); ++i) { - g->assert_expr(lemmas[i].get()); - } - expr_ref tmp(m); - goal_ref_buffer result; - tactic_ref simplifier = mk_arith_bounds_tactic(m); - (*simplifier)(g, result); - lemmas.reset(); - SASSERT(result.size() == 1); - goal* r = result[0]; - for (unsigned i = 0; i < r->size(); ++i) { - lemmas.push_back(r->form(i)); - } - TRACE("farkas_simplify_lemmas", - tout << "simplified:\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - tout << mk_pp(lemmas[i].get(), m) << "\n"; - }); - } - - - void farkas_learner::combine_constraints(unsigned n, app * const * lits, rational const * coeffs, expr_ref& res) - { - ast_manager& m = res.get_manager(); - if (m_combine_farkas_coefficients) { - if (!m_constr) { - m_constr = alloc(constr, m); - } - m_constr->reset(); - for (unsigned i = 0; i < n; ++i) { - m_constr->add(coeffs[i], lits[i]); - } - m_constr->get(res); - } - else { - bool_rewriter rw(m); - rw.mk_or(n, (expr*const*)(lits), res); - res = m.mk_not(res); - } - } - - class farkas_learner::constant_replacer_cfg : public default_rewriter_cfg - { - const obj_map& m_translation; - public: - constant_replacer_cfg(const obj_map& translation) - : m_translation(translation) - { } - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - return m_translation.find(s, t); - } - }; - - // every uninterpreted symbol is in symbs - class is_pure_expr_proc { - func_decl_set const& m_symbs; - public: - struct non_pure {}; - - is_pure_expr_proc(func_decl_set const& s):m_symbs(s) {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - if (!m_symbs.contains(a->get_decl())) { - throw non_pure(); - } - } - } - void operator()(var*) {} - void operator()(quantifier*) {} - }; - - bool farkas_learner::is_pure_expr(func_decl_set const& symbs, expr* e) const { - is_pure_expr_proc proc(symbs); - try { - for_each_expr(proc, e); - } - catch (is_pure_expr_proc::non_pure) { - return false; - } - return true; - }; - - - /** - Revised version of Farkas strengthener. - 1. Mark B-pure nodes as derivations that depend only on B. - 2. Collect B-influenced nodes - 3. (optional) Permute B-pure units over resolution steps to narrow dependencies on B. - 4. Weaken B-pure units for resolution with Farkas Clauses. - 5. Add B-pure units elsewhere. - - Rules: - - hypothesis h |- h - - H |- false - - lemma ---------- - |- not H - - Th |- L \/ C H |- not L - - th-lemma ------------------------- - H |- C - - Note: C is false for theory axioms, C is unit literal for propagation. - - - rewrite |- t = s - - H |- t = s - - monotonicity ---------------- - H |- f(t) = f(s) - - H |- t = s H' |- s = u - - trans ---------------------- - H, H' |- t = u - - H |- C \/ L H' |- not L - - unit_resolve ------------------------ - H, H' |- C - - H |- a ~ b H' |- a - - mp -------------------- - H, H' |- b - - - def-axiom |- C - - - asserted |- f - - Mark nodes by: - - Hypotheses - - Dependency on bs - - Dependency on A - - A node is unit derivable from bs if: - - It has no hypotheses. - - It depends on bs. - - It does not depend on A. - - NB: currently unit derivable is not symmetric: A clause can be - unit derivable, but a unit literal with hypotheses is not. - This is clearly wrong, because hypotheses are just additional literals - in a clausal version. - - NB: the routine is not interpolating, though an interpolating variant would - be preferrable because then we can also use it for model propagation. - - We collect the unit derivable nodes from bs. - These are the weakenings of bs, besides the - units under Farkas. - - */ - -#define INSERT(_x_) if (!lemma_set.contains(_x_)) { lemma_set.insert(_x_); lemmas.push_back(_x_); } - - void farkas_learner::get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas) { - ast_manager& m = lemmas.get_manager(); - bool_rewriter brwr(m); - func_decl_set Bsymbs; - collect_pure_proc collect_proc(Bsymbs); - expr_set::iterator it = bs.begin(), end = bs.end(); - for (; it != end; ++it) { - for_each_expr(collect_proc, *it); - } - - proof_ref pr(root, m); - proof_utils::reduce_hypotheses(pr); - proof_utils::permute_unit_resolution(pr); - IF_VERBOSE(3, verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n";); - - ptr_vector hyprefs; - obj_map hypmap; - obj_hashtable lemma_set; - ast_mark b_depend, a_depend, visited, b_closed; - expr_set* empty_set = alloc(expr_set); - hyprefs.push_back(empty_set); - ptr_vector todo; - TRACE("pdr_verbose", tout << mk_pp(pr, m) << "\n";); - todo.push_back(pr); - while (!todo.empty()) { - proof* p = todo.back(); - SASSERT(m.is_proof(p)); - if (visited.is_marked(p)) { - todo.pop_back(); - continue; - } - bool all_visit = true; - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - expr* arg = p->get_arg(i); - SASSERT(m.is_proof(arg)); - if (!visited.is_marked(arg)) { - all_visit = false; - todo.push_back(to_app(arg)); - } - } - if (!all_visit) { - continue; - } - visited.mark(p, true); - todo.pop_back(); - - // retrieve hypotheses and dependencies on A, bs. - bool b_dep = false, a_dep = false; - expr_set* hyps = empty_set; - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - expr* arg = p->get_arg(i); - a_dep = a_dep || a_depend.is_marked(arg); - b_dep = b_dep || b_depend.is_marked(arg); - expr_set* hyps2 = hypmap.find(arg); - if (hyps != hyps2 && !hyps2->empty()) { - if (hyps->empty()) { - hyps = hyps2; - } - else { - expr_set* hyps3 = alloc(expr_set); - set_union(*hyps3, *hyps); - set_union(*hyps3, *hyps2); - hyps = hyps3; - hyprefs.push_back(hyps); - } - } - } - hypmap.insert(p, hyps); - a_depend.mark(p, a_dep); - b_depend.mark(p, b_dep); - -#define IS_B_PURE(_p) (b_depend.is_marked(_p) && !a_depend.is_marked(_p) && hypmap.find(_p)->empty()) - - - // Add lemmas that depend on bs, have no hypotheses, don't depend on A. - if ((!hyps->empty() || a_depend.is_marked(p)) && - b_depend.is_marked(p) && !is_farkas_lemma(m, p)) { - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - app* arg = to_app(p->get_arg(i)); - if (IS_B_PURE(arg)) { - expr* fact = m.get_fact(arg); - if (is_pure_expr(Bsymbs, fact)) { - TRACE("farkas_learner", - tout << "Add: " << mk_pp(m.get_fact(arg), m) << "\n"; - tout << mk_pp(arg, m) << "\n"; - ); - INSERT(fact); - } - else { - get_asserted(p, bs, b_closed, lemma_set, lemmas); - b_closed.mark(p, true); - } - } - } - } - - switch(p->get_decl_kind()) { - case PR_ASSERTED: - if (bs.contains(m.get_fact(p))) { - b_depend.mark(p, true); - } - else { - a_depend.mark(p, true); - } - break; - case PR_HYPOTHESIS: { - SASSERT(hyps == empty_set); - hyps = alloc(expr_set); - hyps->insert(m.get_fact(p)); - hyprefs.push_back(hyps); - hypmap.insert(p, hyps); - break; - } - case PR_DEF_AXIOM: { - if (!is_pure_expr(Bsymbs, m.get_fact(p))) { - a_depend.mark(p, true); - } - break; - } - case PR_LEMMA: { - expr_set* hyps2 = alloc(expr_set); - hyprefs.push_back(hyps2); - set_union(*hyps2, *hyps); - hyps = hyps2; - expr* fml = m.get_fact(p); - hyps->remove(fml); - if (m.is_or(fml)) { - for (unsigned i = 0; i < to_app(fml)->get_num_args(); ++i) { - expr* f = to_app(fml)->get_arg(i); - expr_ref hyp(m); - brwr.mk_not(f, hyp); - hyps->remove(hyp); - } - } - hypmap.insert(p, hyps); - break; - } - case PR_TH_LEMMA: { - if (!is_farkas_lemma(m, p)) break; - - SASSERT(m.has_fact(p)); - unsigned prem_cnt = m.get_num_parents(p); - func_decl * d = p->get_decl(); - SASSERT(d->get_num_parameters() >= prem_cnt+2); - SASSERT(d->get_parameter(0).get_symbol() == "arith"); - SASSERT(d->get_parameter(1).get_symbol() == "farkas"); - parameter const* params = d->get_parameters() + 2; - - app_ref_vector lits(m); - expr_ref tmp(m); - unsigned num_b_pures = 0; - rational coef; - vector coeffs; - - TRACE("farkas_learner", - for (unsigned i = 0; i < prem_cnt; ++i) { - VERIFY(params[i].is_rational(coef)); - proof* prem = to_app(p->get_arg(i)); - bool b_pure = IS_B_PURE(prem); - tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; - } - tout << mk_pp(m.get_fact(p), m) << "\n"; - ); - - // NB. Taking 'abs' of coefficients is a workaround. - // The Farkas coefficient extraction in arith_core must be wrong. - // The coefficients would be always positive relative to the theory lemma. - - for(unsigned i = 0; i < prem_cnt; ++i) { - expr * prem_e = p->get_arg(i); - SASSERT(is_app(prem_e)); - proof * prem = to_app(prem_e); - - if(IS_B_PURE(prem)) { - ++num_b_pures; - } - else { - VERIFY(params[i].is_rational(coef)); - lits.push_back(to_app(m.get_fact(prem))); - coeffs.push_back(abs(coef)); - } - } - params += prem_cnt; - if (prem_cnt + 2 < d->get_num_parameters()) { - unsigned num_args = 1; - expr* fact = m.get_fact(p); - expr* const* args = &fact; - if (m.is_or(fact)) { - app* _or = to_app(fact); - num_args = _or->get_num_args(); - args = _or->get_args(); - } - SASSERT(prem_cnt + 2 + num_args == d->get_num_parameters()); - for (unsigned i = 0; i < num_args; ++i) { - expr* prem_e = args[i]; - brwr.mk_not(prem_e, tmp); - VERIFY(params[i].is_rational(coef)); - SASSERT(is_app(tmp)); - lits.push_back(to_app(tmp)); - coeffs.push_back(abs(coef)); - } - - } - SASSERT(coeffs.size() == lits.size()); - if (num_b_pures > 0) { - expr_ref res(m); - combine_constraints(coeffs.size(), lits.c_ptr(), coeffs.c_ptr(), res); - TRACE("farkas_learner", tout << "Add: " << mk_pp(res, m) << "\n";); - INSERT(res); - b_closed.mark(p, true); - } - } - default: - break; - } - } - - std::for_each(hyprefs.begin(), hyprefs.end(), delete_proc()); - simplify_lemmas(lemmas); - } - - void farkas_learner::get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences) { - TRACE("farkas_learner", tout << "get consequences\n";); - m_combine_farkas_coefficients = false; - get_lemmas(root, bs, consequences); - m_combine_farkas_coefficients = true; - } - - void farkas_learner::get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas) { - ast_manager& m = lemmas.get_manager(); - ast_mark visited; - proof* p0 = p; - ptr_vector todo; - todo.push_back(p); - - while (!todo.empty()) { - p = todo.back(); - todo.pop_back(); - if (visited.is_marked(p) || b_closed.is_marked(p)) { - continue; - } - visited.mark(p, true); - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - expr* arg = p->get_arg(i); - SASSERT(m.is_proof(arg)); - todo.push_back(to_app(arg)); - } - if (p->get_decl_kind() == PR_ASSERTED && - bs.contains(m.get_fact(p))) { - expr* fact = m.get_fact(p); - (void)p0; - TRACE("farkas_learner", - tout << mk_ll_pp(p0,m) << "\n"; - tout << "Add: " << mk_pp(p,m) << "\n";); - INSERT(fact); - b_closed.mark(p, true); - } - } - } - - - bool farkas_learner::is_farkas_lemma(ast_manager& m, expr* e) { - app * a; - func_decl* d; - symbol sym; - return - is_app(e) && - (a = to_app(e), d = a->get_decl(), true) && - PR_TH_LEMMA == a->get_decl_kind() && - d->get_num_parameters() >= 2 && - d->get_parameter(0).is_symbol(sym) && sym == "arith" && - d->get_parameter(1).is_symbol(sym) && sym == "farkas" && - d->get_num_parameters() >= m.get_num_parents(to_app(e)) + 2; - }; - - - void farkas_learner::test() { - smt_params params; - enable_trace("farkas_learner"); - - bool res; - ast_manager m; - reg_decl_plugins(m); - arith_util a(m); - pdr::farkas_learner fl(params, m); - expr_ref_vector lemmas(m); - - sort_ref int_s(a.mk_int(), m); - expr_ref x(m.mk_const(symbol("x"), int_s), m); - expr_ref y(m.mk_const(symbol("y"), int_s), m); - expr_ref z(m.mk_const(symbol("z"), int_s), m); - expr_ref u(m.mk_const(symbol("u"), int_s), m); - expr_ref v(m.mk_const(symbol("v"), int_s), m); - - // A: x > y >= z - // B: x < z - // Farkas: x <= z - expr_ref A(m.mk_and(a.mk_gt(x,y), a.mk_ge(y,z)),m); - expr_ref B(a.mk_gt(z,x),m); - res = fl.get_lemma_guesses(A, B, lemmas); - std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; - - // A: x > y >= z + 2 - // B: x = 1, z = 8 - // Farkas: x <= z + 2 - expr_ref one(a.mk_numeral(rational(1), true), m); - expr_ref two(a.mk_numeral(rational(2), true), m); - expr_ref eight(a.mk_numeral(rational(8), true), m); - A = m.mk_and(a.mk_gt(x,y),a.mk_ge(y,a.mk_add(z,two))); - B = m.mk_and(m.mk_eq(x,one), m.mk_eq(z, eight)); - res = fl.get_lemma_guesses(A, B, lemmas); - std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; - - // A: x > y >= z \/ x >= u > z - // B: z > x + 1 - // Farkas: z >= x - A = m.mk_or(m.mk_and(a.mk_gt(x,y),a.mk_ge(y,z)),m.mk_and(a.mk_ge(x,u),a.mk_gt(u,z))); - B = a.mk_gt(z, a.mk_add(x,one)); - res = fl.get_lemma_guesses(A, B, lemmas); - std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; - - // A: (x > y >= z \/ x >= u > z \/ u > v) - // B: z > x + 1 & not (u > v) - // Farkas: z >= x & not (u > v) - A = m.mk_or(m.mk_and(a.mk_gt(x,y),a.mk_ge(y,z)),m.mk_and(a.mk_ge(x,u),a.mk_gt(u,z)), a.mk_gt(u, v)); - B = m.mk_and(a.mk_gt(z, a.mk_add(x,one)), m.mk_not(a.mk_gt(u, v))); - res = fl.get_lemma_guesses(A, B, lemmas); - std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; - - } - - void farkas_learner::collect_statistics(statistics& st) const { - if (m_ctx) { - m_ctx->collect_statistics(st); - } - } - - -}; - diff --git a/src/muz/pdr/pdr_farkas_learner.h b/src/muz/pdr/pdr_farkas_learner.h deleted file mode 100644 index a5f3482e6..000000000 --- a/src/muz/pdr/pdr_farkas_learner.h +++ /dev/null @@ -1,128 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_farkas_learner.h - -Abstract: - - SMT2 interface for the datalog PDR - -Author: - - Krystof Hoder (t-khoder) 2011-11-1. - -Revision History: - ---*/ - -#ifndef PDR_FARKAS_LEARNER_H_ -#define PDR_FARKAS_LEARNER_H_ - -#include "ast/arith_decl_plugin.h" -#include "ast/ast_translation.h" -#include "ast/bv_decl_plugin.h" -#include "smt/smt_kernel.h" -#include "ast/rewriter/bool_rewriter.h" -#include "muz/pdr/pdr_util.h" -#include "smt/params/smt_params.h" -#include "tactic/tactic.h" - -namespace pdr { - -class farkas_learner { - class farkas_collector; - class constant_replacer_cfg; - class equality_expander_cfg; - class constr; - - typedef obj_hashtable expr_set; - - smt_params m_proof_params; - ast_manager m_pr; - scoped_ptr m_ctx; - constr* m_constr; - - // - // true: produce a combined constraint by applying Farkas coefficients. - // false: produce a conjunction of the negated literals from the theory lemmas. - // - bool m_combine_farkas_coefficients; - - - static smt_params get_proof_params(smt_params& orig_params); - - // - // all ast objects passed to private functions have m_proof_mgs as their ast_manager - // - - ast_translation p2o; /** Translate expression from inner ast_manager to outer one */ - ast_translation o2p; /** Translate expression from outer ast_manager to inner one */ - - - /** All ast opbjects here are in the m_proof_mgs */ - void get_lemma_guesses_internal(proof * p, expr* A, expr * B, expr_ref_vector& lemmas); - - bool farkas2lemma(proof * fstep, expr* A, expr * B, expr_ref& res); - - void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res); - - bool try_ensure_lemma_in_language(expr_ref& lemma, expr* A, const func_decl_set& lang); - - bool is_farkas_lemma(ast_manager& m, expr* e); - - void get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas); - - bool is_pure_expr(func_decl_set const& symbs, expr* e) const; - - static void test(); - -public: - farkas_learner(smt_params& params, ast_manager& m); - - ~farkas_learner(); - - /** - All ast objects have the ast_manager which was passed as - an argument to the constructor (i.e. m_outer_mgr) - - B is a conjunction of literals. - A && B is unsat, equivalently A => ~B is valid - Find a weakened B' such that - A && B' is unsat and B' uses vocabulary (and constants) in common with A. - return lemmas to weaken B. - */ - - bool get_lemma_guesses(expr * A, expr * B, expr_ref_vector& lemmas); - - /** - Traverse a proof and retrieve lemmas using the vocabulary from bs. - */ - void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas); - - /** - Traverse a proof and retrieve consequences of A that are used to establish ~B. - The assumption is that: - - A => \/ ~consequences[i] and \/ ~consequences[i] => ~B - - e.g., the second implication can be rewritten as: - - B => /\ consequences[i] - */ - void get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences); - - /** - \brief Simplify lemmas using subsumption. - */ - void simplify_lemmas(expr_ref_vector& lemmas); - - void collect_statistics(statistics& st) const; - -}; - - -} - -#endif diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp deleted file mode 100644 index 8e52cb6f4..000000000 --- a/src/muz/pdr/pdr_generalizers.cpp +++ /dev/null @@ -1,777 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_generalizers.cpp - -Abstract: - - Generalizers of satisfiable states and unsat cores. - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-20. - -Revision History: - ---*/ - - -#include "muz/pdr/pdr_context.h" -#include "muz/pdr/pdr_farkas_learner.h" -#include "muz/pdr/pdr_generalizers.h" -#include "ast/expr_abstract.h" -#include "ast/rewriter/var_subst.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "model/model_smt2_pp.h" - - -namespace pdr { - - - // ------------------------ - // core_bool_inductive_generalizer - - // main propositional induction generalizer. - // drop literals one by one from the core and check if the core is still inductive. - // - void core_bool_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - if (core.size() <= 1) { - return; - } - ast_manager& m = core.get_manager(); - TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; }); - unsigned num_failures = 0, i = 0, old_core_size = core.size(); - ptr_vector processed; - - while (i < core.size() && 1 < core.size() && (!m_failure_limit || num_failures <= m_failure_limit)) { - expr_ref lit(m); - lit = core[i].get(); - core[i] = m.mk_true(); - if (n.pt().check_inductive(n.level(), core, uses_level)) { - num_failures = 0; - for (i = 0; i < core.size() && processed.contains(core[i].get()); ++i); - } - else { - core[i] = lit; - processed.push_back(lit); - ++num_failures; - ++i; - } - } - IF_VERBOSE(2, verbose_stream() << "old size: " << old_core_size << " new size: " << core.size() << "\n";); - TRACE("pdr", tout << "old size: " << old_core_size << " new size: " << core.size() << "\n";); - } - - - void core_multi_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - UNREACHABLE(); - } - - /** - \brief Find minimal cores. - Apply a simple heuristic: find a minimal core, then find minimal cores that exclude at least one - literal from each of the literals in the minimal cores. - */ - void core_multi_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - ast_manager& m = core.get_manager(); - expr_ref_vector old_core(m), core0(core); - bool uses_level1 = uses_level; - m_gen(n, core0, uses_level1); - new_cores.push_back(std::make_pair(core0, uses_level1)); - obj_hashtable core_exprs, core1_exprs; - set_union(core_exprs, core0); - for (unsigned i = 0; i < old_core.size(); ++i) { - expr* lit = old_core[i].get(); - if (core_exprs.contains(lit)) { - expr_ref_vector core1(old_core); - core1[i] = core1.back(); - core1.pop_back(); - uses_level1 = uses_level; - m_gen(n, core1, uses_level1); - SASSERT(core1.size() <= old_core.size()); - if (core1.size() < old_core.size()) { - new_cores.push_back(std::make_pair(core1, uses_level1)); - core1_exprs.reset(); - set_union(core1_exprs, core1); - set_intersection(core_exprs, core1_exprs); - } - } - } - } - - // ------------------------ - // core_farkas_generalizer - - // - // for each disjunct of core: - // weaken predecessor. - // - - core_farkas_generalizer::core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p): - core_generalizer(ctx), - m_farkas_learner(p, m) - {} - - void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - ast_manager& m = n.pt().get_manager(); - if (core.empty()) return; - expr_ref A(m), B(mk_and(core)), C(m); - expr_ref_vector Bs(m); - flatten_or(B, Bs); - A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level()); - - bool change = false; - for (unsigned i = 0; i < Bs.size(); ++i) { - expr_ref_vector lemmas(m); - C = Bs[i].get(); - if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) { - TRACE("pdr", - tout << "Old core:\n" << mk_pp(B, m) << "\n"; - tout << "New core:\n" << mk_and(lemmas) << "\n";); - Bs[i] = mk_and(lemmas); - change = true; - } - } - if (change) { - C = mk_or(Bs); - TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";); - core.reset(); - flatten_and(C, core); - uses_level = true; - } - } - - void core_farkas_generalizer::collect_statistics(statistics& st) const { - m_farkas_learner.collect_statistics(st); - } - - - core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure): - core_generalizer(ctx), - m(ctx.get_manager()), - m_is_closure(is_closure) { - } - - void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - // method3(n, core, uses_level, new_cores); - method1(n, core, uses_level, new_cores); - } - - void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - UNREACHABLE(); - } - - // use the entire region as starting point for generalization. - // - // Constraints: - // add_variables: y = y1 + y2 - // core: Ay <= b -> conv1: A*y1 <= b*sigma1 - // sigma1 > 0 - // sigma2 > 0 - // 1 = sigma1 + sigma2 - // A'y <= b' -> conv2: A'*y2 <= b'*sigma2 - // - // If Constraints & Transition(y0, y) is unsat, then - // update with new core. - // - void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - expr_ref_vector conv2(m), fmls(m), fml1_2(m); - bool change = false; - - if (core.empty()) { - new_cores.push_back(std::make_pair(core, uses_level)); - return; - } - closure cl(n.pt(), m_is_closure); - - expr_ref fml1 = mk_and(core); - expr_ref fml2 = n.pt().get_formulas(n.level(), false); - fml1_2.push_back(fml1); - fml1_2.push_back(nullptr); - flatten_and(fml2, fmls); - for (unsigned i = 0; i < fmls.size(); ++i) { - fml2 = m.mk_not(fmls[i].get()); - fml1_2[1] = fml2; - expr_ref state = cl(fml1_2); - TRACE("pdr", - tout << "Check states:\n" << mk_pp(state, m) << "\n"; - tout << "Old states:\n" << mk_pp(fml2, m) << "\n"; - ); - model_node nd(nullptr, state, n.pt(), n.level()); - pred_transformer::scoped_farkas sf(n.pt(), true); - bool uses_level1 = uses_level; - if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) { - new_cores.push_back(std::make_pair(conv2, uses_level1)); - change = true; - expr_ref state1 = mk_and(conv2); - TRACE("pdr", - tout << mk_pp(state, m) << "\n"; - tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";); - IF_VERBOSE(0, - verbose_stream() << mk_pp(state, m) << "\n"; - verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";); - } - } - if (!m_is_closure || !change) { - new_cores.push_back(std::make_pair(core, uses_level)); - } - } - - /* - Extract the lemmas from the transition relation that were used to establish unsatisfiability. - Take convex closures of conbinations of these lemmas. - */ - void core_convex_hull_generalizer::method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - TRACE("dl", tout << "method: generalize consequences of F(R)\n"; - for (unsigned i = 0; i < core.size(); ++i) { - tout << "B:" << mk_pp(core[i], m) << "\n"; - }); - bool uses_level1; - expr_ref_vector core1(m); - core1.append(core); - expr_ref_vector consequences(m); - { - n.pt().get_solver().set_consequences(&consequences); - pred_transformer::scoped_farkas sf (n.pt(), true); - VERIFY(l_false == n.pt().is_reachable(n, &core1, uses_level1)); - n.pt().get_solver().set_consequences(nullptr); - } - IF_VERBOSE(0, - verbose_stream() << "Consequences: " << consequences.size() << "\n"; - for (unsigned i = 0; i < consequences.size(); ++i) { - verbose_stream() << mk_pp(consequences[i].get(), m) << "\n"; - } - verbose_stream() << "core: " << core1.size() << "\n"; - for (unsigned i = 0; i < core1.size(); ++i) { - verbose_stream() << mk_pp(core1[i].get(), m) << "\n"; - }); - - expr_ref tmp(m); - - // Check that F(R) => \/ consequences - { - expr_ref_vector cstate(m); - for (unsigned i = 0; i < consequences.size(); ++i) { - cstate.push_back(m.mk_not(consequences[i].get())); - } - tmp = m.mk_and(cstate.size(), cstate.c_ptr()); - model_node nd(nullptr, tmp, n.pt(), n.level()); - pred_transformer::scoped_farkas sf (n.pt(), false); - VERIFY(l_false == n.pt().is_reachable(nd, &core1, uses_level1)); - } - - // Create disjunction. - tmp = m.mk_and(core.size(), core.c_ptr()); - - // Check that \/ consequences => not (core) - if (!is_unsat(consequences, tmp)) { - IF_VERBOSE(0, verbose_stream() << "Consequences don't contradict the core\n";); - return; - } - IF_VERBOSE(0, verbose_stream() << "Consequences contradict core\n";); - - if (!strengthen_consequences(n, consequences, tmp)) { - return; - } - - IF_VERBOSE(0, verbose_stream() << "consequences strengthened\n";); - // Use the resulting formula to find Farkas lemmas from core. - } - - bool core_convex_hull_generalizer::strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B) { - expr_ref A(m), tmp(m), convA(m); - unsigned sz = As.size(); - closure cl(n.pt(), m_is_closure); - for (unsigned i = 0; i < As.size(); ++i) { - expr_ref_vector Hs(m); - Hs.push_back(As[i].get()); - for (unsigned j = i + 1; j < As.size(); ++j) { - Hs.push_back(As[j].get()); - bool unsat = false; - A = cl(Hs); - tmp = As[i].get(); - As[i] = A; - unsat = is_unsat(As, B); - As[i] = tmp; - if (unsat) { - IF_VERBOSE(0, verbose_stream() << "New convex: " << mk_pp(convA, m) << "\n";); - convA = A; - As[j] = As.back(); - As.pop_back(); - --j; - } - else { - Hs.pop_back(); - } - } - if (Hs.size() > 1) { - As[i] = convA; - } - } - return sz > As.size(); - } - - - bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) { - smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); - expr_ref disj(m); - disj = m.mk_or(As.size(), As.c_ptr()); - ctx.assert_expr(disj); - ctx.assert_expr(B); - std::cout << "Checking\n" << mk_pp(disj, m) << "\n" << mk_pp(B, m) << "\n"; - return l_false == ctx.check(); - } - - - // --------------------------------- - // core_arith_inductive_generalizer - // NB. this is trying out some ideas for generalization in - // an ad hoc specialized way. arith_inductive_generalizer should - // not be used by default. It is a place-holder for a general purpose - // extrapolator of a lattice basis. - - core_arith_inductive_generalizer::core_arith_inductive_generalizer(context& ctx): - core_generalizer(ctx), - m(ctx.get_manager()), - a(m), - m_refs(m) {} - - void core_arith_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - if (core.size() <= 1) { - return; - } - reset(); - expr_ref e(m), t1(m), t2(m), t3(m); - rational r; - - TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; }); - - svector eqs; - get_eqs(core, eqs); - - if (eqs.empty()) { - return; - } - - expr_ref_vector new_core(m); - new_core.append(core); - - for (unsigned eq = 0; eq < eqs.size(); ++eq) { - rational r = eqs[eq].m_value; - expr* x = eqs[eq].m_term; - unsigned k = eqs[eq].m_i; - unsigned l = eqs[eq].m_j; - - new_core[l] = m.mk_true(); - new_core[k] = m.mk_true(); - - for (unsigned i = 0; i < new_core.size(); ++i) { - if (substitute_alias(r, x, new_core[i].get(), e)) { - new_core[i] = e; - } - } - if (abs(r) >= rational(2) && a.is_int(x)) { - new_core[k] = m.mk_eq(a.mk_mod(x, a.mk_numeral(rational(2), true)), a.mk_numeral(rational(0), true)); - new_core[l] = a.mk_le(x, a.mk_numeral(rational(0), true)); - } - } - - bool inductive = n.pt().check_inductive(n.level(), new_core, uses_level); - - IF_VERBOSE(1, - verbose_stream() << (inductive?"":"non") << "inductive\n"; - verbose_stream() << "old\n"; - for (unsigned j = 0; j < core.size(); ++j) { - verbose_stream() << mk_pp(core[j].get(), m) << "\n"; - } - verbose_stream() << "new\n"; - for (unsigned j = 0; j < new_core.size(); ++j) { - verbose_stream() << mk_pp(new_core[j].get(), m) << "\n"; - }); - - if (inductive) { - core.reset(); - core.append(new_core); - } - } - - void core_arith_inductive_generalizer::insert_bound(bool is_lower, expr* x, rational const& r, unsigned i) { - if (r.is_neg()) { - expr_ref e(m); - e = a.mk_uminus(x); - m_refs.push_back(e); - x = e; - is_lower = !is_lower; - } - - vector bound; - bound.push_back(std::make_pair(x, i)); - if (is_lower) { - m_lb.insert(abs(r), bound); - } - else { - m_ub.insert(abs(r), bound); - } - } - - void core_arith_inductive_generalizer::reset() { - m_refs.reset(); - m_lb.reset(); - m_ub.reset(); - } - - void core_arith_inductive_generalizer::get_eqs(expr_ref_vector const& core, svector& eqs) { - expr* e1, *x, *y; - expr_ref e(m); - rational r; - - for (unsigned i = 0; i < core.size(); ++i) { - e = core[i]; - if (m.is_not(e, e1) && a.is_le(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) { - // not (<= x r) <=> x >= r + 1 - insert_bound(true, x, r + rational(1), i); - } - else if (m.is_not(e, e1) && a.is_ge(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) { - // not (>= x r) <=> x <= r - 1 - insert_bound(false, x, r - rational(1), i); - } - else if (a.is_le(e, x, y) && a.is_numeral(y, r)) { - insert_bound(false, x, r, i); - } - else if (a.is_ge(e, x, y) && a.is_numeral(y, r)) { - insert_bound(true, x, r, i); - } - } - bounds_t::iterator it = m_lb.begin(), end = m_lb.end(); - for (; it != end; ++it) { - rational r = it->m_key; - vector & terms1 = it->m_value; - vector terms2; - if (r >= rational(2) && m_ub.find(r, terms2)) { - for (unsigned i = 0; i < terms1.size(); ++i) { - bool done = false; - for (unsigned j = 0; !done && j < terms2.size(); ++j) { - expr* t1 = terms1[i].first; - expr* t2 = terms2[j].first; - if (t1 == t2) { - eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second)); - done = true; - } - else { - e = m.mk_eq(t1, t2); - th_rewriter rw(m); - rw(e); - if (m.is_true(e)) { - eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second)); - done = true; - } - } - } - } - } - } - } - - bool core_arith_inductive_generalizer::substitute_alias(rational const& r, expr* x, expr* e, expr_ref& result) { - rational r2; - expr* y, *z, *e1; - if (m.is_not(e, e1) && substitute_alias(r, x, e1, result)) { - result = m.mk_not(result); - return true; - } - if (a.is_le(e, y, z) && a.is_numeral(z, r2)) { - if (r == r2) { - result = a.mk_le(y, x); - return true; - } - if (r == r2 + rational(1)) { - result = a.mk_lt(y, x); - return true; - } - if (r == r2 - rational(1)) { - result = a.mk_le(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x)))); - return true; - } - - } - if (a.is_ge(e, y, z) && a.is_numeral(z, r2)) { - if (r == r2) { - result = a.mk_ge(y, x); - return true; - } - if (r2 == r + rational(1)) { - result = a.mk_gt(y, x); - return true; - } - if (r2 == r - rational(1)) { - result = a.mk_ge(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x)))); - return true; - } - } - return false; - } - - - // - // < F, phi, i + 1> - // | - // < G, psi, i > - // - // where: - // - // p(x) <- F(x,y,p,q) - // q(x) <- G(x,y) - // - // Hyp: - // Q_k(x) => phi(x) j <= k <= i - // Q_k(x) => R_k(x) j <= k <= i + 1 - // Q_k(x) <=> Trans(Q_{k-1}) j < k <= i + 1 - // Conclusion: - // Q_{i+1}(x) => phi(x) - // - class core_induction_generalizer::imp { - context& m_ctx; - manager& pm; - ast_manager& m; - - // - // Create predicate Q_level - // - func_decl_ref mk_pred(unsigned level, func_decl* f) { - func_decl_ref result(m); - std::ostringstream name; - name << f->get_name() << "_" << level; - symbol sname(name.str().c_str()); - result = m.mk_func_decl(sname, f->get_arity(), f->get_domain(), f->get_range()); - return result; - } - - // - // Create formula exists y . z . F[Q_{level-1}, x, y, z] - // - expr_ref mk_transition_rule( - expr_ref_vector const& reps, - unsigned level, - datalog::rule const& rule) - { - expr_ref_vector conj(m), sub(m); - expr_ref result(m); - svector names; - unsigned ut_size = rule.get_uninterpreted_tail_size(); - unsigned t_size = rule.get_tail_size(); - if (0 == level && 0 < ut_size) { - result = m.mk_false(); - return result; - } - app* atom = rule.get_head(); - SASSERT(atom->get_num_args() == reps.size()); - - for (unsigned i = 0; i < reps.size(); ++i) { - expr* arg = atom->get_arg(i); - if (is_var(arg)) { - unsigned idx = to_var(arg)->get_idx(); - if (idx >= sub.size()) sub.resize(idx+1); - if (sub[idx].get()) { - conj.push_back(m.mk_eq(sub[idx].get(), reps[i])); - } - else { - sub[idx] = reps[i]; - } - } - else { - conj.push_back(m.mk_eq(arg, reps[i])); - } - } - for (unsigned i = 0; 0 < level && i < ut_size; i++) { - app* atom = rule.get_tail(i); - func_decl* head = atom->get_decl(); - func_decl_ref fn = mk_pred(level-1, head); - conj.push_back(m.mk_app(fn, atom->get_num_args(), atom->get_args())); - } - for (unsigned i = ut_size; i < t_size; i++) { - conj.push_back(rule.get_tail(i)); - } - result = mk_and(conj); - if (!sub.empty()) { - expr_ref tmp = result; - var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result); - } - expr_free_vars fv; - fv(result); - fv.set_default_sort(m.mk_bool_sort()); - for (unsigned i = 0; i < fv.size(); ++i) { - names.push_back(symbol(fv.size() - i - 1)); - } - if (!fv.empty()) { - fv.reverse(); - result = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), result); - } - return result; - } - - expr_ref bind_head(expr_ref_vector const& reps, expr* fml) { - expr_ref result(m); - expr_abstract(m, 0, reps.size(), reps.c_ptr(), fml, result); - ptr_vector sorts; - svector names; - unsigned sz = reps.size(); - for (unsigned i = 0; i < sz; ++i) { - sorts.push_back(m.get_sort(reps[sz-i-1])); - names.push_back(symbol(sz-i-1)); - } - if (sz > 0) { - result = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), result); - } - return result; - } - - expr_ref_vector mk_reps(pred_transformer& pt) { - expr_ref_vector reps(m); - expr_ref rep(m); - for (unsigned i = 0; i < pt.head()->get_arity(); ++i) { - rep = m.mk_const(pm.o2n(pt.sig(i), 0)); - reps.push_back(rep); - } - return reps; - } - - // - // extract transition axiom: - // - // forall x . p_lvl(x) <=> exists y z . F[p_{lvl-1}(y), q_{lvl-1}(z), x] - // - expr_ref mk_transition_axiom(pred_transformer& pt, unsigned level) { - expr_ref fml(m.mk_false(), m), tr(m); - expr_ref_vector reps = mk_reps(pt); - ptr_vector const& rules = pt.rules(); - for (unsigned i = 0; i < rules.size(); ++i) { - tr = mk_transition_rule(reps, level, *rules[i]); - fml = (i == 0)?tr.get():m.mk_or(fml, tr); - } - func_decl_ref fn = mk_pred(level, pt.head()); - fml = m.mk_iff(m.mk_app(fn, reps.size(), reps.c_ptr()), fml); - fml = bind_head(reps, fml); - return fml; - } - - // - // Create implication: - // Q_level(x) => phi(x) - // - expr_ref mk_predicate_property(unsigned level, pred_transformer& pt, expr* phi) { - expr_ref_vector reps = mk_reps(pt); - func_decl_ref fn = mk_pred(level, pt.head()); - expr_ref fml(m); - fml = m.mk_implies(m.mk_app(fn, reps.size(), reps.c_ptr()), phi); - fml = bind_head(reps, fml); - return fml; - } - - - - public: - imp(context& ctx): m_ctx(ctx), pm(ctx.get_pdr_manager()), m(ctx.get_manager()) {} - - // - // not exists y . F(x,y) - // - expr_ref mk_blocked_transition(pred_transformer& pt, unsigned level) { - SASSERT(level > 0); - expr_ref fml(m.mk_true(), m); - expr_ref_vector reps = mk_reps(pt), fmls(m); - ptr_vector const& rules = pt.rules(); - for (unsigned i = 0; i < rules.size(); ++i) { - fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[i]))); - } - fml = mk_and(fmls); - TRACE("pdr", tout << mk_pp(fml, m) << "\n";); - return fml; - } - - expr_ref mk_induction_goal(pred_transformer& pt, unsigned level, unsigned depth) { - SASSERT(level >= depth); - expr_ref_vector conjs(m); - ptr_vector pts; - unsigned_vector levels; - // negated goal - expr_ref phi = mk_blocked_transition(pt, level); - conjs.push_back(m.mk_not(mk_predicate_property(level, pt, phi))); - pts.push_back(&pt); - levels.push_back(level); - // Add I.H. - for (unsigned lvl = level-depth; lvl < level; ++lvl) { - if (lvl > 0) { - expr_ref psi = mk_blocked_transition(pt, lvl); - conjs.push_back(mk_predicate_property(lvl, pt, psi)); - pts.push_back(&pt); - levels.push_back(lvl); - } - } - // Transitions: - for (unsigned qhead = 0; qhead < pts.size(); ++qhead) { - pred_transformer& qt = *pts[qhead]; - unsigned lvl = levels[qhead]; - - // Add transition definition and properties at level. - conjs.push_back(mk_transition_axiom(qt, lvl)); - conjs.push_back(mk_predicate_property(lvl, qt, qt.get_formulas(lvl, true))); - - // Enqueue additional hypotheses - ptr_vector const& rules = qt.rules(); - if (lvl + depth < level || lvl == 0) { - continue; - } - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule& r = *rules[i]; - unsigned ut_size = r.get_uninterpreted_tail_size(); - for (unsigned j = 0; j < ut_size; ++j) { - func_decl* f = r.get_tail(j)->get_decl(); - pred_transformer* rt = m_ctx.get_pred_transformers().find(f); - bool found = false; - for (unsigned k = 0; !found && k < levels.size(); ++k) { - found = (rt == pts[k] && levels[k] + 1 == lvl); - } - if (!found) { - levels.push_back(lvl-1); - pts.push_back(rt); - } - } - } - } - - expr_ref result = mk_and(conjs); - TRACE("pdr", tout << mk_pp(result, m) << "\n";); - return result; - } - }; - - // - // Instantiate Peano induction schema. - // - void core_induction_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - model_node* p = n.parent(); - if (p == nullptr) { - return; - } - unsigned depth = 2; - imp imp(m_ctx); - ast_manager& m = core.get_manager(); - expr_ref goal = imp.mk_induction_goal(p->pt(), p->level(), depth); - smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); - ctx.assert_expr(goal); - lbool r = ctx.check(); - TRACE("pdr", tout << r << "\n"; - for (unsigned i = 0; i < core.size(); ++i) { - tout << mk_pp(core[i].get(), m) << "\n"; - }); - if (r == l_false) { - core.reset(); - expr_ref phi = imp.mk_blocked_transition(p->pt(), p->level()); - core.push_back(m.mk_not(phi)); - uses_level = true; - } - } -}; - diff --git a/src/muz/pdr/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h deleted file mode 100644 index a75b347a4..000000000 --- a/src/muz/pdr/pdr_generalizers.h +++ /dev/null @@ -1,110 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_generalizers.h - -Abstract: - - Generalizer plugins. - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-22. - -Revision History: - ---*/ - -#ifndef PDR_GENERALIZERS_H_ -#define PDR_GENERALIZERS_H_ - -#include "muz/pdr/pdr_context.h" -#include "muz/pdr/pdr_closure.h" -#include "ast/arith_decl_plugin.h" - -namespace pdr { - - class core_bool_inductive_generalizer : public core_generalizer { - unsigned m_failure_limit; - public: - core_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : core_generalizer(ctx), m_failure_limit(failure_limit) {} - ~core_bool_inductive_generalizer() override {} - void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override; - }; - - template - class r_map : public map { - }; - - class core_arith_inductive_generalizer : public core_generalizer { - typedef std::pair term_loc_t; - typedef r_map > bounds_t; - - ast_manager& m; - arith_util a; - expr_ref_vector m_refs; - bounds_t m_lb; - bounds_t m_ub; - - struct eq { - expr* m_term; - rational m_value; - unsigned m_i; - unsigned m_j; - eq(expr* t, rational const& r, unsigned i, unsigned j): m_term(t), m_value(r), m_i(i), m_j(j) {} - }; - void reset(); - void insert_bound(bool is_lower, expr* x, rational const& r, unsigned i); - void get_eqs(expr_ref_vector const& core, svector& eqs); - bool substitute_alias(rational const&r, expr* x, expr* e, expr_ref& result); - public: - core_arith_inductive_generalizer(context& ctx); - ~core_arith_inductive_generalizer() override {} - void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override; - }; - - class core_farkas_generalizer : public core_generalizer { - farkas_learner m_farkas_learner; - public: - core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p); - ~core_farkas_generalizer() override {} - void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override; - void collect_statistics(statistics& st) const override; - }; - - - class core_convex_hull_generalizer : public core_generalizer { - ast_manager& m; - obj_map m_models; - bool m_is_closure; - void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); - void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); - bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B); - bool is_unsat(expr_ref_vector const& As, expr* B); - public: - core_convex_hull_generalizer(context& ctx, bool is_closure); - ~core_convex_hull_generalizer() override {} - void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) override; - void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override; - }; - - class core_multi_generalizer : public core_generalizer { - core_bool_inductive_generalizer m_gen; - public: - core_multi_generalizer(context& ctx, unsigned max_failures): core_generalizer(ctx), m_gen(ctx, max_failures) {} - ~core_multi_generalizer() override {} - void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override; - void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) override; - }; - - class core_induction_generalizer : public core_generalizer { - class imp; - public: - core_induction_generalizer(context& ctx): core_generalizer(ctx) {} - ~core_induction_generalizer() override {} - void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override; - }; -}; -#endif diff --git a/src/muz/pdr/pdr_manager.cpp b/src/muz/pdr/pdr_manager.cpp deleted file mode 100644 index da15bf094..000000000 --- a/src/muz/pdr/pdr_manager.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_manager.cpp - -Abstract: - - A manager class for PDR, taking care of creating of AST - objects and conversions between them. - -Author: - - Krystof Hoder (t-khoder) 2011-8-25. - -Revision History: - ---*/ - -#include -#include "muz/pdr/pdr_manager.h" -#include "ast/ast_smt2_pp.h" -#include "ast/for_each_expr.h" -#include "ast/has_free_vars.h" -#include "ast/rewriter/expr_replacer.h" -#include "ast/expr_abstract.h" -#include "model/model2expr.h" -#include "model/model_smt2_pp.h" -#include "tactic/model_converter.h" - -namespace pdr { - - class collect_decls_proc { - func_decl_set& m_bound_decls; - func_decl_set& m_aux_decls; - public: - collect_decls_proc(func_decl_set& bound_decls, func_decl_set& aux_decls): - m_bound_decls(bound_decls), - m_aux_decls(aux_decls) { - } - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - func_decl* f = a->get_decl(); - if (!m_bound_decls.contains(f)) { - m_aux_decls.insert(f); - } - } - } - void operator()(var* v) {} - void operator()(quantifier* q) {} - }; - - typedef hashtable symbol_set; - - expr_ref inductive_property::fixup_clause(expr* fml) const { - expr_ref_vector disjs(m); - flatten_or(fml, disjs); - expr_ref result(m); - bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result); - return result; - } - - expr_ref inductive_property::fixup_clauses(expr* fml) const { - expr_ref_vector conjs(m); - expr_ref result(m); - flatten_and(fml, conjs); - for (unsigned i = 0; i < conjs.size(); ++i) { - conjs[i] = fixup_clause(conjs[i].get()); - } - bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); - return result; - } - - std::string inductive_property::to_string() const { - std::stringstream stm; - model_ref md; - expr_ref result(m); - to_model(md); - model_smt2_pp(stm, m, *md.get(), 0); - return stm.str(); - } - - void inductive_property::to_model(model_ref& md) const { - md = alloc(model, m); - vector const& rs = m_relation_info; - expr_ref_vector conjs(m); - for (unsigned i = 0; i < rs.size(); ++i) { - relation_info ri(rs[i]); - func_decl * pred = ri.m_pred; - expr_ref prop = fixup_clauses(ri.m_body); - func_decl_ref_vector const& sig = ri.m_vars; - expr_ref q(m); - expr_ref_vector sig_vars(m); - for (unsigned j = 0; j < sig.size(); ++j) { - sig_vars.push_back(m.mk_const(sig[sig.size()-j-1])); - } - expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); - if (sig.empty()) { - md->register_decl(pred, q); - } - else { - func_interp* fi = alloc(func_interp, m, sig.size()); - fi->set_else(q); - md->register_decl(pred, fi); - } - } - TRACE("pdr", model_smt2_pp(tout, m, *md, 0);); - apply(const_cast(m_mc), md); - } - - expr_ref inductive_property::to_expr() const { - model_ref md; - expr_ref result(m); - to_model(md); - model2expr(md, result); - return result; - } - - - void inductive_property::display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const { - func_decl_set bound_decls, aux_decls; - collect_decls_proc collect_decls(bound_decls, aux_decls); - - for (unsigned i = 0; i < m_relation_info.size(); ++i) { - bound_decls.insert(m_relation_info[i].m_pred); - func_decl_ref_vector const& sig = m_relation_info[i].m_vars; - for (unsigned j = 0; j < sig.size(); ++j) { - bound_decls.insert(sig[j]); - } - for_each_expr(collect_decls, m_relation_info[i].m_body); - } - for (unsigned i = 0; i < rules.size(); ++i) { - bound_decls.insert(rules[i]->get_decl()); - } - for (unsigned i = 0; i < rules.size(); ++i) { - unsigned u_sz = rules[i]->get_uninterpreted_tail_size(); - unsigned t_sz = rules[i]->get_tail_size(); - for (unsigned j = u_sz; j < t_sz; ++j) { - for_each_expr(collect_decls, rules[i]->get_tail(j)); - } - } - smt2_pp_environment_dbg env(m); - func_decl_set::iterator it = aux_decls.begin(), end = aux_decls.end(); - for (; it != end; ++it) { - func_decl* f = *it; - ast_smt2_pp(out, f, env); - out << "\n"; - } - - out << to_string() << "\n"; - for (unsigned i = 0; i < rules.size(); ++i) { - out << "(push)\n"; - out << "(assert (not\n"; - rm.display_smt2(*rules[i], out); - out << "))\n"; - out << "(check-sat)\n"; - out << "(pop)\n"; - } - } - - manager::manager(smt_params& fparams, unsigned max_num_contexts, ast_manager& manager) : - m(manager), - m_fparams(fparams), - m_brwr(m), - m_mux(m), - m_background(m.mk_true(), m), - m_contexts(fparams, max_num_contexts, m), - m_next_unique_num(0) - { - } - - - void manager::add_new_state(func_decl * s) { - SASSERT(s->get_arity()==0); //we currently don't support non-constant states - decl_vector vect; - SASSERT(o_index(0)==1); //we assume this in the number of retrieved symbols - m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect); - m_o0_preds.push_back(vect[o_index(0)]); - } - - func_decl * manager::get_o_pred(func_decl* s, unsigned idx) - { - func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx)); - if(res) { return res; } - add_new_state(s); - res = m_mux.try_get_by_prefix(s, o_index(idx)); - SASSERT(res); - return res; - } - - func_decl * manager::get_n_pred(func_decl* s) - { - func_decl * res = m_mux.try_get_by_prefix(s, n_index()); - if(res) { return res; } - add_new_state(s); - res = m_mux.try_get_by_prefix(s, n_index()); - SASSERT(res); - return res; - } - - void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res) { - m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res); - } - - void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res) { - m_brwr.mk_and(core.size(), core.c_ptr(), res); - } - - void manager::mk_cube_into_lemma(expr * cube, expr_ref & res) { - m_brwr.mk_not(cube, res); - } - - void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res) { - m_brwr.mk_not(lemma, res); - } - - expr_ref manager::mk_and(unsigned sz, expr* const* exprs) { - expr_ref result(m); - m_brwr.mk_and(sz, exprs, result); - return result; - } - - expr_ref manager::mk_or(unsigned sz, expr* const* exprs) { - expr_ref result(m); - m_brwr.mk_or(sz, exprs, result); - return result; - } - - expr_ref manager::mk_not_and(expr_ref_vector const& conjs) { - expr_ref result(m), e(m); - expr_ref_vector es(conjs); - flatten_and(es); - for (unsigned i = 0; i < es.size(); ++i) { - m_brwr.mk_not(es[i].get(), e); - es[i] = e; - } - m_brwr.mk_or(es.size(), es.c_ptr(), result); - return result; - } - - void manager::get_or(expr* e, expr_ref_vector& result) { - result.push_back(e); - for (unsigned i = 0; i < result.size(); ) { - e = result[i].get(); - if (m.is_or(e)) { - result.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - result[i] = result.back(); - result.pop_back(); - } - else { - ++i; - } - } - } - - bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value) - { - if(!is_app(atom0)) { - return false; - } - app * atom = to_app(atom0); - expr * arg1; - expr * arg2; - app * candidate_state; - app_ref candidate_value(m); - if(m.is_not(atom, arg1)) { - if(!is_app(arg1)) { - return false; - } - candidate_state = to_app(arg1); - candidate_value = m.mk_false(); - } - else if(m.is_eq(atom, arg1, arg2)) { - if(!is_app(arg1) || !is_app(arg2)) { - return false; - } - if(!m_mux.is_muxed(to_app(arg1)->get_decl())) { - std::swap(arg1, arg2); - } - candidate_state = to_app(arg1); - candidate_value = to_app(arg2); - } - else { - candidate_state = atom; - candidate_value = m.mk_true(); - } - if(!m_mux.is_muxed(candidate_state->get_decl())) { - return false; - } - state = candidate_state; - value = candidate_value; - return true; - } - - bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state) { - app_ref dummy_value_holder(m); - app * s; - if(try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) { - state = s->get_decl(); - return true; - } - else { - return false; - } - } - - bool manager::implication_surely_holds(expr * lhs, expr * rhs, expr * bg) { - smt::kernel sctx(m, get_fparams()); - if(bg) { - sctx.assert_expr(bg); - } - sctx.assert_expr(lhs); - expr_ref neg_rhs(m.mk_not(rhs),m); - sctx.assert_expr(neg_rhs); - lbool smt_res = sctx.check(); - return smt_res==l_false; - } - -}; diff --git a/src/muz/pdr/pdr_manager.h b/src/muz/pdr/pdr_manager.h deleted file mode 100644 index ccbbbe356..000000000 --- a/src/muz/pdr/pdr_manager.h +++ /dev/null @@ -1,304 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_manager.h - -Abstract: - - A manager class for PDR, taking care of creating of AST - objects and conversions between them. - -Author: - - Krystof Hoder (t-khoder) 2011-8-25. - -Revision History: - ---*/ - -#ifndef PDR_MANAGER_H_ -#define PDR_MANAGER_H_ - -#include -#include -#include "ast/rewriter/bool_rewriter.h" -#include "ast/rewriter/expr_replacer.h" -#include "ast/expr_substitution.h" -#include "util/map.h" -#include "util/ref_vector.h" -#include "smt/smt_kernel.h" -#include "muz/pdr/pdr_util.h" -#include "muz/pdr/pdr_sym_mux.h" -#include "muz/pdr/pdr_farkas_learner.h" -#include "muz/pdr/pdr_smt_context_manager.h" -#include "muz/base/dl_rule.h" - - -namespace smt { - class context; -} - -namespace pdr { - - struct relation_info { - func_decl_ref m_pred; - func_decl_ref_vector m_vars; - expr_ref m_body; - relation_info(ast_manager& m, func_decl* pred, ptr_vector const& vars, expr* b): - m_pred(pred, m), m_vars(m, vars.size(), vars.c_ptr()), m_body(b, m) {} - relation_info(relation_info const& other): m_pred(other.m_pred), m_vars(other.m_vars), m_body(other.m_body) {} - }; - - class unknown_exception {}; - - class inductive_property { - ast_manager& m; - model_converter_ref m_mc; - vector m_relation_info; - expr_ref fixup_clauses(expr* property) const; - expr_ref fixup_clause(expr* clause) const; - public: - inductive_property(ast_manager& m, model_converter_ref& mc, vector const& relations): - m(m), - m_mc(mc), - m_relation_info(relations) {} - - std::string to_string() const; - - expr_ref to_expr() const; - - void to_model(model_ref& md) const; - - void display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const; - }; - - class manager - { - ast_manager& m; - smt_params& m_fparams; - - mutable bool_rewriter m_brwr; - - sym_mux m_mux; - expr_ref m_background; - decl_vector m_o0_preds; - pdr::smt_context_manager m_contexts; - - /** whenever we need an unique number, we get this one and increase */ - unsigned m_next_unique_num; - - - unsigned n_index() const { return 0; } - unsigned o_index(unsigned i) const { return i+1; } - - void add_new_state(func_decl * s); - - public: - manager(smt_params& fparams, unsigned max_num_contexts, ast_manager & manager); - - ast_manager& get_manager() const { return m; } - smt_params& get_fparams() const { return m_fparams; } - bool_rewriter& get_brwr() const { return m_brwr; } - - expr_ref mk_and(unsigned sz, expr* const* exprs); - expr_ref mk_and(expr_ref_vector const& exprs) { - return mk_and(exprs.size(), exprs.c_ptr()); - } - expr_ref mk_and(expr* a, expr* b) { - expr* args[2] = { a, b }; - return mk_and(2, args); - } - expr_ref mk_or(unsigned sz, expr* const* exprs); - expr_ref mk_or(expr_ref_vector const& exprs) { - return mk_or(exprs.size(), exprs.c_ptr()); - } - - expr_ref mk_not_and(expr_ref_vector const& exprs); - - void get_or(expr* e, expr_ref_vector& result); - - //"o" predicates stand for the old states and "n" for the new states - func_decl * get_o_pred(func_decl * s, unsigned idx); - func_decl * get_n_pred(func_decl * s); - - /** - Marks symbol as non-model which means it will not appear in models collected by - get_state_cube_from_model function. - This is to take care of auxiliary symbols introduced by the disjunction relations - to relativize lemmas coming from disjuncts. - */ - void mark_as_non_model(func_decl * p) { - m_mux.mark_as_non_model(p); - } - - - func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); } - func_decl * const * end_o0_preds() const { return m_o0_preds.end(); } - - bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); } - func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); } - - bool is_o(func_decl * p, unsigned idx) const { - return m_mux.has_index(p, o_index(idx)); - } - bool is_o(expr* e, unsigned idx) const { - return is_app(e) && is_o(to_app(e)->get_decl(), idx); - } - bool is_o(func_decl * p) const { - unsigned idx; - return m_mux.try_get_index(p, idx) && idx!=n_index(); - } - bool is_o(expr* e) const { - return is_app(e) && is_o(to_app(e)->get_decl()); - } - bool is_n(func_decl * p) const { - return m_mux.has_index(p, n_index()); - } - bool is_n(expr* e) const { - return is_app(e) && is_n(to_app(e)->get_decl()); - } - - /** true if p should not appead in models propagates into child relations */ - bool is_non_model_sym(func_decl * p) const - { return m_mux.is_non_model_sym(p); } - - - /** true if f doesn't contain any n predicates */ - bool is_o_formula(expr * f) const { - return !m_mux.contains(f, n_index()); - } - - /** true if f contains only o state preds of index o_idx */ - bool is_o_formula(expr * f, unsigned o_idx) const { - return m_mux.is_homogenous_formula(f, o_index(o_idx)); - } - /** true if f doesn't contain any o predicates */ - bool is_n_formula(expr * f) const { - return m_mux.is_homogenous_formula(f, n_index()); - } - - func_decl * o2n(func_decl * p, unsigned o_idx) { - return m_mux.conv(p, o_index(o_idx), n_index()); - } - func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) { - return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx)); - } - func_decl * n2o(func_decl * p, unsigned o_idx) { - return m_mux.conv(p, n_index(), o_index(o_idx)); - } - - void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true) - { m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); } - - void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true) - { m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); } - - void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) - { m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); } - - void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous=true) - { m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); } - - /** - Return true if all state symbols which e contains are of one kind (either "n" or one of "o"). - */ - bool is_homogenous_formula(expr * e) const { - return m_mux.is_homogenous_formula(e); - } - - /** - Collect indices used in expression. - */ - void collect_indices(expr* e, unsigned_vector& indices) const { - m_mux.collect_indices(e, indices); - } - - /** - Collect used variables of each index. - */ - void collect_variables(expr* e, vector >& vars) const { - m_mux.collect_variables(e, vars); - } - - /** - Return true iff both s1 and s2 are either "n" or "o" of the same index. - If one (or both) of them are not state symbol, return false. - */ - bool have_different_state_kinds(func_decl * s1, func_decl * s2) const { - unsigned i1, i2; - return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1!=i2; - } - - /** - Increase indexes of state symbols in formula by dist. - The 'N' index becomes 'O' index with number dist-1. - */ - void formula_shift(expr * src, expr_ref & tgt, unsigned dist) { - SASSERT(n_index()==0); - SASSERT(o_index(0)==1); - m_mux.shift_formula(src, dist, tgt); - } - - void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res); - void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res); - void mk_cube_into_lemma(expr * cube, expr_ref & res); - void mk_lemma_into_cube(expr * lemma, expr_ref & res); - - /** - Remove from vec all atoms that do not have an "o" state. - The order of elements in vec may change. - An assumption is that atoms having "o" state of given index - do not have "o" states of other indexes or "n" states. - */ - void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const - { m_mux.filter_idx(vec, o_index(o_idx)); } - void filter_n_atoms(expr_ref_vector& vec) const - { m_mux.filter_idx(vec, n_index()); } - - /** - Partition literals into o_lits and others. - */ - void partition_o_atoms(expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, - unsigned o_idx) const { - m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx)); - } - - void filter_out_non_model_atoms(expr_ref_vector& vec) const - { m_mux.filter_non_model_lits(vec); } - - bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value); - bool try_get_state_decl_from_atom(expr * atom, func_decl *& state); - - - std::string pp_model(const model_core & mdl) const - { return m_mux.pp_model(mdl); } - - - void set_background(expr* b) { m_background = b; } - - expr* get_background() const { return m_background; } - - - /** - Return true if we can show that lhs => rhs. The function can have false negatives - (i.e. when smt::context returns unknown), but no false positives. - - bg is background knowledge and can be null - */ - bool implication_surely_holds(expr * lhs, expr * rhs, expr * bg=nullptr); - - unsigned get_unique_num() { return m_next_unique_num++; } - - pdr::smt_context* mk_fresh() { return m_contexts.mk_fresh(); } - - void collect_statistics(statistics& st) const { m_contexts.collect_statistics(st); } - - void reset_statistics() { m_contexts.reset_statistics(); } - }; -} - -#endif diff --git a/src/muz/pdr/pdr_prop_solver.cpp b/src/muz/pdr/pdr_prop_solver.cpp deleted file mode 100644 index 8ebc9b9cb..000000000 --- a/src/muz/pdr/pdr_prop_solver.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - prop_solver.cpp - -Abstract: - - SMT solver abstraction for PDR. - -Author: - - Krystof Hoder (t-khoder) 2011-8-17. - -Revision History: - ---*/ - -#include -#include "model/model.h" -#include "muz/pdr/pdr_util.h" -#include "muz/pdr/pdr_prop_solver.h" -#include "ast/ast_smt2_pp.h" -#include "muz/base/dl_util.h" -#include "model/model_pp.h" -#include "smt/params/smt_params.h" -#include "ast/datatype_decl_plugin.h" -#include "ast/bv_decl_plugin.h" -#include "muz/pdr/pdr_farkas_learner.h" -#include "ast/ast_smt2_pp.h" -#include "ast/rewriter/expr_replacer.h" - -// -// Auxiliary structure to introduce propositional names for assumptions that are not -// propositional. It is to work with the smt::context's restriction -// that assumptions be propositional literals. -// - -namespace pdr { - - class prop_solver::safe_assumptions { - prop_solver& s; - ast_manager& m; - expr_ref_vector m_atoms; - expr_ref_vector m_assumptions; - obj_map m_proxies2expr; - obj_map m_expr2proxies; - unsigned m_num_proxies; - - app * mk_proxy(expr* literal) { - app* res; - SASSERT(!is_var(literal)); //it doesn't make sense to introduce names to variables - if (m_expr2proxies.find(literal, res)) { - return res; - } - SASSERT(s.m_proxies.size() >= m_num_proxies); - if (m_num_proxies == s.m_proxies.size()) { - std::stringstream name; - name << "pdr_proxy_" << s.m_proxies.size(); - res = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); - s.m_proxies.push_back(res); - s.m_aux_symbols.insert(res->get_decl()); - } - else { - res = s.m_proxies[m_num_proxies].get(); - } - ++m_num_proxies; - m_expr2proxies.insert(literal, res); - m_proxies2expr.insert(res, literal); - expr_ref implies(m.mk_or(m.mk_not(res), literal), m); - s.m_ctx->assert_expr(implies); - m_assumptions.push_back(implies); - TRACE("pdr_verbose", tout << "name asserted " << mk_pp(implies, m) << "\n";); - return res; - } - - void mk_safe(expr_ref_vector& conjs) { - flatten_and(conjs); - expand_literals(conjs); - for (unsigned i = 0; i < conjs.size(); ++i) { - expr * lit = conjs[i].get(); - expr * lit_core = lit; - m.is_not(lit, lit_core); - SASSERT(!m.is_true(lit)); - if (!is_uninterp(lit_core) || to_app(lit_core)->get_num_args() != 0) { - conjs[i] = mk_proxy(lit); - } - } - m_assumptions.append(conjs); - } - - expr* apply_accessor( - ptr_vector const& acc, - unsigned j, - func_decl* f, - expr* c) { - if (is_app(c) && to_app(c)->get_decl() == f) { - return to_app(c)->get_arg(j); - } - else { - return m.mk_app(acc[j], c); - } - } - - void expand_literals(expr_ref_vector& conjs) { - arith_util arith(m); - datatype_util dt(m); - bv_util bv(m); - expr* e1, *e2, *c, *val; - rational r; - unsigned bv_size; - - TRACE("pdr", - tout << "begin expand\n"; - for (unsigned i = 0; i < conjs.size(); ++i) { - tout << mk_pp(conjs[i].get(), m) << "\n"; - }); - - for (unsigned i = 0; i < conjs.size(); ++i) { - expr* e = conjs[i].get(); - if (m.is_eq(e, e1, e2) && arith.is_int_real(e1)) { - conjs[i] = arith.mk_le(e1,e2); - if (i+1 == conjs.size()) { - conjs.push_back(arith.mk_ge(e1, e2)); - } - else { - conjs.push_back(conjs[i+1].get()); - conjs[i+1] = arith.mk_ge(e1, e2); - } - ++i; - } - else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) || - (m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))){ - func_decl* f = to_app(val)->get_decl(); - func_decl* r = dt.get_constructor_is(f); - conjs[i] = m.mk_app(r, c); - ptr_vector const& acc = *dt.get_constructor_accessors(f); - for (unsigned j = 0; j < acc.size(); ++j) { - conjs.push_back(m.mk_eq(apply_accessor(acc, j, f, c), to_app(val)->get_arg(j))); - } - } - else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) || - (m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) { - rational two(2); - for (unsigned j = 0; j < bv_size; ++j) { - parameter p(j); - //expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); - expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c)); - if ((r % two).is_zero()) { - e = m.mk_not(e); - } - r = div(r, two); - if (j == 0) { - conjs[i] = e; - } - else { - conjs.push_back(e); - } - } - } - } - TRACE("pdr", - tout << "end expand\n"; - for (unsigned i = 0; i < conjs.size(); ++i) { - tout << mk_pp(conjs[i].get(), m) << "\n"; - }); - } - - public: - safe_assumptions(prop_solver& s, expr_ref_vector const& assumptions): - s(s), m(s.m), m_atoms(assumptions), m_assumptions(m), m_num_proxies(0) { - mk_safe(m_atoms); - } - - ~safe_assumptions() { - } - - expr_ref_vector const& atoms() const { return m_atoms; } - - unsigned assumptions_size() const { return m_assumptions.size(); } - - expr* assumptions(unsigned i) const { return m_assumptions[i]; } - - void undo_proxies(expr_ref_vector& es) { - expr_ref e(m); - expr* r; - for (unsigned i = 0; i < es.size(); ++i) { - e = es[i].get(); - if (is_app(e) && m_proxies2expr.find(to_app(e), r)) { - es[i] = r; - } - } - } - - void elim_proxies(expr_ref_vector& es) { - expr_substitution sub(m, false, m.proofs_enabled()); - proof_ref pr(m); - if (m.proofs_enabled()) { - pr = m.mk_asserted(m.mk_true()); - } - obj_map::iterator it = m_proxies2expr.begin(), end = m_proxies2expr.end(); - for (; it != end; ++it) { - sub.insert(it->m_key, m.mk_true(), pr); - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - replace_proxies(*rep, es); - } - private: - - void replace_proxies(expr_replacer& rep, expr_ref_vector& es) { - expr_ref e(m); - for (unsigned i = 0; i < es.size(); ++i) { - e = es[i].get(); - rep(e); - es[i] = e; - if (m.is_true(e)) { - es[i] = es.back(); - es.pop_back(); - --i; - } - } - } - }; - - - prop_solver::prop_solver(manager& pm, symbol const& name) : - m_fparams(pm.get_fparams()), - m(pm.get_manager()), - m_pm(pm), - m_name(name), - m_ctx(pm.mk_fresh()), - m_pos_level_atoms(m), - m_neg_level_atoms(m), - m_proxies(m), - m_core(nullptr), - m_model(nullptr), - m_consequences(nullptr), - m_subset_based_core(false), - m_use_farkas(false), - m_in_level(false), - m_current_level(0) - { - m_ctx->assert_expr(m_pm.get_background()); - } - - void prop_solver::add_level() { - unsigned idx = level_cnt(); - std::stringstream name; - name << m_name << "#level_" << idx; - func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, nullptr,m.mk_bool_sort()); - m_aux_symbols.insert(lev_pred); - m_level_preds.push_back(lev_pred); - - app_ref pos_la(m.mk_const(lev_pred), m); - app_ref neg_la(m.mk_not(pos_la.get()), m); - - m_pos_level_atoms.push_back(pos_la); - m_neg_level_atoms.push_back(neg_la); - - m_level_atoms_set.insert(pos_la.get()); - m_level_atoms_set.insert(neg_la.get()); - } - - void prop_solver::ensure_level(unsigned lvl) { - while (lvl>=level_cnt()) { - add_level(); - } - } - - unsigned prop_solver::level_cnt() const { - return m_level_preds.size(); - } - - void prop_solver::push_level_atoms(unsigned level, expr_ref_vector& tgt) const { - unsigned lev_cnt = level_cnt(); - for (unsigned i=0; i=level; - app * lev_atom = active ? m_neg_level_atoms[i] : m_pos_level_atoms[i]; - tgt.push_back(lev_atom); - } - } - - void prop_solver::add_formula(expr * form) { - SASSERT(!m_in_level); - m_ctx->assert_expr(form); - IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";); - TRACE("pdr", tout << "add_formula: " << mk_pp(form, m) << "\n";); - } - - void prop_solver::add_level_formula(expr * form, unsigned level) { - ensure_level(level); - app * lev_atom = m_pos_level_atoms[level].get(); - app_ref lform(m.mk_or(form, lev_atom), m); - add_formula(lform.get()); - } - - - lbool prop_solver::check_safe_assumptions( - safe_assumptions& safe, - const expr_ref_vector& atoms) - { - flet _model(m_fparams.m_model, m_model != nullptr); - expr_ref_vector expr_atoms(m); - expr_atoms.append(atoms.size(), atoms.c_ptr()); - - if (m_in_level) { - push_level_atoms(m_current_level, expr_atoms); - } - - lbool result = m_ctx->check(expr_atoms); - - TRACE("pdr", - tout << mk_pp(m_pm.mk_and(expr_atoms), m) << "\n"; - tout << result << "\n";); - - if (result == l_true && m_model) { - m_ctx->get_model(*m_model); - TRACE("pdr_verbose", model_pp(tout, **m_model); ); - } - - if (result == l_false) { - unsigned core_size = m_ctx->get_unsat_core_size(); - m_assumes_level = false; - for (unsigned i = 0; i < core_size; ++i) { - if (m_level_atoms_set.contains(m_ctx->get_unsat_core_expr(i))) { - m_assumes_level = true; - break; - } - } - } - - if (result == l_false && - m_core && - m.proofs_enabled() && - m_use_farkas && - !m_subset_based_core) { - extract_theory_core(safe); - } - else if (result == l_false && m_core) { - extract_subset_core(safe); - SASSERT(expr_atoms.size() >= m_core->size()); - } - m_core = nullptr; - m_model = nullptr; - m_subset_based_core = false; - return result; - } - - void prop_solver::extract_subset_core(safe_assumptions& safe) { - unsigned core_size = m_ctx->get_unsat_core_size(); - m_core->reset(); - for (unsigned i = 0; i < core_size; ++i) { - expr * core_expr = m_ctx->get_unsat_core_expr(i); - SASSERT(is_app(core_expr)); - - if (m_level_atoms_set.contains(core_expr)) { - continue; - } - if (m_ctx->is_aux_predicate(core_expr)) { - continue; - } - m_core->push_back(to_app(core_expr)); - } - - safe.undo_proxies(*m_core); - - TRACE("pdr", - tout << "core_exprs: "; - for (unsigned i = 0; i < core_size; ++i) { - tout << mk_pp(m_ctx->get_unsat_core_expr(i), m) << " "; - } - tout << "\n"; - tout << "core: " << mk_pp(m_pm.mk_and(*m_core), m) << "\n"; - ); - } - - - void prop_solver::extract_theory_core(safe_assumptions& safe) { - proof_ref pr(m); - pr = m_ctx->get_proof(); - IF_VERBOSE(21, verbose_stream() << mk_ismt2_pp(pr, m) << "\n";); - farkas_learner fl(m_fparams, m); - expr_ref_vector lemmas(m); - obj_hashtable bs; - for (unsigned i = 0; i < safe.assumptions_size(); ++i) { - bs.insert(safe.assumptions(i)); - } - fl.get_lemmas(pr, bs, lemmas); - safe.elim_proxies(lemmas); - fl.simplify_lemmas(lemmas); // redundant? - - bool outside_of_logic = - (m_fparams.m_arith_mode == AS_DIFF_LOGIC && - !is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) || - (m_fparams.m_arith_mode == AS_UTVPI && - !is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr())); - - if (outside_of_logic) { - IF_VERBOSE(2, - verbose_stream() << "not diff\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; - }); - extract_subset_core(safe); - } - else { - - IF_VERBOSE(2, - verbose_stream() << "Lemmas\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; - }); - - m_core->reset(); - m_core->append(lemmas); - - if (m_consequences) { - fl.get_consequences(pr, bs, *m_consequences); - } - } - } - - lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) { - return check_assumptions_and_formula(atoms, m.mk_true()); - } - - lbool prop_solver::check_conjunction_as_assumptions(expr * conj) { - expr_ref_vector asmp(m); - asmp.push_back(conj); - return check_assumptions(asmp); - } - - lbool prop_solver::check_assumptions_and_formula(const expr_ref_vector & atoms, expr * form) - { - pdr::smt_context::scoped _scoped(*m_ctx); - safe_assumptions safe(*this, atoms); - m_ctx->assert_expr(form); - CTRACE("pdr", !m.is_true(form), tout << "check with formula: " << mk_pp(form, m) << "\n";); - lbool res = check_safe_assumptions(safe, safe.atoms()); - - // - // we don't have to undo model naming, as from the model - // we extract the values for state variables directly - // - return res; - } - - void prop_solver::collect_statistics(statistics& st) const { - } - - void prop_solver::reset_statistics() { - } - - - - -} diff --git a/src/muz/pdr/pdr_prop_solver.h b/src/muz/pdr/pdr_prop_solver.h deleted file mode 100644 index 463163fbd..000000000 --- a/src/muz/pdr/pdr_prop_solver.h +++ /dev/null @@ -1,139 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - prop_solver.h - -Abstract: - - SAT solver abstraction for PDR. - -Author: - - Krystof Hoder (t-khoder) 2011-8-17. - -Revision History: - ---*/ - -#ifndef PROP_SOLVER_H_ -#define PROP_SOLVER_H_ - -#include -#include -#include -#include "ast/ast.h" -#include "util/obj_hashtable.h" -#include "smt/smt_kernel.h" -#include "util/util.h" -#include "util/vector.h" -#include "muz/pdr/pdr_manager.h" -#include "muz/pdr/pdr_smt_context_manager.h" - - -namespace pdr { - class prop_solver { - - private: - smt_params& m_fparams; - ast_manager& m; - manager& m_pm; - symbol m_name; - scoped_ptr m_ctx; - decl_vector m_level_preds; - app_ref_vector m_pos_level_atoms; // atoms used to identify level - app_ref_vector m_neg_level_atoms; // - obj_hashtable m_level_atoms_set; - app_ref_vector m_proxies; // predicates for assumptions - expr_ref_vector* m_core; - model_ref* m_model; - expr_ref_vector* m_consequences; - bool m_subset_based_core; - bool m_assumes_level; - bool m_use_farkas; - func_decl_set m_aux_symbols; - bool m_in_level; - unsigned m_current_level; // set when m_in_level - - /** Add level atoms activating certain level into a vector */ - void push_level_atoms(unsigned level, expr_ref_vector & tgt) const; - - void ensure_level(unsigned lvl); - - class safe_assumptions; - - void extract_theory_core(safe_assumptions& assumptions); - - void extract_subset_core(safe_assumptions& assumptions); - - lbool check_safe_assumptions( - safe_assumptions& assumptions, - expr_ref_vector const& atoms); - - - public: - prop_solver(pdr::manager& pm, symbol const& name); - - /** return true is s is a symbol introduced by prop_solver */ - bool is_aux_symbol(func_decl * s) const { - return - m_aux_symbols.contains(s) || - m_ctx->is_aux_predicate(s); - } - - void set_core(expr_ref_vector* core) { m_core = core; } - void set_model(model_ref* mdl) { m_model = mdl; } - void set_subset_based_core(bool f) { m_subset_based_core = f; } - void set_consequences(expr_ref_vector* consequences) { m_consequences = consequences; } - - bool assumes_level() const { return m_assumes_level; } - - void add_level(); - unsigned level_cnt() const; - - class scoped_level { - bool& m_lev; - public: - scoped_level(prop_solver& ps, unsigned lvl):m_lev(ps.m_in_level) { - SASSERT(!m_lev); m_lev = true; ps.m_current_level = lvl; - } - ~scoped_level() { m_lev = false; } - }; - - void set_use_farkas(bool f) { m_use_farkas = f; } - bool get_use_farkas() const { return m_use_farkas; } - - void add_formula(expr * form); - void add_level_formula(expr * form, unsigned level); - - /** - * Return true iff conjunction of atoms is consistent with the current state of - * the solver. - * - * If the conjunction of atoms is inconsistent with the solver state and core is non-zero, - * core will contain an unsatisfiable core of atoms. - * - * If the conjunction of atoms is consistent with the solver state and o_model is non-zero, - * o_model will contain the "o" literals true in the assignment. - */ - lbool check_assumptions(const expr_ref_vector & atoms); - - lbool check_conjunction_as_assumptions(expr * conj); - - /** - * Like check_assumptions, except it also asserts an extra formula - */ - lbool check_assumptions_and_formula( - const expr_ref_vector & atoms, - expr * form); - - void collect_statistics(statistics& st) const; - - void reset_statistics(); - - }; -} - - -#endif diff --git a/src/muz/pdr/pdr_reachable_cache.cpp b/src/muz/pdr/pdr_reachable_cache.cpp deleted file mode 100644 index 5d553df4d..000000000 --- a/src/muz/pdr/pdr_reachable_cache.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - reachable_cache.cpp - -Abstract: - - Object for caching of reachable states. - -Author: - - Krystof Hoder (t-khoder) 2011-9-14. - -Revision History: - ---*/ - -#include "muz/pdr/pdr_reachable_cache.h" - -namespace pdr { - - reachable_cache::reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm) - : m(pm.get_manager()), - m_pm(pm), - m_ctx(nullptr), - m_ref_holder(m), - m_disj_connector(m), - m_cache_mode(cm) { - if (m_cache_mode == datalog::CONSTRAINT_CACHE) { - m_ctx = pm.mk_fresh(); - m_ctx->assert_expr(m_pm.get_background()); - } - } - - - void reachable_cache::add_disjuncted_formula(expr * f) { - app_ref new_connector(m.mk_fresh_const("disj_conn", m.mk_bool_sort()), m); - app_ref neg_new_connector(m.mk_not(new_connector), m); - app_ref extended_form(m); - - if(m_disj_connector) { - extended_form = m.mk_or(m_disj_connector, neg_new_connector, f); - } - else { - extended_form = m.mk_or(neg_new_connector, f); - } - if (m_ctx) { - m_ctx->assert_expr(extended_form); - } - - m_disj_connector = new_connector; - } - - void reachable_cache::add_reachable(expr * cube) { - - switch (m_cache_mode) { - case datalog::NO_CACHE: - break; - - case datalog::HASH_CACHE: - m_stats.m_inserts++; - m_cache.insert(cube); - m_ref_holder.push_back(cube); - break; - - case datalog::CONSTRAINT_CACHE: - m_stats.m_inserts++; - TRACE("pdr", tout << mk_pp(cube, m) << "\n";); - add_disjuncted_formula(cube); - break; - - default: - UNREACHABLE(); - } - } - - bool reachable_cache::is_reachable(expr * cube) { - bool found = false; - switch (m_cache_mode) { - case datalog::NO_CACHE: - return false; - - case datalog::HASH_CACHE: - found = m_cache.contains(cube); - break; - - case datalog::CONSTRAINT_CACHE: { - if(!m_disj_connector) { - found = false; - break; - } - expr * connector = m_disj_connector.get(); - expr_ref_vector assms(m); - assms.push_back(connector); - m_ctx->push(); - m_ctx->assert_expr(cube); - lbool res = m_ctx->check(assms); - m_ctx->pop(); - - TRACE("pdr", tout << "is_reachable: " << res << " " << mk_pp(cube, m) << "\n";); - - found = res == l_true; - break; - } - - default: - UNREACHABLE(); - break; - } - if (found) { - m_stats.m_hits++; - } - else { - m_stats.m_miss++; - } - return found; - } - - void reachable_cache::collect_statistics(statistics& st) const { - st.update("cache inserts", m_stats.m_inserts); - st.update("cache miss", m_stats.m_miss); - st.update("cache hits", m_stats.m_hits); - } - - void reachable_cache::reset_statistics() { - m_stats.reset(); - } - - -} diff --git a/src/muz/pdr/pdr_reachable_cache.h b/src/muz/pdr/pdr_reachable_cache.h deleted file mode 100644 index 0833541ba..000000000 --- a/src/muz/pdr/pdr_reachable_cache.h +++ /dev/null @@ -1,66 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - reachable_cache.h - -Abstract: - - Object for caching of reachable states. - -Author: - - Krystof Hoder (t-khoder) 2011-9-14. - -Revision History: - ---*/ - - -#ifndef REACHABLE_CACHE_H_ -#define REACHABLE_CACHE_H_ -#include "ast/ast.h" -#include "util/ref_vector.h" -#include "muz/pdr/pdr_manager.h" -#include "muz/pdr/pdr_smt_context_manager.h" - -namespace pdr { - class reachable_cache { - struct stats { - unsigned m_hits; - unsigned m_miss; - unsigned m_inserts; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - ast_manager & m; - manager & m_pm; - scoped_ptr m_ctx; - ast_ref_vector m_ref_holder; - app_ref m_disj_connector; - obj_hashtable m_cache; - stats m_stats; - datalog::PDR_CACHE_MODE m_cache_mode; - - void add_disjuncted_formula(expr * f); - - public: - reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm); - - void add_init(app * f) { add_disjuncted_formula(f); } - - /** add cube whose all models are reachable */ - void add_reachable(expr * cube); - - /** return true if there is a model of cube which is reachable */ - bool is_reachable(expr * cube); - - void collect_statistics(statistics& st) const; - - void reset_statistics(); - }; -} - -#endif diff --git a/src/muz/pdr/pdr_smt_context_manager.cpp b/src/muz/pdr/pdr_smt_context_manager.cpp deleted file mode 100644 index 88734049b..000000000 --- a/src/muz/pdr/pdr_smt_context_manager.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_smt_context_manager.cpp - -Abstract: - - Manager of smt contexts - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-26. - -Revision History: - ---*/ - -#include "muz/pdr/pdr_smt_context_manager.h" -#include "ast/has_free_vars.h" -#include "ast/ast_pp.h" -#include "ast/ast_smt_pp.h" -#include -#include "smt/params/smt_params.h" - -namespace pdr { - - smt_context::smt_context(smt_context_manager& p, ast_manager& m, app* pred): - m_pred(pred, m), - m_parent(p), - m_in_delay_scope(false), - m_pushed(false) - {} - - bool smt_context::is_aux_predicate(func_decl* p) { - return m_parent.is_aux_predicate(p); - } - - smt_context::scoped::scoped(smt_context& ctx): m_ctx(ctx) { - SASSERT(!m_ctx.m_in_delay_scope); - SASSERT(!m_ctx.m_pushed); - m_ctx.m_in_delay_scope = true; - } - - smt_context::scoped::~scoped() { - SASSERT(m_ctx.m_in_delay_scope); - if (m_ctx.m_pushed) { - m_ctx.pop(); - m_ctx.m_pushed = false; - } - m_ctx.m_in_delay_scope = false; - } - - - _smt_context::_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred): - smt_context(p, ctx.m(), pred), - m_context(ctx) - {} - - void _smt_context::assert_expr(expr* e) { - ast_manager& m = m_context.m(); - if (m.is_true(e)) { - return; - } - CTRACE("pdr", has_free_vars(e), tout << mk_pp(e, m) << "\n";); - SASSERT(!has_free_vars(e)); - if (m_in_delay_scope && !m_pushed) { - m_context.push(); - m_pushed = true; - } - expr_ref fml(m); - fml = m_pushed?e:m.mk_implies(m_pred, e); - m_context.assert_expr(fml); - } - - lbool _smt_context::check(expr_ref_vector& assumptions) { - ast_manager& m = m_pred.get_manager(); - if (!m.is_true(m_pred)) { - assumptions.push_back(m_pred); - } - TRACE("pdr_check", - { - ast_smt_pp pp(m); - for (unsigned i = 0; i < m_context.size(); ++i) { - pp.add_assumption(m_context.get_formula(i)); - } - for (unsigned i = 0; i < assumptions.size(); ++i) { - pp.add_assumption(assumptions[i].get()); - } - - static unsigned lemma_id = 0; - std::ostringstream strm; - strm << "pdr-lemma-" << lemma_id << ".smt2"; - std::ofstream out(strm.str().c_str()); - pp.display_smt2(out, m.mk_true()); - out.close(); - lemma_id++; - tout << "pdr_check: " << strm.str() << "\n"; - }); - lbool result = m_context.check(assumptions.size(), assumptions.c_ptr()); - if (!m.is_true(m_pred)) { - assumptions.pop_back(); - } - return result; - } - - void _smt_context::get_model(model_ref& model) { - m_context.get_model(model); - } - - proof* _smt_context::get_proof() { - return m_context.get_proof(); - } - - smt_context_manager::smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m): - m_fparams(fp), - m(m), - m_max_num_contexts(max_num_contexts), - m_num_contexts(0), - m_predicate_list(m) { - } - - - smt_context_manager::~smt_context_manager() { - TRACE("pdr",tout << "\n";); - std::for_each(m_contexts.begin(), m_contexts.end(), delete_proc()); - } - - smt_context* smt_context_manager::mk_fresh() { - ++m_num_contexts; - app_ref pred(m); - smt::kernel * ctx = nullptr; - if (m_max_num_contexts == 0) { - m_contexts.push_back(alloc(smt::kernel, m, m_fparams)); - pred = m.mk_true(); - ctx = m_contexts[m_num_contexts-1]; - } - else { - if (m_contexts.size() < m_max_num_contexts) { - m_contexts.push_back(alloc(smt::kernel, m, m_fparams)); - } - std::stringstream name; - name << "#context" << m_num_contexts; - pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); - m_predicate_list.push_back(pred); - m_predicate_set.insert(pred->get_decl()); - ctx = m_contexts[(m_num_contexts-1)%m_max_num_contexts]; - } - return alloc(_smt_context, *ctx, *this, pred); - } - - void smt_context_manager::collect_statistics(statistics& st) const { - for (unsigned i = 0; i < m_contexts.size(); ++i) { - m_contexts[i]->collect_statistics(st); - } - } - - void smt_context_manager::reset_statistics() { - for (unsigned i = 0; i < m_contexts.size(); ++i) { - m_contexts[i]->reset_statistics(); - } - } - - -}; - diff --git a/src/muz/pdr/pdr_smt_context_manager.h b/src/muz/pdr/pdr_smt_context_manager.h deleted file mode 100644 index 747cd6457..000000000 --- a/src/muz/pdr/pdr_smt_context_manager.h +++ /dev/null @@ -1,92 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_smt_context_manager.h - -Abstract: - - Manager of smt contexts - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-26. - -Revision History: - ---*/ - -#ifndef PDR_SMT_CONTEXT_MANAGER_H_ -#define PDR_SMT_CONTEXT_MANAGER_H_ - -#include "smt/smt_kernel.h" -#include "ast/func_decl_dependencies.h" -#include "muz/base/dl_util.h" - -namespace pdr { - - class smt_context_manager; - - class smt_context { - protected: - app_ref m_pred; - smt_context_manager& m_parent; - bool m_in_delay_scope; - bool m_pushed; - public: - smt_context(smt_context_manager& p, ast_manager& m, app* pred); - virtual ~smt_context() {} - virtual void assert_expr(expr* e) = 0; - virtual lbool check(expr_ref_vector& assumptions) = 0; - virtual void get_model(model_ref& model) = 0; - virtual proof* get_proof() = 0; - virtual unsigned get_unsat_core_size() = 0; - virtual expr* get_unsat_core_expr(unsigned i) = 0; - virtual void push() = 0; - virtual void pop() = 0; - bool is_aux_predicate(func_decl* p); - bool is_aux_predicate(expr* p) { return is_app(p) && is_aux_predicate(to_app(p)->get_decl()); } - class scoped { - smt_context& m_ctx; - public: - scoped(smt_context& ctx); - ~scoped(); - }; - }; - - class _smt_context : public smt_context { - smt::kernel & m_context; - public: - _smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred); - ~_smt_context() override {} - void assert_expr(expr* e) override; - lbool check(expr_ref_vector& assumptions) override; - void get_model(model_ref& model) override; - proof* get_proof() override; - void push() override { m_context.push(); } - void pop() override { m_context.pop(1); } - unsigned get_unsat_core_size() override { return m_context.get_unsat_core_size(); } - expr* get_unsat_core_expr(unsigned i) override { return m_context.get_unsat_core_expr(i); } - }; - - class smt_context_manager { - smt_params& m_fparams; - ast_manager& m; - unsigned m_max_num_contexts; - ptr_vector m_contexts; - unsigned m_num_contexts; - app_ref_vector m_predicate_list; - func_decl_set m_predicate_set; - public: - smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m); - ~smt_context_manager(); - smt_context* mk_fresh(); - void collect_statistics(statistics& st) const; - void reset_statistics(); - bool is_aux_predicate(func_decl* p) const { return m_predicate_set.contains(p); } - }; - -}; - -#endif diff --git a/src/muz/pdr/pdr_sym_mux.cpp b/src/muz/pdr/pdr_sym_mux.cpp deleted file mode 100644 index 72549f2d6..000000000 --- a/src/muz/pdr/pdr_sym_mux.cpp +++ /dev/null @@ -1,601 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - sym_mux.cpp - -Abstract: - - A symbol multiplexer that helps with having multiple versions of each of a set of symbols. - -Author: - - Krystof Hoder (t-khoder) 2011-9-8. - -Revision History: - ---*/ - -#include -#include "ast/ast_pp.h" -#include "ast/for_each_expr.h" -#include "model/model.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "muz/pdr/pdr_util.h" -#include "muz/pdr/pdr_sym_mux.h" - -using namespace pdr; - -sym_mux::sym_mux(ast_manager & m) - : m(m), m_ref_holder(m), - m_next_sym_suffix_idx(0) { - m_suffixes.push_back("_n"); - size_t suf_sz = m_suffixes.size(); - for(unsigned i = 0; i < suf_sz; ++i) { - symbol suff_sym = symbol(m_suffixes[i].c_str()); - m_used_suffixes.insert(suff_sym); - } -} - -std::string sym_mux::get_suffix(unsigned i) { - while(m_suffixes.size() <= i) { - std::string new_suffix; - symbol new_syffix_sym; - do { - std::stringstream stm; - stm<<'_'<0); - while(tuple.size()get_name().str(); - for(unsigned i=0; iget_arity()==arity); - SASSERT(tuple[i]->get_range()==range); - //domain should match as well, but we won't bother checking an array equality - } - else { - std::string name = pre+get_suffix(i); - tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range); - } - m_ref_holder.push_back(tuple[i]); - m_sym2idx.insert(tuple[i], i); - m_sym2prim.insert(tuple[i], tuple[0]); - } - - m_prim2all.insert(tuple[0], tuple); - m_prefix2prim.insert(prefix, tuple[0]); - m_prim2prefix.insert(tuple[0], prefix); - m_prim_preds.push_back(tuple[0]); - m_ref_holder.push_back(prefix); -} - -void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) { - SASSERT(m_prim2all.contains(prim)); - decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value; - SASSERT(tuple[0]==prim); - - if(sz <= tuple.size()) { return; } - - func_decl * prefix; - TRUSTME(m_prim2prefix.find(prim, prefix)); - std::string prefix_name = prefix->get_name().bare_str(); - for(unsigned i = tuple.size(); i < sz; ++i) { - std::string name = prefix_name + get_suffix(i); - func_decl * new_sym = m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(), - prefix->get_domain(), prefix->get_range()); - - tuple.push_back(new_sym); - m_ref_holder.push_back(new_sym); - m_sym2idx.insert(new_sym, i); - m_sym2prim.insert(new_sym, prim); - } -} - -func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) -{ - if(src_idx==tgt_idx) { return sym; } - func_decl * prim = (src_idx==0) ? sym : get_primary(sym); - if(tgt_idx>src_idx) { - ensure_tuple_size(prim, tgt_idx+1); - } - decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value; - SASSERT(sym_vect[src_idx]==sym); - return sym_vect[tgt_idx]; -} - - -func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, - unsigned arity, sort * const * domain, sort * range) -{ - func_decl * prim = try_get_primary_by_prefix(prefix); - if(prim) { - SASSERT(prim->get_arity()==arity); - SASSERT(prim->get_range()==range); - //domain should match as well, but we won't bother checking an array equality - - return conv(prim, 0, idx); - } - - decl_vector syms; - create_tuple(prefix, arity, domain, range, idx+1, syms); - return syms[idx]; -} - -bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const -{ - if(!is_app(e)) { return false; } - app * a = to_app(e); - if(m.is_not(a) && is_app(a->get_arg(0))) { - a = to_app(a->get_arg(0)); - } - return is_muxed(a->get_decl()); -} - - -struct sym_mux::formula_checker -{ - formula_checker(const sym_mux & parent, bool all, unsigned idx) : - m_parent(parent), m_all(all), m_idx(idx), - m_found_what_needed(false) - { - } - - void operator()(expr * e) - { - if(m_found_what_needed || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - unsigned sym_idx; - if(!m_parent.try_get_index(sym, sym_idx)) { return; } - - bool have_idx = sym_idx==m_idx; - - if( m_all ? (!have_idx) : have_idx ) { - m_found_what_needed = true; - } - - } - - bool all_have_idx() const - { - SASSERT(m_all); //we were looking for the queried property - return !m_found_what_needed; - } - - bool some_with_idx() const - { - SASSERT(!m_all); //we were looking for the queried property - return m_found_what_needed; - } - -private: - const sym_mux & m_parent; - bool m_all; - unsigned m_idx; - - /** - If we check whether all muxed symbols are of given index, we look for - counter-examples, checking whether form contains a muxed symbol of an index, - we look for symbol of index m_idx. - */ - bool m_found_what_needed; -}; - -bool sym_mux::contains(expr * e, unsigned idx) const -{ - formula_checker chck(*this, false, idx); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return chck.some_with_idx(); -} - -bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const -{ - formula_checker chck(*this, true, idx); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return chck.all_have_idx(); -} - -bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const -{ - expr * const * begin = vect.c_ptr(); - expr * const * end = begin + vect.size(); - for(expr * const * it = begin; it!=end; it++) { - if(!is_homogenous_formula(*it, idx)) { - return false; - } - } - return true; -} - -class sym_mux::index_collector { - sym_mux const& m_parent; - svector m_indices; -public: - index_collector(sym_mux const& s): - m_parent(s) {} - - void operator()(expr * e) { - if (is_app(e)) { - func_decl * sym = to_app(e)->get_decl(); - unsigned idx; - if (m_parent.try_get_index(sym, idx)) { - SASSERT(idx > 0); - --idx; - if (m_indices.size() <= idx) { - m_indices.resize(idx+1, false); - } - m_indices[idx] = true; - } - } - } - - void extract(unsigned_vector& indices) { - for (unsigned i = 0; i < m_indices.size(); ++i) { - if (m_indices[i]) { - indices.push_back(i); - } - } - } -}; - - - -void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const { - indices.reset(); - index_collector collector(*this); - for_each_expr(collector, m_visited, e); - m_visited.reset(); - collector.extract(indices); -} - -class sym_mux::variable_collector { - sym_mux const& m_parent; - vector >& m_vars; -public: - variable_collector(sym_mux const& s, vector >& vars): - m_parent(s), m_vars(vars) {} - - void operator()(expr * e) { - if (is_app(e)) { - func_decl * sym = to_app(e)->get_decl(); - unsigned idx; - if (m_parent.try_get_index(sym, idx)) { - SASSERT(idx > 0); - --idx; - if (m_vars.size() <= idx) { - m_vars.resize(idx+1, ptr_vector()); - } - m_vars[idx].push_back(to_app(e)); - } - } - } -}; - -void sym_mux::collect_variables(expr* e, vector >& vars) const { - vars.reset(); - variable_collector collector(*this, vars); - for_each_expr(collector, m_visited, e); - m_visited.reset(); -} - -class sym_mux::hmg_checker { - const sym_mux & m_parent; - - bool m_found_idx; - unsigned m_idx; - bool m_multiple_indexes; - -public: - hmg_checker(const sym_mux & parent) : - m_parent(parent), m_found_idx(false), m_multiple_indexes(false) - { - } - - void operator()(expr * e) - { - if(m_multiple_indexes || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - unsigned sym_idx; - if(!m_parent.try_get_index(sym, sym_idx)) { return; } - - if(!m_found_idx) { - m_found_idx = true; - m_idx = sym_idx; - return; - } - if(m_idx==sym_idx) { return; } - m_multiple_indexes = true; - } - - bool has_multiple_indexes() const - { - return m_multiple_indexes; - } -}; - -bool sym_mux::is_homogenous_formula(expr * e) const { - hmg_checker chck(*this); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return !chck.has_multiple_indexes(); -} - - -struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg -{ -private: - ast_manager & m; - sym_mux & m_parent; - unsigned m_from_idx; - unsigned m_to_idx; - bool m_homogenous; -public: - conv_rewriter_cfg(sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous) - : m(parent.get_manager()), - m_parent(parent), - m_from_idx(from_idx), - m_to_idx(to_idx), - m_homogenous(homogenous) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - if(!is_app(s)) { return false; } - app * a = to_app(s); - func_decl * sym = a->get_decl(); - if(!m_parent.has_index(sym, m_from_idx)) { - (void) m_homogenous; - SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); - return false; - } - func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx); - - t = m.mk_app(tgt, a->get_args()); - return true; - } -}; - -void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) -{ - if(src_idx==tgt_idx) { - res = f; - return; - } - conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); - rewriter_tpl rwr(m, false, r_cfg); - rwr(f, res); -} - -struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg -{ -private: - ast_manager & m; - sym_mux & m_parent; - int m_shift; -public: - shifting_rewriter_cfg(sym_mux & parent, int shift) - : m(parent.get_manager()), - m_parent(parent), - m_shift(shift) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - if(!is_app(s)) { return false; } - app * a = to_app(s); - func_decl * sym = a->get_decl(); - - unsigned idx; - if(!m_parent.try_get_index(sym, idx)) { - return false; - } - SASSERT(static_cast(idx)+m_shift>=0); - func_decl * tgt = m_parent.conv(sym, idx, idx+m_shift); - t = m.mk_app(tgt, a->get_args()); - return true; - } -}; - -void sym_mux::shift_formula(expr * f, int dist, expr_ref & res) -{ - if(dist==0) { - res = f; - return; - } - shifting_rewriter_cfg r_cfg(*this, dist); - rewriter_tpl rwr(m, false, r_cfg); - rwr(f, res); -} - -void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, - expr_ref_vector & res) -{ - res.reset(); - expr * const * begin = vect.c_ptr(); - expr * const * end = begin + vect.size(); - for(expr * const * it = begin; it!=end; it++) { - expr_ref converted(m); - conv_formula(*it, src_idx, tgt_idx, converted); - res.push_back(converted); - } -} - -void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const { - unsigned i = 0; - while (i < vect.size()) { - expr* e = vect[i].get(); - if (contains(e, idx) && is_homogenous_formula(e, idx)) { - i++; - } - else { - // we don't allow mixing states inside vector elements - SASSERT(!contains(e, idx)); - vect[i] = vect.back(); - vect.pop_back(); - } - } -} - -void sym_mux::partition_o_idx( - expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, unsigned idx) const { - - for (unsigned i = 0; i < lits.size(); ++i) { - if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) { - o_lits.push_back(lits[i]); - } - else { - other.push_back(lits[i]); - } - } -} - - - -class sym_mux::nonmodel_sym_checker { - const sym_mux & m_parent; - - bool m_found; -public: - nonmodel_sym_checker(const sym_mux & parent) : - m_parent(parent), m_found(false) { - } - - void operator()(expr * e) { - if(m_found || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - - if(m_parent.is_non_model_sym(sym)) { - m_found = true; - } - } - - bool found() const { - return m_found; - } -}; - -bool sym_mux::has_nonmodel_symbol(expr * e) const { - nonmodel_sym_checker chck(*this); - for_each_expr(chck, e); - return chck.found(); -} - -void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const { - unsigned i = 0; - while (i < vect.size()) { - if (!has_nonmodel_symbol(vect[i].get())) { - i++; - } - else { - vect[i] = vect.back(); - vect.pop_back(); - } - } -} - -class sym_mux::decl_idx_comparator -{ - const sym_mux & m_parent; -public: - decl_idx_comparator(const sym_mux & parent) - : m_parent(parent) - { } - - bool operator()(func_decl * sym1, func_decl * sym2) - { - unsigned idx1, idx2; - if (!m_parent.try_get_index(sym1, idx1)) { idx1 = UINT_MAX; } - if (!m_parent.try_get_index(sym2, idx2)) { idx2 = UINT_MAX; } - - if (idx1 != idx2) { return idx1get_name(), sym2->get_name()); - } -}; - -std::string sym_mux::pp_model(const model_core & mdl) const { - decl_vector consts; - unsigned sz = mdl.get_num_constants(); - for (unsigned i = 0; i < sz; i++) { - func_decl * d = mdl.get_constant(i); - consts.push_back(d); - } - - std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this)); - - std::stringstream res; - - decl_vector::iterator end = consts.end(); - for (decl_vector::iterator it = consts.begin(); it!=end; it++) { - func_decl * d = *it; - std::string name = d->get_name().str(); - const char * arrow = " -> "; - res << name << arrow; - unsigned indent = static_cast(name.length() + strlen(arrow)); - res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n"; - - if (it+1 != end) { - unsigned idx1, idx2; - if (!try_get_index(*it, idx1)) { idx1 = UINT_MAX; } - if (!try_get_index(*(it+1), idx2)) { idx2 = UINT_MAX; } - if (idx1 != idx2) { res << "\n"; } - } - } - return res.str(); -} - - -#if 0 - -class sym_mux::index_renamer_cfg : public default_rewriter_cfg{ - const sym_mux & m_parent; - unsigned m_idx; - -public: - index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - if (!is_app(s)) return false; - app * a = to_app(s); - if (a->get_family_id() != null_family_id) { - return false; - } - func_decl * sym = a->get_decl(); - unsigned idx; - if(!m_parent.try_get_index(sym, idx)) { - return false; - } - if (m_idx == idx) { - return false; - } - ast_manager& m = m_parent.get_manager(); - symbol name = symbol((sym->get_name().str() + "!").c_str()); - func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range()); - t = m.mk_app(tgt, a->get_num_args(), a->get_args()); - return true; - } -}; - -#endif diff --git a/src/muz/pdr/pdr_sym_mux.h b/src/muz/pdr/pdr_sym_mux.h deleted file mode 100644 index 64a2878a9..000000000 --- a/src/muz/pdr/pdr_sym_mux.h +++ /dev/null @@ -1,247 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - sym_mux.h - -Abstract: - - A symbol multiplexer that helps with having multiple versions of each of a set of symbols. - -Author: - - Krystof Hoder (t-khoder) 2011-9-8. - -Revision History: - ---*/ - -#ifndef SYM_MUX_H_ -#define SYM_MUX_H_ - -#include "ast/ast.h" -#include "util/map.h" -#include "util/vector.h" -#include - -class model_core; - -namespace pdr { -class sym_mux -{ -public: - typedef ptr_vector app_vector; - typedef ptr_vector decl_vector; -private: - typedef obj_map sym2u; - typedef obj_map sym2dv; - typedef obj_map sym2sym; - typedef obj_map sym2pred; - typedef hashtable symbols; - - ast_manager & m; - mutable ast_ref_vector m_ref_holder; - mutable expr_mark m_visited; - - mutable unsigned m_next_sym_suffix_idx; - mutable symbols m_used_suffixes; - /** Here we have default suffixes for each of the variants */ - std::vector m_suffixes; - - - /** - Primary symbol is the 0-th variant. This member maps from primary symbol - to vector of all its variants (including the primary variant). - */ - sym2dv m_prim2all; - - /** - For each symbol contains its variant index - */ - mutable sym2u m_sym2idx; - /** - For each symbol contains its primary variant - */ - mutable sym2sym m_sym2prim; - - /** - Maps prefixes passed to the create_tuple to - the primary symbol created from it. - */ - sym2pred m_prefix2prim; - - /** - Maps pripary symbols to prefixes that were used to create them. - */ - sym2sym m_prim2prefix; - - decl_vector m_prim_preds; - - obj_hashtable m_non_model_syms; - - struct formula_checker; - struct conv_rewriter_cfg; - struct shifting_rewriter_cfg; - class decl_idx_comparator; - class hmg_checker; - class nonmodel_sym_checker; - class index_renamer_cfg; - class index_collector; - class variable_collector; - - std::string get_suffix(unsigned i); - void ensure_tuple_size(func_decl * prim, unsigned sz); - -public: - sym_mux(ast_manager & m); - - ast_manager & get_manager() const { return m; } - - bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); } - - bool try_get_index(func_decl * sym, unsigned & idx) const { - return m_sym2idx.find(sym,idx); - } - - bool has_index(func_decl * sym, unsigned idx) const { - unsigned actual_idx; - return try_get_index(sym, actual_idx) && idx==actual_idx; - } - - /** Return primary symbol. sym must be muxed. */ - func_decl * get_primary(func_decl * sym) const { - func_decl * prim; - TRUSTME(m_sym2prim.find(sym, prim)); - return prim; - } - - /** - Return primary symbol created from prefix, or 0 if the prefix was never used. - */ - func_decl * try_get_primary_by_prefix(func_decl* prefix) const { - func_decl * res; - if(!m_prefix2prim.find(prefix, res)) { - return nullptr; - } - return res; - } - - /** - Return symbol created from prefix, or 0 if the prefix was never used. - */ - func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) { - func_decl * prim = try_get_primary_by_prefix(prefix); - if(!prim) { - return nullptr; - } - return conv(prim, 0, idx); - } - - /** - Marks symbol as non-model which means it will not appear in models collected by - get_muxed_cube_from_model function. - This is to take care of auxiliary symbols introduced by the disjunction relations - to relativize lemmas coming from disjuncts. - */ - void mark_as_non_model(func_decl * sym) { - SASSERT(is_muxed(sym)); - m_non_model_syms.insert(get_primary(sym)); - } - - func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, - unsigned arity, sort * const * domain, sort * range); - - - - bool is_muxed_lit(expr * e, unsigned idx) const; - - bool is_non_model_sym(func_decl * s) const { - return is_muxed(s) && m_non_model_syms.contains(get_primary(s)); - } - - /** - Create a multiplexed tuple of propositional constants. - Symbols may be suplied in the tuple vector, - those beyond the size of the array and those with corresponding positions - assigned to zero will be created using prefix. - Tuple length must be at least one. - */ - void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, - unsigned tuple_length, decl_vector & tuple); - - /** - Return true if the only multiplexed symbols which e contains are of index idx. - */ - bool is_homogenous_formula(expr * e, unsigned idx) const; - bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const; - - /** - Return true if all multiplexed symbols which e contains are of one index. - */ - bool is_homogenous_formula(expr * e) const; - - /** - Return true if expression e contains a muxed symbol of index idx. - */ - bool contains(expr * e, unsigned idx) const; - - /** - Collect indices used in expression. - */ - void collect_indices(expr* e, unsigned_vector& indices) const; - - /** - Collect used variables of each index. - */ - void collect_variables(expr* e, vector >& vars) const; - - /** - Convert symbol sym which has to be of src_idx variant into variant tgt_idx. - */ - func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx); - - - /** - Convert src_idx symbols in formula f variant into tgt_idx. - If homogenous is true, formula cannot contain symbols of other variants. - */ - void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous=true); - void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, - expr_ref_vector & res); - - /** - Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift - symbol index to a negative value. - */ - void shift_formula(expr * f, int dist, expr_ref & res); - - /** - Remove from vect literals (atoms or negations of atoms) of symbols - that contain multiplexed symbols with indexes other than idx. - - Each of the literals can contain only symbols multiplexed with one index - (this trivially holds if the literals are propositional). - - Order of elements in vect may be modified by this function - */ - void filter_idx(expr_ref_vector & vect, unsigned idx) const; - - /** - Partition literals into o_literals and others. - */ - void partition_o_idx(expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, unsigned idx) const; - - bool has_nonmodel_symbol(expr * e) const; - void filter_non_model_lits(expr_ref_vector & vect) const; - - func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); } - func_decl * const * end_prim_preds() const { return m_prim_preds.end(); } - - std::string pp_model(const model_core & mdl) const; -}; -} - -#endif diff --git a/src/muz/pdr/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp deleted file mode 100644 index ad75ae799..000000000 --- a/src/muz/pdr/pdr_util.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_util.cpp - -Abstract: - - Utility functions for PDR. - -Author: - - Krystof Hoder (t-khoder) 2011-8-19. - -Revision History: - - -Notes: - - ---*/ - -#include -#include "util/util.h" -#include "util/ref_vector.h" -#include "ast/array_decl_plugin.h" -#include "ast/ast_pp.h" -#include "ast/for_each_expr.h" -#include "ast/scoped_proof.h" -#include "ast/arith_decl_plugin.h" -#include "ast/rewriter/expr_replacer.h" -#include "ast/rewriter/bool_rewriter.h" -#include "ast/rewriter/poly_rewriter.h" -#include "ast/rewriter/poly_rewriter_def.h" -#include "ast/rewriter/arith_rewriter.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "smt/params/smt_params.h" -#include "model/model.h" -#include "muz/base/dl_util.h" -#include "muz/pdr/pdr_manager.h" -#include "muz/pdr/pdr_util.h" -#include "model/model_smt2_pp.h" - - - -namespace pdr { - - unsigned ceil_log2(unsigned u) { - if (u == 0) { return 0; } - unsigned pow2 = next_power_of_two(u); - return get_num_1bits(pow2-1); - } - - std::string pp_cube(const ptr_vector& model, ast_manager& m) { - return pp_cube(model.size(), model.c_ptr(), m); - } - - std::string pp_cube(const expr_ref_vector& model, ast_manager& m) { - return pp_cube(model.size(), model.c_ptr(), m); - } - - std::string pp_cube(const app_ref_vector& model, ast_manager& m) { - return pp_cube(model.size(), model.c_ptr(), m); - } - - std::string pp_cube(const app_vector& model, ast_manager& m) { - return pp_cube(model.size(), model.c_ptr(), m); - } - - std::string pp_cube(unsigned sz, app * const * lits, ast_manager& m) { - return pp_cube(sz, (expr * const *)(lits), m); - } - - std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& m) { - std::stringstream res; - res << "("; - expr * const * end = lits+sz; - for (expr * const * it = lits; it!=end; it++) { - res << mk_pp(*it, m); - if (it+1!=end) { - res << ", "; - } - } - res << ")"; - return res.str(); - } - - void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { - ast_manager& m = fml.get_manager(); - expr_ref_vector conjs(m); - flatten_and(fml, conjs); - obj_map diseqs; - expr* n, *lhs, *rhs; - for (unsigned i = 0; i < conjs.size(); ++i) { - if (m.is_not(conjs[i].get(), n) && - m.is_eq(n, lhs, rhs)) { - if (!m.is_value(rhs)) { - std::swap(lhs, rhs); - } - if (!m.is_value(rhs)) { - continue; - } - diseqs.insert_if_not_there2(lhs, 0)->get_data().m_value++; - } - } - expr_substitution sub(m); - - unsigned orig_size = conjs.size(); - unsigned num_deleted = 0; - expr_ref val(m), tmp(m); - proof_ref pr(m); - pr = m.mk_asserted(m.mk_true()); - obj_map::iterator it = diseqs.begin(); - obj_map::iterator end = diseqs.end(); - for (; it != end; ++it) { - if (it->m_value >= threshold) { - model.eval(it->m_key, val); - sub.insert(it->m_key, val, pr); - conjs.push_back(m.mk_eq(it->m_key, val)); - num_deleted += it->m_value; - } - } - if (orig_size < conjs.size()) { - scoped_ptr rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - for (unsigned i = 0; i < orig_size; ++i) { - tmp = conjs[i].get(); - (*rep)(tmp); - if (m.is_true(tmp)) { - conjs[i] = conjs.back(); - SASSERT(orig_size <= conjs.size()); - conjs.pop_back(); - SASSERT(orig_size <= 1 + conjs.size()); - if (i + 1 == orig_size) { - // no-op. - } - else if (orig_size <= conjs.size()) { - // no-op - } - else { - SASSERT(orig_size == 1 + conjs.size()); - --orig_size; - --i; - } - } - else { - conjs[i] = tmp; - } - } - IF_VERBOSE(2, verbose_stream() << "Deleted " << num_deleted << " disequalities " << conjs.size() << " conjuncts\n";); - } - fml = m.mk_and(conjs.size(), conjs.c_ptr()); - } - - class test_diff_logic { - ast_manager& m; - arith_util a; - bv_util bv; - bool m_is_dl; - bool m_test_for_utvpi; - - bool is_numeric(expr* e) const { - if (a.is_numeral(e)) { - return true; - } - expr* cond, *th, *el; - if (m.is_ite(e, cond, th, el)) { - return is_numeric(th) && is_numeric(el); - } - return false; - } - - bool is_arith_expr(expr *e) const { - return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); - } - - bool is_offset(expr* e) const { - if (a.is_numeral(e)) { - return true; - } - expr* cond, *th, *el, *e1, *e2; - if (m.is_ite(e, cond, th, el)) { - return is_offset(th) && is_offset(el); - } - // recognize offsets. - if (a.is_add(e, e1, e2)) { - if (is_numeric(e1)) { - return is_offset(e2); - } - if (is_numeric(e2)) { - return is_offset(e1); - } - return false; - } - if (m_test_for_utvpi) { - if (a.is_mul(e, e1, e2)) { - if (is_minus_one(e1)) { - return is_offset(e2); - } - if (is_minus_one(e2)) { - return is_offset(e1); - } - } - } - return !is_arith_expr(e); - } - - bool is_minus_one(expr const * e) const { - rational r; return a.is_numeral(e, r) && r.is_minus_one(); - } - - bool test_ineq(expr* e) const { - SASSERT(a.is_le(e) || a.is_ge(e) || m.is_eq(e)); - SASSERT(to_app(e)->get_num_args() == 2); - expr * lhs = to_app(e)->get_arg(0); - expr * rhs = to_app(e)->get_arg(1); - if (is_offset(lhs) && is_offset(rhs)) - return true; - if (!is_numeric(rhs)) - std::swap(lhs, rhs); - if (!is_numeric(rhs)) - return false; - // lhs can be 'x' or '(+ x (* -1 y))' - if (is_offset(lhs)) - return true; - expr* arg1, *arg2; - if (!a.is_add(lhs, arg1, arg2)) - return false; - // x - if (m_test_for_utvpi) { - return is_offset(arg1) && is_offset(arg2); - } - if (is_arith_expr(arg1)) - std::swap(arg1, arg2); - if (is_arith_expr(arg1)) - return false; - // arg2: (* -1 y) - expr* m1, *m2; - if (!a.is_mul(arg2, m1, m2)) - return false; - return is_minus_one(m1) && is_offset(m2); - } - - bool test_eq(expr* e) const { - expr* lhs = nullptr, *rhs = nullptr; - VERIFY(m.is_eq(e, lhs, rhs)); - if (!a.is_int_real(lhs)) { - return true; - } - if (a.is_numeral(lhs) || a.is_numeral(rhs)) { - return test_ineq(e); - } - return - test_term(lhs) && - test_term(rhs) && - !a.is_mul(lhs) && - !a.is_mul(rhs); - } - - bool test_term(expr* e) const { - if (m.is_bool(e)) { - return true; - } - if (a.is_numeral(e)) { - return true; - } - if (is_offset(e)) { - return true; - } - expr* lhs, *rhs; - if (a.is_add(e, lhs, rhs)) { - if (!a.is_numeral(lhs)) { - std::swap(lhs, rhs); - } - return a.is_numeral(lhs) && is_offset(rhs); - } - if (a.is_mul(e, lhs, rhs)) { - return is_minus_one(lhs) || is_minus_one(rhs); - } - return false; - } - - bool is_non_arith_or_basic(expr* e) { - if (!is_app(e)) { - return false; - } - family_id fid = to_app(e)->get_family_id(); - - if (fid == null_family_id && - !m.is_bool(e) && - to_app(e)->get_num_args() > 0) { - return true; - } - return - fid != m.get_basic_family_id() && - fid != null_family_id && - fid != a.get_family_id() && - fid != bv.get_family_id(); - } - - public: - test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - - void test_for_utvpi() { m_test_for_utvpi = true; } - - void operator()(expr* e) { - if (!m_is_dl) { - return; - } - if (a.is_le(e) || a.is_ge(e)) { - m_is_dl = test_ineq(e); - } - else if (m.is_eq(e)) { - m_is_dl = test_eq(e); - } - else if (is_non_arith_or_basic(e)) { - m_is_dl = false; - } - else if (is_app(e)) { - app* a = to_app(e); - for (unsigned i = 0; m_is_dl && i < a->get_num_args(); ++i) { - m_is_dl = test_term(a->get_arg(i)); - } - } - - if (!m_is_dl) { - char const* msg = "non-diff: "; - if (m_test_for_utvpi) { - msg = "non-utvpi: "; - } - IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); - } - } - - bool is_dl() const { return m_is_dl; } - }; - - bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { - test_diff_logic test(m); - expr_fast_mark1 mark; - for (unsigned i = 0; i < num_fmls; ++i) { - quick_for_each_expr(test, mark, fmls[i]); - } - return test.is_dl(); - } - - bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { - test_diff_logic test(m); - test.test_for_utvpi(); - expr_fast_mark1 mark; - for (unsigned i = 0; i < num_fmls; ++i) { - quick_for_each_expr(test, mark, fmls[i]); - } - return test.is_dl(); - } - - class arith_normalizer : public poly_rewriter { - ast_manager& m; - arith_util m_util; - enum op_kind { LE, GE, EQ }; - public: - arith_normalizer(ast_manager& m, params_ref const& p = params_ref()): poly_rewriter(m, p), m(m), m_util(m) {} - - br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { - br_status st = BR_FAILED; - if (m.is_eq(f)) { - SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result); - } - - if (f->get_family_id() != get_fid()) { - return st; - } - switch (f->get_decl_kind()) { - case OP_NUM: st = BR_FAILED; break; - case OP_IRRATIONAL_ALGEBRAIC_NUM: st = BR_FAILED; break; - case OP_LE: SASSERT(num_args == 2); st = mk_le_core(args[0], args[1], result); break; - case OP_GE: SASSERT(num_args == 2); st = mk_ge_core(args[0], args[1], result); break; - case OP_LT: SASSERT(num_args == 2); st = mk_lt_core(args[0], args[1], result); break; - case OP_GT: SASSERT(num_args == 2); st = mk_gt_core(args[0], args[1], result); break; - default: st = BR_FAILED; break; - } - return st; - } - - private: - - br_status mk_eq_core(expr* arg1, expr* arg2, expr_ref& result) { - return mk_le_ge_eq_core(arg1, arg2, EQ, result); - } - br_status mk_le_core(expr* arg1, expr* arg2, expr_ref& result) { - return mk_le_ge_eq_core(arg1, arg2, LE, result); - } - br_status mk_ge_core(expr* arg1, expr* arg2, expr_ref& result) { - return mk_le_ge_eq_core(arg1, arg2, GE, result); - } - br_status mk_lt_core(expr* arg1, expr* arg2, expr_ref& result) { - result = m.mk_not(m_util.mk_ge(arg1, arg2)); - return BR_REWRITE2; - } - br_status mk_gt_core(expr* arg1, expr* arg2, expr_ref& result) { - result = m.mk_not(m_util.mk_le(arg1, arg2)); - return BR_REWRITE2; - } - - br_status mk_le_ge_eq_core(expr* arg1, expr* arg2, op_kind kind, expr_ref& result) { - if (m_util.is_real(arg1)) { - numeral g(0); - get_coeffs(arg1, g); - get_coeffs(arg2, g); - if (!g.is_one() && !g.is_zero()) { - SASSERT(g.is_pos()); - expr_ref new_arg1 = rdiv_polynomial(arg1, g); - expr_ref new_arg2 = rdiv_polynomial(arg2, g); - switch(kind) { - case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_DONE; - case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_DONE; - case EQ: result = m_util.mk_eq(new_arg1, new_arg2); return BR_DONE; - } - } - } - return BR_FAILED; - } - - void update_coeff(numeral const& r, numeral& g) { - if (g.is_zero() || abs(r) < g) { - g = abs(r); - } - } - - void get_coeffs(expr* e, numeral& g) { - rational r; - unsigned sz; - expr* const* args = get_monomials(e, sz); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = args[i]; - if (!m_util.is_numeral(arg, r)) { - get_power_product(arg, r); - } - update_coeff(r, g); - } - } - - expr_ref rdiv_polynomial(expr* e, numeral const& g) { - rational r; - SASSERT(g.is_pos()); - SASSERT(!g.is_one()); - expr_ref_vector monomes(m); - unsigned sz; - expr* const* args = get_monomials(e, sz); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = args[i]; - if (m_util.is_numeral(arg, r)) { - monomes.push_back(m_util.mk_numeral(r/g, false)); - } - else { - expr* p = get_power_product(arg, r); - r /= g; - if (r.is_one()) { - monomes.push_back(p); - } - else { - monomes.push_back(m_util.mk_mul(m_util.mk_numeral(r, false), p)); - } - } - } - expr_ref result(m); - mk_add(monomes.size(), monomes.c_ptr(), result); - return result; - } - - }; - - - struct arith_normalizer_cfg: public default_rewriter_cfg { - arith_normalizer m_r; - bool rewrite_patterns() const { return false; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - return m_r.mk_app_core(f, num, args, result); - } - arith_normalizer_cfg(ast_manager & m, params_ref const & p):m_r(m,p) {} - }; - - class arith_normalizer_star : public rewriter_tpl { - arith_normalizer_cfg m_cfg; - public: - arith_normalizer_star(ast_manager & m, params_ref const & p): - rewriter_tpl(m, false, m_cfg), - m_cfg(m, p) {} - }; - - - void normalize_arithmetic(expr_ref& t) { - ast_manager& m = t.get_manager(); - scoped_no_proof _sp(m); - params_ref p; - arith_normalizer_star rw(m, p); - expr_ref tmp(m); - rw(t, tmp); - t = tmp; - } - -} - -template class rewriter_tpl; - - diff --git a/src/muz/pdr/pdr_util.h b/src/muz/pdr/pdr_util.h deleted file mode 100644 index 51f6978ec..000000000 --- a/src/muz/pdr/pdr_util.h +++ /dev/null @@ -1,81 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_util.h - -Abstract: - - Utility functions for PDR. - -Author: - - Krystof Hoder (t-khoder) 2011-8-19. - -Revision History: - ---*/ - -#ifndef PDR_UTIL_H_ -#define PDR_UTIL_H_ - -#include "ast/ast.h" -#include "ast/ast_pp.h" -#include "ast/ast_util.h" -#include "util/obj_hashtable.h" -#include "util/ref_vector.h" -#include "util/trace.h" -#include "util/vector.h" -#include "ast/arith_decl_plugin.h" -#include "ast/array_decl_plugin.h" -#include "ast/bv_decl_plugin.h" - - -class model; -class model_core; - -namespace pdr { - - /** - * Return the ceiling of base 2 logarithm of a number, - * or zero if the nmber is zero. - */ - unsigned ceil_log2(unsigned u); - - typedef ptr_vector app_vector; - typedef ptr_vector decl_vector; - typedef obj_hashtable func_decl_set; - - std::string pp_cube(const ptr_vector& model, ast_manager& manager); - std::string pp_cube(const expr_ref_vector& model, ast_manager& manager); - std::string pp_cube(const ptr_vector& model, ast_manager& manager); - std::string pp_cube(const app_ref_vector& model, ast_manager& manager); - std::string pp_cube(unsigned sz, app * const * lits, ast_manager& manager); - std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& manager); - - - /** - \brief replace variables that are used in many disequalities by - an equality using the model. - - Assumption: the model satisfies the conjunctions. - */ - void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); - - /** - \brief normalize coefficients in polynomials so that least coefficient is 1. - */ - void normalize_arithmetic(expr_ref& t); - - - /** - \brief determine if formulas belong to difference logic or UTVPI fragment. - */ - bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); - - bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); - -} - -#endif From 4b2196f114b4db60fbd48fb3f389e324c1e5667f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Jun 2018 16:28:07 -0700 Subject: [PATCH 244/364] nits Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_unsat_core_learner.cpp | 3 +- src/muz/spacer/spacer_unsat_core_plugin.cpp | 82 +++++++---------- src/muz/spacer/spacer_unsat_core_plugin.h | 96 ++++++++------------ 3 files changed, 73 insertions(+), 108 deletions(-) diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index 5b3420800..da0d6ec34 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -44,8 +44,7 @@ void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { if (m.get_num_parents(currentNode) > 0) { bool need_to_mark_closed = true; - for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { - proof* premise = m.get_parent(currentNode, i); + for (proof* premise : m.get_parents(currentNode)) { need_to_mark_closed &= (!m_pr.is_b_marked(premise) || m_closed.is_marked(premise)); } diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index 25b12cf25..eff05762b 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -37,8 +37,6 @@ namespace spacer { unsat_core_plugin::unsat_core_plugin(unsat_core_learner& learner): m(learner.m), m_learner(learner) {}; - - void unsat_core_plugin_lemma::compute_partial_core(proof* step) { SASSERT(m_learner.m_pr.is_a_marked(step)); SASSERT(m_learner.m_pr.is_b_marked(step)); @@ -103,12 +101,10 @@ namespace spacer { func_decl* d = step->get_decl(); symbol sym; if (!m_learner.is_closed(step) && // if step is not already interpolated - step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma - d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas" && - d->get_num_parameters() >= m.get_num_parents(step) + 2) { // the following parameters are the Farkas coefficients + is_farkas_lemma(m, step)) { + // weaker check: d->get_num_parameters() >= m.get_num_parents(step) + 2 + SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); SASSERT(m.has_fact(step)); coeff_lits_t coeff_lits; @@ -142,12 +138,10 @@ namespace spacer { STRACE("spacer.farkas", verbose_stream() << "Farkas input: "<< "\n"; for (unsigned i = 0; i < m.get_num_parents(step); ++i) { - proof * prem = m.get_parent(step, i); - - rational coef = params[i].get_rational(); - + proof * prem = m.get_parent(step, i); + rational coef = params[i].get_rational(); bool b_pure = m_learner.m_pr.is_b_pure (prem); - verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m_learner.m) << "\n"; + verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; } ); @@ -197,11 +191,11 @@ namespace spacer { } SASSERT(m.get_num_parents(step) + 2 + num_args == d->get_num_parameters()); - bool_rewriter brw(m_learner.m); + bool_rewriter brw(m); for (unsigned i = 0; i < num_args; ++i) { expr* premise = args[i]; - expr_ref negatedPremise(m_learner.m); + expr_ref negatedPremise(m); brw.mk_not(premise, negatedPremise); pinned.push_back(negatedPremise); rational coefficient = params[i].get_rational(); @@ -235,8 +229,7 @@ namespace spacer { return util.get(); } else { - expr_ref negated_linear_combination = util.get(); - return expr_ref(mk_not(m, negated_linear_combination), m); + return expr_ref(mk_not(m, util.get()), m); } } @@ -248,15 +241,11 @@ namespace spacer { func_decl* d = step->get_decl(); symbol sym; if(!m_learner.is_closed(step) && // if step is not already interpolated - step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma - d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas" && - d->get_num_parameters() >= m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients - { + is_farkas_lemma(m, step)) { + SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); SASSERT(m.has_fact(step)); - vector > linear_combination; // collects all summands of the linear combination + coeff_lits_t linear_combination; // collects all summands of the linear combination parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient @@ -264,9 +253,7 @@ namespace spacer { verbose_stream() << "Farkas input: "<< "\n"; for (unsigned i = 0; i < m.get_num_parents(step); ++i) { proof * prem = m.get_parent(step, i); - - rational coef = params[i].get_rational(); - + rational coef = params[i].get_rational(); bool b_pure = m_learner.m_pr.is_b_pure (prem); verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m_learner.m) << "\n"; } @@ -284,8 +271,7 @@ namespace spacer { { rational coefficient = params[i].get_rational(); linear_combination.push_back - (std::make_pair(to_app(m.get_fact(premise)), - abs(coefficient))); + (std::make_pair(abs(coefficient), to_app(m.get_fact(premise)))); } else { @@ -308,15 +294,15 @@ namespace spacer { struct farkas_optimized_less_than_pairs { - inline bool operator() (const std::pair& pair1, const std::pair& pair2) const + inline bool operator() (const std::pair& pair1, const std::pair& pair2) const { - return (pair1.first->get_id() < pair2.first->get_id()); + return (pair1.second->get_id() < pair2.second->get_id()); } }; void unsat_core_plugin_farkas_lemma_optimized::finalize() { - if(m_linear_combinations.empty()) + if (m_linear_combinations.empty()) { return; } @@ -331,9 +317,9 @@ namespace spacer { unsigned counter = 0; for (const auto& linear_combination : m_linear_combinations) { for (const auto& pair : linear_combination) { - if (!map.contains(pair.first)) { - ordered_basis.push_back(pair.first); - map.insert(pair.first, counter++); + if (!map.contains(pair.second)) { + ordered_basis.push_back(pair.second); + map.insert(pair.second, counter++); } } } @@ -344,7 +330,7 @@ namespace spacer { for (unsigned i = 0; i < m_linear_combinations.size(); ++i) { auto linear_combination = m_linear_combinations[i]; for (const auto& pair : linear_combination) { - matrix.set(i, map[pair.first], pair.second); + matrix.set(i, map[pair.second], pair.first); } } @@ -368,9 +354,7 @@ namespace spacer { } - expr_ref unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const coeff_lits_t& coeff_lits) - { - + expr_ref unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const coeff_lits_t& coeff_lits) { smt::farkas_util util(m); for (auto const & p : coeff_lits) { util.add(p.first, p.second); @@ -387,7 +371,7 @@ namespace spacer { } DEBUG_CODE( for (auto& linear_combination : m_linear_combinations) { - SASSERT(linear_combination.size() > 0); + SASSERT(!linear_combination.empty()); }); // 1. construct ordered basis @@ -396,9 +380,9 @@ namespace spacer { unsigned counter = 0; for (const auto& linear_combination : m_linear_combinations) { for (const auto& pair : linear_combination) { - if (!map.contains(pair.first)) { - ordered_basis.push_back(pair.first); - map.insert(pair.first, counter++); + if (!map.contains(pair.second)) { + ordered_basis.push_back(pair.second); + map.insert(pair.second, counter++); } } } @@ -409,7 +393,7 @@ namespace spacer { for (unsigned i=0; i < m_linear_combinations.size(); ++i) { auto linear_combination = m_linear_combinations[i]; for (const auto& pair : linear_combination) { - matrix.set(i, map[pair.first], pair.second); + matrix.set(i, map[pair.second], pair.first); } } matrix.print_matrix(); @@ -695,14 +679,12 @@ namespace spacer { * computes min-cut on the graph constructed by compute_partial_core-method * and adds the corresponding lemmas to the core */ -void unsat_core_plugin_min_cut::finalize() -{ + void unsat_core_plugin_min_cut::finalize() { unsigned_vector cut_nodes; m_min_cut.compute_min_cut(cut_nodes); - - for (unsigned cut_node : cut_nodes) - { + + for (unsigned cut_node : cut_nodes) { m_learner.add_lemma_to_core(m_node_to_formula[cut_node]); - } - } + } + } } diff --git a/src/muz/spacer/spacer_unsat_core_plugin.h b/src/muz/spacer/spacer_unsat_core_plugin.h index 44a3678b6..c05bcc5b9 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.h +++ b/src/muz/spacer/spacer_unsat_core_plugin.h @@ -23,66 +23,55 @@ Revision History: namespace spacer { -class unsat_core_learner; + class unsat_core_learner; -class unsat_core_plugin { -protected: - typedef vector> coeff_lits_t; - ast_manager& m; -public: - unsat_core_plugin(unsat_core_learner& learner); - virtual ~unsat_core_plugin() {}; - virtual void compute_partial_core(proof* step) = 0; - virtual void finalize(){}; + class unsat_core_plugin { + protected: + typedef vector> coeff_lits_t; + ast_manager& m; + public: + unsat_core_plugin(unsat_core_learner& learner); + virtual ~unsat_core_plugin() {}; + virtual void compute_partial_core(proof* step) = 0; + virtual void finalize(){}; + + unsat_core_learner& m_learner; + }; - unsat_core_learner& m_learner; -}; - - -class unsat_core_plugin_lemma : public unsat_core_plugin { - -public: - unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){}; - - void compute_partial_core(proof* step) override; - -private: - void add_lowest_split_to_core(proof* step) const; -}; - - -class unsat_core_plugin_farkas_lemma : public unsat_core_plugin { - -public: - unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, - bool split_literals, - bool use_constant_from_a=true) : - unsat_core_plugin(learner), - m_split_literals(split_literals), - m_use_constant_from_a(use_constant_from_a) {}; - - void compute_partial_core(proof* step) override; - -private: - bool m_split_literals; - bool m_use_constant_from_a; - /* - * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res - */ - expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); -}; - - class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { + class unsat_core_plugin_lemma : public unsat_core_plugin { + public: + unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){}; + void compute_partial_core(proof* step) override; + private: + void add_lowest_split_to_core(proof* step) const; + }; + class unsat_core_plugin_farkas_lemma : public unsat_core_plugin { + public: + unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, + bool split_literals, + bool use_constant_from_a=true) : + unsat_core_plugin(learner), + m_split_literals(split_literals), + m_use_constant_from_a(use_constant_from_a) {}; + void compute_partial_core(proof* step) override; + private: + bool m_split_literals; + bool m_use_constant_from_a; + /* + * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res + */ + expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); + }; + + class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { public: unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner) {}; - void compute_partial_core(proof* step) override; void finalize() override; - protected: - vector > > m_linear_combinations; + vector m_linear_combinations; /* * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res */ @@ -90,22 +79,17 @@ private: }; class unsat_core_plugin_farkas_lemma_bounded : public unsat_core_plugin_farkas_lemma_optimized { - public: unsat_core_plugin_farkas_lemma_bounded(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin_farkas_lemma_optimized(learner, m) {}; - void finalize() override; }; class unsat_core_plugin_min_cut : public unsat_core_plugin { - public: unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m); - void compute_partial_core(proof* step) override; void finalize() override; private: - ast_mark m_visited; // saves for each node i whether the subproof with root i has already been added to the min-cut-problem obj_map m_proof_to_node_minus; // maps proof-steps to the corresponding minus-nodes (the ones which are closer to source) obj_map m_proof_to_node_plus; // maps proof-steps to the corresponding plus-nodes (the ones which are closer to sink) From 2a6b69437354433037c77a9900cc3ff8fff9d01c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 6 Jun 2018 21:32:08 -0700 Subject: [PATCH 245/364] Imrove hypothesis_reducer --- src/muz/spacer/spacer_proof_utils.cpp | 18 ++++++++++++------ src/muz/spacer/spacer_proof_utils.h | 4 ++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index a2a0ba6b6..bd011b3d6 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -201,10 +201,11 @@ void hypothesis_reducer::reset() { for (auto t : m_pinned_active_hyps) dealloc(t); m_pinned_active_hyps.reset(); m_pinned.reset(); + m_hyp_mark.reset(); } void hypothesis_reducer::compute_hypsets(proof *pr) { - ptr_vector todo; + ptr_buffer todo; todo.push_back(pr); while (!todo.empty()) { @@ -243,6 +244,7 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { if (m.is_hypothesis(p)) { active_hyps->insert(p); parent_hyps->insert(m.get_fact(p)); + m_hyp_mark.mark(m.get_fact(p)); } else { for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { @@ -260,8 +262,6 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { // collect all units that are hyp-free and are used as hypotheses somewhere // requires that m_active_hyps and m_parent_hyps have been computed void hypothesis_reducer::collect_units(proof* pr) { - expr_set* all_hyps = m_parent_hyps.find(pr); - SASSERT(all_hyps); proof_post_order pit(pr, m); while (pit.hasNext()) { @@ -273,12 +273,19 @@ void hypothesis_reducer::collect_units(proof* pr) { // collect units that are hyp-free and are used as // hypotheses in the proof pr if (active_hyps->empty() && m.has_fact(p) && - all_hyps->contains(m.get_fact(p))) + m_hyp_mark.is_marked(m.get_fact(p))) m_units.insert(m.get_fact(p), p); } } } +/** + \brief returns true if p is an ancestor of q + */ +bool hypothesis_reducer::is_ancestor(proof *p, proof *q) { + expr_set* parent_hyps = m_parent_hyps.find(q); + return parent_hyps->contains(m.get_fact(p)); +} proof* hypothesis_reducer::reduce_core(proof* pf) { SASSERT(m.is_false(m.get_fact(pf))); @@ -334,8 +341,7 @@ proof* hypothesis_reducer::reduce_core(proof* pf) { SASSERT(m_parent_hyps.contains(proof_of_unit)); // if the transformation doesn't create a cycle, perform it - expr_set* parent_hyps = m_parent_hyps.find(proof_of_unit); - if (!parent_hyps->contains(p)) { + if (!is_ancestor(p, proof_of_unit)) { res = proof_of_unit; } else { diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index 7ab022814..6c56c204e 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -84,8 +84,12 @@ private: // during proof transformation obj_map m_parent_hyps; + expr_mark m_hyp_mark; + void reset(); + /// true if p is an ancestor of q + bool is_ancestor(proof *p, proof *q); // compute active_hyps and parent_hyps for a given proof node and // all its ancestors void compute_hypsets(proof* pr); From a40e0dce0cee87a29f58d41d985e59cae5065d6d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 7 Jun 2018 08:50:31 -0700 Subject: [PATCH 246/364] proof_utils: use expr_mark instead of hashtable --- src/muz/spacer/spacer_proof_utils.cpp | 28 +++++++++++++-------------- src/muz/spacer/spacer_proof_utils.h | 4 ++++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index bd011b3d6..ca0e67f46 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -202,6 +202,8 @@ void hypothesis_reducer::reset() { m_pinned_active_hyps.reset(); m_pinned.reset(); m_hyp_mark.reset(); + m_open_mark.reset(); + m_visited.reset(); } void hypothesis_reducer::compute_hypsets(proof *pr) { @@ -211,8 +213,7 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { while (!todo.empty()) { proof* p = todo.back(); - if (m_active_hyps.contains(p)) { - SASSERT(m_parent_hyps.contains(p)); + if (m_visited.is_marked(p)) { todo.pop_back(); continue; } @@ -222,15 +223,15 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { SASSERT(m.is_proof(p->get_arg(i))); proof *parent = to_app(p->get_arg(i)); - if (!m_active_hyps.contains(parent)) { - SASSERT(!m_parent_hyps.contains(parent)); + if (!m_visited.is_marked(parent)) todo.push_back(parent); - } } if (todo.size() > todo_sz) continue; todo.pop_back(); + m_visited.mark(p); + // create active_hyps-set and parent_hyps-set for step p proof_set* active_hyps = alloc(proof_set); m_pinned_active_hyps.insert(active_hyps); @@ -243,6 +244,8 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { // fill both sets if (m.is_hypothesis(p)) { active_hyps->insert(p); + m_open_mark.mark(p); + parent_hyps->insert(m.get_fact(p)); m_hyp_mark.mark(m.get_fact(p)); } @@ -251,9 +254,11 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { proof* parent = m.get_parent(p, i); set_union(*parent_hyps, *m_parent_hyps.find(parent)); - if (!m.is_lemma(p)) + if (!m.is_lemma(p)) { // lemmas clear all hypotheses set_union(*active_hyps, *m_active_hyps.find(parent)); + m_open_mark.mark(p, !active_hyps->empty()); + } } } } @@ -267,12 +272,9 @@ void hypothesis_reducer::collect_units(proof* pr) { while (pit.hasNext()) { proof* p = pit.next(); if (!m.is_hypothesis(p)) { - proof_set* active_hyps = m_active_hyps.find(p); - SASSERT(active_hyps); - // collect units that are hyp-free and are used as // hypotheses in the proof pr - if (active_hyps->empty() && m.has_fact(p) && + if (!m_open_mark.is_marked(p) && m.has_fact(p) && m_hyp_mark.is_marked(m.get_fact(p))) m_units.insert(m.get_fact(p), p); } @@ -383,9 +385,7 @@ proof* hypothesis_reducer::reduce_core(proof* pf) { m_cache.insert(p, res); // bail out as soon as found a sub-proof of false - SASSERT(m_active_hyps.contains(res)); - proof_set* active_hyps = m_active_hyps.find(res); - if (active_hyps->empty() && m.has_fact(res) && m.is_false(m.get_fact(res))) + if (!m_open_mark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) return res; } UNREACHABLE(); @@ -399,7 +399,7 @@ proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { proof_set* active_hyps = m_active_hyps.find(premise); // if there is no active hypothesis return the premise - if (active_hyps->empty()) { + if (!m_open_mark.is_marked(premise)) { // XXX just in case premise might go away m_pinned.push_back(premise); return premise; diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index 6c56c204e..c418d932b 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -84,7 +84,11 @@ private: // during proof transformation obj_map m_parent_hyps; + /// marks if an expression is ever used as a hypothesis in a proof expr_mark m_hyp_mark; + /// marks a proof as open, i.e., has a non-discharged hypothesis as ancestor + expr_mark m_open_mark; + expr_mark m_visited; void reset(); From e84ca25f05dc6ec9eb368d0b6219c915d3586eb6 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 7 Jun 2018 09:07:17 -0700 Subject: [PATCH 247/364] Check whether one proof node is an ancestor of another on-demand Instead of pre-computing sets --- src/muz/spacer/spacer_proof_utils.cpp | 34 +++++++++++++++++---------- src/muz/spacer/spacer_proof_utils.h | 7 ------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index ca0e67f46..a8a90fa2b 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -192,12 +192,9 @@ proof_ref hypothesis_reducer::reduce(proof* pr) { } void hypothesis_reducer::reset() { - m_parent_hyps.reset(); m_active_hyps.reset(); m_units.reset(); m_cache.reset(); - for (auto t : m_pinned_parent_hyps) dealloc(t); - m_pinned_parent_hyps.reset(); for (auto t : m_pinned_active_hyps) dealloc(t); m_pinned_active_hyps.reset(); m_pinned.reset(); @@ -237,22 +234,16 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { m_pinned_active_hyps.insert(active_hyps); m_active_hyps.insert(p, active_hyps); - expr_set* parent_hyps = alloc(expr_set); - m_pinned_parent_hyps.insert(parent_hyps); - m_parent_hyps.insert(p, parent_hyps); - // fill both sets if (m.is_hypothesis(p)) { active_hyps->insert(p); m_open_mark.mark(p); - parent_hyps->insert(m.get_fact(p)); m_hyp_mark.mark(m.get_fact(p)); } else { for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { proof* parent = m.get_parent(p, i); - set_union(*parent_hyps, *m_parent_hyps.find(parent)); if (!m.is_lemma(p)) { // lemmas clear all hypotheses @@ -265,7 +256,7 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { } // collect all units that are hyp-free and are used as hypotheses somewhere -// requires that m_active_hyps and m_parent_hyps have been computed +// requires that m_active_hyps has been computed void hypothesis_reducer::collect_units(proof* pr) { proof_post_order pit(pr, m); @@ -285,8 +276,26 @@ void hypothesis_reducer::collect_units(proof* pr) { \brief returns true if p is an ancestor of q */ bool hypothesis_reducer::is_ancestor(proof *p, proof *q) { - expr_set* parent_hyps = m_parent_hyps.find(q); - return parent_hyps->contains(m.get_fact(p)); + if (p == q) return true; + ptr_vector todo; + todo.push_back(q); + + expr_mark visited; + while (!todo.empty()) { + proof *cur; + cur = todo.back(); + todo.pop_back(); + + if (visited.is_marked(cur)) continue; + + if (cur == p) return true; + visited.mark(cur); + + for (unsigned i = 0, sz = m.get_num_parents(cur); i < sz; ++i) { + todo.push_back(m.get_parent(cur, i)); + } + } + return false; } proof* hypothesis_reducer::reduce_core(proof* pf) { @@ -340,7 +349,6 @@ proof* hypothesis_reducer::reduce_core(proof* pf) { // make sure hypsets for the unit are computed // AG: is this needed? compute_hypsets(proof_of_unit); - SASSERT(m_parent_hyps.contains(proof_of_unit)); // if the transformation doesn't create a cycle, perform it if (!is_ancestor(p, proof_of_unit)) { diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index c418d932b..f8d8d263b 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -67,8 +67,6 @@ private: // created sets of active hypothesis ptr_vector m_pinned_active_hyps; - // created sets of parent hypothesis - ptr_vector m_pinned_parent_hyps; // maps a proof to the transformed proof obj_map m_cache; @@ -79,11 +77,6 @@ private: // maps a proof node to the set of its active (i.e., in scope) hypotheses obj_map m_active_hyps; - // maps a proof node to the set of all hypothesis-facts (active or - // not) that can reach it. Used for cycle detection and avoidance - // during proof transformation - obj_map m_parent_hyps; - /// marks if an expression is ever used as a hypothesis in a proof expr_mark m_hyp_mark; /// marks a proof as open, i.e., has a non-discharged hypothesis as ancestor From c5fb1c1223f6123d0e2ac9b762344c7b600ecd24 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 7 Jun 2018 09:45:17 -0700 Subject: [PATCH 248/364] Use vector instead of a hashtable to represent a set --- src/muz/spacer/spacer_proof_utils.cpp | 42 ++++++++++++++++++--------- src/muz/spacer/spacer_proof_utils.h | 9 +++--- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index a8a90fa2b..4a64f3d08 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -229,29 +229,43 @@ void hypothesis_reducer::compute_hypsets(proof *pr) { m_visited.mark(p); - // create active_hyps-set and parent_hyps-set for step p - proof_set* active_hyps = alloc(proof_set); - m_pinned_active_hyps.insert(active_hyps); - m_active_hyps.insert(p, active_hyps); + proof_ptr_vector* active_hyps = nullptr; // fill both sets if (m.is_hypothesis(p)) { - active_hyps->insert(p); + // create active_hyps-set for step p + proof_ptr_vector* active_hyps = alloc(proof_ptr_vector); + m_pinned_active_hyps.insert(active_hyps); + m_active_hyps.insert(p, active_hyps); + active_hyps->push_back(p); m_open_mark.mark(p); - m_hyp_mark.mark(m.get_fact(p)); + continue; } - else { - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - proof* parent = m.get_parent(p, i); - if (!m.is_lemma(p)) { - // lemmas clear all hypotheses - set_union(*active_hyps, *m_active_hyps.find(parent)); - m_open_mark.mark(p, !active_hyps->empty()); + ast_fast_mark1 seen; + + active_hyps = alloc(proof_ptr_vector); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + proof* parent = m.get_parent(p, i); + // lemmas clear all hypotheses above them + if (m.is_lemma(p)) continue; + for (auto *x : *m_active_hyps.find(parent)) { + if (!seen.is_marked(x)) { + seen.mark(x); + active_hyps->push_back(x); + m_open_mark.mark(p); } } } + if (active_hyps->empty()) { + dealloc(active_hyps); + m_active_hyps.insert(p, &m_empty_vector); + } + else { + m_pinned_active_hyps.push_back(active_hyps); + m_active_hyps.insert(p, active_hyps); + } } } @@ -404,7 +418,7 @@ proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { SASSERT(m.is_false(m.get_fact(premise))); SASSERT(m_active_hyps.contains(premise)); - proof_set* active_hyps = m_active_hyps.find(premise); + proof_ptr_vector* active_hyps = m_active_hyps.find(premise); // if there is no active hypothesis return the premise if (!m_open_mark.is_marked(premise)) { diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h index f8d8d263b..ead85f885 100644 --- a/src/muz/spacer/spacer_proof_utils.h +++ b/src/muz/spacer/spacer_proof_utils.h @@ -57,16 +57,17 @@ public: proof_ref reduce(proof* pf); private: - typedef obj_hashtable expr_set; - typedef obj_hashtable proof_set; + typedef ptr_vector proof_ptr_vector; ast_manager &m; + proof_ptr_vector m_empty_vector; + // created expressions expr_ref_vector m_pinned; // created sets of active hypothesis - ptr_vector m_pinned_active_hyps; + ptr_vector m_pinned_active_hyps; // maps a proof to the transformed proof obj_map m_cache; @@ -75,7 +76,7 @@ private: obj_map m_units; // maps a proof node to the set of its active (i.e., in scope) hypotheses - obj_map m_active_hyps; + obj_map m_active_hyps; /// marks if an expression is ever used as a hypothesis in a proof expr_mark m_hyp_mark; From 0534b72c4dd53949c6f10c12b08a1448b35340dd Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 7 Jun 2018 09:54:44 -0700 Subject: [PATCH 249/364] sort hypotheses --- src/muz/spacer/spacer_proof_utils.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 4a64f3d08..082cc4b5d 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -427,6 +427,8 @@ proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { return premise; } + // add some stability + std::stable_sort(active_hyps->begin(), active_hyps->end(), ast_lt_proc()); // otherwise, build a disjunction of the negated active hypotheses // and add a lemma proof step expr_ref_buffer args(m); From 1f0fd38c99da27cf3d4f94c1a56eb5f4e2e59466 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 7 Jun 2018 17:14:26 -0700 Subject: [PATCH 250/364] ground sat refutation from spacer (wip) --- src/muz/spacer/CMakeLists.txt | 1 + src/muz/spacer/spacer_context.cpp | 71 ++++++----- src/muz/spacer/spacer_context.h | 3 +- src/muz/spacer/spacer_sat_answer.cpp | 168 +++++++++++++++++++++++++++ src/muz/spacer/spacer_sat_answer.h | 55 +++++++++ 5 files changed, 267 insertions(+), 31 deletions(-) create mode 100644 src/muz/spacer/spacer_sat_answer.cpp create mode 100644 src/muz/spacer/spacer_sat_answer.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 8e41d5ae7..22218f25f 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -27,6 +27,7 @@ z3_add_component(spacer spacer_iuc_proof.cpp spacer_mbc.cpp spacer_pdr.cpp + spacer_sat_answer.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 6726bb15d..2e199c6e4 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -36,6 +36,7 @@ Notes: #include "muz/base/dl_rule_set.h" #include "smt/tactic/unit_subsumption_tactic.h" #include "model/model_smt2_pp.h" +#include "model/model_evaluator.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" @@ -52,6 +53,9 @@ Notes: #include "ast/expr_abstract.h" #include "smt/smt_solver.h" + +#include "muz/spacer/spacer_sat_answer.h" + namespace spacer { /// pob -- proof obligation @@ -2857,19 +2861,31 @@ expr_ref context::mk_unsat_answer() const return ex.to_expr(); } + +proof_ref context::get_ground_refutation() { + if (m_last_result != l_true) { + IF_VERBOSE(0, verbose_stream() + << "Sat answer unavailable when result is false\n";); + return proof_ref(m); + } + + ground_sat_answer_op op(*this); + return op(*m_query); +} expr_ref context::get_ground_sat_answer() { if (m_last_result != l_true) { - verbose_stream () << "Sat answer unavailable when result is false\n"; - return expr_ref (m); + IF_VERBOSE(0, verbose_stream() + << "Sat answer unavailable when result is false\n";); + return expr_ref(m); } // treat the following as queues: read from left to right and insert at the right reach_fact_ref_vector reach_facts; ptr_vector preds; ptr_vector pts; - expr_ref_vector cex (m), // pre-order list of ground instances of predicates - cex_facts (m); // equalities for the ground cex using signature constants + expr_ref_vector cex (m); // pre-order list of ground instances of predicates + expr_ref_vector cex_facts (m); // equalities for the ground cex using signature constants // temporary reach_fact *reach_fact; @@ -2921,6 +2937,7 @@ expr_ref context::get_ground_sat_answer() // get child pts preds.reset(); pt->find_predecessors(*r, preds); + for (unsigned j = 0; j < preds.size (); j++) { child_pts.push_back (&(get_pred_transformer (preds[j]))); } @@ -2936,12 +2953,11 @@ expr_ref context::get_ground_sat_answer() SASSERT (child_reach_facts.size () == u_tail_sz); for (unsigned i = 0; i < u_tail_sz; i++) { expr_ref ofml (m); - child_pts.get (i)->get_manager ().formula_n2o - (child_reach_facts[i]->get (), ofml, i); + m_pm.formula_n2o(child_reach_facts[i]->get(), ofml, i); cex_ctx->assert_expr (ofml); } - cex_ctx->assert_expr (pt->transition ()); - cex_ctx->assert_expr (pt->rule2tag (r)); + cex_ctx->assert_expr(pt->transition()); + cex_ctx->assert_expr(pt->rule2tag(r)); lbool res = cex_ctx->check_sat(0, nullptr); CTRACE("cex", res == l_false, tout << "Cex fact: " << mk_pp(cex_fact, m) << "\n"; @@ -2956,40 +2972,35 @@ expr_ref context::get_ground_sat_answer() cex_ctx->get_model (local_mdl); cex_ctx->pop (1); - model_evaluator_util mev (m); - mev.set_model (*local_mdl); - for (unsigned i = 0; i < child_pts.size (); i++) { - pred_transformer& ch_pt = *(child_pts.get (i)); - unsigned sig_size = ch_pt.sig_size (); - expr_ref_vector ground_fact_conjs (m); - expr_ref_vector ground_arg_vals (m); + model_evaluator mev(*local_mdl); + for (unsigned i = 0; i < child_pts.size(); i++) { + pred_transformer& ch_pt = *(child_pts.get(i)); + unsigned sig_size = ch_pt.sig_size(); + expr_ref_vector ground_fact_conjs(m); + expr_ref_vector ground_arg_vals(m); for (unsigned j = 0; j < sig_size; j++) { - expr_ref sig_arg (m), sig_val (m); - sig_arg = m.mk_const (ch_pt.get_manager ().o2o (ch_pt.sig (j), 0, i)); + expr_ref sig_arg(m), sig_val(m); + sig_arg = m.mk_const (m_pm.o2o(ch_pt.sig(j), 0, i)); VERIFY(mev.eval (sig_arg, sig_val, true)); - ground_fact_conjs.push_back (m.mk_eq (sig_arg, sig_val)); - ground_arg_vals.push_back (sig_val); + ground_fact_conjs.push_back(m.mk_eq(sig_arg, sig_val)); + ground_arg_vals.push_back(sig_val); } if (ground_fact_conjs.size () > 0) { - expr_ref ground_fact (m); - ground_fact = m.mk_and (ground_fact_conjs.size (), ground_fact_conjs.c_ptr ()); - ch_pt.get_manager ().formula_o2n (ground_fact, ground_fact, i); + expr_ref ground_fact(m); + ground_fact = mk_and(ground_fact_conjs); + m_pm.formula_o2n(ground_fact, ground_fact, i); cex_facts.push_back (ground_fact); } else { cex_facts.push_back (m.mk_true ()); } - cex.push_back (m.mk_app (ch_pt.head (), sig_size, ground_arg_vals.c_ptr ())); + cex.push_back(m.mk_app(ch_pt.head(), + sig_size, ground_arg_vals.c_ptr())); } } - TRACE ("spacer", - tout << "ground cex\n"; - for (unsigned i = 0; i < cex.size (); i++) { - tout << mk_pp (cex.get (i), m) << "\n"; - } - ); + TRACE ("spacer", tout << "ground cex\n" << cex << "\n";); - return expr_ref (m.mk_and (cex.size (), cex.c_ptr ()), m); + return expr_ref(m.mk_and(cex.size(), cex.c_ptr()), m); } ///this is where everything starts diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 34c04d596..e95ddc8ec 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -970,7 +970,8 @@ public: * get bottom-up (from query) sequence of ground predicate instances * (for e.g. P(0,1,0,0,3)) that together form a ground derivation to query */ - expr_ref get_ground_sat_answer (); + expr_ref get_ground_sat_answer (); + proof_ref get_ground_refutation(); void get_rules_along_trace (datalog::rule_ref_vector& rules); void collect_statistics(statistics& st) const; diff --git a/src/muz/spacer/spacer_sat_answer.cpp b/src/muz/spacer/spacer_sat_answer.cpp new file mode 100644 index 000000000..8382133d8 --- /dev/null +++ b/src/muz/spacer/spacer_sat_answer.cpp @@ -0,0 +1,168 @@ +#include "muz/spacer/spacer_sat_answer.h" +#include "muz/base/dl_context.h" +#include "muz/base/dl_rule.h" + +#include "smt/smt_solver.h" + +namespace spacer { + +struct ground_sat_answer_op::frame { + reach_fact *m_rf; + pred_transformer &m_pt; + expr_ref_vector m_gnd_subst; + expr_ref m_gnd_eq; + expr_ref m_fact; + unsigned m_visit; + expr_ref_vector m_kids; + + frame(reach_fact *rf, pred_transformer &pt, const expr_ref_vector &gnd_subst) : + m_rf(rf), m_pt(pt), + m_gnd_subst(gnd_subst), + m_gnd_eq(pt.get_ast_manager()), + m_fact(pt.get_ast_manager()), + m_visit(0), + m_kids(pt.get_ast_manager()) { + + ast_manager &m = pt.get_ast_manager(); + spacer::manager &pm = pt.get_manager(); + + m_fact = m.mk_app(head(), m_gnd_subst.size(), m_gnd_subst.c_ptr()); + if (pt.head()->get_arity() == 0) + m_gnd_eq = m.mk_true(); + else { + SASSERT(m_gnd_subst.size() == pt.head()->get_arity()); + for (unsigned i = 0, sz = pt.sig_size(); i < sz; ++i) { + m_gnd_eq = m.mk_eq(m.mk_const(pm.o2n(pt.sig(i), 0)), + m_gnd_subst.get(i)); + } + } + } + + func_decl* head() {return m_pt.head();} + expr* fact() {return m_fact;} + const datalog::rule &rule() {return m_rf->get_rule();} + pred_transformer &pt() {return m_pt;} +}; + +ground_sat_answer_op::ground_sat_answer_op(context &ctx) : + m_ctx(ctx), m(m_ctx.get_ast_manager()), m_pm(m_ctx.get_manager()), + m_pinned(m) { + m_solver = mk_smt_solver(m, params_ref::get_empty(), symbol::null); +} + +proof_ref ground_sat_answer_op::operator()(pred_transformer &query) { + + + vector todo; + + // -- find substitution for a query if query is not nullary + expr_ref_vector qsubst(m); + if (query.head()->get_arity() > 0) { + solver::scoped_push _s_(*m_solver); + m_solver->assert_expr(query.get_last_rf()->get()); + lbool res = m_solver->check_sat(0, nullptr); + (void)res; + SASSERT(res == l_true); + model_ref mdl; + m_solver->get_model(mdl); + for (unsigned i = 0, sz = query.sig_size(); i < sz; ++i) { + expr_ref arg(m), val(m); + arg = m.mk_const(m_pm.o2n(query.sig(i), 0)); + mdl->eval(arg, val, true); + qsubst.push_back(val); + } + } + + frame root(query.get_last_rf(), query, qsubst); + todo.push_back(root); + + while (!todo.empty()) { + frame &curr = todo.back(); + if (m_cache.contains(curr.fact())) + { + todo.pop_back(); + continue; + } + + if (curr.m_visit == 0) { + mk_children(curr, todo); + curr.m_visit = 1; + } + else { + proof* pf = mk_proof_step(curr); + m_cache.insert(curr.fact(), pf); + todo.pop_back(); + } + + } + return proof_ref(m_cache.find(root.fact()), m); +} + + +void ground_sat_answer_op::mk_children(frame &fr, vector &todo) { + const datalog::rule &r = fr.rule(); + ptr_vector preds; + fr.pt().find_predecessors(r, preds); + + if (preds.empty()) return; + + const reach_fact_ref_vector &kid_rfs = fr.m_rf->get_justifications(); + solver::scoped_push _s_(*m_solver); + m_solver->assert_expr(fr.m_gnd_eq); + unsigned ut_sz = r.get_uninterpreted_tail_size(); + for (unsigned i = 0; i < ut_sz; ++i) { + expr_ref f(m); + m_pm.formula_n2o(kid_rfs.get(i)->get(), f, i); + m_solver->assert_expr(f); + } + m_solver->assert_expr(fr.pt().transition()); + m_solver->assert_expr(fr.pt().rule2tag(&r)); + + lbool res = m_solver->check_sat(0, nullptr); + (void)res; + SASSERT(res == l_true); + + model_ref mdl; + m_solver->get_model(mdl); + expr_ref_vector subst(m); + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { + subst.reset(); + mk_child_subst_from_model(preds.get(i), i, mdl, subst); + todo.push_back(frame(kid_rfs.get(i), + m_ctx.get_pred_transformer(preds.get(i)), subst)); + fr.m_kids.push_back(todo.back().fact()); + } +} + + +void ground_sat_answer_op::mk_child_subst_from_model(func_decl *pred, + unsigned j, model_ref &mdl, + expr_ref_vector &subst) { + pred_transformer &pt = m_ctx.get_pred_transformer(pred); + for (unsigned i = 0, sz = pt.sig_size(); i < sz; ++i) { + expr_ref arg(m), val(m); + arg = m.mk_const(m_pm.o2o(pt.sig(i), 0, j)); + mdl->eval(arg, val, true); + subst.push_back(val); + } +} + +proof *ground_sat_answer_op::mk_proof_step(frame &fr) { + svector> positions; + vector substs; + + proof_ref_vector premises(m); + datalog::rule_manager &rm = m_ctx.get_datalog_context().get_rule_manager(); + expr_ref rule_fml(m); + rm.to_formula(fr.rule(), rule_fml); + premises.push_back(m.mk_asserted(rule_fml)); + for (auto &k : fr.m_kids) {premises.push_back(m_cache.find(k));} + + m_pinned.push_back(m.mk_hyper_resolve(premises.size(), + premises.c_ptr(), + fr.fact(), + positions, substs)); + return to_app(m_pinned.back()); +} + +} diff --git a/src/muz/spacer/spacer_sat_answer.h b/src/muz/spacer/spacer_sat_answer.h new file mode 100644 index 000000000..6cfd3b14c --- /dev/null +++ b/src/muz/spacer/spacer_sat_answer.h @@ -0,0 +1,55 @@ +/*++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + spacer_sat_answer.h + +Abstract: + + Compute refutation proof for CHC + +Author: + + Arie Gurfinkel + +Revision History: + +--*/ + +#ifndef _SPACER_SAT_ANSWER_H_ +#define _SPACER_SAT_ANSWER_H_ + +#include "muz/spacer/spacer_context.h" +#include "ast/ast.h" +#include "util/obj_hashtable.h" +#include "model/model.h" +#include "solver/solver.h" + +namespace spacer { + +class ground_sat_answer_op { + context &m_ctx; + ast_manager &m; + manager &m_pm; + + expr_ref_vector m_pinned; + obj_map m_cache; + + ref m_solver; + + struct frame; + + proof *mk_proof_step(frame &fr); + void mk_children(frame &fr, vector &todo); + void mk_child_subst_from_model(func_decl *pred, unsigned i, + model_ref &mdl, expr_ref_vector &subst); + +public: + ground_sat_answer_op(context &ctx); + + proof_ref operator() (pred_transformer &query); +}; +} + +#endif From 1920450f98b1bf354bc22a54970d5cac9a4715c2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 7 Jun 2018 09:58:27 -0700 Subject: [PATCH 251/364] throttle ite-blasting Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_context.cpp | 2 +- src/tactic/core/blast_term_ite_tactic.cpp | 79 ++++++++++++++++------- src/tactic/core/blast_term_ite_tactic.h | 2 +- 3 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 2e199c6e4..5806c08fb 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1673,7 +1673,7 @@ void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule, // rewrite and simplify th_rewriter rw(m); rw(fml); - if (ctx.blast_term_ite()) {blast_term_ite(fml); rw(fml);} + if (ctx.blast_term_ite()) {blast_term_ite(fml, 3); rw(fml);} TRACE("spacer", tout << mk_pp(fml, m) << "\n";); // allow quantifiers in init rule diff --git a/src/tactic/core/blast_term_ite_tactic.cpp b/src/tactic/core/blast_term_ite_tactic.cpp index 3c4684cb8..eefb9418e 100644 --- a/src/tactic/core/blast_term_ite_tactic.cpp +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -33,50 +33,65 @@ Notes: class blast_term_ite_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { - ast_manager& m; - unsigned long long m_max_memory; // in bytes - unsigned m_num_fresh; // number of expansions + ast_manager& m; + unsigned long long m_max_memory; // in bytes + unsigned m_num_fresh; // number of expansions + unsigned m_max_steps; + unsigned m_max_inflation; + unsigned m_init_term_size; rw_cfg(ast_manager & _m, params_ref const & p): m(_m), - m_num_fresh(0) { + m_num_fresh(0), + m_max_steps(UINT_MAX), + m_max_inflation(UINT_MAX), + m_init_term_size(0) { updt_params(p); } void updt_params(params_ref const & p) { - m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_max_steps = p.get_uint("max_steps", UINT_MAX); + m_max_inflation = p.get_uint("max_inflation", UINT_MAX); // multiplicative factor of initial term size. } + + bool max_steps_exceeded(unsigned num_steps) const { cooperate("blast term ite"); // if (memory::get_allocation_size() > m_max_memory) // throw tactic_exception(TACTIC_MAX_MEMORY_MSG); - return false; + return num_steps >= m_max_steps; } br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; } + if (m_max_inflation < UINT_MAX && + m_init_term_size > 0 && + m_max_inflation * m_init_term_size < m_num_fresh) + return BR_FAILED; + for (unsigned i = 0; i < num_args; ++i) { expr* c, *t, *e; if (!m.is_bool(args[i]) && m.is_ite(args[i], c, t, e)) { - // enable_trace("blast_term_ite"); TRACE("blast_term_ite", result = m.mk_app(f, num_args, args); tout << result << "\n";); expr_ref e1(m), e2(m); ptr_vector args1(num_args, args); args1[i] = t; - ++m_num_fresh; e1 = m.mk_app(f, num_args, args1.c_ptr()); - if (m.are_equal(t,e)) { + if (m.are_equal(t, e)) { result = e1; return BR_REWRITE1; } - args1[i] = e; - e2 = m.mk_app(f, num_args, args1.c_ptr()); - result = m.mk_app(f, num_args, args); - result = m.mk_ite(c, e1, e2); - return BR_REWRITE3; + else { + args1[i] = e; + e2 = m.mk_app(f, num_args, args1.c_ptr()); + result = m.mk_ite(c, e1, e2); + ++m_num_fresh; + return BR_REWRITE3; + } } } return BR_FAILED; @@ -107,10 +122,9 @@ class blast_term_ite_tactic : public tactic { m(_m), m_rw(m, p) { } - - + void updt_params(params_ref const & p) { - m_rw.cfg().updt_params(p); + m_rw.m_cfg.updt_params(p); } void operator()(goal_ref const & g, goal_ref_buffer & result) { @@ -121,8 +135,14 @@ class blast_term_ite_tactic : public tactic { expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); + unsigned num_fresh = 0; for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); + if (m_rw.m_cfg.m_max_inflation < UINT_MAX) { + m_rw.m_cfg.m_init_term_size = get_num_exprs(curr); + num_fresh += m_rw.m_cfg.m_num_fresh; + m_rw.m_cfg.m_num_fresh = 0; + } m_rw(curr, new_curr, new_pr); if (produce_proofs) { proof * pr = g->pr(idx); @@ -130,7 +150,7 @@ class blast_term_ite_tactic : public tactic { } g->update(idx, new_curr, new_pr, g->dep(idx)); } - report_tactic_progress(":blast-term-ite-consts", m_rw.m_cfg.m_num_fresh); + report_tactic_progress(":blast-term-ite-consts", m_rw.m_cfg.m_num_fresh + num_fresh); g->inc_depth(); result.push_back(g.get()); TRACE("blast_term_ite", g->display(tout);); @@ -156,7 +176,7 @@ public: void updt_params(params_ref const & p) override { m_params = p; - m_imp->m_rw.cfg().updt_params(p); + m_imp->m_rw.m_cfg.updt_params(p); } void collect_param_descrs(param_descrs & r) override { @@ -176,14 +196,23 @@ public: m_imp = alloc(imp, m, m_params); } - static void blast_term_ite(expr_ref& fml) { + static void blast_term_ite(expr_ref& fml, unsigned max_inflation) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); params_ref p; rw ite_rw(m, p); - expr_ref tmp(m); - ite_rw(fml, tmp); - fml = tmp; + ite_rw.m_cfg.m_max_inflation = max_inflation; + if (max_inflation < UINT_MAX) { + ite_rw.m_cfg.m_init_term_size = get_num_exprs(fml); + } + try { + expr_ref tmp(m); + ite_rw(fml, tmp); + fml = tmp; + } + catch (z3_exception &) { + // max steps exceeded. + } } }; @@ -191,6 +220,6 @@ tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(blast_term_ite_tactic, m, p)); } -void blast_term_ite(expr_ref& fml) { - blast_term_ite_tactic::blast_term_ite(fml); +void blast_term_ite(expr_ref& fml, unsigned max_inflation) { + blast_term_ite_tactic::blast_term_ite(fml, max_inflation); } diff --git a/src/tactic/core/blast_term_ite_tactic.h b/src/tactic/core/blast_term_ite_tactic.h index 1ecab98be..fcad6c068 100644 --- a/src/tactic/core/blast_term_ite_tactic.h +++ b/src/tactic/core/blast_term_ite_tactic.h @@ -33,6 +33,6 @@ tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p = params ADD_TACTIC("blast-term-ite", "blast term if-then-else by hoisting them.", "mk_blast_term_ite_tactic(m, p)") */ -void blast_term_ite(expr_ref& fml); +void blast_term_ite(expr_ref& fml, unsigned max_inflation); #endif From f3466bb3e4874216265c54c3da4fb1d5bcebf074 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 7 Jun 2018 21:07:46 -0700 Subject: [PATCH 252/364] tidy Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_util.cpp | 1344 +++++++++++--------------------- src/muz/spacer/spacer_util.h | 221 +++--- 2 files changed, 573 insertions(+), 992 deletions(-) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index ebcbb9145..8941c511a 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -87,8 +87,8 @@ namespace spacer { m_mev = nullptr; } m_model = model; - if (!m_model) { return; } - m_mev = alloc(model_evaluator, *m_model); + if (m_model) + m_mev = alloc(model_evaluator, *m_model); } bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { @@ -127,334 +127,6 @@ namespace spacer { return eval(x, res, false) && m.is_true (res); } - void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { - ast_manager& m = fml.get_manager(); - expr_ref_vector conjs(m); - flatten_and(fml, conjs); - obj_map diseqs; - expr* n, *lhs, *rhs; - for (unsigned i = 0; i < conjs.size(); ++i) { - if (m.is_not(conjs[i].get(), n) && m.is_eq(n, lhs, rhs)) { - if (!m.is_value(rhs)) { - std::swap(lhs, rhs); - } - if (!m.is_value(rhs)) { - continue; - } - diseqs.insert_if_not_there2(lhs, 0)->get_data().m_value++; - } - } - expr_substitution sub(m); - - unsigned orig_size = conjs.size(); - unsigned num_deleted = 0; - expr_ref val(m), tmp(m); - proof_ref pr(m); - pr = m.mk_asserted(m.mk_true()); - for (auto const& kv : diseqs) { - if (kv.m_value >= threshold) { - model.eval(kv.m_key, val); - sub.insert(kv.m_key, val, pr); - conjs.push_back(m.mk_eq(kv.m_key, val)); - num_deleted += kv.m_value; - } - } - if (orig_size < conjs.size()) { - scoped_ptr rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - for (unsigned i = 0; i < orig_size; ++i) { - tmp = conjs[i].get(); - (*rep)(tmp); - if (m.is_true(tmp)) { - conjs[i] = conjs.back(); - SASSERT(orig_size <= conjs.size()); - conjs.pop_back(); - SASSERT(orig_size <= 1 + conjs.size()); - if (i + 1 == orig_size) { - // no-op. - } - else if (orig_size <= conjs.size()) { - // no-op - } - else { - SASSERT(orig_size == 1 + conjs.size()); - --orig_size; - --i; - } - } - else { - conjs[i] = tmp; - } - } - IF_VERBOSE(2, verbose_stream() << "Deleted " << num_deleted << " disequalities " << conjs.size() << " conjuncts\n";); - } - fml = m.mk_and(conjs.size(), conjs.c_ptr()); - } - - // - // (f (if c1 (if c2 e1 e2) e3) b c) -> - // (if c1 (if c2 (f e1 b c) - - class ite_hoister { - ast_manager& m; - public: - ite_hoister(ast_manager& m): m(m) {} - - br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { - if (m.is_ite(f)) { - return BR_FAILED; - } - for (unsigned i = 0; i < num_args; ++i) { - expr* c, *t, *e; - if (!m.is_bool(args[i]) && m.is_ite(args[i], c, t, e)) { - expr_ref e1(m), e2(m); - ptr_vector args1(num_args, args); - args1[i] = t; - e1 = m.mk_app(f, num_args, args1.c_ptr()); - if (t == e) { - result = e1; - return BR_REWRITE1; - } - args1[i] = e; - e2 = m.mk_app(f, num_args, args1.c_ptr()); - result = m.mk_app(f, num_args, args); - result = m.mk_ite(c, e1, e2); - return BR_REWRITE3; - } - } - return BR_FAILED; - } - }; - - struct ite_hoister_cfg: public default_rewriter_cfg { - ite_hoister m_r; - bool rewrite_patterns() const { return false; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - return m_r.mk_app_core(f, num, args, result); - } - ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} - }; - - class ite_hoister_star : public rewriter_tpl { - ite_hoister_cfg m_cfg; - public: - ite_hoister_star(ast_manager & m, params_ref const & p): - rewriter_tpl(m, false, m_cfg), - m_cfg(m, p) {} - }; - - void hoist_non_bool_if(expr_ref& fml) { - ast_manager& m = fml.get_manager(); - scoped_no_proof _sp(m); - params_ref p; - ite_hoister_star ite_rw(m, p); - expr_ref tmp(m); - ite_rw(fml, tmp); - fml = tmp; - } - - class test_diff_logic { - ast_manager& m; - arith_util a; - bv_util bv; - bool m_is_dl; - bool m_test_for_utvpi; - - bool is_numeric(expr* e) const { - if (a.is_numeral(e)) { - return true; - } - expr* cond, *th, *el; - if (m.is_ite(e, cond, th, el)) { - return is_numeric(th) && is_numeric(el); - } - return false; - } - - bool is_arith_expr(expr *e) const { - return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); - } - - bool is_offset(expr* e) const { - if (a.is_numeral(e)) { - return true; - } - expr* cond, *th, *el, *e1, *e2; - if (m.is_ite(e, cond, th, el)) { - return is_offset(th) && is_offset(el); - } - // recognize offsets. - if (a.is_add(e, e1, e2)) { - if (is_numeric(e1)) { - return is_offset(e2); - } - if (is_numeric(e2)) { - return is_offset(e1); - } - return false; - } - if (m_test_for_utvpi) { - if (a.is_mul(e, e1, e2)) { - if (is_minus_one(e1)) { - return is_offset(e2); - } - if (is_minus_one(e2)) { - return is_offset(e1); - } - } - } - return !is_arith_expr(e); - } - - bool is_minus_one(expr const * e) const { - rational r; - return a.is_numeral(e, r) && r.is_minus_one(); - } - - bool test_ineq(expr* e) const { - SASSERT(a.is_le(e) || a.is_ge(e) || m.is_eq(e)); - SASSERT(to_app(e)->get_num_args() == 2); - expr * lhs = to_app(e)->get_arg(0); - expr * rhs = to_app(e)->get_arg(1); - if (is_offset(lhs) && is_offset(rhs)) - { return true; } - if (!is_numeric(rhs)) - { std::swap(lhs, rhs); } - if (!is_numeric(rhs)) - { return false; } - // lhs can be 'x' or '(+ x (* -1 y))' - if (is_offset(lhs)) - { return true; } - expr* arg1, *arg2; - if (!a.is_add(lhs, arg1, arg2)) - { return false; } - // x - if (m_test_for_utvpi) { - return is_offset(arg1) && is_offset(arg2); - } - if (is_arith_expr(arg1)) - { std::swap(arg1, arg2); } - if (is_arith_expr(arg1)) - { return false; } - // arg2: (* -1 y) - expr* m1, *m2; - if (!a.is_mul(arg2, m1, m2)) - { return false; } - return is_minus_one(m1) && is_offset(m2); - } - - bool test_eq(expr* e) const { - expr* lhs, *rhs; - VERIFY(m.is_eq(e, lhs, rhs)); - if (!a.is_int_real(lhs)) { - return true; - } - if (a.is_numeral(lhs) || a.is_numeral(rhs)) { - return test_ineq(e); - } - return - test_term(lhs) && - test_term(rhs) && - !a.is_mul(lhs) && - !a.is_mul(rhs); - } - - bool test_term(expr* e) const { - if (m.is_bool(e)) { - return true; - } - if (a.is_numeral(e)) { - return true; - } - if (is_offset(e)) { - return true; - } - expr* lhs, *rhs; - if (a.is_add(e, lhs, rhs)) { - if (!a.is_numeral(lhs)) { - std::swap(lhs, rhs); - } - return a.is_numeral(lhs) && is_offset(rhs); - } - if (a.is_mul(e, lhs, rhs)) { - return is_minus_one(lhs) || is_minus_one(rhs); - } - return false; - } - - bool is_non_arith_or_basic(expr* e) - { - if (!is_app(e)) { - return false; - } - family_id fid = to_app(e)->get_family_id(); - - if (fid == null_family_id && - !m.is_bool(e) && - to_app(e)->get_num_args() > 0) { - return true; - } - return - fid != m.get_basic_family_id() && - fid != null_family_id && - fid != a.get_family_id() && - fid != bv.get_family_id(); - } - - public: - test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - - void test_for_utvpi() { m_test_for_utvpi = true; } - - void operator()(expr* e) { - if (!m_is_dl) { - return; - } - if (a.is_le(e) || a.is_ge(e)) { - m_is_dl = test_ineq(e); - } else if (m.is_eq(e)) { - m_is_dl = test_eq(e); - } else if (is_non_arith_or_basic(e)) { - m_is_dl = false; - } else if (is_app(e)) { - app* a = to_app(e); - for (unsigned i = 0; m_is_dl && i < a->get_num_args(); ++i) { - m_is_dl = test_term(a->get_arg(i)); - } - } - - if (!m_is_dl) { - char const* msg = "non-diff: "; - if (m_test_for_utvpi) { - msg = "non-utvpi: "; - } - IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); - } - } - - bool is_dl() const { return m_is_dl; } - }; - - bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { - test_diff_logic test(m); - expr_fast_mark1 mark; - for (unsigned i = 0; i < num_fmls; ++i) { - quick_for_each_expr(test, mark, fmls[i]); - } - return test.is_dl(); - } - - bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { - test_diff_logic test(m); - test.test_for_utvpi(); - expr_fast_mark1 mark; - for (unsigned i = 0; i < num_fmls; ++i) { - quick_for_each_expr(test, mark, fmls[i]); - } - return test.is_dl(); - } - - void subst_vars(ast_manager& m, app_ref_vector const& vars, model* M, expr_ref& fml) { @@ -469,45 +141,45 @@ namespace spacer { sub (fml); } -void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) { - ast_manager &m = vars.m(); - ast_pp_util pp(m); - pp.collect(fml); - pp.display_decls(out); + void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) { + ast_manager &m = vars.m(); + ast_pp_util pp(m); + pp.collect(fml); + pp.display_decls(out); - out << "(define-fun mbp_benchmark_fml () Bool\n "; - out << mk_pp(fml, m) << ")\n\n"; + out << "(define-fun mbp_benchmark_fml () Bool\n "; + out << mk_pp(fml, m) << ")\n\n"; + + out << "(push)\n" + << "(assert mbp_benchmark_fml)\n" + << "(check-sat)\n" + << "(mbp mbp_benchmark_fml ("; + for (auto v : vars) {out << mk_pp(v, m) << " ";} + out << "))\n" + << "(pop)\n" + << "(exit)\n"; + } - out << "(push)\n" - << "(assert mbp_benchmark_fml)\n" - << "(check-sat)\n" - << "(mbp mbp_benchmark_fml ("; - for (auto v : vars) {out << mk_pp(v, m) << " ";} - out << "))\n" - << "(pop)\n" - << "(exit)\n"; -} - -void qe_project_z3 (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + void qe_project_z3 (ast_manager& m, app_ref_vector& vars, expr_ref& fml, const model_ref& M, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { - params_ref p; - p.set_bool("reduce_all_selects", reduce_all_selects); - p.set_bool("dont_sub", dont_sub); - - qe::mbp mbp(m, p); - // TODO: deal with const - model *mdl = const_cast(M.get()); - mbp.spacer(vars, *mdl, fml); -} - + params_ref p; + p.set_bool("reduce_all_selects", reduce_all_selects); + p.set_bool("dont_sub", dont_sub); + + qe::mbp mbp(m, p); + // TODO: deal with const + model *mdl = const_cast(M.get()); + mbp.spacer(vars, *mdl, fml); + } + /* * eliminate simple equalities using qe_lite * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays */ void qe_project_spacer (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects, bool use_native_mbp, - bool dont_sub) { + const model_ref& M, bool reduce_all_selects, bool use_native_mbp, + bool dont_sub) { th_rewriter rw (m); TRACE ("spacer_mbp", tout << "Before projection:\n"; @@ -533,70 +205,70 @@ void qe_project_z3 (ast_manager& m, app_ref_vector& vars, expr_ref& fml, expr_ref bval (m); while (true) { - params_ref p; - qe_lite qe(m, p, false); - qe (vars, fml); - rw (fml); - - TRACE ("spacer_mbp", - tout << "After qe_lite:\n"; - tout << mk_pp (fml, m) << "\n"; - tout << "Vars:\n" << vars;); - - SASSERT (!m.is_false (fml)); - - - // sort out vars into bools, arith (int/real), and arrays - for (app* v : vars) { - if (m.is_bool (v)) { - // obtain the interpretation of the ith var using model completion - VERIFY (M->eval (v, bval, true)); - bool_sub.insert (v, bval); - } else if (arr_u.is_array(v)) { - array_vars.push_back (v); - } else { - SASSERT (ari_u.is_int (v) || ari_u.is_real (v)); - arith_vars.push_back (v); - } - } - - // substitute Booleans - if (!bool_sub.empty()) { - bool_sub (fml); - // -- bool_sub is not simplifying + params_ref p; + qe_lite qe(m, p, false); + qe (vars, fml); rw (fml); + + TRACE ("spacer_mbp", + tout << "After qe_lite:\n"; + tout << mk_pp (fml, m) << "\n"; + tout << "Vars:\n" << vars;); + SASSERT (!m.is_false (fml)); - TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); - bool_sub.reset (); + + + // sort out vars into bools, arith (int/real), and arrays + for (app* v : vars) { + if (m.is_bool (v)) { + // obtain the interpretation of the ith var using model completion + VERIFY (M->eval (v, bval, true)); + bool_sub.insert (v, bval); + } else if (arr_u.is_array(v)) { + array_vars.push_back (v); + } else { + SASSERT (ari_u.is_int (v) || ari_u.is_real (v)); + arith_vars.push_back (v); + } + } + + // substitute Booleans + if (!bool_sub.empty()) { + bool_sub (fml); + // -- bool_sub is not simplifying + rw (fml); + SASSERT (!m.is_false (fml)); + TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); + bool_sub.reset (); + } + + TRACE ("spacer_mbp", + tout << "Array vars:\n"; + tout << array_vars;); + + vars.reset (); + + // project arrays + { + scoped_no_proof _sp (m); + // -- local rewriter that is aware of current proof mode + th_rewriter srw(m); + spacer_qe::array_project (*M.get (), array_vars, fml, vars, reduce_all_selects); + SASSERT (array_vars.empty ()); + srw (fml); + SASSERT (!m.is_false (fml)); + } + + TRACE ("spacer_mbp", + tout << "extended model:\n"; + model_pp (tout, *M); + tout << "Auxiliary variables of index and value sorts:\n"; + tout << vars; + ); + + if (vars.empty()) { break; } } - - TRACE ("spacer_mbp", - tout << "Array vars:\n"; - tout << array_vars;); - - vars.reset (); - - // project arrays - { - scoped_no_proof _sp (m); - // -- local rewriter that is aware of current proof mode - th_rewriter srw(m); - spacer_qe::array_project (*M.get (), array_vars, fml, vars, reduce_all_selects); - SASSERT (array_vars.empty ()); - srw (fml); - SASSERT (!m.is_false (fml)); - } - - TRACE ("spacer_mbp", - tout << "extended model:\n"; - model_pp (tout, *M); - tout << "Auxiliary variables of index and value sorts:\n"; - tout << vars; - ); - - if (vars.empty()) { break; } - } - + // project reals and ints if (!arith_vars.empty ()) { TRACE ("spacer_mbp", tout << "Arith vars:\n" << arith_vars;); @@ -661,27 +333,26 @@ void qe_project_z3 (ast_manager& m, app_ref_vector& vars, expr_ref& fml, ptr_vector const& acc, unsigned j, func_decl* f, - expr* c) -{ + expr* c) + { if (is_app(c) && to_app(c)->get_decl() == f) { return to_app(c)->get_arg(j); - } else { + } else { return m.mk_app(acc[j], c); } } -void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects, bool use_native_mbp, - bool dont_sub) { - if (use_native_mbp) - qe_project_z3(m, vars, fml, M, reduce_all_selects, use_native_mbp, dont_sub); - else - qe_project_spacer(m, vars, fml, M, reduce_all_selects, use_native_mbp, dont_sub); -} + void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + const model_ref& M, bool reduce_all_selects, bool use_native_mbp, + bool dont_sub) { + if (use_native_mbp) + qe_project_z3(m, vars, fml, M, reduce_all_selects, use_native_mbp, dont_sub); + else + qe_project_spacer(m, vars, fml, M, reduce_all_selects, use_native_mbp, dont_sub); + } -void expand_literals(ast_manager &m, expr_ref_vector& conjs) -{ - if (conjs.empty()) { return; } + void expand_literals(ast_manager &m, expr_ref_vector& conjs) { + if (conjs.empty()) { return; } arith_util arith(m); datatype_util dt(m); bv_util bv(m); @@ -689,11 +360,7 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) rational r; unsigned bv_size; - TRACE("spacer_expand", - tout << "begin expand\n"; - for (unsigned i = 0; i < conjs.size(); ++i) { - tout << mk_pp(conjs[i].get(), m) << "\n"; - }); + TRACE("spacer_expand", tout << "begin expand\n" << conjs << "\n";); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); @@ -701,13 +368,13 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) conjs[i] = arith.mk_le(e1,e2); if (i+1 == conjs.size()) { conjs.push_back(arith.mk_ge(e1, e2)); - } else { + } else { conjs.push_back(conjs[i+1].get()); conjs[i+1] = arith.mk_ge(e1, e2); } ++i; - } else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) || - (m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))){ + } else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) || + (m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))) { func_decl* f = to_app(val)->get_decl(); func_decl* r = dt.get_constructor_is(f); conjs[i] = m.mk_app(r, c); @@ -715,12 +382,11 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) for (unsigned j = 0; j < acc.size(); ++j) { conjs.push_back(m.mk_eq(apply_accessor(m, acc, j, f, c), to_app(val)->get_arg(j))); } - } else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) || - (m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) { + } else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) || + (m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) { rational two(2); for (unsigned j = 0; j < bv_size; ++j) { parameter p(j); - //expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c)); if ((r % two).is_zero()) { e = m.mk_not(e); @@ -728,21 +394,17 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) r = div(r, two); if (j == 0) { conjs[i] = e; - } else { + } else { conjs.push_back(e); } } } } - TRACE("spacer_expand", - tout << "end expand\n"; - for (unsigned i = 0; i < conjs.size(); ++i) { - tout << mk_pp(conjs[i].get(), m) << "\n"; - }); + TRACE("spacer_expand", tout << "end expand\n" << conjs << "\n";); } namespace { -class implicant_picker { + class implicant_picker { model_evaluator_util &m_mev; ast_manager &m; arith_util m_arith; @@ -751,8 +413,7 @@ class implicant_picker { expr_mark m_visited; - void add_literal (expr *e, expr_ref_vector &out) - { + void add_literal (expr *e, expr_ref_vector &out) { SASSERT (m.is_bool (e)); expr_ref res (m), v(m); @@ -773,251 +434,245 @@ class implicant_picker { if (m.is_not(res, nres)) { // -- (not (xor a b)) == (= a b) if (m.is_xor(nres, f1, f2)) - { res = m.mk_eq(f1, f2); } - + { res = m.mk_eq(f1, f2); } + // -- split arithmetic inequality else if (m.is_eq (nres, f1, f2) && m_arith.is_int_real (f1)) { expr_ref u(m); u = m_arith.mk_lt(f1, f2); if (m_mev.eval (u, v, false) && m.is_true (v)) - { res = u; } + { res = u; } else - { res = m_arith.mk_lt(f2, f1); } + { res = m_arith.mk_lt(f2, f1); } } } - if (!m_mev.is_true (res)) - { verbose_stream() << "Bad literal: " << mk_pp(res, m) << "\n"; } + if (!m_mev.is_true (res)) { + verbose_stream() << "Bad literal: " << mk_pp(res, m) << "\n"; + } SASSERT (m_mev.is_true (res)); out.push_back (res); } - void process_app(app *a, expr_ref_vector &out) - { - if (m_visited.is_marked(a)) { return; } + void process_app(app *a, expr_ref_vector &out) { + if (m_visited.is_marked(a)) { return; } SASSERT (m.is_bool (a)); expr_ref v(m); m_mev.eval (a, v, false); - - if (!m.is_true(v) && !m.is_false(v)) { return; } - + bool is_true = m.is_true(v); + + if (!is_true && !m.is_false(v)) return; + expr *na, *f1, *f2, *f3; - - if (a->get_family_id() != m.get_basic_family_id()) - { add_literal(a, out); } - else if (is_uninterp_const(a)) - { add_literal(a, out); } - else if (m.is_not(a, na) && m.is_not(na, na)) - { m_todo.push_back(na); } - else if (m.is_not(a, na)) - { m_todo.push_back(na); } + + if (a->get_family_id() != m.get_basic_family_id()) { + add_literal(a, out); + } + else if (is_uninterp_const(a)) { + add_literal(a, out); + } + else if (m.is_not(a, na)) { + m_todo.push_back(na); + } else if (m.is_distinct(a)) { - if (m.is_false(v)) - m_todo.push_back - (qe::project_plugin::pick_equality(m, *m_mev.get_model(), a)); - else if (a->get_num_args() == 2) - { add_literal(a, out); } - else - m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), - a->get_args())); - } else if (m.is_and(a)) { - if (m.is_true(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } - else if (m.is_false(v)) { - for (unsigned i = 0, sz = a->get_num_args (); i < sz; ++i) { - if (m_mev.is_false(a->get_arg(i))) { - m_todo.push_back(a->get_arg(i)); + if (!is_true) { + f1 = qe::project_plugin::pick_equality(m, *m_mev.get_model(), a); + m_todo.push_back(f1); + } + else if (a->get_num_args() == 2) { + add_literal(a, out); + } + else { + m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), a->get_args())); + } + } + else if (m.is_and(a)) { + if (is_true) { + m_todo.append(a->get_num_args(), a->get_args()); + } + else { + for (expr* e : *a) { + if (m_mev.is_false(e)) { + m_todo.push_back(e); break; } } } - } else if (m.is_or(a)) { - if (m.is_false(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } - else if (m.is_true(v)) { - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - if (m_mev.is_true(a->get_arg(i))) { - m_todo.push_back(a->get_arg(i)); + } + else if (m.is_or(a)) { + if (!is_true) + m_todo.append(a->get_num_args(), a->get_args()); + else { + for (expr * e : *a) { + if (m_mev.is_true(e)) { + m_todo.push_back(e); break; } } } - } else if (m.is_iff(a, f1, f2) || m.is_eq(a, f1, f2) || - (m.is_true(v) && m.is_not(a, na) && m.is_xor (na, f1, f2))) { + } + else if (m.is_eq(a, f1, f2) || (is_true && m.is_not(a, na) && m.is_xor (na, f1, f2))) { if (!m.are_equal(f1, f2) && !m.are_distinct(f1, f2)) { - if (m.is_bool(f1) && - (!is_uninterp_const(f1) || !is_uninterp_const(f2))) - { m_todo.append(a->get_num_args(), a->get_args()); } + if (m.is_bool(f1) && (!is_uninterp_const(f1) || !is_uninterp_const(f2))) + m_todo.append(a->get_num_args(), a->get_args()); else - { add_literal(a, out); } + add_literal(a, out); + } + } + else if (m.is_ite(a, f1, f2, f3)) { + if (m.are_equal(f2, f3)) { + m_todo.push_back(f2); } - } else if (m.is_ite(a, f1, f2, f3)) { - if (m.are_equal(f2, f3)) { m_todo.push_back(f2); } else if (m_mev.is_true (f2) && m_mev.is_true (f3)) { - m_todo.push_back(f2); - m_todo.push_back(f3); - } else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { - m_todo.push_back(f2); - m_todo.push_back(f3); - } else if (m_mev.is_true(f1)) { - m_todo.push_back(f1); - m_todo.push_back(f2); - } else if (m_mev.is_false(f1)) { - m_todo.push_back(f1); - m_todo.push_back(f3); + m_todo.push_back(f2); + m_todo.push_back(f3); + } + else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { + m_todo.push_back(f2); + m_todo.push_back(f3); + } + else if (m_mev.is_true(f1)) { + m_todo.push_back(f1); + m_todo.push_back(f2); + } + else if (m_mev.is_false(f1)) { + m_todo.push_back(f1); + m_todo.push_back(f3); } - } else if (m.is_xor(a, f1, f2)) - { m_todo.append(a->get_num_args(), a->get_args()); } + } + else if (m.is_xor(a, f1, f2)) { + m_todo.append(a->get_num_args(), a->get_args()); + } else if (m.is_implies(a, f1, f2)) { - if (m.is_true (v)) { - if (m_mev.is_true(f2)) { m_todo.push_back(f2); } - else if (m_mev.is_false(f1)) { m_todo.push_back(f1); } - } else if (m.is_false(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } - } else if (m.is_true(a) || m.is_false(a)) { /* nothing */ } + if (is_true) { + if (m_mev.is_true(f2)) + m_todo.push_back(f2); + else if (m_mev.is_false(f1)) + m_todo.push_back(f1); + } + else + m_todo.append(a->get_num_args(), a->get_args()); + } else { - verbose_stream () << "Unexpected expression: " - << mk_pp(a, m) << "\n"; + IF_VERBOSE(0, verbose_stream () << "Unexpected expression: " << mk_pp(a, m) << "\n"); UNREACHABLE(); } } - - void pick_literals(expr *e, expr_ref_vector &out) - { + + void pick_literals(expr *e, expr_ref_vector &out) { SASSERT(m_todo.empty()); - if (m_visited.is_marked(e)) { return; } - SASSERT(is_app(e)); - + if (m_visited.is_marked(e) || !is_app(e)) return; + m_todo.push_back(e); do { - app *a = to_app(m_todo.back()); + e = m_todo.back(); + if (!is_app(e)) continue; + app * a = to_app(e); m_todo.pop_back(); process_app(a, out); m_visited.mark(a, true); } while (!m_todo.empty()); } - - bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) - { + + bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) { m_visited.reset(); - expr_ref e(m); - e = mk_and (in); - bool is_true = m_mev.is_true (e); - - for (unsigned i = 0, sz = in.size (); i < sz; ++i) { - if (is_true || m_mev.is_true(in.get(i))) - { pick_literals(in.get(i), out); } + bool is_true = m_mev.is_true (in); + + for (expr* e : in) { + if (is_true || m_mev.is_true(e)) { + pick_literals(e, out); + } } - m_visited.reset (); return is_true; } public: + implicant_picker (model_evaluator_util &mev) : m_mev (mev), m (m_mev.get_ast_manager ()), m_arith(m), m_todo(m) {} - - void operator() (expr_ref_vector &in, expr_ref_vector& out) - {pick_implicant (in, out);} - }; - } - - void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, - expr_ref_vector &res) { - // XXX what is the point of flattening? - flatten_and (formula); - if (formula.empty()) { return; } - - implicant_picker ipick (mev); - ipick (formula, res); - } - -void simplify_bounds_old(expr_ref_vector& cube) { - ast_manager& m = cube.m(); - - scoped_no_proof _no_pf_(m); - goal_ref g(alloc(goal, m, false, false, false)); - - for (unsigned i = 0; i < cube.size(); ++i) { - g->assert_expr(cube.get(i)); - } - - expr_ref tmp(m); - goal_ref_buffer result; - tactic_ref simplifier = mk_arith_bounds_tactic(m); - (*simplifier)(g, result); - SASSERT(result.size() == 1); - goal* r = result[0]; - - cube.reset(); - for (unsigned i = 0; i < r->size(); ++i) { - cube.push_back(r->form(i)); - } -} - -void simplify_bounds_new (expr_ref_vector &cube) { - ast_manager &m = cube.m(); - - - scoped_no_proof _no_pf_(m); - - goal_ref g(alloc(goal, m, false, false, false)); - for (unsigned i = 0, sz = cube.size(); i < sz; ++i) { - g->assert_expr(cube.get(i)); - } - - goal_ref_buffer goals; - tactic_ref prop_values = mk_propagate_values_tactic(m); - tactic_ref prop_bounds = mk_propagate_ineqs_tactic(m); - tactic_ref t = and_then(prop_values.get(), prop_bounds.get()); - - (*t)(g, goals); - SASSERT(goals.size() == 1); - - g = goals[0]; - cube.reset(); - for (unsigned i = 0; i < g->size(); ++i) { - cube.push_back(g->form(i)); - } -} - -void simplify_bounds(expr_ref_vector &cube) { - simplify_bounds_new(cube); -} - -/// Adhoc rewriting of arithmetic expressions -struct adhoc_rewriter_cfg : public default_rewriter_cfg { - ast_manager &m; - arith_util m_util; - - adhoc_rewriter_cfg (ast_manager &manager) : m(manager), m_util(m) {} - - bool is_le(func_decl const * n) const - { return is_decl_of(n, m_util.get_family_id (), OP_LE); } - bool is_ge(func_decl const * n) const - { return is_decl_of(n, m_util.get_family_id (), OP_GE); } - - br_status reduce_app (func_decl * f, unsigned num, expr * const * args, - expr_ref & result, proof_ref & result_pr) - { - expr * e; - br_status st = BR_FAILED; - if (is_le(f)) { - st = mk_le_core (args[0], args[1], result); - } else if (is_ge(f)) { - st = mk_ge_core (args[0], args[1], result); - } else if (m.is_not(f)) { - if (m.is_not (args[0], e)) { - result = e; - st = BR_DONE; - } - } - - return st; + + void operator() (expr_ref_vector &in, expr_ref_vector& out) { + pick_implicant (in, out); } + }; +} - br_status mk_le_core (expr *arg1, expr * arg2, expr_ref & result) - { + void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, + expr_ref_vector &res) { + implicant_picker ipick (mev); + ipick (formula, res); + } + + void simplify_bounds_old(expr_ref_vector& cube) { + ast_manager& m = cube.m(); + scoped_no_proof _no_pf_(m); + goal_ref g(alloc(goal, m, false, false, false)); + for (expr* c : cube) + g->assert_expr(c); + + goal_ref_buffer result; + tactic_ref simplifier = mk_arith_bounds_tactic(m); + (*simplifier)(g, result); + SASSERT(result.size() == 1); + goal* r = result[0]; + cube.reset(); + for (unsigned i = 0; i < r->size(); ++i) { + cube.push_back(r->form(i)); + } + } + + void simplify_bounds_new (expr_ref_vector &cube) { + ast_manager &m = cube.m(); + scoped_no_proof _no_pf_(m); + goal_ref g(alloc(goal, m, false, false, false)); + for (expr* c : cube) + g->assert_expr(c); + + goal_ref_buffer goals; + tactic_ref prop_values = mk_propagate_values_tactic(m); + tactic_ref prop_bounds = mk_propagate_ineqs_tactic(m); + tactic_ref t = and_then(prop_values.get(), prop_bounds.get()); + + (*t)(g, goals); + SASSERT(goals.size() == 1); + + g = goals[0]; + cube.reset(); + for (unsigned i = 0; i < g->size(); ++i) { + cube.push_back(g->form(i)); + } + } + + void simplify_bounds(expr_ref_vector &cube) { + simplify_bounds_new(cube); + } + + /// Adhoc rewriting of arithmetic expressions + struct adhoc_rewriter_cfg : public default_rewriter_cfg { + ast_manager &m; + arith_util m_util; + + adhoc_rewriter_cfg (ast_manager &manager) : m(manager), m_util(m) {} + + bool is_le(func_decl const * n) const { return m_util.is_le(n); } + bool is_ge(func_decl const * n) const { return m_util.is_ge(n); } + + br_status reduce_app (func_decl * f, unsigned num, expr * const * args, + expr_ref & result, proof_ref & result_pr) { + expr * e; + if (is_le(f)) + return mk_le_core (args[0], args[1], result); + if (is_ge(f)) + return mk_ge_core (args[0], args[1], result); + if (m.is_not(f) && m.is_not (args[0], e)) { + result = e; + return BR_DONE; + } + return BR_FAILED; + } + + br_status mk_le_core (expr *arg1, expr * arg2, expr_ref & result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) if (m_util.is_int (arg1) && m_util.is_minus_one (arg2)) { result = m.mk_not (m_util.mk_ge (arg1, mk_zero ())); @@ -1025,63 +680,63 @@ struct adhoc_rewriter_cfg : public default_rewriter_cfg { } return BR_FAILED; } - br_status mk_ge_core (expr * arg1, expr * arg2, expr_ref & result) - { + br_status mk_ge_core (expr * arg1, expr * arg2, expr_ref & result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) if (m_util.is_int (arg1) && is_one (arg2)) { - + result = m.mk_not (m_util.mk_le (arg1, mk_zero ())); return BR_DONE; } return BR_FAILED; } - expr * mk_zero () {return m_util.mk_numeral (rational (0), true);} - bool is_one (expr const * n) const - {rational val; return m_util.is_numeral (n, val) && val.is_one ();} -}; + expr * mk_zero () {return m_util.mk_numeral (rational (0), true);} + bool is_one (expr const * n) const { + rational val; return m_util.is_numeral (n, val) && val.is_one (); + } + }; -void normalize (expr *e, expr_ref &out, - bool use_simplify_bounds, - bool use_factor_eqs) -{ - - params_ref params; - // arith_rewriter - params.set_bool ("sort_sums", true); - params.set_bool ("gcd_rounding", true); - params.set_bool ("arith_lhs", true); - // poly_rewriter - params.set_bool ("som", true); - params.set_bool ("flat", true); - - // apply rewriter - th_rewriter rw(out.m(), params); - rw (e, out); - - adhoc_rewriter_cfg adhoc_cfg(out.m ()); - rewriter_tpl adhoc_rw (out.m (), false, adhoc_cfg); - adhoc_rw (out.get (), out); - - if (out.m().is_and(out)) { - expr_ref_vector v(out.m()); - flatten_and (out, v); - - if (v.size() > 1) { - // sort arguments of the top-level and - std::stable_sort (v.c_ptr(), v.c_ptr () + v.size (), ast_lt_proc()); - - if (use_simplify_bounds) { - // remove redundant inequalities - simplify_bounds (v); - } - if (use_factor_eqs) { - // -- refactor equivalence classes and choose a representative - spacer::term_graph egraph(out.m()); - egraph.add_lits (v); - v.reset(); - egraph.to_lits(v); - } + void normalize (expr *e, expr_ref &out, + bool use_simplify_bounds, + bool use_factor_eqs) + { + + params_ref params; + // arith_rewriter + params.set_bool ("sort_sums", true); + params.set_bool ("gcd_rounding", true); + params.set_bool ("arith_lhs", true); + // poly_rewriter + params.set_bool ("som", true); + params.set_bool ("flat", true); + + // apply rewriter + th_rewriter rw(out.m(), params); + rw (e, out); + + adhoc_rewriter_cfg adhoc_cfg(out.m ()); + rewriter_tpl adhoc_rw (out.m (), false, adhoc_cfg); + adhoc_rw (out.get (), out); + if (out.m().is_and(out)) { + expr_ref_vector v(out.m()); + flatten_and (out, v); + + if (v.size() > 1) { + // sort arguments of the top-level and + std::stable_sort (v.c_ptr(), v.c_ptr() + v.size(), ast_lt_proc()); + + if (use_simplify_bounds) { + // remove redundant inequalities + simplify_bounds (v); + } + if (use_factor_eqs) { + // -- refactor equivalence classes and choose a representative + spacer::term_graph egraph(out.m()); + egraph.add_lits (v); + v.reset(); + egraph.to_lits(v); + } + TRACE("spacer_normalize", tout << "Normalized:\n" << out << "\n" @@ -1089,39 +744,33 @@ void normalize (expr *e, expr_ref &out, << mk_and(v) << "\n";); TRACE("spacer_normalize", spacer::term_graph egraph(out.m()); - for (unsigned i = 0, sz = v.size(); i < sz; ++i) - egraph.add_lit (to_app(v.get(i))); + for (expr* e : v) egraph.add_lit (to_app(e)); tout << "Reduced app:\n" << mk_pp(egraph.to_app(), out.m()) << "\n";); - out = mk_and (v); + out = mk_and (v); + } } } -} -// rewrite term such that the pretty printing is easier to read -struct adhoc_rewriter_rpp : public default_rewriter_cfg { - ast_manager &m; - arith_util m_arith; - - adhoc_rewriter_rpp (ast_manager &manager) : m(manager), m_arith(m) {} - - bool is_le(func_decl const * n) const - { return is_decl_of(n, m_arith.get_family_id (), OP_LE); } - bool is_ge(func_decl const * n) const - { return is_decl_of(n, m_arith.get_family_id (), OP_GE); } - bool is_lt(func_decl const * n) const - { return is_decl_of(n, m_arith.get_family_id (), OP_LT); } - bool is_gt(func_decl const * n) const - { return is_decl_of(n, m_arith.get_family_id (), OP_GT); } - bool is_zero (expr const * n) const - {rational val; return m_arith.is_numeral(n, val) && val.is_zero();} - - br_status reduce_app (func_decl * f, unsigned num, expr * const * args, - expr_ref & result, proof_ref & result_pr) + // rewrite term such that the pretty printing is easier to read + struct adhoc_rewriter_rpp : public default_rewriter_cfg { + ast_manager &m; + arith_util m_arith; + + adhoc_rewriter_rpp (ast_manager &manager) : m(manager), m_arith(m) {} + + bool is_le(func_decl const * n) const { return m_arith.is_le(n); } + bool is_ge(func_decl const * n) const { return m_arith.is_ge(n); } + bool is_lt(func_decl const * n) const { return m_arith.is_lt(n); } + bool is_gt(func_decl const * n) const { return m_arith.is_gt(n); } + bool is_zero (expr const * n) const {rational val; return m_arith.is_numeral(n, val) && val.is_zero();} + + br_status reduce_app (func_decl * f, unsigned num, expr * const * args, + expr_ref & result, proof_ref & result_pr) { br_status st = BR_FAILED; expr *e1, *e2, *e3, *e4; - + // rewrites (= (+ A (* -1 B)) 0) into (= A B) if (m.is_eq (f) && is_zero (args [1]) && m_arith.is_add (args[0], e1, e2) && @@ -1170,67 +819,63 @@ struct adhoc_rewriter_rpp : public default_rewriter_cfg { } return st; } + }; -}; - -mk_epp::mk_epp(ast *t, ast_manager &m, unsigned indent, - unsigned num_vars, char const * var_prefix) : - mk_pp (t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { - m_epp_params.set_uint("min_alias_size", UINT_MAX); - m_epp_params.set_uint("max_depth", UINT_MAX); - - if (is_expr (m_ast)) { - rw(to_expr(m_ast), m_epp_expr); - m_ast = m_epp_expr; + mk_epp::mk_epp(ast *t, ast_manager &m, unsigned indent, + unsigned num_vars, char const * var_prefix) : + mk_pp (t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { + m_epp_params.set_uint("min_alias_size", UINT_MAX); + m_epp_params.set_uint("max_depth", UINT_MAX); + + if (is_expr (m_ast)) { + rw(to_expr(m_ast), m_epp_expr); + m_ast = m_epp_expr; + } } -} - -void mk_epp::rw(expr *e, expr_ref &out) -{ - adhoc_rewriter_rpp cfg(out.m()); - rewriter_tpl arw(out.m(), false, cfg); - arw(e, out); -} - -void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { - expr_free_vars fv; - ast_manager &m = out.get_manager(); - - fv(e); - if (vars.size() < fv.size()) { - vars.resize(fv.size()); + + void mk_epp::rw(expr *e, expr_ref &out) { + adhoc_rewriter_rpp cfg(out.m()); + rewriter_tpl arw(out.m(), false, cfg); + arw(e, out); } - for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { - sort *s = fv[i] ? fv[i] : m.mk_bool_sort(); - vars[i] = mk_zk_const(m, i, s); - var_subst vs(m, false); - vs(e, vars.size(), (expr * *) vars.c_ptr(), out); + + void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { + expr_free_vars fv; + ast_manager &m = out.get_manager(); + + fv(e); + if (vars.size() < fv.size()) { + vars.resize(fv.size()); + } + for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { + sort *s = fv[i] ? fv[i] : m.mk_bool_sort(); + vars[i] = mk_zk_const(m, i, s); + var_subst vs(m, false); + vs(e, vars.size(), (expr * *) vars.c_ptr(), out); + } } -} - struct index_term_finder { - ast_manager &m; - array_util m_array; - app_ref m_var; + ast_manager &m; + array_util m_array; + app_ref m_var; expr_ref_vector &m_res; index_term_finder (ast_manager &mgr, app* v, expr_ref_vector &res) : m(mgr), m_array (m), m_var (v, m), m_res (res) {} void operator() (var *n) {} void operator() (quantifier *n) {} void operator() (app *n) { - expr *e1, *e2; - if (m_array.is_select (n) && n->get_arg (1) != m_var) { - m_res.push_back (n->get_arg (1)); - } else if (m.is_eq(n, e1, e2)) { - if (e1 == m_var) { m_res.push_back(e2); } - else if (e2 == m_var) { m_res.push_back(e1); } - } + if (m_array.is_select (n) || m.is_eq(n)) { + unsigned i = 0; + for (expr * arg : *n) { + if ((m.is_eq(n) || i > 0) && m_var != arg) m_res.push_back (arg); + ++i; + } + } } }; - bool mbqi_project_var (model_evaluator_util &mev, app* var, expr_ref &fml) - { + bool mbqi_project_var (model_evaluator_util &mev, app* var, expr_ref &fml) { ast_manager &m = fml.get_manager (); expr_ref val(m); @@ -1238,26 +883,21 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { TRACE ("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp (var, m) << "\n" - << "fml: " << mk_pp (fml, m) << "\n";); + << "fml: " << fml << "\n";); expr_ref_vector terms (m); index_term_finder finder (m, var, terms); for_each_expr (finder, fml); - TRACE ("mbqi_project_verbose", - tout << "terms:\n"; - for (unsigned i = 0, e = terms.size (); i < e; ++i) - tout << i << ": " << mk_pp (terms.get (i), m) << "\n"; - ); - - for (unsigned i = 0, e = terms.size(); i < e; ++i) { - expr* term = terms.get (i); + tout << "terms:\n" << terms << "\n";); + + for (expr * term : terms) { expr_ref tval (m); mev.eval (term, tval, false); TRACE ("mbqi_project_verbose", tout << "term: " << mk_pp (term, m) - << " tval: " << mk_pp (tval, m) + << " tval: " << tval << " val: " << mk_pp (val, m) << "\n";); // -- if the term does not contain an occurrence of var @@ -1273,13 +913,12 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { } TRACE ("mbqi_project", - tout << "MBQI: failed to eliminate " << mk_pp (var, m) << " from " << mk_pp (fml, m) << "\n";); + tout << "MBQI: failed to eliminate " << mk_pp (var, m) << " from " << fml << "\n";); return false; } - void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml) - { + void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml) { ast_manager &m = fml.get_manager (); model_evaluator_util mev(m); mev.set_model (M); @@ -1288,110 +927,67 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { mev.eval (fml, tmp, false); tmp.reset (); - for (unsigned idx = 0; idx < vars.size (); ) { - if (mbqi_project_var (mev, vars.get (idx), fml)) { - vars[idx] = vars.back (); - vars.pop_back (); - } else { - idx++; - } + unsigned j = 0; + for (app* v : vars) + if (!mbqi_project_var (mev, v, fml)) + vars[j++] = v; + vars.shrink(j); + } + + struct found {}; + struct check_select { + array_util a; + check_select(ast_manager& m): a(m) {} + void operator()(expr* n) {} + void operator()(app* n) { if (a.is_select(n)) throw found(); } + }; + + bool contains_selects(expr* fml, ast_manager& m) { + check_select cs(m); + try { + for_each_expr(cs, fml); + return false; + } + catch (found) { + return true; } } -bool contains_selects(expr* fml, ast_manager& m) -{ - array_util a_util(m); - if (!is_app(fml)) { return false; } - ast_mark done; - ptr_vector todo; - todo.push_back (to_app (fml)); - while (!todo.empty ()) { - app* a = todo.back (); - if (done.is_marked (a)) { - todo.pop_back (); - continue; - } - unsigned num_args = a->get_num_args (); - bool all_done = true; - for (unsigned i = 0; i < num_args; i++) { - expr* arg = a->get_arg (i); - if (!done.is_marked (arg) && is_app (arg)) { - todo.push_back (to_app (arg)); - all_done = false; - } - } - if (!all_done) { continue; } - todo.pop_back (); - if (a_util.is_select (a)) - { return true; } - done.mark (a, true); + struct collect_indices { + app_ref_vector& m_indices; + array_util a; + collect_indices(app_ref_vector& indices): m_indices(indices), a(indices.get_manager()) {} + void operator()(expr* n) {} + void operator()(app* n) { + if (a.is_select (n)) + for (unsigned i = 1; i < n->get_num_args(); ++i) + if (is_app(n->get_arg(i))) + m_indices.push_back(to_app(n->get_arg(i))); } - return false; + }; + + void get_select_indices(expr* fml, app_ref_vector &indices, ast_manager& m) { + collect_indices ci(indices); + for_each_expr(ci, fml); + } + + struct collect_decls { + app_ref_vector& m_decls; + std::string& prefix; + collect_decls(app_ref_vector& decls, std::string& p): m_decls(decls), prefix(p) {} + void operator()(expr* n) {} + void operator()(app* n) { + if (n->get_decl()->get_name().str().find(prefix) != std::string::npos) + m_decls.push_back(n); + } + }; + + void find_decls(expr* fml, app_ref_vector& decls, std::string& prefix) { + collect_decls cd(decls, prefix); + for_each_expr(cd, fml); } -void get_select_indices(expr* fml, app_ref_vector &indices, ast_manager& m) -{ - array_util a_util(m); - if (!is_app(fml)) { return; } - ast_mark done; - ptr_vector todo; - todo.push_back (to_app (fml)); - while (!todo.empty ()) { - app* a = todo.back (); - if (done.is_marked (a)) { - todo.pop_back (); - continue; - } - unsigned num_args = a->get_num_args (); - bool all_done = true; - for (unsigned i = 0; i < num_args; i++) { - expr* arg = a->get_arg (i); - if (!done.is_marked (arg) && is_app (arg)) { - todo.push_back (to_app (arg)); - all_done = false; - } - } - if (!all_done) { continue; } - todo.pop_back (); - if (a_util.is_select (a)) { - SASSERT(a->get_num_args() == 2); - indices.push_back(to_app(a->get_arg(1))); - } - done.mark (a, true); - } - } - -void find_decls(expr* fml, app_ref_vector& decls, std::string& prefix) -{ - if (!is_app(fml)) { return; } - ast_mark done; - ptr_vector todo; - todo.push_back (to_app (fml)); - while (!todo.empty ()) { - app* a = todo.back (); - if (done.is_marked (a)) { - todo.pop_back (); - continue; - } - unsigned num_args = a->get_num_args (); - bool all_done = true; - for (unsigned i = 0; i < num_args; i++) { - expr* arg = a->get_arg (i); - if (!done.is_marked (arg) && is_app (arg)) { - todo.push_back (to_app (arg)); - all_done = false; - } - } - if (!all_done) { continue; } - todo.pop_back (); - if (a->get_decl()->get_name().str().find(prefix) != std::string::npos) - { decls.push_back(a); } - done.mark (a, true); - } - return; } -} template class rewriter_tpl; template class rewriter_tpl; -template class rewriter_tpl; diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index ffbd81de7..f43195a97 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -44,135 +44,120 @@ class model_evaluator; namespace spacer { -inline unsigned infty_level () {return UINT_MAX;} - -inline bool is_infty_level(unsigned lvl) -{ return lvl == infty_level (); } - -inline unsigned next_level(unsigned lvl) -{ return is_infty_level(lvl)?lvl:(lvl+1); } - -inline unsigned prev_level (unsigned lvl) -{ - if(is_infty_level(lvl)) { return infty_level(); } - if(lvl == 0) { return 0; } - return lvl -1; -} - -struct pp_level { - unsigned m_level; - pp_level(unsigned l): m_level(l) {} -}; - -inline std::ostream& operator<<(std::ostream& out, pp_level const& p) -{ - if (is_infty_level(p.m_level)) { - return out << "oo"; - } else { - return out << p.m_level; + inline unsigned infty_level () { + return UINT_MAX; } -} + inline bool is_infty_level(unsigned lvl) { + return lvl == infty_level (); + } + inline unsigned next_level(unsigned lvl) { + return is_infty_level(lvl)?lvl:(lvl+1); + } + inline unsigned prev_level (unsigned lvl) { + if (is_infty_level(lvl)) return infty_level(); + if (lvl == 0) return 0; + return lvl - 1; + } -typedef ptr_vector app_vector; -typedef ptr_vector decl_vector; -typedef obj_hashtable func_decl_set; + struct pp_level { + unsigned m_level; + pp_level(unsigned l): m_level(l) {} + }; + inline std::ostream& operator<<(std::ostream& out, pp_level const& p) { + if (is_infty_level(p.m_level)) { + return out << "oo"; + } else { + return out << p.m_level; + } + } -class model_evaluator_util { - ast_manager& m; - model_ref m_model; - model_evaluator* m_mev; + typedef ptr_vector app_vector; + typedef ptr_vector decl_vector; + typedef obj_hashtable func_decl_set; + + // TBD: deprecate + class model_evaluator_util { + ast_manager& m; + model_ref m_model; + model_evaluator* m_mev; + + /// initialize with a given model. All previous state is lost. model can be NULL + void reset (model *model); + public: + model_evaluator_util(ast_manager& m); + ~model_evaluator_util(); + + void set_model(model &model) {reset (&model);} + model_ref &get_model() {return m_model;} + ast_manager& get_ast_manager() const {return m;} + + public: + bool is_true (const expr_ref_vector &v); + bool is_false(expr* x); + bool is_true(expr* x); + + bool eval (const expr_ref_vector &v, expr_ref &result, bool model_completion); + /// evaluates an expression + bool eval (expr *e, expr_ref &result, bool model_completion); + // expr_ref eval(expr* e, bool complete=true); + }; - /// initialize with a given model. All previous state is lost. model can be NULL - void reset (model *model); -public: - model_evaluator_util(ast_manager& m); - ~model_evaluator_util(); + /** + \brief hoist non-boolean if expressions. + */ + + void to_mbp_benchmark(std::ostream &out, const expr* fml, const app_ref_vector &vars); - void set_model(model &model) {reset (&model);} - model_ref &get_model() {return m_model;} - ast_manager& get_ast_manager() const {return m;} + + // TBD: deprecate by qe::mbp + /** + * do the following in sequence + * 1. use qe_lite to cheaply eliminate vars + * 2. for remaining boolean vars, substitute using M + * 3. use MBP for remaining array and arith variables + * 4. for any remaining arith variables, substitute using M + */ + void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + const model_ref& M, bool reduce_all_selects=false, bool native_mbp=false, + bool dont_sub=false); -public: - bool is_true (const expr_ref_vector &v); - bool is_false(expr* x); - bool is_true(expr* x); - - bool eval (const expr_ref_vector &v, expr_ref &result, bool model_completion); - /// evaluates an expression - bool eval (expr *e, expr_ref &result, bool model_completion); - // expr_ref eval(expr* e, bool complete=true); -}; - - -/** - \brief replace variables that are used in many disequalities by - an equality using the model. - - Assumption: the model satisfies the conjunctions. -*/ -void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); - -/** - \brief hoist non-boolean if expressions. -*/ -void hoist_non_bool_if(expr_ref& fml); - -bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); - -bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); - -void to_mbp_benchmark(std::ostream &out, const expr* fml, - const app_ref_vector &vars); - -/** - * do the following in sequence - * 1. use qe_lite to cheaply eliminate vars - * 2. for remaining boolean vars, substitute using M - * 3. use MBP for remaining array and arith variables - * 4. for any remaining arith variables, substitute using M - */ -void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects=false, bool native_mbp=false, - bool dont_sub=false); - -void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map); - -void expand_literals(ast_manager &m, expr_ref_vector& conjs); -void compute_implicant_literals (model_evaluator_util &mev, - expr_ref_vector &formula, expr_ref_vector &res); -void simplify_bounds (expr_ref_vector &lemmas); -void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false); - -/** Ground expression by replacing all free variables by skolem - ** constants. On return, out is the resulting expression, and vars is - ** a map from variable ids to corresponding skolem constants. - */ -void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); - - -void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml); - -bool contains_selects (expr* fml, ast_manager& m); -void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m); - -void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix); - -/** extended pretty-printer - * used for debugging - * disables aliasing of common sub-expressions -*/ -struct mk_epp : public mk_pp { - params_ref m_epp_params; - expr_ref m_epp_expr; + void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map); + + // TBD: sort out + void expand_literals(ast_manager &m, expr_ref_vector& conjs); + void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, expr_ref_vector &res); + void simplify_bounds (expr_ref_vector &lemmas); + void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false); + + /** + * Ground expression by replacing all free variables by skolem + * constants. On return, out is the resulting expression, and vars is + * a map from variable ids to corresponding skolem constants. + */ + void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); + + void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml); + + bool contains_selects (expr* fml, ast_manager& m); + void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m); + + void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix); + + /** + * extended pretty-printer + * used for debugging + * disables aliasing of common sub-expressions + */ + struct mk_epp : public mk_pp { + params_ref m_epp_params; + expr_ref m_epp_expr; mk_epp(ast *t, ast_manager &m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); - void rw(expr *e, expr_ref &out); - -}; - + void rw(expr *e, expr_ref &out); + }; } #endif From 18e3c7b13d0832b29ed6de72d90d38a4eae14863 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 8 Jun 2018 08:22:14 -0700 Subject: [PATCH 253/364] Fix bug introduced by formatting --- src/muz/spacer/spacer_util.cpp | 209 +++++++++++++++++---------------- 1 file changed, 106 insertions(+), 103 deletions(-) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 8941c511a..876a6528a 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -87,7 +87,7 @@ namespace spacer { m_mev = nullptr; } m_model = model; - if (m_model) + if (m_model) m_mev = alloc(model_evaluator, *m_model); } @@ -149,7 +149,7 @@ namespace spacer { out << "(define-fun mbp_benchmark_fml () Bool\n "; out << mk_pp(fml, m) << ")\n\n"; - + out << "(push)\n" << "(assert mbp_benchmark_fml)\n" << "(check-sat)\n" @@ -166,13 +166,13 @@ namespace spacer { params_ref p; p.set_bool("reduce_all_selects", reduce_all_selects); p.set_bool("dont_sub", dont_sub); - + qe::mbp mbp(m, p); // TODO: deal with const model *mdl = const_cast(M.get()); mbp.spacer(vars, *mdl, fml); } - + /* * eliminate simple equalities using qe_lite * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays @@ -209,14 +209,14 @@ namespace spacer { qe_lite qe(m, p, false); qe (vars, fml); rw (fml); - + TRACE ("spacer_mbp", tout << "After qe_lite:\n"; tout << mk_pp (fml, m) << "\n"; tout << "Vars:\n" << vars;); - + SASSERT (!m.is_false (fml)); - + // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { @@ -231,7 +231,7 @@ namespace spacer { arith_vars.push_back (v); } } - + // substitute Booleans if (!bool_sub.empty()) { bool_sub (fml); @@ -241,13 +241,13 @@ namespace spacer { TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); bool_sub.reset (); } - + TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); - + vars.reset (); - + // project arrays { scoped_no_proof _sp (m); @@ -258,17 +258,17 @@ namespace spacer { srw (fml); SASSERT (!m.is_false (fml)); } - + TRACE ("spacer_mbp", tout << "extended model:\n"; model_pp (tout, *M); tout << "Auxiliary variables of index and value sorts:\n"; tout << vars; ); - + if (vars.empty()) { break; } } - + // project reals and ints if (!arith_vars.empty ()) { TRACE ("spacer_mbp", tout << "Arith vars:\n" << arith_vars;); @@ -435,7 +435,7 @@ namespace { // -- (not (xor a b)) == (= a b) if (m.is_xor(nres, f1, f2)) { res = m.mk_eq(f1, f2); } - + // -- split arithmetic inequality else if (m.is_eq (nres, f1, f2) && m_arith.is_int_real (f1)) { expr_ref u(m); @@ -447,8 +447,8 @@ namespace { } } - if (!m_mev.is_true (res)) { - verbose_stream() << "Bad literal: " << mk_pp(res, m) << "\n"; + if (!m_mev.is_true (res)) { + verbose_stream() << "Bad literal: " << mk_pp(res, m) << "\n"; } SASSERT (m_mev.is_true (res)); out.push_back (res); @@ -460,35 +460,38 @@ namespace { expr_ref v(m); m_mev.eval (a, v, false); bool is_true = m.is_true(v); - + if (!is_true && !m.is_false(v)) return; - + expr *na, *f1, *f2, *f3; - - if (a->get_family_id() != m.get_basic_family_id()) { - add_literal(a, out); + + if (m.is_true(a) || m.is_false(a)) { + // noop } - else if (is_uninterp_const(a)) { - add_literal(a, out); + else if (a->get_family_id() != m.get_basic_family_id()) { + add_literal(a, out); } - else if (m.is_not(a, na)) { - m_todo.push_back(na); + else if (is_uninterp_const(a)) { + add_literal(a, out); + } + else if (m.is_not(a, na)) { + m_todo.push_back(na); } else if (m.is_distinct(a)) { if (!is_true) { f1 = qe::project_plugin::pick_equality(m, *m_mev.get_model(), a); m_todo.push_back(f1); } - else if (a->get_num_args() == 2) { - add_literal(a, out); + else if (a->get_num_args() == 2) { + add_literal(a, out); } else { m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), a->get_args())); } - } + } else if (m.is_and(a)) { - if (is_true) { - m_todo.append(a->get_num_args(), a->get_args()); + if (is_true) { + m_todo.append(a->get_num_args(), a->get_args()); } else { for (expr* e : *a) { @@ -498,10 +501,10 @@ namespace { } } } - } + } else if (m.is_or(a)) { if (!is_true) - m_todo.append(a->get_num_args(), a->get_args()); + m_todo.append(a->get_num_args(), a->get_args()); else { for (expr * e : *a) { if (m_mev.is_true(e)) { @@ -510,59 +513,59 @@ namespace { } } } - } + } else if (m.is_eq(a, f1, f2) || (is_true && m.is_not(a, na) && m.is_xor (na, f1, f2))) { if (!m.are_equal(f1, f2) && !m.are_distinct(f1, f2)) { if (m.is_bool(f1) && (!is_uninterp_const(f1) || !is_uninterp_const(f2))) - m_todo.append(a->get_num_args(), a->get_args()); + m_todo.append(a->get_num_args(), a->get_args()); else - add_literal(a, out); + add_literal(a, out); } - } + } else if (m.is_ite(a, f1, f2, f3)) { - if (m.are_equal(f2, f3)) { - m_todo.push_back(f2); + if (m.are_equal(f2, f3)) { + m_todo.push_back(f2); } else if (m_mev.is_true (f2) && m_mev.is_true (f3)) { m_todo.push_back(f2); m_todo.push_back(f3); - } + } else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { m_todo.push_back(f2); m_todo.push_back(f3); - } + } else if (m_mev.is_true(f1)) { m_todo.push_back(f1); m_todo.push_back(f2); - } + } else if (m_mev.is_false(f1)) { m_todo.push_back(f1); m_todo.push_back(f3); } - } - else if (m.is_xor(a, f1, f2)) { - m_todo.append(a->get_num_args(), a->get_args()); + } + else if (m.is_xor(a, f1, f2)) { + m_todo.append(a->get_num_args(), a->get_args()); } else if (m.is_implies(a, f1, f2)) { if (is_true) { - if (m_mev.is_true(f2)) - m_todo.push_back(f2); - else if (m_mev.is_false(f1)) - m_todo.push_back(f1); - } - else - m_todo.append(a->get_num_args(), a->get_args()); - } + if (m_mev.is_true(f2)) + m_todo.push_back(f2); + else if (m_mev.is_false(f1)) + m_todo.push_back(f1); + } + else + m_todo.append(a->get_num_args(), a->get_args()); + } else { IF_VERBOSE(0, verbose_stream () << "Unexpected expression: " << mk_pp(a, m) << "\n"); UNREACHABLE(); } } - + void pick_literals(expr *e, expr_ref_vector &out) { SASSERT(m_todo.empty()); - if (m_visited.is_marked(e) || !is_app(e)) return; - + if (m_visited.is_marked(e) || !is_app(e)) return; + m_todo.push_back(e); do { e = m_todo.back(); @@ -573,14 +576,14 @@ namespace { m_visited.mark(a, true); } while (!m_todo.empty()); } - + bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) { m_visited.reset(); bool is_true = m_mev.is_true (in); - + for (expr* e : in) { if (is_true || m_mev.is_true(e)) { - pick_literals(e, out); + pick_literals(e, out); } } m_visited.reset (); @@ -591,7 +594,7 @@ namespace { implicant_picker (model_evaluator_util &mev) : m_mev (mev), m (m_mev.get_ast_manager ()), m_arith(m), m_todo(m) {} - + void operator() (expr_ref_vector &in, expr_ref_vector& out) { pick_implicant (in, out); } @@ -605,17 +608,17 @@ namespace { } void simplify_bounds_old(expr_ref_vector& cube) { - ast_manager& m = cube.m(); + ast_manager& m = cube.m(); scoped_no_proof _no_pf_(m); - goal_ref g(alloc(goal, m, false, false, false)); - for (expr* c : cube) + goal_ref g(alloc(goal, m, false, false, false)); + for (expr* c : cube) g->assert_expr(c); - + goal_ref_buffer result; tactic_ref simplifier = mk_arith_bounds_tactic(m); (*simplifier)(g, result); SASSERT(result.size() == 1); - goal* r = result[0]; + goal* r = result[0]; cube.reset(); for (unsigned i = 0; i < r->size(); ++i) { cube.push_back(r->form(i)); @@ -624,19 +627,19 @@ namespace { void simplify_bounds_new (expr_ref_vector &cube) { ast_manager &m = cube.m(); - scoped_no_proof _no_pf_(m); + scoped_no_proof _no_pf_(m); goal_ref g(alloc(goal, m, false, false, false)); - for (expr* c : cube) + for (expr* c : cube) g->assert_expr(c); goal_ref_buffer goals; tactic_ref prop_values = mk_propagate_values_tactic(m); tactic_ref prop_bounds = mk_propagate_ineqs_tactic(m); tactic_ref t = and_then(prop_values.get(), prop_bounds.get()); - + (*t)(g, goals); SASSERT(goals.size() == 1); - + g = goals[0]; cube.reset(); for (unsigned i = 0; i < g->size(); ++i) { @@ -652,26 +655,26 @@ namespace { struct adhoc_rewriter_cfg : public default_rewriter_cfg { ast_manager &m; arith_util m_util; - + adhoc_rewriter_cfg (ast_manager &manager) : m(manager), m_util(m) {} - + bool is_le(func_decl const * n) const { return m_util.is_le(n); } bool is_ge(func_decl const * n) const { return m_util.is_ge(n); } - + br_status reduce_app (func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { expr * e; - if (is_le(f)) + if (is_le(f)) return mk_le_core (args[0], args[1], result); - if (is_ge(f)) + if (is_ge(f)) return mk_ge_core (args[0], args[1], result); if (m.is_not(f) && m.is_not (args[0], e)) { result = e; return BR_DONE; - } + } return BR_FAILED; } - + br_status mk_le_core (expr *arg1, expr * arg2, expr_ref & result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) if (m_util.is_int (arg1) && m_util.is_minus_one (arg2)) { @@ -683,7 +686,7 @@ namespace { br_status mk_ge_core (expr * arg1, expr * arg2, expr_ref & result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) if (m_util.is_int (arg1) && is_one (arg2)) { - + result = m.mk_not (m_util.mk_le (arg1, mk_zero ())); return BR_DONE; } @@ -699,7 +702,7 @@ namespace { bool use_simplify_bounds, bool use_factor_eqs) { - + params_ref params; // arith_rewriter params.set_bool ("sort_sums", true); @@ -708,11 +711,11 @@ namespace { // poly_rewriter params.set_bool ("som", true); params.set_bool ("flat", true); - + // apply rewriter th_rewriter rw(out.m(), params); rw (e, out); - + adhoc_rewriter_cfg adhoc_cfg(out.m ()); rewriter_tpl adhoc_rw (out.m (), false, adhoc_cfg); adhoc_rw (out.get (), out); @@ -720,11 +723,11 @@ namespace { if (out.m().is_and(out)) { expr_ref_vector v(out.m()); flatten_and (out, v); - + if (v.size() > 1) { // sort arguments of the top-level and std::stable_sort (v.c_ptr(), v.c_ptr() + v.size(), ast_lt_proc()); - + if (use_simplify_bounds) { // remove redundant inequalities simplify_bounds (v); @@ -736,7 +739,7 @@ namespace { v.reset(); egraph.to_lits(v); } - + TRACE("spacer_normalize", tout << "Normalized:\n" << out << "\n" @@ -756,21 +759,21 @@ namespace { struct adhoc_rewriter_rpp : public default_rewriter_cfg { ast_manager &m; arith_util m_arith; - + adhoc_rewriter_rpp (ast_manager &manager) : m(manager), m_arith(m) {} - + bool is_le(func_decl const * n) const { return m_arith.is_le(n); } bool is_ge(func_decl const * n) const { return m_arith.is_ge(n); } bool is_lt(func_decl const * n) const { return m_arith.is_lt(n); } bool is_gt(func_decl const * n) const { return m_arith.is_gt(n); } bool is_zero (expr const * n) const {rational val; return m_arith.is_numeral(n, val) && val.is_zero();} - + br_status reduce_app (func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { br_status st = BR_FAILED; expr *e1, *e2, *e3, *e4; - + // rewrites (= (+ A (* -1 B)) 0) into (= A B) if (m.is_eq (f) && is_zero (args [1]) && m_arith.is_add (args[0], e1, e2) && @@ -826,23 +829,23 @@ namespace { mk_pp (t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { m_epp_params.set_uint("min_alias_size", UINT_MAX); m_epp_params.set_uint("max_depth", UINT_MAX); - + if (is_expr (m_ast)) { rw(to_expr(m_ast), m_epp_expr); m_ast = m_epp_expr; } } - + void mk_epp::rw(expr *e, expr_ref &out) { adhoc_rewriter_rpp cfg(out.m()); rewriter_tpl arw(out.m(), false, cfg); arw(e, out); } - + void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { expr_free_vars fv; ast_manager &m = out.get_manager(); - + fv(e); if (vars.size() < fv.size()) { vars.resize(fv.size()); @@ -871,7 +874,7 @@ namespace { if ((m.is_eq(n) || i > 0) && m_var != arg) m_res.push_back (arg); ++i; } - } + } } }; @@ -890,7 +893,7 @@ namespace { TRACE ("mbqi_project_verbose", tout << "terms:\n" << terms << "\n";); - + for (expr * term : terms) { expr_ref tval (m); mev.eval (term, tval, false); @@ -928,8 +931,8 @@ namespace { tmp.reset (); unsigned j = 0; - for (app* v : vars) - if (!mbqi_project_var (mev, v, fml)) + for (app* v : vars) + if (!mbqi_project_var (mev, v, fml)) vars[j++] = v; vars.shrink(j); } @@ -958,11 +961,11 @@ namespace { array_util a; collect_indices(app_ref_vector& indices): m_indices(indices), a(indices.get_manager()) {} void operator()(expr* n) {} - void operator()(app* n) { - if (a.is_select (n)) - for (unsigned i = 1; i < n->get_num_args(); ++i) - if (is_app(n->get_arg(i))) - m_indices.push_back(to_app(n->get_arg(i))); + void operator()(app* n) { + if (a.is_select (n)) + for (unsigned i = 1; i < n->get_num_args(); ++i) + if (is_app(n->get_arg(i))) + m_indices.push_back(to_app(n->get_arg(i))); } }; @@ -970,15 +973,15 @@ namespace { collect_indices ci(indices); for_each_expr(ci, fml); } - + struct collect_decls { app_ref_vector& m_decls; std::string& prefix; collect_decls(app_ref_vector& decls, std::string& p): m_decls(decls), prefix(p) {} void operator()(expr* n) {} - void operator()(app* n) { + void operator()(app* n) { if (n->get_decl()->get_name().str().find(prefix) != std::string::npos) - m_decls.push_back(n); + m_decls.push_back(n); } }; From 4099f31f4f09dfb9741220390ef6290e779f0e63 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 8 Jun 2018 09:45:19 -0700 Subject: [PATCH 254/364] Fix refutation generation --- src/muz/spacer/spacer_sat_answer.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/muz/spacer/spacer_sat_answer.cpp b/src/muz/spacer/spacer_sat_answer.cpp index 8382133d8..09553de89 100644 --- a/src/muz/spacer/spacer_sat_answer.cpp +++ b/src/muz/spacer/spacer_sat_answer.cpp @@ -53,7 +53,7 @@ ground_sat_answer_op::ground_sat_answer_op(context &ctx) : proof_ref ground_sat_answer_op::operator()(pred_transformer &query) { - vector todo; + vector todo, new_todo; // -- find substitution for a query if query is not nullary expr_ref_vector qsubst(m); @@ -73,29 +73,32 @@ proof_ref ground_sat_answer_op::operator()(pred_transformer &query) { } } - frame root(query.get_last_rf(), query, qsubst); - todo.push_back(root); + todo.push_back(frame(query.get_last_rf(), query, qsubst)); + expr_ref root_fact(m); + root_fact = todo.back().fact(); while (!todo.empty()) { frame &curr = todo.back(); - if (m_cache.contains(curr.fact())) - { + if (m_cache.contains(curr.fact())) { todo.pop_back(); continue; } if (curr.m_visit == 0) { - mk_children(curr, todo); + new_todo.reset(); + mk_children(curr, new_todo); curr.m_visit = 1; + // curr becomes invalid + todo.append(new_todo); } else { proof* pf = mk_proof_step(curr); + m_pinned.push_back(curr.fact()); m_cache.insert(curr.fact(), pf); todo.pop_back(); } - } - return proof_ref(m_cache.find(root.fact()), m); + return proof_ref(m_cache.find(root_fact), m); } @@ -120,7 +123,7 @@ void ground_sat_answer_op::mk_children(frame &fr, vector &todo) { lbool res = m_solver->check_sat(0, nullptr); (void)res; - SASSERT(res == l_true); + VERIFY(res == l_true); model_ref mdl; m_solver->get_model(mdl); @@ -155,9 +158,16 @@ proof *ground_sat_answer_op::mk_proof_step(frame &fr) { datalog::rule_manager &rm = m_ctx.get_datalog_context().get_rule_manager(); expr_ref rule_fml(m); rm.to_formula(fr.rule(), rule_fml); + // premises.push_back(fr.rule().get_proof()); premises.push_back(m.mk_asserted(rule_fml)); for (auto &k : fr.m_kids) {premises.push_back(m_cache.find(k));} + for (unsigned i = 0; i < premises.size(); i++) { + positions.push_back(std::make_pair(0,i)); + } + for (unsigned i = 0; i <= premises.size(); i++) { + substs.push_back(expr_ref_vector(m)); + } m_pinned.push_back(m.mk_hyper_resolve(premises.size(), premises.c_ptr(), fr.fact(), From df7ab0e4964c592a8dc061cf013e6890988a936f Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 8 Jun 2018 18:58:28 -0700 Subject: [PATCH 255/364] pred_transformer: factor rule bookkeeping to a separate class --- src/muz/spacer/spacer_context.cpp | 219 +++++++++++++----------------- src/muz/spacer/spacer_context.h | 84 ++++++++++-- 2 files changed, 167 insertions(+), 136 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 5806c08fb..a8a904aa1 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -659,10 +659,18 @@ void lemma::mk_insts(expr_ref_vector &out, expr* e) } } - - // ---------------- // pred_tansformer +pred_transformer::pt_rule &pred_transformer::pt_rules::mk_rule(const pred_transformer::pt_rule &v) { + pt_rule *p = nullptr; + if (find_by_rule(v.rule(), p)) + return *p; + + p = alloc(pt_rule, v); + m_rules.insert(&p->rule(), p); + if (p->tag()) m_tags.insert(p->tag(), p); + return *p; +} pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): pm(pm), m(pm.get_manager()), @@ -692,9 +700,6 @@ app_ref pred_transformer::mk_extend_lit() { return app_ref(m.mk_not (m.mk_const (pm.get_n_pred (v->get_decl ()))), m); } -pred_transformer::~pred_transformer() { - for (auto &entry : m_rule2transition) {m.dec_ref(entry.m_value);} -} std::ostream& pred_transformer::display(std::ostream& out) const { @@ -819,10 +824,10 @@ reach_fact *pred_transformer::get_used_origin_rf (model_evaluator_util& mev, const datalog::rule *pred_transformer::find_rule(model &model) { expr_ref val(m); - for (auto &entry : m_tag2rule) { - app *tag = to_app(entry.m_key); + for (auto &kv : m_pt_rules) { + app *tag = kv.m_value->tag(); if (model.eval(tag->get_decl(), val) && m.is_true(val)) { - return entry.m_value; + return &kv.m_value->rule(); } } return nullptr; @@ -833,26 +838,16 @@ const datalog::rule *pred_transformer::find_rule(model &model, vector& reach_pred_used, unsigned& num_reuse_reach) { - TRACE ("spacer_verbose", - datalog::rule_manager& rm = ctx.get_datalog_context().get_rule_manager(); - for (auto &entry : m_tag2rule) { - expr* pred = entry.m_key; - tout << mk_pp(pred, m) << ":\n"; - if (entry.m_value) { - rm.display_smt2(*(entry.m_value), tout) << "\n"; - } - } - ); - // find a rule whose tag is true in the model; // prefer a rule where the model intersects with reach facts of all predecessors; // also find how many predecessors' reach facts are true in the model expr_ref vl(m); const datalog::rule *r = ((datalog::rule*)nullptr); - for (auto &entry : m_tag2rule) { - expr* tag = entry.m_key; - if (model.eval(to_app(tag)->get_decl(), vl) && m.is_true(vl)) { - r = entry.m_value; + //for (auto &entry : m_tag2rule) { + for (auto &kv : m_pt_rules) { + app* tag = kv.m_value->tag(); + if (model.eval(tag->get_decl(), vl) && m.is_true(vl)) { + r = &kv.m_value->rule(); is_concrete = true; num_reuse_reach = 0; reach_pred_used.reset(); @@ -1309,9 +1304,8 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, // populate reach_assumps if (n.level () > 0 && !m_all_init) { - for (auto &entry : m_tag2rule) { - datalog::rule const* r = entry.m_value; - if (!r) {continue;} + for (auto &kv : m_pt_rules) { + datalog::rule const* r = &kv.m_value->rule(); find_predecessors(*r, m_predicates); if (m_predicates.empty()) {continue;} for (unsigned i = 0; i < m_predicates.size(); i++) { @@ -1322,7 +1316,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, pm.formula_n2o(pt.get_last_rf_tag(), a, i); reach_assumps.push_back(m.mk_not (a)); } else { - reach_assumps.push_back(m.mk_not (entry.m_key)); + reach_assumps.push_back(m.mk_not (kv.m_value->tag())); break; } } @@ -1504,11 +1498,10 @@ void pred_transformer::mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result) { expr_ref tmp1(m), tmp2(m); - for (auto& kv : m_tag2rule) { - expr* tag = kv.m_key; - datalog::rule const* r = kv.m_value; - if (!r) { continue; } - find_predecessors(*r, m_predicates); + for (auto& kv : m_pt_rules) { + expr* tag = kv.m_value->tag(); + datalog::rule const& r = kv.m_value->rule(); + find_predecessors(r, m_predicates); for (unsigned i = 0; i < m_predicates.size(); i++) { func_decl* d = m_predicates[i]; if (d == head) { @@ -1545,79 +1538,52 @@ void pred_transformer::init_rfs () expr_ref_vector v(m); reach_fact_ref fact; - for (auto &entry : m_rule2tag) { - const datalog::rule* r = entry.m_key; - if (r->get_uninterpreted_tail_size() == 0) { - fact = alloc (reach_fact, m, *r, m_rule2transition.find(r), - get_aux_vars(*r), true); - add_rf(fact.get ()); + for (auto &kv : m_pt_rules) { + pt_rule &ptr = *kv.m_value; + const datalog::rule& r = ptr.rule(); + if (ptr.is_init()) { + fact = alloc(reach_fact, m, r, ptr.trans(), ptr.auxs(), true); + add_rf(fact.get()); } } } void pred_transformer::init_rules(decl2rel const& pts) { - expr_ref_vector transitions(m); - ptr_vector tr_rules; - datalog::rule const* rule; - expr_ref_vector init_conds (m); + expr_ref_vector transitions(m), not_inits(m); app_ref tag(m); - vector is_init; - for (auto r : m_rules) {init_rule(pts, *r, is_init, tr_rules, transitions);} - SASSERT (is_init.size () == transitions.size ()); + for (auto r : m_rules) { + init_rule(pts, *r); + } - std::string name; - switch(transitions.size()) { - case 0: + if (m_pt_rules.empty()) { m_transition = m.mk_false(); m_transition_clause.reset(); - break; - case 1: - // create a dummy tag. - name = head()->get_name().str() + "_dummy"; - tag = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); - - rule = tr_rules[0]; - m_tag2rule.insert(tag, rule); - m_rule2tag.insert(rule, tag); - transitions[0] = m.mk_implies (tag, transitions.get(0)); - - m_transition_clause.push_back(m_extend_lit->get_arg(0)); - m_transition_clause.push_back(tag); - - if (!ctx.use_inc_clause()) { - transitions.push_back(mk_or(m_transition_clause)); - m_transition_clause.reset(); - } - - if (!is_init[0]) {init_conds.push_back(m.mk_not(tag));} - - m_transition = mk_and(transitions); - break; - default: - m_transition_clause.push_back (m_extend_lit->get_arg(0)); - for (unsigned i = 0; i < transitions.size(); ++i) { - name = head()->get_name().str() + "__tr" + std::to_string(i); - tag = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); - rule = tr_rules[i]; - m_tag2rule.insert(tag, rule); - m_rule2tag.insert(rule, tag); - m_transition_clause.push_back(tag); - transitions[i] = m.mk_implies(tag, transitions.get(i)); - // update init conds - if (!is_init[i]) {init_conds.push_back (m.mk_not (tag));} - } - - if (!ctx.use_inc_clause()) { - transitions.push_back(mk_or(m_transition_clause)); - m_transition_clause.reset(); - } - m_transition = mk_and(transitions); - break; } - // mk init condition - m_init = mk_and(init_conds); + else { + unsigned i = 0; + expr_ref_vector transitions(m); + m_transition_clause.push_back (m_extend_lit->get_arg(0)); + for (auto &kv : m_pt_rules) { + pt_rule &r = *kv.m_value; + std::string name = head()->get_name().str() + "__tr" + std::to_string(i); + tag = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); + m_pt_rules.set_tag(tag, r); + m_transition_clause.push_back(tag); + transitions.push_back(m.mk_implies(r.tag(), r.trans())); + if (!r.is_init()) {not_inits.push_back(m.mk_not(tag));} + ++i; + } + + if (!ctx.use_inc_clause()) { + transitions.push_back(mk_or(m_transition_clause)); + m_transition_clause.reset(); + } + m_transition = mk_and(transitions); + } + // mk init condition -- disables all non-initial transitions + m_init = mk_and(not_inits); // no rule has uninterpreted tail - if (init_conds.empty ()) {m_all_init = true;} + if (not_inits.empty ()) {m_all_init = true;} } static bool is_all_non_null(app_ref_vector const& v) @@ -1628,10 +1594,7 @@ static bool is_all_non_null(app_ref_vector const& v) return true; } -void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule, - vector& is_init, - ptr_vector& rules, - expr_ref_vector& transitions) { +void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule) { scoped_watch _t_(m_initialize_watch); // Predicates that are variable representatives. Other predicates at @@ -1652,46 +1615,42 @@ void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule, init_atom(pts, rule.get_tail(i), var_reprs, side, i); } // -- substitute free variables - expr_ref fml(m); + expr_ref trans(m); { expr_ref_vector tail(m); for (unsigned i = ut_size; i < t_size; ++i) - {tail.push_back(rule.get_tail(i));} - fml = mk_and (tail); + tail.push_back(rule.get_tail(i)); + trans= mk_and (tail); - ground_free_vars(fml, var_reprs, aux_vars, ut_size == 0); + ground_free_vars(trans, var_reprs, aux_vars, ut_size == 0); SASSERT(is_all_non_null(var_reprs)); expr_ref tmp(m); - var_subst(m, false)(fml, var_reprs.size (), + var_subst(m, false)(trans, var_reprs.size (), (expr*const*)var_reprs.c_ptr(), tmp); flatten_and (tmp, side); - fml = mk_and(side); + trans = mk_and(side); side.reset (); } // rewrite and simplify th_rewriter rw(m); - rw(fml); - if (ctx.blast_term_ite()) {blast_term_ite(fml, 3); rw(fml);} - TRACE("spacer", tout << mk_pp(fml, m) << "\n";); + rw(trans); + if (ctx.blast_term_ite()) {blast_term_ite(trans, 3); rw(trans);} + TRACE("spacer_init_rule", tout << mk_pp(trans, m) << "\n";); // allow quantifiers in init rule - SASSERT(ut_size == 0 || is_ground(fml)); - if (!m.is_false(fml)) { - is_init.push_back (ut_size == 0); - transitions.push_back(fml); - rules.push_back(&rule); - - m.inc_ref(fml); - m_rule2transition.insert(&rule, fml); + SASSERT(ut_size == 0 || is_ground(trans)); + if (!m.is_false(trans)) { + pt_rule &ptr = m_pt_rules.mk_rule(m, rule); + ptr.set_trans(trans); + ptr.set_auxs(aux_vars); + ptr.set_reps(var_reprs); } - // AG: shouldn't this be under the if-statement above? - m_rule2vars.insert(&rule, aux_vars); - TRACE("spacer", - tout << rule.get_decl()->get_name() << "\n"; - tout << var_reprs << "\n";); + // TRACE("spacer", + // tout << rule.get_decl()->get_name() << "\n"; + // tout << var_reprs << "\n";); } @@ -1857,18 +1816,17 @@ void pred_transformer::updt_solver(prop_solver *solver) { } // -- lemmas and rfs from other predicates - for (auto &entry : m_tag2rule) { - const datalog::rule *r = entry.m_value; - if (!r) continue; - find_predecessors(*r, m_predicates); + for (auto &kv : m_pt_rules) { + const datalog::rule &r = kv.m_value->rule(); + find_predecessors(r, m_predicates); if (m_predicates.empty()) continue; for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { const pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); // assert lemmas of pt - updt_solver_with_lemmas(solver, pt, to_app(entry.m_key), i); + updt_solver_with_lemmas(solver, pt, to_app(kv.m_value->tag()), i); // assert rfs of pt - update_solver_with_rfs(solver, pt, to_app(entry.m_key), i); + update_solver_with_rfs(solver, pt, to_app(kv.m_value->tag()), i); } } } @@ -2447,6 +2405,7 @@ bool context::validate() switch(m_last_result) { case l_true: { +#if 0 expr_ref cex(m); cex = get_ground_sat_answer(); if (!cex.get()) { @@ -2454,6 +2413,14 @@ bool context::validate() throw default_exception("Cex validation failed\n"); return false; } +#endif + proof_ref cex(m); + cex = get_ground_refutation(); + if (!cex.get()) { + IF_VERBOSE(0, verbose_stream() << "Cex validation failed\n";); + throw default_exception("Cex validation failed\n"); + return false; + } break; } case l_false: { diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index e95ddc8ec..402cb63cc 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -292,6 +292,64 @@ class pred_transformer { }; + class pt_rule { + const datalog::rule &m_rule; + expr_ref m_trans; // ground version of m_rule + ptr_vector m_auxs; // auxiliary variables in m_trans + app_ref_vector m_reps; // map from fv in m_rule to ground constants + app_ref m_tag; // a unique tag for the rule + + public: + pt_rule(ast_manager &m, const datalog::rule &r) : + m_rule(r), m_trans(m), m_reps(m), m_tag(m) {} + + const datalog::rule &rule() const {return m_rule;} + + void set_tag(expr *tag) {SASSERT(is_app(tag)); set_tag(to_app(tag));} + void set_tag(app* tag) {m_tag = tag;} + app* tag() const {return m_tag;} + ptr_vector &auxs() {return m_auxs;} + void set_auxs(ptr_vector &v) {m_auxs.reset(); m_auxs.append(v);} + void set_reps(app_ref_vector &v) {m_reps.reset(); m_reps.append(v);} + + void set_trans(expr_ref &v) {m_trans=v;} + expr* trans() const {return m_trans;} + bool is_init() const {return m_rule.get_uninterpreted_tail_size() == 0;} + }; + + class pt_rules { + typedef obj_map rule2ptrule; + typedef obj_map tag2ptrule; + typedef rule2ptrule::iterator iterator; + rule2ptrule m_rules; + tag2ptrule m_tags; + public: + pt_rules() {} + ~pt_rules() {for (auto &kv : m_rules) {dealloc(kv.m_value);}} + + bool find_by_rule(const datalog::rule &r, pt_rule* &ptr) { + return m_rules.find(&r, ptr); + } + bool find_by_tag(const expr* tag, pt_rule* &ptr) { + return m_tags.find(tag, ptr); + } + pt_rule &mk_rule(ast_manager &m, const datalog::rule &r) { + return mk_rule(pt_rule(m, r)); + } + pt_rule &mk_rule(const pt_rule &v); + void set_tag(expr* tag, pt_rule &v) { + pt_rule *p; + VERIFY(find_by_rule(v.rule(), p)); + p->set_tag(tag); + m_tags.insert(tag, p); + } + + bool empty() {return m_rules.empty();} + iterator begin() {return m_rules.begin();} + iterator end() {return m_rules.end();} + + }; + typedef obj_map rule2expr; typedef obj_map > rule2apps; typedef obj_map expr2rule; @@ -302,6 +360,7 @@ class pred_transformer { func_decl_ref m_head; // predicate func_decl_ref_vector m_sig; // signature ptr_vector m_use; // places where 'this' is referenced. + pt_rules m_pt_rules; // pt rules used to derive transformer ptr_vector m_rules; // rules used to derive transformer scoped_ptr m_solver; // solver context ref m_reach_solver; // context for reachability facts @@ -309,10 +368,6 @@ class pred_transformer { frames m_frames; // frames with lemmas reach_fact_ref_vector m_reach_facts; // reach facts unsigned m_rf_init_sz; // number of reach fact from INIT - expr2rule m_tag2rule; // map tag predicate to rule. - rule2expr m_rule2tag; // map rule to predicate tag. - rule2expr m_rule2transition; // map rules to transition - rule2apps m_rule2vars; // map rule to auxiliary variables expr_ref_vector m_transition_clause; // extra clause for trans expr_ref m_transition; // transition relation expr_ref m_init; // initial condition @@ -337,8 +392,7 @@ class pred_transformer { // Initialization void init_rules(decl2rel const& pts); - void init_rule(decl2rel const& pts, datalog::rule const& rule, vector& is_init, - ptr_vector& rules, expr_ref_vector& transition); + void init_rule(decl2rel const& pts, datalog::rule const& rule); void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& side, unsigned tail_idx); @@ -350,7 +404,7 @@ class pred_transformer { public: pred_transformer(context& ctx, manager& pm, func_decl* head); - ~pred_transformer(); + ~pred_transformer() {} inline bool use_native_mbp (); reach_fact *get_rf (expr *v) { @@ -372,7 +426,10 @@ public: unsigned sig_size() const {return m_sig.size();} expr* transition() const {return m_transition;} expr* init() const {return m_init;} - expr* rule2tag(datalog::rule const* r) {return m_rule2tag.find(r);} + expr* rule2tag(datalog::rule const* r) { + pt_rule *p; + return m_pt_rules.find_by_rule(*r, p) ? p->tag() : nullptr; + } unsigned get_num_levels() const {return m_frames.size ();} expr_ref get_cover_delta(func_decl* p_orig, int level); void add_cover(unsigned level, expr* property); @@ -398,8 +455,15 @@ public: const datalog::rule *find_rule(model &mev, bool& is_concrete, vector& reach_pred_used, unsigned& num_reuse_reach); - expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); } - ptr_vector& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); } + expr* get_transition(datalog::rule const& r) { + pt_rule *p; + return m_pt_rules.find_by_rule(r, p) ? p->trans() : nullptr; + } + ptr_vector& get_aux_vars(datalog::rule const& r) { + pt_rule *p = nullptr; + VERIFY(m_pt_rules.find_by_rule(r, p)); + return p->auxs(); + } bool propagate_to_next_level(unsigned level); void propagate_to_infinity(unsigned level); From 8445e2a7a2e41d43b0f157eaeb13dd8f1f50723b Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 8 Jun 2018 23:37:16 -0700 Subject: [PATCH 256/364] Fix bug in weak abs Must ensure that weak model makes all summaries true. Otherwise, it is possible to get stuck discovering the same lemma forever. --- src/muz/spacer/spacer_context.cpp | 8 ++++++++ src/muz/spacer/spacer_context.h | 3 --- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index a8a904aa1..64d047d73 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1166,6 +1166,10 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, summary[i] = v; } + // bail out of if the model is insufficient + if (!mev.is_true(summary)) + return expr_ref(m); + // -- pick an implicant expr_ref_vector lits(m); compute_implicant_literals (mev, summary, lits); @@ -3722,6 +3726,10 @@ bool context::create_children(pob& n, datalog::rule const& r, expr_ref sum(m); sum = pt.get_origin_summary (mev, prev_level(n.level()), j, reach_pred_used[j], &aux); + if (!sum) { + dealloc(deriv); + return false; + } deriv->add_premise (pt, j, sum, reach_pred_used[j], aux); } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 402cb63cc..fcd396a62 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -350,9 +350,6 @@ class pred_transformer { }; - typedef obj_map rule2expr; - typedef obj_map > rule2apps; - typedef obj_map expr2rule; manager& pm; // spacer::manager ast_manager& m; // ast_manager context& ctx; // spacer::context From 4a2eb909bfb96fceb6b61b3bee4a3b15b5f31eb9 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 9 Jun 2018 10:34:05 -0700 Subject: [PATCH 257/364] Re-fixing a bug in compute_implicant_literals() --- src/muz/spacer/spacer_util.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 876a6528a..da946e211 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -603,6 +603,14 @@ namespace { void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, expr_ref_vector &res) { + // flatten the formula and remove all trivial literals + + // TBD: not clear why there is a dependence on it (other than + // not handling of Boolean constants by implicant_picker), however, + // it was a source of a problem on a benchmark + flatten_and(formula); + if (formula.empty()) {return;} + implicant_picker ipick (mev); ipick (formula, res); } From 5fc0f56281318080be0fb1cf67af869b40aaaaed Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 8 Jun 2018 17:03:24 -0700 Subject: [PATCH 258/364] sketch mbi Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbi.cpp | 62 ++++++++++++++++++++++++++++++++ src/qe/qe_mbi.h | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 src/qe/qe_mbi.cpp create mode 100644 src/qe/qe_mbi.h diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp new file mode 100644 index 000000000..13b9067ee --- /dev/null +++ b/src/qe/qe_mbi.cpp @@ -0,0 +1,62 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + qe_mbi.cpp + +Abstract: + + Model-based interpolation utilities + +Author: + + Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 + +Revision History: + + +--*/ + + +namespace qe { + + lbool interpolator::binary(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { + ast_manager& m = vars.get_manager(); + model_ref mdl; + literal_vector lits(m); + bool a_turn = true; + expr_ref_vector itp_a(m), itp_b(m); + mbi_result last_res = mbi_undef; + while (true) { + auto* t1 = a_turn ? &a : &b; + auto* t2 = a_turn ? &b : &a; + mbi_result next_res = (*t1)(vars, lits, mdl); + switch (next_res) { + case mbi_sat: + if (last_res == mbi_sat) { + itp = nullptr; + return l_true; + } + break; // continue + case mbi_unsat: + if (last_res == mbi_unsat) { + // TBD, extract the interpolants + // of what was blocked. + return l_false; + } + t2->block(lits); + lits.reset(); // or find a prefix of lits? + next_res = mbi_undef; + a_turn = !a_turn; + break; + case mbi_augment: + a_turn = !a_turn; + break; + case mbi_undef: + return l_undef; + } + last_res = next_res; + } + } +}; diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h new file mode 100644 index 000000000..4aad92f82 --- /dev/null +++ b/src/qe/qe_mbi.h @@ -0,0 +1,90 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + qe_mbi.h + +Abstract: + + Model-based interpolation utilities + +Author: + + Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 + +Revision History: + + +--*/ + +#pragma once + +namespace qe { + enum mbi_result { + mbi_sat, + mbi_unsat, + mbi_augment, + mbi_undef, + }; + + class mbi_plugin { + virtual ~mbi_plugin(); + /** + * \brief Utility that works modulo a background state. + * - vars + * variables to preferrably project onto (other variables would require quantification to fit interpolation signature) + * - lits + * set of literals to check satisfiability with respect to. + * - mdl + * optional model for caller. + * on return: + * - mbi_sat: + * populates mdl with a satisfying state, and lits with implicant for background state. + * - mbi_unsat: + * populates lits to be inconsistent with background state. + * For all practical purposes it is a weakening of lits or even a subset of lits. + * - mbi_augment: + * populates lits with strengthening of lits (superset) + * - mbi_undef: + * inconclusive, + */ + virtual mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) = 0; + + /** + * \brief Block conjunction of lits from future mbi_augment or mbi_sat. + */ + virtual void block(expr_ref_vector const& lits) = 0; + }; + + class euf_mbi_plugin : mbi_plugin { + solver_ref m_solver; + public: + euf_mbi_plugin(solver* s); + ~euf_mbi_plugin() override {} + mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; + void block(expr_ref_vector const& lits) override; + }; + + /** + * use cases for interpolation. + */ + class interpolator { + static lbool binary(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); + }; + +#if 0 + TBD some other scenario, e.g., Farkas, Cute/Beautiful/maybe even Selfless + + class solver_mbi_plugin : public mbi_plugin { + solver_ref m_solver; + public: + solver_mbi_plugin(solver* s); + ~solver_mbi_plugin() override {} + mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; + void block(expr_ref_vector const& lits) override; + }; + + +#endif +}; From e6468726f5dba1d1822d1948865124238e8d6987 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 8 Jun 2018 18:52:04 -0700 Subject: [PATCH 259/364] more code Signed-off-by: Nikolaj Bjorner --- src/ast/ast_util.cpp | 5 ++++ src/ast/ast_util.h | 2 ++ src/qe/qe_mbi.cpp | 54 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index e2783051a..0e5cf12a5 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -195,6 +195,11 @@ expr * mk_not(ast_manager & m, expr * arg) { return m.mk_not(arg); } +expr_ref mk_not(expr_ref& e) { + return expr_ref(mk_not(e.m(), e), e.m()); +} + + expr_ref push_not(const expr_ref& e) { ast_manager& m = e.get_manager(); if (!is_app(e)) { diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index 12c11c141..1383be157 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -127,6 +127,8 @@ inline expr_ref mk_or(expr_ref_vector const& args) { return expr_ref(mk_or(args. */ expr * mk_not(ast_manager & m, expr * arg); +expr_ref mk_not(expr_ref& e); + /** Negate and push over conjunction or disjunction. */ diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 13b9067ee..eda233c14 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -20,17 +20,52 @@ Revision History: namespace qe { + + euf_mbi_plugin::euf_mbi_plugin(solver* s): m_solver(s) {} + + euf_mbi_plugin::~euf_mbi_plugin() {} + + mbi_result euf_mbi_plugin::operator()( + func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override { + // really just a sketch of propositional at this point + scoped_push _sp(m_solver); + lbool r = m_solver->check_sat(lits); + switch (r) { + case l_false: + lits.reset(); + m_solver->get_unsat_core(lits); + return mbi_unsat; + case l_true: + m_solver->get_model(mdl); + // update lits? to be all propositioal literals or an implicant. + // mdl will have map of all constants that we can walk over. + return mbi_sat; + case l_undef: + return mbi_undef; + } + } + + void euf_mbi_plugin::block(expr_ref_vector const& lits) override { + m_solver->assert_expr(mk_not(mk_and(lits))); + } + /** + * ping-pong interpolation of Gurfinkel & Vizel + */ lbool interpolator::binary(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { ast_manager& m = vars.get_manager(); model_ref mdl; literal_vector lits(m); - bool a_turn = true; - expr_ref_vector itp_a(m), itp_b(m); + bool turn = true; + vector itps, blocks; + ipts.push_back(expr_ref_vector(m)); + ipts.push_back(expr_ref_vector(m)); + blocks.push_back(expr_ref_vector(m)); + blocks.push_back(expr_ref_vector(m)); mbi_result last_res = mbi_undef; while (true) { - auto* t1 = a_turn ? &a : &b; - auto* t2 = a_turn ? &b : &a; + auto* t1 = turn ? &a : &b; + auto* t2 = turn ? &b : &a; mbi_result next_res = (*t1)(vars, lits, mdl); switch (next_res) { case mbi_sat: @@ -41,21 +76,22 @@ namespace qe { break; // continue case mbi_unsat: if (last_res == mbi_unsat) { - // TBD, extract the interpolants - // of what was blocked. + itp = mk_and(itps[turn]); return l_false; } t2->block(lits); + expr_ref lemma(m.mk_not(mk_and(lits)), m); + blocks[turn].push_back(lemma); + itps[turn].push_back(m.mk_implies(mk_and(blocks[!turn]), lemma)); lits.reset(); // or find a prefix of lits? - next_res = mbi_undef; - a_turn = !a_turn; + next_res = mbi_undef; // hard restart. break; case mbi_augment: - a_turn = !a_turn; break; case mbi_undef: return l_undef; } + turn = !turn; last_res = next_res; } } From 688cf79619fbc291938f63d16ffbe7ee368d23cc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 09:55:32 -0700 Subject: [PATCH 260/364] working on mbi Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 50 ++++++++++++- src/qe/CMakeLists.txt | 1 + src/qe/qe_mbi.cpp | 93 ++++++++++++++++++++----- src/qe/qe_mbi.h | 16 ++++- src/smt/smt_context.cpp | 66 ++++++++++++------ src/smt/smt_context.h | 2 + 6 files changed, 184 insertions(+), 44 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 31fc4b008..c6cd1020e 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -31,6 +31,7 @@ Notes: #include "ast/rewriter/var_subst.h" #include "util/gparams.h" #include "qe/qe_mbp.h" +#include "qe/qe_mbi.h" BINARY_SYM_CMD(get_quantifier_body_cmd, @@ -358,7 +359,7 @@ class mbp_cmd : public cmd { ptr_vector m_vars; public: mbp_cmd():cmd("mbp") {} - char const * get_usage() const override { return ""; } + char const * get_usage() const override { return " ()"; } char const * get_descr(cmd_context & ctx) const override { return "perform model based projection"; } unsigned get_arity() const override { return 2; } cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { @@ -390,6 +391,52 @@ public: } }; +class mbi_cmd : public cmd { + expr* m_a; + expr* m_b; + ptr_vector m_vars; +public: + mbi_cmd():cmd("mbi") {} + char const * get_usage() const override { return " (vars)"; } + char const * get_descr(cmd_context & ctx) const override { return "perform model based interpolation"; } + unsigned get_arity() const override { return 3; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { + if (m_a == nullptr) return CPK_EXPR; + if (m_b == nullptr) return CPK_EXPR; + return CPK_FUNC_DECL_LIST; + } + void set_next_arg(cmd_context& ctx, expr * arg) override { + if (m_a == nullptr) + m_a = arg; + else + m_b = arg; + } + void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; m_vars.reset(); } + void execute(cmd_context & ctx) override { + ast_manager& m = ctx.m(); + func_decl_ref_vector vars(m); + for (func_decl* v : m_vars) { + vars.push_back(v); + } + qe::interpolator mbi; + expr_ref a(m_a, m); + expr_ref b(m_b, m); + expr_ref itp(m); + solver_factory& sf = ctx.get_solver_factory(); + params_ref p; + solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null); + solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null); + qe::prop_mbi_plugin pA(sA.get()); + qe::prop_mbi_plugin pB(sB.get()); + lbool res = mbi.binary(pA, pB, vars, itp); + ctx.regular_stream() << res << " " << itp << "\n"; + } + +}; + void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(print_dimacs_cmd)); @@ -416,4 +463,5 @@ void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(instantiate_nested_cmd)); ctx.insert(alloc(set_next_id)); ctx.insert(alloc(mbp_cmd)); + ctx.insert(alloc(mbi_cmd)); } diff --git a/src/qe/CMakeLists.txt b/src/qe/CMakeLists.txt index 2e6052382..d43db34f0 100644 --- a/src/qe/CMakeLists.txt +++ b/src/qe/CMakeLists.txt @@ -15,6 +15,7 @@ z3_add_component(qe qe_dl_plugin.cpp qe_lite.cpp qe_mbp.cpp + qe_mbi.cpp qe_sat_tactic.cpp qe_tactic.cpp qsat.cpp diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index eda233c14..f149af764 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -18,17 +18,22 @@ Revision History: --*/ +#include "ast/ast_util.h" +#include "solver/solver.h" +#include "qe/qe_mbi.h" + namespace qe { - euf_mbi_plugin::euf_mbi_plugin(solver* s): m_solver(s) {} + // ------------------------------- + // prop_mbi - euf_mbi_plugin::~euf_mbi_plugin() {} + prop_mbi_plugin::prop_mbi_plugin(solver* s): m_solver(s) {} - mbi_result euf_mbi_plugin::operator()( - func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override { - // really just a sketch of propositional at this point - scoped_push _sp(m_solver); + // sketch of propositional + + mbi_result prop_mbi_plugin::operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { + ast_manager& m = lits.m(); lbool r = m_solver->check_sat(lits); switch (r) { case l_false: @@ -37,29 +42,77 @@ namespace qe { return mbi_unsat; case l_true: m_solver->get_model(mdl); - // update lits? to be all propositioal literals or an implicant. - // mdl will have map of all constants that we can walk over. + lits.reset(); + for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { + func_decl* c = mdl->get_constant(i); + if (vars.contains(c)) { + if (m.is_true(mdl->get_const_interp(c))) { + lits.push_back(m.mk_const(c)); + } + else { + lits.push_back(m.mk_not(m.mk_const(c))); + } + } + } return mbi_sat; - case l_undef: + default: return mbi_undef; } } - void euf_mbi_plugin::block(expr_ref_vector const& lits) override { + void prop_mbi_plugin::block(expr_ref_vector const& lits) { + m_solver->assert_expr(mk_not(mk_and(lits))); + } + + // ------------------------------- + // euf_mbi, TBD + + euf_mbi_plugin::euf_mbi_plugin(solver* s): m(s->get_manager()), m_solver(s) {} + + mbi_result euf_mbi_plugin::operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { + lbool r = m_solver->check_sat(lits); + switch (r) { + case l_false: + lits.reset(); + m_solver->get_unsat_core(lits); + return mbi_unsat; + case l_true: + m_solver->get_model(mdl); + lits.reset(); + for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { + func_decl* c = mdl->get_constant(i); + if (vars.contains(c)) { + if (m.is_true(mdl->get_const_interp(c))) { + lits.push_back(m.mk_const(c)); + } + else { + lits.push_back(m.mk_not(m.mk_const(c))); + } + } + } + return mbi_sat; + default: + return mbi_undef; + } + } + + void euf_mbi_plugin::block(expr_ref_vector const& lits) { m_solver->assert_expr(mk_not(mk_and(lits))); } - /** + + /** -------------------------------------------------------------- * ping-pong interpolation of Gurfinkel & Vizel + * compute a binary interpolant. */ lbool interpolator::binary(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { ast_manager& m = vars.get_manager(); model_ref mdl; - literal_vector lits(m); + expr_ref_vector lits(m); bool turn = true; vector itps, blocks; - ipts.push_back(expr_ref_vector(m)); - ipts.push_back(expr_ref_vector(m)); + itps.push_back(expr_ref_vector(m)); + itps.push_back(expr_ref_vector(m)); blocks.push_back(expr_ref_vector(m)); blocks.push_back(expr_ref_vector(m)); mbi_result last_res = mbi_undef; @@ -74,18 +127,20 @@ namespace qe { return l_true; } break; // continue - case mbi_unsat: - if (last_res == mbi_unsat) { + case mbi_unsat: { + if (lits.empty()) { itp = mk_and(itps[turn]); return l_false; } t2->block(lits); - expr_ref lemma(m.mk_not(mk_and(lits)), m); + expr_ref lemma(mk_not(mk_and(lits))); blocks[turn].push_back(lemma); - itps[turn].push_back(m.mk_implies(mk_and(blocks[!turn]), lemma)); + itp = m.mk_implies(mk_and(blocks[!turn]), lemma); + // TBD: compute closure over variables not in vars + itps[turn].push_back(itp); lits.reset(); // or find a prefix of lits? - next_res = mbi_undef; // hard restart. break; + } case mbi_augment: break; case mbi_undef: diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index 4aad92f82..dad29167f 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -29,7 +29,8 @@ namespace qe { }; class mbi_plugin { - virtual ~mbi_plugin(); + public: + virtual ~mbi_plugin() {} /** * \brief Utility that works modulo a background state. * - vars @@ -57,8 +58,18 @@ namespace qe { virtual void block(expr_ref_vector const& lits) = 0; }; - class euf_mbi_plugin : mbi_plugin { + class prop_mbi_plugin : public mbi_plugin { solver_ref m_solver; + public: + prop_mbi_plugin(solver* s); + ~prop_mbi_plugin() override {} + mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; + void block(expr_ref_vector const& lits) override; + }; + + class euf_mbi_plugin : public mbi_plugin { + ast_manager& m; + solver_ref m_solver; public: euf_mbi_plugin(solver* s); ~euf_mbi_plugin() override {} @@ -70,6 +81,7 @@ namespace qe { * use cases for interpolation. */ class interpolator { + public: static lbool binary(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); }; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 0680d7bcf..8dec3335e 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3067,6 +3067,41 @@ namespace smt { return m_asserted_formulas.inconsistent(); } + static bool is_valid_assumption(ast_manager & m, expr * assumption) { + expr* arg; + if (!m.is_bool(assumption)) + return false; + if (is_uninterp_const(assumption)) + return true; + if (m.is_not(assumption, arg) && is_uninterp_const(arg)) + return true; + if (!is_app(assumption)) + return false; + if (to_app(assumption)->get_num_args() == 0) + return true; + if (m.is_not(assumption, arg) && is_app(arg) && to_app(arg)->get_num_args() == 0) + return true; + return false; + } + + void context::internalize_proxies(expr_ref_vector const& asms, vector>& asm2proxy) { + for (expr* e : asms) { + if (is_valid_assumption(m_manager, e)) { + asm2proxy.push_back(std::make_pair(e, expr_ref(e, m_manager))); + } + else { + expr_ref proxy(m_manager), fml(m_manager); + proxy = m_manager.mk_fresh_const("proxy", m_manager.mk_bool_sort()); + fml = m_manager.mk_implies(proxy, e); + m_asserted_formulas.assert_expr(fml); + asm2proxy.push_back(std::make_pair(e, proxy)); + } + } + // The new assertions are of the form 'proxy => assumption' + // so clause simplification is sound even as these are removed after pop_scope. + internalize_assertions(); + } + void context::internalize_assertions() { if (get_cancel_flag()) return; TRACE("internalize_assertions", tout << "internalize_assertions()...\n";); @@ -3102,23 +3137,6 @@ namespace smt { TRACE("after_internalize_assertions", display(tout);); } - bool is_valid_assumption(ast_manager & m, expr * assumption) { - expr* arg; - if (!m.is_bool(assumption)) - return false; - if (is_uninterp_const(assumption)) - return true; - if (m.is_not(assumption, arg) && is_uninterp_const(arg)) - return true; - if (!is_app(assumption)) - return false; - if (to_app(assumption)->get_num_args() == 0) - return true; - if (m.is_not(assumption, arg) && is_app(arg) && to_app(arg)->get_num_args() == 0) - return true; - return false; - } - /** \brief Assumptions must be uninterpreted boolean constants (aka propositional variables). */ @@ -3205,17 +3223,21 @@ namespace smt { if (get_cancel_flag()) return; push_scope(); - for (expr * curr_assumption : asms) { + vector> asm2proxy; + internalize_proxies(asms, asm2proxy); + for (auto const& p: asm2proxy) { + expr_ref curr_assumption = p.second; + expr* orig_assumption = p.first; if (m_manager.is_true(curr_assumption)) continue; SASSERT(is_valid_assumption(m_manager, curr_assumption)); proof * pr = m_manager.mk_asserted(curr_assumption); internalize_assertion(curr_assumption, pr, 0); literal l = get_literal(curr_assumption); - m_literal2assumption.insert(l.index(), curr_assumption); + m_literal2assumption.insert(l.index(), orig_assumption); // mark_as_relevant(l); <<< not needed // internalize_assertion marked l as relevant. SASSERT(is_relevant(l)); - TRACE("assumptions", tout << l << ":" << mk_pp(curr_assumption, m_manager) << "\n";); + TRACE("assumptions", tout << l << ":" << curr_assumption << " " << mk_pp(orig_assumption, m_manager) << "\n";); if (m_manager.proofs_enabled()) assign(l, mk_justification(justification_proof_wrapper(*this, pr))); else @@ -3386,7 +3408,7 @@ namespace smt { setup_context(false); expr_ref_vector asms(m_manager, num_assumptions, assumptions); if (!already_did_theory_assumptions) add_theory_assumptions(asms); - if (!validate_assumptions(asms)) return l_undef; + // introducing proxies: if (!validate_assumptions(asms)) return l_undef; TRACE("unsat_core_bug", tout << asms << "\n";); internalize_assertions(); init_assumptions(asms); @@ -3402,7 +3424,7 @@ namespace smt { setup_context(false); expr_ref_vector asms(cube); add_theory_assumptions(asms); - if (!validate_assumptions(asms)) return l_undef; + // introducing proxies: if (!validate_assumptions(asms)) return l_undef; for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; internalize_assertions(); init_assumptions(asms); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 16c1c7ac3..7e09c3be2 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1536,6 +1536,8 @@ namespace smt { void internalize_assertion(expr * n, proof * pr, unsigned generation); + void internalize_proxies(expr_ref_vector const& asms, vector>& asm2proxy); + void internalize_instance(expr * body, proof * pr, unsigned generation) { internalize_assertion(body, pr, generation); if (relevancy()) From 0784074b674c5ad348a22feba03fa476e8a8d637 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 10:18:18 -0700 Subject: [PATCH 261/364] fixes Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 2 ++ src/qe/qe_mbi.cpp | 7 ++++++- src/smt/smt_context.cpp | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index c6cd1020e..5e8a687c7 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -429,6 +429,8 @@ public: params_ref p; solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null); solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null); + sA->assert_expr(a); + sB->assert_expr(b); qe::prop_mbi_plugin pA(sA.get()); qe::prop_mbi_plugin pB(sB.get()); lbool res = mbi.binary(pA, pB, vars, itp); diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index f149af764..8be15ed7f 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -104,6 +104,7 @@ namespace qe { /** -------------------------------------------------------------- * ping-pong interpolation of Gurfinkel & Vizel * compute a binary interpolant. + * TBD: also implement the one-sided versions that create clausal interpolants. */ lbool interpolator::binary(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { ast_manager& m = vars.get_manager(); @@ -126,14 +127,18 @@ namespace qe { itp = nullptr; return l_true; } + TRACE("mbi", tout << "new lits " << lits << "\n";); break; // continue case mbi_unsat: { if (lits.empty()) { - itp = mk_and(itps[turn]); + // TBD, either a => itp and itp => !b + // or b => itp and itp => !a + itp = mk_and(itps[!turn]); return l_false; } t2->block(lits); expr_ref lemma(mk_not(mk_and(lits))); + TRACE("mbi", tout << lemma << "\n";); blocks[turn].push_back(lemma); itp = m.mk_implies(mk_and(blocks[!turn]), lemma); // TBD: compute closure over variables not in vars diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 8dec3335e..b60a9db71 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3061,7 +3061,7 @@ namespace smt { bool context::reduce_assertions() { if (!m_asserted_formulas.inconsistent()) { - SASSERT(at_base_level()); + // SASSERT(at_base_level()); m_asserted_formulas.reduce(); } return m_asserted_formulas.inconsistent(); From 2e44850df9134cd3c1cb6309fe7a126515e1a103 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 10:50:40 -0700 Subject: [PATCH 262/364] move term graph closer to qe Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/CMakeLists.txt | 1 - src/muz/spacer/spacer_generalizers.cpp | 4 +- src/muz/spacer/spacer_quant_generalizer.cpp | 2 +- src/muz/spacer/spacer_term_graph.cpp | 519 -------------------- src/muz/spacer/spacer_term_graph.h | 97 ---- src/muz/spacer/spacer_util.cpp | 6 +- src/qe/CMakeLists.txt | 1 + src/qe/qe_mbi.cpp | 25 +- 8 files changed, 20 insertions(+), 635 deletions(-) delete mode 100644 src/muz/spacer/spacer_term_graph.cpp delete mode 100644 src/muz/spacer/spacer_term_graph.h diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 22218f25f..43fffd9fb 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -19,7 +19,6 @@ z3_add_component(spacer spacer_antiunify.cpp spacer_mev_array.cpp spacer_qe_project.cpp - spacer_term_graph.cpp spacer_sem_matcher.cpp spacer_quant_generalizer.cpp spacer_callback.cpp diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index d7c7429ee..4e5b60698 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -26,11 +26,11 @@ Revision History: #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" #include "ast/factor_equivs.h" -#include "muz/spacer/spacer_term_graph.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" #include "ast/expr_functors.h" #include "smt/smt_solver.h" +#include "qe/qe_term_graph.h" namespace spacer { void lemma_sanity_checker::operator()(lemma_ref &lemma) { @@ -317,7 +317,7 @@ void lemma_eq_generalizer::operator() (lemma_ref &lemma) if (lemma->get_cube().empty()) return; ast_manager &m = m_ctx.get_ast_manager(); - spacer::term_graph egraph(m); + qe::term_graph egraph(m); egraph.add_lits(lemma->get_cube()); // -- expand the cube with all derived equalities diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index f9525b2f6..f3425817b 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -27,7 +27,7 @@ Revision History: #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" #include "ast/factor_equivs.h" -#include "muz/spacer/spacer_term_graph.h" +#include "qe/qe_term_graph.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" #include "ast/expr_functors.h" diff --git a/src/muz/spacer/spacer_term_graph.cpp b/src/muz/spacer/spacer_term_graph.cpp deleted file mode 100644 index 0dc29a7c5..000000000 --- a/src/muz/spacer/spacer_term_graph.cpp +++ /dev/null @@ -1,519 +0,0 @@ -#include "muz/spacer/spacer_term_graph.h" -#include "util/util.h" -#include "ast/ast_pp.h" -#include "ast/ast_util.h" -#include "ast/for_each_expr.h" - -namespace spacer { - -class term { - // -- an app represented by this term - app* m_app; - // -- root of the equivalence class - term* m_root; - // -- next element in the equivalence class (cyclic linked list) - term* m_next; - // -- eq class size - unsigned m_class_size; - - // -- general purpose mark - unsigned m_mark:1; - // -- general purpose second mark - unsigned m_mark2:1; - // -- is an interpreted constant - unsigned m_interpreted:1; - - // -- terms that contain this term as a child - //ptr_vector m_uses; - - // ptr_vector m_args; - -public: - term(app* a) : m_app(a), m_root(this), m_next(this), - m_class_size(1), m_mark(false), m_mark2(false), - m_interpreted(false) {} - - ~term() {} - - unsigned get_id() const {return m_app->get_id();} - - bool is_marked() const {return m_mark;} - void set_mark(bool v){m_mark = v;} - bool is_marked2() const {return m_mark2;} - void set_mark2(bool v){m_mark2 = v;} - - bool is_interpreted() const {return m_interpreted;} - void mark_as_interpreted() {m_interpreted=true;} - app* get_app() const {return m_app;} - - term &get_root() const {return *m_root;} - bool is_root() const {return m_root == this;} - void set_root(term &r) {m_root = &r;} - term &get_next() const {return *m_next;} - - unsigned get_class_size() const {return m_class_size;} - - void merge_eq_class(term &b) { - std::swap(this->m_next, b.m_next); - m_class_size += b.get_class_size(); - // -- reset (useful for debugging) - b.m_class_size = 0; - } - - // -- make this term the root of its equivalence class - void mk_root() { - if (is_root()) return; - - term *curr = this; - do { - if (curr->is_root()) { - // found previous root - SASSERT(curr != this); - m_class_size = curr->get_class_size(); - curr->m_class_size = 0; - } - curr->set_root(*this); - curr = &curr->get_next(); - } - while (curr != this); - } -}; - -class arith_term_graph_plugin : public term_graph_plugin { - term_graph &m_g; - ast_manager &m; - arith_util m_arith; - -public: - arith_term_graph_plugin(term_graph &g) : - term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), - m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} - - virtual ~arith_term_graph_plugin() {} - - bool mk_eq_core (expr *_e1, expr *_e2, app_ref &res) { - expr *e1, *e2; - e1 = _e1; - e2 = _e2; - - if (m_arith.is_zero(e1)) { - std::swap(e1, e2); - } - // y + -1*x == 0 --> y = x - expr *a0 = 0, *a1 = 0, *x = 0; - if (m_arith.is_zero(e2) && m_arith.is_add(e1, a0, a1)) { - if (m_arith.is_times_minus_one(a1, x)) { - e1 = a0; - e2 = x; - } - else if (m_arith.is_times_minus_one(a0, x)) { - e1 = a1; - e2 = x; - } - } - res = m.mk_eq(e1, e2); - return true; - } - - app* mk_le_zero(expr *arg) { - expr *e1, *e2, *e3; - // XXX currently disabled - if (m_arith.is_add(arg, e1, e2)) { - // e1-e2<=0 --> e1<=e2 - if (m_arith.is_times_minus_one(e2, e3)) { - return m_arith.mk_le(e1, e3); - } - // -e1+e2<=0 --> e2<=e1 - else if (m_arith.is_times_minus_one(e1, e3)) { - return m_arith.mk_le(e2, e3); - } - } - return m_arith.mk_le(arg, mk_zero()); - } - app* mk_ge_zero(expr *arg) { - expr *e1, *e2, *e3; - // XXX currently disabled - if (m_arith.is_add(arg, e1, e2)) { - // e1-e2>=0 --> e1>=e2 - if (m_arith.is_times_minus_one(e2, e3)) { - return m_arith.mk_ge(e1, e3); - } - // -e1+e2>=0 --> e2>=e1 - else if (m_arith.is_times_minus_one(e1, e3)) { - return m_arith.mk_ge(e2, e3); - } - } - return m_arith.mk_ge(arg, mk_zero()); - } - - bool mk_le_core (expr *arg1, expr * arg2, app_ref &result) { - // t <= -1 ==> t < 0 ==> ! (t >= 0) - rational n; - if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { - result = m.mk_not (mk_ge_zero (arg1)); - return true; - } - else if (m_arith.is_zero(arg2)) { - result = mk_le_zero(arg1); - return true; - } - else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n < 0) { - // t <= n ==> t < n + 1 ==> ! (t >= n + 1) - result = m.mk_not(m_arith.mk_ge(arg1, m_arith.mk_numeral(n+1, true))); - return true; - } - return false; - } - - expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} - bool is_one (expr const * n) const { - rational val; - return m_arith.is_numeral (n, val) && val.is_one (); - } - - bool mk_ge_core (expr * arg1, expr * arg2, app_ref &result) { - // t >= 1 ==> t > 0 ==> ! (t <= 0) - rational n; - if (m_arith.is_int (arg1) && is_one (arg2)) { - result = m.mk_not (mk_le_zero (arg1)); - return true; - } - else if (m_arith.is_zero(arg2)) { - result = mk_ge_zero(arg1); - return true; - } - else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n > 0) { - // t >= n ==> t > n - 1 ==> ! (t <= n - 1) - result = m.mk_not(m_arith.mk_le(arg1, m_arith.mk_numeral(n-1, true))); - return true; - } - return false; - } - - virtual app_ref process_lit (app *_lit) { - app *lit = _lit; - expr *e1, *e2; - - // strip negation - bool is_neg = m.is_not(lit); - if (is_neg) { - lit = to_app(to_app(lit)->get_arg(0)); - } - - app_ref res(m); - res = lit; - if (m.is_eq (lit, e1, e2)) { - mk_eq_core(e1, e2, res); - } - else if (m_arith.is_le(lit, e1, e2)) { - mk_le_core(e1, e2, res); - } - else if (m_arith.is_ge(lit, e1, e2)) { - mk_ge_core(e1, e2, res); - } - - // restore negation - if (is_neg) { - res = m.mk_not(res); - } - - return res; - } -}; - -term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { - m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); -} -term_graph::~term_graph() { - std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); -} - -static family_id get_family_id(ast_manager &m, app *lit) { - family_id fid = null_family_id; - - expr *e1, *e2, *e3; - // strip negation - if (!m.is_not (lit, e1)) { e1 = lit; } - - // deal with equality using sort of range - if (m.is_eq (e1, e2, e3)) { - fid = get_sort (e2)->get_family_id(); - } - // extract family_id of top level app - else { - fid = to_app(e1)->get_decl()->get_family_id(); - } - - return fid; -} - -void term_graph::add_lit(app *l) { - app_ref lit(m); - - family_id fid = get_family_id (m, l); - term_graph_plugin *pin = m_plugins.get_plugin(fid); - if (pin) { - lit = pin->process_lit(l); - } else { - lit = l; - } - m_lits.push_back(lit); - internalize_lit(lit); -} - -bool term_graph::is_internalized(app *a) { - return m_app2term.contains(a->get_id()); -} - -term* term_graph::get_term(app *a) { - term *res; - return m_app2term.find (a->get_id(), res) ? res : nullptr; -} - -term *term_graph::mk_term(app *a) { - term *t; - t = alloc(term, a); - if (a->get_num_args() == 0 && m.is_unique_value(a)){ - t->mark_as_interpreted(); - } - - m_terms.push_back(t); - m_app2term.insert(a->get_id(), t); - return t; -} - -term *term_graph::internalize_term(app *t) { - term *res = get_term(t); - - if (!res) { - for (unsigned i=0, sz=t->get_num_args(); i < sz; ++i) { - expr *arg = t->get_arg(i); - SASSERT(is_app(arg)); - internalize_term(::to_app(arg)); - } - res = mk_term(t); - } - return res; -} - -void term_graph::internalize_eq(app *a1, app* a2) { - internalize_lit(a1); - internalize_lit(a2); - - term *t1, *t2; - t1 = get_term(a1); - t2 = get_term(a2); - SASSERT(t1); - SASSERT(t2); - - merge(t1->get_root(), t2->get_root()); -} - -void term_graph::internalize_lit(app* lit) { - if (is_internalized(lit)) return; - - expr *e1, *e2; - if (m.is_eq (lit, e1, e2)) { - SASSERT(is_app(e1)); - SASSERT(is_app(e2)); - internalize_eq (::to_app(e1), ::to_app(e2)); - } - else { - internalize_term(lit); - } -} - -void term_graph::merge (term &t1, term &t2) { - SASSERT(t1.is_root()); - SASSERT(t2.is_root()); - - if (&t1 == &t2) return; - - term *a = &t1; - term *b = &t2; - if (a->get_class_size() > b->get_class_size()) { - std::swap(a, b); - } - - // make 'a' be the root of the equivalence class of 'b' - b->set_root(*a); - for (term *it = &b->get_next(); it != b; it = &it->get_next()) { - it->set_root(*a); - } - - // merge equivalence classes - a->merge_eq_class(*b); - - // -- merge might have invalidated term2map cache - m_term2app.reset(); - m_pinned.reset(); -} - -app* term_graph::mk_app_core (app *a) { - expr_ref_vector kids(m); - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - kids.push_back (mk_app(::to_app(a->get_arg(i)))); - } - - app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); - m_pinned.push_back(res); - - return res; -} - -app_ref term_graph::mk_app(term const &r) { - SASSERT(r.is_root()); - - if (r.get_app()->get_num_args() == 0) { - return app_ref(r.get_app(), m); - } - - app* res; - if (m_term2app.find(r.get_id(), res)) { - return app_ref(res, m); - } - - res = mk_app_core (r.get_app()); - m_term2app.insert(r.get_id(), res); - return app_ref(res, m); - -} -app_ref term_graph::mk_app(app *a) { - term *t = get_term(a); - if (!t) {return app_ref(a, m);} - - term &r = t->get_root(); - return mk_app(r); - -} - -void term_graph::mk_equalities(term const &t, app_ref_vector &out) { - SASSERT(t.is_root()); - app_ref rep(m); - rep = mk_app(t); - - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - app* mem; - mem = mk_app_core(it->get_app()); - out.push_back (m.mk_eq (rep, mem)); - } -} - -void term_graph::mk_all_equalities(term const &t, app_ref_vector &out) { - mk_equalities(t, out); - - for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { - app* a1; - a1 = mk_app_core (it->get_app()); - for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { - app *a2; - a2 = mk_app_core(it2->get_app()); - out.push_back (m.mk_eq (a1, a2)); - } - } -} - -void term_graph::reset_marks() { - for (unsigned i = 0, sz = m_terms.size(); i < sz; ++i) { - term *t = m_terms.get(i); - t->set_mark(false); - } -} - -/// Order of preference for roots of equivalence classes -/// XXX This should be factored out to let clients control the preference -bool term_graph::term_le(term const &t1, term const &t2) { - - // prefer constants over applications - // prefer uninterpreted constants over values - // prefer smaller expressions over larger ones - app *a1, *a2; - a1 = t1.get_app(); - a2 = t2.get_app(); - if (a1->get_num_args() == 0 && a2->get_num_args() > 0) { - return true; - } - if (a1->get_num_args() == a2->get_num_args()) { - return m.is_value(a2); - } - - unsigned sz1 = get_num_exprs(a1); - unsigned sz2 = get_num_exprs(a2); - return sz1 < sz2; -} - -void term_graph::pick_root (term &t) { - term *r = &t; - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - it->set_mark(true); - if (term_le(*it, *r)) { r = it; } - } - - // -- if found something better, make it the new root - if (r != &t) { - r->mk_root(); - } -} -/// Choose better roots for equivalence classes -void term_graph::pick_roots() { - for (unsigned i = 0, sz = m_terms.size(); i < sz; ++i) { - term *t = m_terms.get(i); - if (t->is_marked() || !t->is_root()) {continue;} - pick_root(*t); - } - reset_marks(); -} - -void term_graph::display(std::ostream &out) { - for (unsigned i = 0, sz = m_terms.size(); i < sz; ++i) { - term *t = m_terms.get(i); - out << mk_pp(t->get_app(), m) << " is root " << t->is_root() - << " cls sz " << t->get_class_size() - << " term " << t - << "\n"; - } -} -void term_graph::to_lits (app_ref_vector &lits, bool all_equalities) { - pick_roots(); - - for (unsigned i = 0, sz = m_lits.size(); i < sz; ++i) { - app *a = m_lits.get(i); - if (is_internalized(a)) { - lits.push_back (mk_app(a)); - } - } - - for (unsigned i = 0, sz = m_terms.size(); i < sz; ++i) { - term *t = m_terms.get(i); - if (!t->is_root()) {continue;} - - if (all_equalities) { - mk_all_equalities (*t, lits); - } else { - mk_equalities(*t, lits); - } - } -} -void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { - app_ref_vector out(m); - to_lits (out, all_equalities); - for (unsigned i = 0, sz = out.size(); i < sz; ++i) { - lits.push_back(out.get(i)); - } -} - -app_ref term_graph::to_app() { - app_ref_vector lits(m); - to_lits(lits); - return mk_and(lits); -} - -void term_graph::reset() { - m_term2app.reset(); - m_pinned.reset(); - m_app2term.reset(); - m_terms.reset(); - m_lits.reset(); -} - -} diff --git a/src/muz/spacer/spacer_term_graph.h b/src/muz/spacer/spacer_term_graph.h deleted file mode 100644 index b547adef3..000000000 --- a/src/muz/spacer/spacer_term_graph.h +++ /dev/null @@ -1,97 +0,0 @@ -/**++ -Copyright (c) Arie Gurfinkel - -Module Name: - - spacer_term_graph.h - -Abstract: - - Equivalence graph of terms - -Author: - - Arie Gurfinkel - -Notes: - ---*/ -#ifndef _SPACER_TERM_GRAPH_H_ -#define _SPACER_TERM_GRAPH_H_ - -#include "ast/ast.h" - -#include "util/plugin_manager.h" - -namespace spacer { - -class term; - -class term_graph_plugin { - family_id m_id; -public: - term_graph_plugin(family_id fid) : m_id(fid) {} - virtual ~term_graph_plugin() {} - - family_id get_family_id() const {return m_id;} - - /// Process (and potentially augment) a literal - virtual app_ref process_lit (app *lit) = 0; -}; - -class term_graph { - ast_manager &m; - ptr_vector m_terms; - app_ref_vector m_lits; - u_map m_app2term; - - app_ref_vector m_pinned; - u_map m_term2app; - - plugin_manager m_plugins; - - void merge(term &t1, term &t2); - - term *mk_term(app *t); - term *get_term(app *t); - - term *internalize_term(app *t); - void internalize_eq(app *a1, app *a2); - void internalize_lit(app *lit); - - bool is_internalized(app *a); - - bool term_le(term const &t1, term const &t2); - void pick_root (term &t); - void pick_roots(); - - void reset_marks(); - - app *mk_app_core(app* a); - app_ref mk_app(term const &t); - app_ref mk_app(app *a); - void mk_equalities(term const &t, app_ref_vector &out); - void mk_all_equalities(term const &t, app_ref_vector &out); - void display(std::ostream &out); -public: - term_graph(ast_manager &man); - ~term_graph(); - - ast_manager &get_ast_manager() const {return m;} - - void add_lit(app *lit); - void add_lits(expr_ref_vector const &lits) { - for (unsigned i = 0, sz = lits.size(); i < sz; ++i) { - add_lit(::to_app(lits.get(i))); - } - } - - void reset(); - void to_lits(app_ref_vector &lits, bool all_equalities = false); - void to_lits(expr_ref_vector &lits, bool all_equalities = false); - app_ref to_app(); - -}; - -} -#endif diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index da946e211..29d72fabd 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -65,7 +65,7 @@ Notes: #include "tactic/arith/arith_bounds_tactic.h" #include "ast/factor_equivs.h" -#include "muz/spacer/spacer_term_graph.h" +#include "qe/qe_term_graph.h" namespace spacer { @@ -742,7 +742,7 @@ namespace { } if (use_factor_eqs) { // -- refactor equivalence classes and choose a representative - spacer::term_graph egraph(out.m()); + qe::term_graph egraph(out.m()); egraph.add_lits (v); v.reset(); egraph.to_lits(v); @@ -754,7 +754,7 @@ namespace { << "to\n" << mk_and(v) << "\n";); TRACE("spacer_normalize", - spacer::term_graph egraph(out.m()); + qe::term_graph egraph(out.m()); for (expr* e : v) egraph.add_lit (to_app(e)); tout << "Reduced app:\n" << mk_pp(egraph.to_app(), out.m()) << "\n";); diff --git a/src/qe/CMakeLists.txt b/src/qe/CMakeLists.txt index d43db34f0..d771ec62e 100644 --- a/src/qe/CMakeLists.txt +++ b/src/qe/CMakeLists.txt @@ -18,6 +18,7 @@ z3_add_component(qe qe_mbi.cpp qe_sat_tactic.cpp qe_tactic.cpp + qe_term_graph.cpp qsat.cpp COMPONENT_DEPENDENCIES nlsat_tactic diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 8be15ed7f..aea5ad978 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -76,22 +76,23 @@ namespace qe { lits.reset(); m_solver->get_unsat_core(lits); return mbi_unsat; - case l_true: + case l_true: { + expr_ref_vector fmls(m); m_solver->get_model(mdl); lits.reset(); - for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { - func_decl* c = mdl->get_constant(i); - if (vars.contains(c)) { - if (m.is_true(mdl->get_const_interp(c))) { - lits.push_back(m.mk_const(c)); - } - else { - lits.push_back(m.mk_not(m.mk_const(c))); - } - } - } + // 1. extract formulas from solver + // 2. extract implicant over formulas + // 3. extract equalities or other assignments over the congruence classes + m_solver->get_assertions(fmls); + NOT_IMPLEMENTED_YET(); return mbi_sat; + } default: + // TBD: if not running solver to completion, then: + // 1. extract unit literals from m_solver. + // 2. run a cc over the units + // 3. extract equalities or other assignments over the congruence classes + // 4. ensure that at least some progress is made over original lits. return mbi_undef; } } From 14696f03f724ed09b7707175f3acb87786971703 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 10:57:57 -0700 Subject: [PATCH 263/364] add some review comments Signed-off-by: Nikolaj Bjorner --- src/qe/qe_term_graph.cpp | 525 +++++++++++++++++++++++++++++++++++++++ src/qe/qe_term_graph.h | 94 +++++++ 2 files changed, 619 insertions(+) create mode 100644 src/qe/qe_term_graph.cpp create mode 100644 src/qe/qe_term_graph.h diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp new file mode 100644 index 000000000..d0bcc11c4 --- /dev/null +++ b/src/qe/qe_term_graph.cpp @@ -0,0 +1,525 @@ +/**++ +Copyright (c) Arie Gurfinkel + +Module Name: + + qe_term_graph.cpp + +Abstract: + + Equivalence graph of terms + +Author: + + Arie Gurfinkel + +Notes: + +--*/ + +#include "util/util.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/for_each_expr.h" +#include "qe/qe_term_graph.h" + +namespace qe { + +class term { + // -- an app represented by this term + app* m_app; + // -- root of the equivalence class + term* m_root; + // -- next element in the equivalence class (cyclic linked list) + term* m_next; + // -- eq class size + unsigned m_class_size; + + // -- general purpose mark + unsigned m_mark:1; + // -- general purpose second mark + unsigned m_mark2:1; + // -- is an interpreted constant + unsigned m_interpreted:1; + + // -- terms that contain this term as a child + //ptr_vector m_uses; + + // ptr_vector m_args; + +public: + term(app* a) : m_app(a), m_root(this), m_next(this), + m_class_size(1), m_mark(false), m_mark2(false), + m_interpreted(false) {} + + ~term() {} + + unsigned get_id() const {return m_app->get_id();} + + bool is_marked() const {return m_mark;} + void set_mark(bool v){m_mark = v;} + bool is_marked2() const {return m_mark2;} + void set_mark2(bool v){m_mark2 = v;} + + bool is_interpreted() const {return m_interpreted;} + void mark_as_interpreted() {m_interpreted=true;} + app* get_app() const {return m_app;} + + term &get_root() const {return *m_root;} + bool is_root() const {return m_root == this;} + void set_root(term &r) {m_root = &r;} + term &get_next() const {return *m_next;} + + unsigned get_class_size() const {return m_class_size;} + + void merge_eq_class(term &b) { + std::swap(this->m_next, b.m_next); + m_class_size += b.get_class_size(); + // -- reset (useful for debugging) + b.m_class_size = 0; + } + + // -- make this term the root of its equivalence class + void mk_root() { + if (is_root()) return; + + term *curr = this; + do { + if (curr->is_root()) { + // found previous root + SASSERT(curr != this); + m_class_size = curr->get_class_size(); + curr->m_class_size = 0; + } + curr->set_root(*this); + curr = &curr->get_next(); + } + while (curr != this); + } +}; + +class arith_term_graph_plugin : public term_graph_plugin { + term_graph &m_g; + ast_manager &m; + arith_util m_arith; + +public: + arith_term_graph_plugin(term_graph &g) : + term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), + m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} + + virtual ~arith_term_graph_plugin() {} + + bool mk_eq_core (expr *_e1, expr *_e2, app_ref &res) { + expr *e1, *e2; + e1 = _e1; + e2 = _e2; + + if (m_arith.is_zero(e1)) { + std::swap(e1, e2); + } + // y + -1*x == 0 --> y = x + expr *a0 = 0, *a1 = 0, *x = 0; + if (m_arith.is_zero(e2) && m_arith.is_add(e1, a0, a1)) { + if (m_arith.is_times_minus_one(a1, x)) { + e1 = a0; + e2 = x; + } + else if (m_arith.is_times_minus_one(a0, x)) { + e1 = a1; + e2 = x; + } + } + res = m.mk_eq(e1, e2); + return true; + } + + app* mk_le_zero(expr *arg) { + expr *e1, *e2, *e3; + // XXX currently disabled + if (m_arith.is_add(arg, e1, e2)) { + // e1-e2<=0 --> e1<=e2 + if (m_arith.is_times_minus_one(e2, e3)) { + return m_arith.mk_le(e1, e3); + } + // -e1+e2<=0 --> e2<=e1 + else if (m_arith.is_times_minus_one(e1, e3)) { + return m_arith.mk_le(e2, e3); + } + } + return m_arith.mk_le(arg, mk_zero()); + } + + app* mk_ge_zero(expr *arg) { + expr *e1, *e2, *e3; + // XXX currently disabled + if (m_arith.is_add(arg, e1, e2)) { + // e1-e2>=0 --> e1>=e2 + if (m_arith.is_times_minus_one(e2, e3)) { + return m_arith.mk_ge(e1, e3); + } + // -e1+e2>=0 --> e2>=e1 + else if (m_arith.is_times_minus_one(e1, e3)) { + return m_arith.mk_ge(e2, e3); + } + } + return m_arith.mk_ge(arg, mk_zero()); + } + + bool mk_le_core (expr *arg1, expr * arg2, app_ref &result) { + // t <= -1 ==> t < 0 ==> ! (t >= 0) + rational n; + if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { + result = m.mk_not (mk_ge_zero (arg1)); + return true; + } + else if (m_arith.is_zero(arg2)) { + result = mk_le_zero(arg1); + return true; + } + else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n < 0) { + // t <= n ==> t < n + 1 ==> ! (t >= n + 1) + result = m.mk_not(m_arith.mk_ge(arg1, m_arith.mk_numeral(n+1, true))); + return true; + } + return false; + } + + expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} + bool is_one (expr const * n) const { + rational val; + return m_arith.is_numeral (n, val) && val.is_one (); + } + + bool mk_ge_core (expr * arg1, expr * arg2, app_ref &result) { + // t >= 1 ==> t > 0 ==> ! (t <= 0) + rational n; + if (m_arith.is_int (arg1) && is_one (arg2)) { + result = m.mk_not (mk_le_zero (arg1)); + return true; + } + else if (m_arith.is_zero(arg2)) { + result = mk_ge_zero(arg1); + return true; + } + else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n > 0) { + // t >= n ==> t > n - 1 ==> ! (t <= n - 1) + result = m.mk_not(m_arith.mk_le(arg1, m_arith.mk_numeral(n-1, true))); + return true; + } + return false; + } + + virtual app_ref process_lit (app *_lit) { + app *lit = _lit; + expr *e1, *e2; + + // strip negation + bool is_neg = m.is_not(lit); + if (is_neg) { + lit = to_app(to_app(lit)->get_arg(0)); + } + + app_ref res(m); + res = lit; + if (m.is_eq (lit, e1, e2)) { + mk_eq_core(e1, e2, res); + } + else if (m_arith.is_le(lit, e1, e2)) { + mk_le_core(e1, e2, res); + } + else if (m_arith.is_ge(lit, e1, e2)) { + mk_ge_core(e1, e2, res); + } + + // restore negation + if (is_neg) { + res = m.mk_not(res); + } + + return res; + } +}; + +term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { + m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); +} +term_graph::~term_graph() { + reset(); +} + +static family_id get_family_id(ast_manager &m, app *lit) { + family_id fid = null_family_id; + + expr *e1, *e2, *e3; + // strip negation + if (!m.is_not (lit, e1)) { e1 = lit; } + + // deal with equality using sort of range + if (m.is_eq (e1, e2, e3)) { + fid = get_sort (e2)->get_family_id(); + } + // extract family_id of top level app + else { + fid = to_app(e1)->get_decl()->get_family_id(); + } + + return fid; +} + +void term_graph::add_lit(app *l) { + app_ref lit(m); + + family_id fid = get_family_id (m, l); + term_graph_plugin *pin = m_plugins.get_plugin(fid); + if (pin) { + lit = pin->process_lit(l); + } else { + lit = l; + } + m_lits.push_back(lit); + internalize_lit(lit); +} + +bool term_graph::is_internalized(app *a) { + return m_app2term.contains(a->get_id()); +} + +term* term_graph::get_term(app *a) { + term *res; + return m_app2term.find (a->get_id(), res) ? res : nullptr; +} + +term *term_graph::mk_term(app *a) { + term * t = alloc(term, a); + if (a->get_num_args() == 0 && m.is_unique_value(a)){ + t->mark_as_interpreted(); + } + + m_terms.push_back(t); + m_app2term.insert(a->get_id(), t); + return t; +} + +term *term_graph::internalize_term(app *t) { + term *res = get_term(t); + + if (!res) { + for (expr * arg : *t) { + SASSERT(is_app(arg)); + internalize_term(::to_app(arg)); + } + res = mk_term(t); + } + return res; +} + +void term_graph::internalize_eq(app *a1, app* a2) { + internalize_lit(a1); + internalize_lit(a2); + merge(get_term(a1)->get_root(), get_term(a2)->get_root()); +} + +void term_graph::internalize_lit(app* lit) { + if (is_internalized(lit)) return; + + expr *e1, *e2; + if (m.is_eq (lit, e1, e2)) { + SASSERT(is_app(e1)); + SASSERT(is_app(e2)); + internalize_eq (::to_app(e1), ::to_app(e2)); + } + else { + // NSB: this is thrown away. + // Is caller responsible for maintaining other predicates than equalities? + internalize_term(lit); + } +} + +void term_graph::merge (term &t1, term &t2) { + SASSERT(t1.is_root()); + SASSERT(t2.is_root()); + + if (&t1 == &t2) return; + + term *a = &t1; + term *b = &t2; + if (a->get_class_size() > b->get_class_size()) { + std::swap(a, b); + } + + // make 'a' be the root of the equivalence class of 'b' + b->set_root(*a); + for (term *it = &b->get_next(); it != b; it = &it->get_next()) { + it->set_root(*a); + } + + // merge equivalence classes + a->merge_eq_class(*b); + + // -- merge might have invalidated term2map cache + m_term2app.reset(); + m_pinned.reset(); +} + +app* term_graph::mk_app_core (app *a) { + expr_ref_vector kids(m); + for (expr * arg : *a) { + kids.push_back (mk_app(::to_app(arg))); + } + + app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); + m_pinned.push_back(res); + + return res; +} + +app_ref term_graph::mk_app(term const &r) { + SASSERT(r.is_root()); + + if (r.get_app()->get_num_args() == 0) { + return app_ref(r.get_app(), m); + } + + app* res; + if (m_term2app.find(r.get_id(), res)) { + return app_ref(res, m); + } + + res = mk_app_core (r.get_app()); + m_term2app.insert(r.get_id(), res); + return app_ref(res, m); + +} +app_ref term_graph::mk_app(app *a) { + term *t = get_term(a); + if (!t) + return app_ref(a, m); + else + return mk_app(t->get_root()); + +} + +void term_graph::mk_equalities(term const &t, app_ref_vector &out) { + SASSERT(t.is_root()); + app_ref rep(mk_app(t), m); + + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + app* mem = mk_app_core(it->get_app()); + out.push_back (m.mk_eq (rep, mem)); + } +} + +void term_graph::mk_all_equalities(term const &t, app_ref_vector &out) { + mk_equalities(t, out); + + for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { + app* a1 = mk_app_core (it->get_app()); + for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { + app *a2; + a2 = mk_app_core(it2->get_app()); + out.push_back (m.mk_eq (a1, a2)); + } + } +} + +void term_graph::reset_marks() { + for (term * t : m_terms) { + t->set_mark(false); + } +} + +/// Order of preference for roots of equivalence classes +/// XXX This should be factored out to let clients control the preference +bool term_graph::term_le(term const &t1, term const &t2) { + + // prefer constants over applications + // prefer uninterpreted constants over values + // prefer smaller expressions over larger ones + app *a1, *a2; + a1 = t1.get_app(); + a2 = t2.get_app(); + if (a1->get_num_args() == 0 && a2->get_num_args() > 0) { + return true; + } + // NSB: how does this possibly define an order? + if (a1->get_num_args() == a2->get_num_args()) { + return m.is_value(a2); + } + + unsigned sz1 = get_num_exprs(a1); + unsigned sz2 = get_num_exprs(a2); + return sz1 < sz2; +} + +void term_graph::pick_root (term &t) { + term *r = &t; + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + it->set_mark(true); + if (term_le(*it, *r)) { r = it; } + } + + // -- if found something better, make it the new root + if (r != &t) { + r->mk_root(); + } +} +/// Choose better roots for equivalence classes +void term_graph::pick_roots() { + for (term* t : m_terms) { + if (!t->is_marked() && t->is_root()) + pick_root(*t); + } + reset_marks(); +} + +void term_graph::display(std::ostream &out) { + for (term * t : m_terms) { + out << mk_pp(t->get_app(), m) << " is root " << t->is_root() + << " cls sz " << t->get_class_size() + << " term " << t + << "\n"; + } +} +void term_graph::to_lits (app_ref_vector &lits, bool all_equalities) { + pick_roots(); + + for (app * a : m_lits) { + if (is_internalized(a)) { + lits.push_back (mk_app(a)); + } + } + + for (term * t : m_terms) { + if (!t->is_root()) + continue; + else if (all_equalities) + mk_all_equalities (*t, lits); + else + mk_equalities(*t, lits); + } +} +void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { + app_ref_vector out(m); + to_lits (out, all_equalities); + for (app* a : out) { + lits.push_back(a); + } +} + +app_ref term_graph::to_app() { + app_ref_vector lits(m); + to_lits(lits); + return mk_and(lits); +} + +void term_graph::reset() { + m_term2app.reset(); + m_pinned.reset(); + m_app2term.reset(); + std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); + m_terms.reset(); + m_lits.reset(); +} + +} diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h new file mode 100644 index 000000000..76d793e59 --- /dev/null +++ b/src/qe/qe_term_graph.h @@ -0,0 +1,94 @@ +/**++ +Copyright (c) Arie Gurfinkel + +Module Name: + + qe_term_graph.h + +Abstract: + + Equivalence graph of terms + +Author: + + Arie Gurfinkel + +Notes: + +--*/ +#ifndef QE_TERM_GRAPH_H__ +#define QE_TERM_GRAPH_H__ + +#include "ast/ast.h" +#include "util/plugin_manager.h" + +namespace qe { + + class term; + + class term_graph_plugin { + family_id m_id; + public: + term_graph_plugin(family_id fid) : m_id(fid) {} + virtual ~term_graph_plugin() {} + + family_id get_family_id() const {return m_id;} + + /// Process (and potentially augment) a literal + virtual app_ref process_lit (app *lit) = 0; + }; + + class term_graph { + ast_manager &m; + ptr_vector m_terms; + app_ref_vector m_lits; + u_map m_app2term; + + app_ref_vector m_pinned; + u_map m_term2app; + + plugin_manager m_plugins; + + void merge(term &t1, term &t2); + + term *mk_term(app *t); + term *get_term(app *t); + + term *internalize_term(app *t); + void internalize_eq(app *a1, app *a2); + void internalize_lit(app *lit); + + bool is_internalized(app *a); + + bool term_le(term const &t1, term const &t2); + void pick_root (term &t); + void pick_roots(); + + void reset_marks(); + + app *mk_app_core(app* a); + app_ref mk_app(term const &t); + app_ref mk_app(app *a); + void mk_equalities(term const &t, app_ref_vector &out); + void mk_all_equalities(term const &t, app_ref_vector &out); + void display(std::ostream &out); + public: + term_graph(ast_manager &man); + ~term_graph(); + + ast_manager &get_ast_manager() const { return m;} + + void add_lit(app *lit); + void add_lits(expr_ref_vector const &lits) { + for (expr* e : lits) add_lit(::to_app(e)); + } + + void reset(); + void to_lits(app_ref_vector &lits, bool all_equalities = false); + void to_lits(expr_ref_vector &lits, bool all_equalities = false); + app_ref to_app(); + + }; + +} +#endif From d26609ebdd6ade1087e2a7ace6552c74c87f1e82 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 14:50:46 -0700 Subject: [PATCH 264/364] prepare term-graph for cc Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 4 +- src/qe/qe_mbi.cpp | 16 +- src/qe/qe_mbi.h | 5 +- src/qe/qe_term_graph.cpp | 209 ++++++++++++++++-------- src/qe/qe_term_graph.h | 37 +++-- src/smt/smt_context.cpp | 18 +- 6 files changed, 193 insertions(+), 96 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 5e8a687c7..3a2f6fc85 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -421,7 +421,7 @@ public: for (func_decl* v : m_vars) { vars.push_back(v); } - qe::interpolator mbi; + qe::interpolator mbi(m); expr_ref a(m_a, m); expr_ref b(m_b, m); expr_ref itp(m); @@ -433,7 +433,7 @@ public: sB->assert_expr(b); qe::prop_mbi_plugin pA(sA.get()); qe::prop_mbi_plugin pB(sB.get()); - lbool res = mbi.binary(pA, pB, vars, itp); + lbool res = mbi.pingpong(pA, pB, vars, itp); ctx.regular_stream() << res << " " << itp << "\n"; } diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index aea5ad978..eea3087e2 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -49,8 +49,8 @@ namespace qe { if (m.is_true(mdl->get_const_interp(c))) { lits.push_back(m.mk_const(c)); } - else { - lits.push_back(m.mk_not(m.mk_const(c))); + else if (m.is_false(mdl->get_const_interp(c))) { + lits.push_back(m.mk_const(c)); } } } @@ -105,10 +105,8 @@ namespace qe { /** -------------------------------------------------------------- * ping-pong interpolation of Gurfinkel & Vizel * compute a binary interpolant. - * TBD: also implement the one-sided versions that create clausal interpolants. */ - lbool interpolator::binary(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { - ast_manager& m = vars.get_manager(); + lbool interpolator::pingpong(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { model_ref mdl; expr_ref_vector lits(m); bool turn = true; @@ -156,4 +154,12 @@ namespace qe { last_res = next_res; } } + + /** + * TBD: also implement the one-sided versions that create clausal interpolants. + */ + lbool interpolator::pogo(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { + NOT_IMPLEMENTED_YET(); + return l_undef; + } }; diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index dad29167f..d9af62bd0 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -81,8 +81,11 @@ namespace qe { * use cases for interpolation. */ class interpolator { + ast_manager& m; public: - static lbool binary(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); + interpolator(ast_manager& m):m(m) {} + lbool pingpong(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); + lbool pogo(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); }; #if 0 diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index d0bcc11c4..73412c50b 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -27,7 +27,7 @@ namespace qe { class term { // -- an app represented by this term - app* m_app; + expr* m_app; // NSB: to make usable with exprs // -- root of the equivalence class term* m_root; // -- next element in the equivalence class (cyclic linked list) @@ -43,27 +43,87 @@ class term { unsigned m_interpreted:1; // -- terms that contain this term as a child - //ptr_vector m_uses; + ptr_vector m_parents; - // ptr_vector m_args; + // arguments of term. + ptr_vector m_children; public: - term(app* a) : m_app(a), m_root(this), m_next(this), - m_class_size(1), m_mark(false), m_mark2(false), - m_interpreted(false) {} + term(expr* a, u_map& app2term) : + m_app(a), + m_root(this), + m_next(this), + m_class_size(1), + m_mark(false), + m_mark2(false), + m_interpreted(false) { + if (!is_app(a)) return; + for (expr* e : *to_app(a)) { + term* t = app2term[e->get_id()]; + t->m_parents.push_back(this); + m_children.push_back(t); + } + } ~term() {} - unsigned get_id() const {return m_app->get_id();} + class parents { + term const& t; + public: + parents(term const& _t):t(_t) {} + parents(term const* _t):t(*_t) {} + ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } + ptr_vector::const_iterator end() const { return t.m_parents.end(); } + }; + + class children { + term const& t; + public: + children(term const& _t):t(_t) {} + children(term const* _t):t(*_t) {} + ptr_vector::const_iterator begin() const { return t.m_children.begin(); } + ptr_vector::const_iterator end() const { return t.m_children.end(); } + }; + + // Congruence table hash function is based on + // roots of children and function declaration. + + struct cg_hash { + unsigned operator()(term const* t) const { + unsigned a, b, c; + a = b = c = t->get_decl_id(); + for (term * ch : children(t)) { + a = ch->get_root().get_id(); + mix(a, b, c); + } + return c; + } + }; + + struct cg_eq { + bool operator()(term * t1, term * t2) const { + if (t1->get_decl_id() != t2->get_decl_id()) return false; + if (t1->m_children.size() != t2->m_children.size()) return false; + for (unsigned i = 0, sz = t1->m_children.size(); i < sz; ++ i) { + if (t1->m_children[i]->get_root().get_id() != t2->m_children[i]->get_root().get_id()) return false; + } + return true; + } + }; + + unsigned get_id() const { return m_app->get_id();} + + unsigned get_decl_id() const { return is_app(m_app) ? to_app(m_app)->get_decl()->get_id() : m_app->get_id(); } bool is_marked() const {return m_mark;} void set_mark(bool v){m_mark = v;} - bool is_marked2() const {return m_mark2;} - void set_mark2(bool v){m_mark2 = v;} + bool is_marked2() const {return m_mark2;} // NSB: where is this used? + void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? bool is_interpreted() const {return m_interpreted;} void mark_as_interpreted() {m_interpreted=true;} - app* get_app() const {return m_app;} + expr* get_app() const {return m_app;} + unsigned get_num_args() const { return is_app(m_app) ? to_app(m_app)->get_num_args() : 0; } term &get_root() const {return *m_root;} bool is_root() const {return m_root == this;} @@ -98,6 +158,8 @@ public: } }; + + class arith_term_graph_plugin : public term_graph_plugin { term_graph &m_g; ast_manager &m; @@ -244,6 +306,7 @@ public: term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); } + term_graph::~term_graph() { reset(); } @@ -251,7 +314,7 @@ term_graph::~term_graph() { static family_id get_family_id(ast_manager &m, app *lit) { family_id fid = null_family_id; - expr *e1, *e2, *e3; + expr *e1 = nullptr, *e2 = nullptr, *e3 = nullptr; // strip negation if (!m.is_not (lit, e1)) { e1 = lit; } @@ -285,14 +348,14 @@ bool term_graph::is_internalized(app *a) { return m_app2term.contains(a->get_id()); } -term* term_graph::get_term(app *a) { +term* term_graph::get_term(expr *a) { term *res; return m_app2term.find (a->get_id(), res) ? res : nullptr; } -term *term_graph::mk_term(app *a) { - term * t = alloc(term, a); - if (a->get_num_args() == 0 && m.is_unique_value(a)){ +term *term_graph::mk_term(expr *a) { + term * t = alloc(term, a, m_app2term); + if (t->get_num_args() == 0 && m.is_unique_value(a)){ t->mark_as_interpreted(); } @@ -301,37 +364,32 @@ term *term_graph::mk_term(app *a) { return t; } -term *term_graph::internalize_term(app *t) { +term *term_graph::internalize_term(expr *t) { term *res = get_term(t); if (!res) { - for (expr * arg : *t) { - SASSERT(is_app(arg)); - internalize_term(::to_app(arg)); + if (is_app(t)) { + for (expr * arg : *::to_app(t)) { + internalize_term(arg); + } } res = mk_term(t); } return res; } -void term_graph::internalize_eq(app *a1, app* a2) { - internalize_lit(a1); - internalize_lit(a2); +void term_graph::internalize_eq(expr *a1, expr* a2) { + internalize_term(a1); + internalize_term(a2); merge(get_term(a1)->get_root(), get_term(a2)->get_root()); } void term_graph::internalize_lit(app* lit) { - if (is_internalized(lit)) return; - - expr *e1, *e2; - if (m.is_eq (lit, e1, e2)) { - SASSERT(is_app(e1)); - SASSERT(is_app(e2)); - internalize_eq (::to_app(e1), ::to_app(e2)); + expr *e1 = nullptr, *e2 = nullptr; + if (m.is_eq (lit, e1, e2)) { + internalize_eq (e1, e2); } else { - // NSB: this is thrown away. - // Is caller responsible for maintaining other predicates than equalities? internalize_term(lit); } } @@ -351,50 +409,61 @@ void term_graph::merge (term &t1, term &t2) { // make 'a' be the root of the equivalence class of 'b' b->set_root(*a); for (term *it = &b->get_next(); it != b; it = &it->get_next()) { + // TBD: remove parents of it from the cg table. it->set_root(*a); } // merge equivalence classes a->merge_eq_class(*b); + // TBD: insert parents of b's old equilvalence class into the cg table + // and propagate equalities. + // -- merge might have invalidated term2map cache + + // NSB: ??? what is ownership model of pinned in m_terms? m_term2app.reset(); - m_pinned.reset(); + m_pinned.reset(); } -app* term_graph::mk_app_core (app *a) { - expr_ref_vector kids(m); - for (expr * arg : *a) { - kids.push_back (mk_app(::to_app(arg))); +expr* term_graph::mk_app_core (expr *e) { + if (is_app(e)) { + expr_ref_vector kids(m); + app* a = ::to_app(e); + for (expr * arg : *a) { + kids.push_back (mk_app(arg)); + } + app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); + m_pinned.push_back(res); + return res; + } + else { + return e; } - - app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); - m_pinned.push_back(res); - - return res; } -app_ref term_graph::mk_app(term const &r) { +expr_ref term_graph::mk_app(term const &r) { SASSERT(r.is_root()); - if (r.get_app()->get_num_args() == 0) { - return app_ref(r.get_app(), m); + if (r.get_num_args() == 0) { + return expr_ref(r.get_app(), m); } - app* res; + expr* res = nullptr; if (m_term2app.find(r.get_id(), res)) { - return app_ref(res, m); + return expr_ref(res, m); } res = mk_app_core (r.get_app()); m_term2app.insert(r.get_id(), res); - return app_ref(res, m); + return expr_ref(res, m); } -app_ref term_graph::mk_app(app *a) { + +expr_ref term_graph::mk_app(expr *a) { term *t = get_term(a); if (!t) - return app_ref(a, m); + return expr_ref(a, m); else return mk_app(t->get_root()); @@ -402,10 +471,10 @@ app_ref term_graph::mk_app(app *a) { void term_graph::mk_equalities(term const &t, app_ref_vector &out) { SASSERT(t.is_root()); - app_ref rep(mk_app(t), m); + expr_ref rep(mk_app(t), m); for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - app* mem = mk_app_core(it->get_app()); + expr* mem = mk_app_core(it->get_app()); out.push_back (m.mk_eq (rep, mem)); } } @@ -414,10 +483,9 @@ void term_graph::mk_all_equalities(term const &t, app_ref_vector &out) { mk_equalities(t, out); for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { - app* a1 = mk_app_core (it->get_app()); + expr* a1 = mk_app_core (it->get_app()); for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { - app *a2; - a2 = mk_app_core(it2->get_app()); + expr* a2 = mk_app_core(it2->get_app()); out.push_back (m.mk_eq (a1, a2)); } } @@ -436,19 +504,16 @@ bool term_graph::term_le(term const &t1, term const &t2) { // prefer constants over applications // prefer uninterpreted constants over values // prefer smaller expressions over larger ones - app *a1, *a2; - a1 = t1.get_app(); - a2 = t2.get_app(); - if (a1->get_num_args() == 0 && a2->get_num_args() > 0) { + if (t1.get_num_args() == 0 && t2.get_num_args() > 0) { return true; } - // NSB: how does this possibly define an order? - if (a1->get_num_args() == a2->get_num_args()) { - return m.is_value(a2); + if (t1.get_num_args() == t2.get_num_args()) { + // NSB: how does this possibly define an order? + return m.is_value(t2.get_app()); } - unsigned sz1 = get_num_exprs(a1); - unsigned sz2 = get_num_exprs(a2); + unsigned sz1 = get_num_exprs(t1.get_app()); + unsigned sz2 = get_num_exprs(t1.get_app()); return sz1 < sz2; } @@ -481,12 +546,13 @@ void term_graph::display(std::ostream &out) { << "\n"; } } + void term_graph::to_lits (app_ref_vector &lits, bool all_equalities) { pick_roots(); for (app * a : m_lits) { if (is_internalized(a)) { - lits.push_back (mk_app(a)); + lits.push_back (::to_app(mk_app(a))); } } @@ -499,6 +565,7 @@ void term_graph::to_lits (app_ref_vector &lits, bool all_equalities) { mk_equalities(*t, lits); } } + void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { app_ref_vector out(m); to_lits (out, all_equalities); @@ -522,4 +589,18 @@ void term_graph::reset() { m_lits.reset(); } +expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { + obj_hashtable _decls; + for (func_decl* f : decls) _decls.insert(f); + // . propagate representatives up over parents. + // use work-list + marking to propagate. + // . produce equalities over represented classes. + // . produce other literals over represented classes + // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) + + expr_ref_vector result(m); + NOT_IMPLEMENTED_YET(); + return result; +} + } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 76d793e59..0879fb386 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -39,23 +39,22 @@ namespace qe { }; class term_graph { - ast_manager &m; + ast_manager & m; ptr_vector m_terms; - app_ref_vector m_lits; - u_map m_app2term; - - app_ref_vector m_pinned; - u_map m_term2app; + app_ref_vector m_lits; + u_map m_app2term; + app_ref_vector m_pinned; + u_map m_term2app; plugin_manager m_plugins; void merge(term &t1, term &t2); - term *mk_term(app *t); - term *get_term(app *t); + term *mk_term(expr *t); + term *get_term(expr *t); - term *internalize_term(app *t); - void internalize_eq(app *a1, app *a2); + term *internalize_term(expr *t); + void internalize_eq(expr *a1, expr *a2); void internalize_lit(app *lit); bool is_internalized(app *a); @@ -66,27 +65,35 @@ namespace qe { void reset_marks(); - app *mk_app_core(app* a); - app_ref mk_app(term const &t); - app_ref mk_app(app *a); + expr* mk_app_core(expr* a); + expr_ref mk_app(term const &t); + expr_ref mk_app(expr *a); void mk_equalities(term const &t, app_ref_vector &out); void mk_all_equalities(term const &t, app_ref_vector &out); - void display(std::ostream &out); + void display(std::ostream &out); public: term_graph(ast_manager &man); ~term_graph(); - ast_manager &get_ast_manager() const { return m;} + ast_manager& get_ast_manager() const { return m;} void add_lit(app *lit); void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(::to_app(e)); } + void add_eq(expr* a, expr* b); void reset(); void to_lits(app_ref_vector &lits, bool all_equalities = false); void to_lits(expr_ref_vector &lits, bool all_equalities = false); app_ref to_app(); + + /** + * Return literals obtained by projecting added literals + * onto the vocabulary of decls (if exclude is false) or outside the + * vocabulary of decls (if exclude is true). + */ + expr_ref_vector project(func_decl_ref_vector const& decls, bool exclude); }; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index b60a9db71..bc5814186 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -627,7 +627,7 @@ namespace smt { */ void context::remove_parents_from_cg_table(enode * r1) { // Remove parents from the congruence table - for (enode * parent : r1->get_parents()) { + for (enode * parent : enode::parents(r1)) { #if 0 { static unsigned num_eqs = 0; @@ -672,7 +672,7 @@ namespace smt { */ void context::reinsert_parents_into_cg_table(enode * r1, enode * r2, enode * n1, enode * n2, eq_justification js) { enode_vector & r2_parents = r2->m_parents; - for (enode * parent : r1->get_parents()) { + for (enode * parent : enode::parents(r1)) { if (!parent->is_marked()) continue; parent->unset_mark(); @@ -1002,7 +1002,7 @@ namespace smt { r2->m_parents.shrink(r2_num_parents); // try to reinsert parents of r1 that are not cgr - for (enode * parent : r1->get_parents()) { + for (enode * parent : enode::parents(r1)) { TRACE("add_eq_parents", tout << "visiting: #" << parent->get_owner_id() << "\n";); if (parent->is_cgc_enabled()) { enode * cg = parent->m_cg; @@ -1197,7 +1197,7 @@ namespace smt { bool context::is_diseq_slow(enode * n1, enode * n2) const { if (n1->get_num_parents() > n2->get_num_parents()) std::swap(n1, n2); - for (enode * parent : n1->get_parents()) { + for (enode * parent : enode::parents(n1)) { if (parent->is_eq() && is_relevant(parent->get_owner()) && get_assignment(enode2bool_var(parent)) == l_false && ((parent->get_arg(0)->get_root() == n1->get_root() && parent->get_arg(1)->get_root() == n2->get_root()) || (parent->get_arg(1)->get_root() == n1->get_root() && parent->get_arg(0)->get_root() == n2->get_root()))) { @@ -1229,7 +1229,7 @@ namespace smt { return false; if (r1->get_num_parents() < SMALL_NUM_PARENTS) { TRACE("is_ext_diseq", tout << mk_bounded_pp(n1->get_owner(), m_manager) << " " << mk_bounded_pp(n2->get_owner(), m_manager) << " " << depth << "\n";); - for (enode* p1 : r1->get_parents()) { + for (enode * p1 : enode::parents(r1)) { if (!is_relevant(p1)) continue; if (p1->is_eq()) @@ -1239,7 +1239,7 @@ namespace smt { func_decl * f = p1->get_decl(); TRACE("is_ext_diseq", tout << "p1: " << mk_bounded_pp(p1->get_owner(), m_manager) << "\n";); unsigned num_args = p1->get_num_args(); - for (enode * p2 : r2->get_parents()) { + for (enode * p2 : enode::parents(r2)) { if (!is_relevant(p2)) continue; if (p2->is_eq()) @@ -1277,7 +1277,7 @@ namespace smt { } almost_cg_table & table = *(m_almost_cg_tables[depth]); table.reset(r1, r2); - for (enode* p1 : r1->get_parents()) { + for (enode * p1 : enode::parents(r1)) { if (!is_relevant(p1)) continue; if (p1->is_eq()) @@ -1288,7 +1288,7 @@ namespace smt { } if (table.empty()) return false; - for (enode * p2 : r2->get_parents()) { + for (enode * p2 : enode::parents(r2)) { if (!is_relevant(p2)) continue; if (p2->is_eq()) @@ -4285,7 +4285,7 @@ namespace smt { theory_var_list * l = n->get_th_var_list(); theory_id th_id = l->get_th_id(); - for (enode* parent : n->get_parents()) { + for (enode * parent : enode::parents(n)) { family_id fid = parent->get_owner()->get_family_id(); if (fid != th_id && fid != m_manager.get_basic_family_id()) { TRACE("is_shared", tout << mk_pp(n->get_owner(), m_manager) << "\nis shared because of:\n" << mk_pp(parent->get_owner(), m_manager) << "\n";); From 362d9258a497596a6534363400da513821b7d822 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 15:24:06 -0700 Subject: [PATCH 265/364] prepare term-graph for cc Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbi.cpp | 2 +- src/qe/qe_term_graph.cpp | 65 ++++++++++++++++++++++++++++++++++------ src/qe/qe_term_graph.h | 20 ++++++------- 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index eea3087e2..c6927f7d2 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -50,7 +50,7 @@ namespace qe { lits.push_back(m.mk_const(c)); } else if (m.is_false(mdl->get_const_interp(c))) { - lits.push_back(m.mk_const(c)); + lits.push_back(m.mk_not(m.mk_const(c))); } } } diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 73412c50b..5ef543868 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -18,6 +18,7 @@ Notes: --*/ #include "util/util.h" +#include "util/uint_set.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/for_each_expr.h" @@ -344,7 +345,7 @@ void term_graph::add_lit(app *l) { internalize_lit(lit); } -bool term_graph::is_internalized(app *a) { +bool term_graph::is_internalized(expr *a) { return m_app2term.contains(a->get_id()); } @@ -365,14 +366,24 @@ term *term_graph::mk_term(expr *a) { } term *term_graph::internalize_term(expr *t) { - term *res = get_term(t); - - if (!res) { + ptr_buffer todo; + todo.push_back(t); + term* res = nullptr; + while (!todo.empty()) { + term* res = get_term(t); + if (res) { + todo.pop_back(); + continue; + } + unsigned sz = todo.size(); if (is_app(t)) { for (expr * arg : *::to_app(t)) { - internalize_term(arg); + if (!get_term(arg)) + todo.push_back(arg); } } + if (sz < todo.size()) continue; + todo.pop_back(); res = mk_term(t); } return res; @@ -384,7 +395,7 @@ void term_graph::internalize_eq(expr *a1, expr* a2) { merge(get_term(a1)->get_root(), get_term(a2)->get_root()); } -void term_graph::internalize_lit(app* lit) { +void term_graph::internalize_lit(expr* lit) { expr *e1 = nullptr, *e2 = nullptr; if (m.is_eq (lit, e1, e2)) { internalize_eq (e1, e2); @@ -590,15 +601,51 @@ void term_graph::reset() { } expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { - obj_hashtable _decls; - for (func_decl* f : decls) _decls.insert(f); + uint_set _decls; + for (func_decl* f : decls) _decls.insert(f->get_id()); // . propagate representatives up over parents. // use work-list + marking to propagate. // . produce equalities over represented classes. // . produce other literals over represented classes // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) - + ptr_vector worklist(m_terms); + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + if (t->get_root().is_marked()) continue; + // if exclude = true, but t in decls, then skip + // if exclude = false, but t not in decls, then skip + if (exclude != _decls.contains(t->get_decl_id())) { + continue; + } + // + // if all children roots are marked + // then mark this as well, reorganize root + // and add parents to worklist + // + bool all_marked = true; + for (term* ch : term::children(t)) { + all_marked &= ch->get_root().is_marked(); + } + if (!all_marked) continue; + + // make this the new root. + term* r = t; + do { + r->set_root(*t); + for (term* p : term::parents(r)) { + worklist.push_back(p); + } + r = &r->get_next(); + } + while (t != r); + t->set_mark(true); + } + // Now, marked roots in m_terms can be used in projection + expr_ref_vector result(m); + + reset_marks(); NOT_IMPLEMENTED_YET(); return result; } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 0879fb386..5319c410a 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -39,11 +39,11 @@ namespace qe { }; class term_graph { - ast_manager & m; - ptr_vector m_terms; - app_ref_vector m_lits; - u_map m_app2term; - app_ref_vector m_pinned; + ast_manager & m; + ptr_vector m_terms; + app_ref_vector m_lits; // NSB: expr_ref_vector? + u_map m_app2term; + ast_ref_vector m_pinned; u_map m_term2app; plugin_manager m_plugins; @@ -55,9 +55,9 @@ namespace qe { term *internalize_term(expr *t); void internalize_eq(expr *a1, expr *a2); - void internalize_lit(app *lit); + void internalize_lit(expr *lit); - bool is_internalized(app *a); + bool is_internalized(expr *a); bool term_le(term const &t1, term const &t2); void pick_root (term &t); @@ -77,16 +77,16 @@ namespace qe { ast_manager& get_ast_manager() const { return m;} - void add_lit(app *lit); + void add_lit(app *lit); // NSB: replace by expr* void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(::to_app(e)); } void add_eq(expr* a, expr* b); void reset(); - void to_lits(app_ref_vector &lits, bool all_equalities = false); + void to_lits(app_ref_vector &lits, bool all_equalities = false); // NSB: swap roles void to_lits(expr_ref_vector &lits, bool all_equalities = false); - app_ref to_app(); + app_ref to_app(); /** * Return literals obtained by projecting added literals From da18f0e0b7458f68504b200111b8f09933b7dfe4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 17:59:49 -0700 Subject: [PATCH 266/364] prepare term-graph unit testing Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 37 ++++++++++++ src/qe/qe_term_graph.cpp | 76 +++++++++++++++++++++---- src/qe/qe_term_graph.h | 3 +- 3 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 3a2f6fc85..534e3740b 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -32,6 +32,7 @@ Notes: #include "util/gparams.h" #include "qe/qe_mbp.h" #include "qe/qe_mbi.h" +#include "qe/qe_term_graph.h" BINARY_SYM_CMD(get_quantifier_body_cmd, @@ -440,6 +441,42 @@ public: }; +class euf_project_cmd : public cmd { + unsigned m_arg_index; + ptr_vector m_lits; + ptr_vector m_vars; +public: + euf_project_cmd():cmd("euf-project") {} + char const * get_usage() const override { return "(exprs) (vars)"; } + char const * get_descr(cmd_context & ctx) const override { return "perform congruence projection"; } + unsigned get_arity() const override { return 2; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { + if (m_arg_index == 0) return CPK_EXPR_LIST; + return CPK_FUNC_DECL_LIST; + } + void set_next_arg(cmd_context& ctx, unsigned num, expr * const* args) override { + m_lits.append(num, args); + m_arg_index = 1; + } + void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context & ctx) override { m_arg_index = 0; m_lits.reset(); m_vars.reset(); } + void execute(cmd_context & ctx) override { + ast_manager& m = ctx.m(); + func_decl_ref_vector vars(m); + expr_ref_vector lits(m); + for (func_decl* v : m_vars) vars.push_back(v); + for (expr* e : m_lits) lits.push_back(e); + qe::term_graph tg(m); + tg.add_lits(lits); + expr_ref_vector p = tg.project(vars, false); + ctx.regular_stream() << p << "\n"; + } + +}; + + void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(print_dimacs_cmd)); ctx.insert(alloc(get_quantifier_body_cmd)); diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 5ef543868..03d2f082e 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -365,10 +365,12 @@ term *term_graph::mk_term(expr *a) { return t; } -term *term_graph::internalize_term(expr *t) { +term* term_graph::internalize_term(expr *t) { + + term* res = get_term(t); + if (res) return res; ptr_buffer todo; todo.push_back(t); - term* res = nullptr; while (!todo.empty()) { term* res = get_term(t); if (res) { @@ -390,9 +392,7 @@ term *term_graph::internalize_term(expr *t) { } void term_graph::internalize_eq(expr *a1, expr* a2) { - internalize_term(a1); - internalize_term(a2); - merge(get_term(a1)->get_root(), get_term(a2)->get_root()); + merge(internalize_term(a1)->get_root(), internalize_term(a2)->get_root()); } void term_graph::internalize_lit(expr* lit) { @@ -600,9 +600,25 @@ void term_graph::reset() { m_lits.reset(); } +expr_ref term_graph::mk_pure(term& t) { + expr* e = t.get_app(); + if (m_term2app.find(t.get_id(), e)) return expr_ref(e, m); + if (!is_app(e)) return expr_ref(m); + app* a = ::to_app(e); + expr_ref_vector kids(m); + for (term* ch : term::children(t)) { + if (!ch->get_root().is_marked()) return expr_ref(m); + kids.push_back(mk_pure(ch->get_root())); + } + expr_ref result(m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()), m); + m_pinned.push_back(result); + m_term2app.insert(t.get_id(), result); + return result; +} + expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { - uint_set _decls; - for (func_decl* f : decls) _decls.insert(f->get_id()); + u_map _decls; + for (func_decl* f : decls) _decls.insert(f->get_id(), true); // . propagate representatives up over parents. // use work-list + marking to propagate. // . produce equalities over represented classes. @@ -632,7 +648,8 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl // make this the new root. term* r = t; do { - r->set_root(*t); + r->set_root(*t); // TBD: invalidates hash-table, only one-shot + // TBD: optimize worklist traversal? for (term* p : term::parents(r)) { worklist.push_back(p); } @@ -641,12 +658,47 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl while (t != r); t->set_mark(true); } - // Now, marked roots in m_terms can be used in projection - + // marked roots in m_terms can be used in projection + // walk each root. Then traverse each term in the equivalence class + // create pure variant of the terms (if possible) + // equate t0 (that comes from the root, which can be purified) + // with any other t1. expr_ref_vector result(m); - + m_term2app.reset(); + m_pinned.reset(); + for (term * t : m_terms) { + if (!t->is_root() || !t->is_marked() || t->get_class_size() == 1) continue; + term* r = t; + expr_ref t0 = mk_pure(*t); + SASSERT(t0); + obj_hashtable roots; + roots.insert(t0); + for (term* r = &t->get_next(); r != t; r = &r->get_next()) { + // main symbol of term must be consistent with what is included/excluded + if (exclude != _decls.contains(r->get_decl_id())) { + continue; + } + expr_ref t1 = mk_pure(*r); + if (t1 && !roots.contains(t1)) { + result.push_back(m.mk_eq(t0, t1)); + roots.insert(t1); + } + } + } + // walk disequalities and expose projected disequality + for (expr* e : m_lits) { + expr* e1 = nullptr, *e2 = nullptr; + if (m.is_not(e, e) && m.is_eq(e, e1, e2)) { + expr_ref t1 = mk_pure(*get_term(e1)); + expr_ref t2 = mk_pure(*get_term(e2)); + if (t1 && t2) { + result.push_back(m.mk_not(m.mk_eq(t1, t2))); + } + } + } reset_marks(); - NOT_IMPLEMENTED_YET(); + m_term2app.reset(); + m_pinned.reset(); return result; } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 5319c410a..8e8f54c8c 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -67,12 +67,13 @@ namespace qe { expr* mk_app_core(expr* a); expr_ref mk_app(term const &t); + expr_ref mk_pure(term& t); expr_ref mk_app(expr *a); void mk_equalities(term const &t, app_ref_vector &out); void mk_all_equalities(term const &t, app_ref_vector &out); void display(std::ostream &out); public: - term_graph(ast_manager &man); + term_graph(ast_manager &m); ~term_graph(); ast_manager& get_ast_manager() const { return m;} From 008f003aa0e575fefa42ad8dd20f11aecf6b12b3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 20:58:41 -0700 Subject: [PATCH 267/364] initial working version Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 1 + src/qe/qe_term_graph.cpp | 832 ++++++++++++------------ src/qe/qe_term_graph.h | 9 +- 3 files changed, 438 insertions(+), 404 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 534e3740b..bedbf84aa 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -503,4 +503,5 @@ void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(set_next_id)); ctx.insert(alloc(mbp_cmd)); ctx.insert(alloc(mbi_cmd)); + ctx.insert(alloc(euf_project_cmd)); } diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 03d2f082e..5e8c22919 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -26,83 +26,80 @@ Notes: namespace qe { -class term { - // -- an app represented by this term - expr* m_app; // NSB: to make usable with exprs - // -- root of the equivalence class - term* m_root; - // -- next element in the equivalence class (cyclic linked list) - term* m_next; - // -- eq class size - unsigned m_class_size; - - // -- general purpose mark - unsigned m_mark:1; - // -- general purpose second mark - unsigned m_mark2:1; - // -- is an interpreted constant - unsigned m_interpreted:1; - - // -- terms that contain this term as a child - ptr_vector m_parents; - - // arguments of term. - ptr_vector m_children; - -public: - term(expr* a, u_map& app2term) : - m_app(a), - m_root(this), - m_next(this), - m_class_size(1), - m_mark(false), - m_mark2(false), - m_interpreted(false) { - if (!is_app(a)) return; - for (expr* e : *to_app(a)) { - term* t = app2term[e->get_id()]; - t->m_parents.push_back(this); - m_children.push_back(t); + class term { + // -- an app represented by this term + expr* m_app; // NSB: to make usable with exprs + // -- root of the equivalence class + term* m_root; + // -- next element in the equivalence class (cyclic linked list) + term* m_next; + // -- eq class size + unsigned m_class_size; + + // -- general purpose mark + unsigned m_mark:1; + // -- general purpose second mark + unsigned m_mark2:1; + // -- is an interpreted constant + unsigned m_interpreted:1; + + // -- terms that contain this term as a child + ptr_vector m_parents; + + // arguments of term. + ptr_vector m_children; + + public: + term(expr* a, u_map& app2term) : + m_app(a), + m_root(this), + m_next(this), + m_class_size(1), + m_mark(false), + m_mark2(false), + m_interpreted(false) { + if (!is_app(a)) return; + for (expr* e : *to_app(a)) { + term* t = app2term[e->get_id()]; + t->m_parents.push_back(this); + m_children.push_back(t); + } } - } - - ~term() {} - - class parents { + + ~term() {} + + class parents { + term const& t; + public: + parents(term const& _t):t(_t) {} + parents(term const* _t):t(*_t) {} + ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } + ptr_vector::const_iterator end() const { return t.m_parents.end(); } + }; + + class children { term const& t; - public: - parents(term const& _t):t(_t) {} - parents(term const* _t):t(*_t) {} - ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } - ptr_vector::const_iterator end() const { return t.m_parents.end(); } - }; - - class children { - term const& t; - public: - children(term const& _t):t(_t) {} - children(term const* _t):t(*_t) {} - ptr_vector::const_iterator begin() const { return t.m_children.begin(); } - ptr_vector::const_iterator end() const { return t.m_children.end(); } - }; - - // Congruence table hash function is based on - // roots of children and function declaration. - - struct cg_hash { - unsigned operator()(term const* t) const { + public: + children(term const& _t):t(_t) {} + children(term const* _t):t(*_t) {} + ptr_vector::const_iterator begin() const { return t.m_children.begin(); } + ptr_vector::const_iterator end() const { return t.m_children.end(); } + }; + + // Congruence table hash function is based on + // roots of children and function declaration. + + unsigned get_hash() const { unsigned a, b, c; - a = b = c = t->get_decl_id(); - for (term * ch : children(t)) { + a = b = c = get_decl_id(); + for (term * ch : children(this)) { a = ch->get_root().get_id(); mix(a, b, c); } return c; } - }; - - struct cg_eq { - bool operator()(term * t1, term * t2) const { + + static bool cg_eq(term const * t1, term const * t2) { if (t1->get_decl_id() != t2->get_decl_id()) return false; if (t1->m_children.size() != t2->m_children.size()) return false; for (unsigned i = 0, sz = t1->m_children.size(); i < sz; ++ i) { @@ -110,348 +107,378 @@ public: } return true; } + + unsigned get_id() const { return m_app->get_id();} + + unsigned get_decl_id() const { return is_app(m_app) ? to_app(m_app)->get_decl()->get_id() : m_app->get_id(); } + + bool is_marked() const {return m_mark;} + void set_mark(bool v){m_mark = v;} + bool is_marked2() const {return m_mark2;} // NSB: where is this used? + void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? + + bool is_interpreted() const {return m_interpreted;} + void mark_as_interpreted() {m_interpreted=true;} + expr* get_app() const {return m_app;} + unsigned get_num_args() const { return is_app(m_app) ? to_app(m_app)->get_num_args() : 0; } + + term &get_root() const {return *m_root;} + bool is_root() const {return m_root == this;} + void set_root(term &r) {m_root = &r;} + term &get_next() const {return *m_next;} + void add_parent(term* p) { m_parents.push_back(p); } + + unsigned get_class_size() const {return m_class_size;} + + void merge_eq_class(term &b) { + std::swap(this->m_next, b.m_next); + m_class_size += b.get_class_size(); + // -- reset (useful for debugging) + b.m_class_size = 0; + } + + // -- make this term the root of its equivalence class + void mk_root() { + if (is_root()) return; + + term *curr = this; + do { + if (curr->is_root()) { + // found previous root + SASSERT(curr != this); + m_class_size = curr->get_class_size(); + curr->m_class_size = 0; + } + curr->set_root(*this); + curr = &curr->get_next(); + } + while (curr != this); + } + }; + + + + class arith_term_graph_plugin : public term_graph_plugin { + term_graph &m_g; + ast_manager &m; + arith_util m_arith; + + public: + arith_term_graph_plugin(term_graph &g) : + term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), + m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} + + virtual ~arith_term_graph_plugin() {} + + bool mk_eq_core (expr *_e1, expr *_e2, app_ref &res) { + expr *e1, *e2; + e1 = _e1; + e2 = _e2; + + if (m_arith.is_zero(e1)) { + std::swap(e1, e2); + } + // y + -1*x == 0 --> y = x + expr *a0 = 0, *a1 = 0, *x = 0; + if (m_arith.is_zero(e2) && m_arith.is_add(e1, a0, a1)) { + if (m_arith.is_times_minus_one(a1, x)) { + e1 = a0; + e2 = x; + } + else if (m_arith.is_times_minus_one(a0, x)) { + e1 = a1; + e2 = x; + } + } + res = m.mk_eq(e1, e2); + return true; + } + + app* mk_le_zero(expr *arg) { + expr *e1, *e2, *e3; + // XXX currently disabled + if (m_arith.is_add(arg, e1, e2)) { + // e1-e2<=0 --> e1<=e2 + if (m_arith.is_times_minus_one(e2, e3)) { + return m_arith.mk_le(e1, e3); + } + // -e1+e2<=0 --> e2<=e1 + else if (m_arith.is_times_minus_one(e1, e3)) { + return m_arith.mk_le(e2, e3); + } + } + return m_arith.mk_le(arg, mk_zero()); + } + + app* mk_ge_zero(expr *arg) { + expr *e1, *e2, *e3; + // XXX currently disabled + if (m_arith.is_add(arg, e1, e2)) { + // e1-e2>=0 --> e1>=e2 + if (m_arith.is_times_minus_one(e2, e3)) { + return m_arith.mk_ge(e1, e3); + } + // -e1+e2>=0 --> e2>=e1 + else if (m_arith.is_times_minus_one(e1, e3)) { + return m_arith.mk_ge(e2, e3); + } + } + return m_arith.mk_ge(arg, mk_zero()); + } + + bool mk_le_core (expr *arg1, expr * arg2, app_ref &result) { + // t <= -1 ==> t < 0 ==> ! (t >= 0) + rational n; + if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { + result = m.mk_not (mk_ge_zero (arg1)); + return true; + } + else if (m_arith.is_zero(arg2)) { + result = mk_le_zero(arg1); + return true; + } + else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n < 0) { + // t <= n ==> t < n + 1 ==> ! (t >= n + 1) + result = m.mk_not(m_arith.mk_ge(arg1, m_arith.mk_numeral(n+1, true))); + return true; + } + return false; + } + + expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} + bool is_one (expr const * n) const { + rational val; + return m_arith.is_numeral (n, val) && val.is_one (); + } + + bool mk_ge_core (expr * arg1, expr * arg2, app_ref &result) { + // t >= 1 ==> t > 0 ==> ! (t <= 0) + rational n; + if (m_arith.is_int (arg1) && is_one (arg2)) { + result = m.mk_not (mk_le_zero (arg1)); + return true; + } + else if (m_arith.is_zero(arg2)) { + result = mk_ge_zero(arg1); + return true; + } + else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n > 0) { + // t >= n ==> t > n - 1 ==> ! (t <= n - 1) + result = m.mk_not(m_arith.mk_le(arg1, m_arith.mk_numeral(n-1, true))); + return true; + } + return false; + } + + virtual app_ref process_lit (app *_lit) { + app *lit = _lit; + expr *e1, *e2; + + // strip negation + bool is_neg = m.is_not(lit); + if (is_neg) { + lit = to_app(to_app(lit)->get_arg(0)); + } + + app_ref res(m); + res = lit; + if (m.is_eq (lit, e1, e2)) { + mk_eq_core(e1, e2, res); + } + else if (m_arith.is_le(lit, e1, e2)) { + mk_le_core(e1, e2, res); + } + else if (m_arith.is_ge(lit, e1, e2)) { + mk_ge_core(e1, e2, res); + } + + // restore negation + if (is_neg) { + res = m.mk_not(res); + } + + return res; + } }; - unsigned get_id() const { return m_app->get_id();} + unsigned term_graph::term_hash::operator()(term const* t) const { return t->get_hash(); } - unsigned get_decl_id() const { return is_app(m_app) ? to_app(m_app)->get_decl()->get_id() : m_app->get_id(); } + bool term_graph::term_eq::operator()(term const* a, term const* b) const { return term::cg_eq(a, b); } - bool is_marked() const {return m_mark;} - void set_mark(bool v){m_mark = v;} - bool is_marked2() const {return m_mark2;} // NSB: where is this used? - void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? - - bool is_interpreted() const {return m_interpreted;} - void mark_as_interpreted() {m_interpreted=true;} - expr* get_app() const {return m_app;} - unsigned get_num_args() const { return is_app(m_app) ? to_app(m_app)->get_num_args() : 0; } - - term &get_root() const {return *m_root;} - bool is_root() const {return m_root == this;} - void set_root(term &r) {m_root = &r;} - term &get_next() const {return *m_next;} - - unsigned get_class_size() const {return m_class_size;} - - void merge_eq_class(term &b) { - std::swap(this->m_next, b.m_next); - m_class_size += b.get_class_size(); - // -- reset (useful for debugging) - b.m_class_size = 0; + term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { + m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); } - - // -- make this term the root of its equivalence class - void mk_root() { - if (is_root()) return; - - term *curr = this; - do { - if (curr->is_root()) { - // found previous root - SASSERT(curr != this); - m_class_size = curr->get_class_size(); - curr->m_class_size = 0; - } - curr->set_root(*this); - curr = &curr->get_next(); - } - while (curr != this); - } -}; - - - -class arith_term_graph_plugin : public term_graph_plugin { - term_graph &m_g; - ast_manager &m; - arith_util m_arith; - -public: - arith_term_graph_plugin(term_graph &g) : - term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), - m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} - - virtual ~arith_term_graph_plugin() {} - - bool mk_eq_core (expr *_e1, expr *_e2, app_ref &res) { - expr *e1, *e2; - e1 = _e1; - e2 = _e2; - - if (m_arith.is_zero(e1)) { - std::swap(e1, e2); - } - // y + -1*x == 0 --> y = x - expr *a0 = 0, *a1 = 0, *x = 0; - if (m_arith.is_zero(e2) && m_arith.is_add(e1, a0, a1)) { - if (m_arith.is_times_minus_one(a1, x)) { - e1 = a0; - e2 = x; - } - else if (m_arith.is_times_minus_one(a0, x)) { - e1 = a1; - e2 = x; - } - } - res = m.mk_eq(e1, e2); - return true; - } - - app* mk_le_zero(expr *arg) { - expr *e1, *e2, *e3; - // XXX currently disabled - if (m_arith.is_add(arg, e1, e2)) { - // e1-e2<=0 --> e1<=e2 - if (m_arith.is_times_minus_one(e2, e3)) { - return m_arith.mk_le(e1, e3); - } - // -e1+e2<=0 --> e2<=e1 - else if (m_arith.is_times_minus_one(e1, e3)) { - return m_arith.mk_le(e2, e3); - } - } - return m_arith.mk_le(arg, mk_zero()); - } - - app* mk_ge_zero(expr *arg) { - expr *e1, *e2, *e3; - // XXX currently disabled - if (m_arith.is_add(arg, e1, e2)) { - // e1-e2>=0 --> e1>=e2 - if (m_arith.is_times_minus_one(e2, e3)) { - return m_arith.mk_ge(e1, e3); - } - // -e1+e2>=0 --> e2>=e1 - else if (m_arith.is_times_minus_one(e1, e3)) { - return m_arith.mk_ge(e2, e3); - } - } - return m_arith.mk_ge(arg, mk_zero()); - } - - bool mk_le_core (expr *arg1, expr * arg2, app_ref &result) { - // t <= -1 ==> t < 0 ==> ! (t >= 0) - rational n; - if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { - result = m.mk_not (mk_ge_zero (arg1)); - return true; - } - else if (m_arith.is_zero(arg2)) { - result = mk_le_zero(arg1); - return true; - } - else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n < 0) { - // t <= n ==> t < n + 1 ==> ! (t >= n + 1) - result = m.mk_not(m_arith.mk_ge(arg1, m_arith.mk_numeral(n+1, true))); - return true; - } - return false; - } - - expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} - bool is_one (expr const * n) const { - rational val; - return m_arith.is_numeral (n, val) && val.is_one (); - } - - bool mk_ge_core (expr * arg1, expr * arg2, app_ref &result) { - // t >= 1 ==> t > 0 ==> ! (t <= 0) - rational n; - if (m_arith.is_int (arg1) && is_one (arg2)) { - result = m.mk_not (mk_le_zero (arg1)); - return true; - } - else if (m_arith.is_zero(arg2)) { - result = mk_ge_zero(arg1); - return true; - } - else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n > 0) { - // t >= n ==> t > n - 1 ==> ! (t <= n - 1) - result = m.mk_not(m_arith.mk_le(arg1, m_arith.mk_numeral(n-1, true))); - return true; - } - return false; - } - - virtual app_ref process_lit (app *_lit) { - app *lit = _lit; - expr *e1, *e2; - - // strip negation - bool is_neg = m.is_not(lit); - if (is_neg) { - lit = to_app(to_app(lit)->get_arg(0)); - } - - app_ref res(m); - res = lit; - if (m.is_eq (lit, e1, e2)) { - mk_eq_core(e1, e2, res); - } - else if (m_arith.is_le(lit, e1, e2)) { - mk_le_core(e1, e2, res); - } - else if (m_arith.is_ge(lit, e1, e2)) { - mk_ge_core(e1, e2, res); - } - - // restore negation - if (is_neg) { - res = m.mk_not(res); - } - - return res; - } -}; - -term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { - m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); -} - -term_graph::~term_graph() { - reset(); -} - -static family_id get_family_id(ast_manager &m, app *lit) { - family_id fid = null_family_id; - - expr *e1 = nullptr, *e2 = nullptr, *e3 = nullptr; - // strip negation - if (!m.is_not (lit, e1)) { e1 = lit; } - - // deal with equality using sort of range - if (m.is_eq (e1, e2, e3)) { - fid = get_sort (e2)->get_family_id(); - } - // extract family_id of top level app - else { - fid = to_app(e1)->get_decl()->get_family_id(); - } - - return fid; -} - -void term_graph::add_lit(app *l) { - app_ref lit(m); - - family_id fid = get_family_id (m, l); - term_graph_plugin *pin = m_plugins.get_plugin(fid); - if (pin) { - lit = pin->process_lit(l); - } else { - lit = l; - } - m_lits.push_back(lit); - internalize_lit(lit); -} - -bool term_graph::is_internalized(expr *a) { - return m_app2term.contains(a->get_id()); -} - -term* term_graph::get_term(expr *a) { - term *res; - return m_app2term.find (a->get_id(), res) ? res : nullptr; -} - -term *term_graph::mk_term(expr *a) { - term * t = alloc(term, a, m_app2term); - if (t->get_num_args() == 0 && m.is_unique_value(a)){ - t->mark_as_interpreted(); - } - - m_terms.push_back(t); - m_app2term.insert(a->get_id(), t); - return t; -} - -term* term_graph::internalize_term(expr *t) { - term* res = get_term(t); - if (res) return res; - ptr_buffer todo; - todo.push_back(t); - while (!todo.empty()) { + term_graph::~term_graph() { + reset(); + } + + static family_id get_family_id(ast_manager &m, app *lit) { + family_id fid = null_family_id; + + expr *e1 = nullptr, *e2 = nullptr, *e3 = nullptr; + // strip negation + if (!m.is_not (lit, e1)) { e1 = lit; } + + // deal with equality using sort of range + if (m.is_eq (e1, e2, e3)) { + fid = get_sort (e2)->get_family_id(); + } + // extract family_id of top level app + else { + fid = to_app(e1)->get_decl()->get_family_id(); + } + + return fid; + } + + void term_graph::add_lit(app *l) { + app_ref lit(m); + + family_id fid = get_family_id (m, l); + term_graph_plugin *pin = m_plugins.get_plugin(fid); + if (pin) { + lit = pin->process_lit(l); + } else { + lit = l; + } + m_lits.push_back(lit); + internalize_lit(lit); + } + + bool term_graph::is_internalized(expr *a) { + return m_app2term.contains(a->get_id()); + } + + term* term_graph::get_term(expr *a) { + term *res; + return m_app2term.find (a->get_id(), res) ? res : nullptr; + } + + term *term_graph::mk_term(expr *a) { + term * t = alloc(term, a, m_app2term); + if (t->get_num_args() == 0 && m.is_unique_value(a)){ + t->mark_as_interpreted(); + } + + m_terms.push_back(t); + m_app2term.insert(a->get_id(), t); + return t; + } + + term* term_graph::internalize_term(expr *t) { term* res = get_term(t); - if (res) { - todo.pop_back(); - continue; - } - unsigned sz = todo.size(); - if (is_app(t)) { - for (expr * arg : *::to_app(t)) { - if (!get_term(arg)) - todo.push_back(arg); + if (res) return res; + ptr_buffer todo; + todo.push_back(t); + while (!todo.empty()) { + res = get_term(t); + if (res) { + todo.pop_back(); + continue; } + unsigned sz = todo.size(); + if (is_app(t)) { + for (expr * arg : *::to_app(t)) { + if (!get_term(arg)) + todo.push_back(arg); + } + } + if (sz < todo.size()) continue; + todo.pop_back(); + res = mk_term(t); } - if (sz < todo.size()) continue; - todo.pop_back(); - res = mk_term(t); - } - return res; -} - -void term_graph::internalize_eq(expr *a1, expr* a2) { - merge(internalize_term(a1)->get_root(), internalize_term(a2)->get_root()); -} - -void term_graph::internalize_lit(expr* lit) { - expr *e1 = nullptr, *e2 = nullptr; - if (m.is_eq (lit, e1, e2)) { - internalize_eq (e1, e2); - } - else { - internalize_term(lit); - } -} - -void term_graph::merge (term &t1, term &t2) { - SASSERT(t1.is_root()); - SASSERT(t2.is_root()); - - if (&t1 == &t2) return; - - term *a = &t1; - term *b = &t2; - if (a->get_class_size() > b->get_class_size()) { - std::swap(a, b); - } - - // make 'a' be the root of the equivalence class of 'b' - b->set_root(*a); - for (term *it = &b->get_next(); it != b; it = &it->get_next()) { - // TBD: remove parents of it from the cg table. - it->set_root(*a); - } - - // merge equivalence classes - a->merge_eq_class(*b); - - // TBD: insert parents of b's old equilvalence class into the cg table - // and propagate equalities. - - // -- merge might have invalidated term2map cache - - // NSB: ??? what is ownership model of pinned in m_terms? - m_term2app.reset(); - m_pinned.reset(); -} - -expr* term_graph::mk_app_core (expr *e) { - if (is_app(e)) { - expr_ref_vector kids(m); - app* a = ::to_app(e); - for (expr * arg : *a) { - kids.push_back (mk_app(arg)); - } - app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); - m_pinned.push_back(res); + SASSERT(res); return res; } - else { - return e; + + void term_graph::internalize_eq(expr *a1, expr* a2) { + SASSERT(m_merge.empty()); + merge(internalize_term(a1)->get_root(), internalize_term(a2)->get_root()); + merge_flush(); + SASSERT(m_merge.empty()); + } + + void term_graph::internalize_lit(expr* lit) { + expr *e1 = nullptr, *e2 = nullptr; + if (m.is_eq (lit, e1, e2)) { + internalize_eq (e1, e2); + } + else { + internalize_term(lit); + } + } + + void term_graph::merge_flush() { + while (!m_merge.empty()) { + term* t1 = m_merge.back().first; + term* t2 = m_merge.back().second; + m_merge.pop_back(); + merge(*t1, *t2); + } + } + + void term_graph::merge(term &t1, term &t2) { + // -- merge might invalidate term2map cache + m_term2app.reset(); + m_pinned.reset(); + + SASSERT(t1.is_root()); + SASSERT(t2.is_root()); + + if (&t1 == &t2) return; + + term *a = &t1; + term *b = &t2; + if (a->get_class_size() > b->get_class_size()) { + std::swap(a, b); + } + + // Remove parents of it from the cg table. + for (term* p : term::parents(b)) { + if (!p->is_marked()) { + p->set_mark(true); + m_cg_table.erase(p); + } + } + // make 'a' be the root of the equivalence class of 'b' + b->set_root(*a); + for (term *it = &b->get_next(); it != b; it = &it->get_next()) { + it->set_root(*a); + } + + // merge equivalence classes + a->merge_eq_class(*b); + + // Insert parents of b's old equilvalence class into the cg table + for (term* p : term::parents(a)) { + if (p->is_marked()) { + term* p_old = m_cg_table.insert_if_not_there(p); + p->set_mark(false); + a->add_parent(p); + // propagate new equalities. + if (p->get_root().get_id() != p_old->get_root().get_id()) { + m_merge.push_back(std::make_pair(p, p_old)); + } + } + } + } + + expr* term_graph::mk_app_core (expr *e) { + if (is_app(e)) { + expr_ref_vector kids(m); + app* a = ::to_app(e); + for (expr * arg : *a) { + kids.push_back (mk_app(arg)); + } + app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); + m_pinned.push_back(res); + return res; + } + else { + return e; + } } -} expr_ref term_graph::mk_app(term const &r) { SASSERT(r.is_root()); @@ -598,6 +625,7 @@ void term_graph::reset() { std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); m_terms.reset(); m_lits.reset(); + m_cg_table.reset(); } expr_ref term_graph::mk_pure(term& t) { @@ -631,7 +659,7 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl if (t->get_root().is_marked()) continue; // if exclude = true, but t in decls, then skip // if exclude = false, but t not in decls, then skip - if (exclude != _decls.contains(t->get_decl_id())) { + if (exclude == _decls.contains(t->get_decl_id())) { continue; } // @@ -662,7 +690,7 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl // walk each root. Then traverse each term in the equivalence class // create pure variant of the terms (if possible) // equate t0 (that comes from the root, which can be purified) - // with any other t1. + // with any other purifiable t1. expr_ref_vector result(m); m_term2app.reset(); m_pinned.reset(); @@ -675,7 +703,7 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl roots.insert(t0); for (term* r = &t->get_next(); r != t; r = &r->get_next()) { // main symbol of term must be consistent with what is included/excluded - if (exclude != _decls.contains(r->get_decl_id())) { + if (exclude == _decls.contains(r->get_decl_id())) { continue; } expr_ref t1 = mk_pure(*r); diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 8e8f54c8c..c3f1e21aa 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -37,18 +37,23 @@ namespace qe { /// Process (and potentially augment) a literal virtual app_ref process_lit (app *lit) = 0; }; - + + class term_graph { + struct term_hash { unsigned operator()(term const* t) const; }; + struct term_eq { bool operator()(term const* a, term const* b) const; }; ast_manager & m; ptr_vector m_terms; app_ref_vector m_lits; // NSB: expr_ref_vector? u_map m_app2term; ast_ref_vector m_pinned; u_map m_term2app; - plugin_manager m_plugins; + ptr_hashtable m_cg_table; + vector> m_merge; void merge(term &t1, term &t2); + void merge_flush(); term *mk_term(expr *t); term *get_term(expr *t); From 1f634efe0447e23a3d52997052951fcddf495da6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 21:11:55 -0700 Subject: [PATCH 268/364] initial working version Signed-off-by: Nikolaj Bjorner --- src/qe/qe_term_graph.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 5e8c22919..7244f4e22 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -372,6 +372,7 @@ namespace qe { ptr_buffer todo; todo.push_back(t); while (!todo.empty()) { + t = todo.back(); res = get_term(t); if (res) { todo.pop_back(); From c4188fc4be9b542339ca430b05f4d6c28bdf6073 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Jun 2018 21:27:30 -0700 Subject: [PATCH 269/364] initial working version Signed-off-by: Nikolaj Bjorner --- src/qe/qe_term_graph.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 7244f4e22..e7d13824c 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -118,6 +118,7 @@ namespace qe { void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? bool is_interpreted() const {return m_interpreted;} + bool is_theory() const { return !is_app(m_app) || to_app(m_app)->get_family_id() != null_family_id; } void mark_as_interpreted() {m_interpreted=true;} expr* get_app() const {return m_app;} unsigned get_num_args() const { return is_app(m_app) ? to_app(m_app)->get_num_args() : 0; } @@ -660,7 +661,8 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl if (t->get_root().is_marked()) continue; // if exclude = true, but t in decls, then skip // if exclude = false, but t not in decls, then skip - if (exclude == _decls.contains(t->get_decl_id())) { + + if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) { continue; } // @@ -704,7 +706,7 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl roots.insert(t0); for (term* r = &t->get_next(); r != t; r = &r->get_next()) { // main symbol of term must be consistent with what is included/excluded - if (exclude == _decls.contains(r->get_decl_id())) { + if (!r->is_theory() && exclude == _decls.contains(r->get_decl_id())) { continue; } expr_ref t1 = mk_pure(*r); @@ -716,12 +718,10 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl } // walk disequalities and expose projected disequality for (expr* e : m_lits) { - expr* e1 = nullptr, *e2 = nullptr; - if (m.is_not(e, e) && m.is_eq(e, e1, e2)) { - expr_ref t1 = mk_pure(*get_term(e1)); - expr_ref t2 = mk_pure(*get_term(e2)); - if (t1 && t2) { - result.push_back(m.mk_not(m.mk_eq(t1, t2))); + if (!m.is_eq(e)) { + expr_ref t = mk_pure(*get_term(e)); + if (t) { + result.push_back(t); } } } From a6848c79b7600525be23ac03dffb581bb82e6022 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 10 Jun 2018 05:41:15 -0700 Subject: [PATCH 270/364] redo representative generator to respect stratification Signed-off-by: Nikolaj Bjorner --- src/qe/qe_term_graph.cpp | 125 +++++++++++++++++++-------------------- src/qe/qe_term_graph.h | 2 +- 2 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index e7d13824c..d8670089a 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -630,17 +630,17 @@ void term_graph::reset() { m_cg_table.reset(); } -expr_ref term_graph::mk_pure(term& t) { +expr* term_graph::mk_pure(term& t) { expr* e = t.get_app(); - if (m_term2app.find(t.get_id(), e)) return expr_ref(e, m); - if (!is_app(e)) return expr_ref(m); + if (m_term2app.find(t.get_id(), e)) e; + if (!is_app(e)) return nullptr; app* a = ::to_app(e); expr_ref_vector kids(m); for (term* ch : term::children(t)) { - if (!ch->get_root().is_marked()) return expr_ref(m); - kids.push_back(mk_pure(ch->get_root())); + if (!m_term2app.find(ch->get_root().get_id(), e)) return nullptr; + kids.push_back(e); } - expr_ref result(m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()), m); + expr* result = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); m_pinned.push_back(result); m_term2app.insert(t.get_id(), result); return result; @@ -654,78 +654,75 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl // . produce equalities over represented classes. // . produce other literals over represented classes // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) - ptr_vector worklist(m_terms); - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); - if (t->get_root().is_marked()) continue; - // if exclude = true, but t in decls, then skip - // if exclude = false, but t not in decls, then skip - if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) { - continue; - } - // - // if all children roots are marked - // then mark this as well, reorganize root - // and add parents to worklist - // - bool all_marked = true; - for (term* ch : term::children(t)) { - all_marked &= ch->get_root().is_marked(); - } - if (!all_marked) continue; - - // make this the new root. - term* r = t; - do { - r->set_root(*t); // TBD: invalidates hash-table, only one-shot - // TBD: optimize worklist traversal? - for (term* p : term::parents(r)) { - worklist.push_back(p); - } - r = &r->get_next(); - } - while (t != r); - t->set_mark(true); - } - // marked roots in m_terms can be used in projection - // walk each root. Then traverse each term in the equivalence class - // create pure variant of the terms (if possible) - // equate t0 (that comes from the root, which can be purified) - // with any other purifiable t1. expr_ref_vector result(m); m_term2app.reset(); m_pinned.reset(); - for (term * t : m_terms) { - if (!t->is_root() || !t->is_marked() || t->get_class_size() == 1) continue; + + ptr_vector worklist(m_terms); + obj_hashtable roots; + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + if (!t->is_root() || m_term2app.contains(t->get_id())) continue; term* r = t; - expr_ref t0 = mk_pure(*t); - SASSERT(t0); - obj_hashtable roots; - roots.insert(t0); - for (term* r = &t->get_next(); r != t; r = &r->get_next()) { - // main symbol of term must be consistent with what is included/excluded + roots.reset(); + expr_ref rep(m), other(m); + // walk the equivalence class of t to produce + // a representative. + do { + // if exclude = true, but t in decls, then skip + // if exclude = false, but t not in decls, then skip if (!r->is_theory() && exclude == _decls.contains(r->get_decl_id())) { + r = &r->get_next(); continue; } - expr_ref t1 = mk_pure(*r); - if (t1 && !roots.contains(t1)) { - result.push_back(m.mk_eq(t0, t1)); - roots.insert(t1); + other = mk_pure(*r); + if (other) { + if (!rep) { + rep = other; + roots.insert(other); + } + else if (!roots.contains(other)) { + roots.insert(other); + result.push_back(m.mk_eq(rep, other)); + // give preference to non-values as roots. + if (m.is_unique_value(rep)) { + std::swap(other, rep); + } + } } + r = &r->get_next(); + } + while (r != t); + + if (rep) { + // update the representative of t to the preferred one. + // used by mk_pure to determine representative of child. + m_term2app.insert(t->get_id(), rep); + // TBD: add_parent in merge ensures that + // congruence closure root t contains all parents. + // TBD: could tune this by using marking to only add roots to worklist if not already there. + r = t; + do { + for (term * p : term::parents(r)) { + worklist.push_back(p); + } + r = &r->get_next(); + } + while (r != t); } } - // walk disequalities and expose projected disequality + // walk other predicates than equalities for (expr* e : m_lits) { - if (!m.is_eq(e)) { - expr_ref t = mk_pure(*get_term(e)); - if (t) { - result.push_back(t); - } + if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { + result.push_back(e); } } - reset_marks(); + // Here we could also walk equivalence classes that contain interpreted values by sort and + // extract disequalities bewteen non-unique value representatives. + // these disequalities are implied and can be mined using other means, such as + // theory aware core minimization m_term2app.reset(); m_pinned.reset(); return result; diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index c3f1e21aa..4f0430812 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -72,7 +72,7 @@ namespace qe { expr* mk_app_core(expr* a); expr_ref mk_app(term const &t); - expr_ref mk_pure(term& t); + expr* mk_pure(term& t); expr_ref mk_app(expr *a); void mk_equalities(term const &t, app_ref_vector &out); void mk_all_equalities(term const &t, app_ref_vector &out); From ad153592a2f298c62b3eabfffdfd6890e52dac97 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 10 Jun 2018 06:07:05 -0700 Subject: [PATCH 271/364] fix parent list Signed-off-by: Nikolaj Bjorner --- src/qe/qe_term_graph.cpp | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index d8670089a..95a024de6 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -61,7 +61,7 @@ namespace qe { if (!is_app(a)) return; for (expr* e : *to_app(a)) { term* t = app2term[e->get_id()]; - t->m_parents.push_back(this); + t->get_root().m_parents.push_back(this); m_children.push_back(t); } } @@ -659,12 +659,20 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl m_term2app.reset(); m_pinned.reset(); - ptr_vector worklist(m_terms); obj_hashtable roots; + ptr_vector worklist; + for (term * t : m_terms) { + if (t->is_root()) { + worklist.push_back(t); + t->set_mark(true); + } + } while (!worklist.empty()) { term* t = worklist.back(); worklist.pop_back(); - if (!t->is_root() || m_term2app.contains(t->get_id())) continue; + SASSERT(t->is_root()); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) continue; term* r = t; roots.reset(); expr_ref rep(m), other(m); @@ -700,17 +708,29 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl // update the representative of t to the preferred one. // used by mk_pure to determine representative of child. m_term2app.insert(t->get_id(), rep); - // TBD: add_parent in merge ensures that - // congruence closure root t contains all parents. - // TBD: could tune this by using marking to only add roots to worklist if not already there. +#if 1 + // The root should contain all parents relative to the equivalence class. + // To ensure this, merge uses add_parent on the chosen root with respect to the old root. + // To enable adding terms after merge the constructor for terms also has to ensure + // that new terms are added as parents of the roots of their children. + + for (term * p : term::parents(t)) { + p = &p->get_root(); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } + } +#else r = t; do { for (term * p : term::parents(r)) { - worklist.push_back(p); + worklist.push_back(&p->get_root()); } r = &r->get_next(); } - while (r != t); + while (r != t); +#endif } } // walk other predicates than equalities @@ -725,6 +745,7 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl // theory aware core minimization m_term2app.reset(); m_pinned.reset(); + reset_marks(); return result; } From 0d71d850691d48fdca84203e89d7bc495476bb20 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 10 Jun 2018 10:59:09 -0700 Subject: [PATCH 272/364] redo representative algorithm Signed-off-by: Nikolaj Bjorner --- src/qe/qe_term_graph.cpp | 108 ++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 95a024de6..fa91eced6 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -373,7 +373,7 @@ namespace qe { ptr_buffer todo; todo.push_back(t); while (!todo.empty()) { - t = todo.back(); + t = todo.back(); res = get_term(t); if (res) { todo.pop_back(); @@ -659,78 +659,70 @@ expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool excl m_term2app.reset(); m_pinned.reset(); - obj_hashtable roots; + obj_hashtable eqs; + expr_ref eq(m); ptr_vector worklist; for (term * t : m_terms) { - if (t->is_root()) { - worklist.push_back(t); - t->set_mark(true); - } + worklist.push_back(t); + t->set_mark(true); } + while (!worklist.empty()) { term* t = worklist.back(); worklist.pop_back(); - SASSERT(t->is_root()); t->set_mark(false); - if (m_term2app.contains(t->get_id())) continue; - term* r = t; - roots.reset(); - expr_ref rep(m), other(m); - // walk the equivalence class of t to produce - // a representative. - do { - // if exclude = true, but t in decls, then skip - // if exclude = false, but t not in decls, then skip - if (!r->is_theory() && exclude == _decls.contains(r->get_decl_id())) { - r = &r->get_next(); - continue; - } - other = mk_pure(*r); - if (other) { - if (!rep) { - rep = other; - roots.insert(other); - } - else if (!roots.contains(other)) { - roots.insert(other); - result.push_back(m.mk_eq(rep, other)); - // give preference to non-values as roots. - if (m.is_unique_value(rep)) { - std::swap(other, rep); - } - } - } - r = &r->get_next(); + if (m_term2app.contains(t->get_id())) + continue; + if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) + continue; + + term& root = t->get_root(); + bool has_rep = m_term2app.contains(root.get_id()); + expr* pure = mk_pure(*t); + if (!pure) continue; + + // ensure that the root has a representative + // either by looking up cached version, + // computing it for the first time, or + // inheriting pure. + expr* rep = nullptr; + if (root.is_theory() || exclude != _decls.contains(root.get_decl_id())) { + rep = mk_pure(root); } - while (r != t); + else if (has_rep) { + rep = m_term2app.find(root.get_id()); + } + else { + rep = pure; + m_term2app.insert(root.get_id(), pure); + } + bool update_rep = false; - if (rep) { - // update the representative of t to the preferred one. - // used by mk_pure to determine representative of child. - m_term2app.insert(t->get_id(), rep); -#if 1 - // The root should contain all parents relative to the equivalence class. - // To ensure this, merge uses add_parent on the chosen root with respect to the old root. - // To enable adding terms after merge the constructor for terms also has to ensure - // that new terms are added as parents of the roots of their children. + // Add equations between pure and rep, + // optionally swap the roles of rep and pure if + // pure makes a better representative. + if (rep != pure) { + if (m.is_unique_value(rep) && !m.is_unique_value(pure)) { + m_term2app.insert(root.get_id(), pure); + update_rep = true; + } + eq = m.mk_eq(rep, pure); + if (!eqs.contains(eq)) { + eqs.insert(eq); + result.push_back(eq); + } + } - for (term * p : term::parents(t)) { - p = &p->get_root(); + // update the worklist if this is the first + // representative or pure was swapped into rep. + if (!has_rep || update_rep) { + for (term * p : term::parents(root)) { + if (update_rep) m_term2app.remove(p->get_id()); if (!p->is_marked()) { p->set_mark(true); worklist.push_back(p); } } -#else - r = t; - do { - for (term * p : term::parents(r)) { - worklist.push_back(&p->get_root()); - } - r = &r->get_next(); - } - while (r != t); -#endif } } // walk other predicates than equalities From 9a0406d181f010b6026071f001b0dab2442b2f91 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 10 Jun 2018 11:28:38 -0700 Subject: [PATCH 273/364] replace app by expr Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_quant_generalizer.cpp | 1 - src/qe/qe_term_graph.cpp | 521 ++++++++++---------- src/qe/qe_term_graph.h | 22 +- 3 files changed, 268 insertions(+), 276 deletions(-) diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index f3425817b..fb2964c40 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -27,7 +27,6 @@ Revision History: #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" #include "ast/factor_equivs.h" -#include "qe/qe_term_graph.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" #include "ast/expr_functors.h" diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index fa91eced6..e723542a2 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -171,7 +171,7 @@ namespace qe { virtual ~arith_term_graph_plugin() {} - bool mk_eq_core (expr *_e1, expr *_e2, app_ref &res) { + bool mk_eq_core (expr *_e1, expr *_e2, expr_ref &res) { expr *e1, *e2; e1 = _e1; e2 = _e2; @@ -227,7 +227,7 @@ namespace qe { return m_arith.mk_ge(arg, mk_zero()); } - bool mk_le_core (expr *arg1, expr * arg2, app_ref &result) { + bool mk_le_core (expr *arg1, expr * arg2, expr_ref &result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) rational n; if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { @@ -252,7 +252,7 @@ namespace qe { return m_arith.is_numeral (n, val) && val.is_one (); } - bool mk_ge_core (expr * arg1, expr * arg2, app_ref &result) { + bool mk_ge_core (expr * arg1, expr * arg2, expr_ref &result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) rational n; if (m_arith.is_int (arg1) && is_one (arg2)) { @@ -271,8 +271,8 @@ namespace qe { return false; } - virtual app_ref process_lit (app *_lit) { - app *lit = _lit; + expr_ref process_lit (expr *_lit) override { + expr *lit = _lit; expr *e1, *e2; // strip negation @@ -281,7 +281,7 @@ namespace qe { lit = to_app(to_app(lit)->get_arg(0)); } - app_ref res(m); + expr_ref res(m); res = lit; if (m.is_eq (lit, e1, e2)) { mk_eq_core(e1, e2, res); @@ -295,10 +295,10 @@ namespace qe { // restore negation if (is_neg) { - res = m.mk_not(res); + res = mk_not(m, res); } - return res; + return res; } }; @@ -314,27 +314,26 @@ namespace qe { reset(); } - static family_id get_family_id(ast_manager &m, app *lit) { - family_id fid = null_family_id; - - expr *e1 = nullptr, *e2 = nullptr, *e3 = nullptr; - // strip negation - if (!m.is_not (lit, e1)) { e1 = lit; } - + static family_id get_family_id(ast_manager &m, expr *lit) { + if (m.is_not(lit, lit)) + return get_family_id(m, lit); + + expr *a = nullptr, *b = nullptr; // deal with equality using sort of range - if (m.is_eq (e1, e2, e3)) { - fid = get_sort (e2)->get_family_id(); + if (m.is_eq (lit, a, b)) { + return get_sort (a)->get_family_id(); } // extract family_id of top level app - else { - fid = to_app(e1)->get_decl()->get_family_id(); + else if (is_app(lit)) { + return to_app(lit)->get_decl()->get_family_id(); + } + else { + return null_family_id; } - - return fid; } - void term_graph::add_lit(app *l) { - app_ref lit(m); + void term_graph::add_lit(expr *l) { + expr_ref lit(m); family_id fid = get_family_id (m, l); term_graph_plugin *pin = m_plugins.get_plugin(fid); @@ -482,263 +481,257 @@ namespace qe { } } -expr_ref term_graph::mk_app(term const &r) { - SASSERT(r.is_root()); - - if (r.get_num_args() == 0) { - return expr_ref(r.get_app(), m); - } - - expr* res = nullptr; - if (m_term2app.find(r.get_id(), res)) { + expr_ref term_graph::mk_app(term const &r) { + SASSERT(r.is_root()); + + if (r.get_num_args() == 0) { + return expr_ref(r.get_app(), m); + } + + expr* res = nullptr; + if (m_term2app.find(r.get_id(), res)) { + return expr_ref(res, m); + } + + res = mk_app_core (r.get_app()); + m_term2app.insert(r.get_id(), res); return expr_ref(res, m); + } - res = mk_app_core (r.get_app()); - m_term2app.insert(r.get_id(), res); - return expr_ref(res, m); - -} - -expr_ref term_graph::mk_app(expr *a) { - term *t = get_term(a); - if (!t) - return expr_ref(a, m); - else - return mk_app(t->get_root()); - -} - -void term_graph::mk_equalities(term const &t, app_ref_vector &out) { - SASSERT(t.is_root()); - expr_ref rep(mk_app(t), m); - - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - expr* mem = mk_app_core(it->get_app()); - out.push_back (m.mk_eq (rep, mem)); - } -} - -void term_graph::mk_all_equalities(term const &t, app_ref_vector &out) { - mk_equalities(t, out); - - for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { - expr* a1 = mk_app_core (it->get_app()); - for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { - expr* a2 = mk_app_core(it2->get_app()); - out.push_back (m.mk_eq (a1, a2)); - } - } -} - -void term_graph::reset_marks() { - for (term * t : m_terms) { - t->set_mark(false); - } -} - -/// Order of preference for roots of equivalence classes -/// XXX This should be factored out to let clients control the preference -bool term_graph::term_le(term const &t1, term const &t2) { - - // prefer constants over applications - // prefer uninterpreted constants over values - // prefer smaller expressions over larger ones - if (t1.get_num_args() == 0 && t2.get_num_args() > 0) { - return true; - } - if (t1.get_num_args() == t2.get_num_args()) { - // NSB: how does this possibly define an order? - return m.is_value(t2.get_app()); - } - - unsigned sz1 = get_num_exprs(t1.get_app()); - unsigned sz2 = get_num_exprs(t1.get_app()); - return sz1 < sz2; -} - -void term_graph::pick_root (term &t) { - term *r = &t; - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - it->set_mark(true); - if (term_le(*it, *r)) { r = it; } - } - - // -- if found something better, make it the new root - if (r != &t) { - r->mk_root(); - } -} -/// Choose better roots for equivalence classes -void term_graph::pick_roots() { - for (term* t : m_terms) { - if (!t->is_marked() && t->is_root()) - pick_root(*t); - } - reset_marks(); -} - -void term_graph::display(std::ostream &out) { - for (term * t : m_terms) { - out << mk_pp(t->get_app(), m) << " is root " << t->is_root() - << " cls sz " << t->get_class_size() - << " term " << t - << "\n"; - } -} - -void term_graph::to_lits (app_ref_vector &lits, bool all_equalities) { - pick_roots(); - - for (app * a : m_lits) { - if (is_internalized(a)) { - lits.push_back (::to_app(mk_app(a))); - } - } - - for (term * t : m_terms) { - if (!t->is_root()) - continue; - else if (all_equalities) - mk_all_equalities (*t, lits); + expr_ref term_graph::mk_app(expr *a) { + term *t = get_term(a); + if (!t) + return expr_ref(a, m); else - mk_equalities(*t, lits); - } -} - -void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { - app_ref_vector out(m); - to_lits (out, all_equalities); - for (app* a : out) { - lits.push_back(a); - } -} - -app_ref term_graph::to_app() { - app_ref_vector lits(m); - to_lits(lits); - return mk_and(lits); -} - -void term_graph::reset() { - m_term2app.reset(); - m_pinned.reset(); - m_app2term.reset(); - std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); - m_terms.reset(); - m_lits.reset(); - m_cg_table.reset(); -} - -expr* term_graph::mk_pure(term& t) { - expr* e = t.get_app(); - if (m_term2app.find(t.get_id(), e)) e; - if (!is_app(e)) return nullptr; - app* a = ::to_app(e); - expr_ref_vector kids(m); - for (term* ch : term::children(t)) { - if (!m_term2app.find(ch->get_root().get_id(), e)) return nullptr; - kids.push_back(e); - } - expr* result = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); - m_pinned.push_back(result); - m_term2app.insert(t.get_id(), result); - return result; -} - -expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { - u_map _decls; - for (func_decl* f : decls) _decls.insert(f->get_id(), true); - // . propagate representatives up over parents. - // use work-list + marking to propagate. - // . produce equalities over represented classes. - // . produce other literals over represented classes - // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) - - expr_ref_vector result(m); - m_term2app.reset(); - m_pinned.reset(); - - obj_hashtable eqs; - expr_ref eq(m); - ptr_vector worklist; - for (term * t : m_terms) { - worklist.push_back(t); - t->set_mark(true); + return mk_app(t->get_root()); + } - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); - t->set_mark(false); - if (m_term2app.contains(t->get_id())) - continue; - if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) - continue; - - term& root = t->get_root(); - bool has_rep = m_term2app.contains(root.get_id()); - expr* pure = mk_pure(*t); - if (!pure) continue; - - // ensure that the root has a representative - // either by looking up cached version, - // computing it for the first time, or - // inheriting pure. - expr* rep = nullptr; - if (root.is_theory() || exclude != _decls.contains(root.get_decl_id())) { - rep = mk_pure(root); + void term_graph::mk_equalities(term const &t, expr_ref_vector &out) { + SASSERT(t.is_root()); + expr_ref rep(mk_app(t), m); + + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + expr* mem = mk_app_core(it->get_app()); + out.push_back (m.mk_eq (rep, mem)); } - else if (has_rep) { - rep = m_term2app.find(root.get_id()); + } + + void term_graph::mk_all_equalities(term const &t, expr_ref_vector &out) { + mk_equalities(t, out); + + for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { + expr* a1 = mk_app_core (it->get_app()); + for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { + expr* a2 = mk_app_core(it2->get_app()); + out.push_back (m.mk_eq (a1, a2)); + } } - else { - rep = pure; - m_term2app.insert(root.get_id(), pure); + } + + void term_graph::reset_marks() { + for (term * t : m_terms) { + t->set_mark(false); } - bool update_rep = false; + } + + /// Order of preference for roots of equivalence classes + /// XXX This should be factored out to let clients control the preference + bool term_graph::term_le(term const &t1, term const &t2) { + + // prefer constants over applications + // prefer uninterpreted constants over values + // prefer smaller expressions over larger ones + if (t1.get_num_args() == 0 && t2.get_num_args() > 0) { + return true; + } + if (t1.get_num_args() == t2.get_num_args()) { + // NSB: how does this possibly define an order? + return m.is_value(t2.get_app()); + } + + unsigned sz1 = get_num_exprs(t1.get_app()); + unsigned sz2 = get_num_exprs(t1.get_app()); + return sz1 < sz2; + } - // Add equations between pure and rep, - // optionally swap the roles of rep and pure if - // pure makes a better representative. - if (rep != pure) { - if (m.is_unique_value(rep) && !m.is_unique_value(pure)) { + void term_graph::pick_root (term &t) { + term *r = &t; + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + it->set_mark(true); + if (term_le(*it, *r)) { r = it; } + } + + // -- if found something better, make it the new root + if (r != &t) { + r->mk_root(); + } + } + + /// Choose better roots for equivalence classes + void term_graph::pick_roots() { + for (term* t : m_terms) { + if (!t->is_marked() && t->is_root()) + pick_root(*t); + } + reset_marks(); + } + + void term_graph::display(std::ostream &out) { + for (term * t : m_terms) { + out << mk_pp(t->get_app(), m) << " is root " << t->is_root() + << " cls sz " << t->get_class_size() + << " term " << t + << "\n"; + } + } + + void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { + pick_roots(); + + for (expr * a : m_lits) { + if (is_internalized(a)) { + lits.push_back (::to_app(mk_app(a))); + } + } + + for (term * t : m_terms) { + if (!t->is_root()) + continue; + else if (all_equalities) + mk_all_equalities (*t, lits); + else + mk_equalities(*t, lits); + } + } + + + expr_ref term_graph::to_app() { + expr_ref_vector lits(m); + to_lits(lits); + return mk_and(lits); + } + + void term_graph::reset() { + m_term2app.reset(); + m_pinned.reset(); + m_app2term.reset(); + std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); + m_terms.reset(); + m_lits.reset(); + m_cg_table.reset(); + } + + expr* term_graph::mk_pure(term& t) { + expr* e = t.get_app(); + if (m_term2app.find(t.get_id(), e)) e; + if (!is_app(e)) return nullptr; + app* a = ::to_app(e); + expr_ref_vector kids(m); + for (term* ch : term::children(t)) { + if (!m_term2app.find(ch->get_root().get_id(), e)) return nullptr; + kids.push_back(e); + } + expr* result = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); + m_pinned.push_back(result); + m_term2app.insert(t.get_id(), result); + return result; + } + + expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { + u_map _decls; + for (func_decl* f : decls) _decls.insert(f->get_id(), true); + // . propagate representatives up over parents. + // use work-list + marking to propagate. + // . produce equalities over represented classes. + // . produce other literals over represented classes + // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) + + expr_ref_vector result(m); + m_term2app.reset(); + m_pinned.reset(); + + obj_hashtable eqs; + expr_ref eq(m); + ptr_vector worklist; + for (term * t : m_terms) { + worklist.push_back(t); + t->set_mark(true); + } + + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; + if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) + continue; + + term& root = t->get_root(); + bool has_rep = m_term2app.contains(root.get_id()); + expr* pure = mk_pure(*t); + if (!pure) continue; + + // ensure that the root has a representative + // either by looking up cached version, + // computing it for the first time, or + // inheriting pure. + expr* rep = nullptr; + if (root.is_theory() || exclude != _decls.contains(root.get_decl_id())) { + rep = mk_pure(root); + } + else if (has_rep) { + rep = m_term2app.find(root.get_id()); + } + else { + rep = pure; m_term2app.insert(root.get_id(), pure); - update_rep = true; } - eq = m.mk_eq(rep, pure); - if (!eqs.contains(eq)) { - eqs.insert(eq); - result.push_back(eq); - } - } + bool update_rep = false; - // update the worklist if this is the first - // representative or pure was swapped into rep. - if (!has_rep || update_rep) { - for (term * p : term::parents(root)) { - if (update_rep) m_term2app.remove(p->get_id()); - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); + // Add equations between pure and rep, + // optionally swap the roles of rep and pure if + // pure makes a better representative. + if (rep != pure) { + if (m.is_unique_value(rep) && !m.is_unique_value(pure)) { + m_term2app.insert(root.get_id(), pure); + update_rep = true; + } + eq = m.mk_eq(rep, pure); + if (!eqs.contains(eq)) { + eqs.insert(eq); + result.push_back(eq); + } + } + + // update the worklist if this is the first + // representative or pure was swapped into rep. + if (!has_rep || update_rep) { + for (term * p : term::parents(root)) { + if (update_rep) m_term2app.remove(p->get_id()); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } } } } + // walk other predicates than equalities + for (expr* e : m_lits) { + if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { + result.push_back(e); + } + } + // Here we could also walk equivalence classes that contain interpreted values by sort and + // extract disequalities bewteen non-unique value representatives. + // these disequalities are implied and can be mined using other means, such as + // theory aware core minimization + m_term2app.reset(); + m_pinned.reset(); + reset_marks(); + return result; } - // walk other predicates than equalities - for (expr* e : m_lits) { - if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { - result.push_back(e); - } - } - // Here we could also walk equivalence classes that contain interpreted values by sort and - // extract disequalities bewteen non-unique value representatives. - // these disequalities are implied and can be mined using other means, such as - // theory aware core minimization - m_term2app.reset(); - m_pinned.reset(); - reset_marks(); - return result; -} } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 4f0430812..4c2c2c6ff 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -35,7 +35,7 @@ namespace qe { family_id get_family_id() const {return m_id;} /// Process (and potentially augment) a literal - virtual app_ref process_lit (app *lit) = 0; + virtual expr_ref process_lit (expr *lit) = 0; }; @@ -44,7 +44,7 @@ namespace qe { struct term_eq { bool operator()(term const* a, term const* b) const; }; ast_manager & m; ptr_vector m_terms; - app_ref_vector m_lits; // NSB: expr_ref_vector? + expr_ref_vector m_lits; // NSB: expr_ref_vector? u_map m_app2term; ast_ref_vector m_pinned; u_map m_term2app; @@ -74,25 +74,25 @@ namespace qe { expr_ref mk_app(term const &t); expr* mk_pure(term& t); expr_ref mk_app(expr *a); - void mk_equalities(term const &t, app_ref_vector &out); - void mk_all_equalities(term const &t, app_ref_vector &out); + void mk_equalities(term const &t, expr_ref_vector &out); + void mk_all_equalities(term const &t, expr_ref_vector &out); void display(std::ostream &out); + public: term_graph(ast_manager &m); ~term_graph(); ast_manager& get_ast_manager() const { return m;} - void add_lit(app *lit); // NSB: replace by expr* - void add_lits(expr_ref_vector const &lits) { - for (expr* e : lits) add_lit(::to_app(e)); - } - void add_eq(expr* a, expr* b); + void add_lit(expr *lit); + void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(e); } + void add_eq(expr* a, expr* b) { internalize_eq(a, b); } void reset(); - void to_lits(app_ref_vector &lits, bool all_equalities = false); // NSB: swap roles + + // deprecate? void to_lits(expr_ref_vector &lits, bool all_equalities = false); - app_ref to_app(); + expr_ref to_app(); /** * Return literals obtained by projecting added literals From dda65fdd2e9207d6a9e1ea2d3c72c2157844effb Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 11:11:44 -0700 Subject: [PATCH 274/364] mk_not: fix clang compilation issue --- src/ast/ast_util.cpp | 24 ++++++++++++------------ src/ast/ast_util.h | 3 +-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index 0e5cf12a5..42c0d698b 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -98,7 +98,7 @@ bool is_atom(ast_manager & m, expr * n) { return true; SASSERT(is_app(n)); if (to_app(n)->get_family_id() != m.get_basic_family_id()) { - return true; + return true; } // the other operators of the basic family are not considered atomic: distinct, ite, and, or, iff, xor, not, implies. return (m.is_eq(n) && !m.is_bool(to_app(n)->get_arg(0))) || m.is_true(n) || m.is_false(n); @@ -106,7 +106,7 @@ bool is_atom(ast_manager & m, expr * n) { bool is_literal(ast_manager & m, expr * n) { - return + return is_atom(m, n) || (m.is_not(n) && is_atom(m, to_app(n)->get_arg(0))); } @@ -187,7 +187,7 @@ expr * mk_not(ast_manager & m, expr * arg) { expr * atom; if (m.is_not(arg, atom)) return atom; - else if (m.is_true(arg)) + else if (m.is_true(arg)) return m.mk_false(); else if (m.is_false(arg)) return m.mk_true(); @@ -195,7 +195,7 @@ expr * mk_not(ast_manager & m, expr * arg) { return m.mk_not(arg); } -expr_ref mk_not(expr_ref& e) { +expr_ref mk_not(const expr_ref& e) { return expr_ref(mk_not(e.m(), e), e.m()); } @@ -226,7 +226,7 @@ expr_ref push_not(const expr_ref& e) { } return mk_and(args); } - return expr_ref(mk_not(m, e), m); + return expr_ref(mk_not(m, e), m); } expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) { @@ -282,7 +282,7 @@ void flatten_and(expr_ref_vector& result) { } result[i] = result.back(); result.pop_back(); - --i; + --i; } else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) { result.push_back(e2); @@ -294,7 +294,7 @@ void flatten_and(expr_ref_vector& result) { m.is_false(e1))) { result[i] = result.back(); result.pop_back(); - --i; + --i; } else if (m.is_false(result[i].get()) || (m.is_not(result[i].get(), e1) && @@ -308,7 +308,7 @@ void flatten_and(expr_ref_vector& result) { void flatten_and(expr* fml, expr_ref_vector& result) { SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); + result.push_back(fml); flatten_and(result); } @@ -345,7 +345,7 @@ void flatten_or(expr_ref_vector& result) { } result[i] = result.back(); result.pop_back(); - --i; + --i; } else if (m.is_implies(result[i].get(),e2,e3)) { result.push_back(e3); @@ -357,7 +357,7 @@ void flatten_or(expr_ref_vector& result) { m.is_true(e1))) { result[i] = result.back(); result.pop_back(); - --i; + --i; } else if (m.is_true(result[i].get()) || (m.is_not(result[i].get(), e1) && @@ -366,12 +366,12 @@ void flatten_or(expr_ref_vector& result) { result.push_back(m.mk_true()); return; } - } + } } void flatten_or(expr* fml, expr_ref_vector& result) { SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); + result.push_back(fml); flatten_or(result); } diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index 1383be157..23c2205bb 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -127,7 +127,7 @@ inline expr_ref mk_or(expr_ref_vector const& args) { return expr_ref(mk_or(args. */ expr * mk_not(ast_manager & m, expr * arg); -expr_ref mk_not(expr_ref& e); +expr_ref mk_not(const expr_ref& e); /** Negate and push over conjunction or disjunction. @@ -162,4 +162,3 @@ void flatten_or(expr* fml, expr_ref_vector& result); #endif /* AST_UTIL_H_ */ - From 9c7d9818d304ab0212d946d7d7c59c3dbfb63ad9 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 11:36:46 -0700 Subject: [PATCH 275/364] get_app --> get_expr + fix term_lt() --- src/qe/qe_term_graph.cpp | 257 ++++++++++++++++++++------------------- src/qe/qe_term_graph.h | 40 +++--- 2 files changed, 151 insertions(+), 146 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index e723542a2..b951244f3 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -28,46 +28,46 @@ namespace qe { class term { // -- an app represented by this term - expr* m_app; // NSB: to make usable with exprs + expr* m_expr; // NSB: to make usable with exprs // -- root of the equivalence class term* m_root; // -- next element in the equivalence class (cyclic linked list) term* m_next; // -- eq class size unsigned m_class_size; - + // -- general purpose mark unsigned m_mark:1; // -- general purpose second mark unsigned m_mark2:1; // -- is an interpreted constant unsigned m_interpreted:1; - + // -- terms that contain this term as a child ptr_vector m_parents; - + // arguments of term. ptr_vector m_children; - + public: - term(expr* a, u_map& app2term) : - m_app(a), - m_root(this), + term(expr* v, u_map& app2term) : + m_expr(v), + m_root(this), m_next(this), - m_class_size(1), - m_mark(false), + m_class_size(1), + m_mark(false), m_mark2(false), m_interpreted(false) { - if (!is_app(a)) return; - for (expr* e : *to_app(a)) { + if (!is_app()) return; + for (expr* e : *to_app(m_expr)) { term* t = app2term[e->get_id()]; t->get_root().m_parents.push_back(this); m_children.push_back(t); } } - + ~term() {} - + class parents { term const& t; public: @@ -76,7 +76,7 @@ namespace qe { ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } ptr_vector::const_iterator end() const { return t.m_parents.end(); } }; - + class children { term const& t; public: @@ -85,20 +85,20 @@ namespace qe { ptr_vector::const_iterator begin() const { return t.m_children.begin(); } ptr_vector::const_iterator end() const { return t.m_children.end(); } }; - + // Congruence table hash function is based on // roots of children and function declaration. - + unsigned get_hash() const { unsigned a, b, c; - a = b = c = get_decl_id(); + a = b = c = get_decl_id(); for (term * ch : children(this)) { a = ch->get_root().get_id(); mix(a, b, c); } return c; } - + static bool cg_eq(term const * t1, term const * t2) { if (t1->get_decl_id() != t2->get_decl_id()) return false; if (t1->m_children.size() != t2->m_children.size()) return false; @@ -107,41 +107,43 @@ namespace qe { } return true; } - - unsigned get_id() const { return m_app->get_id();} - - unsigned get_decl_id() const { return is_app(m_app) ? to_app(m_app)->get_decl()->get_id() : m_app->get_id(); } - + + unsigned get_id() const { return m_expr->get_id();} + + unsigned get_decl_id() const { return is_app() ? get_app()->get_decl()->get_id() : m_expr->get_id(); } + bool is_marked() const {return m_mark;} void set_mark(bool v){m_mark = v;} bool is_marked2() const {return m_mark2;} // NSB: where is this used? void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? - + bool is_interpreted() const {return m_interpreted;} - bool is_theory() const { return !is_app(m_app) || to_app(m_app)->get_family_id() != null_family_id; } + bool is_theory() const { return !is_app() || get_app()->get_family_id() != null_family_id; } void mark_as_interpreted() {m_interpreted=true;} - expr* get_app() const {return m_app;} - unsigned get_num_args() const { return is_app(m_app) ? to_app(m_app)->get_num_args() : 0; } - + expr* get_expr() const {return m_expr;} + bool is_app() const {return ::is_app(m_expr);} + app *get_app() const {return is_app() ? to_app(m_expr) : nullptr;} + unsigned get_num_args() const { return is_app() ? get_app()->get_num_args() : 0; } + term &get_root() const {return *m_root;} bool is_root() const {return m_root == this;} void set_root(term &r) {m_root = &r;} term &get_next() const {return *m_next;} void add_parent(term* p) { m_parents.push_back(p); } - + unsigned get_class_size() const {return m_class_size;} - + void merge_eq_class(term &b) { std::swap(this->m_next, b.m_next); m_class_size += b.get_class_size(); // -- reset (useful for debugging) b.m_class_size = 0; } - + // -- make this term the root of its equivalence class void mk_root() { if (is_root()) return; - + term *curr = this; do { if (curr->is_root()) { @@ -156,26 +158,26 @@ namespace qe { while (curr != this); } }; - + class arith_term_graph_plugin : public term_graph_plugin { term_graph &m_g; ast_manager &m; arith_util m_arith; - + public: arith_term_graph_plugin(term_graph &g) : term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} - + virtual ~arith_term_graph_plugin() {} - + bool mk_eq_core (expr *_e1, expr *_e2, expr_ref &res) { expr *e1, *e2; e1 = _e1; e2 = _e2; - + if (m_arith.is_zero(e1)) { std::swap(e1, e2); } @@ -194,7 +196,7 @@ namespace qe { res = m.mk_eq(e1, e2); return true; } - + app* mk_le_zero(expr *arg) { expr *e1, *e2, *e3; // XXX currently disabled @@ -226,7 +228,7 @@ namespace qe { } return m_arith.mk_ge(arg, mk_zero()); } - + bool mk_le_core (expr *arg1, expr * arg2, expr_ref &result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) rational n; @@ -245,13 +247,13 @@ namespace qe { } return false; } - + expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} bool is_one (expr const * n) const { rational val; return m_arith.is_numeral (n, val) && val.is_one (); } - + bool mk_ge_core (expr * arg1, expr * arg2, expr_ref &result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) rational n; @@ -270,17 +272,17 @@ namespace qe { } return false; } - + expr_ref process_lit (expr *_lit) override { expr *lit = _lit; expr *e1, *e2; - + // strip negation bool is_neg = m.is_not(lit); if (is_neg) { lit = to_app(to_app(lit)->get_arg(0)); } - + expr_ref res(m); res = lit; if (m.is_eq (lit, e1, e2)) { @@ -292,12 +294,12 @@ namespace qe { else if (m_arith.is_ge(lit, e1, e2)) { mk_ge_core(e1, e2, res); } - + // restore negation if (is_neg) { res = mk_not(m, res); } - + return res; } }; @@ -309,16 +311,16 @@ namespace qe { term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); } - + term_graph::~term_graph() { reset(); } static family_id get_family_id(ast_manager &m, expr *lit) { - if (m.is_not(lit, lit)) + if (m.is_not(lit, lit)) return get_family_id(m, lit); - expr *a = nullptr, *b = nullptr; + expr *a = nullptr, *b = nullptr; // deal with equality using sort of range if (m.is_eq (lit, a, b)) { return get_sort (a)->get_family_id(); @@ -331,10 +333,10 @@ namespace qe { return null_family_id; } } - + void term_graph::add_lit(expr *l) { expr_ref lit(m); - + family_id fid = get_family_id (m, l); term_graph_plugin *pin = m_plugins.get_plugin(fid); if (pin) { @@ -345,16 +347,16 @@ namespace qe { m_lits.push_back(lit); internalize_lit(lit); } - + bool term_graph::is_internalized(expr *a) { return m_app2term.contains(a->get_id()); } - + term* term_graph::get_term(expr *a) { term *res; return m_app2term.find (a->get_id(), res) ? res : nullptr; } - + term *term_graph::mk_term(expr *a) { term * t = alloc(term, a, m_app2term); if (t->get_num_args() == 0 && m.is_unique_value(a)){ @@ -365,8 +367,8 @@ namespace qe { m_app2term.insert(a->get_id(), t); return t; } - - term* term_graph::internalize_term(expr *t) { + + term* term_graph::internalize_term(expr *t) { term* res = get_term(t); if (res) return res; ptr_buffer todo; @@ -381,7 +383,7 @@ namespace qe { unsigned sz = todo.size(); if (is_app(t)) { for (expr * arg : *::to_app(t)) { - if (!get_term(arg)) + if (!get_term(arg)) todo.push_back(arg); } } @@ -392,7 +394,7 @@ namespace qe { SASSERT(res); return res; } - + void term_graph::internalize_eq(expr *a1, expr* a2) { SASSERT(m_merge.empty()); merge(internalize_term(a1)->get_root(), internalize_term(a2)->get_root()); @@ -402,7 +404,7 @@ namespace qe { void term_graph::internalize_lit(expr* lit) { expr *e1 = nullptr, *e2 = nullptr; - if (m.is_eq (lit, e1, e2)) { + if (m.is_eq (lit, e1, e2)) { internalize_eq (e1, e2); } else { @@ -422,19 +424,19 @@ namespace qe { void term_graph::merge(term &t1, term &t2) { // -- merge might invalidate term2map cache m_term2app.reset(); - m_pinned.reset(); - + m_pinned.reset(); + SASSERT(t1.is_root()); SASSERT(t2.is_root()); - + if (&t1 == &t2) return; - + term *a = &t1; term *b = &t2; if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } - + // Remove parents of it from the cg table. for (term* p : term::parents(b)) { if (!p->is_marked()) { @@ -442,15 +444,15 @@ namespace qe { m_cg_table.erase(p); } } - // make 'a' be the root of the equivalence class of 'b' + // make 'a' be the root of the equivalence class of 'b' b->set_root(*a); for (term *it = &b->get_next(); it != b; it = &it->get_next()) { it->set_root(*a); } - + // merge equivalence classes a->merge_eq_class(*b); - + // Insert parents of b's old equilvalence class into the cg table for (term* p : term::parents(a)) { if (p->is_marked()) { @@ -462,16 +464,16 @@ namespace qe { m_merge.push_back(std::make_pair(p, p_old)); } } - } + } } - + expr* term_graph::mk_app_core (expr *e) { if (is_app(e)) { - expr_ref_vector kids(m); + expr_ref_vector kids(m); app* a = ::to_app(e); for (expr * arg : *a) { kids.push_back (mk_app(arg)); - } + } app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); m_pinned.push_back(res); return res; @@ -483,44 +485,44 @@ namespace qe { expr_ref term_graph::mk_app(term const &r) { SASSERT(r.is_root()); - + if (r.get_num_args() == 0) { - return expr_ref(r.get_app(), m); + return expr_ref(r.get_expr(), m); } - + expr* res = nullptr; if (m_term2app.find(r.get_id(), res)) { return expr_ref(res, m); } - + res = mk_app_core (r.get_app()); m_term2app.insert(r.get_id(), res); return expr_ref(res, m); - + } expr_ref term_graph::mk_app(expr *a) { term *t = get_term(a); - if (!t) + if (!t) return expr_ref(a, m); - else + else return mk_app(t->get_root()); - + } void term_graph::mk_equalities(term const &t, expr_ref_vector &out) { SASSERT(t.is_root()); expr_ref rep(mk_app(t), m); - + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { expr* mem = mk_app_core(it->get_app()); out.push_back (m.mk_eq (rep, mem)); } } - + void term_graph::mk_all_equalities(term const &t, expr_ref_vector &out) { mk_equalities(t, out); - + for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { expr* a1 = mk_app_core (it->get_app()); for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { @@ -529,30 +531,32 @@ namespace qe { } } } - + void term_graph::reset_marks() { for (term * t : m_terms) { t->set_mark(false); } } - + /// Order of preference for roots of equivalence classes /// XXX This should be factored out to let clients control the preference - bool term_graph::term_le(term const &t1, term const &t2) { - + bool term_graph::term_lt(term const &t1, term const &t2) { + // prefer constants over applications // prefer uninterpreted constants over values // prefer smaller expressions over larger ones - if (t1.get_num_args() == 0 && t2.get_num_args() > 0) { - return true; + if (t1.get_num_args() == 0 || t2.get_num_args() == 0) { + if (t1.get_num_args() == t2.get_num_args()) { + // t1.get_num_args() == t2.get_num_args() == 0 + if (m.is_value(t1.get_expr()) == m.is_value(t2.get_expr())) + return t1.get_id() < t2.get_id(); + return m.is_value(t2.get_expr()); + } + return t1.get_num_args() < t2.get_num_args(); } - if (t1.get_num_args() == t2.get_num_args()) { - // NSB: how does this possibly define an order? - return m.is_value(t2.get_app()); - } - - unsigned sz1 = get_num_exprs(t1.get_app()); - unsigned sz2 = get_num_exprs(t1.get_app()); + + unsigned sz1 = get_num_exprs(t1.get_expr()); + unsigned sz2 = get_num_exprs(t1.get_expr()); return sz1 < sz2; } @@ -560,9 +564,9 @@ namespace qe { term *r = &t; for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { it->set_mark(true); - if (term_le(*it, *r)) { r = it; } + if (term_lt(*it, *r)) { r = it; } } - + // -- if found something better, make it the new root if (r != &t) { r->mk_root(); @@ -572,47 +576,47 @@ namespace qe { /// Choose better roots for equivalence classes void term_graph::pick_roots() { for (term* t : m_terms) { - if (!t->is_marked() && t->is_root()) + if (!t->is_marked() && t->is_root()) pick_root(*t); } reset_marks(); } - + void term_graph::display(std::ostream &out) { for (term * t : m_terms) { - out << mk_pp(t->get_app(), m) << " is root " << t->is_root() + out << mk_pp(t->get_expr(), m) << " is root " << t->is_root() << " cls sz " << t->get_class_size() << " term " << t << "\n"; } } - + void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { pick_roots(); - + for (expr * a : m_lits) { if (is_internalized(a)) { lits.push_back (::to_app(mk_app(a))); } } - + for (term * t : m_terms) { - if (!t->is_root()) + if (!t->is_root()) continue; - else if (all_equalities) - mk_all_equalities (*t, lits); - else + else if (all_equalities) + mk_all_equalities (*t, lits); + else mk_equalities(*t, lits); } } - - + + expr_ref term_graph::to_app() { expr_ref_vector lits(m); to_lits(lits); return mk_and(lits); } - + void term_graph::reset() { m_term2app.reset(); m_pinned.reset(); @@ -622,9 +626,10 @@ namespace qe { m_lits.reset(); m_cg_table.reset(); } - + expr* term_graph::mk_pure(term& t) { - expr* e = t.get_app(); + expr* e = t.get_expr(); + // AG: the if-statement looks wrong if (m_term2app.find(t.get_id(), e)) e; if (!is_app(e)) return nullptr; app* a = ::to_app(e); @@ -638,35 +643,35 @@ namespace qe { m_term2app.insert(t.get_id(), result); return result; } - + expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { u_map _decls; for (func_decl* f : decls) _decls.insert(f->get_id(), true); // . propagate representatives up over parents. // use work-list + marking to propagate. // . produce equalities over represented classes. - // . produce other literals over represented classes + // . produce other literals over represented classes // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) - + expr_ref_vector result(m); m_term2app.reset(); m_pinned.reset(); - + obj_hashtable eqs; expr_ref eq(m); ptr_vector worklist; for (term * t : m_terms) { worklist.push_back(t); - t->set_mark(true); + t->set_mark(true); } - + while (!worklist.empty()) { term* t = worklist.back(); worklist.pop_back(); t->set_mark(false); - if (m_term2app.contains(t->get_id())) + if (m_term2app.contains(t->get_id())) continue; - if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) + if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) continue; term& root = t->get_root(); @@ -675,7 +680,7 @@ namespace qe { if (!pure) continue; // ensure that the root has a representative - // either by looking up cached version, + // either by looking up cached version, // computing it for the first time, or // inheriting pure. expr* rep = nullptr; @@ -691,7 +696,7 @@ namespace qe { } bool update_rep = false; - // Add equations between pure and rep, + // Add equations between pure and rep, // optionally swap the roles of rep and pure if // pure makes a better representative. if (rep != pure) { @@ -706,7 +711,7 @@ namespace qe { } } - // update the worklist if this is the first + // update the worklist if this is the first // representative or pure was swapped into rep. if (!has_rep || update_rep) { for (term * p : term::parents(root)) { @@ -723,10 +728,10 @@ namespace qe { if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { result.push_back(e); } - } + } // Here we could also walk equivalence classes that contain interpreted values by sort and // extract disequalities bewteen non-unique value representatives. - // these disequalities are implied and can be mined using other means, such as + // these disequalities are implied and can be mined using other means, such as // theory aware core minimization m_term2app.reset(); m_pinned.reset(); diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 4c2c2c6ff..e32f7271c 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -33,11 +33,11 @@ namespace qe { virtual ~term_graph_plugin() {} family_id get_family_id() const {return m_id;} - + /// Process (and potentially augment) a literal virtual expr_ref process_lit (expr *lit) = 0; }; - + class term_graph { struct term_hash { unsigned operator()(term const* t) const; }; @@ -45,62 +45,62 @@ namespace qe { ast_manager & m; ptr_vector m_terms; expr_ref_vector m_lits; // NSB: expr_ref_vector? - u_map m_app2term; + u_map m_app2term; ast_ref_vector m_pinned; u_map m_term2app; plugin_manager m_plugins; ptr_hashtable m_cg_table; vector> m_merge; - + void merge(term &t1, term &t2); void merge_flush(); - + term *mk_term(expr *t); term *get_term(expr *t); - + term *internalize_term(expr *t); void internalize_eq(expr *a1, expr *a2); void internalize_lit(expr *lit); - + bool is_internalized(expr *a); - - bool term_le(term const &t1, term const &t2); + + bool term_lt(term const &t1, term const &t2); void pick_root (term &t); void pick_roots(); - + void reset_marks(); - + expr* mk_app_core(expr* a); expr_ref mk_app(term const &t); expr* mk_pure(term& t); expr_ref mk_app(expr *a); void mk_equalities(term const &t, expr_ref_vector &out); void mk_all_equalities(term const &t, expr_ref_vector &out); - void display(std::ostream &out); + void display(std::ostream &out); public: term_graph(ast_manager &m); ~term_graph(); - + ast_manager& get_ast_manager() const { return m;} - - void add_lit(expr *lit); + + void add_lit(expr *lit); void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(e); } void add_eq(expr* a, expr* b) { internalize_eq(a, b); } - + void reset(); // deprecate? void to_lits(expr_ref_vector &lits, bool all_equalities = false); - expr_ref to_app(); + expr_ref to_app(); /** - * Return literals obtained by projecting added literals - * onto the vocabulary of decls (if exclude is false) or outside the + * Return literals obtained by projecting added literals + * onto the vocabulary of decls (if exclude is false) or outside the * vocabulary of decls (if exclude is true). */ expr_ref_vector project(func_decl_ref_vector const& decls, bool exclude); - + }; } From 73486be590472ff40bfe77618c2adc4fee764126 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 13:46:27 -0700 Subject: [PATCH 276/364] fix typo in mk_pure --- src/qe/qe_term_graph.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index b951244f3..6ea2194b7 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -78,7 +78,7 @@ namespace qe { }; class children { - term const& t; + term const& t; public: children(term const& _t):t(_t) {} children(term const* _t):t(*_t) {} @@ -628,9 +628,9 @@ namespace qe { } expr* term_graph::mk_pure(term& t) { - expr* e = t.get_expr(); - // AG: the if-statement looks wrong - if (m_term2app.find(t.get_id(), e)) e; + expr* e = nullptr; + if (m_term2app.find(t.get_id(), e)) return e; + e = t.get_expr(); if (!is_app(e)) return nullptr; app* a = ::to_app(e); expr_ref_vector kids(m); @@ -647,11 +647,11 @@ namespace qe { expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { u_map _decls; for (func_decl* f : decls) _decls.insert(f->get_id(), true); - // . propagate representatives up over parents. + // - propagate representatives up over parents. // use work-list + marking to propagate. - // . produce equalities over represented classes. - // . produce other literals over represented classes - // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) + // - produce equalities over represented classes. + // - produce other literals over represented classes + // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) expr_ref_vector result(m); m_term2app.reset(); From 6e61a7c1b282caf68399a2ab4ba1de3a8161f6ca Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 14:40:53 -0700 Subject: [PATCH 277/364] minor --- src/qe/qe_term_graph.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 6ea2194b7..412db2909 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -199,7 +199,6 @@ namespace qe { app* mk_le_zero(expr *arg) { expr *e1, *e2, *e3; - // XXX currently disabled if (m_arith.is_add(arg, e1, e2)) { // e1-e2<=0 --> e1<=e2 if (m_arith.is_times_minus_one(e2, e3)) { @@ -215,7 +214,6 @@ namespace qe { app* mk_ge_zero(expr *arg) { expr *e1, *e2, *e3; - // XXX currently disabled if (m_arith.is_add(arg, e1, e2)) { // e1-e2>=0 --> e1>=e2 if (m_arith.is_times_minus_one(e2, e3)) { @@ -422,7 +420,7 @@ namespace qe { } void term_graph::merge(term &t1, term &t2) { - // -- merge might invalidate term2map cache + // -- merge might invalidate term2app cache m_term2app.reset(); m_pinned.reset(); @@ -469,7 +467,7 @@ namespace qe { expr* term_graph::mk_app_core (expr *e) { if (is_app(e)) { - expr_ref_vector kids(m); + expr_ref_buffer kids(m); app* a = ::to_app(e); for (expr * arg : *a) { kids.push_back (mk_app(arg)); From 44a32bc076e79840edd3530d658d065a752378f8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 13:55:04 -0700 Subject: [PATCH 278/364] updates Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbi.cpp | 49 ++++++++- src/qe/qe_mbi.h | 6 ++ src/qe/qe_term_graph.cpp | 218 +++++++++++++++++++-------------------- 3 files changed, 160 insertions(+), 113 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index c6927f7d2..0fe2d49c7 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -19,12 +19,32 @@ Revision History: --*/ #include "ast/ast_util.h" +#include "ast/rewriter/bool_rewriter.h" #include "solver/solver.h" #include "qe/qe_mbi.h" namespace qe { + lbool mbi_plugin::check(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { + SASSERT(lits.empty()); + while (true) { + switch ((*this)(vars, lits, mdl)) { + case mbi_sat: + return l_true; + case mbi_unsat: + if (lits.empty()) return l_false; + block(lits); + break; + case mbi_undef: + return l_undef; + case mbi_augment: + break; + } + } + } + + // ------------------------------- // prop_mbi @@ -116,6 +136,7 @@ namespace qe { blocks.push_back(expr_ref_vector(m)); blocks.push_back(expr_ref_vector(m)); mbi_result last_res = mbi_undef; + bool_rewriter rw(m); while (true) { auto* t1 = turn ? &a : &b; auto* t2 = turn ? &b : &a; @@ -156,10 +177,32 @@ namespace qe { } /** - * TBD: also implement the one-sided versions that create clausal interpolants. + * One-sided pogo creates clausal interpolants. + * It creates a set of consequences of b that are inconsistent with a. */ lbool interpolator::pogo(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { - NOT_IMPLEMENTED_YET(); - return l_undef; + expr_ref_vector lits(m), itps(m); + while (true) { + model_ref mdl; + lits.reset(); + switch (a.check(vars, lits, mdl)) { + case l_true: + switch (b.check(vars, lits, mdl)) { + case l_true: + return l_true; + case l_false: + a.block(lits); + itps.push_back(mk_not(mk_and(lits))); + break; + case l_undef: + return l_undef; + } + case l_false: + itp = mk_and(itps); + return l_false; + case l_undef: + return l_undef; + } + } } }; diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index d9af62bd0..d58430602 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -56,6 +56,12 @@ namespace qe { * \brief Block conjunction of lits from future mbi_augment or mbi_sat. */ virtual void block(expr_ref_vector const& lits) = 0; + + /** + * \brief perform a full check, consume internal auguments if necessary. + */ + lbool check(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl); + }; class prop_mbi_plugin : public mbi_plugin { diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 412db2909..eaf5a372d 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -35,27 +35,27 @@ namespace qe { term* m_next; // -- eq class size unsigned m_class_size; - + // -- general purpose mark unsigned m_mark:1; // -- general purpose second mark unsigned m_mark2:1; // -- is an interpreted constant unsigned m_interpreted:1; - + // -- terms that contain this term as a child ptr_vector m_parents; - + // arguments of term. ptr_vector m_children; - + public: term(expr* v, u_map& app2term) : m_expr(v), - m_root(this), + m_root(this), m_next(this), - m_class_size(1), - m_mark(false), + m_class_size(1), + m_mark(false), m_mark2(false), m_interpreted(false) { if (!is_app()) return; @@ -65,9 +65,9 @@ namespace qe { m_children.push_back(t); } } - + ~term() {} - + class parents { term const& t; public: @@ -76,29 +76,29 @@ namespace qe { ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } ptr_vector::const_iterator end() const { return t.m_parents.end(); } }; - + class children { - term const& t; + term const& t; public: children(term const& _t):t(_t) {} children(term const* _t):t(*_t) {} ptr_vector::const_iterator begin() const { return t.m_children.begin(); } ptr_vector::const_iterator end() const { return t.m_children.end(); } }; - + // Congruence table hash function is based on // roots of children and function declaration. - + unsigned get_hash() const { unsigned a, b, c; - a = b = c = get_decl_id(); + a = b = c = get_decl_id(); for (term * ch : children(this)) { a = ch->get_root().get_id(); mix(a, b, c); } return c; } - + static bool cg_eq(term const * t1, term const * t2) { if (t1->get_decl_id() != t2->get_decl_id()) return false; if (t1->m_children.size() != t2->m_children.size()) return false; @@ -107,16 +107,16 @@ namespace qe { } return true; } - + unsigned get_id() const { return m_expr->get_id();} - + unsigned get_decl_id() const { return is_app() ? get_app()->get_decl()->get_id() : m_expr->get_id(); } - + bool is_marked() const {return m_mark;} void set_mark(bool v){m_mark = v;} bool is_marked2() const {return m_mark2;} // NSB: where is this used? void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? - + bool is_interpreted() const {return m_interpreted;} bool is_theory() const { return !is_app() || get_app()->get_family_id() != null_family_id; } void mark_as_interpreted() {m_interpreted=true;} @@ -124,26 +124,26 @@ namespace qe { bool is_app() const {return ::is_app(m_expr);} app *get_app() const {return is_app() ? to_app(m_expr) : nullptr;} unsigned get_num_args() const { return is_app() ? get_app()->get_num_args() : 0; } - + term &get_root() const {return *m_root;} bool is_root() const {return m_root == this;} void set_root(term &r) {m_root = &r;} term &get_next() const {return *m_next;} void add_parent(term* p) { m_parents.push_back(p); } - + unsigned get_class_size() const {return m_class_size;} - + void merge_eq_class(term &b) { std::swap(this->m_next, b.m_next); m_class_size += b.get_class_size(); // -- reset (useful for debugging) b.m_class_size = 0; } - + // -- make this term the root of its equivalence class void mk_root() { if (is_root()) return; - + term *curr = this; do { if (curr->is_root()) { @@ -158,26 +158,26 @@ namespace qe { while (curr != this); } }; - + class arith_term_graph_plugin : public term_graph_plugin { term_graph &m_g; ast_manager &m; arith_util m_arith; - + public: arith_term_graph_plugin(term_graph &g) : term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} - + virtual ~arith_term_graph_plugin() {} - + bool mk_eq_core (expr *_e1, expr *_e2, expr_ref &res) { expr *e1, *e2; e1 = _e1; e2 = _e2; - + if (m_arith.is_zero(e1)) { std::swap(e1, e2); } @@ -196,7 +196,7 @@ namespace qe { res = m.mk_eq(e1, e2); return true; } - + app* mk_le_zero(expr *arg) { expr *e1, *e2, *e3; if (m_arith.is_add(arg, e1, e2)) { @@ -226,7 +226,7 @@ namespace qe { } return m_arith.mk_ge(arg, mk_zero()); } - + bool mk_le_core (expr *arg1, expr * arg2, expr_ref &result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) rational n; @@ -245,13 +245,13 @@ namespace qe { } return false; } - + expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} bool is_one (expr const * n) const { rational val; return m_arith.is_numeral (n, val) && val.is_one (); } - + bool mk_ge_core (expr * arg1, expr * arg2, expr_ref &result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) rational n; @@ -270,17 +270,17 @@ namespace qe { } return false; } - + expr_ref process_lit (expr *_lit) override { expr *lit = _lit; expr *e1, *e2; - + // strip negation bool is_neg = m.is_not(lit); if (is_neg) { lit = to_app(to_app(lit)->get_arg(0)); } - + expr_ref res(m); res = lit; if (m.is_eq (lit, e1, e2)) { @@ -292,12 +292,12 @@ namespace qe { else if (m_arith.is_ge(lit, e1, e2)) { mk_ge_core(e1, e2, res); } - + // restore negation if (is_neg) { res = mk_not(m, res); } - + return res; } }; @@ -309,16 +309,16 @@ namespace qe { term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); } - + term_graph::~term_graph() { reset(); } static family_id get_family_id(ast_manager &m, expr *lit) { - if (m.is_not(lit, lit)) + if (m.is_not(lit, lit)) return get_family_id(m, lit); - expr *a = nullptr, *b = nullptr; + expr *a = nullptr, *b = nullptr; // deal with equality using sort of range if (m.is_eq (lit, a, b)) { return get_sort (a)->get_family_id(); @@ -331,10 +331,10 @@ namespace qe { return null_family_id; } } - + void term_graph::add_lit(expr *l) { expr_ref lit(m); - + family_id fid = get_family_id (m, l); term_graph_plugin *pin = m_plugins.get_plugin(fid); if (pin) { @@ -345,16 +345,16 @@ namespace qe { m_lits.push_back(lit); internalize_lit(lit); } - + bool term_graph::is_internalized(expr *a) { return m_app2term.contains(a->get_id()); } - + term* term_graph::get_term(expr *a) { term *res; return m_app2term.find (a->get_id(), res) ? res : nullptr; } - + term *term_graph::mk_term(expr *a) { term * t = alloc(term, a, m_app2term); if (t->get_num_args() == 0 && m.is_unique_value(a)){ @@ -365,8 +365,8 @@ namespace qe { m_app2term.insert(a->get_id(), t); return t; } - - term* term_graph::internalize_term(expr *t) { + + term* term_graph::internalize_term(expr *t) { term* res = get_term(t); if (res) return res; ptr_buffer todo; @@ -381,7 +381,7 @@ namespace qe { unsigned sz = todo.size(); if (is_app(t)) { for (expr * arg : *::to_app(t)) { - if (!get_term(arg)) + if (!get_term(arg)) todo.push_back(arg); } } @@ -392,17 +392,17 @@ namespace qe { SASSERT(res); return res; } - + void term_graph::internalize_eq(expr *a1, expr* a2) { SASSERT(m_merge.empty()); - merge(internalize_term(a1)->get_root(), internalize_term(a2)->get_root()); + merge(*internalize_term(a1), *internalize_term(a2)); merge_flush(); SASSERT(m_merge.empty()); } void term_graph::internalize_lit(expr* lit) { expr *e1 = nullptr, *e2 = nullptr; - if (m.is_eq (lit, e1, e2)) { + if (m.is_eq (lit, e1, e2)) { internalize_eq (e1, e2); } else { @@ -422,19 +422,17 @@ namespace qe { void term_graph::merge(term &t1, term &t2) { // -- merge might invalidate term2app cache m_term2app.reset(); - m_pinned.reset(); - - SASSERT(t1.is_root()); - SASSERT(t2.is_root()); - - if (&t1 == &t2) return; - - term *a = &t1; - term *b = &t2; + m_pinned.reset(); + + term *a = &t1.get_root(); + term *b = &t2.get_root(); + + if (a == b) return; + if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } - + // Remove parents of it from the cg table. for (term* p : term::parents(b)) { if (!p->is_marked()) { @@ -442,15 +440,15 @@ namespace qe { m_cg_table.erase(p); } } - // make 'a' be the root of the equivalence class of 'b' + // make 'a' be the root of the equivalence class of 'b' b->set_root(*a); for (term *it = &b->get_next(); it != b; it = &it->get_next()) { it->set_root(*a); } - + // merge equivalence classes a->merge_eq_class(*b); - + // Insert parents of b's old equilvalence class into the cg table for (term* p : term::parents(a)) { if (p->is_marked()) { @@ -462,16 +460,16 @@ namespace qe { m_merge.push_back(std::make_pair(p, p_old)); } } - } + } } - + expr* term_graph::mk_app_core (expr *e) { if (is_app(e)) { expr_ref_buffer kids(m); app* a = ::to_app(e); for (expr * arg : *a) { kids.push_back (mk_app(arg)); - } + } app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); m_pinned.push_back(res); return res; @@ -483,44 +481,44 @@ namespace qe { expr_ref term_graph::mk_app(term const &r) { SASSERT(r.is_root()); - + if (r.get_num_args() == 0) { return expr_ref(r.get_expr(), m); } - + expr* res = nullptr; if (m_term2app.find(r.get_id(), res)) { return expr_ref(res, m); } - + res = mk_app_core (r.get_app()); m_term2app.insert(r.get_id(), res); return expr_ref(res, m); - + } expr_ref term_graph::mk_app(expr *a) { term *t = get_term(a); - if (!t) + if (!t) return expr_ref(a, m); - else + else return mk_app(t->get_root()); - + } void term_graph::mk_equalities(term const &t, expr_ref_vector &out) { SASSERT(t.is_root()); expr_ref rep(mk_app(t), m); - + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { expr* mem = mk_app_core(it->get_app()); out.push_back (m.mk_eq (rep, mem)); } } - + void term_graph::mk_all_equalities(term const &t, expr_ref_vector &out) { mk_equalities(t, out); - + for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { expr* a1 = mk_app_core (it->get_app()); for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { @@ -529,22 +527,22 @@ namespace qe { } } } - + void term_graph::reset_marks() { for (term * t : m_terms) { t->set_mark(false); } } - + /// Order of preference for roots of equivalence classes /// XXX This should be factored out to let clients control the preference bool term_graph::term_lt(term const &t1, term const &t2) { - + // prefer constants over applications // prefer uninterpreted constants over values // prefer smaller expressions over larger ones if (t1.get_num_args() == 0 || t2.get_num_args() == 0) { - if (t1.get_num_args() == t2.get_num_args()) { + if (t1.get_num_args() == t2.get_num_args()) { // t1.get_num_args() == t2.get_num_args() == 0 if (m.is_value(t1.get_expr()) == m.is_value(t2.get_expr())) return t1.get_id() < t2.get_id(); @@ -552,7 +550,7 @@ namespace qe { } return t1.get_num_args() < t2.get_num_args(); } - + unsigned sz1 = get_num_exprs(t1.get_expr()); unsigned sz2 = get_num_exprs(t1.get_expr()); return sz1 < sz2; @@ -564,7 +562,7 @@ namespace qe { it->set_mark(true); if (term_lt(*it, *r)) { r = it; } } - + // -- if found something better, make it the new root if (r != &t) { r->mk_root(); @@ -574,12 +572,12 @@ namespace qe { /// Choose better roots for equivalence classes void term_graph::pick_roots() { for (term* t : m_terms) { - if (!t->is_marked() && t->is_root()) + if (!t->is_marked() && t->is_root()) pick_root(*t); } reset_marks(); } - + void term_graph::display(std::ostream &out) { for (term * t : m_terms) { out << mk_pp(t->get_expr(), m) << " is root " << t->is_root() @@ -588,33 +586,33 @@ namespace qe { << "\n"; } } - + void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { pick_roots(); - + for (expr * a : m_lits) { if (is_internalized(a)) { lits.push_back (::to_app(mk_app(a))); } } - + for (term * t : m_terms) { - if (!t->is_root()) + if (!t->is_root()) continue; - else if (all_equalities) - mk_all_equalities (*t, lits); - else + else if (all_equalities) + mk_all_equalities (*t, lits); + else mk_equalities(*t, lits); } } - - + + expr_ref term_graph::to_app() { expr_ref_vector lits(m); to_lits(lits); return mk_and(lits); } - + void term_graph::reset() { m_term2app.reset(); m_pinned.reset(); @@ -624,7 +622,7 @@ namespace qe { m_lits.reset(); m_cg_table.reset(); } - + expr* term_graph::mk_pure(term& t) { expr* e = nullptr; if (m_term2app.find(t.get_id(), e)) return e; @@ -641,7 +639,7 @@ namespace qe { m_term2app.insert(t.get_id(), result); return result; } - + expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { u_map _decls; for (func_decl* f : decls) _decls.insert(f->get_id(), true); @@ -649,27 +647,27 @@ namespace qe { // use work-list + marking to propagate. // - produce equalities over represented classes. // - produce other literals over represented classes - // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) - + // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) + expr_ref_vector result(m); m_term2app.reset(); m_pinned.reset(); - + obj_hashtable eqs; expr_ref eq(m); ptr_vector worklist; for (term * t : m_terms) { worklist.push_back(t); - t->set_mark(true); + t->set_mark(true); } - + while (!worklist.empty()) { term* t = worklist.back(); worklist.pop_back(); t->set_mark(false); - if (m_term2app.contains(t->get_id())) + if (m_term2app.contains(t->get_id())) continue; - if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) + if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) continue; term& root = t->get_root(); @@ -678,7 +676,7 @@ namespace qe { if (!pure) continue; // ensure that the root has a representative - // either by looking up cached version, + // either by looking up cached version, // computing it for the first time, or // inheriting pure. expr* rep = nullptr; @@ -694,7 +692,7 @@ namespace qe { } bool update_rep = false; - // Add equations between pure and rep, + // Add equations between pure and rep, // optionally swap the roles of rep and pure if // pure makes a better representative. if (rep != pure) { @@ -709,7 +707,7 @@ namespace qe { } } - // update the worklist if this is the first + // update the worklist if this is the first // representative or pure was swapped into rep. if (!has_rep || update_rep) { for (term * p : term::parents(root)) { @@ -726,10 +724,10 @@ namespace qe { if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { result.push_back(e); } - } + } // Here we could also walk equivalence classes that contain interpreted values by sort and // extract disequalities bewteen non-unique value representatives. - // these disequalities are implied and can be mined using other means, such as + // these disequalities are implied and can be mined using other means, such as // theory aware core minimization m_term2app.reset(); m_pinned.reset(); From f963fc06f40c6b762f45e22a6fd4aa3a10654f6f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 14:28:24 -0700 Subject: [PATCH 279/364] sketch out euf-solver based on complete projection Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbi.cpp | 66 ++++++++++++++++++++++++++++++++++++---- src/qe/qe_mbi.h | 5 ++- src/qe/qe_term_graph.cpp | 10 ------ 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 0fe2d49c7..d47c2ab2c 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -19,9 +19,12 @@ Revision History: --*/ #include "ast/ast_util.h" +#include "ast/for_each_expr.h" #include "ast/rewriter/bool_rewriter.h" +#include "model/model_evaluator.h" #include "solver/solver.h" #include "qe/qe_mbi.h" +#include "qe/qe_term_graph.h" namespace qe { @@ -87,7 +90,35 @@ namespace qe { // ------------------------------- // euf_mbi, TBD - euf_mbi_plugin::euf_mbi_plugin(solver* s): m(s->get_manager()), m_solver(s) {} + struct euf_mbi_plugin::is_atom_proc { + ast_manager& m; + expr_ref_vector& m_atoms; + is_atom_proc(expr_ref_vector& atoms): m(atoms.m()), m_atoms(atoms) {} + void operator()(app* a) { + if (m.is_eq(a)) { + m_atoms.push_back(a); + } + else if (m.is_bool(a) && a->get_family_id() != m.get_basic_family_id()) { + m_atoms.push_back(a); + } + } + void operator()(expr*) {} + }; + + euf_mbi_plugin::euf_mbi_plugin(solver* s, solver* sNot): + m(s->get_manager()), + m_atoms(m), + m_solver(s), + m_dual_solver(sNot) + { + expr_ref_vector fmls(m); + m_solver->get_assertions(fmls); + expr_fast_mark1 marks; + is_atom_proc proc(m_atoms); + for (expr* e : fmls) { + quick_for_each_expr(proc, marks, e); + } + } mbi_result euf_mbi_plugin::operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { lbool r = m_solver->check_sat(lits); @@ -95,16 +126,39 @@ namespace qe { case l_false: lits.reset(); m_solver->get_unsat_core(lits); + // optionally minimize core using superposition. return mbi_unsat; case l_true: { expr_ref_vector fmls(m); m_solver->get_model(mdl); + model_evaluator mev(*mdl.get()); lits.reset(); - // 1. extract formulas from solver - // 2. extract implicant over formulas - // 3. extract equalities or other assignments over the congruence classes - m_solver->get_assertions(fmls); - NOT_IMPLEMENTED_YET(); + for (expr* e : m_atoms) { + if (mev.is_true(e)) { + lits.push_back(e); + } + else if (mev.is_false(e)) { + lits.push_back(m.mk_not(e)); + } + } + r = m_dual_solver->check_sat(lits); + expr_ref_vector core(m); + term_graph tg(m); + switch (r) { + case l_false: + // use the dual solver to find a 'small' implicant + m_dual_solver->get_unsat_core(core); + // project the implicant onto vars + tg.add_lits(core); + lits.reset(); + lits.append(tg.project(vars, false)); + return mbi_sat; + case l_undef: + return mbi_undef; + case l_true: + UNREACHABLE(); + return mbi_undef; + } return mbi_sat; } default: diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index d58430602..8ecb6532e 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -76,8 +76,11 @@ namespace qe { class euf_mbi_plugin : public mbi_plugin { ast_manager& m; solver_ref m_solver; + solver_ref m_dual_solver; + expr_ref_vector m_atoms; + struct is_atom_proc; public: - euf_mbi_plugin(solver* s); + euf_mbi_plugin(solver* s, solver* sNot); ~euf_mbi_plugin() override {} mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; void block(expr_ref_vector const& lits) override; diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index eaf5a372d..a5e45d389 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -35,7 +35,6 @@ namespace qe { term* m_next; // -- eq class size unsigned m_class_size; - // -- general purpose mark unsigned m_mark:1; // -- general purpose second mark @@ -160,12 +159,10 @@ namespace qe { }; - class arith_term_graph_plugin : public term_graph_plugin { term_graph &m_g; ast_manager &m; arith_util m_arith; - public: arith_term_graph_plugin(term_graph &g) : term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), @@ -177,7 +174,6 @@ namespace qe { expr *e1, *e2; e1 = _e1; e2 = _e2; - if (m_arith.is_zero(e1)) { std::swap(e1, e2); } @@ -245,7 +241,6 @@ namespace qe { } return false; } - expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} bool is_one (expr const * n) const { rational val; @@ -292,12 +287,10 @@ namespace qe { else if (m_arith.is_ge(lit, e1, e2)) { mk_ge_core(e1, e2, res); } - // restore negation if (is_neg) { res = mk_not(m, res); } - return res; } }; @@ -331,7 +324,6 @@ namespace qe { return null_family_id; } } - void term_graph::add_lit(expr *l) { expr_ref lit(m); @@ -509,7 +501,6 @@ namespace qe { void term_graph::mk_equalities(term const &t, expr_ref_vector &out) { SASSERT(t.is_root()); expr_ref rep(mk_app(t), m); - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { expr* mem = mk_app_core(it->get_app()); out.push_back (m.mk_eq (rep, mem)); @@ -606,7 +597,6 @@ namespace qe { } } - expr_ref term_graph::to_app() { expr_ref_vector lits(m); to_lits(lits); From 5ab6d6ca16b081ba1903ab3a18c32e8c821512ac Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 14:31:20 -0700 Subject: [PATCH 280/364] term_le -> term_lt Signed-off-by: Nikolaj Bjorner --- src/qe/qe_term_graph.cpp | 164 +++++++++++++++++++-------------------- src/qe/qe_term_graph.h | 38 ++++----- 2 files changed, 101 insertions(+), 101 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index a5e45d389..4101d6d56 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -41,20 +41,20 @@ namespace qe { unsigned m_mark2:1; // -- is an interpreted constant unsigned m_interpreted:1; - + // -- terms that contain this term as a child ptr_vector m_parents; - + // arguments of term. ptr_vector m_children; - + public: term(expr* v, u_map& app2term) : m_expr(v), - m_root(this), + m_root(this), m_next(this), - m_class_size(1), - m_mark(false), + m_class_size(1), + m_mark(false), m_mark2(false), m_interpreted(false) { if (!is_app()) return; @@ -75,29 +75,29 @@ namespace qe { ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } ptr_vector::const_iterator end() const { return t.m_parents.end(); } }; - + class children { - term const& t; + term const& t; public: children(term const& _t):t(_t) {} children(term const* _t):t(*_t) {} ptr_vector::const_iterator begin() const { return t.m_children.begin(); } ptr_vector::const_iterator end() const { return t.m_children.end(); } }; - + // Congruence table hash function is based on // roots of children and function declaration. - + unsigned get_hash() const { unsigned a, b, c; - a = b = c = get_decl_id(); + a = b = c = get_decl_id(); for (term * ch : children(this)) { a = ch->get_root().get_id(); mix(a, b, c); } return c; } - + static bool cg_eq(term const * t1, term const * t2) { if (t1->get_decl_id() != t2->get_decl_id()) return false; if (t1->m_children.size() != t2->m_children.size()) return false; @@ -106,16 +106,16 @@ namespace qe { } return true; } - + unsigned get_id() const { return m_expr->get_id();} - + unsigned get_decl_id() const { return is_app() ? get_app()->get_decl()->get_id() : m_expr->get_id(); } - + bool is_marked() const {return m_mark;} void set_mark(bool v){m_mark = v;} bool is_marked2() const {return m_mark2;} // NSB: where is this used? void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? - + bool is_interpreted() const {return m_interpreted;} bool is_theory() const { return !is_app() || get_app()->get_family_id() != null_family_id; } void mark_as_interpreted() {m_interpreted=true;} @@ -123,26 +123,26 @@ namespace qe { bool is_app() const {return ::is_app(m_expr);} app *get_app() const {return is_app() ? to_app(m_expr) : nullptr;} unsigned get_num_args() const { return is_app() ? get_app()->get_num_args() : 0; } - + term &get_root() const {return *m_root;} bool is_root() const {return m_root == this;} void set_root(term &r) {m_root = &r;} term &get_next() const {return *m_next;} void add_parent(term* p) { m_parents.push_back(p); } - + unsigned get_class_size() const {return m_class_size;} - + void merge_eq_class(term &b) { std::swap(this->m_next, b.m_next); m_class_size += b.get_class_size(); // -- reset (useful for debugging) b.m_class_size = 0; } - + // -- make this term the root of its equivalence class void mk_root() { if (is_root()) return; - + term *curr = this; do { if (curr->is_root()) { @@ -157,7 +157,7 @@ namespace qe { while (curr != this); } }; - + class arith_term_graph_plugin : public term_graph_plugin { term_graph &m_g; @@ -192,7 +192,7 @@ namespace qe { res = m.mk_eq(e1, e2); return true; } - + app* mk_le_zero(expr *arg) { expr *e1, *e2, *e3; if (m_arith.is_add(arg, e1, e2)) { @@ -222,7 +222,7 @@ namespace qe { } return m_arith.mk_ge(arg, mk_zero()); } - + bool mk_le_core (expr *arg1, expr * arg2, expr_ref &result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) rational n; @@ -246,7 +246,7 @@ namespace qe { rational val; return m_arith.is_numeral (n, val) && val.is_one (); } - + bool mk_ge_core (expr * arg1, expr * arg2, expr_ref &result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) rational n; @@ -265,17 +265,17 @@ namespace qe { } return false; } - + expr_ref process_lit (expr *_lit) override { expr *lit = _lit; expr *e1, *e2; - + // strip negation bool is_neg = m.is_not(lit); if (is_neg) { lit = to_app(to_app(lit)->get_arg(0)); } - + expr_ref res(m); res = lit; if (m.is_eq (lit, e1, e2)) { @@ -302,16 +302,16 @@ namespace qe { term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); } - + term_graph::~term_graph() { reset(); } static family_id get_family_id(ast_manager &m, expr *lit) { - if (m.is_not(lit, lit)) + if (m.is_not(lit, lit)) return get_family_id(m, lit); - expr *a = nullptr, *b = nullptr; + expr *a = nullptr, *b = nullptr; // deal with equality using sort of range if (m.is_eq (lit, a, b)) { return get_sort (a)->get_family_id(); @@ -326,7 +326,7 @@ namespace qe { } void term_graph::add_lit(expr *l) { expr_ref lit(m); - + family_id fid = get_family_id (m, l); term_graph_plugin *pin = m_plugins.get_plugin(fid); if (pin) { @@ -337,16 +337,16 @@ namespace qe { m_lits.push_back(lit); internalize_lit(lit); } - + bool term_graph::is_internalized(expr *a) { return m_app2term.contains(a->get_id()); } - + term* term_graph::get_term(expr *a) { term *res; return m_app2term.find (a->get_id(), res) ? res : nullptr; } - + term *term_graph::mk_term(expr *a) { term * t = alloc(term, a, m_app2term); if (t->get_num_args() == 0 && m.is_unique_value(a)){ @@ -357,8 +357,8 @@ namespace qe { m_app2term.insert(a->get_id(), t); return t; } - - term* term_graph::internalize_term(expr *t) { + + term* term_graph::internalize_term(expr *t) { term* res = get_term(t); if (res) return res; ptr_buffer todo; @@ -373,7 +373,7 @@ namespace qe { unsigned sz = todo.size(); if (is_app(t)) { for (expr * arg : *::to_app(t)) { - if (!get_term(arg)) + if (!get_term(arg)) todo.push_back(arg); } } @@ -394,7 +394,7 @@ namespace qe { void term_graph::internalize_lit(expr* lit) { expr *e1 = nullptr, *e2 = nullptr; - if (m.is_eq (lit, e1, e2)) { + if (m.is_eq (lit, e1, e2)) { internalize_eq (e1, e2); } else { @@ -432,15 +432,15 @@ namespace qe { m_cg_table.erase(p); } } - // make 'a' be the root of the equivalence class of 'b' + // make 'a' be the root of the equivalence class of 'b' b->set_root(*a); for (term *it = &b->get_next(); it != b; it = &it->get_next()) { it->set_root(*a); } - + // merge equivalence classes a->merge_eq_class(*b); - + // Insert parents of b's old equilvalence class into the cg table for (term* p : term::parents(a)) { if (p->is_marked()) { @@ -452,16 +452,16 @@ namespace qe { m_merge.push_back(std::make_pair(p, p_old)); } } - } + } } - + expr* term_graph::mk_app_core (expr *e) { if (is_app(e)) { expr_ref_buffer kids(m); app* a = ::to_app(e); for (expr * arg : *a) { kids.push_back (mk_app(arg)); - } + } app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); m_pinned.push_back(res); return res; @@ -473,29 +473,29 @@ namespace qe { expr_ref term_graph::mk_app(term const &r) { SASSERT(r.is_root()); - + if (r.get_num_args() == 0) { return expr_ref(r.get_expr(), m); } - + expr* res = nullptr; if (m_term2app.find(r.get_id(), res)) { return expr_ref(res, m); } - + res = mk_app_core (r.get_app()); m_term2app.insert(r.get_id(), res); return expr_ref(res, m); - + } expr_ref term_graph::mk_app(expr *a) { term *t = get_term(a); - if (!t) + if (!t) return expr_ref(a, m); - else + else return mk_app(t->get_root()); - + } void term_graph::mk_equalities(term const &t, expr_ref_vector &out) { @@ -506,10 +506,10 @@ namespace qe { out.push_back (m.mk_eq (rep, mem)); } } - + void term_graph::mk_all_equalities(term const &t, expr_ref_vector &out) { mk_equalities(t, out); - + for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { expr* a1 = mk_app_core (it->get_app()); for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { @@ -518,22 +518,22 @@ namespace qe { } } } - + void term_graph::reset_marks() { for (term * t : m_terms) { t->set_mark(false); } } - + /// Order of preference for roots of equivalence classes /// XXX This should be factored out to let clients control the preference bool term_graph::term_lt(term const &t1, term const &t2) { - + // prefer constants over applications // prefer uninterpreted constants over values // prefer smaller expressions over larger ones if (t1.get_num_args() == 0 || t2.get_num_args() == 0) { - if (t1.get_num_args() == t2.get_num_args()) { + if (t1.get_num_args() == t2.get_num_args()) { // t1.get_num_args() == t2.get_num_args() == 0 if (m.is_value(t1.get_expr()) == m.is_value(t2.get_expr())) return t1.get_id() < t2.get_id(); @@ -541,7 +541,7 @@ namespace qe { } return t1.get_num_args() < t2.get_num_args(); } - + unsigned sz1 = get_num_exprs(t1.get_expr()); unsigned sz2 = get_num_exprs(t1.get_expr()); return sz1 < sz2; @@ -553,7 +553,7 @@ namespace qe { it->set_mark(true); if (term_lt(*it, *r)) { r = it; } } - + // -- if found something better, make it the new root if (r != &t) { r->mk_root(); @@ -563,12 +563,12 @@ namespace qe { /// Choose better roots for equivalence classes void term_graph::pick_roots() { for (term* t : m_terms) { - if (!t->is_marked() && t->is_root()) + if (!t->is_marked() && t->is_root()) pick_root(*t); } reset_marks(); } - + void term_graph::display(std::ostream &out) { for (term * t : m_terms) { out << mk_pp(t->get_expr(), m) << " is root " << t->is_root() @@ -577,32 +577,32 @@ namespace qe { << "\n"; } } - + void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { pick_roots(); - + for (expr * a : m_lits) { if (is_internalized(a)) { lits.push_back (::to_app(mk_app(a))); } } - + for (term * t : m_terms) { - if (!t->is_root()) + if (!t->is_root()) continue; - else if (all_equalities) - mk_all_equalities (*t, lits); - else + else if (all_equalities) + mk_all_equalities (*t, lits); + else mk_equalities(*t, lits); } } - + expr_ref term_graph::to_app() { expr_ref_vector lits(m); to_lits(lits); return mk_and(lits); } - + void term_graph::reset() { m_term2app.reset(); m_pinned.reset(); @@ -612,7 +612,7 @@ namespace qe { m_lits.reset(); m_cg_table.reset(); } - + expr* term_graph::mk_pure(term& t) { expr* e = nullptr; if (m_term2app.find(t.get_id(), e)) return e; @@ -629,7 +629,7 @@ namespace qe { m_term2app.insert(t.get_id(), result); return result; } - + expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { u_map _decls; for (func_decl* f : decls) _decls.insert(f->get_id(), true); @@ -637,27 +637,27 @@ namespace qe { // use work-list + marking to propagate. // - produce equalities over represented classes. // - produce other literals over represented classes - // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) - + // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) + expr_ref_vector result(m); m_term2app.reset(); m_pinned.reset(); - + obj_hashtable eqs; expr_ref eq(m); ptr_vector worklist; for (term * t : m_terms) { worklist.push_back(t); - t->set_mark(true); + t->set_mark(true); } - + while (!worklist.empty()) { term* t = worklist.back(); worklist.pop_back(); t->set_mark(false); - if (m_term2app.contains(t->get_id())) + if (m_term2app.contains(t->get_id())) continue; - if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) + if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) continue; term& root = t->get_root(); @@ -714,10 +714,10 @@ namespace qe { if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { result.push_back(e); } - } + } // Here we could also walk equivalence classes that contain interpreted values by sort and // extract disequalities bewteen non-unique value representatives. - // these disequalities are implied and can be mined using other means, such as + // these disequalities are implied and can be mined using other means, such as // theory aware core minimization m_term2app.reset(); m_pinned.reset(); diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index e32f7271c..fbb2ceb16 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -33,11 +33,11 @@ namespace qe { virtual ~term_graph_plugin() {} family_id get_family_id() const {return m_id;} - + /// Process (and potentially augment) a literal virtual expr_ref process_lit (expr *lit) = 0; }; - + class term_graph { struct term_hash { unsigned operator()(term const* t) const; }; @@ -45,62 +45,62 @@ namespace qe { ast_manager & m; ptr_vector m_terms; expr_ref_vector m_lits; // NSB: expr_ref_vector? - u_map m_app2term; + u_map m_app2term; ast_ref_vector m_pinned; u_map m_term2app; plugin_manager m_plugins; ptr_hashtable m_cg_table; vector> m_merge; - + void merge(term &t1, term &t2); void merge_flush(); - + term *mk_term(expr *t); term *get_term(expr *t); - + term *internalize_term(expr *t); void internalize_eq(expr *a1, expr *a2); void internalize_lit(expr *lit); - + bool is_internalized(expr *a); - + bool term_lt(term const &t1, term const &t2); void pick_root (term &t); void pick_roots(); - + void reset_marks(); - + expr* mk_app_core(expr* a); expr_ref mk_app(term const &t); expr* mk_pure(term& t); expr_ref mk_app(expr *a); void mk_equalities(term const &t, expr_ref_vector &out); void mk_all_equalities(term const &t, expr_ref_vector &out); - void display(std::ostream &out); + void display(std::ostream &out); public: term_graph(ast_manager &m); ~term_graph(); - + ast_manager& get_ast_manager() const { return m;} - - void add_lit(expr *lit); + + void add_lit(expr *lit); void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(e); } void add_eq(expr* a, expr* b) { internalize_eq(a, b); } - + void reset(); // deprecate? void to_lits(expr_ref_vector &lits, bool all_equalities = false); - expr_ref to_app(); + expr_ref to_app(); /** - * Return literals obtained by projecting added literals - * onto the vocabulary of decls (if exclude is false) or outside the + * Return literals obtained by projecting added literals + * onto the vocabulary of decls (if exclude is false) or outside the * vocabulary of decls (if exclude is true). */ expr_ref_vector project(func_decl_ref_vector const& decls, bool exclude); - + }; } From 5566b5689c288fc843b76d389b47b364e99cbb4a Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 15:22:44 -0700 Subject: [PATCH 281/364] Silence clang warning --- src/qe/qe_mbi.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index 8ecb6532e..671099015 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -35,20 +35,20 @@ namespace qe { * \brief Utility that works modulo a background state. * - vars * variables to preferrably project onto (other variables would require quantification to fit interpolation signature) - * - lits + * - lits * set of literals to check satisfiability with respect to. * - mdl - * optional model for caller. + * optional model for caller. * on return: * - mbi_sat: * populates mdl with a satisfying state, and lits with implicant for background state. * - mbi_unsat: - * populates lits to be inconsistent with background state. + * populates lits to be inconsistent with background state. * For all practical purposes it is a weakening of lits or even a subset of lits. * - mbi_augment: - * populates lits with strengthening of lits (superset) + * populates lits with strengthening of lits (superset) * - mbi_undef: - * inconclusive, + * inconclusive, */ virtual mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) = 0; @@ -72,12 +72,12 @@ namespace qe { mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; void block(expr_ref_vector const& lits) override; }; - + class euf_mbi_plugin : public mbi_plugin { ast_manager& m; + expr_ref_vector m_atoms; solver_ref m_solver; solver_ref m_dual_solver; - expr_ref_vector m_atoms; struct is_atom_proc; public: euf_mbi_plugin(solver* s, solver* sNot); From 2e616c482b6954b1771ef31864b1c3f0b1604cce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 15:25:20 -0700 Subject: [PATCH 282/364] plugin work Signed-off-by: Nikolaj Bjorner --- src/qe/CMakeLists.txt | 1 + src/qe/qe_solve_plugin.cpp | 305 +++++++++++++++++++++++++++++++++++++ src/qe/qe_solve_plugin.h | 52 +++++++ 3 files changed, 358 insertions(+) create mode 100644 src/qe/qe_solve_plugin.cpp create mode 100644 src/qe/qe_solve_plugin.h diff --git a/src/qe/CMakeLists.txt b/src/qe/CMakeLists.txt index d771ec62e..e9f91ae3e 100644 --- a/src/qe/CMakeLists.txt +++ b/src/qe/CMakeLists.txt @@ -17,6 +17,7 @@ z3_add_component(qe qe_mbp.cpp qe_mbi.cpp qe_sat_tactic.cpp + qe_solve_plugin.cpp qe_tactic.cpp qe_term_graph.cpp qsat.cpp diff --git a/src/qe/qe_solve_plugin.cpp b/src/qe/qe_solve_plugin.cpp new file mode 100644 index 000000000..7a4dd97f4 --- /dev/null +++ b/src/qe/qe_solve_plugin.cpp @@ -0,0 +1,305 @@ +/** +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + qe_solve.cpp + +Abstract: + + Light-weight variable solving plugin model for qe-lite and term_graph. + +Author: + + Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 + +Revision History: + + +--*/ + +#pragma once + +#include "ast/arith_decl_plugin.h" +#include "ast/ast_util.h" +#include "ast/ast_pp.h" +#include "qe/qe_solve_plugin.h" + +namespace qe { + + expr_ref solve_plugin::operator()(expr* lit) { + if (m.is_not(lit, lit)) + return solve(lit, false); + else + return solve(lit, true); + } + + class arith_solve_plugin : public solve_plugin { + arith_util a; + public: + arith_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("arith"), is_var), a(m) {} + + typedef std::pair signed_expr; + + /** + *\brief + * return r * (sum_{(sign,e) \in exprs} sign * e) + */ + expr_ref mk_term(bool is_int, rational const& r, bool sign, svector const& exprs) { + expr_ref_vector result(m); + for (auto const& kv : exprs) { + bool sign2 = kv.first; + expr* e = kv.second; + rational r2(r); + if (sign == sign2) { + r2.neg(); + } + if (!r2.is_one()) { + result.push_back(a.mk_mul(a.mk_numeral(r2, is_int), e)); + } + else { + result.push_back(e); + } + } + return a.mk_add_simplify(result); + } + + /** + * \brief return true if lhs = rhs can be solved as v = t, where v is a variable. + */ + bool solve_arith(expr* lhs, expr* rhs, expr_ref& v, expr_ref& t) { + if (!a.is_int(lhs) && !a.is_real(rhs)) { + return false; + } + rational a_val; + bool is_int = a.is_int(lhs); + svector todo, done; + todo.push_back(std::make_pair(true, lhs)); + todo.push_back(std::make_pair(false, rhs)); + while (!todo.empty()) { + expr* e = todo.back().second; + bool sign = todo.back().first; + todo.pop_back(); + if (a.is_add(e)) { + for (expr* e : *to_app(e)) { + todo.push_back(std::make_pair(sign, e)); + } + } + else if (is_invertible_mul(is_int, e, a_val)) { + done.append(todo); + v = e; + a_val = rational(1)/a_val; + t = mk_term(is_int, a_val, sign, done); + TRACE("qe", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << e << " := " << t << "\n";); + return true; + } + else { + done.push_back(std::make_pair(sign, e)); + } + } + return false; + } + + // can we solve for a variable by ... + bool is_invertible_const(bool is_int, expr* x, rational& a_val) { + expr* y; + if (a.is_uminus(x, y) && is_invertible_const(is_int, y, a_val)) { + a_val.neg(); + return true; + } + else if (a.is_numeral(x, a_val) && !a_val.is_zero()) { + if (!is_int || a_val.is_one() || a_val.is_minus_one()) { + return true; + } + } + return false; + } + + bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { + if (is_variable(arg)) { + a_val = rational(1); + return true; + } + expr* x, *y; + if (a.is_mul(arg, x, y)) { + if (is_variable(x) && is_invertible_const(is_int, y, a_val)) { + arg = x; + return true; + } + if (is_variable(y) && is_invertible_const(is_int, x, a_val)) { + arg = y; + return true; + } + } + return false; + } + + + expr_ref mk_eq_core (expr *_e1, expr *_e2) { + expr *e1, *e2; + e1 = _e1; + e2 = _e2; + + if (a.is_zero(e1)) { + std::swap(e1, e2); + } + // y + -1*x == 0 --> y = x + expr *a0 = 0, *a1 = 0, *x = 0; + if (a.is_zero(e2) && a.is_add(e1, a0, a1)) { + if (a.is_times_minus_one(a1, x)) { + e1 = a0; + e2 = x; + } + else if (a.is_times_minus_one(a0, x)) { + e1 = a1; + e2 = x; + } + } + return expr_ref(m.mk_eq(e1, e2), m); + } + + app* mk_le_zero(expr *arg) { + expr *e1, *e2, *e3; + // XXX currently disabled + if (a.is_add(arg, e1, e2)) { + // e1-e2<=0 --> e1<=e2 + if (a.is_times_minus_one(e2, e3)) { + return a.mk_le(e1, e3); + } + // -e1+e2<=0 --> e2<=e1 + else if (a.is_times_minus_one(e1, e3)) { + return a.mk_le(e2, e3); + } + } + return a.mk_le(arg, mk_zero()); + } + + app* mk_ge_zero(expr *arg) { + expr *e1, *e2, *e3; + // XXX currently disabled + if (a.is_add(arg, e1, e2)) { + // e1-e2>=0 --> e1>=e2 + if (a.is_times_minus_one(e2, e3)) { + return a.mk_ge(e1, e3); + } + // -e1+e2>=0 --> e2>=e1 + else if (a.is_times_minus_one(e1, e3)) { + return a.mk_ge(e2, e3); + } + } + return a.mk_ge(arg, mk_zero()); + } + + bool mk_le_core (expr *arg1, expr * arg2, expr_ref &result) { + // t <= -1 ==> t < 0 ==> ! (t >= 0) + rational n; + if (a.is_int (arg1) && a.is_minus_one (arg2)) { + result = m.mk_not (mk_ge_zero (arg1)); + return true; + } + else if (a.is_zero(arg2)) { + result = mk_le_zero(arg1); + return true; + } + else if (a.is_int(arg1) && a.is_numeral(arg2, n) && n < 0) { + // t <= n ==> t < n + 1 ==> ! (t >= n + 1) + result = m.mk_not(a.mk_ge(arg1, a.mk_numeral(n+1, true))); + return true; + } + return false; + } + + expr * mk_zero () { + return a.mk_numeral (rational (0), true); + } + + bool is_one (expr const * n) const { + rational val; + return a.is_numeral (n, val) && val.is_one (); + } + + bool mk_ge_core (expr * arg1, expr * arg2, expr_ref &result) { + // t >= 1 ==> t > 0 ==> ! (t <= 0) + rational n; + if (a.is_int (arg1) && is_one (arg2)) { + result = m.mk_not (mk_le_zero (arg1)); + return true; + } + else if (a.is_zero(arg2)) { + result = mk_ge_zero(arg1); + return true; + } + else if (a.is_int(arg1) && a.is_numeral(arg2, n) && n > 0) { + // t >= n ==> t > n - 1 ==> ! (t <= n - 1) + result = m.mk_not(a.mk_le(arg1, a.mk_numeral(n-1, true))); + return true; + } + return false; + } + + expr_ref solve(expr* lit, bool is_pos) override { + expr *e1, *e2; + + expr_ref res(lit, m); + if (m.is_eq (lit, e1, e2)) { + res = mk_eq_core(e1, e2); + } + else if (a.is_le(lit, e1, e2)) { + mk_le_core(e1, e2, res); + } + else if (a.is_ge(lit, e1, e2)) { + mk_ge_core(e1, e2, res); + } + + // restore negation + if (!is_pos) { + res = mk_not(m, res); + } + return res; + } + }; + + class bv_solve_plugin : public solve_plugin { + public: + bv_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("bv"), is_var) {} + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + return is_pos ? res : mk_not(res); + } + }; + + class dt_solve_plugin : public solve_plugin { + public: + dt_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("dt"), is_var) {} + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + return is_pos ? res : mk_not(res); + } + }; + + class array_solve_plugin : public solve_plugin { + public: + array_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("array"), is_var) {} + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + return is_pos ? res : mk_not(res); + } + }; + + + solve_plugin* mk_arith_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(arith_solve_plugin, m, is_var); + } + + solve_plugin* mk_dt_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(dt_solve_plugin, m, is_var); + } + + solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(bv_solve_plugin, m, is_var); + } + + solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(array_solve_plugin, m, is_var); + } +} diff --git a/src/qe/qe_solve_plugin.h b/src/qe/qe_solve_plugin.h new file mode 100644 index 000000000..064d3b84d --- /dev/null +++ b/src/qe/qe_solve_plugin.h @@ -0,0 +1,52 @@ +/** +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + qe_solve.h + +Abstract: + + Light-weight variable solving plugin model for qe-lite and term_graph. + +Author: + + Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 + +Revision History: + + +--*/ + +#pragma once + +#include "ast/ast.h" +#include "util/plugin_manager.h" +#include "qe/qe_vartest.h" + +namespace qe { + + class solve_plugin { + protected: + ast_manager& m; + family_id m_id; + is_variable_proc& m_is_var; + + virtual expr_ref solve(expr* atom, bool is_pos) = 0; + bool is_variable(expr* e) const { return m_is_var(e); } + public: + solve_plugin(ast_manager& m, family_id fid, is_variable_proc& is_var) : m(m), m_id(fid), m_is_var(is_var) {} + virtual ~solve_plugin() {} + family_id get_family_id() const { return m_id; } + /// Process (and potentially augment) a literal + expr_ref operator() (expr *lit); + }; + + solve_plugin* mk_arith_solve_plugin(ast_manager& m, is_variable_proc& is_var); + + solve_plugin* mk_dt_solve_plugin(ast_manager& m, is_variable_proc& is_var); + + solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var); + + solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var); +} From e226b39914182af59ee6196228f07668bc4f6924 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 15:29:06 -0700 Subject: [PATCH 283/364] Remove pragma once from cpp --- src/qe/qe_solve_plugin.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/qe/qe_solve_plugin.cpp b/src/qe/qe_solve_plugin.cpp index 7a4dd97f4..bbaf15325 100644 --- a/src/qe/qe_solve_plugin.cpp +++ b/src/qe/qe_solve_plugin.cpp @@ -18,8 +18,6 @@ Revision History: --*/ -#pragma once - #include "ast/arith_decl_plugin.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" From 7714d05c1ab45182f98814918b0766b3dcc9a01a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 18:30:36 -0700 Subject: [PATCH 284/364] fill out qe_solve_plugin functionality Signed-off-by: Nikolaj Bjorner --- src/ast/datatype_decl_plugin.h | 2 + src/qe/qe_lite.cpp | 169 +++++---------------------------- src/qe/qe_solve_plugin.cpp | 125 +++++++++++++++++++++--- src/qe/qe_solve_plugin.h | 6 +- src/util/plugin_manager.h | 5 + 5 files changed, 144 insertions(+), 163 deletions(-) diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 88b50af82..17602efcf 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -358,8 +358,10 @@ namespace datatype { bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); } bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); } bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); } + bool is_constructor(expr* e) const { return is_app(e) && is_constructor(to_app(e)); } bool is_recognizer0(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER);} bool is_is(app * f) const { return is_app_of(f, m_family_id, OP_DT_IS);} + bool is_is(expr * e) const { return is_app(e) && is_is(to_app(e)); } bool is_recognizer(app * f) const { return is_recognizer0(f) || is_is(f); } bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); } bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); } diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index c027d2d07..3b8d41316 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -36,6 +36,7 @@ Revision History: #include "ast/datatype_decl_plugin.h" #include "qe/qe_vartest.h" +#include "qe/qe_solve_plugin.h" namespace eq { @@ -72,6 +73,7 @@ namespace eq { beta_reducer m_subst; expr_ref_vector m_subst_map; expr_ref_vector m_new_exprs; + plugin_manager m_solvers; ptr_vector m_map; int_vector m_pos2var; @@ -221,97 +223,6 @@ namespace eq { } } - bool is_invertible_const(bool is_int, expr* x, rational& a_val) { - expr* y; - if (a.is_uminus(x, y) && is_invertible_const(is_int, y, a_val)) { - a_val.neg(); - return true; - } - else if (a.is_numeral(x, a_val) && !a_val.is_zero()) { - if (!is_int || a_val.is_one() || a_val.is_minus_one()) { - return true; - } - } - return false; - } - - bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { - if (is_variable(arg)) { - a_val = rational(1); - return true; - } - expr* x, *y; - if (a.is_mul(arg, x, y)) { - if (is_variable(x) && is_invertible_const(is_int, y, a_val)) { - arg = x; - return true; - } - if (is_variable(y) && is_invertible_const(is_int, x, a_val)) { - arg = y; - return true; - } - } - return false; - } - - typedef std::pair signed_expr; - - expr_ref solve_arith(bool is_int, rational const& r, bool sign, svector const& exprs) { - expr_ref_vector result(m); - for (unsigned i = 0; i < exprs.size(); ++i) { - bool sign2 = exprs[i].first; - expr* e = exprs[i].second; - rational r2(r); - if (sign == sign2) { - r2.neg(); - } - if (!r2.is_one()) { - result.push_back(a.mk_mul(a.mk_numeral(r2, is_int), e)); - } - else { - result.push_back(e); - } - } - return expr_ref(a.mk_add(result.size(), result.c_ptr()), m); - } - - bool solve_arith(expr* lhs, expr* rhs, ptr_vector& vs, expr_ref_vector& ts) { - if (!a.is_int(lhs) && !a.is_real(rhs)) { - return false; - } - rational a_val; - bool is_int = a.is_int(lhs); - svector todo, done; - todo.push_back(std::make_pair(true, lhs)); - todo.push_back(std::make_pair(false, rhs)); - while (!todo.empty()) { - expr* e = todo.back().second; - bool sign = todo.back().first; - todo.pop_back(); - if (a.is_add(e)) { - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - todo.push_back(std::make_pair(sign, to_app(e)->get_arg(i))); - } - } - else if (is_invertible_mul(is_int, e, a_val)) { - done.append(todo); - vs.push_back(to_var(e)); - a_val = rational(1)/a_val; - ts.push_back(solve_arith(is_int, a_val, sign, done)); - TRACE("qe_lite", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << mk_pp(e, m) << " := " << mk_pp(ts.back(), m) << "\n";); - return true; - } - else { - done.push_back(std::make_pair(sign, e)); - } - } - return false; - } - - bool arith_solve(expr * lhs, expr * rhs, expr * eq, ptr_vector& vs, expr_ref_vector& ts) { - return solve_arith(lhs, rhs, vs, ts); - } - bool trivial_solve(expr* lhs, expr* rhs, expr* eq, ptr_vector& vs, expr_ref_vector& ts) { if (!is_variable(lhs)) { std::swap(lhs, rhs); @@ -343,65 +254,25 @@ namespace eq { */ bool is_var_eq(expr * e, ptr_vector& vs, expr_ref_vector & ts) { - expr* lhs, *rhs; - var* v; + expr* lhs = nullptr, *rhs = nullptr; // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases + if (m.is_eq(e, lhs, rhs) && trivial_solve(lhs, rhs, e, vs, ts)) { + return true; + } + family_id fid = get_sort(e)->get_family_id(); if (m.is_eq(e, lhs, rhs)) { - // (iff (not VAR) t) (iff t (not VAR)) cases - if (!is_variable(lhs) && !is_variable(rhs) && m.is_bool(lhs)) { - if (!is_neg_var(m, lhs, v)) { - std::swap(lhs, rhs); - } - if (!is_neg_var(m, lhs, v)) { - return false; - } - vs.push_back(v); - ts.push_back(m.mk_not(rhs)); - TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); + fid = get_sort(lhs)->get_family_id(); + } + qe::solve_plugin* p = m_solvers.get_plugin(fid); + if (p) { + expr_ref res = (*p)(e); + if (res != e && m.is_eq(res, lhs, rhs) && is_variable(lhs)) { + vs.push_back(to_var(lhs)); + ts.push_back(rhs); return true; } - if (trivial_solve(lhs, rhs, e, vs, ts)) { - return true; - } - if (arith_solve(lhs, rhs, e, vs, ts)) { - return true; - } - return false; } - - // (ite cond (= VAR t) (= VAR t2)) case - expr* cond, *e2, *e3; - if (m.is_ite(e, cond, e2, e3)) { - if (is_var_eq(e2, vs, ts)) { - expr_ref_vector ts2(m); - ptr_vector vs2; - if (is_var_eq(e3, vs2, ts2) && same_vars(vs, vs2)) { - for (unsigned i = 0; i < vs.size(); ++i) { - ts[i] = m.mk_ite(cond, ts[i].get(), ts2[i].get()); - } - return true; - } - } - return false; - } - - // VAR = true case - if (is_variable(e)) { - ts.push_back(m.mk_true()); - vs.push_back(to_var(e)); - TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); - return true; - } - - // VAR = false case - if (is_neg_var(m, e, v)) { - ts.push_back(m.mk_false()); - vs.push_back(v); - TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); - return true; - } - return false; } @@ -785,9 +656,15 @@ namespace eq { m_new_exprs(m), m_new_args(m), m_rewriter(m), - m_params(p) {} + m_params(p) { + } - void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} + void set_is_variable_proc(is_variable_proc& proc) { + m_is_variable = &proc; + m_solvers.reset(); + m_solvers.register_plugin(qe::mk_arith_solve_plugin(m, proc)); + m_solvers.register_plugin(qe::mk_basic_solve_plugin(m, proc)); + } void operator()(quantifier * q, expr_ref & r, proof_ref & pr) { TRACE("qe_lite", tout << mk_pp(q, m) << "\n";); diff --git a/src/qe/qe_solve_plugin.cpp b/src/qe/qe_solve_plugin.cpp index bbaf15325..ad4a03b5d 100644 --- a/src/qe/qe_solve_plugin.cpp +++ b/src/qe/qe_solve_plugin.cpp @@ -19,6 +19,7 @@ Revision History: --*/ #include "ast/arith_decl_plugin.h" +#include "ast/datatype_decl_plugin.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "qe/qe_solve_plugin.h" @@ -65,7 +66,7 @@ namespace qe { /** * \brief return true if lhs = rhs can be solved as v = t, where v is a variable. */ - bool solve_arith(expr* lhs, expr* rhs, expr_ref& v, expr_ref& t) { + bool solve(expr* lhs, expr* rhs, expr_ref& v, expr_ref& t) { if (!a.is_int(lhs) && !a.is_real(rhs)) { return false; } @@ -83,6 +84,16 @@ namespace qe { todo.push_back(std::make_pair(sign, e)); } } + else if (a.is_sub(e)) { + app* sub = to_app(e); + todo.push_back(std::make_pair(sign, sub->get_arg(0))); + for (unsigned i = 1; i < sub->get_num_args(); ++i) { + todo.push_back(std::make_pair(!sign, sub->get_arg(i))); + } + } + else if (a.is_uminus(e)) { + todo.push_back(std::make_pair(!sign, to_app(e)->get_arg(0))); + } else if (is_invertible_mul(is_int, e, a_val)) { done.append(todo); v = e; @@ -98,7 +109,7 @@ namespace qe { return false; } - // can we solve for a variable by ... + // is x a constant and can we safely divide by x to solve for a variable? bool is_invertible_const(bool is_int, expr* x, rational& a_val) { expr* y; if (a.is_uminus(x, y) && is_invertible_const(is_int, y, a_val)) { @@ -113,6 +124,8 @@ namespace qe { return false; } + // is arg of the form a_val * v, where a_val + // is a constant that we can safely divide by. bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { if (is_variable(arg)) { a_val = rational(1); @@ -133,10 +146,11 @@ namespace qe { } - expr_ref mk_eq_core (expr *_e1, expr *_e2) { - expr *e1, *e2; - e1 = _e1; - e2 = _e2; + expr_ref mk_eq_core (expr *e1, expr *e2) { + expr_ref v(m), t(m); + if (solve(e1, e2, v, t)) { + return expr_ref(m.mk_eq(v, t), m); + } if (a.is_zero(e1)) { std::swap(e1, e2); @@ -257,6 +271,90 @@ namespace qe { } }; + class basic_solve_plugin : public solve_plugin { + public: + basic_solve_plugin(ast_manager& m, is_variable_proc& is_var): + solve_plugin(m, m.get_basic_family_id(), is_var) {} + + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + expr* lhs = nullptr, *rhs = nullptr, *n = nullptr; + if (m.is_eq(atom, lhs, rhs)) { + if (m.is_not(lhs, n) && is_variable(n)) { + res = m.mk_eq(n, mk_not(m, rhs)); + } + else if (m.is_not(rhs, n) && is_variable(n)) { + res = m.mk_eq(n, mk_not(m, lhs)); + } + else if (is_variable(rhs) && !is_variable(lhs)) { + res = m.mk_eq(rhs, lhs); + } + } + // (ite cond (= VAR t) (= VAR t2)) case + expr* cond = nullptr, *th = nullptr, *el = nullptr; + if (m.is_ite(atom, cond, th, el)) { + expr_ref r1 = solve(th, true); + expr_ref r2 = solve(el, true); + expr* v1 = nullptr, *t1 = nullptr, *v2 = nullptr, *t2 = nullptr; + if (m.is_eq(r1, v1, t1) && m.is_eq(r2, v2, t2) && v1 == v2) { + res = m.mk_eq(v1, m.mk_ite(cond, t1, t2)); + } + } + + if (is_variable(atom) && m.is_bool(atom)) { + res = m.mk_eq(atom, m.mk_bool_val(is_pos)); + return res; + } + + return is_pos ? res : mk_not(res); + } + }; + + class dt_solve_plugin : public solve_plugin { + datatype_util dt; + public: + dt_solve_plugin(ast_manager& m, is_variable_proc& is_var): + solve_plugin(m, m.get_family_id("datatype"), is_var), + dt(m) {} + + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + expr* lhs = nullptr, *rhs = nullptr; + if (m.is_eq(atom, lhs, rhs)) { + if (dt.is_constructor(rhs)) { + std::swap(lhs, rhs); + } + if (dt.is_constructor(lhs) && dt.is_constructor(rhs)) { + app* l = to_app(lhs), *r = to_app(rhs); + if (l->get_decl() == r->get_decl()) { + expr_ref_vector eqs(m); + for (unsigned i = 0, sz = l->get_num_args(); i < sz; ++i) { + eqs.push_back(m.mk_eq(l->get_arg(i), r->get_arg(i))); + } + res = mk_and(eqs); + } + else { + res = m.mk_false(); + } + } + else if (dt.is_constructor(lhs)) { + app* l = to_app(lhs); + func_decl* d = l->get_decl(); + expr_ref_vector conjs(m); + conjs.push_back(dt.mk_is(d, rhs)); + ptr_vector const& acc = *dt.get_constructor_accessors(d); + for (unsigned i = 0; i < acc.size(); ++i) { + conjs.push_back(m.mk_eq(l->get_arg(i), m.mk_app(acc[i], rhs))); + } + res = mk_and(conjs); + } + } + // TBD: can also solve for is_nil(x) by x = nil + // + return is_pos ? res : mk_not(res); + } + }; + class bv_solve_plugin : public solve_plugin { public: bv_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("bv"), is_var) {} @@ -266,15 +364,6 @@ namespace qe { } }; - class dt_solve_plugin : public solve_plugin { - public: - dt_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("dt"), is_var) {} - expr_ref solve(expr *atom, bool is_pos) override { - expr_ref res(atom, m); - return is_pos ? res : mk_not(res); - } - }; - class array_solve_plugin : public solve_plugin { public: array_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("array"), is_var) {} @@ -284,6 +373,9 @@ namespace qe { } }; + solve_plugin* mk_basic_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(basic_solve_plugin, m, is_var); + } solve_plugin* mk_arith_solve_plugin(ast_manager& m, is_variable_proc& is_var) { return alloc(arith_solve_plugin, m, is_var); @@ -293,6 +385,7 @@ namespace qe { return alloc(dt_solve_plugin, m, is_var); } +#if 0 solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var) { return alloc(bv_solve_plugin, m, is_var); } @@ -300,4 +393,6 @@ namespace qe { solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var) { return alloc(array_solve_plugin, m, is_var); } +#endif + } diff --git a/src/qe/qe_solve_plugin.h b/src/qe/qe_solve_plugin.h index 064d3b84d..571d568f1 100644 --- a/src/qe/qe_solve_plugin.h +++ b/src/qe/qe_solve_plugin.h @@ -42,11 +42,13 @@ namespace qe { expr_ref operator() (expr *lit); }; + solve_plugin* mk_basic_solve_plugin(ast_manager& m, is_variable_proc& is_var); + solve_plugin* mk_arith_solve_plugin(ast_manager& m, is_variable_proc& is_var); solve_plugin* mk_dt_solve_plugin(ast_manager& m, is_variable_proc& is_var); - solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var); + // solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var); - solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var); + // solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var); } diff --git a/src/util/plugin_manager.h b/src/util/plugin_manager.h index c8908fd57..2c4223eeb 100644 --- a/src/util/plugin_manager.h +++ b/src/util/plugin_manager.h @@ -27,7 +27,12 @@ class plugin_manager { ptr_vector m_plugins; public: ~plugin_manager() { + reset(); + } + + void reset() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); + release(); } /** From 771d3b1349a255e2653023cfccf3a862f81d868d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 17:38:14 -0700 Subject: [PATCH 285/364] wip: term_graph::project and term_graph::solve --- src/qe/qe_term_graph.cpp | 134 ++++++++++++++++++++++++++++++++------- src/qe/qe_term_graph.h | 43 +++++++------ 2 files changed, 136 insertions(+), 41 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 4101d6d56..eccb7669f 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -22,6 +22,7 @@ Notes: #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/for_each_expr.h" +#include "ast/occurs.h" #include "qe/qe_term_graph.h" namespace qe { @@ -64,9 +65,9 @@ namespace qe { m_children.push_back(t); } } - + ~term() {} - + class parents { term const& t; public: @@ -167,9 +168,9 @@ namespace qe { arith_term_graph_plugin(term_graph &g) : term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} - + virtual ~arith_term_graph_plugin() {} - + bool mk_eq_core (expr *_e1, expr *_e2, expr_ref &res) { expr *e1, *e2; e1 = _e1; @@ -384,7 +385,7 @@ namespace qe { SASSERT(res); return res; } - + void term_graph::internalize_eq(expr *a1, expr* a2) { SASSERT(m_merge.empty()); merge(*internalize_term(a1), *internalize_term(a2)); @@ -414,17 +415,17 @@ namespace qe { void term_graph::merge(term &t1, term &t2) { // -- merge might invalidate term2app cache m_term2app.reset(); - m_pinned.reset(); - + m_pinned.reset(); + term *a = &t1.get_root(); term *b = &t2.get_root(); - + if (a == b) return; - + if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } - + // Remove parents of it from the cg table. for (term* p : term::parents(b)) { if (!p->is_marked()) { @@ -630,7 +631,9 @@ namespace qe { return result; } - expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { + void term_graph::project_core(func_decl_ref_vector const& decls, + bool exclude, + expr_ref_vector &result) { u_map _decls; for (func_decl* f : decls) _decls.insert(f->get_id(), true); // - propagate representatives up over parents. @@ -639,10 +642,6 @@ namespace qe { // - produce other literals over represented classes // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) - expr_ref_vector result(m); - m_term2app.reset(); - m_pinned.reset(); - obj_hashtable eqs; expr_ref eq(m); ptr_vector worklist; @@ -666,7 +665,7 @@ namespace qe { if (!pure) continue; // ensure that the root has a representative - // either by looking up cached version, + // either by looking up cached version, // computing it for the first time, or // inheriting pure. expr* rep = nullptr; @@ -682,7 +681,10 @@ namespace qe { } bool update_rep = false; - // Add equations between pure and rep, + // AG: can rep be null at this point? + // AG: why mk_pure(root) is guaranteed to return non-null? + + // Add equations between pure and rep, // optionally swap the roles of rep and pure if // pure makes a better representative. if (rep != pure) { @@ -697,7 +699,7 @@ namespace qe { } } - // update the worklist if this is the first + // update the worklist if this is the first // representative or pure was swapped into rep. if (!has_rep || update_rep) { for (term * p : term::parents(root)) { @@ -709,19 +711,107 @@ namespace qe { } } } + // Here we could also walk equivalence classes that contain interpreted values by sort and + // extract disequalities bewteen non-unique value representatives. + // these disequalities are implied and can be mined using other means, such as + // theory aware core minimization + reset_marks(); + } + + expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { + expr_ref_vector result(m); + m_term2app.reset(); + m_pinned.reset(); + project_core(decls, exclude, result); // walk other predicates than equalities for (expr* e : m_lits) { if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { result.push_back(e); } } - // Here we could also walk equivalence classes that contain interpreted values by sort and - // extract disequalities bewteen non-unique value representatives. - // these disequalities are implied and can be mined using other means, such as - // theory aware core minimization + m_term2app.reset(); m_pinned.reset(); + return result; + } + + bool term_graph::is_solved_eq(expr *_lhs, expr* _rhs) { + if (!is_app(_lhs) || !is_app(_rhs)) return false; + app *lhs, *rhs; + lhs = ::to_app(_lhs); + rhs = ::to_app(_rhs); + + if (rhs->get_num_args() > 0) return false; + if (rhs->get_family_id() != null_family_id) return false; + + return !occurs(rhs, lhs); + } + void term_graph::solve_core(func_decl_ref_vector const &decls, bool exclude, + expr_ref_vector &result) { + u_map _decls; + for (func_decl* f : decls) _decls.insert(f->get_id(), true); + obj_hashtable eqs; + expr_ref eq(m); + ptr_vector worklist; + for (term * t : m_terms) { + worklist.push_back(t); + t->set_mark(true); + } + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; + term &root = t->get_root(); + bool has_rep = m_term2app.contains(root.get_id()); + expr *pure = mk_pure(*t); + if (!pure) continue; + + if (has_rep) { + // add equality if needed + expr* rep = m_term2app.find(root.get_id()); + if (exclude != _decls.contains(t->get_decl_id()) || !is_solved_eq(rep, pure)) { + eq = m.mk_eq(rep, pure); + if (!eqs.contains(eq)) { + eqs.insert(eq); + result.push_back(eq); + } + } + } + else { + // let pure be the representative + m_term2app.insert(root.get_id(), pure); + // schedule parents of root to be processed again + for (term * p : term::parents(root)) { + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } + } + SASSERT(false); + // TBD: deal with root + } + } + reset_marks(); + } + + + expr_ref_vector term_graph::solve(func_decl_ref_vector const &decls, bool exclude) { + expr_ref_vector result(m); + m_term2app.reset(); + m_pinned.reset(); + project_core(decls, exclude, result); + solve_core(decls, exclude, result); + // walk other predicates than equalities + for (expr* e : m_lits) { + if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { + result.push_back(e); + } + } + m_term2app.reset(); + m_pinned.reset(); return result; } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index fbb2ceb16..f3564f0e9 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -33,11 +33,11 @@ namespace qe { virtual ~term_graph_plugin() {} family_id get_family_id() const {return m_id;} - + /// Process (and potentially augment) a literal virtual expr_ref process_lit (expr *lit) = 0; }; - + class term_graph { struct term_hash { unsigned operator()(term const* t) const; }; @@ -45,62 +45,67 @@ namespace qe { ast_manager & m; ptr_vector m_terms; expr_ref_vector m_lits; // NSB: expr_ref_vector? - u_map m_app2term; + u_map m_app2term; ast_ref_vector m_pinned; u_map m_term2app; plugin_manager m_plugins; ptr_hashtable m_cg_table; vector> m_merge; - + void merge(term &t1, term &t2); void merge_flush(); - + term *mk_term(expr *t); term *get_term(expr *t); - + term *internalize_term(expr *t); void internalize_eq(expr *a1, expr *a2); void internalize_lit(expr *lit); - + bool is_internalized(expr *a); - + bool term_lt(term const &t1, term const &t2); void pick_root (term &t); void pick_roots(); - + void reset_marks(); - + expr* mk_app_core(expr* a); expr_ref mk_app(term const &t); expr* mk_pure(term& t); expr_ref mk_app(expr *a); void mk_equalities(term const &t, expr_ref_vector &out); void mk_all_equalities(term const &t, expr_ref_vector &out); - void display(std::ostream &out); + void display(std::ostream &out); + void project_core(func_decl_ref_vector const &decls, bool exclude, expr_ref_vector &result); + void solve_core(func_decl_ref_vector const &decls, bool exclude, expr_ref_vector &result); + bool is_solved_eq(expr *lhs, expr *rhs); public: term_graph(ast_manager &m); ~term_graph(); - + ast_manager& get_ast_manager() const { return m;} - - void add_lit(expr *lit); + + void add_lit(expr *lit); void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(e); } void add_eq(expr* a, expr* b) { internalize_eq(a, b); } - + void reset(); // deprecate? void to_lits(expr_ref_vector &lits, bool all_equalities = false); - expr_ref to_app(); + expr_ref to_app(); /** - * Return literals obtained by projecting added literals - * onto the vocabulary of decls (if exclude is false) or outside the + * Return literals obtained by projecting added literals + * onto the vocabulary of decls (if exclude is false) or outside the * vocabulary of decls (if exclude is true). */ expr_ref_vector project(func_decl_ref_vector const& decls, bool exclude); - + expr_ref_vector solve(func_decl_ref_vector const& decls, bool exclude); + + }; } From 144d8df5d5476f10df68d2a2d7bbfeb11bea4a0d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 21:16:49 -0700 Subject: [PATCH 286/364] Rewrite term_graph::project and term_graph::solve --- src/qe/qe_term_graph.cpp | 407 +++++++++++++++++++++------------------ src/qe/qe_term_graph.h | 3 + 2 files changed, 226 insertions(+), 184 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index eccb7669f..2dc9bd739 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -614,205 +614,244 @@ namespace qe { m_cg_table.reset(); } - expr* term_graph::mk_pure(term& t) { - expr* e = nullptr; - if (m_term2app.find(t.get_id(), e)) return e; - e = t.get_expr(); - if (!is_app(e)) return nullptr; - app* a = ::to_app(e); - expr_ref_vector kids(m); - for (term* ch : term::children(t)) { - if (!m_term2app.find(ch->get_root().get_id(), e)) return nullptr; - kids.push_back(e); - } - expr* result = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); - m_pinned.push_back(result); - m_term2app.insert(t.get_id(), result); - return result; - } + namespace { + class projector { + term_graph &m_tg; + ast_manager &m; + u_map m_term2app; + u_map m_root2rep; + u_map m_decls; + bool m_exclude; - void term_graph::project_core(func_decl_ref_vector const& decls, - bool exclude, - expr_ref_vector &result) { - u_map _decls; - for (func_decl* f : decls) _decls.insert(f->get_id(), true); - // - propagate representatives up over parents. - // use work-list + marking to propagate. - // - produce equalities over represented classes. - // - produce other literals over represented classes - // (walk disequalities in m_lits and represent lhs/rhs over decls or excluding decls) + expr_ref_vector m_pinned; // tracks expr in the maps - obj_hashtable eqs; - expr_ref eq(m); - ptr_vector worklist; - for (term * t : m_terms) { - worklist.push_back(t); - t->set_mark(true); - } - - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); - t->set_mark(false); - if (m_term2app.contains(t->get_id())) - continue; - if (!t->is_theory() && exclude == _decls.contains(t->get_decl_id())) - continue; - - term& root = t->get_root(); - bool has_rep = m_term2app.contains(root.get_id()); - expr* pure = mk_pure(*t); - if (!pure) continue; - - // ensure that the root has a representative - // either by looking up cached version, - // computing it for the first time, or - // inheriting pure. - expr* rep = nullptr; - if (root.is_theory() || exclude != _decls.contains(root.get_decl_id())) { - rep = mk_pure(root); - } - else if (has_rep) { - rep = m_term2app.find(root.get_id()); - } - else { - rep = pure; - m_term2app.insert(root.get_id(), pure); - } - bool update_rep = false; - - // AG: can rep be null at this point? - // AG: why mk_pure(root) is guaranteed to return non-null? - - // Add equations between pure and rep, - // optionally swap the roles of rep and pure if - // pure makes a better representative. - if (rep != pure) { - if (m.is_unique_value(rep) && !m.is_unique_value(pure)) { - m_term2app.insert(root.get_id(), pure); - update_rep = true; + expr* mk_pure(term& t) { + expr* e = nullptr; + if (m_term2app.find(t.get_id(), e)) return e; + e = t.get_expr(); + if (!is_app(e)) return nullptr; + app* a = ::to_app(e); + expr_ref_buffer kids(m); + for (term* ch : term::children(t)) { + if (!m_root2rep.find(ch->get_root().get_id(), e)) return nullptr; + kids.push_back(e); } - eq = m.mk_eq(rep, pure); - if (!eqs.contains(eq)) { - eqs.insert(eq); - result.push_back(eq); + expr* pure = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); + m_pinned.push_back(pure); + m_term2app.insert(t.get_id(), pure); + return pure; + } + + bool is_better_rep(expr *t1, expr *t2) { + if (!t2) return t1; + return m.is_unique_value(t1) && !m.is_unique_value(t2); + } + + void purify() { + // - propagate representatives up over parents. + // use work-list + marking to propagate. + // - produce equalities over represented classes. + // - produce other literals over represented classes + // (walk disequalities in m_lits and represent + // lhs/rhs over decls or excluding decls) + + ptr_vector worklist; + for (term * t : m_tg.m_terms) { + worklist.push_back(t); + t->set_mark(true); + } + + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; + if (!t->is_theory() && is_projected(*t)) + continue; + + expr* pure = mk_pure(*t); + if (!pure) continue; + + m_term2app.insert(t->get_id(), pure); + expr* rep = nullptr; + // ensure that the root has a representative + m_root2rep.find(t->get_root().get_id(), rep); + + // update rep with pure if it is better + if (pure != rep && is_better_rep(pure, rep)) { + m_root2rep.insert(t->get_root().get_id(), pure); + for (term * p : term::parents(t->get_root())) { + m_term2app.remove(p->get_id()); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } + } + } + } + + // Here we could also walk equivalence classes that + // contain interpreted values by sort and extract + // disequalities bewteen non-unique value + // representatives. these disequalities are implied + // and can be mined using other means, such as theory + // aware core minimization + m_tg.reset_marks(); + } + + void solve() { + ptr_vector worklist; + for (term * t : m_tg.m_terms) { + // skip pure terms + if (m_term2app.contains(t->get_id())) continue; + worklist.push_back(t); + t->set_mark(true); + } + + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; + + expr* pure = mk_pure(*t); + if (!pure) continue; + + m_term2app.insert(t->get_id(), pure); + expr* rep = nullptr; + // ensure that the root has a representative + m_root2rep.find(t->get_root().get_id(), rep); + + if (!rep) { + m_root2rep.insert(t->get_root().get_id(), pure); + for (term * p : term::parents(t->get_root())) { + SASSERT(!m_term2app.contains(p->get_id())); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } + } + } + } + m_tg.reset_marks(); + } + + bool find_app(term &t, expr *&res) { + return m_root2rep.find(t.get_root().get_id(), res); + } + + bool find_app(expr *lit, expr *&res) { + return m_root2rep.find(m_tg.get_term(lit)->get_root().get_id(), res); + } + + void mk_lits(expr_ref_vector &res) { + expr *e = nullptr; + for (auto *lit : m_tg.m_lits) { + if (!m.is_eq(lit) && find_app(lit, e)) + res.push_back(e); } } - // update the worklist if this is the first - // representative or pure was swapped into rep. - if (!has_rep || update_rep) { - for (term * p : term::parents(root)) { - if (update_rep) m_term2app.remove(p->get_id()); - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); + void mk_pure_equalities(const term &t, expr_ref_vector &res) { + expr *rep = nullptr; + if (!m_root2rep.find(t.get_id(), rep)) return; + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + expr* member = mk_pure(*it); + if (member) + res.push_back (m.mk_eq (rep, member)); + } + } + + bool is_projected(const term &t) { + return m_exclude == m_decls.contains(t.get_decl_id()); + } + + void mk_unpure_equalities(const term &t, expr_ref_vector &res) { + expr *rep = nullptr; + if (!m_root2rep.find(t.get_id(), rep)) return; + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + expr* member = mk_pure(*it); + SASSERT(member); + if (!is_projected(*it) || !is_solved_eq(rep, member)) { + res.push_back(m.mk_eq(rep, member)); } } } - } - // Here we could also walk equivalence classes that contain interpreted values by sort and - // extract disequalities bewteen non-unique value representatives. - // these disequalities are implied and can be mined using other means, such as - // theory aware core minimization - reset_marks(); + + void mk_equalities(bool pure, expr_ref_vector &res) { + for (term *t : m_tg.m_terms) { + if (!t->is_root()) continue; + if (!m_root2rep.contains(t->get_id())) continue; + if (pure) + mk_pure_equalities(*t, res); + else + mk_unpure_equalities(*t, res); + } + } + + void mk_pure_equalities(expr_ref_vector &res) { + return mk_equalities(true, res); + } + + void mk_unpure_equalities(expr_ref_vector &res) { + return mk_equalities(false, res); + } + + bool is_solved_eq(expr *_lhs, expr* _rhs) { + if (!is_app(_lhs) || !is_app(_rhs)) return false; + app *lhs, *rhs; + lhs = ::to_app(_lhs); + rhs = ::to_app(_rhs); + + if (rhs->get_num_args() > 0) return false; + if (rhs->get_family_id() != null_family_id) return false; + + return !occurs(rhs, lhs); + } + + public: + projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} + + void reset() { + m_tg.reset_marks(); + m_term2app.reset(); + m_root2rep.reset(); + m_decls.reset(); + m_pinned.reset(); + } + expr_ref_vector project(func_decl_ref_vector const &decls, bool exclude) { + expr_ref_vector res(m); + m_exclude = exclude; + for (auto *d : decls) {m_decls.insert(d->get_id(), true);} + purify(); + mk_lits(res); + mk_pure_equalities(res); + reset(); + return res; + } + expr_ref_vector solve(func_decl_ref_vector const &decls, bool exclude) { + expr_ref_vector res(m); + m_exclude = exclude; + purify(); + solve(); + mk_lits(res); + mk_unpure_equalities(res); + reset(); + return res; + } + }; } expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { - expr_ref_vector result(m); - m_term2app.reset(); - m_pinned.reset(); - project_core(decls, exclude, result); - // walk other predicates than equalities - for (expr* e : m_lits) { - if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { - result.push_back(e); - } - } - - m_term2app.reset(); - m_pinned.reset(); - return result; + projector p(*this); + return p.project(decls, exclude); } - bool term_graph::is_solved_eq(expr *_lhs, expr* _rhs) { - if (!is_app(_lhs) || !is_app(_rhs)) return false; - app *lhs, *rhs; - lhs = ::to_app(_lhs); - rhs = ::to_app(_rhs); - - if (rhs->get_num_args() > 0) return false; - if (rhs->get_family_id() != null_family_id) return false; - - return !occurs(rhs, lhs); - } - void term_graph::solve_core(func_decl_ref_vector const &decls, bool exclude, - expr_ref_vector &result) { - u_map _decls; - for (func_decl* f : decls) _decls.insert(f->get_id(), true); - obj_hashtable eqs; - expr_ref eq(m); - ptr_vector worklist; - for (term * t : m_terms) { - worklist.push_back(t); - t->set_mark(true); - } - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); - t->set_mark(false); - if (m_term2app.contains(t->get_id())) - continue; - term &root = t->get_root(); - bool has_rep = m_term2app.contains(root.get_id()); - expr *pure = mk_pure(*t); - if (!pure) continue; - - if (has_rep) { - // add equality if needed - expr* rep = m_term2app.find(root.get_id()); - if (exclude != _decls.contains(t->get_decl_id()) || !is_solved_eq(rep, pure)) { - eq = m.mk_eq(rep, pure); - if (!eqs.contains(eq)) { - eqs.insert(eq); - result.push_back(eq); - } - } - } - else { - // let pure be the representative - m_term2app.insert(root.get_id(), pure); - // schedule parents of root to be processed again - for (term * p : term::parents(root)) { - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); - } - } - SASSERT(false); - // TBD: deal with root - } - } - - reset_marks(); - } - - expr_ref_vector term_graph::solve(func_decl_ref_vector const &decls, bool exclude) { - expr_ref_vector result(m); - m_term2app.reset(); - m_pinned.reset(); - project_core(decls, exclude, result); - solve_core(decls, exclude, result); - // walk other predicates than equalities - for (expr* e : m_lits) { - if (!m.is_eq(e) && m_term2app.find(get_term(e)->get_root().get_id(), e)) { - result.push_back(e); - } - } - m_term2app.reset(); - m_pinned.reset(); - return result; + projector p(*this); + return p.solve(decls, exclude); } } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index f3564f0e9..210941dac 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -26,6 +26,8 @@ namespace qe { class term; + namespace {class projector;} + class term_graph_plugin { family_id m_id; public: @@ -40,6 +42,7 @@ namespace qe { class term_graph { + friend class projector; struct term_hash { unsigned operator()(term const* t) const; }; struct term_eq { bool operator()(term const* a, term const* b) const; }; ast_manager & m; From e0aaf4452bfaaa445b6e4fbd78f7bd60481b2f1c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 17:38:14 -0700 Subject: [PATCH 287/364] wip: term_graph::project and term_graph::solve --- src/qe/qe_term_graph.cpp | 120 +++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 2dc9bd739..40f3998e7 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -65,9 +65,9 @@ namespace qe { m_children.push_back(t); } } - + ~term() {} - + class parents { term const& t; public: @@ -168,9 +168,9 @@ namespace qe { arith_term_graph_plugin(term_graph &g) : term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} - + virtual ~arith_term_graph_plugin() {} - + bool mk_eq_core (expr *_e1, expr *_e2, expr_ref &res) { expr *e1, *e2; e1 = _e1; @@ -385,7 +385,7 @@ namespace qe { SASSERT(res); return res; } - + void term_graph::internalize_eq(expr *a1, expr* a2) { SASSERT(m_merge.empty()); merge(*internalize_term(a1), *internalize_term(a2)); @@ -415,17 +415,17 @@ namespace qe { void term_graph::merge(term &t1, term &t2) { // -- merge might invalidate term2app cache m_term2app.reset(); - m_pinned.reset(); - + m_pinned.reset(); + term *a = &t1.get_root(); term *b = &t2.get_root(); - + if (a == b) return; - + if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } - + // Remove parents of it from the cg table. for (term* p : term::parents(b)) { if (!p->is_marked()) { @@ -626,21 +626,21 @@ namespace qe { expr_ref_vector m_pinned; // tracks expr in the maps expr* mk_pure(term& t) { - expr* e = nullptr; - if (m_term2app.find(t.get_id(), e)) return e; - e = t.get_expr(); - if (!is_app(e)) return nullptr; - app* a = ::to_app(e); + expr* e = nullptr; + if (m_term2app.find(t.get_id(), e)) return e; + e = t.get_expr(); + if (!is_app(e)) return nullptr; + app* a = ::to_app(e); expr_ref_buffer kids(m); - for (term* ch : term::children(t)) { + for (term* ch : term::children(t)) { if (!m_root2rep.find(ch->get_root().get_id(), e)) return nullptr; - kids.push_back(e); - } + kids.push_back(e); + } expr* pure = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); m_pinned.push_back(pure); m_term2app.insert(t.get_id(), pure); return pure; - } + } bool is_better_rep(expr *t1, expr *t2) { if (!t2) return t1; @@ -648,34 +648,34 @@ namespace qe { } void purify() { - // - propagate representatives up over parents. - // use work-list + marking to propagate. - // - produce equalities over represented classes. - // - produce other literals over represented classes + // - propagate representatives up over parents. + // use work-list + marking to propagate. + // - produce equalities over represented classes. + // - produce other literals over represented classes // (walk disequalities in m_lits and represent // lhs/rhs over decls or excluding decls) - ptr_vector worklist; + ptr_vector worklist; for (term * t : m_tg.m_terms) { - worklist.push_back(t); - t->set_mark(true); - } + worklist.push_back(t); + t->set_mark(true); + } - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); - t->set_mark(false); - if (m_term2app.contains(t->get_id())) - continue; + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; if (!t->is_theory() && is_projected(*t)) - continue; + continue; - expr* pure = mk_pure(*t); - if (!pure) continue; + expr* pure = mk_pure(*t); + if (!pure) continue; m_term2app.insert(t->get_id(), pure); expr* rep = nullptr; - // ensure that the root has a representative + // ensure that the root has a representative m_root2rep.find(t->get_root().get_id(), rep); // update rep with pure if it is better @@ -686,9 +686,9 @@ namespace qe { if (!p->is_marked()) { p->set_mark(true); worklist.push_back(p); - } - } - } + } + } + } } // Here we could also walk equivalence classes that @@ -698,7 +698,7 @@ namespace qe { // and can be mined using other means, such as theory // aware core minimization m_tg.reset_marks(); - } + } void solve() { ptr_vector worklist; @@ -707,7 +707,7 @@ namespace qe { if (m_term2app.contains(t->get_id())) continue; worklist.push_back(t); t->set_mark(true); - } + } while (!worklist.empty()) { term* t = worklist.back(); @@ -728,13 +728,13 @@ namespace qe { m_root2rep.insert(t->get_root().get_id(), pure); for (term * p : term::parents(t->get_root())) { SASSERT(!m_term2app.contains(p->get_id())); - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); - } - } + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); } } + } + } m_tg.reset_marks(); } @@ -744,7 +744,7 @@ namespace qe { bool find_app(expr *lit, expr *&res) { return m_root2rep.find(m_tg.get_term(lit)->get_root().get_id(), res); - } + } void mk_lits(expr_ref_vector &res) { expr *e = nullptr; @@ -800,16 +800,16 @@ namespace qe { } bool is_solved_eq(expr *_lhs, expr* _rhs) { - if (!is_app(_lhs) || !is_app(_rhs)) return false; - app *lhs, *rhs; - lhs = ::to_app(_lhs); - rhs = ::to_app(_rhs); + if (!is_app(_lhs) || !is_app(_rhs)) return false; + app *lhs, *rhs; + lhs = ::to_app(_lhs); + rhs = ::to_app(_rhs); - if (rhs->get_num_args() > 0) return false; - if (rhs->get_family_id() != null_family_id) return false; + if (rhs->get_num_args() > 0) return false; + if (rhs->get_family_id() != null_family_id) return false; - return !occurs(rhs, lhs); - } + return !occurs(rhs, lhs); + } public: projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} @@ -820,7 +820,7 @@ namespace qe { m_root2rep.reset(); m_decls.reset(); m_pinned.reset(); - } + } expr_ref_vector project(func_decl_ref_vector const &decls, bool exclude) { expr_ref_vector res(m); m_exclude = exclude; @@ -830,7 +830,7 @@ namespace qe { mk_pure_equalities(res); reset(); return res; - } + } expr_ref_vector solve(func_decl_ref_vector const &decls, bool exclude) { expr_ref_vector res(m); m_exclude = exclude; @@ -842,12 +842,12 @@ namespace qe { return res; } }; - } + } expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { projector p(*this); return p.project(decls, exclude); - } + } expr_ref_vector term_graph::solve(func_decl_ref_vector const &decls, bool exclude) { projector p(*this); From ba504e4243908ea2074fc6f2cdaa8894be78ed22 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 21:22:53 -0700 Subject: [PATCH 288/364] debugging mbi Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 54 +++++++++++++++++++++++++ src/qe/qe_mbi.cpp | 13 +++++- src/qe/qe_term_graph.cpp | 30 +++++++------- src/smt/smt_solver.cpp | 2 +- 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index bedbf84aa..62d5ea3ea 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -29,6 +29,7 @@ Notes: #include "tactic/arith/bound_manager.h" #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" +#include "ast/ast_util.h" #include "util/gparams.h" #include "qe/qe_mbp.h" #include "qe/qe_mbi.h" @@ -437,7 +438,58 @@ public: lbool res = mbi.pingpong(pA, pB, vars, itp); ctx.regular_stream() << res << " " << itp << "\n"; } +}; + +class eufi_cmd : public cmd { + expr* m_a; + expr* m_b; + ptr_vector m_vars; +public: + eufi_cmd():cmd("eufi") {} + char const * get_usage() const override { return " (vars)"; } + char const * get_descr(cmd_context & ctx) const override { return "perform model based interpolation"; } + unsigned get_arity() const override { return 3; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { + if (m_a == nullptr) return CPK_EXPR; + if (m_b == nullptr) return CPK_EXPR; + return CPK_FUNC_DECL_LIST; + } + void set_next_arg(cmd_context& ctx, expr * arg) override { + if (m_a == nullptr) + m_a = arg; + else + m_b = arg; + } + void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; m_vars.reset(); } + void execute(cmd_context & ctx) override { + ast_manager& m = ctx.m(); + func_decl_ref_vector vars(m); + for (func_decl* v : m_vars) { + vars.push_back(v); + } + qe::interpolator mbi(m); + expr_ref a(m_a, m); + expr_ref b(m_b, m); + expr_ref itp(m); + solver_factory& sf = ctx.get_solver_factory(); + params_ref p; + solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null); + solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null); + solver_ref sNotA = sf(m, p, false /* no proofs */, true, true, symbol::null); + solver_ref sNotB = sf(m, p, false /* no proofs */, true, true, symbol::null); + sA->assert_expr(a); + sNotA->assert_expr(m.mk_not(a)); + sB->assert_expr(b); + sNotB->assert_expr(m.mk_not(b)); + qe::euf_mbi_plugin pA(sA.get(), sNotA.get()); + qe::euf_mbi_plugin pB(sB.get(), sNotB.get()); + lbool res = mbi.pogo(pA, pB, vars, itp); + ctx.regular_stream() << res << " " << itp << "\n"; + } }; @@ -468,6 +520,7 @@ public: expr_ref_vector lits(m); for (func_decl* v : m_vars) vars.push_back(v); for (expr* e : m_lits) lits.push_back(e); + flatten_and(lits); qe::term_graph tg(m); tg.add_lits(lits); expr_ref_vector p = tg.project(vars, false); @@ -504,4 +557,5 @@ void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(mbp_cmd)); ctx.insert(alloc(mbi_cmd)); ctx.insert(alloc(euf_project_cmd)); + ctx.insert(alloc(eufi_cmd)); } diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index d47c2ab2c..4b86a10a4 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -30,13 +30,17 @@ Revision History: namespace qe { lbool mbi_plugin::check(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { - SASSERT(lits.empty()); + ast_manager& m = lits.get_manager(); + expr_ref_vector lits0(lits); while (true) { + lits.reset(); + lits.append(lits0); switch ((*this)(vars, lits, mdl)) { case mbi_sat: return l_true; case mbi_unsat: if (lits.empty()) return l_false; + TRACE("qe", tout << "block: " << lits << "\n";); block(lits); break; case mbi_undef: @@ -111,6 +115,10 @@ namespace qe { m_solver(s), m_dual_solver(sNot) { + params_ref p; + p.set_bool("smt.core.minimize", true); + m_solver->updt_params(p); + m_dual_solver->updt_params(p); expr_ref_vector fmls(m); m_solver->get_assertions(fmls); expr_fast_mark1 marks; @@ -141,6 +149,7 @@ namespace qe { lits.push_back(m.mk_not(e)); } } + TRACE("qe", tout << "atoms from model: " << lits << "\n";); r = m_dual_solver->check_sat(lits); expr_ref_vector core(m); term_graph tg(m); @@ -148,10 +157,12 @@ namespace qe { case l_false: // use the dual solver to find a 'small' implicant m_dual_solver->get_unsat_core(core); + TRACE("qe", tout << "core: " << core << "\n";); // project the implicant onto vars tg.add_lits(core); lits.reset(); lits.append(tg.project(vars, false)); + TRACE("qe", tout << "project: " << lits << "\n";); return mbi_sat; case l_undef: return mbi_undef; diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 40f3998e7..8e3981e64 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -65,9 +65,9 @@ namespace qe { m_children.push_back(t); } } - + ~term() {} - + class parents { term const& t; public: @@ -168,9 +168,9 @@ namespace qe { arith_term_graph_plugin(term_graph &g) : term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} - + virtual ~arith_term_graph_plugin() {} - + bool mk_eq_core (expr *_e1, expr *_e2, expr_ref &res) { expr *e1, *e2; e1 = _e1; @@ -385,7 +385,7 @@ namespace qe { SASSERT(res); return res; } - + void term_graph::internalize_eq(expr *a1, expr* a2) { SASSERT(m_merge.empty()); merge(*internalize_term(a1), *internalize_term(a2)); @@ -415,17 +415,17 @@ namespace qe { void term_graph::merge(term &t1, term &t2) { // -- merge might invalidate term2app cache m_term2app.reset(); - m_pinned.reset(); - + m_pinned.reset(); + term *a = &t1.get_root(); term *b = &t2.get_root(); - + if (a == b) return; - + if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } - + // Remove parents of it from the cg table. for (term* p : term::parents(b)) { if (!p->is_marked()) { @@ -736,7 +736,7 @@ namespace qe { } } m_tg.reset_marks(); - } + } bool find_app(term &t, expr *&res) { return m_root2rep.find(t.get_root().get_id(), res); @@ -744,7 +744,7 @@ namespace qe { bool find_app(expr *lit, expr *&res) { return m_root2rep.find(m_tg.get_term(lit)->get_root().get_id(), res); - } + } void mk_lits(expr_ref_vector &res) { expr *e = nullptr; @@ -752,7 +752,7 @@ namespace qe { if (!m.is_eq(lit) && find_app(lit, e)) res.push_back(e); } - } + } void mk_pure_equalities(const term &t, expr_ref_vector &res) { expr *rep = nullptr; @@ -842,12 +842,12 @@ namespace qe { return res; } }; - } + } expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { projector p(*this); return p.project(decls, exclude); - } + } expr_ref_vector term_graph::solve(func_decl_ref_vector const &decls, bool exclude) { projector p(*this); diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 1e49f7f87..b0a8153a5 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -215,7 +215,7 @@ namespace smt { r.push_back(m_context.get_unsat_core_expr(i)); } - if (m_minimizing_core && smt_params_helper(get_params()).core_minimize()) { + if (!m_minimizing_core && smt_params_helper(get_params()).core_minimize()) { scoped_minimize_core scm(*this); mus mus(*this); mus.add_soft(r.size(), r.c_ptr()); From 6d79b191703ea9b85fbb1304ee97fd5b7832b4ab Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 22:08:50 -0700 Subject: [PATCH 289/364] fix a few bugs, debugging eufi Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbi.cpp | 2 +- src/qe/qe_term_graph.cpp | 28 ++++++++++++++++++++-------- src/solver/mus.cpp | 4 ++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 4b86a10a4..298a1da99 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -116,7 +116,7 @@ namespace qe { m_dual_solver(sNot) { params_ref p; - p.set_bool("smt.core.minimize", true); + p.set_bool("core.minimize", true); m_solver->updt_params(p); m_dual_solver->updt_params(p); expr_ref_vector fmls(m); diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 8e3981e64..f537c5da8 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -625,7 +625,7 @@ namespace qe { expr_ref_vector m_pinned; // tracks expr in the maps - expr* mk_pure(term& t) { + expr* mk_pure(term const& t) { expr* e = nullptr; if (m_term2app.find(t.get_id(), e)) return e; e = t.get_expr(); @@ -643,7 +643,7 @@ namespace qe { } bool is_better_rep(expr *t1, expr *t2) { - if (!t2) return t1; + if (!t2) return t1 != nullptr; return m.is_unique_value(t1) && !m.is_unique_value(t2); } @@ -755,13 +755,21 @@ namespace qe { } void mk_pure_equalities(const term &t, expr_ref_vector &res) { + SASSERT(t.is_root()); expr *rep = nullptr; if (!m_root2rep.find(t.get_id(), rep)) return; - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - expr* member = mk_pure(*it); - if (member) + obj_hashtable members; + members.insert(rep); + term const * r = &t; + do { + expr* member = nullptr; + if (m_term2app.find(r->get_id(), member) && !members.contains(member)) { res.push_back (m.mk_eq (rep, member)); + members.insert(member); + } + r = &r->get_next(); } + while (r != &t); } bool is_projected(const term &t) { @@ -771,13 +779,17 @@ namespace qe { void mk_unpure_equalities(const term &t, expr_ref_vector &res) { expr *rep = nullptr; if (!m_root2rep.find(t.get_id(), rep)) return; - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - expr* member = mk_pure(*it); + obj_hashtable members; + term const * r = &t; + do { + expr* member = mk_pure(*r); SASSERT(member); - if (!is_projected(*it) || !is_solved_eq(rep, member)) { + if (member != rep && (!is_projected(*r) || !is_solved_eq(rep, member))) { res.push_back(m.mk_eq(rep, member)); } + r = &r->get_next(); } + while (r != &t); } void mk_equalities(bool pure, expr_ref_vector &res) { diff --git a/src/solver/mus.cpp b/src/solver/mus.cpp index e4ebd7e3b..a7925f268 100644 --- a/src/solver/mus.cpp +++ b/src/solver/mus.cpp @@ -50,12 +50,12 @@ struct mus::imp { } bool is_literal(expr* lit) const { - expr* l; + expr* l; return is_uninterp_const(lit) || (m.is_not(lit, l) && is_uninterp_const(l)); } unsigned add_soft(expr* lit) { - SASSERT(is_literal(lit)); + //SASSERT(is_literal(lit)); unsigned idx = m_lit2expr.size(); m_expr2lit.insert(lit, idx); m_lit2expr.push_back(lit); From 0f799eb2ae609d737c74131bafd54b1b45641de4 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 22:44:11 -0700 Subject: [PATCH 290/364] formatting. no change to code --- src/qe/qe_term_graph.cpp | 104 +++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index f537c5da8..a0ebf6f33 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -626,21 +626,21 @@ namespace qe { expr_ref_vector m_pinned; // tracks expr in the maps expr* mk_pure(term const& t) { - expr* e = nullptr; - if (m_term2app.find(t.get_id(), e)) return e; - e = t.get_expr(); - if (!is_app(e)) return nullptr; - app* a = ::to_app(e); + expr* e = nullptr; + if (m_term2app.find(t.get_id(), e)) return e; + e = t.get_expr(); + if (!is_app(e)) return nullptr; + app* a = ::to_app(e); expr_ref_buffer kids(m); - for (term* ch : term::children(t)) { + for (term* ch : term::children(t)) { if (!m_root2rep.find(ch->get_root().get_id(), e)) return nullptr; - kids.push_back(e); - } + kids.push_back(e); + } expr* pure = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); m_pinned.push_back(pure); m_term2app.insert(t.get_id(), pure); return pure; - } + } bool is_better_rep(expr *t1, expr *t2) { if (!t2) return t1 != nullptr; @@ -648,34 +648,34 @@ namespace qe { } void purify() { - // - propagate representatives up over parents. - // use work-list + marking to propagate. - // - produce equalities over represented classes. - // - produce other literals over represented classes + // - propagate representatives up over parents. + // use work-list + marking to propagate. + // - produce equalities over represented classes. + // - produce other literals over represented classes // (walk disequalities in m_lits and represent // lhs/rhs over decls or excluding decls) - ptr_vector worklist; + ptr_vector worklist; for (term * t : m_tg.m_terms) { - worklist.push_back(t); - t->set_mark(true); - } + worklist.push_back(t); + t->set_mark(true); + } - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); - t->set_mark(false); - if (m_term2app.contains(t->get_id())) - continue; + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; if (!t->is_theory() && is_projected(*t)) - continue; + continue; - expr* pure = mk_pure(*t); - if (!pure) continue; + expr* pure = mk_pure(*t); + if (!pure) continue; m_term2app.insert(t->get_id(), pure); expr* rep = nullptr; - // ensure that the root has a representative + // ensure that the root has a representative m_root2rep.find(t->get_root().get_id(), rep); // update rep with pure if it is better @@ -686,9 +686,9 @@ namespace qe { if (!p->is_marked()) { p->set_mark(true); worklist.push_back(p); - } - } - } + } + } + } } // Here we could also walk equivalence classes that @@ -698,16 +698,16 @@ namespace qe { // and can be mined using other means, such as theory // aware core minimization m_tg.reset_marks(); - } + } void solve() { - ptr_vector worklist; + ptr_vector worklist; for (term * t : m_tg.m_terms) { // skip pure terms if (m_term2app.contains(t->get_id())) continue; worklist.push_back(t); t->set_mark(true); - } + } while (!worklist.empty()) { term* t = worklist.back(); @@ -728,15 +728,15 @@ namespace qe { m_root2rep.insert(t->get_root().get_id(), pure); for (term * p : term::parents(t->get_root())) { SASSERT(!m_term2app.contains(p->get_id())); - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } + } } } - } - } m_tg.reset_marks(); - } + } bool find_app(term &t, expr *&res) { return m_root2rep.find(t.get_root().get_id(), res); @@ -744,7 +744,7 @@ namespace qe { bool find_app(expr *lit, expr *&res) { return m_root2rep.find(m_tg.get_term(lit)->get_root().get_id(), res); - } + } void mk_lits(expr_ref_vector &res) { expr *e = nullptr; @@ -752,7 +752,7 @@ namespace qe { if (!m.is_eq(lit) && find_app(lit, e)) res.push_back(e); } - } + } void mk_pure_equalities(const term &t, expr_ref_vector &res) { SASSERT(t.is_root()); @@ -812,16 +812,16 @@ namespace qe { } bool is_solved_eq(expr *_lhs, expr* _rhs) { - if (!is_app(_lhs) || !is_app(_rhs)) return false; - app *lhs, *rhs; - lhs = ::to_app(_lhs); - rhs = ::to_app(_rhs); + if (!is_app(_lhs) || !is_app(_rhs)) return false; + app *lhs, *rhs; + lhs = ::to_app(_lhs); + rhs = ::to_app(_rhs); - if (rhs->get_num_args() > 0) return false; - if (rhs->get_family_id() != null_family_id) return false; + if (rhs->get_num_args() > 0) return false; + if (rhs->get_family_id() != null_family_id) return false; - return !occurs(rhs, lhs); - } + return !occurs(rhs, lhs); + } public: projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} @@ -832,7 +832,7 @@ namespace qe { m_root2rep.reset(); m_decls.reset(); m_pinned.reset(); - } + } expr_ref_vector project(func_decl_ref_vector const &decls, bool exclude) { expr_ref_vector res(m); m_exclude = exclude; @@ -842,7 +842,7 @@ namespace qe { mk_pure_equalities(res); reset(); return res; - } + } expr_ref_vector solve(func_decl_ref_vector const &decls, bool exclude) { expr_ref_vector res(m); m_exclude = exclude; @@ -854,7 +854,7 @@ namespace qe { return res; } }; - } + } expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { projector p(*this); From 2288931b4669e2f13b9892a1d9bbc0c1e6268370 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 11 Jun 2018 22:48:24 -0700 Subject: [PATCH 291/364] fix mk_unpure_equalities --- src/qe/qe_term_graph.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index a0ebf6f33..084391368 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -780,12 +780,15 @@ namespace qe { expr *rep = nullptr; if (!m_root2rep.find(t.get_id(), rep)) return; obj_hashtable members; + members.insert(rep); term const * r = &t; do { expr* member = mk_pure(*r); SASSERT(member); - if (member != rep && (!is_projected(*r) || !is_solved_eq(rep, member))) { + if (!members.contains(member) && + (!is_projected(*r) || !is_solved_eq(rep, member))) { res.push_back(m.mk_eq(rep, member)); + members.insert(member); } r = &r->get_next(); } From 74621e0b7d366bd83c1b88ea3aba64a5f17d57b4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 23:28:50 -0700 Subject: [PATCH 292/364] first eufi example running Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 22 +++++++------- src/cmd_context/basic_cmds.cpp | 2 +- src/muz/spacer/spacer_iuc_solver.cpp | 12 ++------ src/muz/spacer/spacer_iuc_solver.h | 9 ++++-- src/muz/spacer/spacer_prop_solver.cpp | 2 +- src/opt/maxres.cpp | 13 ++++---- src/opt/opt_context.cpp | 2 +- src/opt/opt_context.h | 2 +- src/opt/opt_solver.cpp | 3 +- src/opt/opt_solver.h | 2 +- src/qe/qe_mbi.cpp | 13 ++------ src/qe/qe_term_graph.cpp | 6 ++-- src/sat/sat_solver/inc_sat_solver.cpp | 2 +- src/smt/smt_context.cpp | 14 ++++----- src/smt/smt_solver.cpp | 14 ++++----- src/solver/check_sat_result.cpp | 6 ++-- src/solver/check_sat_result.h | 9 ++---- src/solver/combined_solver.cpp | 2 +- src/solver/mus.cpp | 30 ++++++------------- src/solver/mus.h | 2 -- src/solver/solver2tactic.cpp | 2 +- src/solver/solver_pool.cpp | 6 ++-- src/solver/tactic2solver.cpp | 4 +-- .../portfolio/bounded_int2bv_solver.cpp | 2 +- src/tactic/portfolio/enum2bv_solver.cpp | 2 +- src/tactic/portfolio/pb2bv_solver.cpp | 2 +- 26 files changed, 80 insertions(+), 105 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 657ff025c..ad5e7731d 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -460,12 +460,12 @@ extern "C" { LOG_Z3_solver_get_unsat_core(c, s); RESET_ERROR_CODE(); init_solver(c, s); - ptr_vector core; + expr_ref_vector core(mk_c(c)->m()); to_solver_ref(s)->get_unsat_core(core); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); - for (unsigned i = 0; i < core.size(); i++) { - v->m_ast_vector.push_back(core[i]); + for (expr* e : core) { + v->m_ast_vector.push_back(e); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); @@ -537,23 +537,23 @@ extern "C" { expr_ref_vector _assumptions(m), _consequences(m), _variables(m); ast_ref_vector const& __assumptions = to_ast_vector_ref(assumptions); unsigned sz = __assumptions.size(); - for (unsigned i = 0; i < sz; ++i) { - if (!is_expr(__assumptions[i])) { + for (ast* e : __assumptions) { + if (!is_expr(e)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE); return Z3_L_UNDEF; } - _assumptions.push_back(to_expr(__assumptions[i])); + _assumptions.push_back(to_expr(e)); } ast_ref_vector const& __variables = to_ast_vector_ref(variables); sz = __variables.size(); - for (unsigned i = 0; i < sz; ++i) { - if (!is_expr(__variables[i])) { + for (ast* a : __variables) { + if (!is_expr(a)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE); return Z3_L_UNDEF; } - _variables.push_back(to_expr(__variables[i])); + _variables.push_back(to_expr(a)); } lbool result = l_undef; unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); @@ -578,8 +578,8 @@ extern "C" { if (result == l_undef) { to_solver_ref(s)->set_reason_unknown(eh); } - for (unsigned i = 0; i < _consequences.size(); ++i) { - to_ast_vector_ref(consequences).push_back(_consequences[i].get()); + for (expr* e : _consequences) { + to_ast_vector_ref(consequences).push_back(e); } return static_cast(result); Z3_CATCH_RETURN(Z3_L_UNDEF); diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 1d8fdb3de..4e5202fd8 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -223,7 +223,7 @@ ATOMIC_CMD(get_proof_graph_cmd, "get-proof-graph", "retrieve proof and print it }); static void print_core(cmd_context& ctx) { - ptr_vector core; + expr_ref_vector core(ctx.m()); ctx.get_check_sat_result()->get_unsat_core(core); ctx.regular_stream() << "("; bool first = true; diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index a0bc2a627..6d824a1f2 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -215,10 +215,11 @@ void iuc_solver::reset_statistics () m_learn_core_sw.reset(); } -void iuc_solver::get_unsat_core (ptr_vector &core) +void iuc_solver::get_unsat_core (expr_ref_vector &core) { m_solver.get_unsat_core (core); - undo_proxies_in_core (core); + ptr_vector _core(core.size(), core.c_ptr()); + undo_proxies_in_core (_core); } void iuc_solver::undo_proxies_in_core (ptr_vector &r) { @@ -258,13 +259,6 @@ void iuc_solver::undo_proxies (expr_ref_vector &r) } } -void iuc_solver::get_unsat_core (expr_ref_vector &_core) -{ - ptr_vector core; - get_unsat_core (core); - _core.append (core.size (), core.c_ptr ()); -} - void iuc_solver::elim_proxies (expr_ref_vector &v) { expr_ref f = mk_and (v); diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index 0195ea134..3c6251be0 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -92,7 +92,6 @@ public: ~iuc_solver() override {} /* iuc solver specific */ - void get_unsat_core(expr_ref_vector &core) override; virtual void get_iuc(expr_ref_vector &core); void set_split_literals(bool v) { m_split_literals = v; } bool mk_proxies(expr_ref_vector &v, unsigned from = 0); @@ -102,7 +101,11 @@ public: void pop_bg(unsigned n); unsigned get_num_bg(); - void get_full_unsat_core(ptr_vector &core) { m_solver.get_unsat_core(core); } + void get_full_unsat_core(ptr_vector &core) { + expr_ref_vector _core(m); + m_solver.get_unsat_core(_core); + core.append(_core.size(), _core.c_ptr()); + } /* solver interface */ @@ -142,7 +145,7 @@ public: void collect_statistics(statistics &st) const override ; virtual void reset_statistics(); - void get_unsat_core(ptr_vector &r) override; + void get_unsat_core(expr_ref_vector &r) override; void get_model_core(model_ref &m) override {m_solver.get_model(m);} proof *get_proof() override {return m_solver.get_proof();} std::string reason_unknown() const override { return m_solver.reason_unknown(); } diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 64f1bfc9a..435f0af3a 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -245,7 +245,7 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, soft.reset(); expr_ref saved(m); - ptr_vector core; + expr_ref_vector core(m); m_ctx->get_unsat_core(core); // while there are soft constraints diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index d49044d8d..e86b20820 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -88,7 +88,7 @@ private: expr_ref_vector m_asms; expr_ref_vector m_defs; obj_map m_asm2weight; - ptr_vector m_new_core; + expr_ref_vector m_new_core; mus m_mus; expr_ref_vector m_trail; strategy_t m_st; @@ -119,6 +119,7 @@ public: maxsmt_solver_base(c, ws, soft), m_index(index), m_B(m), m_asms(m), m_defs(m), + m_new_core(m), m_mus(c.get_solver()), m_trail(m), m_st(st), @@ -351,11 +352,13 @@ public: exprs core; while (is_sat == l_false) { core.reset(); - s().get_unsat_core(core); - // verify_core(core); + expr_ref_vector _core(m); + s().get_unsat_core(_core); model_ref mdl; get_mus_model(mdl); - is_sat = minimize_core(core); + is_sat = minimize_core(_core); + core.append(_core.size(), _core.c_ptr()); + // verify_core(core); ++m_stats.m_num_cores; if (is_sat != l_true) { IF_VERBOSE(100, verbose_stream() << "(opt.maxres minimization failed)\n";); @@ -538,7 +541,7 @@ public: return nullptr != mdl.get(); } - lbool minimize_core(exprs& core) { + lbool minimize_core(expr_ref_vector& core) { if (core.empty()) { return l_true; } diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 2850dd59c..37dbd0817 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -172,7 +172,7 @@ namespace opt { r.append(m_labels); } - void context::get_unsat_core(ptr_vector & r) { + void context::get_unsat_core(expr_ref_vector & r) { throw default_exception("Unsat cores are not supported with optimization"); } diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 29b327855..1172e68b6 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -195,7 +195,7 @@ namespace opt { void collect_statistics(statistics& stats) const override; proof* get_proof() override { return nullptr; } void get_labels(svector & r) override; - void get_unsat_core(ptr_vector & r) override; + void get_unsat_core(expr_ref_vector & r) override; std::string reason_unknown() const override; void set_reason_unknown(char const* msg) override { m_unknown = msg; } diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index 1a6587585..ca0474314 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -294,7 +294,8 @@ namespace opt { return r; } - void opt_solver::get_unsat_core(ptr_vector & r) { + void opt_solver::get_unsat_core(expr_ref_vector & r) { + r.reset(); unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index 7ffb531be..39562ec54 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -96,7 +96,7 @@ namespace opt { void push_core() override; void pop_core(unsigned n) override; lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; - void get_unsat_core(ptr_vector & r) override; + void get_unsat_core(expr_ref_vector & r) override; void get_model_core(model_ref & _m) override; proof * get_proof() override; std::string reason_unknown() const override; diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 298a1da99..31b6001ea 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -30,19 +30,12 @@ Revision History: namespace qe { lbool mbi_plugin::check(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { - ast_manager& m = lits.get_manager(); - expr_ref_vector lits0(lits); while (true) { - lits.reset(); - lits.append(lits0); switch ((*this)(vars, lits, mdl)) { case mbi_sat: return l_true; case mbi_unsat: - if (lits.empty()) return l_false; - TRACE("qe", tout << "block: " << lits << "\n";); - block(lits); - break; + return l_false; case mbi_undef: return l_undef; case mbi_augment: @@ -113,8 +106,7 @@ namespace qe { m(s->get_manager()), m_atoms(m), m_solver(s), - m_dual_solver(sNot) - { + m_dual_solver(sNot) { params_ref p; p.set_bool("core.minimize", true); m_solver->updt_params(p); @@ -256,6 +248,7 @@ namespace qe { case l_true: return l_true; case l_false: + std::cout << lits << "\n"; a.block(lits); itps.push_back(mk_not(mk_and(lits))); break; diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 084391368..2fa652828 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -701,7 +701,7 @@ namespace qe { } void solve() { - ptr_vector worklist; + ptr_vector worklist; for (term * t : m_tg.m_terms) { // skip pure terms if (m_term2app.contains(t->get_id())) continue; @@ -785,8 +785,7 @@ namespace qe { do { expr* member = mk_pure(*r); SASSERT(member); - if (!members.contains(member) && - (!is_projected(*r) || !is_solved_eq(rep, member))) { + if (!members.contains(member) && (!is_projected(*r) || !is_solved_eq(rep, member))) { res.push_back(m.mk_eq(rep, member)); members.insert(member); } @@ -814,6 +813,7 @@ namespace qe { return mk_equalities(false, res); } + // TBD: generalize for also the case of a (:var n) bool is_solved_eq(expr *_lhs, expr* _rhs) { if (!is_app(_lhs) || !is_app(_rhs)) return false; app *lhs, *rhs; diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index e1e0e0b0a..8b7e2e63c 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -299,7 +299,7 @@ public: if (m_preprocess) m_preprocess->collect_statistics(st); m_solver.collect_statistics(st); } - void get_unsat_core(ptr_vector & r) override { + void get_unsat_core(expr_ref_vector & r) override { r.reset(); r.append(m_core.size(), m_core.c_ptr()); } diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index bc5814186..0bc21d627 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3258,7 +3258,7 @@ namespace smt { m_assumptions.reset(); } - lbool context::mk_unsat_core(lbool r) { + lbool context::mk_unsat_core(lbool r) { if (r != l_false) return r; SASSERT(inconsistent()); if (!tracking_assumptions()) { @@ -3276,18 +3276,16 @@ namespace smt { SASSERT(m_literal2assumption.contains(l.index())); if (!already_found_assumptions.contains(l.index())) { already_found_assumptions.insert(l.index()); - m_unsat_core.push_back(m_literal2assumption[l.index()]); + expr* orig_assumption = m_literal2assumption[l.index()]; + m_unsat_core.push_back(orig_assumption); + TRACE("assumptions", tout << l << ": " << mk_pp(orig_assumption, m_manager) << "\n";); } } reset_assumptions(); pop_to_base_lvl(); // undo the push_scope() performed by init_assumptions m_search_lvl = m_base_lvl; std::sort(m_unsat_core.c_ptr(), m_unsat_core.c_ptr() + m_unsat_core.size(), ast_lt_proc()); - TRACE("unsat_core_bug", tout << "unsat core:\n"; - unsigned sz = m_unsat_core.size(); - for (unsigned i = 0; i < sz; i++) { - tout << mk_pp(m_unsat_core.get(i), m_manager) << "\n"; - }); + TRACE("unsat_core_bug", tout << "unsat core:\n" << m_unsat_core << "\n";); validate_unsat_core(); // theory validation of unsat core for (theory* th : m_theory_set) { @@ -3403,7 +3401,6 @@ namespace smt { lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel, bool already_did_theory_assumptions) { if (!check_preamble(reset_cancel)) return l_undef; - TRACE("before_search", display(tout);); SASSERT(at_base_level()); setup_context(false); expr_ref_vector asms(m_manager, num_assumptions, assumptions); @@ -3412,6 +3409,7 @@ namespace smt { TRACE("unsat_core_bug", tout << asms << "\n";); internalize_assertions(); init_assumptions(asms); + TRACE("before_search", display(tout);); lbool r = search(); r = mk_unsat_core(r); r = check_finalize(r); diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index b0a8153a5..48f6053fc 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -209,7 +209,7 @@ namespace smt { } }; - void get_unsat_core(ptr_vector & r) override { + void get_unsat_core(expr_ref_vector & r) override { unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); @@ -219,7 +219,7 @@ namespace smt { scoped_minimize_core scm(*this); mus mus(*this); mus.add_soft(r.size(), r.c_ptr()); - ptr_vector r2; + expr_ref_vector r2(m); if (l_true == mus.get_mus(r2)) { r.reset(); r.append(r2); @@ -333,7 +333,7 @@ namespace smt { for_each_expr(p, visited, e); } - void compute_assrtn_fds(ptr_vector & core, vector & assrtn_fds) { + void compute_assrtn_fds(expr_ref_vector & core, vector & assrtn_fds) { assrtn_fds.resize(m_name2assertion.size()); unsigned i = 0; for (auto & kv : m_name2assertion) { @@ -354,7 +354,7 @@ namespace smt { return false; } - void add_pattern_literals_to_core(ptr_vector & core) { + void add_pattern_literals_to_core(expr_ref_vector & core) { ast_manager & m = get_manager(); expr_ref_vector new_core_literals(m); @@ -413,7 +413,7 @@ namespace smt { for_each_expr(p, visited, e); } - void add_nonlocal_pattern_literals_to_core(ptr_vector & core) { + void add_nonlocal_pattern_literals_to_core(expr_ref_vector & core) { ast_manager & m = get_manager(); for (auto const& kv : m_name2assertion) { expr_ref name(kv.m_key, m); @@ -425,8 +425,8 @@ namespace smt { collect_body_func_decls(assrtn, body_fds); for (func_decl *fd : pattern_fds) { - if (!body_fds.contains(fd)) { - core.insert(name); + if (!body_fds.contains(fd) && !core.contains(name)) { + core.push_back(name); break; } } diff --git a/src/solver/check_sat_result.cpp b/src/solver/check_sat_result.cpp index 0d3c112b3..28e6afeca 100644 --- a/src/solver/check_sat_result.cpp +++ b/src/solver/check_sat_result.cpp @@ -48,9 +48,11 @@ void simple_check_sat_result::collect_statistics(statistics & st) const { st.copy(m_stats); } -void simple_check_sat_result::get_unsat_core(ptr_vector & r) { - if (m_status == l_false) +void simple_check_sat_result::get_unsat_core(expr_ref_vector & r) { + if (m_status == l_false) { + r.reset(); r.append(m_core.size(), m_core.c_ptr()); + } } void simple_check_sat_result::get_model_core(model_ref & m) { diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index 762735b8d..7e698b806 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -50,12 +50,7 @@ public: lbool set_status(lbool r) { return m_status = r; } lbool status() const { return m_status; } virtual void collect_statistics(statistics & st) const = 0; - virtual void get_unsat_core(ptr_vector & r) = 0; - virtual void get_unsat_core(expr_ref_vector & r) { - ptr_vector core; - get_unsat_core(core); - r.append(core.size(), core.c_ptr()); - } + virtual void get_unsat_core(expr_ref_vector & r) = 0; void set_model_converter(model_converter* mc) { m_mc0 = mc; } model_converter* mc0() const { return m_mc0.get(); } virtual void get_model_core(model_ref & m) = 0; @@ -87,7 +82,7 @@ struct simple_check_sat_result : public check_sat_result { ~simple_check_sat_result() override; ast_manager& get_manager() const override { return m_proof.get_manager(); } void collect_statistics(statistics & st) const override; - void get_unsat_core(ptr_vector & r) override; + void get_unsat_core(expr_ref_vector & r) override; void get_model_core(model_ref & m) override; proof * get_proof() override; std::string reason_unknown() const override; diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index a47302f5d..ad166d425 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -298,7 +298,7 @@ public: m_solver1->collect_statistics(st); } - void get_unsat_core(ptr_vector & r) override { + void get_unsat_core(expr_ref_vector & r) override { if (m_use_solver1_results) m_solver1->get_unsat_core(r); else diff --git a/src/solver/mus.cpp b/src/solver/mus.cpp index a7925f268..b3d6c9ad9 100644 --- a/src/solver/mus.cpp +++ b/src/solver/mus.cpp @@ -64,7 +64,6 @@ struct mus::imp { } void add_assumption(expr* lit) { - SASSERT(is_literal(lit)); m_assumptions.push_back(lit); } @@ -78,17 +77,9 @@ struct mus::imp { return get_mus1(mus); } - lbool get_mus(ptr_vector& mus) { - mus.reset(); - expr_ref_vector result(m); - lbool r = get_mus(result); - mus.append(result.size(), result.c_ptr()); - return r; - } - lbool get_mus1(expr_ref_vector& mus) { ptr_vector unknown(m_lit2expr.size(), m_lit2expr.c_ptr()); - ptr_vector core_exprs; + expr_ref_vector core_exprs(m); TRACE("mus", m_solver.display(tout);); while (!unknown.empty()) { IF_VERBOSE(12, verbose_stream() << "(mus reducing core: " << unknown.size() << " new core: " << mus.size() << ")\n";); @@ -116,12 +107,12 @@ struct mus::imp { if (!core_exprs.contains(not_lit)) { // unknown := core_exprs \ mus unknown.reset(); - for (unsigned i = 0; i < core_exprs.size(); ++i) { - if (!mus.contains(core_exprs[i])) { - unknown.push_back(core_exprs[i]); + for (expr* c : core_exprs) { + if (!mus.contains(c)) { + unknown.push_back(c); } } - TRACE("mus", display_vec(tout << "core exprs:", core_exprs); + TRACE("mus", tout << "core exprs:" << core_exprs << "\n"; display_vec(tout << "core:", unknown); display_vec(tout << "mus:", mus); ); @@ -242,11 +233,11 @@ struct mus::imp { void get_core(expr_set& core) { core.reset(); - ptr_vector core_exprs; + expr_ref_vector core_exprs(m); m_solver.get_unsat_core(core_exprs); - for (unsigned i = 0; i < core_exprs.size(); ++i) { - if (m_expr2lit.contains(core_exprs[i])) { - core.insert(core_exprs[i]); + for (expr* c : core_exprs) { + if (m_expr2lit.contains(c)) { + core.insert(c); } } } @@ -375,9 +366,6 @@ void mus::add_assumption(expr* lit) { return m_imp->add_assumption(lit); } -lbool mus::get_mus(ptr_vector& mus) { - return m_imp->get_mus(mus); -} lbool mus::get_mus(expr_ref_vector& mus) { return m_imp->get_mus(mus); diff --git a/src/solver/mus.h b/src/solver/mus.h index f2e543f04..807b07873 100644 --- a/src/solver/mus.h +++ b/src/solver/mus.h @@ -47,8 +47,6 @@ class mus { */ void add_assumption(expr* lit); - lbool get_mus(ptr_vector& mus); - lbool get_mus(expr_ref_vector& mus); void reset(); diff --git a/src/solver/solver2tactic.cpp b/src/solver/solver2tactic.cpp index 68d4b11f6..3c4f291a5 100644 --- a/src/solver/solver2tactic.cpp +++ b/src/solver/solver2tactic.cpp @@ -134,7 +134,7 @@ public: in->set(proof2proof_converter(m, pr)); } if (in->unsat_core_enabled()) { - ptr_vector core; + expr_ref_vector core(m); local_solver->get_unsat_core(core); for (expr* c : core) { lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 1d0fd0c11..9dacfb5ce 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -83,12 +83,12 @@ public: unsigned get_num_assertions() const override { return m_base->get_num_assertions(); } expr * get_assertion(unsigned idx) const override { return m_base->get_assertion(idx); } - void get_unsat_core(ptr_vector & r) override { + void get_unsat_core(expr_ref_vector& r) override { m_base->get_unsat_core(r); unsigned j = 0; for (unsigned i = 0; i < r.size(); ++i) - if (m_pred != r[i]) - r[j++] = r[i]; + if (m_pred != r.get(i)) + r[j++] = r.get(i); r.shrink(j); } diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 9118bb658..94509e3f6 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -62,7 +62,7 @@ public: lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; void collect_statistics(statistics & st) const override; - void get_unsat_core(ptr_vector & r) override; + void get_unsat_core(expr_ref_vector & r) override; void get_model_core(model_ref & m) override; proof * get_proof() override; std::string reason_unknown() const override; @@ -219,7 +219,7 @@ void tactic2solver::collect_statistics(statistics & st) const { //SASSERT(m_stats.size() > 0); } -void tactic2solver::get_unsat_core(ptr_vector & r) { +void tactic2solver::get_unsat_core(expr_ref_vector & r) { if (m_result.get()) { m_result->get_unsat_core(r); } diff --git a/src/tactic/portfolio/bounded_int2bv_solver.cpp b/src/tactic/portfolio/bounded_int2bv_solver.cpp index 8767644f3..6389ed739 100644 --- a/src/tactic/portfolio/bounded_int2bv_solver.cpp +++ b/src/tactic/portfolio/bounded_int2bv_solver.cpp @@ -148,7 +148,7 @@ public: void set_produce_models(bool f) override { m_solver->set_produce_models(f); } void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } void collect_statistics(statistics & st) const override { m_solver->collect_statistics(st); } - void get_unsat_core(ptr_vector & r) override { m_solver->get_unsat_core(r); } + void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp index 6b5fe9056..29c6aeb39 100644 --- a/src/tactic/portfolio/enum2bv_solver.cpp +++ b/src/tactic/portfolio/enum2bv_solver.cpp @@ -88,7 +88,7 @@ public: void set_produce_models(bool f) override { m_solver->set_produce_models(f); } void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } void collect_statistics(statistics & st) const override { m_solver->collect_statistics(st); } - void get_unsat_core(ptr_vector & r) override { m_solver->get_unsat_core(r); } + void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { diff --git a/src/tactic/portfolio/pb2bv_solver.cpp b/src/tactic/portfolio/pb2bv_solver.cpp index f8794ca41..60ca6a5dc 100644 --- a/src/tactic/portfolio/pb2bv_solver.cpp +++ b/src/tactic/portfolio/pb2bv_solver.cpp @@ -87,7 +87,7 @@ public: m_rewriter.collect_statistics(st); m_solver->collect_statistics(st); } - void get_unsat_core(ptr_vector & r) override { m_solver->get_unsat_core(r); } + void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { From 5dc2b7172d7a55278b40b92b58d3d2213b74a50c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 23:31:40 -0700 Subject: [PATCH 293/364] merge Signed-off-by: Nikolaj Bjorner --- src/qe/qe_term_graph.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 2fa652828..396b5f092 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -701,7 +701,7 @@ namespace qe { } void solve() { - ptr_vector worklist; + ptr_vector worklist; for (term * t : m_tg.m_terms) { // skip pure terms if (m_term2app.contains(t->get_id())) continue; @@ -785,7 +785,8 @@ namespace qe { do { expr* member = mk_pure(*r); SASSERT(member); - if (!members.contains(member) && (!is_projected(*r) || !is_solved_eq(rep, member))) { + if (!members.contains(member) && + (!is_projected(*r) || !is_solved_eq(rep, member))) { res.push_back(m.mk_eq(rep, member)); members.insert(member); } From 8da84ec69e5fbbf9e1505b0ad92563fd4cb2e1df Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Jun 2018 23:31:52 -0700 Subject: [PATCH 294/364] merge Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbi.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 31b6001ea..d9b62fb96 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -248,7 +248,6 @@ namespace qe { case l_true: return l_true; case l_false: - std::cout << lits << "\n"; a.block(lits); itps.push_back(mk_not(mk_and(lits))); break; From d5081a48b0778441383b2e5b546957540a53b61b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Jun 2018 07:27:50 -0700 Subject: [PATCH 295/364] merge while skyping Signed-off-by: Nikolaj Bjorner --- src/opt/maxres.cpp | 30 +++++++++++---------- src/qe/qe_mbi.cpp | 58 ++++++++++++++++++++++++++++++++++++++++- src/smt/smt_context.cpp | 1 - src/smt/smt_context.h | 1 - 4 files changed, 73 insertions(+), 17 deletions(-) diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index e86b20820..a8a4dfa71 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -383,8 +383,8 @@ public: } TRACE("opt", tout << "num cores: " << cores.size() << "\n"; - for (unsigned i = 0; i < cores.size(); ++i) { - display_vec(tout, cores[i]); + for (auto const& c : cores) { + display_vec(tout, c); } tout << "num satisfying: " << asms.size() << "\n";); @@ -476,8 +476,8 @@ public: } void process_unsat(vector const& cores) { - for (unsigned i = 0; i < cores.size(); ++i) { - process_unsat(cores[i]); + for (auto const & c : cores) { + process_unsat(c); } } @@ -602,8 +602,7 @@ public: } void display(std::ostream& out) { - for (unsigned i = 0; i < m_asms.size(); ++i) { - expr* a = m_asms[i].get(); + for (expr * a : m_asms) { out << mk_pp(a, m) << " : " << get_weight(a) << "\n"; } } @@ -710,8 +709,8 @@ public: void update_assignment(model* mdl) { unsigned correction_set_size = 0; - for (unsigned i = 0; i < m_asms.size(); ++i) { - if (is_false(mdl, m_asms[i].get())) { + for (expr* a : m_asms) { + if (is_false(mdl, a)) { ++correction_set_size; } } @@ -722,10 +721,12 @@ public: rational upper(0); expr_ref tmp(m); - for (unsigned i = 0; i < m_soft.size(); ++i) { - if (!is_true(mdl, m_soft[i])) { + unsigned i = 0; + for (expr* s : m_soft) { + if (!is_true(mdl, s)) { upper += m_weights[i]; } + ++i; } if (upper > m_upper) { @@ -741,8 +742,9 @@ public: TRACE("opt", model_smt2_pp(tout << "updated model\n", m, *m_model, 0);); - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment[i] = is_true(m_soft[i]); + i = 0; + for (expr* s : m_soft) { + m_assignment[i++] = is_true(s); } // DEBUG_CODE(verify_assignment();); @@ -759,8 +761,8 @@ public: pb_util u(m); expr_ref_vector nsoft(m); expr_ref fml(m); - for (unsigned i = 0; i < m_soft.size(); ++i) { - nsoft.push_back(mk_not(m, m_soft[i])); + for (expr* s : m_soft) { + nsoft.push_back(mk_not(m, s)); } fml = u.mk_lt(nsoft.size(), m_weights.c_ptr(), nsoft.c_ptr(), m_upper); TRACE("opt", tout << "block upper bound " << fml << "\n";);; diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index d9b62fb96..509800c62 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -16,6 +16,62 @@ Author: Revision History: +Notes: + + Reduction into: + T_EUF + T_LIRA + + Other theories: DT, ARR reduced to EUF + BV is EUF/Boolean. + + Purify EUF1 & LIRA1 & EUF2 & LIRA2 + + Then EUF1 & EUF2 |- false + LIRA1 & LIRA2 |- false + + Sketch of approach by example: + + A: s <= 2a <= t & f(a) = q + + B: t <= 2b <= s + 1 & f(b) != q + + 1. Extract arithmetic consequences of A over shared vocabulary. + + A -> s <= t & (even(t) | s < t) + + 2a. Send to B, have B solve shared variables with EUF_B. + epsilon b . B & A_pure + epsilon b . t <= 2b <= s + 1 & s <= t & (even(t) | s < t) + = t <= s + 1 & (even(t) | t <= s) & s <= t & (even(t) | s < t) + = even(t) & t = s + b := t div 2 + + B & A_pure -> B[b/t div 2] = f(t div 2) != q & t <= s + 1 + + 3a. Send purified result to A + A & B_pure -> false + + Invoke the ping-pong principle to extract interpolant. + + 2b. Solve for shared variables with EUF. + + epsilon a . A + = a := (s + 1) div 2 & s < t & f((s + 1) div 2) = q + + 3b. Send to B. Produces core + s < t & f((s + 1) div 2) = q + + 4b Solve again in arithmetic for shared variables with EUF. + + epsion a . A & (s >= t | f((s + 1) div 2) != q) + + a := t div 2 | s = t & f(t div 2) = q & even(t) + + Send to B, produces core (s != t | f(t div 2) != q) + + 5b. There is no longer a solution for A. A is unsat. + --*/ #include "ast/ast_util.h" @@ -129,7 +185,6 @@ namespace qe { // optionally minimize core using superposition. return mbi_unsat; case l_true: { - expr_ref_vector fmls(m); m_solver->get_model(mdl); model_evaluator mev(*mdl.get()); lits.reset(); @@ -263,3 +318,4 @@ namespace qe { } } }; + diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 0bc21d627..54fd1cb2b 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3438,7 +3438,6 @@ namespace smt { th->init_search_eh(); } m_qmanager->init_search_eh(); - m_assumption_core.reset(); m_incomplete_theories.reset(); m_num_conflicts = 0; m_num_conflicts_since_restart = 0; diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 7e09c3be2..e85348a07 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -894,7 +894,6 @@ namespace smt { failure m_last_search_failure; ptr_vector m_incomplete_theories; //!< theories that failed to produce a model bool m_searching; - ptr_vector m_assumption_core; unsigned m_num_conflicts; unsigned m_num_conflicts_since_restart; unsigned m_num_conflicts_since_lemma_gc; From 5fce4a1d1af81cd912a12d0f7dbb57acb7783a9d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 12 Jun 2018 11:59:18 -0700 Subject: [PATCH 296/364] Wire qe_solve_plugin into qe_term_graph Compiles. Not tested. --- src/qe/qe_solve_plugin.cpp | 45 +++---- src/qe/qe_term_graph.cpp | 241 +++++++++++++------------------------ src/qe/qe_term_graph.h | 41 ++++--- src/qe/qe_vartest.h | 7 +- 4 files changed, 136 insertions(+), 198 deletions(-) diff --git a/src/qe/qe_solve_plugin.cpp b/src/qe/qe_solve_plugin.cpp index ad4a03b5d..0a499d3b8 100644 --- a/src/qe/qe_solve_plugin.cpp +++ b/src/qe/qe_solve_plugin.cpp @@ -27,7 +27,7 @@ Revision History: namespace qe { expr_ref solve_plugin::operator()(expr* lit) { - if (m.is_not(lit, lit)) + if (m.is_not(lit, lit)) return solve(lit, false); else return solve(lit, true); @@ -39,9 +39,9 @@ namespace qe { arith_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("arith"), is_var), a(m) {} typedef std::pair signed_expr; - + /** - *\brief + *\brief * return r * (sum_{(sign,e) \in exprs} sign * e) */ expr_ref mk_term(bool is_int, rational const& r, bool sign, svector const& exprs) { @@ -124,7 +124,7 @@ namespace qe { return false; } - // is arg of the form a_val * v, where a_val + // is arg of the form a_val * v, where a_val // is a constant that we can safely divide by. bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { if (is_variable(arg)) { @@ -144,9 +144,9 @@ namespace qe { } return false; } - - expr_ref mk_eq_core (expr *e1, expr *e2) { + + expr_ref mk_eq_core (expr *e1, expr *e2) { expr_ref v(m), t(m); if (solve(e1, e2, v, t)) { return expr_ref(m.mk_eq(v, t), m); @@ -172,7 +172,6 @@ namespace qe { app* mk_le_zero(expr *arg) { expr *e1, *e2, *e3; - // XXX currently disabled if (a.is_add(arg, e1, e2)) { // e1-e2<=0 --> e1<=e2 if (a.is_times_minus_one(e2, e3)) { @@ -188,7 +187,6 @@ namespace qe { app* mk_ge_zero(expr *arg) { expr *e1, *e2, *e3; - // XXX currently disabled if (a.is_add(arg, e1, e2)) { // e1-e2>=0 --> e1>=e2 if (a.is_times_minus_one(e2, e3)) { @@ -249,17 +247,22 @@ namespace qe { return false; } - expr_ref solve(expr* lit, bool is_pos) override { + expr_ref solve(expr* atom, bool is_pos) override { expr *e1, *e2; - expr_ref res(lit, m); - if (m.is_eq (lit, e1, e2)) { - res = mk_eq_core(e1, e2); + expr_ref res(atom, m); + if (m.is_eq (atom, e1, e2)) { + expr_ref v(m), t(m); + v = e1; t = e2; + // -- attempt to solve using arithmetic + solve(e1, e2, v, t); + // -- normalize equality + res = mk_eq_core(v, t); } - else if (a.is_le(lit, e1, e2)) { + else if (a.is_le(atom, e1, e2)) { mk_le_core(e1, e2, res); } - else if (a.is_ge(lit, e1, e2)) { + else if (a.is_ge(atom, e1, e2)) { mk_ge_core(e1, e2, res); } @@ -273,7 +276,7 @@ namespace qe { class basic_solve_plugin : public solve_plugin { public: - basic_solve_plugin(ast_manager& m, is_variable_proc& is_var): + basic_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_basic_family_id(), is_var) {} expr_ref solve(expr *atom, bool is_pos) override { @@ -288,7 +291,7 @@ namespace qe { } else if (is_variable(rhs) && !is_variable(lhs)) { res = m.mk_eq(rhs, lhs); - } + } } // (ite cond (= VAR t) (= VAR t2)) case expr* cond = nullptr, *th = nullptr, *el = nullptr; @@ -296,7 +299,7 @@ namespace qe { expr_ref r1 = solve(th, true); expr_ref r2 = solve(el, true); expr* v1 = nullptr, *t1 = nullptr, *v2 = nullptr, *t2 = nullptr; - if (m.is_eq(r1, v1, t1) && m.is_eq(r2, v2, t2) && v1 == v2) { + if (m.is_eq(r1, v1, t1) && m.is_eq(r2, v2, t2) && v1 == v2) { res = m.mk_eq(v1, m.mk_ite(cond, t1, t2)); } } @@ -313,8 +316,8 @@ namespace qe { class dt_solve_plugin : public solve_plugin { datatype_util dt; public: - dt_solve_plugin(ast_manager& m, is_variable_proc& is_var): - solve_plugin(m, m.get_family_id("datatype"), is_var), + dt_solve_plugin(ast_manager& m, is_variable_proc& is_var): + solve_plugin(m, m.get_family_id("datatype"), is_var), dt(m) {} expr_ref solve(expr *atom, bool is_pos) override { @@ -350,11 +353,11 @@ namespace qe { } } // TBD: can also solve for is_nil(x) by x = nil - // + // return is_pos ? res : mk_not(res); } }; - + class bv_solve_plugin : public solve_plugin { public: bv_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("bv"), is_var) {} diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 396b5f092..9bf007428 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -27,6 +27,28 @@ Notes: namespace qe { + namespace is_pure_ns { + struct found{}; + struct proc { + is_variable_proc &m_is_var; + proc(is_variable_proc &is_var) : m_is_var(is_var) {} + void operator()(var *n) const {if (m_is_var(n)) throw found();} + void operator()(app const *n) const {if (m_is_var(n)) throw found();} + void operator()(quantifier *n) const {} + }; + } + + bool is_pure(is_variable_proc &is_var, expr *e) { + try { + is_pure_ns::proc v(is_var); + quick_for_each_expr(v, e); + } + catch (is_pure_ns::found) { + return false; + } + return true; + } + class term { // -- an app represented by this term expr* m_expr; // NSB: to make usable with exprs @@ -160,154 +182,46 @@ namespace qe { }; - class arith_term_graph_plugin : public term_graph_plugin { - term_graph &m_g; - ast_manager &m; - arith_util m_arith; - public: - arith_term_graph_plugin(term_graph &g) : - term_graph_plugin (g.get_ast_manager().mk_family_id("arith")), - m_g(g), m(g.get_ast_manager()), m_arith(m) {(void)m_g;} + bool term_graph::is_variable_proc::operator()(const expr * e) const { + if (!is_app(e)) return false; + const app *a = ::to_app(e); + if (a->get_family_id() != null_family_id) return false; + if (m_solved.contains(a->get_decl()->get_id())) return false; + return m_exclude == m_decls.contains(a->get_decl()->get_id()); + } + bool term_graph::is_variable_proc::operator()(const term &t) const { + return !t.is_theory() && m_exclude == m_decls.contains(t.get_decl_id()); + } - virtual ~arith_term_graph_plugin() {} + void term_graph::is_variable_proc::set_decls(const func_decl_ref_vector &decls, bool exclude) { + reset(); + m_exclude = exclude; + for (auto *d : decls) m_decls.insert(d->get_id(), true); + } + void term_graph::is_variable_proc::mark_solved(const expr *e) { + if ((*this)(e)) + m_solved.insert(::to_app(e)->get_decl()->get_id(), true); + } - bool mk_eq_core (expr *_e1, expr *_e2, expr_ref &res) { - expr *e1, *e2; - e1 = _e1; - e2 = _e2; - if (m_arith.is_zero(e1)) { - std::swap(e1, e2); - } - // y + -1*x == 0 --> y = x - expr *a0 = 0, *a1 = 0, *x = 0; - if (m_arith.is_zero(e2) && m_arith.is_add(e1, a0, a1)) { - if (m_arith.is_times_minus_one(a1, x)) { - e1 = a0; - e2 = x; - } - else if (m_arith.is_times_minus_one(a0, x)) { - e1 = a1; - e2 = x; - } - } - res = m.mk_eq(e1, e2); - return true; - } - - app* mk_le_zero(expr *arg) { - expr *e1, *e2, *e3; - if (m_arith.is_add(arg, e1, e2)) { - // e1-e2<=0 --> e1<=e2 - if (m_arith.is_times_minus_one(e2, e3)) { - return m_arith.mk_le(e1, e3); - } - // -e1+e2<=0 --> e2<=e1 - else if (m_arith.is_times_minus_one(e1, e3)) { - return m_arith.mk_le(e2, e3); - } - } - return m_arith.mk_le(arg, mk_zero()); - } - - app* mk_ge_zero(expr *arg) { - expr *e1, *e2, *e3; - if (m_arith.is_add(arg, e1, e2)) { - // e1-e2>=0 --> e1>=e2 - if (m_arith.is_times_minus_one(e2, e3)) { - return m_arith.mk_ge(e1, e3); - } - // -e1+e2>=0 --> e2>=e1 - else if (m_arith.is_times_minus_one(e1, e3)) { - return m_arith.mk_ge(e2, e3); - } - } - return m_arith.mk_ge(arg, mk_zero()); - } - - bool mk_le_core (expr *arg1, expr * arg2, expr_ref &result) { - // t <= -1 ==> t < 0 ==> ! (t >= 0) - rational n; - if (m_arith.is_int (arg1) && m_arith.is_minus_one (arg2)) { - result = m.mk_not (mk_ge_zero (arg1)); - return true; - } - else if (m_arith.is_zero(arg2)) { - result = mk_le_zero(arg1); - return true; - } - else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n < 0) { - // t <= n ==> t < n + 1 ==> ! (t >= n + 1) - result = m.mk_not(m_arith.mk_ge(arg1, m_arith.mk_numeral(n+1, true))); - return true; - } - return false; - } - expr * mk_zero () {return m_arith.mk_numeral (rational (0), true);} - bool is_one (expr const * n) const { - rational val; - return m_arith.is_numeral (n, val) && val.is_one (); - } - - bool mk_ge_core (expr * arg1, expr * arg2, expr_ref &result) { - // t >= 1 ==> t > 0 ==> ! (t <= 0) - rational n; - if (m_arith.is_int (arg1) && is_one (arg2)) { - result = m.mk_not (mk_le_zero (arg1)); - return true; - } - else if (m_arith.is_zero(arg2)) { - result = mk_ge_zero(arg1); - return true; - } - else if (m_arith.is_int(arg1) && m_arith.is_numeral(arg2, n) && n > 0) { - // t >= n ==> t > n - 1 ==> ! (t <= n - 1) - result = m.mk_not(m_arith.mk_le(arg1, m_arith.mk_numeral(n-1, true))); - return true; - } - return false; - } - - expr_ref process_lit (expr *_lit) override { - expr *lit = _lit; - expr *e1, *e2; - - // strip negation - bool is_neg = m.is_not(lit); - if (is_neg) { - lit = to_app(to_app(lit)->get_arg(0)); - } - - expr_ref res(m); - res = lit; - if (m.is_eq (lit, e1, e2)) { - mk_eq_core(e1, e2, res); - } - else if (m_arith.is_le(lit, e1, e2)) { - mk_le_core(e1, e2, res); - } - else if (m_arith.is_ge(lit, e1, e2)) { - mk_ge_core(e1, e2, res); - } - // restore negation - if (is_neg) { - res = mk_not(m, res); - } - return res; - } - }; unsigned term_graph::term_hash::operator()(term const* t) const { return t->get_hash(); } bool term_graph::term_eq::operator()(term const* a, term const* b) const { return term::cg_eq(a, b); } term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { - m_plugins.register_plugin (alloc(arith_term_graph_plugin, *this)); + m_plugins.register_plugin(mk_basic_solve_plugin(m, m_is_var)); + m_plugins.register_plugin(mk_arith_solve_plugin(m, m_is_var)); } term_graph::~term_graph() { reset(); } + bool term_graph::is_pure_def(expr *atom, expr *v) { + expr *e = nullptr; + return m.is_eq(atom, v, e) && m_is_var(v) && is_pure(m_is_var, e); + } + static family_id get_family_id(ast_manager &m, expr *lit) { if (m.is_not(lit, lit)) return get_family_id(m, lit); @@ -328,13 +242,9 @@ namespace qe { void term_graph::add_lit(expr *l) { expr_ref lit(m); - family_id fid = get_family_id (m, l); - term_graph_plugin *pin = m_plugins.get_plugin(fid); - if (pin) { - lit = pin->process_lit(l); - } else { - lit = l; - } + family_id fid = get_family_id(m, l); + qe::solve_plugin *pin = m_plugins.get_plugin(fid); + lit = pin ? (*pin)(l) : l; m_lits.push_back(lit); internalize_lit(lit); } @@ -620,8 +530,6 @@ namespace qe { ast_manager &m; u_map m_term2app; u_map m_root2rep; - u_map m_decls; - bool m_exclude; expr_ref_vector m_pinned; // tracks expr in the maps @@ -700,7 +608,7 @@ namespace qe { m_tg.reset_marks(); } - void solve() { + void solve_core() { ptr_vector worklist; for (term * t : m_tg.m_terms) { // skip pure terms @@ -772,9 +680,7 @@ namespace qe { while (r != &t); } - bool is_projected(const term &t) { - return m_exclude == m_decls.contains(t.get_decl_id()); - } + bool is_projected(const term &t) {return m_tg.m_is_var(t);} void mk_unpure_equalities(const term &t, expr_ref_vector &res) { expr *rep = nullptr; @@ -834,24 +740,20 @@ namespace qe { m_tg.reset_marks(); m_term2app.reset(); m_root2rep.reset(); - m_decls.reset(); m_pinned.reset(); } - expr_ref_vector project(func_decl_ref_vector const &decls, bool exclude) { + expr_ref_vector project() { expr_ref_vector res(m); - m_exclude = exclude; - for (auto *d : decls) {m_decls.insert(d->get_id(), true);} purify(); mk_lits(res); mk_pure_equalities(res); reset(); return res; } - expr_ref_vector solve(func_decl_ref_vector const &decls, bool exclude) { + expr_ref_vector solve() { expr_ref_vector res(m); - m_exclude = exclude; purify(); - solve(); + solve_core(); mk_lits(res); mk_unpure_equalities(res); reset(); @@ -860,14 +762,41 @@ namespace qe { }; } + void term_graph::solve_for_vars() { + expr_ref new_lit(m); + expr *old_lit = nullptr, *v = nullptr; + for (unsigned i = 0, sz = m_lits.size(); i < sz; ++i) { + old_lit = m_lits.get(i); + qe::solve_plugin *pin = m_plugins.get_plugin(get_family_id(m, old_lit)); + if (pin) { + new_lit = (*pin)(old_lit); + if (new_lit.get() != old_lit) { + m_lits.set(i, new_lit); + internalize_lit(new_lit); + } + if (is_pure_def(new_lit, v)) { + m_is_var.mark_solved(v); + } + } + } + m_is_var.reset_solved(); + } expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { + m_is_var.set_decls(decls, exclude); + solve_for_vars(); projector p(*this); - return p.project(decls, exclude); + m_is_var.reset(); + expr_ref_vector v = p.project(); + return v; } expr_ref_vector term_graph::solve(func_decl_ref_vector const &decls, bool exclude) { + m_is_var.set_decls(decls, exclude); + solve_for_vars(); projector p(*this); - return p.solve(decls, exclude); + expr_ref_vector v = p.solve(); + m_is_var.reset(); + return v; } } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 210941dac..e60b535c0 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -21,28 +21,31 @@ Notes: #include "ast/ast.h" #include "util/plugin_manager.h" +#include "qe/qe_solve_plugin.h" +#include "qe/qe_vartest.h" namespace qe { class term; - namespace {class projector;} - class term_graph_plugin { - family_id m_id; - public: - term_graph_plugin(family_id fid) : m_id(fid) {} - virtual ~term_graph_plugin() {} - - family_id get_family_id() const {return m_id;} - - /// Process (and potentially augment) a literal - virtual expr_ref process_lit (expr *lit) = 0; - }; - - class term_graph { friend class projector; + + class is_variable_proc : public ::is_variable_proc { + bool m_exclude; + u_map m_decls; + u_map m_solved; + public: + bool operator()(const expr *e) const override; + bool operator()(const term &t) const; + + void set_decls(const func_decl_ref_vector &decls, bool exclude); + void mark_solved(const expr *e); + void reset_solved() {m_solved.reset();} + void reset() {m_decls.reset(); m_solved.reset(); m_exclude = true;} + }; + struct term_hash { unsigned operator()(term const* t) const; }; struct term_eq { bool operator()(term const* a, term const* b) const; }; ast_manager & m; @@ -51,10 +54,11 @@ namespace qe { u_map m_app2term; ast_ref_vector m_pinned; u_map m_term2app; - plugin_manager m_plugins; + plugin_manager m_plugins; ptr_hashtable m_cg_table; vector> m_merge; + term_graph::is_variable_proc m_is_var; void merge(term &t1, term &t2); void merge_flush(); @@ -80,9 +84,10 @@ namespace qe { void mk_equalities(term const &t, expr_ref_vector &out); void mk_all_equalities(term const &t, expr_ref_vector &out); void display(std::ostream &out); - void project_core(func_decl_ref_vector const &decls, bool exclude, expr_ref_vector &result); - void solve_core(func_decl_ref_vector const &decls, bool exclude, expr_ref_vector &result); - bool is_solved_eq(expr *lhs, expr *rhs); + + bool is_pure_def(expr* atom, expr *v); + void solve_for_vars(); + public: term_graph(ast_manager &m); diff --git a/src/qe/qe_vartest.h b/src/qe/qe_vartest.h index 56d9229b8..52609893f 100644 --- a/src/qe/qe_vartest.h +++ b/src/qe/qe_vartest.h @@ -22,9 +22,10 @@ Revision History: #include "ast/ast.h" #include "util/uint_set.h" -class is_variable_proc { +// TBD: move under qe namespace +class is_variable_proc : public std::unary_function { public: - virtual bool operator()(expr* e) const = 0; + virtual bool operator()(const expr* e) const = 0; }; class is_variable_test : public is_variable_proc { @@ -42,7 +43,7 @@ public: m_num_decls(num_decls), m_var_kind(BY_NUM_DECLS) {} - bool operator()(expr* e) const override { + bool operator()(const expr* e) const override { if (!is_var(e)) { return false; } From 0ae246ad2b858f422ca0595de56cc758126f1345 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Jun 2018 12:41:01 -0700 Subject: [PATCH 297/364] add defs to arith solver Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 278 ++++++++++++++++++++------- src/math/simplex/model_based_opt.h | 33 +++- src/qe/qe_arith.cpp | 51 ++++- src/qe/qe_arith.h | 2 +- src/qe/qe_arrays.cpp | 4 +- src/qe/qe_arrays.h | 1 + src/qe/qe_datatypes.cpp | 3 + src/qe/qe_datatypes.h | 1 + src/qe/qe_mbp.h | 18 ++ src/test/CMakeLists.txt | 1 - src/test/arith_rewriter.cpp | 5 - src/test/main.cpp | 1 - src/test/model_based_opt.cpp | 25 ++- src/test/pdr.cpp | 128 ------------ 14 files changed, 326 insertions(+), 225 deletions(-) delete mode 100644 src/test/pdr.cpp diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 86b3cfa0c..2b175506f 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -35,11 +35,113 @@ std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { namespace opt { + /** + * Convert a row ax + coeffs + coeff = 0 into a definition for x + * x = -(coeffs + coeff)/a + * For rows ax + coeffs + coeff < 0 convert into + * x = -(coeffs + coeff - a)/a + */ + model_based_opt::def::def(row const& r, unsigned x) { + rational c = r.get_coefficient(x); + bool is_pos = c.is_pos(); + for (var const & v : r.m_vars) { + if (v.m_id != x) { + m_vars.push_back(v); + if (is_pos) m_vars.back().m_coeff.neg(); + } + } + m_coeff = r.m_coeff; + if (is_pos) m_coeff.neg(); + if (r.m_type == opt::t_lt) m_coeff += abs(c); + m_div = abs(c); + normalize(); + SASSERT(m_div.is_pos()); + } + + model_based_opt::def model_based_opt::def::operator+(def const& other) const { + def result; + vector const& vs1 = m_vars; + vector const& vs2 = other.m_vars; + vector & vs = result.m_vars; + rational c1(1), c2(1); + if (m_div != other.m_div) { + c1 = other.m_div; + c2 = m_div; + } + unsigned i = 0, j = 0; + while (i < vs1.size() || j < vs2.size()) { + unsigned v1 = UINT_MAX, v2 = UINT_MAX; + if (i < vs1.size()) v1 = vs1[i].m_id; + if (j < vs2.size()) v2 = vs2[j].m_id; + if (v1 == v2) { + vs.push_back(vs1[i]); + vs.back().m_coeff *= c1; + vs.back().m_coeff += c2 * vs2[j].m_coeff; + ++i; ++j; + if (vs.back().m_coeff.is_zero()) { + vs.pop_back(); + } + } + else if (v1 < v2) { + vs.push_back(vs1[i]); + vs.back().m_coeff *= c1; + } + else { + vs.push_back(vs2[j]); + vs.back().m_coeff *= c2; + } + } + result.m_div = c1*m_div; + result.m_coeff = (m_coeff*c1) + (other.m_coeff*c2); + result.normalize(); + return result; + } + + model_based_opt::def model_based_opt::def::operator/(rational const& r) const { + def result(*this); + result.m_div *= r; + result.normalize(); + return result; + } + + model_based_opt::def model_based_opt::def::operator*(rational const& n) const { + def result(*this); + for (var& v : result.m_vars) { + v.m_coeff *= n; + } + result.m_coeff *= n; + result.normalize(); + return result; + } + + model_based_opt::def model_based_opt::def::operator+(rational const& n) const { + def result(*this); + result.m_coeff += n * result.m_div; + result.normalize(); + return result; + } + + void model_based_opt::def::normalize() { + if (m_div.is_one()) return; + rational g(m_div); + g = gcd(g, m_coeff); + for (var const& v : m_vars) { + g = gcd(g, abs(v.m_coeff)); + if (g.is_one()) break; + } + if (!g.is_one()) { + for (var& v : m_vars) { + v.m_coeff /= g; + } + m_coeff /= g; + m_div /= g; + } + } + model_based_opt::model_based_opt() { m_rows.push_back(row()); } - bool model_based_opt::invariant() { for (unsigned i = 0; i < m_rows.size(); ++i) { @@ -105,14 +207,14 @@ namespace opt { if (find_bound(x, bound_row_index, bound_coeff, coeff.is_pos())) { SASSERT(!bound_coeff.is_zero()); TRACE("opt", display(tout << "update: " << v << " ", objective()); - for (unsigned i = 0; i < m_above.size(); ++i) { - display(tout << "resolve: ", m_rows[m_above[i]]); + for (unsigned above : m_above) { + display(tout << "resolve: ", m_rows[above]); }); - for (unsigned i = 0; i < m_above.size(); ++i) { - resolve(bound_row_index, bound_coeff, m_above[i], x); + for (unsigned above : m_above) { + resolve(bound_row_index, bound_coeff, above, x); } - for (unsigned i = 0; i < m_below.size(); ++i) { - resolve(bound_row_index, bound_coeff, m_below[i], x); + for (unsigned below : m_below) { + resolve(bound_row_index, bound_coeff, below, x); } // coeff*x + objective <= ub // a2*x + t2 <= 0 @@ -151,8 +253,7 @@ namespace opt { rational old_val = m_var2value[x]; m_var2value[x] = val; unsigned_vector const& row_ids = m_var2row_ids[x]; - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id = row_ids[i]; + for (unsigned row_id : row_ids) { rational coeff = get_coefficient(row_id, x); if (coeff.is_zero()) { continue; @@ -166,8 +267,7 @@ namespace opt { void model_based_opt::update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail) { - for (unsigned i = bound_trail.size(); i > 0; ) { - --i; + for (unsigned i = bound_trail.size(); i-- > 0; ) { unsigned x = bound_vars[i]; row& r = m_rows[bound_trail[i]]; rational val = r.m_coeff; @@ -222,8 +322,7 @@ namespace opt { } // update and check bounds for all other affected rows. - for (unsigned i = bound_trail.size(); i > 0; ) { - --i; + for (unsigned i = bound_trail.size(); i-- > 0; ) { unsigned x = bound_vars[i]; unsigned_vector const& row_ids = m_var2row_ids[x]; for (unsigned row_id : row_ids) { @@ -243,8 +342,7 @@ namespace opt { uint_set visited; m_above.reset(); m_below.reset(); - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id = row_ids[i]; + for (unsigned row_id : row_ids) { SASSERT(row_id != m_objective_id); if (visited.contains(row_id)) { continue; @@ -291,8 +389,7 @@ namespace opt { rational model_based_opt::get_row_value(row const& r) const { vector const& vars = r.m_vars; rational val = r.m_coeff; - for (unsigned i = 0; i < vars.size(); ++i) { - var const& v = vars[i]; + for (var const& v : vars) { val += v.m_coeff * m_var2value[v.m_id]; } return val; @@ -300,14 +397,18 @@ namespace opt { rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) const { row const& r = m_rows[row_id]; - if (r.m_vars.empty()) { + return r.get_coefficient(var_id); + } + + rational model_based_opt::row::get_coefficient(unsigned var_id) const { + if (m_vars.empty()) { return rational::zero(); } - unsigned lo = 0, hi = r.m_vars.size(); + unsigned lo = 0, hi = m_vars.size(); while (lo < hi) { unsigned mid = lo + (hi - lo)/2; SASSERT(mid < hi); - unsigned id = r.m_vars[mid].m_id; + unsigned id = m_vars[mid].m_id; if (id == var_id) { lo = mid; break; @@ -319,12 +420,12 @@ namespace opt { hi = mid; } } - if (lo == r.m_vars.size()) { + if (lo == m_vars.size()) { return rational::zero(); } - unsigned id = r.m_vars[lo].m_id; + unsigned id = m_vars[lo].m_id; if (id == var_id) { - return r.m_vars[lo].m_coeff; + return m_vars[lo].m_coeff; } else { return rational::zero(); @@ -386,7 +487,6 @@ namespace opt { SASSERT(a1 == get_coefficient(row_src, x)); SASSERT(a1.is_pos()); SASSERT(row_src != row_dst); - SASSERT(m_rows[row_src].m_type == t_eq); if (!m_rows[row_dst].m_alive) return; rational a2 = get_coefficient(row_dst, x); mul(row_dst, a1); @@ -593,40 +693,52 @@ namespace opt { } void model_based_opt::display(std::ostream& out) const { - for (unsigned i = 0; i < m_rows.size(); ++i) { - display(out, m_rows[i]); + for (auto const& r : m_rows) { + display(out, r); } for (unsigned i = 0; i < m_var2row_ids.size(); ++i) { unsigned_vector const& rows = m_var2row_ids[i]; out << i << ": "; - for (unsigned j = 0; j < rows.size(); ++j) { - out << rows[j] << " "; + for (auto const& r : rows) { + out << r << " "; } out << "\n"; } } - void model_based_opt::display(std::ostream& out, row const& r) const { - vector const& vars = r.m_vars; - out << (r.m_alive?"+":"-") << " "; + void model_based_opt::display(std::ostream& out, vector const& vars, rational const& coeff) { for (unsigned i = 0; i < vars.size(); ++i) { if (i > 0 && vars[i].m_coeff.is_pos()) { out << "+ "; } out << vars[i].m_coeff << "* v" << vars[i].m_id << " "; } - if (r.m_coeff.is_pos()) { - out << " + " << r.m_coeff << " "; + if (coeff.is_pos()) { + out << " + " << coeff << " "; } - else if (r.m_coeff.is_neg()) { - out << r.m_coeff << " "; - } + else if (coeff.is_neg()) { + out << coeff << " "; + } + } + + std::ostream& model_based_opt::display(std::ostream& out, row const& r) { + out << (r.m_alive?"+":"-") << " "; + display(out, r.m_vars, r.m_coeff); if (r.m_type == opt::t_mod) { out << r.m_type << " " << r.m_mod << " = 0; value: " << r.m_value << "\n"; } else { out << r.m_type << " 0; value: " << r.m_value << "\n"; } + return out; + } + + std::ostream& model_based_opt::display(std::ostream& out, def const& r) { + display(out, r.m_vars, r.m_coeff); + if (!r.m_div.is_one()) { + out << " / " << r.m_div; + } + return out; } unsigned model_based_opt::add_var(rational const& value, bool is_int) { @@ -648,10 +760,10 @@ namespace opt { r.m_vars.append(coeffs.size(), coeffs.c_ptr()); bool is_int_row = true; std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); - for (unsigned i = 0; i < coeffs.size(); ++i) { - val += m_var2value[coeffs[i].m_id] * coeffs[i].m_coeff; - SASSERT(!is_int(coeffs[i].m_id) || coeffs[i].m_coeff.is_int()); - is_int_row &= is_int(coeffs[i].m_id); + for (auto const& c : coeffs) { + val += m_var2value[c.m_id] * c.m_coeff; + SASSERT(!is_int(c.m_id) || c.m_coeff.is_int()); + is_int_row &= is_int(c.m_id); } r.m_alive = true; r.m_coeff = c; @@ -738,7 +850,7 @@ namespace opt { // t0 <= s for each s (M inequalities). // If N >= M the construction is symmetric. // - void model_based_opt::project(unsigned x) { + model_based_opt::def model_based_opt::project(unsigned x, bool compute_def) { unsigned_vector& lub_rows = m_lub; unsigned_vector& glb_rows = m_glb; unsigned_vector& mod_rows = m_mod; @@ -802,28 +914,41 @@ namespace opt { } if (!mod_rows.empty()) { - solve_mod(x, mod_rows); - return; + return solve_mod(x, mod_rows, compute_def); } if (eq_row != UINT_MAX) { - solve_for(eq_row, x); - return; + return solve_for(eq_row, x, compute_def); } - + + def result; unsigned lub_size = lub_rows.size(); unsigned glb_size = glb_rows.size(); unsigned row_index = (lub_size <= glb_size) ? lub_index : glb_index; - glb_rows.append(lub_rows); // There are only upper or only lower bounds. if (row_index == UINT_MAX) { - for (unsigned row_id : glb_rows) { - SASSERT(m_rows[row_id].m_alive); - SASSERT(!get_coefficient(row_id, x).is_zero()); - retire_row(row_id); + if (compute_def) { + if (lub_index != UINT_MAX) { + result = solve_for(lub_index, x, true); + } + else if (glb_index != UINT_MAX) { + result = solve_for(glb_index, x, true); + } } - return; + else { + for (unsigned row_id : lub_rows) retire_row(row_id); + for (unsigned row_id : glb_rows) retire_row(row_id); + } + return result; + } + + SASSERT(lub_index != UINT_MAX); + SASSERT(glb_index != UINT_MAX); + if (compute_def) { + def d1(m_rows[lub_index], x); + def d2(m_rows[lub_index], x); + result = (d1 + d2)/2; } // The number of matching lower and upper bounds is small. @@ -832,10 +957,9 @@ namespace opt { (!is_int(x) || lub_is_unit || glb_is_unit)) { for (unsigned i = 0; i < lub_size; ++i) { unsigned row_id1 = lub_rows[i]; - bool last = i + 1 == lub_rows.size(); + bool last = i + 1 == lub_size; rational coeff = get_coefficient(row_id1, x); - for (unsigned j = 0; j < glb_size; ++j) { - unsigned row_id2 = glb_rows[j]; + for (unsigned row_id2 : glb_rows) { if (last) { resolve(row_id1, coeff, row_id2, x); } @@ -845,20 +969,25 @@ namespace opt { } } } - for (unsigned i = 0; i < lub_size; ++i) { - retire_row(lub_rows[i]); - } - return; + for (unsigned row_id : lub_rows) retire_row(row_id); + + return result; } // General case. rational coeff = get_coefficient(row_index, x); + for (unsigned row_id : lub_rows) { + if (row_id != row_index) { + resolve(row_index, coeff, row_id, x); + } + } for (unsigned row_id : glb_rows) { if (row_id != row_index) { resolve(row_index, coeff, row_id, x); } } retire_row(row_index); + return result; } // @@ -876,7 +1005,7 @@ namespace opt { // x := D*x' + u // - void model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows) { + model_based_opt::def model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows, bool compute_def) { SASSERT(!mod_rows.empty()); rational D(1); for (unsigned idx : mod_rows) { @@ -914,7 +1043,11 @@ namespace opt { visited.insert(row_id); } } - project(y); + def result = project(y, compute_def); + if (compute_def) { + result = (result * D) + u; + } + return result; } // update row with: x |-> C @@ -961,16 +1094,21 @@ namespace opt { // 3x + t = 0 & 7 | (c*x + s) & ax <= u // 3 | -t & 21 | (-ct + 3s) & a-t <= 3u - void model_based_opt::solve_for(unsigned row_id1, unsigned x) { + model_based_opt::def model_based_opt::solve_for(unsigned row_id1, unsigned x, bool compute_def) { + TRACE("opt", tout << "v" << x << "\n" << m_rows[row_id1] << "\n";); rational a = get_coefficient(row_id1, x), b; + ineq_type ty = m_rows[row_id1].m_type; SASSERT(!a.is_zero()); - SASSERT(m_rows[row_id1].m_type == t_eq); SASSERT(m_rows[row_id1].m_alive); if (a.is_neg()) { a.neg(); m_rows[row_id1].neg(); } SASSERT(a.is_pos()); + if (ty == t_lt) { + SASSERT(compute_def); + m_rows[row_id1].m_coeff += a; + } if (m_var2is_int[x] && !a.is_one()) { row& r1 = m_rows[row_id1]; vector coeffs; @@ -983,8 +1121,7 @@ namespace opt { visited.insert(row_id1); for (unsigned row_id2 : row_ids) { if (!visited.contains(row_id2)) { - visited.insert(row_id2); - + visited.insert(row_id2); b = get_coefficient(row_id2, x); if (!b.is_zero()) { row& dst = m_rows[row_id2]; @@ -1002,14 +1139,19 @@ namespace opt { } } } + // TBD: -t div a + def result(m_rows[row_id1], x); retire_row(row_id1); + return result; } - - void model_based_opt::project(unsigned num_vars, unsigned const* vars) { + + vector model_based_opt::project(unsigned num_vars, unsigned const* vars, bool compute_def) { + vector result; for (unsigned i = 0; i < num_vars; ++i) { - project(vars[i]); + result.push_back(project(vars[i], compute_def)); TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n");); } + return result; } } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 9546529f2..268d3d81d 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -60,6 +60,23 @@ namespace opt { void reset() { m_vars.reset(); m_coeff.reset(); m_value.reset(); } void neg() { for (var & v : m_vars) v.m_coeff.neg(); m_coeff.neg(); m_value.neg(); } + rational get_coefficient(unsigned x) const; + }; + + // A definition is a linear term of the form (vars + coeff) / div + struct def { + def(): m_div(1) {} + def(row const& r, unsigned x); + def(def const& other): m_vars(other.m_vars), m_coeff(other.m_coeff), m_div(other.m_div) {} + vector m_vars; + rational m_coeff; + rational m_div; + def operator+(def const& other) const; + def operator/(unsigned n) const { return *this / rational(n); } + def operator/(rational const& n) const; + def operator*(rational const& n) const; + def operator+(rational const& n) const; + void normalize(); }; private: @@ -121,12 +138,12 @@ namespace opt { void update_value(unsigned x, rational const& val); - void project(unsigned var); + def project(unsigned var, bool compute_def); - void solve_for(unsigned row_id, unsigned x); - - void solve_mod(unsigned x, unsigned_vector const& mod_rows); + def solve_for(unsigned row_id, unsigned x, bool compute_def); + def solve_mod(unsigned x, unsigned_vector const& mod_rows, bool compute_def); + bool is_int(unsigned x) const { return m_var2is_int[x]; } void retire_row(unsigned row_id); @@ -163,7 +180,7 @@ namespace opt { // // Project set of variables from inequalities. // - void project(unsigned num_vars, unsigned const* vars); + vector project(unsigned num_vars, unsigned const* vars, bool compute_def); // // Extract current rows (after projection). @@ -171,13 +188,17 @@ namespace opt { void get_live_rows(vector& rows); void display(std::ostream& out) const; - void display(std::ostream& out, row const& r) const; + static std::ostream& display(std::ostream& out, row const& r); + static std::ostream& display(std::ostream& out, def const& r); + static void display(std::ostream& out, vector const& vars, rational const& coeff); }; } std::ostream& operator<<(std::ostream& out, opt::ineq_type ie); +inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::def const& d) { return opt::model_based_opt::display(out, d); } +inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::row const& r) { return opt::model_based_opt::display(out, r); } inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::var const v) { return out << "v" << v.m_id; } diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 0623bc8bb..0b5ad72ca 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -283,14 +283,29 @@ namespace qe { typedef opt::model_based_opt::row row; typedef vector vars; + vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return project(model, vars, lits, true); + } + void operator()(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + project(model, vars, fmls, false); + } + + expr_ref var2expr(ptr_vector const& index2expr, var const& v) { + expr_ref t(index2expr[v.m_id], m); + if (!v.m_coeff.is_one()) { + t = a.mk_mul(a.mk_numeral(v.m_coeff, a.is_int(t)), t); + } + return t; + } + + vector project(model& model, app_ref_vector& vars, expr_ref_vector& fmls, bool compute_def) { bool has_arith = false; - for (unsigned i = 0; !has_arith && i < vars.size(); ++i) { - expr* v = vars[i].get(); + for (expr* v : vars) { has_arith |= is_arith(v); } if (!has_arith) { - return; + return vector(); } model_evaluator eval(model); // eval.set_model_completion(true); @@ -359,7 +374,7 @@ namespace qe { tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n"; } mbo.display(tout);); - mbo.project(real_vars.size(), real_vars.c_ptr()); + vector defs = mbo.project(real_vars.size(), real_vars.c_ptr(), compute_def); TRACE("qe", mbo.display(tout);); vector rows; mbo.get_live_rows(rows); @@ -419,6 +434,30 @@ namespace qe { val = eval(t); CTRACE("qe", !m.is_true(val), tout << "Evaluated " << t << " to " << val << "\n";); } + vector result; + if (compute_def) { + SASSERT(defs.size() == real_vars.size()); + for (unsigned i = 0; i < defs.size(); ++i) { + auto const& d = defs[i]; + expr* x = index2expr[real_vars[i]]; + bool is_int = a.is_int(x); + expr_ref_vector ts(m); + expr_ref t(m); + for (var const& v : d.m_vars) { + ts.push_back(var2expr(index2expr, v)); + } + ts.push_back(a.mk_numeral(d.m_coeff, is_int)); + t = a.mk_add(ts.size(), ts.c_ptr()); + if (!d.m_div.is_one() && is_int) { + t = a.mk_idiv(t, a.mk_numeral(d.m_div, is_int)); + } + else if (!d.m_div.is_one() && !is_int) { + t = a.mk_div(t, a.mk_numeral(d.m_div, is_int)); + } + result.push_back(def(expr_ref(x, m), t)); + } + } + return result; } opt::inf_eps maximize(expr_ref_vector const& fmls0, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { @@ -534,6 +573,10 @@ namespace qe { (*m_imp)(model, vars, lits); } + vector arith_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return m_imp->project(model, vars, lits); + } + bool arith_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return m_imp->solve(model, vars, lits); } diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index 8e2400ac7..c01d7bbb6 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -30,7 +30,7 @@ namespace qe { bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; family_id get_family_id() override; void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; - + vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); }; diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 651c167b6..138aed1df 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -1419,7 +1419,9 @@ namespace qe { ); } - + vector array_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return vector(); + } }; diff --git a/src/qe/qe_arrays.h b/src/qe/qe_arrays.h index f2d69b564..3bb90335d 100644 --- a/src/qe/qe_arrays.h +++ b/src/qe/qe_arrays.h @@ -36,6 +36,7 @@ namespace qe { bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; void operator()(model& model, app_ref_vector& vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects); family_id get_family_id() override; + vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; }; }; diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp index 770beb59d..87ae97857 100644 --- a/src/qe/qe_datatypes.cpp +++ b/src/qe/qe_datatypes.cpp @@ -300,6 +300,9 @@ namespace qe { return m_imp->solve(model, vars, lits); } + vector datatype_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return vector(); + } family_id datatype_project_plugin::get_family_id() { return m_imp->dt.get_family_id(); diff --git a/src/qe/qe_datatypes.h b/src/qe/qe_datatypes.h index 9c8eb5a76..0483f4cce 100644 --- a/src/qe/qe_datatypes.h +++ b/src/qe/qe_datatypes.h @@ -35,6 +35,7 @@ namespace qe { bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; family_id get_family_id() override; + vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; }; }; diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index bddec8065..c35351fb3 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -31,14 +31,32 @@ namespace qe { struct cant_project {}; + struct def { + expr_ref var, term; + def(expr_ref& v, expr_ref& t): var(v), term(t) {} + }; + class project_plugin { public: virtual ~project_plugin() {} virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) = 0; + /** + \brief partial solver. + */ virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0; virtual family_id get_family_id() = 0; + virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { }; + /** + \brief project vars modulo model, return set of definitions for eliminated variables. + - vars in/out: returns variables that were not eliminated + - lits in/out: returns projected literals + - returns set of definitions + (TBD: in triangular form, the last definition can be substituted into definitions that come before) + */ + virtual vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0; + static expr_ref pick_equality(ast_manager& m, model& model, expr* t); static void erase(expr_ref_vector& lits, unsigned& i); static void push_back(expr_ref_vector& lits, expr* lit); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 1dc0290d8..7367a52ff 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -81,7 +81,6 @@ add_executable(test-z3 optional.cpp parray.cpp pb2bv.cpp - pdr.cpp permutation.cpp polynomial.cpp polynorm.cpp diff --git a/src/test/arith_rewriter.cpp b/src/test/arith_rewriter.cpp index a279d58b2..d0a110c4f 100644 --- a/src/test/arith_rewriter.cpp +++ b/src/test/arith_rewriter.cpp @@ -10,7 +10,6 @@ Copyright (c) 2015 Microsoft Corporation #include "ast/reg_decl_plugins.h" #include "ast/rewriter/th_rewriter.h" #include "model/model.h" -#include "muz/pdr/pdr_util.h" #include "parsers/smt2/smt2parser.h" @@ -53,13 +52,9 @@ void tst_arith_rewriter() { expr_ref fml = parse_fml(m, example1); rw(fml); std::cout << mk_pp(fml, m) << "\n"; - pdr::normalize_arithmetic(fml); - std::cout << mk_pp(fml, m) << "\n"; fml = parse_fml(m, example2); rw(fml); std::cout << mk_pp(fml, m) << "\n"; - pdr::normalize_arithmetic(fml); - std::cout << mk_pp(fml, m) << "\n"; } diff --git a/src/test/main.cpp b/src/test/main.cpp index 41f051f24..d330610d9 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -239,7 +239,6 @@ int main(int argc, char ** argv) { TST(theory_pb); TST(simplex); TST(sat_user_scope); - TST(pdr); TST_ARGV(ddnf); TST(ddnf1); TST(model_evaluator); diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp index 7b3b95afd..f2f6004ff 100644 --- a/src/test/model_based_opt.cpp +++ b/src/test/model_based_opt.cpp @@ -218,6 +218,12 @@ static void test4() { std::cout << "u: " << mbo.get_value(u) << "\n"; } +static void display_project(vector const& defs) { + for (auto const& d : defs) { + std::cout << d << "\n"; + } +} + static void test5() { opt::model_based_opt mbo; unsigned x = mbo.add_var(rational(2)); @@ -231,13 +237,13 @@ static void test5() { add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); unsigned vars[2] = { y, z }; - mbo.project(1, vars); + display_project(mbo.project(1, vars, true)); mbo.display(std::cout); - mbo.project(1, vars); + display_project(mbo.project(1, vars, true)); mbo.display(std::cout); - mbo.project(1, vars+1); + display_project(mbo.project(1, vars+1, true)); mbo.display(std::cout); vector rows; @@ -263,7 +269,7 @@ static void test6() { add_ineq(mbo, y, 1, w, -1, 1, opt::t_le); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } @@ -285,7 +291,7 @@ static void test7() { add_ineq(mbo, y, 1, w, -1, 1, opt::t_lt); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } @@ -306,7 +312,7 @@ static void test8() { add_ineq(mbo, y, 1, v, -1, 1, opt::t_le); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } @@ -327,7 +333,7 @@ static void test9() { add_ineq(mbo, y, 1, v, -1, 1, opt::t_le); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } @@ -348,9 +354,9 @@ static void test10() { add_ineq(mbo, y, 3, v, -6, 1, opt::t_le); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); - mbo.project(1, &x0); + display_project(mbo.project(1, &x0, true)); mbo.display(std::cout); } @@ -358,7 +364,6 @@ static void test10() { void tst_model_based_opt() { test10(); - return; check_random_ineqs(); test1(); test2(); diff --git a/src/test/pdr.cpp b/src/test/pdr.cpp deleted file mode 100644 index 45d9eea7e..000000000 --- a/src/test/pdr.cpp +++ /dev/null @@ -1,128 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -#include "muz/pdr/pdr_context.h" -#include "ast/reg_decl_plugins.h" - - -using namespace pdr; - -static expr_ref mk_state(expr_ref_vector const& states, random_gen& rand) { - expr_ref result(states.get_manager()); - result = states[rand(states.size())]; - return result; -} - - -struct test_model_search { - struct init_test { - init_test(func_decl_ref& fn) { - ast_manager& m = fn.get_manager(); - reg_decl_plugins(m); - fn = m.mk_const_decl(symbol("f"), m.mk_bool_sort()); - } - }; - - ast_manager m; - smt_params m_smt_params; - fixedpoint_params fp_params; - context ctx; - manager pm; - func_decl_ref fn; - init_test initt; - pred_transformer pt; - random_gen rand; - model_search search; - expr_ref_vector states; - - - test_model_search(): - ctx(m_smt_params, fp_params, m), - pm(m_smt_params, 10, m), - fn(m), - initt(fn), - pt(ctx, pm, fn), - rand(10), - search(true), - states(m) { - } - - void add_tree(model_node* parent, bool force_goal) { - unsigned level = parent->level(); - search.add_leaf(*parent); - expr_ref state(m); - if (level > 0 && (force_goal || parent->is_goal())) { - search.remove_goal(*parent); - - state = mk_state(states, rand); - add_tree(alloc(model_node, parent, state, pt, level-1), false); - - state = mk_state(states, rand); - add_tree(alloc(model_node, parent, state, pt, level-1), false); - parent->check_pre_closed(); - } - } - - bool mutate() { - model_node* leaf = search.next(); - if (!leaf) return false; - unsigned level = leaf->level(); - if (level == 0) { - if (rand(2) == 0) { - leaf->display(std::cout << "backtrack to grandparent\n", 1); - search.backtrack_level(false, *leaf->parent()); - } - else { - leaf->display(std::cout << "backtrack to parent\n", 1); - search.backtrack_level(false, *leaf); - } - } - else { - leaf->display(std::cout << "grow tree\n", 1); - add_tree(leaf, true); - } - return true; - } - - void init() { - std::cout << "pdr state-hash search tree\n"; - - expr_ref state(m); - func_decl_ref fn(m); - for (unsigned i = 0; i < 10; ++i) { - std::ostringstream strm; - strm << "s" << i; - state = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); - fn = to_app(state)->get_decl(); - states.push_back(state); - } - state = states[0].get(); - unsigned level = 4; - for(unsigned n = 0; n < 100; ++n) { - state = mk_state(states, rand); - model_node* root = alloc(model_node, nullptr, state, pt, level); - search.set_root(root); - add_tree(root, false); - search.display(std::cout); - - while (true) { - search.well_formed(); - if (!mutate()) break; - search.display(std::cout); - } - search.reset(); - //++level; - } - search.reset(); - } - -}; - -void tst_pdr() { - test_model_search test; - - test.init(); -} From ec8e3f2aeef9c9c3bfe6ee76bf8de92450c1a525 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Jun 2018 13:09:06 -0700 Subject: [PATCH 298/364] consolidate use of plugin by moving declarations up front (separate from constructor at this point) Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 3 +- src/qe/qe_mbi.cpp | 3 +- src/qe/qe_term_graph.cpp | 103 +++++++++++------------- src/qe/qe_term_graph.h | 14 ++-- 4 files changed, 56 insertions(+), 67 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 62d5ea3ea..d45cb9df9 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -522,8 +522,9 @@ public: for (expr* e : m_lits) lits.push_back(e); flatten_and(lits); qe::term_graph tg(m); + tg.set_vars(vars, false); tg.add_lits(lits); - expr_ref_vector p = tg.project(vars, false); + expr_ref_vector p = tg.project(); ctx.regular_stream() << p << "\n"; } diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 509800c62..09ab76559 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -206,9 +206,10 @@ namespace qe { m_dual_solver->get_unsat_core(core); TRACE("qe", tout << "core: " << core << "\n";); // project the implicant onto vars + tg.set_vars(vars, false); tg.add_lits(core); lits.reset(); - lits.append(tg.project(vars, false)); + lits.append(tg.project()); TRACE("qe", tout << "project: " << lits << "\n";); return mbi_sat; case l_undef: diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 9bf007428..d4cde6b4f 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -51,7 +51,7 @@ namespace qe { class term { // -- an app represented by this term - expr* m_expr; // NSB: to make usable with exprs + expr_ref m_expr; // NSB: to make usable with exprs // -- root of the equivalence class term* m_root; // -- next element in the equivalence class (cyclic linked list) @@ -72,7 +72,7 @@ namespace qe { ptr_vector m_children; public: - term(expr* v, u_map& app2term) : + term(expr_ref const& v, u_map& app2term) : m_expr(v), m_root(this), m_next(this), @@ -185,22 +185,24 @@ namespace qe { bool term_graph::is_variable_proc::operator()(const expr * e) const { if (!is_app(e)) return false; const app *a = ::to_app(e); - if (a->get_family_id() != null_family_id) return false; - if (m_solved.contains(a->get_decl()->get_id())) return false; - return m_exclude == m_decls.contains(a->get_decl()->get_id()); + return + a->get_family_id() == null_family_id && + !m_solved.contains(a->get_decl()) && + m_exclude == m_decls.contains(a->get_decl()); } + bool term_graph::is_variable_proc::operator()(const term &t) const { - return !t.is_theory() && m_exclude == m_decls.contains(t.get_decl_id()); + return (*this)(t.get_app()); } void term_graph::is_variable_proc::set_decls(const func_decl_ref_vector &decls, bool exclude) { reset(); m_exclude = exclude; - for (auto *d : decls) m_decls.insert(d->get_id(), true); + for (auto *d : decls) m_decls.insert(d); } void term_graph::is_variable_proc::mark_solved(const expr *e) { - if ((*this)(e)) - m_solved.insert(::to_app(e)->get_decl()->get_id(), true); + if ((*this)(e) && is_app(e)) + m_solved.insert(::to_app(e)->get_decl()); } @@ -217,7 +219,7 @@ namespace qe { reset(); } - bool term_graph::is_pure_def(expr *atom, expr *v) { + bool term_graph::is_pure_def(expr *atom, expr*& v) { expr *e = nullptr; return m.is_eq(atom, v, e) && m_is_var(v) && is_pure(m_is_var, e); } @@ -241,12 +243,21 @@ namespace qe { } void term_graph::add_lit(expr *l) { expr_ref lit(m); - - family_id fid = get_family_id(m, l); - qe::solve_plugin *pin = m_plugins.get_plugin(fid); - lit = pin ? (*pin)(l) : l; - m_lits.push_back(lit); - internalize_lit(lit); + expr_ref_vector lits(m); + lits.push_back(l); + for (unsigned i = 0; i < lits.size(); ++i) { + l = lits.get(i); + family_id fid = get_family_id(m, l); + qe::solve_plugin *pin = m_plugins.get_plugin(fid); + lit = pin ? (*pin)(l) : l; + if (m.is_and(lit)) { + lits.append(::to_app(lit)->get_num_args(), ::to_app(lit)->get_args()); + } + else { + m_lits.push_back(lit); + internalize_lit(lit); + } + } } bool term_graph::is_internalized(expr *a) { @@ -259,7 +270,8 @@ namespace qe { } term *term_graph::mk_term(expr *a) { - term * t = alloc(term, a, m_app2term); + expr_ref e(a, m); + term * t = alloc(term, e, m_app2term); if (t->get_num_args() == 0 && m.is_unique_value(a)){ t->mark_as_interpreted(); } @@ -304,13 +316,16 @@ namespace qe { } void term_graph::internalize_lit(expr* lit) { - expr *e1 = nullptr, *e2 = nullptr; + expr *e1 = nullptr, *e2 = nullptr, *v = nullptr; if (m.is_eq (lit, e1, e2)) { internalize_eq (e1, e2); } else { internalize_term(lit); } + if (is_pure_def(lit, v)) { + m_is_var.mark_solved(v); + } } void term_graph::merge_flush() { @@ -721,16 +736,8 @@ namespace qe { } // TBD: generalize for also the case of a (:var n) - bool is_solved_eq(expr *_lhs, expr* _rhs) { - if (!is_app(_lhs) || !is_app(_rhs)) return false; - app *lhs, *rhs; - lhs = ::to_app(_lhs); - rhs = ::to_app(_rhs); - - if (rhs->get_num_args() > 0) return false; - if (rhs->get_family_id() != null_family_id) return false; - - return !occurs(rhs, lhs); + bool is_solved_eq(expr *lhs, expr* rhs) { + return is_uninterp_const(rhs) && !occurs(rhs, lhs); } public: @@ -762,41 +769,21 @@ namespace qe { }; } - void term_graph::solve_for_vars() { - expr_ref new_lit(m); - expr *old_lit = nullptr, *v = nullptr; - for (unsigned i = 0, sz = m_lits.size(); i < sz; ++i) { - old_lit = m_lits.get(i); - qe::solve_plugin *pin = m_plugins.get_plugin(get_family_id(m, old_lit)); - if (pin) { - new_lit = (*pin)(old_lit); - if (new_lit.get() != old_lit) { - m_lits.set(i, new_lit); - internalize_lit(new_lit); - } - if (is_pure_def(new_lit, v)) { - m_is_var.mark_solved(v); - } - } - } - m_is_var.reset_solved(); - } - expr_ref_vector term_graph::project(func_decl_ref_vector const& decls, bool exclude) { + void term_graph::set_vars(func_decl_ref_vector const& decls, bool exclude) { m_is_var.set_decls(decls, exclude); - solve_for_vars(); - projector p(*this); - m_is_var.reset(); - expr_ref_vector v = p.project(); - return v; } - expr_ref_vector term_graph::solve(func_decl_ref_vector const &decls, bool exclude) { - m_is_var.set_decls(decls, exclude); - solve_for_vars(); + expr_ref_vector term_graph::project() { + m_is_var.reset_solved(); projector p(*this); - expr_ref_vector v = p.solve(); m_is_var.reset(); - return v; + return p.project(); + } + + expr_ref_vector term_graph::solve() { + m_is_var.reset_solved(); + projector p(*this); + return p.solve(); } } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index e60b535c0..ac13e1e44 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -34,8 +34,7 @@ namespace qe { class is_variable_proc : public ::is_variable_proc { bool m_exclude; - u_map m_decls; - u_map m_solved; + obj_hashtable m_decls, m_solved; public: bool operator()(const expr *e) const override; bool operator()(const term &t) const; @@ -85,7 +84,7 @@ namespace qe { void mk_all_equalities(term const &t, expr_ref_vector &out); void display(std::ostream &out); - bool is_pure_def(expr* atom, expr *v); + bool is_pure_def(expr* atom, expr *& v); void solve_for_vars(); @@ -93,6 +92,8 @@ namespace qe { term_graph(ast_manager &m); ~term_graph(); + void set_vars(func_decl_ref_vector const& decls, bool exclude); + ast_manager& get_ast_manager() const { return m;} void add_lit(expr *lit); @@ -110,10 +111,9 @@ namespace qe { * onto the vocabulary of decls (if exclude is false) or outside the * vocabulary of decls (if exclude is true). */ - expr_ref_vector project(func_decl_ref_vector const& decls, bool exclude); - expr_ref_vector solve(func_decl_ref_vector const& decls, bool exclude); - - + expr_ref_vector project(); + expr_ref_vector solve(); + }; } From 5e198f4119097b8718baae4f37ca9768ce159753 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 12 Jun 2018 13:28:49 -0700 Subject: [PATCH 299/364] Fix clang compilation issues --- src/qe/qe_mbp.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index c35351fb3..0bb8ba00f 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -33,7 +33,7 @@ namespace qe { struct def { expr_ref var, term; - def(expr_ref& v, expr_ref& t): var(v), term(t) {} + def(const expr_ref& v, expr_ref& t): var(v), term(t) {} }; class project_plugin { @@ -69,17 +69,17 @@ namespace qe { impl * m_impl; public: mbp(ast_manager& m, params_ref const& p = params_ref()); - + ~mbp(); void updt_params(params_ref const& p); - + static void get_param_descrs(param_descrs & r); - + /** \brief - Apply model-based qe on constants provided as vector of variables. - Return the updated formula and updated set of variables that were not eliminated. + Apply model-based qe on constants provided as vector of variables. + Return the updated formula and updated set of variables that were not eliminated. */ void operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls); @@ -90,13 +90,13 @@ namespace qe { void solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); /** - \brief + \brief Extract literals from formulas based on model. */ void extract_literals(model& model, expr_ref_vector& lits); /** - \brief + \brief Maximize objective t under current model for constraints in fmls. */ opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); @@ -105,11 +105,11 @@ namespace qe { \brief Apply spacer friendly MBP. Use parameters to control behavior. - - reduce_all_selects (false) - - dont_sub (false) + - reduce_all_selects (false) + - dont_sub (false) */ void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml); }; } -#endif +#endif From b246389267eba16a14415650f41c91b0eddef301 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 12 Jun 2018 13:41:45 -0700 Subject: [PATCH 300/364] Don't reset m_is_var in project --- src/qe/qe_term_graph.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index d4cde6b4f..82b6e9d2e 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -185,9 +185,9 @@ namespace qe { bool term_graph::is_variable_proc::operator()(const expr * e) const { if (!is_app(e)) return false; const app *a = ::to_app(e); - return + return a->get_family_id() == null_family_id && - !m_solved.contains(a->get_decl()) && + !m_solved.contains(a->get_decl()) && m_exclude == m_decls.contains(a->get_decl()); } @@ -774,13 +774,14 @@ namespace qe { } expr_ref_vector term_graph::project() { + // reset solved vars so that they are not considered pure by projector m_is_var.reset_solved(); projector p(*this); - m_is_var.reset(); return p.project(); } expr_ref_vector term_graph::solve() { + // reset solved vars so that they are not considered pure by projector m_is_var.reset_solved(); projector p(*this); return p.solve(); From 535b8893aea4d13669944e527041f2bd2d910779 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 12 Jun 2018 17:23:46 -0700 Subject: [PATCH 301/364] Complete euf project with eq and diseq on pure representatives --- src/qe/qe_mbi.cpp | 43 ++++++++++++++++++++-------------------- src/qe/qe_term_graph.cpp | 39 ++++++++++++++++++++++++++++++++++++ src/qe/qe_term_graph.h | 10 +++++----- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 09ab76559..ae989ba02 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -33,18 +33,18 @@ Notes: Sketch of approach by example: A: s <= 2a <= t & f(a) = q - + B: t <= 2b <= s + 1 & f(b) != q 1. Extract arithmetic consequences of A over shared vocabulary. - A -> s <= t & (even(t) | s < t) - + A -> s <= t & (even(t) | s < t) + 2a. Send to B, have B solve shared variables with EUF_B. epsilon b . B & A_pure - epsilon b . t <= 2b <= s + 1 & s <= t & (even(t) | s < t) - = t <= s + 1 & (even(t) | t <= s) & s <= t & (even(t) | s < t) - = even(t) & t = s + epsilon b . t <= 2b <= s + 1 & s <= t & (even(t) | s < t) + = t <= s + 1 & (even(t) | t <= s) & s <= t & (even(t) | s < t) + = even(t) & t = s b := t div 2 B & A_pure -> B[b/t div 2] = f(t div 2) != q & t <= s + 1 @@ -53,13 +53,13 @@ Notes: A & B_pure -> false Invoke the ping-pong principle to extract interpolant. - + 2b. Solve for shared variables with EUF. - epsilon a . A + epsilon a . A = a := (s + 1) div 2 & s < t & f((s + 1) div 2) = q - 3b. Send to B. Produces core + 3b. Send to B. Produces core s < t & f((s + 1) div 2) = q 4b Solve again in arithmetic for shared variables with EUF. @@ -71,7 +71,7 @@ Notes: Send to B, produces core (s != t | f(t div 2) != q) 5b. There is no longer a solution for A. A is unsat. - + --*/ #include "ast/ast_util.h" @@ -138,7 +138,7 @@ namespace qe { void prop_mbi_plugin::block(expr_ref_vector const& lits) { m_solver->assert_expr(mk_not(mk_and(lits))); - } + } // ------------------------------- // euf_mbi, TBD @@ -158,8 +158,8 @@ namespace qe { void operator()(expr*) {} }; - euf_mbi_plugin::euf_mbi_plugin(solver* s, solver* sNot): - m(s->get_manager()), + euf_mbi_plugin::euf_mbi_plugin(solver* s, solver* sNot): + m(s->get_manager()), m_atoms(m), m_solver(s), m_dual_solver(sNot) { @@ -205,11 +205,11 @@ namespace qe { // use the dual solver to find a 'small' implicant m_dual_solver->get_unsat_core(core); TRACE("qe", tout << "core: " << core << "\n";); - // project the implicant onto vars + // project the implicant onto vars tg.set_vars(vars, false); tg.add_lits(core); - lits.reset(); - lits.append(tg.project()); + lits.reset(); + lits.append(tg.project(*mdl)); TRACE("qe", tout << "project: " << lits << "\n";); return mbi_sat; case l_undef: @@ -232,8 +232,8 @@ namespace qe { void euf_mbi_plugin::block(expr_ref_vector const& lits) { m_solver->assert_expr(mk_not(mk_and(lits))); - } - + } + /** -------------------------------------------------------------- * ping-pong interpolation of Gurfinkel & Vizel @@ -255,7 +255,7 @@ namespace qe { auto* t2 = turn ? &b : &a; mbi_result next_res = (*t1)(vars, lits, mdl); switch (next_res) { - case mbi_sat: + case mbi_sat: if (last_res == mbi_sat) { itp = nullptr; return l_true; @@ -264,7 +264,7 @@ namespace qe { break; // continue case mbi_unsat: { if (lits.empty()) { - // TBD, either a => itp and itp => !b + // TBD, either a => itp and itp => !b // or b => itp and itp => !a itp = mk_and(itps[!turn]); return l_false; @@ -282,7 +282,7 @@ namespace qe { case mbi_augment: break; case mbi_undef: - return l_undef; + return l_undef; } turn = !turn; last_res = next_res; @@ -319,4 +319,3 @@ namespace qe { } } }; - diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 82b6e9d2e..467a0cc48 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -24,6 +24,7 @@ Notes: #include "ast/for_each_expr.h" #include "ast/occurs.h" #include "qe/qe_term_graph.h" +#include "model/model_evaluator.h" namespace qe { @@ -546,6 +547,7 @@ namespace qe { u_map m_term2app; u_map m_root2rep; + model_ref m_model; expr_ref_vector m_pinned; // tracks expr in the maps expr* mk_pure(term const& t) { @@ -565,6 +567,7 @@ namespace qe { return pure; } + bool is_better_rep(expr *t1, expr *t2) { if (!t2) return t1 != nullptr; return m.is_unique_value(t1) && !m.is_unique_value(t2); @@ -740,20 +743,49 @@ namespace qe { return is_uninterp_const(rhs) && !occurs(rhs, lhs); } + /// Add equalities and disequalities for all pure representatives + /// based on their equivalence in the model + void model_complete(expr_ref_vector &res) { + if (!m_model) return; + obj_map val2rep; + model_evaluator mev(*m_model); + for (auto &kv : m_root2rep) { + expr *rep = kv.m_value; + expr_ref val(m); + expr *u = nullptr; + if (!mev.eval(rep, val)) continue; + if (val2rep.find(val, u)) { + res.push_back(m.mk_eq(u, rep)); + } + else { + val2rep.insert(val, rep); + } + } + ptr_buffer reps; + for (auto &kv : val2rep) { + reps.push_back(kv.m_value); + } + res.push_back(m.mk_distinct(reps.size(), reps.c_ptr())); + } + public: projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} + void set_model(model &mdl) { m_model = &mdl; } + void reset() { m_tg.reset_marks(); m_term2app.reset(); m_root2rep.reset(); m_pinned.reset(); + m_model.reset(); } expr_ref_vector project() { expr_ref_vector res(m); purify(); mk_lits(res); mk_pure_equalities(res); + model_complete(res); reset(); return res; } @@ -780,6 +812,13 @@ namespace qe { return p.project(); } + expr_ref_vector term_graph::project(model &mdl) { + m_is_var.reset_solved(); + projector p(*this); + p.set_model(mdl); + return p.project(); + } + expr_ref_vector term_graph::solve() { // reset solved vars so that they are not considered pure by projector m_is_var.reset_solved(); diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index ac13e1e44..97e14dc62 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -23,6 +23,7 @@ Notes: #include "util/plugin_manager.h" #include "qe/qe_solve_plugin.h" #include "qe/qe_vartest.h" +#include "model/model.h" namespace qe { @@ -85,8 +86,6 @@ namespace qe { void display(std::ostream &out); bool is_pure_def(expr* atom, expr *& v); - void solve_for_vars(); - public: term_graph(ast_manager &m); @@ -111,9 +110,10 @@ namespace qe { * onto the vocabulary of decls (if exclude is false) or outside the * vocabulary of decls (if exclude is true). */ - expr_ref_vector project(); - expr_ref_vector solve(); - + expr_ref_vector project(); + expr_ref_vector solve(); + expr_ref_vector project(model &mdl); + }; } From 81575fae7c3b35a0c4cdf59c9d450a8ff445c078 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Jun 2018 13:34:31 -0700 Subject: [PATCH 302/364] Remove unused function --- src/qe/qe_arrays.cpp | 108 ++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 57 deletions(-) diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 138aed1df..a4da829a0 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -54,10 +54,10 @@ namespace { app_ref m_peq; // partial equality application app_ref m_eq; // equivalent std equality using def. of partial eq array_util m_arr_u; - + public: static const char* PARTIAL_EQ; - + peq (app* p, ast_manager& m): m (m), m_lhs (p->get_arg (0), m), @@ -79,7 +79,7 @@ namespace { m_diff_indices.push_back (vec); } } - + peq (expr* lhs, expr* rhs, vector const& diff_indices, ast_manager& m): m (m), m_lhs (lhs, m), @@ -102,13 +102,13 @@ namespace { } m_decl = m.mk_func_decl (symbol (PARTIAL_EQ), sorts.size (), sorts.c_ptr (), m.mk_bool_sort ()); } - + expr_ref lhs () { return m_lhs; } - + expr_ref rhs () { return m_rhs; } - + void get_diff_indices (vector& result) { result.append(m_diff_indices); } - + app_ref mk_peq () { if (!m_peq) { ptr_vector args; @@ -121,18 +121,18 @@ namespace { } return m_peq; } - + app_ref mk_eq (app_ref_vector& aux_consts, bool stores_on_rhs = true) { if (!m_eq) { expr_ref lhs (m_lhs, m), rhs (m_rhs, m); if (!stores_on_rhs) { std::swap (lhs, rhs); - } + } // lhs = (...(store (store rhs i0 v0) i1 v1)...) sort* val_sort = get_array_range (m.get_sort (lhs)); for (expr_ref_vector const& diff : m_diff_indices) { ptr_vector store_args; - store_args.push_back (rhs); + store_args.push_back (rhs); store_args.append (diff.size(), diff.c_ptr()); app_ref val(m.mk_fresh_const ("diff", val_sort), m); store_args.push_back (val); @@ -145,8 +145,8 @@ namespace { } }; - const char* peq::PARTIAL_EQ = "!partial_eq"; - + const char* peq::PARTIAL_EQ = "!partial_eq"; + bool is_partial_eq (app* a) { return a->get_decl ()->get_name () == peq::PARTIAL_EQ; } @@ -160,11 +160,6 @@ namespace qe { return true; } - static bool is_eq(vector const& xs, vector const& ys) { - for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; - return true; - } - static expr_ref mk_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { ast_manager& m = xs.get_manager(); expr_ref_vector eqs(m); @@ -177,9 +172,9 @@ namespace qe { * where x appears and the equalities do not evaluate to false in the current model. * 2. Track them as partial equivalence relations. * 3. Sort them according to nesting depth. - * 4. Solve for x by potentially introducing fresh variables. - * Thus, (store x i v) = y, then - * x = (store y i w), (select y i) = v, where w is fresh and evaluates to (select x i). + * 4. Solve for x by potentially introducing fresh variables. + * Thus, (store x i v) = y, then + * x = (store y i w), (select y i) = v, where w is fresh and evaluates to (select x i). * Generally, equalities are tracked as x =_idxs y, where x, y are equal up to idxs. */ @@ -524,9 +519,9 @@ namespace qe { bool lhs_has_v = (lhs == m_v || m_has_stores_v.is_marked (lhs)); bool rhs_has_v = (rhs == m_v || m_has_stores_v.is_marked (rhs)); app* store = nullptr; - + SASSERT (lhs_has_v || rhs_has_v); - + if (!lhs_has_v && is_app(rhs)) { store = to_app (rhs); } @@ -538,7 +533,7 @@ namespace qe { // put it in the beginning to simplify it away return 0; } - + unsigned nd = 0; // nesting depth for (nd = 1; m_arr_u.is_store (store); nd++, store = to_app (store->get_arg (0))) /* empty */ ; @@ -635,7 +630,7 @@ namespace qe { M = &mdl; m_mev = &mev; - unsigned j = 0; + unsigned j = 0; for (unsigned i = 0; i < arr_vars.size (); i++) { reset_v (); m_v = arr_vars.get (i); @@ -798,14 +793,14 @@ namespace qe { /** * \brief reduce (select (store (store x i1 v1) i2 v2) idx) under model M - * such that the result is v2 if idx = i2 under M, it is v1 if idx = i1, idx != i2 under M, + * such that the result is v2 if idx = i2 under M, it is v1 if idx = i1, idx != i2 under M, * and it is (select x idx) if idx != i1, idx !+ i2 under M. */ expr* reduce_core (app *a) { if (!m_arr_u.is_store (a->get_arg (0))) return a; - expr* array = a->get_arg(0); + expr* array = a->get_arg(0); unsigned arity = get_array_arity(m.get_sort(array)); - + expr* const* js = a->get_args() + 1; while (m_arr_u.is_store (array)) { @@ -888,7 +883,7 @@ namespace qe { * and generally distinct(i1, i2, i3) for arbitrary index sorts. * - for equal indices accumulate constraint i1 = i2, i3 = i5, .. * - introduce variables v1, v2, .., for each equivalence class. - * - replace occurrences of select by v1, v2, ... + * - replace occurrences of select by v1, v2, ... * - update M to evaluate v1, v2, the same as (select a i1) (select a i2) */ @@ -900,10 +895,10 @@ namespace qe { expr_ref_vector val; vector rval; idx_val(expr_ref_vector & idx, expr_ref_vector & val, vector const& rval): idx(idx), val(val), rval(rval) {} - idx_val& operator=(idx_val const& o) { + idx_val& operator=(idx_val const& o) { idx.reset(); val.reset(); rval.reset(); idx.append(o.idx); val.append(o.val); rval.append(o.rval); - return *this; + return *this; } }; ast_manager& m; @@ -981,7 +976,7 @@ namespace qe { compare_idx(array_project_selects_util& u):u(u) {} bool operator()(idx_val const& x, idx_val const& y) { SASSERT(x.rval.size() == y.rval.size()); - for (unsigned j = 0; j < x.rval.size(); ++j) { + for (unsigned j = 0; j < x.rval.size(); ++j) { rational const& xv = x.rval[j]; rational const& yv = y.rval[j]; if (xv < yv) return true; @@ -1004,7 +999,7 @@ namespace qe { SASSERT(xs.size() == ys.size() && !xs.empty()); expr_ref result(mk_lt(xs.back(), ys.back()), m); for (unsigned i = xs.size()-1; i-- > 0; ) { - result = m.mk_or(mk_lt(xs[i], ys[i]), + result = m.mk_or(mk_lt(xs[i], ys[i]), m.mk_and(m.mk_eq(xs[i], ys[i]), result)); } return result; @@ -1028,9 +1023,9 @@ namespace qe { if (!m_ari_u.is_real(srt) && !m_ari_u.is_int(srt) && !m_bv_u.is_bv_sort(srt)) { TRACE("qe", tout << "non-numeric index sort for Ackerman" << mk_pp(srt, m) << "\n";); is_numeric = false; - } + } } - + unsigned start = m_idxs.size (); // append at the end for (app * a : sel_terms) { expr_ref_vector idxs(m, arity, a->get_args() + 1); @@ -1045,7 +1040,7 @@ namespace qe { // add equality (idx == repr) m_idx_lits.push_back (mk_eq (idxs, m_idxs[j].idx)); is_new = false; - break; + break; } if (is_new) { // new repr, val, and sel const @@ -1067,7 +1062,7 @@ namespace qe { else if (is_numeric) { // sort reprs by their value and add a chain of strict inequalities compare_idx cmp(*this); - std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); + std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); for (unsigned i = start; i + 1 < m_idxs.size(); ++i) { m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); } @@ -1183,7 +1178,7 @@ namespace qe { expr_ref_vector m_values; expr* const* m_vars; - indices(ast_manager& m, model& model, unsigned n, expr* const* vars): + indices(ast_manager& m, model& model, unsigned n, expr* const* vars): m_values(m), m_vars(vars) { expr_ref val(m); for (unsigned i = 0; i < n; ++i) { @@ -1192,11 +1187,11 @@ namespace qe { } } }; - + ast_manager& m; array_util a; scoped_ptr m_var; - + imp(ast_manager& m): m(m), a(m) {} ~imp() {} @@ -1209,7 +1204,7 @@ namespace qe { if (m.is_true(lits[i].get())) { project_plugin::erase(lits, i); } - } + } } bool contains_x(expr* e) { @@ -1223,7 +1218,7 @@ namespace qe { lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); } } - + bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { // find an equality to solve for. expr* s, *t; @@ -1270,13 +1265,13 @@ namespace qe { var = m.mk_fresh_const("value", range); vars.push_back(var); args.reset(); - + args.push_back (s); args.append(idxs[i].m_values.size(), idxs[i].m_vars); sel = a.mk_select (args.size (), args.c_ptr ()); VERIFY (model.eval (sel, val)); model.register_decl (var->get_decl (), val); - + args[0] = result; args.push_back(var); result = a.mk_store(args.size(), args.c_ptr()); @@ -1297,7 +1292,7 @@ namespace qe { if (contains_x(s->get_arg(i))) { return false; } - } + } unsigned i; expr_ref_vector args(m); switch (contains(idx, idxs, i)) { @@ -1317,7 +1312,7 @@ namespace qe { return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); case l_undef: return false; - } + } } return false; } @@ -1325,13 +1320,13 @@ namespace qe { lbool contains(indices const& idx, vector const& idxs, unsigned& j) { for (unsigned i = 0; i < idxs.size(); ++i) { switch (compare(idx, idxs[i])) { - case l_true: + case l_true: j = i; return l_true; case l_false: break; case l_undef: - return l_undef; + return l_undef; } } return l_false; @@ -1355,25 +1350,25 @@ namespace qe { lbool compare(expr* val1, expr* val2) { if (m.are_equal (val1, val2)) return l_true; if (m.are_distinct (val1, val2)) return l_false; - + if (is_uninterp(val1) || is_uninterp(val2)) { // TBD chase definition of nested array. return l_undef; } return l_undef; - } + } }; - - + + array_project_plugin::array_project_plugin(ast_manager& m) { m_imp = alloc(imp, m); } - + array_project_plugin::~array_project_plugin() { dealloc(m_imp); } - + bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { ast_manager& m = vars.get_manager(); app_ref_vector vvars(m, 1, &var); @@ -1387,7 +1382,7 @@ namespace qe { bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return m_imp->solve(model, vars, lits); } - + family_id array_project_plugin::get_family_id() { return m_imp->a.get_family_id(); } @@ -1406,9 +1401,9 @@ namespace qe { // 2. reduce selects array_select_reducer rs (m); rs (mdl, arr_vars, fml, reduce_all_selects); - + TRACE ("qe", tout << "Reduced selects:\n" << fml << "\n"; ); - + // 3. project selects using model based ackermannization array_project_selects_util ps (m); ps (mdl, arr_vars, fml, aux_vars); @@ -1424,4 +1419,3 @@ namespace qe { } }; - From d38879e478546151ee7cb46cd7a0583e678a043c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Jun 2018 13:35:27 -0700 Subject: [PATCH 303/364] Renamed spacer options --- src/muz/base/fixedpoint_params.pyg | 92 +++++++++----------------- src/muz/spacer/spacer_context.cpp | 51 +++++++------- src/muz/spacer/spacer_context.h | 14 ++-- src/muz/spacer/spacer_dl_interface.cpp | 2 +- src/muz/spacer/spacer_pdr.cpp | 2 +- 5 files changed, 67 insertions(+), 94 deletions(-) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index f11e2faf9..7e92b0d2c 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -56,43 +56,19 @@ def_module_params('fixedpoint', "table columns, if it would have been empty otherwise"), ('datalog.subsumption', BOOL, True, "if true, removes/filters predicates with total transitions"), - ('pdr.bfs_model_search', BOOL, True, - "use BFS strategy for expanding model search"), - ('pdr.farkas', BOOL, True, - "use lemma generator based on Farkas (for linear real arithmetic)"), ('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"), - ('pdr.flexible_trace', BOOL, False, - "allow PDR generate long counter-examples " + - "by extending candidate trace within search area"), - ('pdr.flexible_trace_depth', UINT, UINT_MAX, - 'Controls the depth (below the current level) at which flexible trace can be applied'), - ('pdr.use_model_generalizer', BOOL, False, - "use model for backwards propagation (instead of symbolic simulation)"), - ('pdr.validate_result', BOOL, False, + ('spacer.push_pob', BOOL, False, "push blocked pobs to higher level"), + ('spacer.push_pob_max_depth', UINT, UINT_MAX, + 'Maximum depth at which push_pob is enabled'), + ('validate', BOOL, False, "validate result (by proof checking or model checking)"), - ('pdr.simplify_formulas_pre', BOOL, False, - "simplify derived formulas before inductive propagation"), - ('pdr.simplify_formulas_post', BOOL, False, - "simplify derived formulas after inductive propagation"), - ('pdr.use_multicore_generalizer', BOOL, False, - "extract multiple cores for blocking states"), - ('pdr.use_inductive_generalizer', BOOL, True, + ('spacer.simplify_lemmas_pre', BOOL, False, + "simplify derived lemmas before inductive propagation"), + ('spacer.simplify_lemmas_post', BOOL, False, + "simplify derived lemmas after inductive propagation"), + ('spacer.use_inductive_generalizer', BOOL, True, "generalize lemmas using induction strengthening"), - ('pdr.use_arith_inductive_generalizer', BOOL, False, - "generalize lemmas using arithmetic heuristics for induction strengthening"), - ('pdr.use_convex_closure_generalizer', BOOL, False, - "generalize using convex closures of lemmas"), - ('pdr.use_convex_interior_generalizer', BOOL, False, - "generalize using convex interiors of lemmas"), - ('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " + - "cache (2) for model search"), - ('pdr.inductive_reachability_check', BOOL, False, - "assume negation of the cube on the previous level when " + - "checking for reachability (not only during cube weakening)"), - ('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"), - ('pdr.try_minimize_core', BOOL, False, - "try to reduce core size (before inductive minimization)"), - ('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'), + ('spacer.max_num_contexts', UINT, 500, "maximal number of contexts to create"), ('print_fixedpoint_extensions', BOOL, True, "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + "when printing rules"), @@ -126,11 +102,11 @@ def_module_params('fixedpoint', ('xform.compress_unbound', BOOL, True, "compress tails with unbound variables"), ('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"), ('xform.unfold_rules', UINT, 0, - "unfold rules statically using iterative squarring"), + "unfold rules statically using iterative squaring"), ('xform.slice', BOOL, True, "simplify clause set using slicing"), ('xform.karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), - ('spacer.use_eqclass', BOOL, False, "Generalizes equalities to equivalence classes"), + ('spacer.use_euf_gen', BOOL, False, 'Generalize lemmas and pobs using implied equalities'), ('xform.transform_arrays', BOOL, False, "Rewrites arrays equalities and applies select over store"), ('xform.instantiate_arrays', BOOL, False, @@ -149,36 +125,31 @@ def_module_params('fixedpoint', ('xform.tail_simplifier_pve', BOOL, True, "propagate_variable_equivalences"), ('xform.subsumption_checker', BOOL, True, "Enable subsumption checker (no support for model conversion)"), ('xform.coi', BOOL, True, "use cone of influence simplification"), - ('spacer.order_children', UINT, 0, 'SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse)'), - ('spacer.eager_reach_check', BOOL, True, 'SPACER: eagerly check if a query is reachable using reachability facts of predecessors'), + ('spacer.order_children', UINT, 0, 'SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse), 2 (random)'), ('spacer.use_lemma_as_cti', BOOL, False, 'SPACER: use a lemma instead of a CTI in flexible_trace'), - ('spacer.reset_obligation_queue', BOOL, True, 'SPACER: reset obligation queue when entering a new level'), + ('spacer.reset_pob_queue', BOOL, True, 'SPACER: reset pob obligation queue when entering a new level'), ('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'), ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), - ('spacer.skip_propagate', BOOL, False, "Skip propagate/pushing phase. Turns PDR into a BMC that returns either reachable or unknown"), + ('spacer.propagate', BOOL, True, 'Enable propagate/pushing phase'), ('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"), ('spacer.elim_aux', BOOL, True, "Eliminate auxiliary variables in reachability facts"), ('spacer.blast_term_ite', BOOL, True, "Expand non-Boolean ite-terms"), - ('spacer.nondet_tie_break', BOOL, False, "Break ties in obligation queue non-deterministically"), ('spacer.reach_dnf', BOOL, True, "Restrict reachability facts to DNF"), ('bmc.linear_unrolling_depth', UINT, UINT_MAX, "Maximal level to explore"), ('spacer.iuc.split_farkas_literals', BOOL, False, "Split Farkas literals"), - ('spacer.native_mbp', BOOL, False, "Use native mbp of Z3"), + ('spacer.native_mbp', BOOL, True, "Use native mbp of Z3"), ('spacer.eq_prop', BOOL, True, "Enable equality and bound propagation in arithmetic"), ('spacer.weak_abs', BOOL, True, "Weak abstraction"), ('spacer.restarts', BOOL, False, "Enable reseting obligation queue"), ('spacer.restart_initial_threshold', UINT, 10, "Intial threshold for restarts"), ('spacer.random_seed', UINT, 0, "Random seed to be used by SMT solver"), - ('spacer.ground_cti', BOOL, True, "Require CTI to be ground"), - ('spacer.vs.dump_benchmarks', BOOL, False, 'dump benchmarks in virtual solver'), - ('spacer.vs.dump_min_time', DOUBLE, 5.0, 'min time to dump benchmark'), - ('spacer.vs.recheck', BOOL, False, 're-check locally during benchmark dumping'), - ('spacer.mbqi', BOOL, True, 'use model-based quantifier instantiation'), + + ('spacer.mbqi', BOOL, True, 'Enable mbqi'), ('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'), - ('spacer.q3.instantiate', BOOL, True, 'instantiate quantified lemmas'), - ('spacer.q3', BOOL, True, 'allow quantified lemmas in frames'), + ('spacer.q3', BOOL, True, 'Allow quantified lemmas in frames'), + ('spacer.q3.instantiate', BOOL, True, 'Instantiate quantified lemmas'), ('spacer.iuc', UINT, 1, '0 = use old implementation of unsat-core-generation, ' + '1 = use new implementation of IUC generation, ' + @@ -187,21 +158,24 @@ def_module_params('fixedpoint', '0 = use simple Farkas plugin, ' + '1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation),' + '2 = use Gaussian elimination optimization (broken), 3 = use additive IUC plugin'), - ('spacer.iuc.old_hyp_reducer', BOOL, True, 'use old hyp reducer instead of new implementation, for debugging only'), - ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), - ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), - ('spacer.iuc.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut'), - ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes'), - ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints'), + ('spacer.iuc.old_hyp_reducer', BOOL, False, 'use old hyp reducer instead of new implementation, for debugging only'), + ('spacer.validate_lemmas', BOOL, False, 'Validate each lemma after generalization'), + ('spacer.reuse_pobs', BOOL, True, 'Reuse pobs'), + ('spacer.ground_pobs', BOOL, True, 'Ground pobs by using values from a model'), + ('spacer.iuc.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut (for debugging)'), + ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes (debugging)'), + ('spacer.simplify_pob', BOOL, False, 'simplify pobs by removing redundant constraints'), ('spacer.q3.use_qgen', BOOL, False, 'use quantified lemma generalizer'), ('spacer.q3.qgen.normalize', BOOL, True, 'normalize cube before quantified generalization'), ('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'), ('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"), - ('spacer.from_level', UINT, 0, 'starting level to explore'), - ('spacer.print_json', SYMBOL, '', 'print pobs tree in JSON format to a given file'), - ('spacer.ctp', BOOL, False, 'enable counterexample-to-pushing technique'), - ('spacer.use_inc_clause', BOOL, False, 'Use incremental clause to represent trans'), + ('spacer.min_level', UINT, 0, 'Minimal level to explore'), + ('spacer.print_json', SYMBOL, '', 'Print pobs tree in JSON format to a given file'), + ('spacer.ctp', BOOL, True, 'Enable counterexample-to-pushing'), + ('spacer.use_inc_clause', BOOL, True, 'Use incremental clause to represent trans'), ('spacer.dump_benchmarks', BOOL, False, 'Dump SMT queries as benchmarks'), ('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'), ('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'), + ('spacer.gpdr.bfs', BOOL, True, 'Use BFS exploration strategy for expanding model search'), + )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 64d047d73..40c42015b 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -82,7 +82,7 @@ void pob::set_post(expr* post) { void pob::set_post(expr* post, app_ref_vector const &binding) { normalize(post, m_post, m_pt.get_context().simplify_pob(), - m_pt.get_context().use_eqclass()); + m_pt.get_context().use_euf_gen()); m_binding.reset(); if (!binding.empty()) {m_binding.append(binding);} @@ -1590,13 +1590,12 @@ void pred_transformer::init_rules(decl2rel const& pts) { if (not_inits.empty ()) {m_all_init = true;} } -static bool is_all_non_null(app_ref_vector const& v) -{ - for (unsigned i = 0; i < v.size(); ++i) { - if (!v[i]) { return false; } - } +#ifdef Z3DEBUG +static bool is_all_non_null(app_ref_vector const& apps) { + for (auto *a : apps) if (!a) return false; return true; } +#endif void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule) { scoped_watch _t_(m_initialize_watch); @@ -2196,7 +2195,7 @@ context::context(fixedpoint_params const& params, ref pool2_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); - unsigned max_num_contexts = params.pdr_max_num_contexts(); + unsigned max_num_contexts = params.spacer_max_num_contexts(); m_pool0 = alloc(solver_pool, pool0_base.get(), max_num_contexts); m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); @@ -2214,42 +2213,42 @@ void context::updt_params() { m_random.set_seed(m_params.spacer_random_seed()); m_children_order = static_cast(m_params.spacer_order_children()); m_simplify_pob = m_params.spacer_simplify_pob(); - m_use_eqclass = m_params.spacer_use_eqclass(); + m_use_euf_gen = m_params.spacer_use_euf_gen(); m_use_ctp = m_params.spacer_ctp(); m_use_inc_clause = m_params.spacer_use_inc_clause(); m_blast_term_ite = m_params.spacer_blast_term_ite(); m_reuse_pobs = m_params.spacer_reuse_pobs(); - m_use_ind_gen = m_params.pdr_use_inductive_generalizer(); + m_use_ind_gen = m_params.spacer_use_inductive_generalizer(); m_use_array_eq_gen = m_params.spacer_use_array_eq_generalizer(); - m_check_lemmas = m_params.spacer_lemma_sanity_check(); + m_validate_lemmas = m_params.spacer_validate_lemmas(); m_max_level = m_params.spacer_max_level (); - m_skip_propagate = m_params.spacer_skip_propagate (); - m_reset_obligation_queue = m_params.spacer_reset_obligation_queue(); - m_flexible_trace = m_params.pdr_flexible_trace(); - m_flexible_trace_depth = m_params.pdr_flexible_trace_depth(); + m_use_propagate = m_params.spacer_propagate (); + m_reset_obligation_queue = m_params.spacer_reset_pob_queue(); + m_push_pob = m_params.spacer_push_pob(); + m_push_pob_max_depth = m_params.spacer_push_pob_max_depth(); m_use_lemma_as_pob = m_params.spacer_use_lemma_as_cti(); m_elim_aux = m_params.spacer_elim_aux(); m_reach_dnf = m_params.spacer_reach_dnf(); m_use_derivations = m_params.spacer_use_derivations(); - m_validate_result = m_params.pdr_validate_result(); + m_validate_result = m_params.validate(); m_use_eq_prop = m_params.spacer_eq_prop(); - m_ground_pob = m_params.spacer_ground_cti(); + m_ground_pob = m_params.spacer_ground_pobs(); m_q3_qgen = m_params.spacer_q3_use_qgen(); m_use_gpdr = m_params.spacer_gpdr(); - m_simplify_formulas_pre = m_params.pdr_simplify_formulas_pre(); - m_simplify_formulas_post = m_params.pdr_simplify_formulas_post(); + m_simplify_formulas_pre = m_params.spacer_simplify_lemmas_pre(); + m_simplify_formulas_post = m_params.spacer_simplify_lemmas_post(); m_use_native_mbp = m_params.spacer_native_mbp (); m_instantiate = m_params.spacer_q3_instantiate (); m_use_qlemmas = m_params.spacer_q3(); m_weak_abs = m_params.spacer_weak_abs(); m_use_restarts = m_params.spacer_restarts(); m_restart_initial_threshold = m_params.spacer_restart_initial_threshold(); - + m_pdr_bfs = m_params.spacer_gpdr_bfs(); if (m_use_gpdr) { // set options to be compatible with GPDR m_weak_abs = false; - m_flexible_trace = false; + m_push_pob = false; m_use_qlemmas = false; m_ground_pob = true; m_reset_obligation_queue = false; @@ -2552,7 +2551,7 @@ void context::init_lemma_generalizers() m_params.spacer_q3_qgen_normalize())); } - if (use_eqclass()) { + if (m_use_euf_gen) { m_lemma_generalizers.push_back (alloc(lemma_eq_generalizer, *this)); } @@ -2567,7 +2566,7 @@ void context::init_lemma_generalizers() m_lemma_generalizers.push_back(alloc(lemma_array_eq_generalizer, *this)); } - if (m_check_lemmas) { + if (m_validate_lemmas) { m_lemma_generalizers.push_back(alloc(lemma_sanity_checker, *this)); } @@ -2995,7 +2994,7 @@ lbool context::solve_core (unsigned from_lvl) if (check_reachability()) { return l_true; } - if (lvl > 0 && !m_skip_propagate) + if (lvl > 0 && m_use_propagate) if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { dump_json(); return l_false; } dump_json(); @@ -3142,8 +3141,8 @@ bool context::check_reachability () /// returns true if the given pob can be re-scheduled bool context::is_requeue(pob &n) { - if (!m_flexible_trace) {return false;} - unsigned max_depth = m_flexible_trace_depth; + if (!m_push_pob) {return false;} + unsigned max_depth = m_push_pob_max_depth; return (n.level() >= m_pob_queue.max_level() || m_pob_queue.max_level() - n.level() <= max_depth); } @@ -3309,7 +3308,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) unsigned num_reuse_reach = 0; - if (m_flexible_trace && n.pt().is_blocked(n, uses_level)) { + if (m_push_pob && n.pt().is_blocked(n, uses_level)) { // if (!m_pob_queue.is_root (n)) n.close (); IF_VERBOSE (1, verbose_stream () << " K " << std::fixed << std::setprecision(2) diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index fcd396a62..f3d00a857 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -907,17 +907,17 @@ class context { bool m_weak_abs; bool m_use_restarts; bool m_simplify_pob; - bool m_use_eqclass; + bool m_use_euf_gen; bool m_use_ctp; bool m_use_inc_clause; bool m_blast_term_ite; bool m_reuse_pobs; bool m_use_ind_gen; bool m_use_array_eq_gen; - bool m_check_lemmas; - bool m_skip_propagate; + bool m_validate_lemmas; + bool m_use_propagate; bool m_reset_obligation_queue; - bool m_flexible_trace; + bool m_push_pob; bool m_use_lemma_as_pob; bool m_elim_aux; bool m_reach_dnf; @@ -929,8 +929,8 @@ class context { bool m_use_gpdr; bool m_simplify_formulas_pre; bool m_simplify_formulas_post; - - unsigned m_flexible_trace_depth; + bool m_pdr_bfs; + unsigned m_push_pob_max_depth; unsigned m_max_level; unsigned m_restart_initial_threshold; scoped_ptr_vector m_callbacks; @@ -1003,7 +1003,7 @@ public: bool use_instantiate () {return m_instantiate;} bool weak_abs() {return m_weak_abs;} bool use_qlemmas () {return m_use_qlemmas;} - bool use_eqclass() { return m_use_eqclass;} + bool use_euf_gen() {return m_use_euf_gen;} bool simplify_pob() {return m_simplify_pob;} bool use_ctp() {return m_use_ctp;} bool use_inc_clause() {return m_use_inc_clause;} diff --git a/src/muz/spacer/spacer_dl_interface.cpp b/src/muz/spacer/spacer_dl_interface.cpp index cadbcfb0e..807e9b751 100644 --- a/src/muz/spacer/spacer_dl_interface.cpp +++ b/src/muz/spacer/spacer_dl_interface.cpp @@ -164,7 +164,7 @@ lbool dl_interface::query(expr * query) return l_false; } - return m_context->solve(m_ctx.get_params().spacer_from_level()); + return m_context->solve(m_ctx.get_params().spacer_min_level()); } diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index 6b13e82f8..ad2b6200d 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -230,7 +230,7 @@ lbool context::gpdr_solve_core() { //if there is no query predicate, abort if (!m_rels.find(m_query_pred, m_query)) { return l_false; } - model_search ms(true); + model_search ms(m_pdr_bfs); unsigned lvl = 0; unsigned max_level = m_max_level; for (lvl = 0; lvl < max_level; ++lvl) { From 619f681d2844ca16d5353a9956f3253ec675eae2 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Jun 2018 15:11:35 -0700 Subject: [PATCH 304/364] Fix bug in iuc_solver::get_unsat_core() that prevented clean cores --- src/muz/spacer/spacer_iuc_solver.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 6d824a1f2..5a3a53f0b 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -215,32 +215,31 @@ void iuc_solver::reset_statistics () m_learn_core_sw.reset(); } -void iuc_solver::get_unsat_core (expr_ref_vector &core) -{ +void iuc_solver::get_unsat_core (expr_ref_vector &core) { m_solver.get_unsat_core (core); - ptr_vector _core(core.size(), core.c_ptr()); - undo_proxies_in_core (_core); + undo_proxies_in_core (core); } -void iuc_solver::undo_proxies_in_core (ptr_vector &r) + +void iuc_solver::undo_proxies_in_core (expr_ref_vector &r) { app_ref e(m); expr_fast_mark1 bg; - for (unsigned i = 0; i < m_first_assumption; ++i) { - bg.mark(m_assumptions.get(i)); + for (unsigned i = 0; i < m_first_assumption; ++i) { + bg.mark(m_assumptions.get(i)); } // expand proxies unsigned j = 0; for (expr* rr : r) { // skip background assumptions - if (bg.is_marked(rr)) + if (bg.is_marked(rr)) continue; // -- undo proxies, but only if they were introduced in check_sat if (m_is_proxied && is_proxy(rr, e)) { SASSERT (m.is_or (e)); r[j++] = e->get_arg (1); - } + } else { r[j++] = rr; } From 9109968e5541d58159139773ffeda20e661781a5 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Jun 2018 15:29:34 -0700 Subject: [PATCH 305/364] Cleanup fixedpoint options Replace pdr options with spacer Repace fixedpoint module with fp --- src/muz/base/CMakeLists.txt | 2 +- src/muz/base/dl_context.cpp | 103 +++++++------- src/muz/base/dl_context.h | 63 +++++---- .../{fixedpoint_params.pyg => fp_params.pyg} | 2 +- src/muz/bmc/dl_bmc_engine.cpp | 2 +- src/muz/fp/dl_cmds.cpp | 68 ++++----- src/muz/fp/horn_tactic.cpp | 59 ++++---- src/muz/spacer/spacer_context.cpp | 3 +- src/muz/spacer/spacer_context.h | 8 +- src/muz/spacer/spacer_iuc_solver.cpp | 40 +++--- src/muz/spacer/spacer_iuc_solver.h | 20 +-- src/muz/spacer/spacer_prop_solver.cpp | 7 +- src/muz/spacer/spacer_prop_solver.h | 4 +- src/muz/tab/tab_context.cpp | 2 +- src/muz/transforms/dl_mk_array_eq_rewrite.cpp | 2 +- .../transforms/dl_mk_array_instantiation.cpp | 2 +- src/muz/transforms/dl_mk_bit_blast.cpp | 52 +++---- .../dl_mk_interp_tail_simplifier.cpp | 31 ++--- .../dl_mk_quantifier_abstraction.cpp | 49 ++++--- src/muz/transforms/dl_mk_rule_inliner.cpp | 129 +++++++++--------- src/muz/transforms/dl_mk_scale.cpp | 20 +-- .../transforms/dl_mk_subsumption_checker.cpp | 25 ++-- src/muz/transforms/dl_transforms.cpp | 4 +- 23 files changed, 344 insertions(+), 353 deletions(-) rename src/muz/base/{fixedpoint_params.pyg => fp_params.pyg} (99%) diff --git a/src/muz/base/CMakeLists.txt b/src/muz/base/CMakeLists.txt index 6b0334664..8c21e1557 100644 --- a/src/muz/base/CMakeLists.txt +++ b/src/muz/base/CMakeLists.txt @@ -18,5 +18,5 @@ z3_add_component(muz smt smt2parser PYG_FILES - fixedpoint_params.pyg + fp_params.pyg ) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 2b0987be7..220b2516d 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -27,7 +27,7 @@ Revision History: #include "ast/ast_smt2_pp.h" #include "ast/datatype_decl_plugin.h" #include "ast/scoped_proof.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "ast/ast_pp_util.h" @@ -152,15 +152,15 @@ namespace datalog { class context::restore_rules : public trail { rule_set* m_old_rules; - void reset() { - dealloc(m_old_rules); + void reset() { + dealloc(m_old_rules); m_old_rules = nullptr; } public: restore_rules(rule_set& r): m_old_rules(alloc(rule_set, r)) {} ~restore_rules() override {} - + void undo(context& ctx) override { ctx.replace_rules(*m_old_rules); reset(); @@ -189,7 +189,7 @@ namespace datalog { throw default_exception("there are no backtracking points to pop to"); } throw default_exception("pop operation is not supported"); - m_trail.pop_scope(1); + m_trail.pop_scope(1); } // ----------------------------------- @@ -203,7 +203,7 @@ namespace datalog { m_register_engine(re), m_fparams(fp), m_params_ref(pa), - m_params(alloc(fixedpoint_params, m_params_ref)), + m_params(alloc(fp_params, m_params_ref)), m_decl_util(m), m_rewriter(m), m_var_subst(m), @@ -235,7 +235,7 @@ namespace datalog { context::~context() { reset(); - dealloc(m_params); + dealloc(m_params); } void context::reset() { @@ -291,14 +291,14 @@ namespace datalog { bool context::similarity_compressor() const { return m_params->datalog_similarity_compressor(); } unsigned context::similarity_compressor_threshold() const { return m_params->datalog_similarity_compressor_threshold(); } unsigned context::soft_timeout() const { return m_fparams.m_timeout; } - unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); } + unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); } bool context::generate_explanations() const { return m_params->datalog_generate_explanations(); } bool context::explanations_on_relation_level() const { return m_params->datalog_explanations_on_relation_level(); } bool context::magic_sets_for_queries() const { return m_params->datalog_magic_sets_for_queries(); } symbol context::tab_selection() const { return m_params->tab_selection(); } bool context::xform_coi() const { return m_params->xform_coi(); } bool context::xform_slice() const { return m_params->xform_slice(); } - bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); } + bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); } bool context::karr() const { return m_params->xform_karr(); } bool context::scale() const { return m_params->xform_scale(); } bool context::magic() const { return m_params->xform_magic(); } @@ -428,7 +428,7 @@ namespace datalog { } - void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { if (relation_name_cnt > 0) { ensure_engine(); @@ -438,9 +438,9 @@ namespace datalog { } } - func_decl * context::mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, + func_decl * context::mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, func_decl* orig_pred) { - func_decl* new_pred = + func_decl* new_pred = m.mk_fresh_func_decl(prefix, suffix, arity, domain, m.mk_bool_sort()); register_predicate(new_pred, true); @@ -473,7 +473,7 @@ namespace datalog { // // Update a rule with a new. // It requires basic subsumption. - // + // void context::update_rule(expr* rl, symbol const& name) { datalog::rule_manager& rm = get_rule_manager(); proof* p = nullptr; @@ -494,13 +494,13 @@ namespace datalog { rule* old_rule = nullptr; for (unsigned i = 0; i < size_before; ++i) { if (rls[i]->name() == name) { - if (old_rule) { + if (old_rule) { std::stringstream strm; strm << "Rule " << name << " occurs twice. It cannot be modified"; m_rule_set.del_rule(r); throw default_exception(strm.str()); } - old_rule = rls[i]; + old_rule = rls[i]; } } if (old_rule) { @@ -556,7 +556,7 @@ namespace datalog { ensure_engine(); m_engine->add_cover(level, pred, property); } - + void context::add_invariant(func_decl* pred, expr *property) { ensure_engine(); @@ -566,11 +566,11 @@ namespace datalog { void context::check_rules(rule_set& r) { m_rule_properties.set_generate_proof(generate_proof_trace()); switch(get_engine()) { - case DATALOG_ENGINE: + case DATALOG_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_quantifier_free(); m_rule_properties.check_uninterpreted_free(); - m_rule_properties.check_nested_free(); + m_rule_properties.check_nested_free(); m_rule_properties.check_infinite_sorts(); break; case SPACER_ENGINE: @@ -582,12 +582,12 @@ namespace datalog { case BMC_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_for_negated_predicates(); - break; + break; case QBMC_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); - break; + break; case TAB_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); @@ -650,7 +650,7 @@ namespace datalog { add_fact(pred, rfact); } } - + void context::add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]) { if (pred->get_arity() != num_args) { std::ostringstream out; @@ -682,7 +682,7 @@ namespace datalog { reopen(); } } - + void context::reopen() { SASSERT(m_closed); m_rule_set.reopen(); @@ -695,7 +695,7 @@ namespace datalog { transformer.register_plugin(plugin); transform_rules(transformer); } - + void context::transform_rules(rule_transformer& transf) { SASSERT(m_closed); //we must finish adding rules before we start transforming them TRACE("dl", display_rules(tout);); @@ -724,7 +724,7 @@ namespace datalog { } void context::collect_params(param_descrs& p) { - fixedpoint_params::collect_param_descrs(p); + fp_params::collect_param_descrs(p); insert_timeout(p); } @@ -732,8 +732,8 @@ namespace datalog { m_params_ref.copy(p); if (m_engine.get()) m_engine->updt_params(); m_generate_proof_trace = m_params->generate_proof_trace(); - m_unbound_compressor = m_params->datalog_unbound_compressor(); - m_default_relation = m_params->datalog_default_relation(); + m_unbound_compressor = m_params->datalog_unbound_compressor(); + m_default_relation = m_params->datalog_default_relation(); } expr_ref context::get_background_assertion() { @@ -748,7 +748,7 @@ namespace datalog { void context::assert_expr(expr* e) { TRACE("dl", tout << mk_ismt2_pp(e, m) << "\n";); - m_background.push_back(e); + m_background.push_back(e); } void context::cleanup() { @@ -785,7 +785,7 @@ namespace datalog { return; } symbol e = m_params->engine(); - + if (e == symbol("datalog")) { m_engine_type = DATALOG_ENGINE; } @@ -811,7 +811,7 @@ namespace datalog { if (m_engine_type == LAST_ENGINE) { expr_fast_mark1 mark; engine_type_proc proc(m); - m_engine_type = DATALOG_ENGINE; + m_engine_type = DATALOG_ENGINE; for (unsigned i = 0; m_engine_type == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { rule * r = m_rule_set.get_rule(i); quick_for_each_expr(proc, mark, r->get_head()); @@ -893,15 +893,15 @@ namespace datalog { m_rel = dynamic_cast(m_engine.get()); } - } + } } - lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { + lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { m_last_answer = nullptr; ensure_engine(); return m_engine->query(num_rels, rels); } - + expr* context::get_answer_as_formula() { if (m_last_answer) { return m_last_answer.get(); @@ -954,7 +954,7 @@ namespace datalog { void context::display(std::ostream & out) const { display_rules(out); - if (m_rel) m_rel->display_facts(out); + if (m_rel) m_rel->display_facts(out); } void context::display_profile(std::ostream& out) const { @@ -990,10 +990,10 @@ namespace datalog { bool context::result_contains_fact(relation_fact const& f) { return m_rel && m_rel->result_contains_fact(f); } - + // NB: algebraic data-types declarations will not be printed. - static void collect_free_funcs(unsigned sz, expr* const* exprs, + static void collect_free_funcs(unsigned sz, expr* const* exprs, ast_pp_util& v, mk_fresh_name& fresh_names) { v.collect(sz, exprs); @@ -1005,7 +1005,7 @@ namespace datalog { fresh_names.add(e); } } - + void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names, unsigned_vector &bounds) { for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { expr_ref r = bind_vars(m_rule_fmls[i].get(), true); @@ -1018,7 +1018,7 @@ namespace datalog { void context::get_rules_as_formulas(expr_ref_vector& rules, expr_ref_vector& queries, svector& names) { expr_ref fml(m); rule_manager& rm = get_rule_manager(); - + // ensure that rules are all using bound variables. for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { m_free_vars(m_rule_fmls[i].get()); @@ -1067,7 +1067,7 @@ namespace datalog { } for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { rules.push_back(m_rule_fmls[i].get()); - names.push_back(m_rule_names[i]); + names.push_back(m_rule_names[i]); } } @@ -1080,7 +1080,7 @@ namespace datalog { } return out; } - + void context::display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out) { ast_manager& m = get_manager(); ast_pp_util visitor(m); @@ -1109,7 +1109,7 @@ namespace datalog { for (unsigned i = 0; i < sz; ++i) { func_decl* f = visitor.coll.get_func_decls()[i]; if (f->get_family_id() != null_family_id) { - // + // } else if (is_predicate(f) && use_fixedpoint_extensions) { rels.insert(f); @@ -1122,12 +1122,12 @@ namespace datalog { if (!use_fixedpoint_extensions) { out << "(set-logic HORN)\n"; } - for (func_decl * f : rels) + for (func_decl * f : rels) visitor.remove_decl(f); visitor.display_decls(out); - for (func_decl * f : rels) + for (func_decl * f : rels) display_rel_decl(out, f); if (use_fixedpoint_extensions && do_declare_vars) { @@ -1143,7 +1143,7 @@ namespace datalog { PP(axioms[i]); out << ")\n"; } - for (unsigned i = 0; i < rules.size(); ++i) { + for (unsigned i = 0; i < rules.size(); ++i) { out << (use_fixedpoint_extensions?"(rule ":"(assert "); expr* r = rules[i].get(); symbol nm = names[i]; @@ -1156,7 +1156,7 @@ namespace datalog { while (fresh_names.contains(nm)) { std::ostringstream s; s << nm << "!"; - nm = symbol(s.str().c_str()); + nm = symbol(s.str().c_str()); } fresh_names.add(nm); display_symbol(out, nm) << ")"; @@ -1182,7 +1182,7 @@ namespace datalog { args.push_back(m.mk_var(j, m_free_vars[j])); } qfn = m.mk_implies(q, m.mk_app(fn, args.size(), args.c_ptr())); - + out << "(assert "; PP(qfn); out << ")\n"; @@ -1209,7 +1209,7 @@ namespace datalog { smt2_pp_environment_dbg env(m); out << "(declare-rel "; display_symbol(out, f->get_name()) << " ("; - for (unsigned i = 0; i < f->get_arity(); ++i) { + for (unsigned i = 0; i < f->get_arity(); ++i) { ast_smt2_pp(out, f->get_domain(i), env); if (i + 1 < f->get_arity()) { out << " "; @@ -1239,12 +1239,12 @@ namespace datalog { void context::declare_vars(expr_ref_vector& rules, mk_fresh_name& fresh_names, std::ostream& out) { // // replace bound variables in rules by 'var declarations' - // First remove quantifers, then replace bound variables + // First remove quantifers, then replace bound variables // by fresh constants. - // + // smt2_pp_environment_dbg env(m); var_subst vsubst(m, false); - + expr_ref_vector fresh_vars(m), subst(m); expr_ref res(m); obj_map var_idxs; @@ -1257,7 +1257,7 @@ namespace datalog { quantifier* q = to_quantifier(r); if (!q->is_forall()) { continue; - } + } if (has_quantifiers(q->get_expr())) { continue; } @@ -1287,7 +1287,7 @@ namespace datalog { fresh_vars.push_back(m.mk_const(name, s)); out << "(declare-var " << name << " "; ast_smt2_pp(out, s, env); - out << ")\n"; + out << ")\n"; } subst.push_back(fresh_vars[vars[max_var]].get()); } @@ -1299,4 +1299,3 @@ namespace datalog { }; - diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index b49c7e665..6e6bc80dc 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -44,7 +44,7 @@ Revision History: #include "muz/base/bind_variables.h" #include "muz/base/rule_properties.h" -struct fixedpoint_params; +struct fp_params; namespace datalog { @@ -98,7 +98,7 @@ namespace datalog { relation_fact(ast_manager & m) : app_ref_vector(m) {} relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); } relation_fact(context & ctx); - + iterator begin() const { return c_ptr(); } iterator end() const { return c_ptr()+size(); } @@ -126,7 +126,7 @@ namespace datalog { virtual bool has_facts(func_decl * pred) const = 0; virtual void store_relation(func_decl * pred, relation_base * rel) = 0; virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) = 0; - virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) = 0; virtual bool output_profile() const = 0; virtual void collect_non_empty_predicates(func_decl_set& preds) = 0; @@ -147,7 +147,7 @@ namespace datalog { public: contains_pred(context& ctx): ctx(ctx) {} ~contains_pred() override {} - + bool operator()(expr* e) override { return ctx.is_predicate(e); } @@ -170,7 +170,7 @@ namespace datalog { register_engine_base& m_register_engine; smt_params & m_fparams; params_ref m_params_ref; - fixedpoint_params* m_params; + fp_params* m_params; bool m_generate_proof_trace; // cached configuration parameter bool m_unbound_compressor; // cached configuration parameter symbol m_default_relation; // cached configuration parameter @@ -227,7 +227,7 @@ namespace datalog { void push(); void pop(); - + bool saturation_was_run() const { return m_saturation_was_run; } void notify_saturation_was_run() { m_saturation_was_run = true; } @@ -236,7 +236,7 @@ namespace datalog { ast_manager & get_manager() const { return m; } rule_manager & get_rule_manager() { return m_rule_manager; } smt_params & get_fparams() const { return m_fparams; } - fixedpoint_params const& get_params() const { return *m_params; } + fp_params const& get_params() const { return *m_params; } DL_ENGINE get_engine() { configure_engine(); return m_engine_type; } register_engine_base& get_register_engine() { return m_register_engine; } th_rewriter& get_rewriter() { return m_rewriter; } @@ -251,7 +251,7 @@ namespace datalog { symbol default_table() const; symbol default_relation() const; void set_default_relation(symbol const& s); - symbol default_table_checker() const; + symbol default_table_checker() const; symbol check_relation() const; bool default_table_checked() const; bool dbg_fpr_nonempty_relation_signature() const; @@ -275,7 +275,7 @@ namespace datalog { bool compress_unbound() const; bool quantify_arrays() const; bool instantiate_quantifiers() const; - bool xform_bit_blast() const; + bool xform_bit_blast() const; bool xform_slice() const; bool xform_coi() const; bool array_blast() const; @@ -291,9 +291,9 @@ namespace datalog { void register_variable(func_decl* var); /* - Replace constants that have been registered as + Replace constants that have been registered as variables by de-Bruijn indices and corresponding - universal (if is_forall is true) or existential + universal (if is_forall is true) or existential quantifier. */ expr_ref bind_vars(expr* fml, bool is_forall); @@ -303,7 +303,7 @@ namespace datalog { /** Register datalog relation. - If named is true, we associate the predicate with its name, so that it can be + If named is true, we associate the predicate with its name, so that it can be retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced e.g. by rule transformations do not need to be named. */ @@ -326,7 +326,7 @@ namespace datalog { /** \brief If a predicate name has a \c func_decl object assigned, return pointer to it; otherwise return 0. - + Not all \c func_decl object used as relation identifiers need to be assigned to their names. Generally, the names coming from the parses are registered here. */ @@ -334,13 +334,13 @@ namespace datalog { func_decl * res = nullptr; m_preds_by_name.find(pred_name, res); return res; - } + } /** \brief Create a fresh head predicate declaration. */ - func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, + func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, func_decl* orig_pred=nullptr); @@ -365,13 +365,13 @@ namespace datalog { /** \brief Assign names of variables used in the declaration of a predicate. - These names are used when printing out the relations to make the output conform + These names are used when printing out the relations to make the output conform to the one of bddbddb. */ void set_argument_names(const func_decl * pred, const svector & var_names); symbol get_argument_name(const func_decl * pred, unsigned arg_index); - void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); } @@ -385,9 +385,9 @@ namespace datalog { void add_fact(func_decl * pred, const relation_fact & fact); bool has_facts(func_decl * pred) const; - + void add_rule(rule_ref& r); - + void assert_expr(expr* e); expr_ref get_background_assertion(); unsigned get_num_assertions() { return m_background.size(); } @@ -397,7 +397,7 @@ namespace datalog { Method exposed from API for adding rules. */ void add_rule(expr* rl, symbol const& name, unsigned bound = UINT_MAX); - + /** Update a named rule. @@ -421,9 +421,9 @@ namespace datalog { at 'level+1', 'level+2' etc, and include level=-1. */ expr_ref get_cover_delta(int level, func_decl* pred); - + /** - Add a property of predicate 'pred' at 'level'. + Add a property of predicate 'pred' at 'level'. It gets pushed forward when possible. */ void add_cover(int level, func_decl* pred, expr* property); @@ -432,7 +432,7 @@ namespace datalog { Add an invariant of predicate 'pred'. */ void add_invariant (func_decl *pred, expr *property); - + /** \brief Check rule subsumption. */ @@ -471,15 +471,15 @@ namespace datalog { proof_converter_ref& get_proof_converter() { return m_pc; } void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); } - void transform_rules(rule_transformer& transf); + void transform_rules(rule_transformer& transf); void transform_rules(rule_transformer::plugin* plugin); void replace_rules(rule_set const& rs); void record_transformed_rules(); - void apply_default_transformation(); + void apply_default_transformation(); void collect_params(param_descrs& r); - + void updt_params(params_ref const& p); void display_rules(std::ostream & out) const { @@ -507,7 +507,7 @@ namespace datalog { /** \brief check if query 'q' is satisfied under asserted rules and background. - If successful, return OK and into \c result assign a relation with all + If successful, return OK and into \c result assign a relation with all tuples matching the query. Otherwise return reason for failure and do not modify \c result. @@ -515,7 +515,7 @@ namespace datalog { starting from zero. The caller becomes an owner of the relation object returned in \c result. The - relation object, however, should not outlive the datalog context since it is + relation object, however, should not outlive the datalog context since it is linked to a relation plugin in the context. */ @@ -524,7 +524,7 @@ namespace datalog { lbool query_from_lvl (expr* q, unsigned lvl); /** \brief retrieve model from inductive invariant that shows query is unsat. - + \pre engine == 'pdr' || engine == 'duality' - this option is only supported for PDR mode and Duality mode. */ @@ -532,7 +532,7 @@ namespace datalog { /** \brief retrieve proof from derivation of the query. - + \pre engine == 'pdr' || engine == 'duality'- this option is only supported for PDR mode and Duality mode. */ @@ -606,7 +606,7 @@ namespace datalog { /** Just reset all tables. */ - void reset_tables(); + void reset_tables(); void flush_add_rules(); @@ -627,4 +627,3 @@ namespace datalog { }; #endif /* DL_CONTEXT_H_ */ - diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fp_params.pyg similarity index 99% rename from src/muz/base/fixedpoint_params.pyg rename to src/muz/base/fp_params.pyg index 7e92b0d2c..c96a12ca2 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fp_params.pyg @@ -1,4 +1,4 @@ -def_module_params('fixedpoint', +def_module_params('fp', description='fixedpoint parameters', export=True, params=(('timeout', UINT, UINT_MAX, 'set timeout'), diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 56b7e449d..c7657a0d7 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -33,7 +33,7 @@ Revision History: #include "muz/transforms/dl_mk_rule_inliner.h" #include "ast/scoped_proof.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index ba12c849c..4605826ba 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -30,23 +30,23 @@ Notes: #include "util/scoped_ctrl_c.h" #include "util/scoped_timer.h" #include "util/trail.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include struct dl_context { smt_params m_fparams; params_ref m_params_ref; - fixedpoint_params m_params; + fp_params m_params; cmd_context & m_cmd; datalog::register_engine m_register_engine; dl_collected_cmds* m_collected_cmds; unsigned m_ref_count; datalog::dl_decl_plugin* m_decl_plugin; - scoped_ptr m_context; + scoped_ptr m_context; trail_stack m_trail; - fixedpoint_params const& get_params() { + fp_params const& get_params() { init(); return m_context->get_params(); } @@ -58,18 +58,18 @@ struct dl_context { m_ref_count(0), m_decl_plugin(nullptr), m_trail(*this) {} - + void inc_ref() { ++m_ref_count; } - + void dec_ref() { --m_ref_count; if (0 == m_ref_count) { dealloc(this); } } - + void init() { ast_manager& m = m_cmd.m(); if (!m_context) { @@ -83,10 +83,10 @@ struct dl_context { else { m_decl_plugin = alloc(datalog::dl_decl_plugin); m.register_plugin(symbol("datalog_relation"), m_decl_plugin); - } + } } } - + void reset() { m_context = nullptr; } @@ -97,9 +97,9 @@ struct dl_context { m_trail.push(push_back_vector(m_collected_cmds->m_rels)); } dlctx().register_predicate(pred, false); - dlctx().set_predicate_representation(pred, num_kinds, kinds); + dlctx().set_predicate_representation(pred, num_kinds, kinds); } - + void add_rule(expr * rule, symbol const& name, unsigned bound) { init(); if (m_collected_cmds) { @@ -112,7 +112,7 @@ struct dl_context { else { m_context->add_rule(rule, name, bound); } - } + } bool collect_query(func_decl* q) { if (m_collected_cmds) { @@ -127,7 +127,7 @@ struct dl_context { m_collected_cmds->m_queries.push_back(qr); m_trail.push(push_back_vector(m_collected_cmds->m_queries)); return true; - } + } else { return false; } @@ -142,7 +142,7 @@ struct dl_context { m_trail.pop_scope(1); dlctx().pop(); } - + datalog::context & dlctx() { init(); return *m_context; @@ -162,7 +162,7 @@ class dl_rule_cmd : public cmd { public: dl_rule_cmd(dl_context * dl_ctx): cmd("rule"), - m_dl_ctx(dl_ctx), + m_dl_ctx(dl_ctx), m_arg_idx(0), m_t(nullptr), m_bound(UINT_MAX) {} @@ -210,7 +210,7 @@ public: } char const * get_usage() const override { return "predicate"; } char const * get_main_descr() const override { - return "pose a query to a predicate based on the Horn rules."; + return "pose a query to a predicate based on the Horn rules."; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { @@ -243,9 +243,9 @@ public: return; } datalog::context& dlctx = m_dl_ctx->dlctx(); - set_background(ctx); + set_background(ctx); dlctx.updt_params(m_params); - unsigned timeout = m_dl_ctx->get_params().timeout(); + unsigned timeout = m_dl_ctx->get_params().timeout(); cancel_eh eh(ctx.m().limit()); bool query_exn = false; lbool status = l_undef; @@ -271,12 +271,12 @@ public: ctx.regular_stream() << "unsat\n"; print_certificate(ctx); break; - case l_true: + case l_true: ctx.regular_stream() << "sat\n"; print_answer(ctx); print_certificate(ctx); break; - case l_undef: + case l_undef: if (dlctx.get_status() == datalog::BOUNDED){ ctx.regular_stream() << "bounded\n"; print_certificate(ctx); @@ -287,7 +287,7 @@ public: case datalog::INPUT_ERROR: ctx.regular_stream() << "input error\n"; break; - + case datalog::MEMOUT: ctx.regular_stream() << "memory bounds exceeded\n"; break; @@ -295,12 +295,12 @@ public: case datalog::TIMEOUT: ctx.regular_stream() << "timeout\n"; break; - + case datalog::APPROX: ctx.regular_stream() << "approximated relations\n"; break; - case datalog::OK: + case datalog::OK: (void)query_exn; SASSERT(query_exn); break; @@ -324,7 +324,7 @@ public: void init_pdescrs(cmd_context & ctx, param_descrs & p) override { m_dl_ctx->dlctx().collect_params(p); } - + private: void set_background(cmd_context& ctx) { @@ -356,8 +356,8 @@ private: statistics st; datalog::context& dlctx = m_dl_ctx->dlctx(); dlctx.collect_statistics(st); - st.update("time", ctx.get_seconds()); - st.display_smt2(ctx.regular_stream()); + st.update("time", ctx.get_seconds()); + st.display_smt2(ctx.regular_stream()); } } @@ -391,8 +391,8 @@ public: void prepare(cmd_context & ctx) override { ctx.m(); // ensure manager is initialized. - m_arg_idx = 0; - m_query_arg_idx = 0; + m_arg_idx = 0; + m_query_arg_idx = 0; m_domain.reset(); m_kinds.reset(); } @@ -443,21 +443,21 @@ public: m_arg_idx(0), m_dl_ctx(dl_ctx) {} - + char const * get_usage() const override { return " "; } char const * get_descr(cmd_context & ctx) const override { return "declare constant as variable"; } unsigned get_arity() const override { return 2; } void prepare(cmd_context & ctx) override { ctx.m(); // ensure manager is initialized. - m_arg_idx = 0; + m_arg_idx = 0; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { SASSERT(m_arg_idx <= 1); if (m_arg_idx == 0) { - return CPK_SYMBOL; + return CPK_SYMBOL; } - return CPK_SORT; + return CPK_SORT; } void set_next_arg(cmd_context & ctx, sort* s) override { @@ -466,7 +466,7 @@ public: } void set_next_arg(cmd_context & ctx, symbol const & s) override { - m_var_name = s; + m_var_name = s; ++m_arg_idx; } @@ -523,7 +523,7 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c ctx.insert(alloc(dl_query_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_var_cmd, dl_ctx)); - ctx.insert(alloc(dl_push_cmd, dl_ctx)); + ctx.insert(alloc(dl_push_cmd, dl_ctx)); ctx.insert(alloc(dl_pop_cmd, dl_ctx)); } diff --git a/src/muz/fp/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index 4843a2623..eef95915d 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -27,7 +27,7 @@ Revision History: #include "muz/transforms/dl_mk_slice.h" #include "tactic/generic_model_converter.h" #include "muz/transforms/dl_transforms.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "ast/ast_util.h" #include "ast/rewriter/var_subst.h" @@ -71,7 +71,7 @@ class horn_tactic : public tactic { f = to_quantifier(f)->get_expr(); } else if (is_exists(f) && !is_positive) { - f = to_quantifier(f)->get_expr(); + f = to_quantifier(f)->get_expr(); } else if (m.is_not(f, e)) { is_positive = !is_positive; @@ -84,7 +84,7 @@ class horn_tactic : public tactic { if (!is_positive) { f = m.mk_not(f); } - + } bool is_predicate(expr* a) { @@ -144,7 +144,7 @@ class horn_tactic : public tactic { expr* a = nullptr, *a1 = nullptr; flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { - a = args[i].get(); + a = args[i].get(); check_predicate(mark, a); if (m.is_not(a, a1)) { body.push_back(a1); @@ -176,13 +176,13 @@ class horn_tactic : public tactic { return expr_ref(m.mk_implies(body, head), m); } - void operator()(goal_ref const & g, + void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("horn", *g); bool produce_proofs = g->proofs_enabled(); - if (produce_proofs) { + if (produce_proofs) { if (!m_ctx.generate_proof_trace()) { params_ref params = m_ctx.get_params().p; params.set_bool("generate_proof_trace", true); @@ -208,7 +208,7 @@ class horn_tactic : public tactic { case IS_QUERY: queries.push_back(f); break; - default: + default: msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n"; TRACE("horn", tout << msg.str();); throw tactic_exception(msg.str().c_str()); @@ -243,10 +243,10 @@ class horn_tactic : public tactic { g->set(mc.get()); } - void verify(expr* q, + void verify(expr* q, goal_ref const& g, - goal_ref_buffer & result, - model_converter_ref & mc, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc) { lbool is_reachable = l_undef; @@ -275,9 +275,9 @@ class horn_tactic : public tactic { else { g->assert_expr(m.mk_false()); } - break; + break; } - case l_false: { + case l_false: { // goal is sat g->reset(); if (produce_models) { @@ -290,11 +290,11 @@ class horn_tactic : public tactic { mc = mc2; } } - break; + break; } - case l_undef: + case l_undef: // subgoal is unchanged. - break; + break; } TRACE("horn", g->display(tout);); SASSERT(g->is_well_sorted()); @@ -314,20 +314,20 @@ class horn_tactic : public tactic { } } - void simplify(expr* q, + void simplify(expr* q, goal_ref const& g, - goal_ref_buffer & result, - model_converter_ref & mc, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc) { - expr_ref fml(m); + expr_ref fml(m); func_decl* query_pred = to_app(q)->get_decl(); m_ctx.set_output_predicate(query_pred); m_ctx.get_rules(); // flush adding rules. apply_default_transformation(m_ctx); - + if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); @@ -351,7 +351,7 @@ class horn_tactic : public tactic { g->assert_expr(fml); } } - + }; bool m_is_simplify; @@ -368,7 +368,7 @@ public: tactic * translate(ast_manager & m) override { return alloc(horn_tactic, m_is_simplify, m, m_params); } - + ~horn_tactic() override { dealloc(m_imp); } @@ -378,16 +378,16 @@ public: m_imp->updt_params(p); } - + void collect_param_descrs(param_descrs & r) override { m_imp->collect_param_descrs(r); } - - void operator()(goal_ref const & in, + + void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } - + void collect_statistics(statistics & st) const override { m_imp->collect_statistics(st); st.copy(m_stats); @@ -397,15 +397,15 @@ public: m_stats.reset(); m_imp->reset_statistics(); } - + void cleanup() override { ast_manager & m = m_imp->m; m_imp->collect_statistics(m_stats); dealloc(m_imp); m_imp = alloc(imp, m_is_simplify, m, m_params); - + } - + }; @@ -416,4 +416,3 @@ tactic * mk_horn_tactic(ast_manager & m, params_ref const & p) { tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(horn_tactic, true, m, p)); } - diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 40c42015b..03093ab63 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2175,8 +2175,7 @@ pob* pred_transformer::pobs::mk_pob(pob *parent, // ---------------- // context -context::context(fixedpoint_params const& params, - ast_manager& m) : +context::context(fp_params const& params, ast_manager& m) : m_params(params), m(m), m_context(nullptr), diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index f3d00a857..760f38f69 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -33,7 +33,7 @@ Notes: #include "muz/spacer/spacer_prop_solver.h" #include "muz/spacer/spacer_json.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { class rule_set; @@ -877,7 +877,7 @@ class context { stopwatch m_create_children_watch; stopwatch m_init_rules_watch; - fixedpoint_params const& m_params; + fp_params const& m_params; ast_manager& m; datalog::context* m_context; manager m_pm; @@ -993,11 +993,11 @@ public: Initial values of predicates are stored in corresponding relations in dctx. We check whether there is some reachable state of the relation checked_relation. */ - context(fixedpoint_params const& params, ast_manager& m); + context(fp_params const& params, ast_manager& m); ~context(); - const fixedpoint_params &get_params() const { return m_params; } + const fp_params &get_params() const { return m_params; } bool use_native_mbp () {return m_use_native_mbp;} bool use_ground_pob () {return m_ground_pob;} bool use_instantiate () {return m_instantiate;} diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index 5a3a53f0b..a68db4c0a 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -99,8 +99,8 @@ void iuc_solver::pop_bg (unsigned n) { if (n == 0) { return; } - if (m_assumptions.size () > m_first_assumption) { - m_assumptions.shrink(m_first_assumption); + if (m_assumptions.size () > m_first_assumption) { + m_assumptions.shrink(m_first_assumption); } m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0; m_assumptions.shrink (m_first_assumption); @@ -111,8 +111,8 @@ unsigned iuc_solver::get_num_bg () {return m_first_assumption;} lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions) { // -- remove any old assumptions - m_assumptions.shrink(m_first_assumption); - + m_assumptions.shrink(m_first_assumption); + // -- replace theory literals in background assumptions with proxies mk_proxies (m_assumptions); // -- in case mk_proxies added new literals, they are all background @@ -126,12 +126,12 @@ lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, vector const & clauses) { - if (clauses.empty()) + if (clauses.empty()) return check_sat(cube.size(), cube.c_ptr()); - + // -- remove any old assumptions - m_assumptions.shrink(m_first_assumption); - + m_assumptions.shrink(m_first_assumption); + // -- replace theory literals in background assumptions with proxies mk_proxies(m_assumptions); // -- in case mk_proxies added new literals, they are all background @@ -147,8 +147,8 @@ lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, app* iuc_solver::def_manager::mk_proxy (expr *v) { app* r; - if (m_expr2proxy.find(v, r)) - return r; + if (m_expr2proxy.find(v, r)) + return r; ast_manager &m = m_parent.m; app* proxy = m_parent.fresh_proxy (); @@ -184,14 +184,14 @@ bool iuc_solver::def_manager::is_proxy_def (expr *v) bool iuc_solver::is_proxy(expr *e, app_ref &def) { - if (!is_uninterp_const(e)) - return false; + if (!is_uninterp_const(e)) + return false; app* a = to_app (e); for (int i = m_defs.size (); i-- > 0; ) if (m_defs[i].is_proxy (a, def)) - return true; + return true; return m_base_defs.is_proxy (a, def); } @@ -263,9 +263,9 @@ void iuc_solver::elim_proxies (expr_ref_vector &v) expr_ref f = mk_and (v); scoped_ptr rep = mk_expr_simp_replacer (m); rep->set_substitution (&m_elim_proxies_sub); - (*rep) (f); - v.reset (); - flatten_and (f, v); + (*rep)(f); + v.reset(); + flatten_and(f, v); } void iuc_solver::get_iuc(expr_ref_vector &core) @@ -362,7 +362,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core) // -- register iuc plugins switch (m_iuc_arith) { case 0: - case 1: + case 1: plugin = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, @@ -398,18 +398,18 @@ void iuc_solver::get_iuc(expr_ref_vector &core) UNREACHABLE(); break; } - + { scoped_watch _t_ (m_learn_core_sw); // compute interpolating unsat core learner.compute_unsat_core(core); } - + elim_proxies (core); // AG: this should be taken care of by minimizing the iuc cut simplify_bounds (core); } - + IF_VERBOSE(2, verbose_stream () << "IUC Core:\n" << core << "\n";); } diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h index 3c6251be0..c0a0072ed 100644 --- a/src/muz/spacer/spacer_iuc_solver.h +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -65,7 +65,7 @@ private: bool m_print_farkas_stats; bool m_old_hyp_reducer; bool is_proxy(expr *e, app_ref &def); - void undo_proxies_in_core(ptr_vector &v); + void undo_proxies_in_core(expr_ref_vector &v); app* mk_proxy(expr *v); app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); @@ -101,16 +101,16 @@ public: void pop_bg(unsigned n); unsigned get_num_bg(); - void get_full_unsat_core(ptr_vector &core) { + void get_full_unsat_core(ptr_vector &core) { expr_ref_vector _core(m); - m_solver.get_unsat_core(_core); + m_solver.get_unsat_core(_core); core.append(_core.size(), _core.c_ptr()); } /* solver interface */ - solver* translate(ast_manager &m, params_ref const &p) override { - return m_solver.translate(m, p); + solver* translate(ast_manager &m, params_ref const &p) override { + return m_solver.translate(m, p); } void updt_params(params_ref const &p) override { m_solver.updt_params(p); } void reset_params(params_ref const &p) override { m_solver.reset_params(p); } @@ -136,8 +136,8 @@ public: expr * get_assertion(unsigned idx) const override { return m_solver.get_assertion(idx); } unsigned get_num_assumptions() const override { return m_solver.get_num_assumptions(); } expr * get_assumption(unsigned idx) const override { return m_solver.get_assumption(idx); } - std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const override { - return m_solver.display(out, n, es); + std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const override { + return m_solver.display(out, n, es); } /* check_sat_result interface */ @@ -159,7 +159,7 @@ public: iuc_solver &m_s; expr_ref_vector &m_v; public: - scoped_mk_proxy(iuc_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) { + scoped_mk_proxy(iuc_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) { m_s.mk_proxies(m_v); } ~scoped_mk_proxy() { m_s.undo_proxies(m_v); } @@ -171,8 +171,8 @@ public: public: scoped_bg(iuc_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} ~scoped_bg() { - if (m_s.get_num_bg() > m_bg_sz) { - m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); + if (m_s.get_num_bg() > m_bg_sz) { + m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); } } }; diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 435f0af3a..7bd21a1b5 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -36,13 +36,13 @@ Revision History: #include "muz/spacer/spacer_prop_solver.h" #include "model/model_evaluator.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace spacer { prop_solver::prop_solver(ast_manager &m, solver *solver0, solver *solver1, - fixedpoint_params const& p, symbol const& name) : + fp_params const& p, symbol const& name) : m(m), m_name(name), m_ctx(nullptr), @@ -329,13 +329,14 @@ lbool prop_solver::internal_check_assumptions(expr_ref_vector &hard_atoms, } if (result == l_false && m_core && m.proofs_enabled() && !m_subset_based_core) { - TRACE("spacer", tout << "theory core\n";); + TRACE("spacer", tout << "Using IUC core\n";); m_core->reset(); m_ctx->get_iuc(*m_core); } else if (result == l_false && m_core) { m_core->reset(); m_ctx->get_unsat_core(*m_core); // manually undo proxies because maxsmt() call above manually adds proxies + // AG: don't think this is needed. maxsmt() undoes the proxies already m_ctx->undo_proxies(*m_core); } diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 042215eff..1db65dc3e 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -33,7 +33,7 @@ Revision History: #include "muz/spacer/spacer_iuc_solver.h" #include "muz/spacer/spacer_util.h" -struct fixedpoint_params; +struct fp_params; namespace spacer { typedef ptr_vector decl_vector; @@ -76,7 +76,7 @@ private: public: prop_solver(ast_manager &m, solver *solver0, solver* solver1, - fixedpoint_params const& p, symbol const& name); + fp_params const& p, symbol const& name); void set_core(expr_ref_vector* core) { m_core = core; } diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index 39b7af634..2fb329ff4 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -30,7 +30,7 @@ Revision History: #include "ast/for_each_expr.h" #include "ast/substitution/matcher.h" #include "ast/scoped_proof.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "ast/ast_util.h" namespace tb { diff --git a/src/muz/transforms/dl_mk_array_eq_rewrite.cpp b/src/muz/transforms/dl_mk_array_eq_rewrite.cpp index c33cecda9..61eafae64 100644 --- a/src/muz/transforms/dl_mk_array_eq_rewrite.cpp +++ b/src/muz/transforms/dl_mk_array_eq_rewrite.cpp @@ -20,7 +20,7 @@ Revision History: #include "ast/expr_abstract.h" #include "muz/base/dl_context.h" #include "muz/base/dl_context.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "muz/transforms/dl_mk_array_eq_rewrite.h" #include "ast/factor_equivs.h" diff --git a/src/muz/transforms/dl_mk_array_instantiation.cpp b/src/muz/transforms/dl_mk_array_instantiation.cpp index 362d83865..6a8f0ce81 100644 --- a/src/muz/transforms/dl_mk_array_instantiation.cpp +++ b/src/muz/transforms/dl_mk_array_instantiation.cpp @@ -23,7 +23,7 @@ Revision History: #include "muz/base/dl_context.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 8a21bc37c..992b8f51b 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -24,7 +24,7 @@ Revision History: #include "ast/rewriter/expr_safe_replace.h" #include "tactic/generic_model_converter.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "ast/scoped_proof.h" #include "model/model_v2_pp.h" @@ -32,9 +32,9 @@ namespace datalog { // // P(v) :- Q(extract[1:1]v ++ 0), R(1 ++ extract[0:0]v). - // -> + // -> // P(bv(x,y)) :- Q(bv(x,0)), R(bv(1,y)) . - // + // // Introduce P_bv: // P_bv(x,y) :- Q_bv(x,0), R_bv(1,y) // P(bv(x,y)) :- P_bv(x,y) @@ -51,7 +51,7 @@ namespace datalog { bit_blast_model_converter(ast_manager& m): m(m), m_bv(m), - m_old_funcs(m), + m_old_funcs(m), m_new_funcs(m) {} void insert(func_decl* old_f, func_decl* new_f) { @@ -73,7 +73,7 @@ namespace datalog { func_decl* q = m_old_funcs[i].get(); func_interp* f = model->get_func_interp(p); if (!f) continue; - expr_ref body(m); + expr_ref body(m); unsigned arity_q = q->get_arity(); TRACE("dl", model_v2_pp(tout, *model); @@ -87,10 +87,10 @@ namespace datalog { if (f) { body = f->get_interp(); SASSERT(!f->is_partial()); - SASSERT(body); + SASSERT(body); } else { - body = m.mk_false(); + body = m.mk_false(); } unsigned idx = 0; expr_ref arg(m), proj(m); @@ -104,18 +104,18 @@ namespace datalog { for (unsigned k = 0; k < sz; ++k) { parameter p(k); proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t); - sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); + sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); } } else { sub.insert(m.mk_var(idx++, s), arg); } } - sub(body); + sub(body); g->set_else(body); model->register_decl(q, g); - } - } + } + } }; class expand_mkbv_cfg : public default_rewriter_cfg { @@ -134,10 +134,10 @@ namespace datalog { public: expand_mkbv_cfg(context& ctx): - m_context(ctx), + m_context(ctx), m(ctx.get_manager()), m_util(m), - m_args(m), + m_args(m), m_f_vars(m), m_g_vars(m), m_old_funcs(m), @@ -152,8 +152,8 @@ namespace datalog { void set_dst(rule_set* dst) { m_dst = dst; } func_decl_ref_vector const& old_funcs() const { return m_old_funcs; } func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } - - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num == 0) { if (m_src->is_output_predicate(f)) m_dst->set_output_predicate(f); @@ -165,9 +165,9 @@ namespace datalog { return BR_FAILED; } - // + // // f(mk_bv(args),...) - // + // m_args.reset(); m_g_vars.reset(); m_f_vars.reset(); @@ -191,9 +191,9 @@ namespace datalog { } } func_decl* g = nullptr; - + if (!m_pred2blast.find(f, g)) { - + ptr_vector domain; for (unsigned i = 0; i < m_args.size(); ++i) { domain.push_back(m.get_sort(m_args[i].get())); @@ -262,7 +262,7 @@ namespace datalog { m_params.set_bool("blast_quant", true); m_blaster.updt_params(m_params); } - + rule_set * operator()(rule_set const & source) { // TODO pc if (!m_context.xform_bit_blast()) { @@ -270,8 +270,8 @@ namespace datalog { } rule_manager& rm = m_context.get_rule_manager(); unsigned sz = source.get_num_rules(); - expr_ref fml(m); - rule_set * result = alloc(rule_set, m_context); + expr_ref fml(m); + rule_set * result = alloc(rule_set, m_context); m_rewriter.m_cfg.set_src(&source); m_rewriter.m_cfg.set_dst(result); for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { @@ -299,8 +299,8 @@ namespace datalog { if (!source.contains(*I)) result->set_output_predicate(*I); } - - if (m_context.get_model_converter()) { + + if (m_context.get_model_converter()) { generic_model_converter* fmc = alloc(generic_model_converter, m, "dl_mk_bit_blast"); bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m); func_decl_ref_vector const& old_funcs = m_rewriter.m_cfg.old_funcs(); @@ -311,7 +311,7 @@ namespace datalog { } m_context.add_model_converter(concat(bvmc, fmc)); } - + return result; } }; @@ -326,6 +326,6 @@ namespace datalog { rule_set * mk_bit_blast::operator()(rule_set const & source) { return (*m_impl)(source); - } + } }; diff --git a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp index 38000c65a..c1abd7bef 100644 --- a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp @@ -27,7 +27,7 @@ Revision History: #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "ast/ast_util.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { // ----------------------------------- @@ -46,7 +46,7 @@ namespace datalog { bool mk_interp_tail_simplifier::rule_substitution::unify(expr * e1, expr * e2) { SASSERT(m_rule); - //we need to apply the current substitution in order to ensure the unifier + //we need to apply the current substitution in order to ensure the unifier //works in an incremental way expr_ref e1_s(m); expr_ref e2_s(m); @@ -268,7 +268,7 @@ namespace datalog { if (neq) { have_pair = false; v[prev_pair_idx] = neq; - + read_idx++; continue; } @@ -294,7 +294,7 @@ namespace datalog { //bool detect_same_variable_conj_pairs - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (m.is_not(f) && (m.is_and(args[0]) || m.is_or(args[0]))) { @@ -307,15 +307,15 @@ namespace datalog { m_app_args.push_back(tmp); } if (m.is_and(args[0])) { - result = mk_or(m_app_args); + result = mk_or(m_app_args); } else { - result = mk_and(m_app_args); + result = mk_and(m_app_args); } return BR_REWRITE2; } - if (!m.is_and(f) && !m.is_or(f)) { - return BR_FAILED; + if (!m.is_and(f) && !m.is_or(f)) { + return BR_FAILED; } if (num == 0) { if (m.is_and(f)) { @@ -375,7 +375,7 @@ namespace datalog { m_simp(ctx.get_rewriter()), a(m), m_rule_subst(ctx), - m_tail(m), + m_tail(m), m_itail_members(m), m_conj(m) { m_cfg = alloc(normalizer_cfg, m); @@ -386,7 +386,7 @@ namespace datalog { dealloc(m_rw); dealloc(m_cfg); } - + void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res) { @@ -537,7 +537,7 @@ namespace datalog { simplify_expr(itail.get(), simp_res); modified |= itail.get() != simp_res.get(); - + if (m.is_false(simp_res)) { TRACE("dl", r->display(m_context, tout << "rule is infeasible\n");); return false; @@ -568,7 +568,7 @@ namespace datalog { rule_ref pro_var_eq_result(m_context.get_rule_manager()); if (propagate_variable_equivalences(res, pro_var_eq_result)) { - SASSERT(rule_counter().get_max_rule_var(*r.get())==0 || + SASSERT(rule_counter().get_max_rule_var(*r.get())==0 || rule_counter().get_max_rule_var(*r.get()) > rule_counter().get_max_rule_var(*pro_var_eq_result.get())); r = pro_var_eq_result; goto start; @@ -607,8 +607,8 @@ namespace datalog { rule_set * res = alloc(rule_set, m_context); if (transform_rules(source, *res)) { res->inherit_predicates(source); - TRACE("dl", - source.display(tout); + TRACE("dl", + source.display(tout); res->display(tout);); } else { dealloc(res); @@ -616,6 +616,5 @@ namespace datalog { } return res; } - -}; +}; diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp index e9a4f2f54..dafb0ddd5 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp @@ -11,7 +11,7 @@ Abstract: Author: - Ken McMillan + Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 @@ -23,13 +23,12 @@ Revision History: #include "muz/base/dl_context.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" -#include "muz/base/fixedpoint_params.hpp" namespace datalog { - // model converter: + // model converter: // Given model for P^(x, y, i, a[i]) // create model: P(x,y,a) == forall i . P^(x,y,i,a[i]) // requires substitution and list of bound variables. @@ -55,7 +54,7 @@ namespace datalog { void display(std::ostream& out) override { display_add(out, m); } - void get_units(obj_map& units) override { units.reset(); } + void get_units(obj_map& units) override { units.reset(); } void insert(func_decl* old_p, func_decl* new_p, expr_ref_vector& sub, sort_ref_vector& sorts, svector const& bound) { m_old_funcs.push_back(old_p); @@ -74,7 +73,7 @@ namespace datalog { sort_ref_vector const& sorts = m_sorts[i]; svector const& is_bound = m_bound[i]; func_interp* f = old_model->get_func_interp(p); - expr_ref body(m); + expr_ref body(m); unsigned arity_q = q->get_arity(); SASSERT(0 < p->get_arity()); func_interp* g = alloc(func_interp, m, arity_q); @@ -82,7 +81,7 @@ namespace datalog { if (f) { body = f->get_interp(); SASSERT(!f->is_partial()); - SASSERT(body); + SASSERT(body); } else { expr_ref_vector args(m); @@ -94,7 +93,7 @@ namespace datalog { // Create quantifier wrapper around body. TRACE("dl", tout << mk_pp(body, m) << "\n";); - // 1. replace variables by the compound terms from + // 1. replace variables by the compound terms from // the original predicate. expr_safe_replace rep(m); for (unsigned i = 0; i < sub.size(); ++i) { @@ -121,7 +120,7 @@ namespace datalog { _free.push_back(consts.back()); } } - rep(body); + rep(body); rep.reset(); TRACE("dl", tout << mk_pp(body, m) << "\n";); @@ -130,18 +129,18 @@ namespace datalog { body = m.mk_forall(names.size(), bound_sorts.c_ptr(), names.c_ptr(), body); TRACE("dl", tout << mk_pp(body, m) << "\n";); - // 4. replace remaining constants by variables. + // 4. replace remaining constants by variables. for (unsigned i = 0; i < _free.size(); ++i) { rep.insert(_free[i].get(), m.mk_var(i, m.get_sort(_free[i].get()))); } - rep(body); + rep(body); g->set_else(body); TRACE("dl", tout << mk_pp(body, m) << "\n";); new_model->register_decl(q, g); - } + } old_model = new_model; - } + } }; mk_quantifier_abstraction::mk_quantifier_abstraction( @@ -154,7 +153,7 @@ namespace datalog { m_mc(nullptr) { } - mk_quantifier_abstraction::~mk_quantifier_abstraction() { + mk_quantifier_abstraction::~mk_quantifier_abstraction() { } func_decl* mk_quantifier_abstraction::declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p) { @@ -178,7 +177,7 @@ namespace datalog { func_decl* new_p = nullptr; if (!m_old2new.find(old_p, new_p)) { expr_ref_vector sub(m), vars(m); - svector bound; + svector bound; sort_ref_vector domain(m), sorts(m); expr_ref arg(m); for (unsigned i = 0; i < sz; ++i) { @@ -208,7 +207,7 @@ namespace datalog { bound.push_back(false); sub.push_back(arg); sorts.push_back(s0); - } + } SASSERT(old_p->get_range() == m.mk_bool_sort()); new_p = m.mk_func_decl(old_p->get_name(), domain.size(), domain.c_ptr(), old_p->get_range()); m_refs.push_back(new_p); @@ -242,12 +241,12 @@ namespace datalog { } args.push_back(arg); } - TRACE("dl", + TRACE("dl", tout << mk_pp(new_p, m) << "\n"; for (unsigned i = 0; i < args.size(); ++i) { tout << mk_pp(args[i].get(), m) << "\n"; }); - return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); + return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); } app_ref mk_quantifier_abstraction::mk_tail(rule_set const& rules, rule_set& dst, app* p) { @@ -272,7 +271,7 @@ namespace datalog { for (unsigned i = 0; i < sz; ++i) { arg = ps->get_arg(i); sort* s = m.get_sort(arg); - bool is_pattern = false; + bool is_pattern = false; while (a.is_array(s)) { is_pattern = true; unsigned arity = get_array_arity(s); @@ -304,9 +303,9 @@ namespace datalog { ptr_vector args2; args2.push_back(arg); args2.append(num_args, args); - return a.mk_select(args2.size(), args2.c_ptr()); + return a.mk_select(args2.size(), args2.c_ptr()); } - + rule_set * mk_quantifier_abstraction::operator()(rule_set const & source) { if (!m_ctx.quantify_arrays()) { return nullptr; @@ -334,10 +333,10 @@ namespace datalog { } rule_set * result = alloc(rule_set, m_ctx); - for (unsigned i = 0; i < sz; ++i) { + for (unsigned i = 0; i < sz; ++i) { tail.reset(); rule & r = *source.get_rule(i); - TRACE("dl", r.display(m_ctx, tout); ); + TRACE("dl", r.display(m_ctx, tout); ); unsigned cnt = vc.get_max_rule_var(r)+1; unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); @@ -352,8 +351,8 @@ namespace datalog { proof_ref pr(m); rm.mk_rule(fml, pr, *result, r.name()); TRACE("dl", result->last()->display(m_ctx, tout);); - } - + } + // proof converter: proofs are not necessarily preserved using this transformation. if (m_old2new.empty()) { @@ -371,5 +370,3 @@ namespace datalog { }; - - diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index 584f44303..317f13ff8 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -18,7 +18,7 @@ Revision History: Added linear_inline 2012-9-10 (nbjorner) Disable inliner for quantified rules 2012-10-31 (nbjorner) - + Notes: Resolution transformation (resolve): @@ -27,7 +27,7 @@ Resolution transformation (resolve): -------------------------------------------------- P(x) :- R(z), phi(x,y), psi(y,z) - Proof converter: + Proof converter: replace assumption (*) by rule and upper assumptions. @@ -37,9 +37,9 @@ Subsumption transformation (remove rule): P(x) :- Q(y), phi(x,y) Rules --------------------------------- Rules - - - Model converter: + + + Model converter: P(x) := P(x) or (exists y . Q(y) & phi(x,y)) @@ -52,7 +52,7 @@ Subsumption transformation (remove rule): #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_rule_inliner.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { @@ -67,15 +67,15 @@ namespace datalog { unsigned var_cnt = std::max(vc.get_max_rule_var(tgt), vc.get_max_rule_var(src))+1; m_subst.reset(); m_subst.reserve(2, var_cnt); - + m_ready = m_unif(tgt.get_tail(tgt_idx), src.get_head(), m_subst); if (m_ready) { m_deltas[0] = 0; m_deltas[1] = var_cnt; - TRACE("dl", - output_predicate(m_context, src.get_head(), tout << "unify rules "); - output_predicate(m_context, tgt.get_head(), tout << "\n"); + TRACE("dl", + output_predicate(m_context, src.get_head(), tout << "unify rules "); + output_predicate(m_context, tgt.get_head(), tout << "\n"); tout << "\n";); } return m_ready; @@ -90,7 +90,7 @@ namespace datalog { } void rule_unifier::apply( - rule const& r, bool is_tgt, unsigned skipped_index, + rule const& r, bool is_tgt, unsigned skipped_index, app_ref_vector& res, svector& res_neg) { unsigned rule_len = r.get_tail_size(); for (unsigned i = 0; i < rule_len; i++) { @@ -127,7 +127,7 @@ namespace datalog { ); if (m_normalize) { - m_rm.fix_unbound_vars(res, true); + m_rm.fix_unbound_vars(res, true); if (m_interp_simplifier.transform_rule(res.get(), simpl_rule)) { res = simpl_rule; return true; @@ -150,8 +150,8 @@ namespace datalog { for (unsigned i = 0; i < sorts.size(); ++i) { v = m.mk_var(i, sorts[i]); m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w); - result.push_back(w); - } + result.push_back(w); + } return result; } @@ -184,7 +184,7 @@ namespace datalog { expr_ref_vector s2 = m_unifier.get_rule_subst(src, false); datalog::resolve_rule(m_rm, tgt, src, tail_index, s1, s2, *res.get()); } - return true; + return true; } else { TRACE("dl", res->display(m_context, tout << "interpreted tail is unsat\n");); @@ -240,12 +240,12 @@ namespace datalog { return false; } - // - // these conditions are optional, they avoid possible exponential increase + // + // these conditions are optional, they avoid possible exponential increase // in the size of the problem - // + // - return + return //m_head_pred_non_empty_tails_ctr.get(pred)<=1 m_head_pred_ctr.get(pred) <= 1 || (m_tail_pred_ctr.get(pred) <= 1 && m_head_pred_ctr.get(pred) <= 4) @@ -253,7 +253,7 @@ namespace datalog { } /** Caller has to dealloc the returned object */ - rule_set * mk_rule_inliner::create_allowed_rule_set(rule_set const & orig) + rule_set * mk_rule_inliner::create_allowed_rule_set(rule_set const & orig) { rule_set * res = alloc(rule_set, m_context); for (rule * r : orig) { @@ -268,7 +268,7 @@ namespace datalog { /** Try to make the set of inlined predicates acyclic by forbidding inlining of one - predicate from each strongly connected component. Return true if we did forbide some + predicate from each strongly connected component. Return true if we did forbide some predicate, and false if the set of rules is already acyclic. */ bool mk_rule_inliner::forbid_preds_from_cycles(rule_set const & r) @@ -276,7 +276,7 @@ namespace datalog { SASSERT(r.is_closed()); bool something_forbidden = false; - + const rule_stratifier::comp_vector& comps = r.get_stratifier().get_strats(); for (rule_stratifier::item_set * stratum : comps) { @@ -293,12 +293,12 @@ namespace datalog { return something_forbidden; } - bool mk_rule_inliner::forbid_multiple_multipliers(const rule_set & orig, + bool mk_rule_inliner::forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules) { bool something_forbidden = false; - const rule_stratifier::comp_vector& comps = + const rule_stratifier::comp_vector& comps = proposed_inlined_rules.get_stratifier().get_strats(); for (rule_stratifier::item_set * stratum : comps) { @@ -332,7 +332,7 @@ namespace datalog { } else { is_multi_head_pred = true; - m_head_pred_ctr.get(head_pred) = + m_head_pred_ctr.get(head_pred) = m_head_pred_ctr.get(head_pred)*tail_pred_head_cnt; } } @@ -379,7 +379,7 @@ namespace datalog { void mk_rule_inliner::plan_inlining(rule_set const & orig) { count_pred_occurrences(orig); - + scoped_ptr candidate_inlined_set = create_allowed_rule_set(orig); while (forbid_preds_from_cycles(*candidate_inlined_set)) { candidate_inlined_set = create_allowed_rule_set(orig); @@ -458,8 +458,8 @@ namespace datalog { rule_ref r(rl, m_rm); func_decl * pred = r->get_decl(); - // if inlining is allowed, then we are eliminating - // this relation through inlining, + // if inlining is allowed, then we are eliminating + // this relation through inlining, // so we don't add its rules to the result something_done |= !inlining_allowed(orig, pred) && transform_rule(orig, r, tgt); @@ -472,14 +472,14 @@ namespace datalog { } } } - + return something_done; } /** Check whether rule r is oriented in a particular ordering. This is to avoid infinite cycle of inlining in the eager inliner. - + Out ordering is lexicographic, comparing atoms first on stratum they are in, then on arity and then on ast ID of their func_decl. */ @@ -488,7 +488,7 @@ namespace datalog { unsigned head_strat = strat.get_predicate_strat(head_pred); unsigned head_arity = head_pred->get_arity(); unsigned pt_len = r->get_positive_tail_size(); - for (unsigned ti=0; ti < pt_len; ++ti) { + for (unsigned ti=0; ti < pt_len; ++ti) { func_decl * pred = r->get_decl(ti); unsigned pred_strat = strat.get_predicate_strat(pred); SASSERT(pred_strat <= head_strat); @@ -516,7 +516,7 @@ namespace datalog { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; ti < pt_len; ++ti) { - + func_decl * pred = r->get_decl(ti); if (pred == head_pred || m_preds_with_facts.contains(pred)) { continue; } @@ -532,7 +532,7 @@ namespace datalog { } else { inlining_candidate = nullptr; - + for (unsigned ri = 0; ri < rule_cnt; ++ri) { rule * pred_rule = pred_rules[ri]; if (!m_unifier.unify_rules(*r, ti, *pred_rule)) { @@ -540,9 +540,9 @@ namespace datalog { continue; } if (inlining_candidate != nullptr) { - // We have two rules that can be inlined into the current + // We have two rules that can be inlined into the current // tail predicate. In this situation we don't do inlinning - // on this tail atom, as we don't want the overall number + // on this tail atom, as we don't want the overall number // of rules to increase. goto process_next_tail; } @@ -608,14 +608,14 @@ namespace datalog { P(1,x) :- P(1,z), phi(x,y), psi(y,z) - whenever P(0,x) is not unifiable with the + whenever P(0,x) is not unifiable with the body of the rule where it appears (P(1,z)) - and P(0,x) is unifiable with at most one (?) + and P(0,x) is unifiable with at most one (?) other rule (and it does not occur negatively). */ bool mk_rule_inliner::visitor::operator()(expr* e) { m_unifiers.append(m_positions.find(e)); - TRACE("dl", + TRACE("dl", tout << "unifier: " << (m_unifiers.empty()?0:m_unifiers.back()); tout << " num unifiers: " << m_unifiers.size(); tout << " num positions: " << m_positions.find(e).size() << "\n"; @@ -640,7 +640,7 @@ namespace datalog { } unsigned_vector const& mk_rule_inliner::visitor::del_position(expr* e, unsigned j) { - obj_map::obj_map_entry * et = m_positions.find_core(e); + obj_map::obj_map_entry * et = m_positions.find_core(e); SASSERT(et && et->get_data().m_value.contains(j)); et->get_data().m_value.erase(j); return et->get_data().m_value; @@ -654,7 +654,7 @@ namespace datalog { m_head_visitor.add_position(head, i); m_head_index.insert(head); m_pinned.push_back(r); - + if (source.is_output_predicate(headd) || m_preds_with_facts.contains(headd)) { can_remove.set(i, false); @@ -667,10 +667,10 @@ namespace datalog { m_tail_visitor.add_position(tail, i); m_tail_index.insert(tail); } - bool can_exp = + bool can_exp = tl_sz == 1 - && r->get_positive_tail_size() == 1 - && !m_preds_with_facts.contains(r->get_decl(0)) + && r->get_positive_tail_size() == 1 + && !m_preds_with_facts.contains(r->get_decl(0)) && !source.is_output_predicate(r->get_decl(0)); can_expand.set(i, can_exp); } @@ -682,14 +682,14 @@ namespace datalog { for (unsigned j = 0; j < tl_sz; ++j) { app* tail = r->get_tail(j); m_tail_visitor.del_position(tail, i); - } + } } #define PRT(_x_) ((_x_)?"T":"F") bool mk_rule_inliner::inline_linear(scoped_ptr& rules) { - bool done_something = false; + bool done_something = false; unsigned sz = rules->get_num_rules(); m_head_visitor.reset(sz); @@ -704,7 +704,7 @@ namespace datalog { acc.push_back(rules->get_rule(i)); } - // set up unification index. + // set up unification index. svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); @@ -729,7 +729,7 @@ namespace datalog { svector valid; valid.reset(); - valid.resize(sz, true); + valid.resize(sz, true); bool allow_branching = m_context.get_params().xform_inline_linear_branch(); @@ -738,9 +738,9 @@ namespace datalog { while (true) { rule_ref r(acc[i].get(), m_rm); - + TRACE("dl", r->display(m_context, tout << "processing: " << i << "\n");); - + if (!valid.get(i)) { TRACE("dl", tout << "invalid: " << i << "\n";); break; @@ -762,9 +762,9 @@ namespace datalog { TRACE("dl", tout << PRT(can_remove.get(j)) << " " << PRT(valid.get(j)) << " " << PRT(i != j) << "\n";); break; } - + rule* r2 = acc[j].get(); - + // check that the head of r2 only unifies with this single body position. TRACE("dl", output_predicate(m_context, r2->get_head(), tout << "unify head: "); tout << "\n";); m_tail_visitor.reset(); @@ -776,7 +776,7 @@ namespace datalog { TRACE("dl", tout << "too many tails " << num_tail_unifiers << "\n";); break; } - + rule_ref rl_res(m_rm); if (!try_to_inline_rule(*r.get(), *r2, 0, rl_res)) { TRACE("dl", r->display(m_context, tout << "inlining failed\n"); r2->display(m_context, tout); ); @@ -787,12 +787,12 @@ namespace datalog { del_rule(r, i); add_rule(*rules, rl_res.get(), i); - + r = rl_res; acc[i] = r.get(); can_expand.set(i, can_expand.get(j)); - + if (num_tail_unifiers == 1) { TRACE("dl", tout << "setting invalid: " << j << "\n";); valid.set(j, false); @@ -815,22 +815,22 @@ namespace datalog { res->inherit_predicates(*rules); TRACE("dl", res->display(tout);); rules = res.detach(); - } + } return done_something; } rule_set * mk_rule_inliner::operator()(rule_set const & source) { bool something_done = false; - ref hsmc; + ref hsmc; if (source.get_num_rules() == 0) { return nullptr; } - for (rule const* r : source) - if (has_quantifier(*r)) - return nullptr; + for (rule const* r : source) + if (has_quantifier(*r)) + return nullptr; if (m_context.get_model_converter()) { hsmc = alloc(horn_subsume_model_converter, m); @@ -841,15 +841,15 @@ namespace datalog { if (m_context.get_params().xform_inline_eager()) { TRACE("dl", source.display(tout << "before eager inlining\n");); - plan_inlining(source); - something_done = transform_rules(source, *res); + plan_inlining(source); + something_done = transform_rules(source, *res); VERIFY(res->close()); //this transformation doesn't break the negation stratification // try eager inlining if (do_eager_inlining(res)) { something_done = true; - } + } TRACE("dl", res->display(tout << "after eager inlining\n");); - } + } if (something_done) { res->inherit_predicates(source); } @@ -870,6 +870,5 @@ namespace datalog { return res.detach(); } - -}; +}; diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index 0144948bd..9ceaeeab3 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -18,7 +18,7 @@ Revision History: #include "muz/transforms/dl_mk_scale.h" #include "muz/base/dl_context.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { @@ -37,7 +37,7 @@ namespace datalog { m_trail.push_back(new_f); m_new2old.insert(new_f, old_f); } - + void get_units(obj_map& units) override { units.reset(); } void operator()(model_ref& md) override { @@ -74,7 +74,7 @@ namespace datalog { old_model->register_decl(old_p, old_fi); } } - + // register values that have not been scaled. unsigned sz = md->get_num_constants(); for (unsigned i = 0; i < sz; ++i) { @@ -111,12 +111,12 @@ namespace datalog { m_ctx(ctx), a(m), m_trail(m), - m_eqs(m) { + m_eqs(m) { } - mk_scale::~mk_scale() { + mk_scale::~mk_scale() { } - + rule_set * mk_scale::operator()(rule_set const & source) { if (!m_ctx.scale()) { return nullptr; @@ -135,7 +135,7 @@ namespace datalog { } m_mc = smc.get(); - for (unsigned i = 0; i < sz; ++i) { + for (unsigned i = 0; i < sz; ++i) { rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); @@ -157,10 +157,10 @@ namespace datalog { tail.push_back(a.mk_gt(m.mk_var(num_vars, a.mk_real()), a.mk_numeral(rational(0), false))); neg.resize(tail.size(), false); new_rule = rm.mk(new_pred, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); - result->add_rule(new_rule); + result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); - } + } } TRACE("dl", result->display(tout);); if (m_mc) { @@ -227,7 +227,7 @@ namespace datalog { a.is_lt(e) || a.is_gt(e)) { expr_ref_vector args(m); for (unsigned i = 0; i < ap->get_num_args(); ++i) { - args.push_back(linearize(sigma_idx, ap->get_arg(i))); + args.push_back(linearize(sigma_idx, ap->get_arg(i))); } result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); } diff --git a/src/muz/transforms/dl_mk_subsumption_checker.cpp b/src/muz/transforms/dl_mk_subsumption_checker.cpp index 56883460a..da41b4ba4 100644 --- a/src/muz/transforms/dl_mk_subsumption_checker.cpp +++ b/src/muz/transforms/dl_mk_subsumption_checker.cpp @@ -24,7 +24,7 @@ Revision History: #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_subsumption_checker.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "tactic/generic_model_converter.h" @@ -39,8 +39,8 @@ namespace datalog { bool mk_subsumption_checker::is_total_rule(const rule * r) { - if (r->get_tail_size() != 0) { - return false; + if (r->get_tail_size() != 0) { + return false; } unsigned pt_len = r->get_positive_tail_size(); @@ -113,7 +113,7 @@ namespace datalog { } - bool mk_subsumption_checker::transform_rule(rule * r, + bool mk_subsumption_checker::transform_rule(rule * r, rule_subsumption_index& subs_index, rule_ref & res) { unsigned u_len = r->get_uninterpreted_tail_size(); @@ -133,7 +133,7 @@ namespace datalog { if(m_total_relations.contains(tail_atom->get_decl()) || subs_index.is_subsumed(tail_atom)) { if(neg) { - //rule contains negated total relation, this means that it is unsatisfiable + //rule contains negated total relation, this means that it is unsatisfiable //and can be removed return false; } @@ -143,8 +143,8 @@ namespace datalog { } } if(!neg && head.get()==tail_atom) { - //rule contains its head positively in the tail, therefore - //it will never add any new facts to the relation, so it + //rule contains its head positively in the tail, therefore + //it will never add any new facts to the relation, so it //can be removed return false; } @@ -197,9 +197,9 @@ namespace datalog { if (m_total_relations.contains(head_pred)) { if (!orig.is_output_predicate(head_pred) || total_relations_with_included_rules.contains(head_pred)) { - //We just skip definitions of total non-output relations as + //We just skip definitions of total non-output relations as //we'll eliminate them from the problem. - //We also skip rules of total output relations for which we have + //We also skip rules of total output relations for which we have //already output the rule which implies their totality. modified = true; continue; @@ -286,7 +286,7 @@ namespace datalog { obj_hashtable * head_store; if(m_ground_unconditional_rule_heads.find(pred, head_store)) { //Some relations may receive facts by ground unconditioned rules. - //We scanned for those earlier, so now we check whether we cannot get a + //We scanned for those earlier, so now we check whether we cannot get a //better estimate of relation size from these. unsigned gnd_rule_cnt = head_store->size(); @@ -334,7 +334,7 @@ namespace datalog { rule_set * mk_subsumption_checker::operator()(rule_set const & source) { // TODO mc - if (!m_context.get_params ().xform_subsumption_checker()) + if (!m_context.get_params ().xform_subsumption_checker()) return nullptr; m_have_new_total_rule = false; @@ -366,6 +366,5 @@ namespace datalog { return res; } - -}; +}; diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp index d456d95f9..d4b9506e8 100644 --- a/src/muz/transforms/dl_transforms.cpp +++ b/src/muz/transforms/dl_transforms.cpp @@ -35,7 +35,7 @@ Revision History: #include "muz/transforms/dl_mk_scale.h" #include "muz/transforms/dl_mk_array_eq_rewrite.h" #include "muz/transforms/dl_mk_array_instantiation.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { @@ -72,7 +72,7 @@ namespace datalog { transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34970)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34960)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34950)); - + if (ctx.get_params().datalog_subsumption()) { transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34940)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930)); From b62d73f20958fe295d2ec728dd10e2d19218d0c9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 13 Jun 2018 07:55:45 -0700 Subject: [PATCH 306/364] first round for combined mbi Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 95 ++++++++++++------ src/math/simplex/model_based_opt.h | 6 +- src/qe/qe_arith.cpp | 53 ++++++---- src/qe/qe_arith.h | 6 ++ src/qe/qe_mbi.cpp | 139 ++++++++++++++++++++++++++- src/qe/qe_mbi.h | 30 +++--- src/test/model_based_opt.cpp | 32 ++++++ 7 files changed, 297 insertions(+), 64 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 2b175506f..56d51648d 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -36,24 +36,22 @@ std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { namespace opt { /** - * Convert a row ax + coeffs + coeff = 0 into a definition for x - * x = -(coeffs + coeff)/a - * For rows ax + coeffs + coeff < 0 convert into - * x = -(coeffs + coeff - a)/a + * Convert a row ax + coeffs + coeff = value into a definition for x + * x = (value - coeffs - coeff)/a + * as backdrop we have existing assignments to x and other variables that + * satisfy the equality with value, and such that value satisfies + * the row constraint ( = , <= , < , mod) */ model_based_opt::def::def(row const& r, unsigned x) { - rational c = r.get_coefficient(x); - bool is_pos = c.is_pos(); for (var const & v : r.m_vars) { if (v.m_id != x) { m_vars.push_back(v); - if (is_pos) m_vars.back().m_coeff.neg(); + } + else { + m_div = -v.m_coeff; } } - m_coeff = r.m_coeff; - if (is_pos) m_coeff.neg(); - if (r.m_type == opt::t_lt) m_coeff += abs(c); - m_div = abs(c); + m_coeff = r.m_coeff - r.m_value; normalize(); SASSERT(m_div.is_pos()); } @@ -129,6 +127,9 @@ namespace opt { g = gcd(g, abs(v.m_coeff)); if (g.is_one()) break; } + if (m_div.is_neg()) { + g.neg(); + } if (!g.is_one()) { for (var& v : m_vars) { v.m_coeff /= g; @@ -138,7 +139,6 @@ namespace opt { } } - model_based_opt::model_based_opt() { m_rows.push_back(row()); } @@ -163,7 +163,7 @@ namespace opt { PASSERT(index == 0 || m_var2row_ids[vars[i].m_id].contains(index)); } - PASSERT(r.m_value == get_row_value(r)); + PASSERT(r.m_value == eval(r)); PASSERT(r.m_type != t_eq || r.m_value.is_zero()); // values satisfy constraints PASSERT(index == 0 || r.m_type != t_lt || r.m_value.is_neg()); @@ -317,7 +317,7 @@ namespace opt { << " eps: " << eps << " ", r); ); m_var2value[x] = new_x_val; - r.m_value = get_row_value(r); + r.m_value = eval(r); SASSERT(invariant(bound_trail[i], r)); } @@ -327,7 +327,7 @@ namespace opt { unsigned_vector const& row_ids = m_var2row_ids[x]; for (unsigned row_id : row_ids) { row & r = m_rows[row_id]; - r.m_value = get_row_value(r); + r.m_value = eval(r); SASSERT(invariant(row_id, r)); } } @@ -385,19 +385,32 @@ namespace opt { m_rows[row_id].m_alive = false; m_retired_rows.push_back(row_id); } + + rational model_based_opt::eval(unsigned x) const { + return m_var2value[x]; + } + + rational model_based_opt::eval(def const& d) const { + vector const& vars = d.m_vars; + rational val = d.m_coeff; + for (var const& v : vars) { + val += v.m_coeff * eval(v.m_id); + } + val /= d.m_div; + return val; + } - rational model_based_opt::get_row_value(row const& r) const { + rational model_based_opt::eval(row const& r) const { vector const& vars = r.m_vars; rational val = r.m_coeff; for (var const& v : vars) { - val += v.m_coeff * m_var2value[v.m_id]; + val += v.m_coeff * eval(v.m_id); } return val; } rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) const { - row const& r = m_rows[row_id]; - return r.get_coefficient(var_id); + return m_rows[row_id].get_coefficient(var_id); } rational model_based_opt::row::get_coefficient(unsigned var_id) const { @@ -639,7 +652,7 @@ namespace opt { row& r1 = m_rows[row_id1]; row const& r2 = m_rows[row_id2]; unsigned i = 0, j = 0; - for(; i < r1.m_vars.size() || j < r2.m_vars.size(); ) { + while (i < r1.m_vars.size() || j < r2.m_vars.size()) { if (j == r2.m_vars.size()) { m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.c_ptr() + i); break; @@ -707,11 +720,18 @@ namespace opt { } void model_based_opt::display(std::ostream& out, vector const& vars, rational const& coeff) { - for (unsigned i = 0; i < vars.size(); ++i) { - if (i > 0 && vars[i].m_coeff.is_pos()) { + unsigned i = 0; + for (var const& v : vars) { + if (i > 0 && v.m_coeff.is_pos()) { out << "+ "; } - out << vars[i].m_coeff << "* v" << vars[i].m_id << " "; + ++i; + if (v.m_coeff.is_one()) { + out << "v" << v.m_id << " "; + } + else { + out << v.m_coeff << "*v" << v.m_id << " "; + } } if (coeff.is_pos()) { out << " + " << coeff << " "; @@ -921,7 +941,7 @@ namespace opt { return solve_for(eq_row, x, compute_def); } - def result; + def result; unsigned lub_size = lub_rows.size(); unsigned glb_size = glb_rows.size(); unsigned row_index = (lub_size <= glb_size) ? lub_index : glb_index; @@ -935,6 +955,11 @@ namespace opt { else if (glb_index != UINT_MAX) { result = solve_for(glb_index, x, true); } + else { + result = def(); + result.m_coeff = eval(x); + } + SASSERT(eval(result) == eval(x)); } else { for (unsigned row_id : lub_rows) retire_row(row_id); @@ -946,9 +971,19 @@ namespace opt { SASSERT(lub_index != UINT_MAX); SASSERT(glb_index != UINT_MAX); if (compute_def) { +#if 0 def d1(m_rows[lub_index], x); - def d2(m_rows[lub_index], x); + def d2(m_rows[glb_index], x); result = (d1 + d2)/2; +#else + if (lub_size <= glb_size) { + result = def(m_rows[lub_index], x); + } + else { + result = def(m_rows[glb_index], x); + } +#endif + SASSERT(eval(result) == eval(x)); } // The number of matching lower and upper bounds is small. @@ -1045,8 +1080,9 @@ namespace opt { } def result = project(y, compute_def); if (compute_def) { - result = (result * D) + u; + result = (result * D) + u; } + SASSERT(!compute_def || eval(result) == eval(x)); return result; } @@ -1139,8 +1175,11 @@ namespace opt { } } } - // TBD: -t div a - def result(m_rows[row_id1], x); + def result; + if (compute_def) { + result = def(m_rows[row_id1], x); + SASSERT(eval(result) == eval(x)); + } retire_row(row_id1); return result; } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 268d3d81d..a1137d2fa 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -100,7 +100,11 @@ namespace opt { rational get_coefficient(unsigned row_id, unsigned var_id) const; - rational get_row_value(row const& r) const; + rational eval(row const& r) const; + + rational eval(unsigned x) const; + + rational eval(def const& d) const; void resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 0b5ad72ca..1709711d7 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -37,6 +37,7 @@ namespace qe { ast_manager& m; arith_util a; + bool m_check_purified; // check that variables are properly pure void insert_mul(expr* x, rational const& v, obj_map& ts) { TRACE("qe", tout << "Adding variable " << mk_pp(x, m) << " " << v << "\n";); @@ -264,7 +265,7 @@ namespace qe { } imp(ast_manager& m): - m(m), a(m) {} + m(m), a(m), m_check_purified(true) {} ~imp() {} @@ -347,17 +348,23 @@ namespace qe { tids.insert(v, mbo.add_var(r, a.is_int(v))); } } - for (expr* fml : fmls) { - mark_rec(fmls_mark, fml); + if (m_check_purified) { + for (expr* fml : fmls) { + mark_rec(fmls_mark, fml); + } + for (auto& kv : tids) { + expr* e = kv.m_key; + if (!var_mark.is_marked(e)) { + mark_rec(fmls_mark, e); + } + } } + ptr_vector index2expr; for (auto& kv : tids) { - expr* e = kv.m_key; - if (!var_mark.is_marked(e)) { - mark_rec(fmls_mark, e); - } - index2expr.setx(kv.m_value, e, 0); + index2expr.setx(kv.m_value, kv.m_key, 0); } + j = 0; unsigned_vector real_vars; for (app* v : vars) { @@ -369,6 +376,7 @@ namespace qe { } } vars.shrink(j); + TRACE("qe", tout << "remaining vars: " << vars << "\n"; for (unsigned v : real_vars) { tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n"; @@ -379,10 +387,9 @@ namespace qe { vector rows; mbo.get_live_rows(rows); - for (unsigned i = 0; i < rows.size(); ++i) { + for (row const& r : rows) { expr_ref_vector ts(m); expr_ref t(m), s(m), val(m); - row const& r = rows[i]; if (r.m_vars.size() == 0) { continue; } @@ -411,25 +418,19 @@ namespace qe { } ts.push_back(t); } + t = mk_add(ts); s = a.mk_numeral(-r.m_coeff, a.is_int(t)); - if (ts.size() == 1) { - t = ts[0].get(); - } - else { - t = a.mk_add(ts.size(), ts.c_ptr()); - } switch (r.m_type) { case opt::t_lt: t = a.mk_lt(t, s); break; case opt::t_le: t = a.mk_le(t, s); break; case opt::t_eq: t = a.mk_eq(t, s); break; - case opt::t_mod: { + case opt::t_mod: if (!r.m_coeff.is_zero()) { t = a.mk_sub(t, s); } t = a.mk_eq(a.mk_mod(t, a.mk_numeral(r.m_mod, true)), a.mk_int(0)); break; } - } fmls.push_back(t); val = eval(t); CTRACE("qe", !m.is_true(val), tout << "Evaluated " << t << " to " << val << "\n";); @@ -447,19 +448,29 @@ namespace qe { ts.push_back(var2expr(index2expr, v)); } ts.push_back(a.mk_numeral(d.m_coeff, is_int)); - t = a.mk_add(ts.size(), ts.c_ptr()); + t = mk_add(ts); if (!d.m_div.is_one() && is_int) { t = a.mk_idiv(t, a.mk_numeral(d.m_div, is_int)); } else if (!d.m_div.is_one() && !is_int) { t = a.mk_div(t, a.mk_numeral(d.m_div, is_int)); } + SASSERT(eval(t) == eval(x)); result.push_back(def(expr_ref(x, m), t)); } } return result; } + expr_ref mk_add(expr_ref_vector const& ts) { + if (ts.size() == 1) { + return expr_ref(ts.get(0), m); + } + else { + return expr_ref(a.mk_add(ts.size(), ts.c_ptr()), m); + } + } + opt::inf_eps maximize(expr_ref_vector const& fmls0, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { SASSERT(a.is_real(t)); expr_ref_vector fmls(fmls0); @@ -577,6 +588,10 @@ namespace qe { return m_imp->project(model, vars, lits); } + void arith_project_plugin::set_check_purified(bool check_purified) { + m_imp->m_check_purified = check_purified; + } + bool arith_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return m_imp->solve(model, vars, lits); } diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index c01d7bbb6..b55e63fcf 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -33,6 +33,12 @@ namespace qe { vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); + + /** + * \brief check if formulas are purified, or leave it to caller to ensure that + * arithmetic variables nested under foreign functions are handled properly. + */ + void set_check_purified(bool check_purified); }; bool arith_project(model& model, app* var, expr_ref_vector& lits); diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index ae989ba02..20eaf9675 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -77,10 +77,12 @@ Notes: #include "ast/ast_util.h" #include "ast/for_each_expr.h" #include "ast/rewriter/bool_rewriter.h" +#include "ast/arith_decl_plugin.h" #include "model/model_evaluator.h" #include "solver/solver.h" #include "qe/qe_mbi.h" #include "qe/qe_term_graph.h" +#include "qe/qe_arith.h" namespace qe { @@ -141,7 +143,7 @@ namespace qe { } // ------------------------------- - // euf_mbi, TBD + // euf_mbi struct euf_mbi_plugin::is_atom_proc { ast_manager& m; @@ -235,6 +237,140 @@ namespace qe { } + // ------------------------------- + // euf_arith_mbi + + struct euf_arith_mbi_plugin::is_atom_proc { + ast_manager& m; + expr_ref_vector& m_atoms; + is_atom_proc(expr_ref_vector& atoms): m(atoms.m()), m_atoms(atoms) {} + void operator()(app* a) { + if (m.is_eq(a)) { + m_atoms.push_back(a); + } + else if (m.is_bool(a) && a->get_family_id() != m.get_basic_family_id()) { + m_atoms.push_back(a); + } + } + void operator()(expr*) {} + }; + + struct euf_arith_mbi_plugin::is_arith_var_proc { + ast_manager& m; + app_ref_vector& m_vars; + arith_util arith; + obj_hashtable m_exclude; + is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& excl): + m(vars.m()), m_vars(vars), arith(m) { + for (func_decl* f : excl) m_exclude.insert(f); + } + void operator()(app* a) { + if (arith.is_int_real(a) && a->get_family_id() != a->get_family_id() && !m_exclude.contains(a->get_decl())) { + m_vars.push_back(a); + } + } + void operator()(expr*) {} + + }; + + euf_arith_mbi_plugin::euf_arith_mbi_plugin(solver* s, solver* sNot): + m(s->get_manager()), + m_atoms(m), + m_solver(s), + m_dual_solver(sNot) { + params_ref p; + p.set_bool("core.minimize", true); + m_solver->updt_params(p); + m_dual_solver->updt_params(p); + expr_ref_vector fmls(m); + m_solver->get_assertions(fmls); + expr_fast_mark1 marks; + is_atom_proc proc(m_atoms); + for (expr* e : fmls) { + quick_for_each_expr(proc, marks, e); + } + } + + mbi_result euf_arith_mbi_plugin::operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { + lbool r = m_solver->check_sat(lits); + + // populate set of arithmetic variables to be projected. + app_ref_vector avars(m); + is_arith_var_proc _proc(avars, vars); + for (expr* l : lits) quick_for_each_expr(_proc, l); + switch (r) { + case l_false: + lits.reset(); + m_solver->get_unsat_core(lits); + // optionally minimize core using superposition. + return mbi_unsat; + case l_true: { + m_solver->get_model(mdl); + model_evaluator mev(*mdl.get()); + lits.reset(); + for (expr* e : m_atoms) { + if (mev.is_true(e)) { + lits.push_back(e); + } + else if (mev.is_false(e)) { + lits.push_back(m.mk_not(e)); + } + } + TRACE("qe", tout << "atoms from model: " << lits << "\n";); + r = m_dual_solver->check_sat(lits); + expr_ref_vector core(m); + term_graph tg(m); + switch (r) { + case l_false: { + // use the dual solver to find a 'small' implicant + m_dual_solver->get_unsat_core(core); + TRACE("qe", tout << "core: " << core << "\n";); + lits.reset(); + lits.append(core); + arith_util a(m); + + // 1. project arithmetic variables using mdl that satisfies core. + // ground any remaining arithmetic variables using model. + arith_project_plugin ap(m); + ap.set_check_purified(false); + + auto defs = ap.project(*mdl.get(), avars, lits); + // 2. Add the projected definitions to the remaining (euf) literals + for (auto const& def : defs) { + lits.push_back(m.mk_eq(def.var, def.term)); + } + + // 3. Project the remaining literals with respect to EUF. + tg.set_vars(vars, false); + tg.add_lits(lits); + lits.reset(); + lits.append(tg.project(*mdl)); + TRACE("qe", tout << "project: " << lits << "\n";); + return mbi_sat; + } + case l_undef: + return mbi_undef; + case l_true: + UNREACHABLE(); + return mbi_undef; + } + return mbi_sat; + } + default: + // TBD: if not running solver to completion, then: + // 1. extract unit literals from m_solver. + // 2. run a cc over the units + // 3. extract equalities or other assignments over the congruence classes + // 4. ensure that at least some progress is made over original lits. + return mbi_undef; + } + } + + void euf_arith_mbi_plugin::block(expr_ref_vector const& lits) { + m_solver->assert_expr(mk_not(mk_and(lits))); + } + + /** -------------------------------------------------------------- * ping-pong interpolation of Gurfinkel & Vizel * compute a binary interpolant. @@ -318,4 +454,5 @@ namespace qe { } } } + }; diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index 671099015..6d0d8dcf6 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -86,29 +86,29 @@ namespace qe { void block(expr_ref_vector const& lits) override; }; + class euf_arith_mbi_plugin : mbi_plugin { + ast_manager& m; + expr_ref_vector m_atoms; + solver_ref m_solver; + solver_ref m_dual_solver; + struct is_atom_proc; + struct is_arith_var_proc; + public: + euf_arith_mbi_plugin(solver* s, solver* sNot); + ~euf_arith_mbi_plugin() override {} + mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; + void block(expr_ref_vector const& lits) override; + }; + /** * use cases for interpolation. */ class interpolator { - ast_manager& m; + ast_manager& m; public: interpolator(ast_manager& m):m(m) {} lbool pingpong(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); lbool pogo(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); }; -#if 0 - TBD some other scenario, e.g., Farkas, Cute/Beautiful/maybe even Selfless - - class solver_mbi_plugin : public mbi_plugin { - solver_ref m_solver; - public: - solver_mbi_plugin(solver* s); - ~solver_mbi_plugin() override {} - mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; - void block(expr_ref_vector const& lits) override; - }; - - -#endif }; diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp index f2f6004ff..1bae7435f 100644 --- a/src/test/model_based_opt.cpp +++ b/src/test/model_based_opt.cpp @@ -360,6 +360,37 @@ static void test10() { mbo.display(std::cout); } +static void test11() { + { + opt::model_based_opt mbo; + unsigned s = mbo.add_var(rational(2), true); + unsigned a = mbo.add_var(rational(1), true); + unsigned t = mbo.add_var(rational(3), true); + + add_ineq(mbo, s, 1, a, -2, 0, opt::t_le); // s - 2a <= 0 + add_ineq(mbo, a, 2, t, -1, 0, opt::t_le); // 2a - t <= 0 + + mbo.display(std::cout); + display_project(mbo.project(1, &a, true)); + mbo.display(std::cout); + } + + { + opt::model_based_opt mbo; + unsigned s = mbo.add_var(rational(3), true); + unsigned a = mbo.add_var(rational(2), true); + unsigned t = mbo.add_var(rational(4), true); + + add_ineq(mbo, s, 1, a, -2, 0, opt::t_le); // s - 2a <= 0 + add_ineq(mbo, a, 2, t, -1, 0, opt::t_le); // 2a - t <= 0 + + mbo.display(std::cout); + display_project(mbo.project(1, &a, true)); + mbo.display(std::cout); + } + +} + // test with mix of upper and lower bounds void tst_model_based_opt() { @@ -374,4 +405,5 @@ void tst_model_based_opt() { test7(); test8(); test9(); + test11(); } From 9ba76a13326641dff18e52343617e852a278edb8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 13 Jun 2018 14:43:38 -0700 Subject: [PATCH 307/364] fixing eufi Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/dbg_cmds.cpp | 12 ++++-- src/qe/qe_arith.cpp | 16 ++------ src/qe/qe_mbi.cpp | 50 +++++++++++++------------ src/qe/qe_mbi.h | 31 ++++++++++----- src/qe/qe_term_graph.cpp | 6 ++- 5 files changed, 64 insertions(+), 51 deletions(-) diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index d45cb9df9..64eeac42e 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -435,7 +435,9 @@ public: sB->assert_expr(b); qe::prop_mbi_plugin pA(sA.get()); qe::prop_mbi_plugin pB(sB.get()); - lbool res = mbi.pingpong(pA, pB, vars, itp); + pA.set_shared(vars); + pB.set_shared(vars); + lbool res = mbi.pingpong(pA, pB, itp); ctx.regular_stream() << res << " " << itp << "\n"; } }; @@ -485,9 +487,11 @@ public: sNotA->assert_expr(m.mk_not(a)); sB->assert_expr(b); sNotB->assert_expr(m.mk_not(b)); - qe::euf_mbi_plugin pA(sA.get(), sNotA.get()); - qe::euf_mbi_plugin pB(sB.get(), sNotB.get()); - lbool res = mbi.pogo(pA, pB, vars, itp); + qe::euf_arith_mbi_plugin pA(sA.get(), sNotA.get()); + qe::euf_arith_mbi_plugin pB(sB.get(), sNotB.get()); + pA.set_shared(vars); + pB.set_shared(vars); + lbool res = mbi.pogo(pA, pB, itp); ctx.regular_stream() << res << " " << itp << "\n"; } }; diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 1709711d7..cd11d4635 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -257,7 +257,7 @@ namespace qe { }; bool is_arith(expr* e) { - return a.is_int(e) || a.is_real(e); + return a.is_int_real(e); } rational n_sign(rational const& b) { @@ -276,7 +276,7 @@ namespace qe { bool operator()(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) { app_ref_vector vs(m); vs.push_back(v); - (*this)(model, vs, lits); + project(model, vs, lits, false); return vs.empty(); } @@ -284,14 +284,6 @@ namespace qe { typedef opt::model_based_opt::row row; typedef vector vars; - vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - return project(model, vars, lits, true); - } - - void operator()(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { - project(model, vars, fmls, false); - } - expr_ref var2expr(ptr_vector const& index2expr, var const& v) { expr_ref t(index2expr[v.m_id], m); if (!v.m_coeff.is_one()) { @@ -581,11 +573,11 @@ namespace qe { } void arith_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - (*m_imp)(model, vars, lits); + m_imp->project(model, vars, lits, false); } vector arith_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - return m_imp->project(model, vars, lits); + return m_imp->project(model, vars, lits, true); } void arith_project_plugin::set_check_purified(bool check_purified) { diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 20eaf9675..e3bd5e17f 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -87,9 +87,9 @@ Notes: namespace qe { - lbool mbi_plugin::check(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { + lbool mbi_plugin::check(expr_ref_vector& lits, model_ref& mdl) { while (true) { - switch ((*this)(vars, lits, mdl)) { + switch ((*this)(lits, mdl)) { case mbi_sat: return l_true; case mbi_unsat: @@ -106,12 +106,11 @@ namespace qe { // ------------------------------- // prop_mbi - prop_mbi_plugin::prop_mbi_plugin(solver* s): m_solver(s) {} + prop_mbi_plugin::prop_mbi_plugin(solver* s): mbi_plugin(s->get_manager()), m_solver(s) {} // sketch of propositional - mbi_result prop_mbi_plugin::operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { - ast_manager& m = lits.m(); + mbi_result prop_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { lbool r = m_solver->check_sat(lits); switch (r) { case l_false: @@ -123,7 +122,7 @@ namespace qe { lits.reset(); for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { func_decl* c = mdl->get_constant(i); - if (vars.contains(c)) { + if (m_shared.contains(c)) { if (m.is_true(mdl->get_const_interp(c))) { lits.push_back(m.mk_const(c)); } @@ -161,7 +160,7 @@ namespace qe { }; euf_mbi_plugin::euf_mbi_plugin(solver* s, solver* sNot): - m(s->get_manager()), + mbi_plugin(s->get_manager()), m_atoms(m), m_solver(s), m_dual_solver(sNot) { @@ -178,7 +177,7 @@ namespace qe { } } - mbi_result euf_mbi_plugin::operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { + mbi_result euf_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { lbool r = m_solver->check_sat(lits); switch (r) { case l_false: @@ -208,7 +207,7 @@ namespace qe { m_dual_solver->get_unsat_core(core); TRACE("qe", tout << "core: " << core << "\n";); // project the implicant onto vars - tg.set_vars(vars, false); + tg.set_vars(m_shared, false); tg.add_lits(core); lits.reset(); lits.append(tg.project(*mdl)); @@ -260,12 +259,13 @@ namespace qe { app_ref_vector& m_vars; arith_util arith; obj_hashtable m_exclude; - is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& excl): + is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): m(vars.m()), m_vars(vars), arith(m) { - for (func_decl* f : excl) m_exclude.insert(f); + for (func_decl* f : shared) m_exclude.insert(f); } void operator()(app* a) { - if (arith.is_int_real(a) && a->get_family_id() != a->get_family_id() && !m_exclude.contains(a->get_decl())) { + TRACE("qe", tout << expr_ref(a, m) << " " << arith.is_int_real(a) << " " << a->get_family_id() << "\n";); + if (arith.is_int_real(a) && a->get_family_id() != arith.get_family_id() && !m_exclude.contains(a->get_decl())) { m_vars.push_back(a); } } @@ -274,7 +274,7 @@ namespace qe { }; euf_arith_mbi_plugin::euf_arith_mbi_plugin(solver* s, solver* sNot): - m(s->get_manager()), + mbi_plugin(s->get_manager()), m_atoms(m), m_solver(s), m_dual_solver(sNot) { @@ -291,13 +291,9 @@ namespace qe { } } - mbi_result euf_arith_mbi_plugin::operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) { + mbi_result euf_arith_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { lbool r = m_solver->check_sat(lits); - // populate set of arithmetic variables to be projected. - app_ref_vector avars(m); - is_arith_var_proc _proc(avars, vars); - for (expr* l : lits) quick_for_each_expr(_proc, l); switch (r) { case l_false: lits.reset(); @@ -328,6 +324,11 @@ namespace qe { lits.reset(); lits.append(core); arith_util a(m); + // populate set of arithmetic variables to be projected. + app_ref_vector avars(m); + is_arith_var_proc _proc(avars, m_shared); + for (expr* l : lits) quick_for_each_expr(_proc, l); + TRACE("qe", tout << "vars: " << avars << " lits: " << lits << "\n";); // 1. project arithmetic variables using mdl that satisfies core. // ground any remaining arithmetic variables using model. @@ -339,9 +340,10 @@ namespace qe { for (auto const& def : defs) { lits.push_back(m.mk_eq(def.var, def.term)); } + TRACE("qe", tout << "# arith defs" << defs.size() << " avars: " << avars << " " << lits << "\n";); // 3. Project the remaining literals with respect to EUF. - tg.set_vars(vars, false); + tg.set_vars(m_shared, false); tg.add_lits(lits); lits.reset(); lits.append(tg.project(*mdl)); @@ -375,7 +377,7 @@ namespace qe { * ping-pong interpolation of Gurfinkel & Vizel * compute a binary interpolant. */ - lbool interpolator::pingpong(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { + lbool interpolator::pingpong(mbi_plugin& a, mbi_plugin& b, expr_ref& itp) { model_ref mdl; expr_ref_vector lits(m); bool turn = true; @@ -389,7 +391,7 @@ namespace qe { while (true) { auto* t1 = turn ? &a : &b; auto* t2 = turn ? &b : &a; - mbi_result next_res = (*t1)(vars, lits, mdl); + mbi_result next_res = (*t1)(lits, mdl); switch (next_res) { case mbi_sat: if (last_res == mbi_sat) { @@ -429,14 +431,14 @@ namespace qe { * One-sided pogo creates clausal interpolants. * It creates a set of consequences of b that are inconsistent with a. */ - lbool interpolator::pogo(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp) { + lbool interpolator::pogo(mbi_plugin& a, mbi_plugin& b, expr_ref& itp) { expr_ref_vector lits(m), itps(m); while (true) { model_ref mdl; lits.reset(); - switch (a.check(vars, lits, mdl)) { + switch (a.check(lits, mdl)) { case l_true: - switch (b.check(vars, lits, mdl)) { + switch (b.check(lits, mdl)) { case l_true: return l_true; case l_false: diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index 6d0d8dcf6..e0e2021d8 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -29,8 +29,21 @@ namespace qe { }; class mbi_plugin { + protected: + ast_manager& m; + func_decl_ref_vector m_shared; public: + mbi_plugin(ast_manager& m): m(m), m_shared(m) {} virtual ~mbi_plugin() {} + + /** + * Set the shared symbols. + */ + virtual void set_shared(func_decl_ref_vector const& vars) { + m_shared.reset(); + m_shared.append(vars); + } + /** * \brief Utility that works modulo a background state. * - vars @@ -50,7 +63,7 @@ namespace qe { * - mbi_undef: * inconclusive, */ - virtual mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) = 0; + virtual mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) = 0; /** * \brief Block conjunction of lits from future mbi_augment or mbi_sat. @@ -60,7 +73,7 @@ namespace qe { /** * \brief perform a full check, consume internal auguments if necessary. */ - lbool check(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl); + lbool check(expr_ref_vector& lits, model_ref& mdl); }; @@ -69,12 +82,11 @@ namespace qe { public: prop_mbi_plugin(solver* s); ~prop_mbi_plugin() override {} - mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; + mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; void block(expr_ref_vector const& lits) override; }; class euf_mbi_plugin : public mbi_plugin { - ast_manager& m; expr_ref_vector m_atoms; solver_ref m_solver; solver_ref m_dual_solver; @@ -82,12 +94,11 @@ namespace qe { public: euf_mbi_plugin(solver* s, solver* sNot); ~euf_mbi_plugin() override {} - mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; + mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; void block(expr_ref_vector const& lits) override; }; - class euf_arith_mbi_plugin : mbi_plugin { - ast_manager& m; + class euf_arith_mbi_plugin : public mbi_plugin { expr_ref_vector m_atoms; solver_ref m_solver; solver_ref m_dual_solver; @@ -96,7 +107,7 @@ namespace qe { public: euf_arith_mbi_plugin(solver* s, solver* sNot); ~euf_arith_mbi_plugin() override {} - mbi_result operator()(func_decl_ref_vector const& vars, expr_ref_vector& lits, model_ref& mdl) override; + mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; void block(expr_ref_vector const& lits) override; }; @@ -107,8 +118,8 @@ namespace qe { ast_manager& m; public: interpolator(ast_manager& m):m(m) {} - lbool pingpong(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); - lbool pogo(mbi_plugin& a, mbi_plugin& b, func_decl_ref_vector const& vars, expr_ref& itp); + lbool pingpong(mbi_plugin& a, mbi_plugin& b, expr_ref& itp); + lbool pogo(mbi_plugin& a, mbi_plugin& b, expr_ref& itp); }; }; diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 467a0cc48..ac68516c3 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -761,11 +761,15 @@ namespace qe { val2rep.insert(val, rep); } } + // TBD: this ignores types, need one use of 'distinct' per sort. + // TBD: probably ignore distinct on values + // TBD: ignore distinct on Booleans ptr_buffer reps; for (auto &kv : val2rep) { reps.push_back(kv.m_value); + std::cout << mk_pp(kv.m_value, m) << "\n"; } - res.push_back(m.mk_distinct(reps.size(), reps.c_ptr())); + // res.push_back(m.mk_distinct(reps.size(), reps.c_ptr())); } public: From f0e105612c7bc7f208a12609fa06c4ca476c811a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 13 Jun 2018 15:09:45 -0700 Subject: [PATCH 308/364] adding dbg Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index cd11d4635..e9653bf14 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -30,6 +30,7 @@ Revision History: #include "ast/rewriter/expr_safe_replace.h" #include "math/simplex/model_based_opt.h" #include "model/model_evaluator.h" +#include "model/model_smt2_pp.h" namespace qe { @@ -91,6 +92,8 @@ namespace qe { rational r1, r2; expr_ref val1 = eval(e1); expr_ref val2 = eval(e2); + TRACE("qe", tout << mk_pp(e1, m) << " " << val1 << "\n";); + TRACE("qe", tout << mk_pp(e2, m) << " " << val2 << "\n";); if (!a.is_numeral(val1, r1)) return false; if (!a.is_numeral(val2, r2)) return false; SASSERT(r1 != r2); @@ -108,6 +111,7 @@ namespace qe { vector > nums; for (expr* arg : *alit) { val = eval(arg); + TRACE("qe", tout << mk_pp(arg, m) << " " << val << "\n";); if (!a.is_numeral(val, r)) return false; nums.push_back(std::make_pair(arg, r)); } @@ -130,6 +134,7 @@ namespace qe { expr* arg1 = to_app(lit)->get_arg(i), *arg2 = nullptr; rational r; expr_ref val = eval(arg1); + TRACE("qe", tout << mk_pp(arg1, m) << " " << val << "\n";); if (!a.is_numeral(val, r)) return false; if (values.find(r, arg2)) { ty = opt::t_eq; @@ -301,6 +306,7 @@ namespace qe { return vector(); } model_evaluator eval(model); + TRACE("qe", model_smt2_pp(tout, m, model, 0);); // eval.set_model_completion(true); opt::model_based_opt mbo; @@ -311,10 +317,10 @@ namespace qe { for (unsigned i = 0; i < fmls.size(); ++i) { expr * fml = fmls.get(i); if (!linearize(mbo, eval, fml, fmls, tids)) { + TRACE("qe", tout << "could not linearize: " << mk_pp(fml, m) << "\n";); fmls[j++] = fml; } else { - TRACE("qe", tout << "could not linearize: " << mk_pp(fml, m) << "\n";); pinned.push_back(fml); } } From a56c9faedbe20cd296b09cfc8071e1b4c5066490 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Jun 2018 19:33:18 -0700 Subject: [PATCH 309/364] A sketch of vurtego --- src/qe/qe_mbi.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++-- src/qe/qe_mbi.h | 12 +++++-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index e3bd5e17f..57dfff941 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -259,7 +259,7 @@ namespace qe { app_ref_vector& m_vars; arith_util arith; obj_hashtable m_exclude; - is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): + is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): m(vars.m()), m_vars(vars), arith(m) { for (func_decl* f : shared) m_exclude.insert(f); } @@ -326,7 +326,7 @@ namespace qe { arith_util a(m); // populate set of arithmetic variables to be projected. app_ref_vector avars(m); - is_arith_var_proc _proc(avars, m_shared); + is_arith_var_proc _proc(avars, m_shared); for (expr* l : lits) quick_for_each_expr(_proc, l); TRACE("qe", tout << "vars: " << avars << " lits: " << lits << "\n";); @@ -457,4 +457,79 @@ namespace qe { } } + lbool interpolator::vurtego(mbi_plugin& a, mbi_plugin& b, expr_ref& itp, model_ref &mdl) { + /** + Assumptions on mbi_plugin() + Let local be assertions local to the plugin + Let blocked be clauses added by blocked, kept separately from local + mbi_plugin::check(lits, mdl, bool force_model): + if lits.empty() and mdl == nullptr then + if is_sat(local & blocked) then + return l_true, mbp of local, mdl of local & blocked + else + return l_false + else if !lits.empty() then + if is_sat(local & mdl & blocked) + return l_true, lits, extension of mdl to local + else if is_sat(local & lits & blocked) + if (force_model) then + return l_false, core of model, nullptr + else + return l_true, mbp of local, mdl of local & blocked + else if !is_sat(local & lits) then + return l_false, mbp of local, nullptr + else if is_sat(local & lits) && !is_sat(local & lits & blocked) + MISSING CASE. IS IT POSSIBLE? + MUST PRODUCE AN IMPLICANT OF LOCAL that is inconsistent with lits & blocked + + mbi_plugin::block(phi): add phi to blocked + + probably should use the operator() instead of check. + mbi_augment -- means consistent with lits but not with the mdl + mbi_sat -- means consistent with lits and mdl + + */ + expr_ref_vector lits(m), itps(m); + while (true) { + // when lits.empty(), this picks an A-implicant consistent with B + // when !lits.empty(), checks whether mdl of shared vocab extends to A + switch (a.check_ag(lits, mdl, !lits.empty())) { + case l_true: + if (!lits.empty()) + // mdl is a model for a && b + return l_true; + switch (b.check_ag(lits, mdl, false)) { + case l_true: + /* can return true if know that b did not change + the model. For now, cycle back to A. */ + SASSERT(!lits.empty()); + SASSERT(mdl); + break; + case l_false: + // Force a different A-implicant + a.block(lits); + lits.reset(); + mdl.reset(); + break; + case l_undef: + return l_undef; + } + case l_false: + if (lits.empty()) { + // no more A-implicants, terminate + itp = mk_and(itps); + return l_false; + } + // force B to pick a different model or a different implicant + b.block(lits); + itps.push_back(mk_not(mk_and(lits))); + lits.reset(); + mdl.reset(); + break; + case l_undef: + return l_undef; + } + } + } + }; diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index e0e2021d8..5e43a6412 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -39,9 +39,9 @@ namespace qe { /** * Set the shared symbols. */ - virtual void set_shared(func_decl_ref_vector const& vars) { - m_shared.reset(); - m_shared.append(vars); + virtual void set_shared(func_decl_ref_vector const& vars) { + m_shared.reset(); + m_shared.append(vars); } /** @@ -75,6 +75,11 @@ namespace qe { */ lbool check(expr_ref_vector& lits, model_ref& mdl); + virtual lbool check_ag(expr_ref_vector& lits, model_ref& mdl, bool force_model) { + return l_undef; + } + + }; class prop_mbi_plugin : public mbi_plugin { @@ -120,6 +125,7 @@ namespace qe { interpolator(ast_manager& m):m(m) {} lbool pingpong(mbi_plugin& a, mbi_plugin& b, expr_ref& itp); lbool pogo(mbi_plugin& a, mbi_plugin& b, expr_ref& itp); + lbool vurtego(mbi_plugin &a, mbi_plugin &b, expr_ref &itp, model_ref &mdl); }; }; From 732a8149d8b3662e6482f3070319c1de40120b12 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 13 Jun 2018 19:46:12 -0700 Subject: [PATCH 310/364] vurtego update --- src/qe/qe_mbi.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 57dfff941..6f2c346f0 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -479,8 +479,11 @@ namespace qe { else if !is_sat(local & lits) then return l_false, mbp of local, nullptr else if is_sat(local & lits) && !is_sat(local & lits & blocked) - MISSING CASE. IS IT POSSIBLE? + MISSING CASE MUST PRODUCE AN IMPLICANT OF LOCAL that is inconsistent with lits & blocked + in this case !is_sat(local & lits & mdl) so + return l_false, core of lits & mdl, nullptr + this will force a new mdl mbi_plugin::block(phi): add phi to blocked From 49279d70470d9acd5ab018a0058570835486b097 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 13 Jun 2018 21:17:35 -0700 Subject: [PATCH 311/364] debugging mbi Signed-off-by: Nikolaj Bjorner --- src/ast/for_each_expr.h | 14 ++++ src/math/simplex/model_based_opt.cpp | 36 ++++++++-- src/math/simplex/model_based_opt.h | 2 + src/qe/qe_arith.cpp | 13 ++-- src/qe/qe_mbi.cpp | 102 ++++++++++++++------------- src/qe/qe_mbi.h | 4 ++ 6 files changed, 112 insertions(+), 59 deletions(-) diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index 50a4089dc..6e203ccee 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -128,6 +128,20 @@ void for_each_expr(ForEachProc & proc, expr * n) { for_each_expr_core(proc, visited, n); } +template +void for_each_expr(ForEachProc & proc, unsigned n, expr * const* es) { + expr_mark visited; + for (unsigned i = 0; i < n; ++i) + for_each_expr_core(proc, visited, es[i]); +} + +template +void for_each_expr(ForEachProc & proc, expr_ref_vector const& es) { + expr_mark visited; + for (expr* e : es) + for_each_expr_core(proc, visited, e); +} + template void quick_for_each_expr(ForEachProc & proc, expr_fast_mark1 & visited, expr * n) { for_each_expr_core(proc, visited, n); diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 56d51648d..93e55bedd 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -523,18 +523,26 @@ namespace opt { rational slack = (abs_src_c - rational::one()) * (abs_dst_c - rational::one()); rational dst_val = dst.m_value - x_val*dst_c; rational src_val = src.m_value - x_val*src_c; - bool use_case1 = - (src_c * dst_val + dst_c * src_val + slack).is_nonpos() - || abs_src_c.is_one() - || abs_dst_c.is_one(); + rational distance = src_c * dst_val + dst_c * src_val + slack; + bool use_case1 = distance.is_nonpos() || abs_src_c.is_one() || abs_dst_c.is_one(); + + if (distance.is_nonpos() && !abs_src_c.is_one() && !abs_dst_c.is_one()) { + unsigned r = copy_row(row_src); + mul_add(false, r, rational::one(), row_dst); + del_var(r, x); + add(r, slack); + TRACE("qe", tout << m_rows[r];); + SASSERT(!m_rows[r].m_value.is_pos()); + } if (use_case1) { + TRACE("opt", tout << "slack: " << slack << " " << src_c << " " << dst_val << " " << dst_c << " " << src_val << "\n";); // dst <- abs_src_c*dst + abs_dst_c*src - slack mul(row_dst, abs_src_c); sub(row_dst, slack); - mul_add(false, row_dst, abs_dst_c, row_src); + mul_add(false, row_dst, abs_dst_c, row_src); return; - } + } // // create finite disjunction for |b|. @@ -555,6 +563,7 @@ namespace opt { // exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 // + TRACE("qe", tout << "finite disjunction " << distance << " " << src_c << " " << dst_c << "\n";); vector coeffs; if (abs_dst_c <= abs_src_c) { rational z = mod(dst_val, abs_dst_c); @@ -611,6 +620,21 @@ namespace opt { r.m_value -= c; } + void model_based_opt::del_var(unsigned dst, unsigned x) { + row& r = m_rows[dst]; + unsigned j = 0; + for (var & v : r.m_vars) { + if (v.m_id == x) { + r.m_value -= eval(x)*r.m_coeff; + } + else { + r.m_vars[j++] = v; + } + } + r.m_vars.shrink(j); + } + + void model_based_opt::normalize(unsigned row_id) { row& r = m_rows[row_id]; if (r.m_vars.empty()) return; diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index a1137d2fa..e52d0cfe0 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -120,6 +120,8 @@ namespace opt { void sub(unsigned dst, rational const& c); + void del_var(unsigned dst, unsigned x); + void set_row(unsigned row_id, vector const& coeffs, rational const& c, rational const& m, ineq_type rel); void add_constraint(vector const& coeffs, rational const& c, rational const& m, ineq_type r); diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index e9653bf14..7e619ccd5 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -417,7 +417,7 @@ namespace qe { ts.push_back(t); } t = mk_add(ts); - s = a.mk_numeral(-r.m_coeff, a.is_int(t)); + s = a.mk_numeral(-r.m_coeff, r.m_coeff.is_int() && a.is_int(t)); switch (r.m_type) { case opt::t_lt: t = a.mk_lt(t, s); break; case opt::t_le: t = a.mk_le(t, s); break; @@ -445,7 +445,8 @@ namespace qe { for (var const& v : d.m_vars) { ts.push_back(var2expr(index2expr, v)); } - ts.push_back(a.mk_numeral(d.m_coeff, is_int)); + if (!d.m_coeff.is_zero()) + ts.push_back(a.mk_numeral(d.m_coeff, is_int)); t = mk_add(ts); if (!d.m_div.is_one() && is_int) { t = a.mk_idiv(t, a.mk_numeral(d.m_div, is_int)); @@ -461,10 +462,12 @@ namespace qe { } expr_ref mk_add(expr_ref_vector const& ts) { - if (ts.size() == 1) { + switch (ts.size()) { + case 0: + return expr_ref(a.mk_int(0), m); + case 1: return expr_ref(ts.get(0), m); - } - else { + default: return expr_ref(a.mk_add(ts.size(), ts.c_ptr()), m); } } diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 6f2c346f0..070c5279c 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -259,12 +259,11 @@ namespace qe { app_ref_vector& m_vars; arith_util arith; obj_hashtable m_exclude; - is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): + is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): m(vars.m()), m_vars(vars), arith(m) { for (func_decl* f : shared) m_exclude.insert(f); } void operator()(app* a) { - TRACE("qe", tout << expr_ref(a, m) << " " << arith.is_int_real(a) << " " << a->get_family_id() << "\n";); if (arith.is_int_real(a) && a->get_family_id() != arith.get_family_id() && !m_exclude.contains(a->get_decl())) { m_vars.push_back(a); } @@ -291,17 +290,7 @@ namespace qe { } } - mbi_result euf_arith_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { - lbool r = m_solver->check_sat(lits); - - switch (r) { - case l_false: - lits.reset(); - m_solver->get_unsat_core(lits); - // optionally minimize core using superposition. - return mbi_unsat; - case l_true: { - m_solver->get_model(mdl); + bool euf_arith_mbi_plugin::get_literals(model_ref& mdl, expr_ref_vector& lits) { model_evaluator mev(*mdl.get()); lits.reset(); for (expr* e : m_atoms) { @@ -313,49 +302,65 @@ namespace qe { } } TRACE("qe", tout << "atoms from model: " << lits << "\n";); - r = m_dual_solver->check_sat(lits); - expr_ref_vector core(m); - term_graph tg(m); - switch (r) { - case l_false: { + lbool r = m_dual_solver->check_sat(lits); + if (l_false == r) { // use the dual solver to find a 'small' implicant - m_dual_solver->get_unsat_core(core); - TRACE("qe", tout << "core: " << core << "\n";); lits.reset(); - lits.append(core); + m_dual_solver->get_unsat_core(lits); + return true; + } + else { + return false; + } + } + + app_ref_vector euf_arith_mbi_plugin::get_arith_vars(expr_ref_vector const& lits) { arith_util a(m); - // populate set of arithmetic variables to be projected. app_ref_vector avars(m); - is_arith_var_proc _proc(avars, m_shared); - for (expr* l : lits) quick_for_each_expr(_proc, l); - TRACE("qe", tout << "vars: " << avars << " lits: " << lits << "\n";); + is_arith_var_proc _proc(avars, m_shared); + for_each_expr(_proc, lits); + return avars; + } - // 1. project arithmetic variables using mdl that satisfies core. - // ground any remaining arithmetic variables using model. - arith_project_plugin ap(m); - ap.set_check_purified(false); + mbi_result euf_arith_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { + lbool r = m_solver->check_sat(lits); - auto defs = ap.project(*mdl.get(), avars, lits); - // 2. Add the projected definitions to the remaining (euf) literals - for (auto const& def : defs) { - lits.push_back(m.mk_eq(def.var, def.term)); - } - TRACE("qe", tout << "# arith defs" << defs.size() << " avars: " << avars << " " << lits << "\n";); - - // 3. Project the remaining literals with respect to EUF. - tg.set_vars(m_shared, false); - tg.add_lits(lits); - lits.reset(); - lits.append(tg.project(*mdl)); - TRACE("qe", tout << "project: " << lits << "\n";); - return mbi_sat; - } - case l_undef: - return mbi_undef; - case l_true: - UNREACHABLE(); + switch (r) { + case l_false: + lits.reset(); + m_solver->get_unsat_core(lits); + TRACE("qe", tout << "unsat core: " << lits << "\n";); + // optionally minimize core using superposition. + return mbi_unsat; + case l_true: { + m_solver->get_model(mdl); + if (!get_literals(mdl, lits)) { return mbi_undef; } + app_ref_vector avars = get_arith_vars(lits); + + TRACE("qe", tout << "vars: " << avars << " lits: " << lits << "\n";); + + // 1. project arithmetic variables using mdl that satisfies core. + // ground any remaining arithmetic variables using model. + arith_project_plugin ap(m); + ap.set_check_purified(false); + + auto defs = ap.project(*mdl.get(), avars, lits); + // 2. Add the projected definitions to the remaining (euf) literals + for (auto const& def : defs) { + lits.push_back(m.mk_eq(def.var, def.term)); + } + TRACE("qe", tout << "# arith defs" << defs.size() << " avars: " << avars << " " << lits << "\n";); + + // 3. Project the remaining literals with respect to EUF. + term_graph tg(m); + tg.set_vars(m_shared, false); + tg.add_lits(lits); + lits.reset(); + //lits.append(tg.project(*mdl)); + lits.append(tg.project()); + TRACE("qe", tout << "project: " << lits << "\n";); return mbi_sat; } default: @@ -448,6 +453,7 @@ namespace qe { case l_undef: return l_undef; } + break; case l_false: itp = mk_and(itps); return l_false; diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index 5e43a6412..22a0864ba 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -109,6 +109,10 @@ namespace qe { solver_ref m_dual_solver; struct is_atom_proc; struct is_arith_var_proc; + + app_ref_vector get_arith_vars(expr_ref_vector const& lits); + bool get_literals(model_ref& mdl, expr_ref_vector& lits); + public: euf_arith_mbi_plugin(solver* s, solver* sNot); ~euf_arith_mbi_plugin() override {} From 19023603612b75cc921e2cae031b45086dbd5acb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 13 Jun 2018 21:33:21 -0700 Subject: [PATCH 312/364] debugging mbi Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 93e55bedd..4efab1935 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -526,6 +526,7 @@ namespace opt { rational distance = src_c * dst_val + dst_c * src_val + slack; bool use_case1 = distance.is_nonpos() || abs_src_c.is_one() || abs_dst_c.is_one(); +#if 0 if (distance.is_nonpos() && !abs_src_c.is_one() && !abs_dst_c.is_one()) { unsigned r = copy_row(row_src); mul_add(false, r, rational::one(), row_dst); @@ -534,12 +535,13 @@ namespace opt { TRACE("qe", tout << m_rows[r];); SASSERT(!m_rows[r].m_value.is_pos()); } +#endif if (use_case1) { TRACE("opt", tout << "slack: " << slack << " " << src_c << " " << dst_val << " " << dst_c << " " << src_val << "\n";); - // dst <- abs_src_c*dst + abs_dst_c*src - slack + // dst <- abs_src_c*dst + abs_dst_c*src + slack mul(row_dst, abs_src_c); - sub(row_dst, slack); + add(row_dst, slack); mul_add(false, row_dst, abs_dst_c, row_src); return; } From bbd917a0e66b90ab237f6076d8c88d83abd977cb Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 14 Jun 2018 06:36:44 -0700 Subject: [PATCH 313/364] Remove dead comment --- src/sat/ba_solver.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp index 669389d2a..d7cdea132 100644 --- a/src/sat/ba_solver.cpp +++ b/src/sat/ba_solver.cpp @@ -527,14 +527,12 @@ namespace sat { bool ba_solver::init_watch(pb& p) { clear_watch(p); if (p.lit() != null_literal && value(p.lit()) == l_false) { - //IF_VERBOSE(0, verbose_stream() << "negate: " << p.k() << "\n"); p.negate(); } VERIFY(p.lit() == null_literal || value(p.lit()) == l_true); unsigned sz = p.size(), bound = p.k(); - //IF_VERBOSE(0, verbose_stream() << "bound: " << p.k() << "\n"); - + // put the non-false literals into the head. unsigned slack = 0, slack1 = 0, num_watch = 0, j = 0; for (unsigned i = 0; i < sz; ++i) { From e355123e372551606ca96c550501e0d0876bde2e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 14 Jun 2018 06:37:09 -0700 Subject: [PATCH 314/364] Change declaration of projector --- src/qe/qe_term_graph.cpp | 470 +++++++++++++++++++-------------------- src/qe/qe_term_graph.h | 3 +- 2 files changed, 235 insertions(+), 238 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index ac68516c3..f12d51b9d 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -540,270 +540,268 @@ namespace qe { m_cg_table.reset(); } - namespace { - class projector { - term_graph &m_tg; - ast_manager &m; - u_map m_term2app; - u_map m_root2rep; + class term_graph::projector { + term_graph &m_tg; + ast_manager &m; + u_map m_term2app; + u_map m_root2rep; - model_ref m_model; - expr_ref_vector m_pinned; // tracks expr in the maps + model_ref m_model; + expr_ref_vector m_pinned; // tracks expr in the maps - expr* mk_pure(term const& t) { - expr* e = nullptr; - if (m_term2app.find(t.get_id(), e)) return e; - e = t.get_expr(); - if (!is_app(e)) return nullptr; - app* a = ::to_app(e); - expr_ref_buffer kids(m); - for (term* ch : term::children(t)) { - if (!m_root2rep.find(ch->get_root().get_id(), e)) return nullptr; - kids.push_back(e); - } - expr* pure = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); - m_pinned.push_back(pure); - m_term2app.insert(t.get_id(), pure); - return pure; + expr* mk_pure(term const& t) { + expr* e = nullptr; + if (m_term2app.find(t.get_id(), e)) return e; + e = t.get_expr(); + if (!is_app(e)) return nullptr; + app* a = ::to_app(e); + expr_ref_buffer kids(m); + for (term* ch : term::children(t)) { + if (!m_root2rep.find(ch->get_root().get_id(), e)) return nullptr; + kids.push_back(e); + } + expr* pure = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); + m_pinned.push_back(pure); + m_term2app.insert(t.get_id(), pure); + return pure; + } + + + bool is_better_rep(expr *t1, expr *t2) { + if (!t2) return t1 != nullptr; + return m.is_unique_value(t1) && !m.is_unique_value(t2); + } + + void purify() { + // - propagate representatives up over parents. + // use work-list + marking to propagate. + // - produce equalities over represented classes. + // - produce other literals over represented classes + // (walk disequalities in m_lits and represent + // lhs/rhs over decls or excluding decls) + + ptr_vector worklist; + for (term * t : m_tg.m_terms) { + worklist.push_back(t); + t->set_mark(true); } + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; + if (!t->is_theory() && is_projected(*t)) + continue; - bool is_better_rep(expr *t1, expr *t2) { - if (!t2) return t1 != nullptr; - return m.is_unique_value(t1) && !m.is_unique_value(t2); - } + expr* pure = mk_pure(*t); + if (!pure) continue; - void purify() { - // - propagate representatives up over parents. - // use work-list + marking to propagate. - // - produce equalities over represented classes. - // - produce other literals over represented classes - // (walk disequalities in m_lits and represent - // lhs/rhs over decls or excluding decls) + m_term2app.insert(t->get_id(), pure); + expr* rep = nullptr; + // ensure that the root has a representative + m_root2rep.find(t->get_root().get_id(), rep); - ptr_vector worklist; - for (term * t : m_tg.m_terms) { - worklist.push_back(t); - t->set_mark(true); - } - - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); - t->set_mark(false); - if (m_term2app.contains(t->get_id())) - continue; - if (!t->is_theory() && is_projected(*t)) - continue; - - expr* pure = mk_pure(*t); - if (!pure) continue; - - m_term2app.insert(t->get_id(), pure); - expr* rep = nullptr; - // ensure that the root has a representative - m_root2rep.find(t->get_root().get_id(), rep); - - // update rep with pure if it is better - if (pure != rep && is_better_rep(pure, rep)) { - m_root2rep.insert(t->get_root().get_id(), pure); - for (term * p : term::parents(t->get_root())) { - m_term2app.remove(p->get_id()); - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); - } + // update rep with pure if it is better + if (pure != rep && is_better_rep(pure, rep)) { + m_root2rep.insert(t->get_root().get_id(), pure); + for (term * p : term::parents(t->get_root())) { + m_term2app.remove(p->get_id()); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); } } } - - // Here we could also walk equivalence classes that - // contain interpreted values by sort and extract - // disequalities bewteen non-unique value - // representatives. these disequalities are implied - // and can be mined using other means, such as theory - // aware core minimization - m_tg.reset_marks(); } - void solve_core() { - ptr_vector worklist; - for (term * t : m_tg.m_terms) { - // skip pure terms - if (m_term2app.contains(t->get_id())) continue; - worklist.push_back(t); - t->set_mark(true); - } + // Here we could also walk equivalence classes that + // contain interpreted values by sort and extract + // disequalities bewteen non-unique value + // representatives. these disequalities are implied + // and can be mined using other means, such as theory + // aware core minimization + m_tg.reset_marks(); + } - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); - t->set_mark(false); - if (m_term2app.contains(t->get_id())) - continue; + void solve_core() { + ptr_vector worklist; + for (term * t : m_tg.m_terms) { + // skip pure terms + if (m_term2app.contains(t->get_id())) continue; + worklist.push_back(t); + t->set_mark(true); + } - expr* pure = mk_pure(*t); - if (!pure) continue; + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; - m_term2app.insert(t->get_id(), pure); - expr* rep = nullptr; - // ensure that the root has a representative - m_root2rep.find(t->get_root().get_id(), rep); + expr* pure = mk_pure(*t); + if (!pure) continue; - if (!rep) { - m_root2rep.insert(t->get_root().get_id(), pure); - for (term * p : term::parents(t->get_root())) { - SASSERT(!m_term2app.contains(p->get_id())); - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); - } + m_term2app.insert(t->get_id(), pure); + expr* rep = nullptr; + // ensure that the root has a representative + m_root2rep.find(t->get_root().get_id(), rep); + + if (!rep) { + m_root2rep.insert(t->get_root().get_id(), pure); + for (term * p : term::parents(t->get_root())) { + SASSERT(!m_term2app.contains(p->get_id())); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); } } } - m_tg.reset_marks(); } + m_tg.reset_marks(); + } - bool find_app(term &t, expr *&res) { - return m_root2rep.find(t.get_root().get_id(), res); + bool find_app(term &t, expr *&res) { + return m_root2rep.find(t.get_root().get_id(), res); + } + + bool find_app(expr *lit, expr *&res) { + return m_root2rep.find(m_tg.get_term(lit)->get_root().get_id(), res); + } + + void mk_lits(expr_ref_vector &res) { + expr *e = nullptr; + for (auto *lit : m_tg.m_lits) { + if (!m.is_eq(lit) && find_app(lit, e)) + res.push_back(e); } + } - bool find_app(expr *lit, expr *&res) { - return m_root2rep.find(m_tg.get_term(lit)->get_root().get_id(), res); + void mk_pure_equalities(const term &t, expr_ref_vector &res) { + SASSERT(t.is_root()); + expr *rep = nullptr; + if (!m_root2rep.find(t.get_id(), rep)) return; + obj_hashtable members; + members.insert(rep); + term const * r = &t; + do { + expr* member = nullptr; + if (m_term2app.find(r->get_id(), member) && !members.contains(member)) { + res.push_back (m.mk_eq (rep, member)); + members.insert(member); + } + r = &r->get_next(); } + while (r != &t); + } - void mk_lits(expr_ref_vector &res) { - expr *e = nullptr; - for (auto *lit : m_tg.m_lits) { - if (!m.is_eq(lit) && find_app(lit, e)) - res.push_back(e); + bool is_projected(const term &t) {return m_tg.m_is_var(t);} + + void mk_unpure_equalities(const term &t, expr_ref_vector &res) { + expr *rep = nullptr; + if (!m_root2rep.find(t.get_id(), rep)) return; + obj_hashtable members; + members.insert(rep); + term const * r = &t; + do { + expr* member = mk_pure(*r); + SASSERT(member); + if (!members.contains(member) && + (!is_projected(*r) || !is_solved_eq(rep, member))) { + res.push_back(m.mk_eq(rep, member)); + members.insert(member); + } + r = &r->get_next(); + } + while (r != &t); + } + + void mk_equalities(bool pure, expr_ref_vector &res) { + for (term *t : m_tg.m_terms) { + if (!t->is_root()) continue; + if (!m_root2rep.contains(t->get_id())) continue; + if (pure) + mk_pure_equalities(*t, res); + else + mk_unpure_equalities(*t, res); + } + } + + void mk_pure_equalities(expr_ref_vector &res) { + return mk_equalities(true, res); + } + + void mk_unpure_equalities(expr_ref_vector &res) { + return mk_equalities(false, res); + } + + // TBD: generalize for also the case of a (:var n) + bool is_solved_eq(expr *lhs, expr* rhs) { + return is_uninterp_const(rhs) && !occurs(rhs, lhs); + } + + /// Add equalities and disequalities for all pure representatives + /// based on their equivalence in the model + void model_complete(expr_ref_vector &res) { + if (!m_model) return; + obj_map val2rep; + model_evaluator mev(*m_model); + for (auto &kv : m_root2rep) { + expr *rep = kv.m_value; + expr_ref val(m); + expr *u = nullptr; + if (!mev.eval(rep, val)) continue; + if (val2rep.find(val, u)) { + res.push_back(m.mk_eq(u, rep)); + } + else { + val2rep.insert(val, rep); } } - - void mk_pure_equalities(const term &t, expr_ref_vector &res) { - SASSERT(t.is_root()); - expr *rep = nullptr; - if (!m_root2rep.find(t.get_id(), rep)) return; - obj_hashtable members; - members.insert(rep); - term const * r = &t; - do { - expr* member = nullptr; - if (m_term2app.find(r->get_id(), member) && !members.contains(member)) { - res.push_back (m.mk_eq (rep, member)); - members.insert(member); - } - r = &r->get_next(); - } - while (r != &t); + // TBD: this ignores types, need one use of 'distinct' per sort. + // TBD: probably ignore distinct on values + // TBD: ignore distinct on Booleans + ptr_buffer reps; + for (auto &kv : val2rep) { + reps.push_back(kv.m_value); + std::cout << mk_pp(kv.m_value, m) << "\n"; } + // res.push_back(m.mk_distinct(reps.size(), reps.c_ptr())); + } - bool is_projected(const term &t) {return m_tg.m_is_var(t);} + public: + projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} - void mk_unpure_equalities(const term &t, expr_ref_vector &res) { - expr *rep = nullptr; - if (!m_root2rep.find(t.get_id(), rep)) return; - obj_hashtable members; - members.insert(rep); - term const * r = &t; - do { - expr* member = mk_pure(*r); - SASSERT(member); - if (!members.contains(member) && - (!is_projected(*r) || !is_solved_eq(rep, member))) { - res.push_back(m.mk_eq(rep, member)); - members.insert(member); - } - r = &r->get_next(); - } - while (r != &t); - } + void set_model(model &mdl) { m_model = &mdl; } - void mk_equalities(bool pure, expr_ref_vector &res) { - for (term *t : m_tg.m_terms) { - if (!t->is_root()) continue; - if (!m_root2rep.contains(t->get_id())) continue; - if (pure) - mk_pure_equalities(*t, res); - else - mk_unpure_equalities(*t, res); - } - } - - void mk_pure_equalities(expr_ref_vector &res) { - return mk_equalities(true, res); - } - - void mk_unpure_equalities(expr_ref_vector &res) { - return mk_equalities(false, res); - } - - // TBD: generalize for also the case of a (:var n) - bool is_solved_eq(expr *lhs, expr* rhs) { - return is_uninterp_const(rhs) && !occurs(rhs, lhs); - } - - /// Add equalities and disequalities for all pure representatives - /// based on their equivalence in the model - void model_complete(expr_ref_vector &res) { - if (!m_model) return; - obj_map val2rep; - model_evaluator mev(*m_model); - for (auto &kv : m_root2rep) { - expr *rep = kv.m_value; - expr_ref val(m); - expr *u = nullptr; - if (!mev.eval(rep, val)) continue; - if (val2rep.find(val, u)) { - res.push_back(m.mk_eq(u, rep)); - } - else { - val2rep.insert(val, rep); - } - } - // TBD: this ignores types, need one use of 'distinct' per sort. - // TBD: probably ignore distinct on values - // TBD: ignore distinct on Booleans - ptr_buffer reps; - for (auto &kv : val2rep) { - reps.push_back(kv.m_value); - std::cout << mk_pp(kv.m_value, m) << "\n"; - } - // res.push_back(m.mk_distinct(reps.size(), reps.c_ptr())); - } - - public: - projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} - - void set_model(model &mdl) { m_model = &mdl; } - - void reset() { - m_tg.reset_marks(); - m_term2app.reset(); - m_root2rep.reset(); - m_pinned.reset(); - m_model.reset(); - } - expr_ref_vector project() { - expr_ref_vector res(m); - purify(); - mk_lits(res); - mk_pure_equalities(res); - model_complete(res); - reset(); - return res; - } - expr_ref_vector solve() { - expr_ref_vector res(m); - purify(); - solve_core(); - mk_lits(res); - mk_unpure_equalities(res); - reset(); - return res; - } - }; - } + void reset() { + m_tg.reset_marks(); + m_term2app.reset(); + m_root2rep.reset(); + m_pinned.reset(); + m_model.reset(); + } + expr_ref_vector project() { + expr_ref_vector res(m); + purify(); + mk_lits(res); + mk_pure_equalities(res); + model_complete(res); + reset(); + return res; + } + expr_ref_vector solve() { + expr_ref_vector res(m); + purify(); + solve_core(); + mk_lits(res); + mk_unpure_equalities(res); + reset(); + return res; + } + }; void term_graph::set_vars(func_decl_ref_vector const& decls, bool exclude) { m_is_var.set_decls(decls, exclude); @@ -812,13 +810,13 @@ namespace qe { expr_ref_vector term_graph::project() { // reset solved vars so that they are not considered pure by projector m_is_var.reset_solved(); - projector p(*this); + term_graph::projector p(*this); return p.project(); } expr_ref_vector term_graph::project(model &mdl) { m_is_var.reset_solved(); - projector p(*this); + term_graph::projector p(*this); p.set_model(mdl); return p.project(); } @@ -826,7 +824,7 @@ namespace qe { expr_ref_vector term_graph::solve() { // reset solved vars so that they are not considered pure by projector m_is_var.reset_solved(); - projector p(*this); + term_graph::projector p(*this); return p.solve(); } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 97e14dc62..19b694a76 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -28,10 +28,9 @@ Notes: namespace qe { class term; - namespace {class projector;} class term_graph { - friend class projector; + class projector; class is_variable_proc : public ::is_variable_proc { bool m_exclude; From bc8ddedc54e8492d5caed5a3fe7c1224bf4fdbfd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 14 Jun 2018 08:34:52 -0700 Subject: [PATCH 315/364] fix a few build regressions Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 2 -- src/ast/arith_decl_plugin.cpp | 1 + src/qe/qe_solve_plugin.cpp | 4 ++-- src/smt/smt_setup.cpp | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index ad5e7731d..951b9e51e 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -536,7 +536,6 @@ extern "C" { init_solver(c, s); expr_ref_vector _assumptions(m), _consequences(m), _variables(m); ast_ref_vector const& __assumptions = to_ast_vector_ref(assumptions); - unsigned sz = __assumptions.size(); for (ast* e : __assumptions) { if (!is_expr(e)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); @@ -546,7 +545,6 @@ extern "C" { _assumptions.push_back(to_expr(e)); } ast_ref_vector const& __variables = to_ast_vector_ref(variables); - sz = __variables.size(); for (ast* a : __variables) { if (!is_expr(a)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index fe5bc3af5..d2b17daa0 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -401,6 +401,7 @@ inline decl_kind arith_decl_plugin::fix_kind(decl_kind k, unsigned arity) { app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { if (is_int && !val.is_int()) { + SASSERT(false); m_manager->raise_exception("invalid rational value passed as an integer"); } if (val.is_unsigned()) { diff --git a/src/qe/qe_solve_plugin.cpp b/src/qe/qe_solve_plugin.cpp index 0a499d3b8..1f314848f 100644 --- a/src/qe/qe_solve_plugin.cpp +++ b/src/qe/qe_solve_plugin.cpp @@ -80,8 +80,8 @@ namespace qe { bool sign = todo.back().first; todo.pop_back(); if (a.is_add(e)) { - for (expr* e : *to_app(e)) { - todo.push_back(std::make_pair(sign, e)); + for (expr* arg : *to_app(e)) { + todo.push_back(std::make_pair(sign, arg)); } } else if (a.is_sub(e)) { diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 97f5c433c..b90f08cd3 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -730,8 +730,8 @@ namespace smt { } void setup::setup_i_arith() { - m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); - // m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); + // m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); + m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); } void setup::setup_r_arith() { From 2a6b7e54825790012fbc316c2392f88c88536970 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 14 Jun 2018 08:48:28 -0700 Subject: [PATCH 316/364] fixes Signed-off-by: Nikolaj Bjorner --- src/ast/arith_decl_plugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index d2b17daa0..fe5bc3af5 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -401,7 +401,6 @@ inline decl_kind arith_decl_plugin::fix_kind(decl_kind k, unsigned arity) { app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { if (is_int && !val.is_int()) { - SASSERT(false); m_manager->raise_exception("invalid rational value passed as an integer"); } if (val.is_unsigned()) { From 87715620d943186b1306d49ab30fbb7f7668257e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 14 Jun 2018 16:02:19 -0700 Subject: [PATCH 317/364] Fix mk_distict in term_graph --- src/qe/qe_term_graph.cpp | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index f12d51b9d..0f1f75b39 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -28,6 +28,13 @@ Notes: namespace qe { + namespace { + struct sort_lt_proc { + bool operator()(const expr* a, const expr *b) const { + return get_sort(a)->get_id() < get_sort(b)->get_id(); + } + }; + } namespace is_pure_ns { struct found{}; struct proc { @@ -760,15 +767,29 @@ namespace qe { val2rep.insert(val, rep); } } - // TBD: this ignores types, need one use of 'distinct' per sort. - // TBD: probably ignore distinct on values // TBD: ignore distinct on Booleans ptr_buffer reps; for (auto &kv : val2rep) { - reps.push_back(kv.m_value); - std::cout << mk_pp(kv.m_value, m) << "\n"; + expr *val = kv.m_value; + if (!m.is_unique_value(val)) + reps.push_back(kv.m_value); + } + + if (reps.size() <= 1) return; + + // -- sort representatives, call mk_distinct on any range + // -- of the same sort longer than 1 + std::sort(reps.c_ptr(), reps.c_ptr() + reps.size(), sort_lt_proc()); + unsigned i = 0; + unsigned sz = res.size(); + while (i < sz) { + sort* last_sort = get_sort(reps.get(i)); + unsigned j = i + 1; + while (j < sz && last_sort == get_sort(reps.get(j))) {++j;} + if (j - i > 1) + res.push_back(m.mk_distinct(j - i, reps.c_ptr() + i)); + i = j; } - // res.push_back(m.mk_distinct(reps.size(), reps.c_ptr())); } public: From 83cee9e81fe91f76c1569b764f5a205b2acf5e33 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 14 Jun 2018 16:02:57 -0700 Subject: [PATCH 318/364] Comments --- src/qe/qe_mbi.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 070c5279c..3af377371 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -259,7 +259,7 @@ namespace qe { app_ref_vector& m_vars; arith_util arith; obj_hashtable m_exclude; - is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): + is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): m(vars.m()), m_vars(vars), arith(m) { for (func_decl* f : shared) m_exclude.insert(f); } @@ -317,8 +317,8 @@ namespace qe { app_ref_vector euf_arith_mbi_plugin::get_arith_vars(expr_ref_vector const& lits) { arith_util a(m); app_ref_vector avars(m); - is_arith_var_proc _proc(avars, m_shared); - for_each_expr(_proc, lits); + is_arith_var_proc _proc(avars, m_shared); + for_each_expr(_proc, lits); return avars; } @@ -487,9 +487,9 @@ namespace qe { else if is_sat(local & lits) && !is_sat(local & lits & blocked) MISSING CASE MUST PRODUCE AN IMPLICANT OF LOCAL that is inconsistent with lits & blocked - in this case !is_sat(local & lits & mdl) so - return l_false, core of lits & mdl, nullptr - this will force a new mdl + in this case !is_sat(local & lits & mdl) and is_sat(mdl, blocked) + let mdl_blocked be lits of blocked that are true in mdl + return l_false, core of lits & mdl_blocked, nullptr mbi_plugin::block(phi): add phi to blocked @@ -502,9 +502,10 @@ namespace qe { while (true) { // when lits.empty(), this picks an A-implicant consistent with B // when !lits.empty(), checks whether mdl of shared vocab extends to A - switch (a.check_ag(lits, mdl, !lits.empty())) { + bool force_model = !lits.empty(); + switch (a.check_ag(lits, mdl, force_model)) { case l_true: - if (!lits.empty()) + if (force_model) // mdl is a model for a && b return l_true; switch (b.check_ag(lits, mdl, false)) { From a3de478c9300c7b57910c4c85348ebf567167f5e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 14 Jun 2018 16:13:53 -0700 Subject: [PATCH 319/364] typo --- src/qe/qe_term_graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 0f1f75b39..4a08eeb89 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -781,7 +781,7 @@ namespace qe { // -- of the same sort longer than 1 std::sort(reps.c_ptr(), reps.c_ptr() + reps.size(), sort_lt_proc()); unsigned i = 0; - unsigned sz = res.size(); + unsigned sz = reps.size(); while (i < sz) { sort* last_sort = get_sort(reps.get(i)); unsigned j = i + 1; From 05abf190098d8dea5f9bdc8957a049171bb99c0e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 14 Jun 2018 16:38:27 -0700 Subject: [PATCH 320/364] comment --- src/qe/qe_term_graph.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 4a08eeb89..6018d763a 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -767,11 +767,15 @@ namespace qe { val2rep.insert(val, rep); } } - // TBD: ignore distinct on Booleans + + // TBD: optimize further based on implied values (e.g., + // some literals are forced to be true/false) and based on + // unique_values (e.g., (x=1 & y=1) does not require + // (x!=y) to be added ptr_buffer reps; for (auto &kv : val2rep) { - expr *val = kv.m_value; - if (!m.is_unique_value(val)) + expr *rep = kv.m_value; + if (!m.is_unique_value(rep)) reps.push_back(kv.m_value); } From a0af3383db03d07a0ce337c5009c38616ebfba54 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 14 Jun 2018 16:49:04 -0700 Subject: [PATCH 321/364] fixes to bdd Signed-off-by: Nikolaj Bjorner --- src/qe/qe_mbi.cpp | 8 ++++---- src/sat/ba_solver.cpp | 2 +- src/sat/sat_bdd.cpp | 10 +++++----- src/sat/sat_bdd.h | 2 ++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 3af377371..6b777aa02 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -259,7 +259,7 @@ namespace qe { app_ref_vector& m_vars; arith_util arith; obj_hashtable m_exclude; - is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): + is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): m(vars.m()), m_vars(vars), arith(m) { for (func_decl* f : shared) m_exclude.insert(f); } @@ -317,8 +317,8 @@ namespace qe { app_ref_vector euf_arith_mbi_plugin::get_arith_vars(expr_ref_vector const& lits) { arith_util a(m); app_ref_vector avars(m); - is_arith_var_proc _proc(avars, m_shared); - for_each_expr(_proc, lits); + is_arith_var_proc _proc(avars, m_shared); + for_each_expr(_proc, lits); return avars; } @@ -484,7 +484,7 @@ namespace qe { return l_true, mbp of local, mdl of local & blocked else if !is_sat(local & lits) then return l_false, mbp of local, nullptr - else if is_sat(local & lits) && !is_sat(local & lits & blocked) + else // is_sat(local & lits) && !is_sat(local & lits & blocked) MISSING CASE MUST PRODUCE AN IMPLICANT OF LOCAL that is inconsistent with lits & blocked in this case !is_sat(local & lits & mdl) and is_sat(mdl, blocked) diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp index d7cdea132..930617301 100644 --- a/src/sat/ba_solver.cpp +++ b/src/sat/ba_solver.cpp @@ -556,7 +556,7 @@ namespace sat { bool is_false = false; for (unsigned k = 0; k < sz; ++k) { SASSERT(!is_false || value(p[k].second) == l_false); - SASSERT(k < j == (value(p[k].second) != l_false)); + SASSERT((k < j) == (value(p[k].second) != l_false)); is_false = value(p[k].second) == l_false; }); diff --git a/src/sat/sat_bdd.cpp b/src/sat/sat_bdd.cpp index 74af4bf5e..bd1745765 100644 --- a/src/sat/sat_bdd.cpp +++ b/src/sat/sat_bdd.cpp @@ -108,7 +108,7 @@ namespace sat { bool bdd_manager::check_result(op_entry*& e1, op_entry const* e2, BDD a, BDD b, BDD c) { if (e1 != e2) { - SASSERT(e2->m_result != -1); + SASSERT(e2->m_result != null_bdd); push_entry(e1); e1 = nullptr; return true; @@ -117,7 +117,7 @@ namespace sat { e1->m_bdd1 = a; e1->m_bdd2 = b; e1->m_op = c; - SASSERT(e1->m_result == -1); + SASSERT(e1->m_result == null_bdd); return false; } } @@ -203,7 +203,7 @@ namespace sat { void * mem = m_alloc.allocate(sizeof(op_entry)); result = new (mem) op_entry(l, r, op); } - result->m_result = -1; + result->m_result = null_bdd; return result; } @@ -667,7 +667,7 @@ namespace sat { r = e2->m_result; } else { - SASSERT(e1->m_result == -1); + SASSERT(e1->m_result == null_bdd); push(mk_quant_rec(l, lo(b), op)); push(mk_quant_rec(l, hi(b), op)); r = make_node(lvl, read(2), read(1)); @@ -782,7 +782,7 @@ namespace sat { ptr_vector to_delete, to_keep; for (auto* e : m_op_cache) { - if (e->m_result != -1) { + if (e->m_result != null_bdd) { to_delete.push_back(e); } else { diff --git a/src/sat/sat_bdd.h b/src/sat/sat_bdd.h index 41d1115b9..70f6960fe 100644 --- a/src/sat/sat_bdd.h +++ b/src/sat/sat_bdd.h @@ -32,6 +32,8 @@ namespace sat { typedef unsigned BDD; + const BDD null_bdd = UINT_MAX; + enum bdd_op { bdd_and_op = 2, bdd_or_op = 3, From baa96909cca4fd81e48e1d51d6b35103e60ae77c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 14 Jun 2018 17:13:10 -0700 Subject: [PATCH 322/364] mb-skolem for arithmetic with model repair The contract is that users of mb-skolem ensure that interface equalities are preserved (by adding a sufficient set of disequalities, such as a chain x1 < x2 < x3 .., to force that solutions for x_i does not clash). Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 12 +++++++----- src/model/model_core.cpp | 8 ++++---- src/qe/qe_arith.cpp | 20 +++++++++++++++++++- src/qe/qe_mbi.cpp | 4 ++-- src/qe/qe_term_graph.cpp | 3 ++- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 4efab1935..09f6d5c2d 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -51,7 +51,8 @@ namespace opt { m_div = -v.m_coeff; } } - m_coeff = r.m_coeff - r.m_value; + m_coeff = r.m_coeff; + if (r.m_type == opt::t_lt) m_coeff += m_div; normalize(); SASSERT(m_div.is_pos()); } @@ -983,7 +984,7 @@ namespace opt { } else { result = def(); - result.m_coeff = eval(x); + m_var2value[x] = rational::zero(); } SASSERT(eval(result) == eval(x)); } @@ -1008,8 +1009,8 @@ namespace opt { else { result = def(m_rows[glb_index], x); } + m_var2value[x] = eval(result); #endif - SASSERT(eval(result) == eval(x)); } // The number of matching lower and upper bounds is small. @@ -1106,7 +1107,8 @@ namespace opt { } def result = project(y, compute_def); if (compute_def) { - result = (result * D) + u; + result = (result * D) + u; + m_var2value[x] = eval(result); } SASSERT(!compute_def || eval(result) == eval(x)); return result; @@ -1204,7 +1206,7 @@ namespace opt { def result; if (compute_def) { result = def(m_rows[row_id1], x); - SASSERT(eval(result) == eval(x)); + m_var2value[x] = eval(result); } retire_row(row_id1); return result; diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index 648768bf3..d41854c22 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -47,8 +47,8 @@ bool model_core::eval(func_decl* f, expr_ref & r) const { void model_core::register_decl(func_decl * d, expr * v) { SASSERT(d->get_arity() == 0); - decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, 0); - if (entry->get_data().m_value == 0) { + decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, nullptr); + if (entry->get_data().m_value == nullptr) { // new entry m_decls.push_back(d); m_const_decls.push_back(d); @@ -67,8 +67,8 @@ void model_core::register_decl(func_decl * d, expr * v) { void model_core::register_decl(func_decl * d, func_interp * fi) { SASSERT(d->get_arity() > 0); SASSERT(&fi->m() == &m_manager); - decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, 0); - if (entry->get_data().m_value == 0) { + decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, nullptr); + if (entry->get_data().m_value == nullptr) { // new entry m_decls.push_back(d); m_func_decls.push_back(d); diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 7e619ccd5..ff4c7dc52 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -360,7 +360,7 @@ namespace qe { ptr_vector index2expr; for (auto& kv : tids) { - index2expr.setx(kv.m_value, kv.m_key, 0); + index2expr.setx(kv.m_value, kv.m_key, nullptr); } j = 0; @@ -454,6 +454,8 @@ namespace qe { else if (!d.m_div.is_one() && !is_int) { t = a.mk_div(t, a.mk_numeral(d.m_div, is_int)); } + update_model(model, to_app(x), eval(t)); + SASSERT(eval(t) == eval(x)); result.push_back(def(expr_ref(x, m), t)); } @@ -461,6 +463,22 @@ namespace qe { return result; } + void update_model(model& mdl, app* x, expr_ref const& val) { + if (is_uninterp_const(x)) { + mdl.register_decl(x->get_decl(), val); + } + else { + func_interp* fi = mdl.get_func_interp(x->get_decl()); + if (!fi) return; + model_evaluator eval(mdl); + expr_ref_vector args(m); + for (expr* arg : *x) { + args.push_back(eval(arg)); + } + fi->insert_entry(args.c_ptr(), val); + } + } + expr_ref mk_add(expr_ref_vector const& ts) { switch (ts.size()) { case 0: diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 6b777aa02..60fb0f6f0 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -259,7 +259,7 @@ namespace qe { app_ref_vector& m_vars; arith_util arith; obj_hashtable m_exclude; - is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): + is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): m(vars.m()), m_vars(vars), arith(m) { for (func_decl* f : shared) m_exclude.insert(f); } @@ -484,7 +484,7 @@ namespace qe { return l_true, mbp of local, mdl of local & blocked else if !is_sat(local & lits) then return l_false, mbp of local, nullptr - else // is_sat(local & lits) && !is_sat(local & lits & blocked) + else if is_sat(local & lits) && !is_sat(local & lits & blocked) MISSING CASE MUST PRODUCE AN IMPLICANT OF LOCAL that is inconsistent with lits & blocked in this case !is_sat(local & lits & mdl) and is_sat(mdl, blocked) diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 6018d763a..95518d7fc 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -35,6 +35,7 @@ namespace qe { } }; } + namespace is_pure_ns { struct found{}; struct proc { @@ -776,7 +777,7 @@ namespace qe { for (auto &kv : val2rep) { expr *rep = kv.m_value; if (!m.is_unique_value(rep)) - reps.push_back(kv.m_value); + reps.push_back(kv.m_value); } if (reps.size() <= 1) return; From f936c92efc111639de507def47b5612b7843e98e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 14 Jun 2018 22:27:57 -0700 Subject: [PATCH 323/364] Improve distinct constraint generation still many more optimizations possible --- src/qe/qe_mbi.cpp | 12 ++++++------ src/qe/qe_term_graph.cpp | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 60fb0f6f0..86fc1ac3c 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -315,10 +315,10 @@ namespace qe { } app_ref_vector euf_arith_mbi_plugin::get_arith_vars(expr_ref_vector const& lits) { - arith_util a(m); - app_ref_vector avars(m); - is_arith_var_proc _proc(avars, m_shared); - for_each_expr(_proc, lits); + arith_util a(m); + app_ref_vector avars(m); + is_arith_var_proc _proc(avars, m_shared); + for_each_expr(_proc, lits); return avars; } @@ -358,8 +358,8 @@ namespace qe { tg.set_vars(m_shared, false); tg.add_lits(lits); lits.reset(); - //lits.append(tg.project(*mdl)); - lits.append(tg.project()); + lits.append(tg.project(*mdl)); + //lits.append(tg.project()); TRACE("qe", tout << "project: " << lits << "\n";); return mbi_sat; } diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 95518d7fc..510868366 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -28,6 +28,16 @@ Notes: namespace qe { + static expr_ref mk_neq(ast_manager &m, expr *e1, expr *e2) { + expr *t = nullptr; + // x != !x == true + if ((m.is_not(e1, t) && t == e2) || (m.is_not(e2, t) && t == e1)) + return expr_ref(m.mk_true(), m); + else if (m.are_distinct(e1, e2)) + return expr_ref(m.mk_true(), m); + return expr_ref(m.mk_not(m.mk_eq(e1, e2)), m); + } + namespace { struct sort_lt_proc { bool operator()(const expr* a, const expr *b) const { @@ -791,10 +801,16 @@ namespace qe { sort* last_sort = get_sort(reps.get(i)); unsigned j = i + 1; while (j < sz && last_sort == get_sort(reps.get(j))) {++j;} - if (j - i > 1) + if (j - i == 2) { + expr_ref d(m); + d = mk_neq(m, reps.get(i), reps.get(i+1)); + if (!m.is_true(d)) res.push_back(d); + } + else if (j - i > 2) res.push_back(m.mk_distinct(j - i, reps.c_ptr() + i)); i = j; } + TRACE("qe", tout << "after distinct: " << res << "\n";); } public: From a51d6cbcbc8d72db4b6ea5199a92d36e1fe18816 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Jun 2018 14:58:02 -0700 Subject: [PATCH 324/364] debug model evaluator Signed-off-by: Nikolaj Bjorner --- src/model/model_evaluator.cpp | 9 +++++++-- src/muz/rel/dl_sparse_table.h | 32 ++++++++++++++++---------------- src/smt/smt_model_checker.cpp | 2 +- src/util/util.h | 4 +++- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 0b25a250b..fcff20afc 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -290,6 +290,7 @@ struct evaluator_cfg : public default_rewriter_cfg { br_status mk_array_eq(expr* a, expr* b, expr_ref& result) { + TRACE("model_evaluator", tout << "mk_array_eq " << m_array_equalities << "\n";); if (a == b) { result = m.mk_true(); return BR_DONE; @@ -315,6 +316,7 @@ struct evaluator_cfg : public default_rewriter_cfg { conj.push_back(m.mk_eq(else1, else2)); } if (args_are_unique1 && args_are_unique2 && !stores1.empty()) { + TRACE("model_evalator", tout << "argss are unique";); return mk_array_eq_core(stores1, else1, stores2, else2, conj, result); } @@ -329,7 +331,10 @@ struct evaluator_cfg : public default_rewriter_cfg { expr_ref s2(m_ar.mk_select(args2.size(), args2.c_ptr()), m); conj.push_back(m.mk_eq(s1, s2)); } - result = m.mk_and(conj.size(), conj.c_ptr()); + result = mk_and(conj); + TRACE("model_evaluator", tout << mk_pp(a, m) << " == " << mk_pp(b, m) << " -> " << conj << "\n"; + for (auto& s : stores1) tout << "store: " << s << "\n"; + ); return BR_REWRITE_FULL; } return BR_FAILED; @@ -454,7 +459,7 @@ struct evaluator_cfg : public default_rewriter_cfg { func_decl* f = m_ar.get_as_array_func_decl(to_app(a)); func_interp* g = m_model.get_func_interp(f); - if (!g) return false; + if (!g) return false; unsigned sz = g->num_entries(); unsigned arity = f->get_arity(); unsigned base_sz = stores.size(); diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h index fbbbd12f8..43a967729 100644 --- a/src/muz/rel/dl_sparse_table.h +++ b/src/muz/rel/dl_sparse_table.h @@ -326,29 +326,29 @@ namespace datalog { public: unsigned m_offset; //!< in bits unsigned m_length; //!< in bits - - column_info(unsigned offset, unsigned length) \ - : m_big_offset(offset/8), - m_small_offset(offset%8), - m_mask( length==64 ? ULLONG_MAX : (static_cast(1)<(1)<(rec+m_big_offset); + const uint64_t * ptr = reinterpret_cast(rec + m_big_offset); uint64_t res = *ptr; - res>>=m_small_offset; - res&=m_mask; + res >>= m_small_offset; + res &= m_mask; return res; } void set(char * rec, table_element val) const { SASSERT( (val&~m_mask)==0 ); //the value fits into the column - uint64_t * ptr = reinterpret_cast(rec+m_big_offset); - *ptr&=m_write_mask; - *ptr|=val<(rec + m_big_offset); + *ptr &= m_write_mask; + *ptr |= val << m_small_offset; } unsigned next_ofs() const { return m_offset+m_length; } }; diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 765cc87f5..5ab52b57c 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -438,7 +438,7 @@ namespace smt { } else if (!check(q)) { if (m_params.m_mbqi_trace || get_verbosity_level() >= 5) { - verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"; + IF_VERBOSE(0, verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"); } TRACE("model_checker", tout << "checking quantifier " << mk_pp(q, m) << " failed\n";); num_failures++; diff --git a/src/util/util.h b/src/util/util.h index 6e7ee5ce5..77204977e 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -193,8 +193,10 @@ bool is_threaded(); #ifdef _MSC_VER #define DO_PRAGMA(x) __pragma(x) +#define PRAGMA_LOCK __pragma(omp critical (verbose_lock)) #else #define DO_PRAGMA(x) _Pragma(#x) +#define PRAGMA_LOCK _Pragma("omp critical (verbose_lock){) #endif #ifdef _NO_OMP_ @@ -202,7 +204,7 @@ bool is_threaded(); #else #define LOCK_CODE(CODE) \ { \ - DO_PRAGMA(omp critical (verbose_lock)) \ + PRAGMA_LOCK \ { \ CODE; \ } \ From 1debbc29c4695d5046cad7828fdc5b17a681a9e4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Jun 2018 14:59:33 -0700 Subject: [PATCH 325/364] release notes Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 7439342de..e55b329db 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -26,12 +26,16 @@ Version 4.8.0 (known as ACCE, ABCE, CCE). Asymmetric branching also uses features introduced in Lingeling by exploiting binary implication graphs. Use sat.acce=true to enable the full repertoire of inprocessing methods. By default, clauses that are "eliminated" by acce are tagged as lemmas (redundant) and are garbage collected if their glue level is high. + - Substantial overhaul of the spacer horn clause engine. - Removed features: - interpolation API - duality engine for constrained Horn clauses. + - pdr engine for constrained Horn clauses. The engine's functionality has been + folded into spacer as one of optional strategies. - long deprecated API functions have been removed from z3_api.h + Version 4.7.1 ============= From 6e27ad42c8a1ba7ce91fa4f918bd59db037821ec Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Jun 2018 15:02:50 -0700 Subject: [PATCH 326/364] remove pdr reference from legacy build script Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index f2720c893..22b640305 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -59,13 +59,12 @@ def init_project_def(): add_lib('dataflow', ['muz'], 'muz/dataflow') add_lib('transforms', ['muz', 'hilbert', 'dataflow'], 'muz/transforms') add_lib('rel', ['muz', 'transforms'], 'muz/rel') - add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'core_tactics', 'smt_tactic'], 'muz/pdr') add_lib('spacer', ['muz', 'transforms', 'arith_tactics', 'smt_tactic'], 'muz/spacer') add_lib('clp', ['muz', 'transforms'], 'muz/clp') add_lib('tab', ['muz', 'transforms'], 'muz/tab') add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') add_lib('ddnf', ['muz', 'transforms', 'rel'], 'muz/ddnf') - add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'ddnf', 'spacer'], 'muz/fp') + add_lib('fp', ['muz', 'clp', 'tab', 'rel', 'bmc', 'ddnf', 'spacer'], 'muz/fp') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') add_lib('smtlogic_tactics', ['ackermannization', 'sat_solver', 'arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') From 3ad14465971a27c28d6c291262952f466532828e Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Fri, 15 Jun 2018 15:09:25 -0700 Subject: [PATCH 327/364] Remove spurious quote --- src/util/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/util.h b/src/util/util.h index 77204977e..994faf6a3 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -196,7 +196,7 @@ bool is_threaded(); #define PRAGMA_LOCK __pragma(omp critical (verbose_lock)) #else #define DO_PRAGMA(x) _Pragma(#x) -#define PRAGMA_LOCK _Pragma("omp critical (verbose_lock){) +#define PRAGMA_LOCK _Pragma(omp critical (verbose_lock){) #endif #ifdef _NO_OMP_ From b6c43f6143f07de5eae4a76fc62fe26d8d1b41b1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Jun 2018 15:13:55 -0700 Subject: [PATCH 328/364] move files for build script Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 2 +- src/ast/CMakeLists.txt | 1 - src/ast/rewriter/CMakeLists.txt | 1 + src/ast/{ => rewriter}/factor_equivs.cpp | 3 ++- src/ast/{ => rewriter}/factor_equivs.h | 0 src/muz/spacer/spacer_generalizers.cpp | 2 +- src/muz/spacer/spacer_legacy_frames.cpp | 2 +- src/muz/spacer/spacer_quant_generalizer.cpp | 2 +- src/muz/spacer/spacer_util.cpp | 2 +- 9 files changed, 8 insertions(+), 7 deletions(-) rename src/ast/{ => rewriter}/factor_equivs.cpp (98%) rename src/ast/{ => rewriter}/factor_equivs.h (100%) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 22b640305..dada93069 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -41,7 +41,6 @@ def init_project_def(): add_lib('aig_tactic', ['tactic'], 'tactic/aig') add_lib('ackermannization', ['model', 'rewriter', 'ast', 'solver', 'tactic'], 'ackermannization') add_lib('cmd_context', ['solver', 'rewriter']) - add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'arith_tactics'], 'cmd_context/extra_cmds') add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') add_lib('fpa', ['ast', 'util', 'rewriter', 'model'], 'ast/fpa') add_lib('pattern', ['normal_forms', 'smt2parser', 'rewriter'], 'ast/pattern') @@ -74,6 +73,7 @@ def init_project_def(): API_files = ['z3_api.h', 'z3_ast_containers.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fixedpoint.h', 'z3_optimization.h', 'z3_fpa.h', 'z3_spacer.h'] add_lib('api', ['portfolio', 'realclosure', 'opt'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) + add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'qe', 'arith_tactics'], 'cmd_context/extra_cmds') add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3') add_exe('test', ['api', 'fuzzing', 'simplex'], exe_name='test-z3', install=False) _libz3Component = add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll', diff --git a/src/ast/CMakeLists.txt b/src/ast/CMakeLists.txt index 4dcdd2a35..80543bb05 100644 --- a/src/ast/CMakeLists.txt +++ b/src/ast/CMakeLists.txt @@ -24,7 +24,6 @@ z3_add_component(ast expr_map.cpp expr_stat.cpp expr_substitution.cpp - factor_equivs.cpp for_each_ast.cpp for_each_expr.cpp format.cpp diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index f030e2704..9d80fd5ac 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -16,6 +16,7 @@ z3_add_component(rewriter enum2bv_rewriter.cpp expr_replacer.cpp expr_safe_replace.cpp + factor_equivs.cpp factor_rewriter.cpp fpa_rewriter.cpp inj_axiom.cpp diff --git a/src/ast/factor_equivs.cpp b/src/ast/rewriter/factor_equivs.cpp similarity index 98% rename from src/ast/factor_equivs.cpp rename to src/ast/rewriter/factor_equivs.cpp index be402e628..6384ad8ce 100644 --- a/src/ast/factor_equivs.cpp +++ b/src/ast/rewriter/factor_equivs.cpp @@ -25,11 +25,12 @@ Revision History: */ -#include "ast/factor_equivs.h" #include "ast/arith_decl_plugin.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_safe_replace.h" +#include "ast/rewriter/factor_equivs.h" + /** Factors input vector v into equivalence classes and the rest */ diff --git a/src/ast/factor_equivs.h b/src/ast/rewriter/factor_equivs.h similarity index 100% rename from src/ast/factor_equivs.h rename to src/ast/rewriter/factor_equivs.h diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 4e5b60698..b0fb6c2d3 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -25,7 +25,7 @@ Revision History: #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" #include "ast/expr_functors.h" diff --git a/src/muz/spacer/spacer_legacy_frames.cpp b/src/muz/spacer/spacer_legacy_frames.cpp index 49157a085..a21df5038 100644 --- a/src/muz/spacer/spacer_legacy_frames.cpp +++ b/src/muz/spacer/spacer_legacy_frames.cpp @@ -34,7 +34,7 @@ #include "util/luby.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" namespace spacer { diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index fb2964c40..f66fe7b29 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -26,7 +26,7 @@ Revision History: #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" #include "ast/expr_functors.h" diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 29d72fabd..f53577b5b 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -64,7 +64,7 @@ Notes: #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/arith/arith_bounds_tactic.h" -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" #include "qe/qe_term_graph.h" namespace spacer { From caca07c85f5ca8174102bc2e842efbb7bd213877 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Jun 2018 15:28:18 -0700 Subject: [PATCH 329/364] fix path to moved header file Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_array_eq_rewrite.cpp | 2 +- src/muz/transforms/dl_mk_array_instantiation.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/muz/transforms/dl_mk_array_eq_rewrite.cpp b/src/muz/transforms/dl_mk_array_eq_rewrite.cpp index 61eafae64..c8a3d4357 100644 --- a/src/muz/transforms/dl_mk_array_eq_rewrite.cpp +++ b/src/muz/transforms/dl_mk_array_eq_rewrite.cpp @@ -22,7 +22,7 @@ Revision History: #include "muz/base/dl_context.h" #include "muz/base/fp_params.hpp" #include "muz/transforms/dl_mk_array_eq_rewrite.h" -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" namespace datalog { diff --git a/src/muz/transforms/dl_mk_array_instantiation.h b/src/muz/transforms/dl_mk_array_instantiation.h index b2e80ab84..51a818a3f 100644 --- a/src/muz/transforms/dl_mk_array_instantiation.h +++ b/src/muz/transforms/dl_mk_array_instantiation.h @@ -70,7 +70,7 @@ Revision History: #define DL_MK_ARRAY_INSTANTIATION_H_ -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { From c0e378b0453e42331ca92c842288fdd4e7d0b8d3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Jun 2018 15:44:33 -0700 Subject: [PATCH 330/364] remove { Signed-off-by: Nikolaj Bjorner --- src/util/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/util.h b/src/util/util.h index 994faf6a3..4b18ec5e6 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -196,7 +196,7 @@ bool is_threaded(); #define PRAGMA_LOCK __pragma(omp critical (verbose_lock)) #else #define DO_PRAGMA(x) _Pragma(#x) -#define PRAGMA_LOCK _Pragma(omp critical (verbose_lock){) +#define PRAGMA_LOCK _Pragma(omp critical (verbose_lock)) #endif #ifdef _NO_OMP_ From 9149048f348b57c0f13c6019b6477786fc3e0589 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Jun 2018 15:53:47 -0700 Subject: [PATCH 331/364] use quotes Signed-off-by: Nikolaj Bjorner --- src/util/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/util.h b/src/util/util.h index 4b18ec5e6..12fd2fe3b 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -196,7 +196,7 @@ bool is_threaded(); #define PRAGMA_LOCK __pragma(omp critical (verbose_lock)) #else #define DO_PRAGMA(x) _Pragma(#x) -#define PRAGMA_LOCK _Pragma(omp critical (verbose_lock)) +#define PRAGMA_LOCK _Pragma("omp critical (verbose_lock)") #endif #ifdef _NO_OMP_ From 450da5ea0c93719c63ef3629149d10e5e2f58dac Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Jun 2018 17:40:54 -0700 Subject: [PATCH 332/364] moving model_evaluator to model Signed-off-by: Nikolaj Bjorner --- src/model/model.cpp | 79 ++++++++++++++++++++-------------- src/model/model.h | 36 ++++++++++++++++ src/model/model_core.h | 1 + src/model/model_evaluator.cpp | 6 ++- src/model/model_evaluator.h | 1 + src/tactic/arith/fm_tactic.cpp | 15 +++---- 6 files changed, 96 insertions(+), 42 deletions(-) diff --git a/src/model/model.cpp b/src/model/model.cpp index e6c7b74a0..9ac971afe 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -26,16 +26,15 @@ Revision History: #include "model/model_evaluator.h" model::model(ast_manager & m): - model_core(m) { + model_core(m), + m_mev(*this) { } model::~model() { - sort2universe::iterator it3 = m_usort2universe.begin(); - sort2universe::iterator end3 = m_usort2universe.end(); - for (; it3 != end3; ++it3) { - m_manager.dec_ref(it3->m_key); - m_manager.dec_array_ref(it3->m_value->size(), it3->m_value->c_ptr()); - dealloc(it3->m_value); + for (auto & kv : m_usort2universe) { + m_manager.dec_ref(kv.m_key); + m_manager.dec_array_ref(kv.m_value->size(), kv.m_value->c_ptr()); + dealloc(kv.m_value); } } @@ -48,19 +47,13 @@ void model::copy_const_interps(model const & source) { } void model::copy_func_interps(model const & source) { - decl2finterp::iterator it2 = source.m_finterp.begin(); - decl2finterp::iterator end2 = source.m_finterp.end(); - for (; it2 != end2; ++it2) { - register_decl(it2->m_key, it2->m_value->copy()); - } + for (auto const& kv : source.m_finterp) + register_decl(kv.m_key, kv.m_value->copy()); } void model::copy_usort_interps(model const & source) { - sort2universe::iterator it3 = source.m_usort2universe.begin(); - sort2universe::iterator end3 = source.m_usort2universe.end(); - for (; it3 != end3; ++it3) { - register_usort(it3->m_key, it3->m_value->size(), it3->m_value->c_ptr()); - } + for (auto const& kv : source.m_usort2universe) + register_usort(kv.m_key, kv.m_value->size(), kv.m_value->c_ptr()); } model * model::copy() const { @@ -151,28 +144,21 @@ model * model::translate(ast_translation & translator) const { model * res = alloc(model, translator.to()); // Translate const interps - decl2expr::iterator it1 = m_interp.begin(); - decl2expr::iterator end1 = m_interp.end(); - for (; it1 != end1; ++it1) { - res->register_decl(translator(it1->m_key), translator(it1->m_value)); - } + for (auto const& kv : m_interp) + res->register_decl(translator(kv.m_key), translator(kv.m_value)); // Translate func interps - decl2finterp::iterator it2 = m_finterp.begin(); - decl2finterp::iterator end2 = m_finterp.end(); - for (; it2 != end2; ++it2) { - func_interp * fi = it2->m_value; - res->register_decl(translator(it2->m_key), fi->translate(translator)); + for (auto const& kv : m_finterp) { + func_interp * fi = kv.m_value; + res->register_decl(translator(kv.m_key), fi->translate(translator)); } // Translate usort interps - sort2universe::iterator it3 = m_usort2universe.begin(); - sort2universe::iterator end3 = m_usort2universe.end(); - for (; it3 != end3; ++it3) { + for (auto const& kv : m_usort2universe) { ptr_vector new_universe; - for (unsigned i=0; im_value->size(); i++) - new_universe.push_back(translator(it3->m_value->get(i))); - res->register_usort(translator(it3->m_key), + for (unsigned i=0; i < kv.m_value->size(); i++) + new_universe.push_back(translator(kv.m_value->get(i))); + res->register_usort(translator(kv.m_key), new_universe.size(), new_universe.c_ptr()); } @@ -180,3 +166,30 @@ model * model::translate(ast_translation & translator) const { return res; } +expr_ref model::operator()(expr* t) { + return m_mev(t); +} + +expr_ref_vector model::operator()(expr_ref_vector const& ts) { + expr_ref_vector rs(m()); + for (expr* t : ts) rs.push_back((*this)(t)); + return rs; +} + +bool model::is_true(expr* t) { + return m().is_true((*this)(t)); +} + +bool model::is_false(expr* t) { + return m().is_false((*this)(t)); +} + +bool model::is_true(expr_ref_vector const& ts) { + for (expr* t : ts) if (!is_true(t)) return false; + return true; +} + +void model::reset_eval_cache() { + m_mev.reset(); +} + diff --git a/src/model/model.h b/src/model/model.h index 758d3d451..3fda2832f 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -20,6 +20,7 @@ Revision History: #define MODEL_H_ #include "model/model_core.h" +#include "model/model_evaluator.h" #include "util/ref.h" #include "ast/ast_translation.h" @@ -29,6 +30,7 @@ protected: ptr_vector m_usorts; sort2universe m_usort2universe; + model_evaluator m_mev; struct value_proc; public: @@ -58,6 +60,40 @@ public: // Model translation // model * translate(ast_translation & translator) const; + + void set_model_completion(bool f) { m_mev.set_model_completion(f); } + void updt_params(params_ref const & p) { m_mev.updt_params(p); } + + /** + * evaluation using the model evaluator. Caches results. + */ + expr_ref operator()(expr* t); + expr_ref_vector operator()(expr_ref_vector const& ts); + bool is_true(expr* t); + bool is_false(expr* t); + bool is_true(expr_ref_vector const& ts); + void reset_eval_cache(); + + class scoped_model_completion { + bool m_old_completion; + model& m_model; + public: + scoped_model_completion(model& m, bool c): + m_old_completion(m.m_mev.get_model_completion()), m_model(m) { + m.set_model_completion(c); + } +#if 0 + scoped_model_completion(model_ref& m, bool c): + m_old_completion(m->m_mev.get_model_completion()), m_model(*m.get()) { + m->set_model_completion(c); + } +#endif + ~scoped_model_completion() { + m_model.set_model_completion(m_old_completion); + } + }; + + }; typedef ref model_ref; diff --git a/src/model/model_core.h b/src/model/model_core.h index ce211c0b8..400b2fcab 100644 --- a/src/model/model_core.h +++ b/src/model/model_core.h @@ -40,6 +40,7 @@ public: virtual ~model_core(); ast_manager & get_manager() const { return m_manager; } + ast_manager& m() { return m_manager; } unsigned get_num_decls() const { return m_decls.size(); } func_decl * get_decl(unsigned i) const { return m_decls[i]; } diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 250b28ff2..f7c7fa74d 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -85,8 +85,8 @@ struct evaluator_cfg : public default_rewriter_cfg { model_evaluator_params p(_p); m_max_memory = megabytes_to_bytes(p.max_memory()); m_max_steps = p.max_steps(); - m_model_completion = p.completion(); m_cache = p.cache(); + m_model_completion = p.completion(); m_array_equalities = p.array_equalities(); m_array_as_stores = p.array_as_stores(); } @@ -544,6 +544,10 @@ void model_evaluator::set_model_completion(bool f) { m_imp->cfg().m_model_completion = f; } +bool model_evaluator::get_model_completion() const { + return m_imp->cfg().m_model_completion; +} + void model_evaluator::set_expand_array_equalities(bool f) { m_imp->cfg().m_array_equalities = f; } diff --git a/src/model/model_evaluator.h b/src/model/model_evaluator.h index d9bf3c375..8666e3519 100644 --- a/src/model/model_evaluator.h +++ b/src/model/model_evaluator.h @@ -37,6 +37,7 @@ public: ast_manager & m () const; void set_model_completion(bool f); + bool get_model_completion() const; void set_expand_array_equalities(bool f); void updt_params(params_ref const & p); diff --git a/src/tactic/arith/fm_tactic.cpp b/src/tactic/arith/fm_tactic.cpp index 679adb2dd..fc41f54a4 100644 --- a/src/tactic/arith/fm_tactic.cpp +++ b/src/tactic/arith/fm_tactic.cpp @@ -62,7 +62,7 @@ class fm_tactic : public tactic { return m.is_false(val); } - r_kind process(func_decl * x, expr * cls, arith_util & u, model_evaluator & ev, rational & r) { + r_kind process(func_decl * x, expr * cls, arith_util & u, model& ev, rational & r) { unsigned num_lits; expr * const * lits; if (m.is_or(cls)) { @@ -80,9 +80,7 @@ class fm_tactic : public tactic { expr * l = lits[i]; expr * atom; if (is_uninterp_const(l) || (m.is_not(l, atom) && is_uninterp_const(atom))) { - expr_ref val(m); - ev(l, val); - if (m.is_true(val)) + if (ev.is_true(l)) return NONE; // clause was satisfied } else { @@ -131,7 +129,7 @@ class fm_tactic : public tactic { } else { expr_ref val(m); - ev(monomial, val); + val = ev(monomial); SASSERT(u.is_numeral(val)); rational tmp; u.is_numeral(val, tmp); @@ -184,8 +182,9 @@ class fm_tactic : public tactic { void operator()(model_ref & md) override { TRACE("fm_mc", model_v2_pp(tout, *md); display(tout);); - model_evaluator ev(*(md.get())); - ev.set_model_completion(true); + model::scoped_model_completion _sc(*md, true); + //model_evaluator ev(*(md.get())); + //ev.set_model_completion(true); arith_util u(m); unsigned i = m_xs.size(); while (i > 0) { @@ -201,7 +200,7 @@ class fm_tactic : public tactic { clauses::iterator end = m_clauses[i].end(); for (; it != end; ++it) { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); - switch (process(x, *it, u, ev, val)) { + switch (process(x, *it, u, *md, val)) { case NONE: TRACE("fm_mc", tout << "no bound for:\n" << mk_ismt2_pp(*it, m) << "\n";); break; From c64321c2e466e58d8df49b2ed1fa6a8416ef2bc0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 16 Jun 2018 11:22:58 -0700 Subject: [PATCH 333/364] debugging maxres bug report Signed-off-by: Nikolaj Bjorner --- src/opt/maxres.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index d49044d8d..e3777ee9b 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -781,9 +781,8 @@ public: bool is_true(expr_ref_vector const& es) { unsigned i = 0; for (; i < es.size() && is_true(es[i]); ++i) { } - CTRACE("opt", i < es.size(), tout << mk_pp(es[i], m) << "\n"; - model_smt2_pp(tout, m, *m_model, 0); - ); + CTRACE("opt_bug", i < es.size(), tout << mk_pp(es[i], m) << "\n"; + model_smt2_pp(tout, m, *m_model, 0);); return i == es.size(); } From 60888a93eb24d4f5ce0b3defaaeccfba2c977af7 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 16 Jun 2018 13:42:26 -0700 Subject: [PATCH 334/364] Minor fixes to model --- src/model/model.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/model/model.h b/src/model/model.h index 3fda2832f..429e7b51a 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -24,10 +24,13 @@ Revision History: #include "util/ref.h" #include "ast/ast_translation.h" +class model; +typedef ref model_ref; + class model : public model_core { protected: typedef obj_map*> sort2universe; - + ptr_vector m_usorts; sort2universe m_usort2universe; model_evaluator m_mev; @@ -42,10 +45,10 @@ public: void copy_usort_interps(model const & source); model * copy() const; - + bool eval(func_decl * f, expr_ref & r) const { return model_core::eval(f, r); } bool eval(expr * e, expr_ref & result, bool model_completion = false); - + expr * get_some_value(sort * s) override; ptr_vector const & get_universe(sort * s) const override; unsigned get_num_uninterpreted_sorts() const override; @@ -78,25 +81,21 @@ public: bool m_old_completion; model& m_model; public: - scoped_model_completion(model& m, bool c): + scoped_model_completion(model& m, bool c): m_old_completion(m.m_mev.get_model_completion()), m_model(m) { m.set_model_completion(c); } -#if 0 - scoped_model_completion(model_ref& m, bool c): + scoped_model_completion(model_ref& m, bool c): m_old_completion(m->m_mev.get_model_completion()), m_model(*m.get()) { m->set_model_completion(c); } -#endif ~scoped_model_completion() { m_model.set_model_completion(m_old_completion); } }; - + }; -typedef ref model_ref; #endif /* MODEL_H_ */ - From fffc8489bff01631591aea95be1e5b18e4c938dd Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 16 Jun 2018 13:43:30 -0700 Subject: [PATCH 335/364] Switched compute_implicant_literals to use new model API --- src/api/api_qe.cpp | 7 +- src/muz/spacer/spacer_context.cpp | 8 +- src/muz/spacer/spacer_util.cpp | 309 +++++++++++++++--------------- src/muz/spacer/spacer_util.h | 46 ++--- 4 files changed, 188 insertions(+), 182 deletions(-) diff --git a/src/api/api_qe.cpp b/src/api/api_qe.cpp index 2516aacfb..0073ef274 100644 --- a/src/api/api_qe.cpp +++ b/src/api/api_qe.cpp @@ -52,7 +52,7 @@ extern "C" Z3_TRY; LOG_Z3_qe_model_project (c, m, num_bounds, bound, body); RESET_ERROR_CODE(); - + app_ref_vector vars(mk_c(c)->m ()); if (!to_apps(num_bounds, bound, vars)) { SET_ERROR_CODE (Z3_INVALID_ARG); @@ -119,11 +119,8 @@ extern "C" facts.push_back (to_expr (fml)); flatten_and (facts); - spacer::model_evaluator_util mev (mk_c(c)->m()); - mev.set_model (*model); - expr_ref_vector lits (mk_c(c)->m()); - spacer::compute_implicant_literals (mev, facts, lits); + spacer::compute_implicant_literals (*model, facts, lits); expr_ref result (mk_c(c)->m ()); result = mk_and (lits); diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 03093ab63..db1153de3 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -366,7 +366,7 @@ pob *derivation::create_next_child () // get an implicant of the summary expr_ref_vector u(m), lits (m); u.push_back (rf->get ()); - compute_implicant_literals (mev, u, lits); + compute_implicant_literals (*model, u, lits); expr_ref v(m); v = mk_and (lits); @@ -1172,7 +1172,7 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, // -- pick an implicant expr_ref_vector lits(m); - compute_implicant_literals (mev, summary, lits); + compute_implicant_literals (*mev.get_model(), summary, lits); return mk_and(lits); } @@ -3599,7 +3599,7 @@ reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, if (ctx.reach_dnf()) { expr_ref_vector u(m), lits(m); u.push_back (res); - compute_implicant_literals (mev, u, lits); + compute_implicant_literals (*mev.get_model(), u, lits); res = mk_and (lits); } @@ -3670,7 +3670,7 @@ bool context::create_children(pob& n, datalog::rule const& r, forms.push_back(pt.get_transition(r)); forms.push_back(n.post()); - compute_implicant_literals (mev, forms, lits); + compute_implicant_literals (*mev.get_model(), forms, lits); expr_ref phi = mk_and (lits); // primed variables of the head diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index f53577b5b..3eb18b0e5 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -405,67 +405,67 @@ namespace spacer { namespace { class implicant_picker { - model_evaluator_util &m_mev; + model &m_model; ast_manager &m; arith_util m_arith; expr_ref_vector m_todo; expr_mark m_visited; + // add literal to the implicant + // applies lightweight normalization + void add_literal(expr *e, expr_ref_vector &out) { + SASSERT(m.is_bool(e)); - void add_literal (expr *e, expr_ref_vector &out) { - SASSERT (m.is_bool (e)); + expr_ref res(m), v(m); + v = m_model(e); + // the literal must have a value + SASSERT(m.is_true(v) || m.is_false(v)); - expr_ref res (m), v(m); - m_mev.eval (e, v, false); - SASSERT (m.is_true (v) || m.is_false (v)); + res = m.is_false(v) ? m.mk_not(e) : e; - res = m.is_false (v) ? m.mk_not (e) : e; - - if (m.is_distinct (res)) { - // -- (distinct a b) == (not (= a b)) + if (m.is_distinct(res)) { + // --(distinct a b) == (not (= a b)) if (to_app(res)->get_num_args() == 2) { - res = m.mk_eq (to_app(res)->get_arg(0), to_app(res)->get_arg(1)); - res = m.mk_not (res); + res = m.mk_eq(to_app(res)->get_arg(0), + to_app(res)->get_arg(1)); + res = m.mk_not(res); } } expr *nres, *f1, *f2; if (m.is_not(res, nres)) { - // -- (not (xor a b)) == (= a b) + // --(not (xor a b)) == (= a b) if (m.is_xor(nres, f1, f2)) - { res = m.mk_eq(f1, f2); } - + res = m.mk_eq(f1, f2); // -- split arithmetic inequality - else if (m.is_eq (nres, f1, f2) && m_arith.is_int_real (f1)) { + else if (m.is_eq(nres, f1, f2) && m_arith.is_int_real(f1)) { expr_ref u(m); u = m_arith.mk_lt(f1, f2); - if (m_mev.eval (u, v, false) && m.is_true (v)) - { res = u; } - else - { res = m_arith.mk_lt(f2, f1); } + res = m_model.is_true(u) ? u : m_arith.mk_lt(f2, f1); } } - if (!m_mev.is_true (res)) { - verbose_stream() << "Bad literal: " << mk_pp(res, m) << "\n"; + if (!m_model.is_true(res)) { + verbose_stream() << "Bad literal: " << res << "\n"; } - SASSERT (m_mev.is_true (res)); - out.push_back (res); + SASSERT(m_model.is_true(res)); + out.push_back(res); } void process_app(app *a, expr_ref_vector &out) { - if (m_visited.is_marked(a)) { return; } - SASSERT (m.is_bool (a)); + if (m_visited.is_marked(a)) return; + SASSERT(m.is_bool(a)); expr_ref v(m); - m_mev.eval (a, v, false); + v = m_model(a); bool is_true = m.is_true(v); if (!is_true && !m.is_false(v)) return; expr *na, *f1, *f2, *f3; - if (m.is_true(a) || m.is_false(a)) { + SASSERT(!m.is_false(a)); + if (m.is_true(a)) { // noop } else if (a->get_family_id() != m.get_basic_family_id()) { @@ -479,14 +479,15 @@ namespace { } else if (m.is_distinct(a)) { if (!is_true) { - f1 = qe::project_plugin::pick_equality(m, *m_mev.get_model(), a); + f1 = qe::project_plugin::pick_equality(m, m_model, a); m_todo.push_back(f1); } else if (a->get_num_args() == 2) { add_literal(a, out); } else { - m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), a->get_args())); + m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), + a->get_args())); } } else if (m.is_and(a)) { @@ -494,8 +495,8 @@ namespace { m_todo.append(a->get_num_args(), a->get_args()); } else { - for (expr* e : *a) { - if (m_mev.is_false(e)) { + for(expr* e : *a) { + if (m_model.is_false(e)) { m_todo.push_back(e); break; } @@ -506,17 +507,19 @@ namespace { if (!is_true) m_todo.append(a->get_num_args(), a->get_args()); else { - for (expr * e : *a) { - if (m_mev.is_true(e)) { + for(expr * e : *a) { + if (m_model.is_true(e)) { m_todo.push_back(e); break; } } } } - else if (m.is_eq(a, f1, f2) || (is_true && m.is_not(a, na) && m.is_xor (na, f1, f2))) { + else if (m.is_eq(a, f1, f2) || + (is_true && m.is_not(a, na) && m.is_xor(na, f1, f2))) { if (!m.are_equal(f1, f2) && !m.are_distinct(f1, f2)) { - if (m.is_bool(f1) && (!is_uninterp_const(f1) || !is_uninterp_const(f2))) + if (m.is_bool(f1) && + (!is_uninterp_const(f1) || !is_uninterp_const(f2))) m_todo.append(a->get_num_args(), a->get_args()); else add_literal(a, out); @@ -526,19 +529,19 @@ namespace { if (m.are_equal(f2, f3)) { m_todo.push_back(f2); } - else if (m_mev.is_true (f2) && m_mev.is_true (f3)) { + else if (m_model.is_true(f2) && m_model.is_true(f3)) { m_todo.push_back(f2); m_todo.push_back(f3); } - else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { + else if (m_model.is_false(f2) && m_model.is_false(f3)) { m_todo.push_back(f2); m_todo.push_back(f3); } - else if (m_mev.is_true(f1)) { + else if (m_model.is_true(f1)) { m_todo.push_back(f1); m_todo.push_back(f2); } - else if (m_mev.is_false(f1)) { + else if (m_model.is_false(f1)) { m_todo.push_back(f1); m_todo.push_back(f3); } @@ -548,16 +551,18 @@ namespace { } else if (m.is_implies(a, f1, f2)) { if (is_true) { - if (m_mev.is_true(f2)) + if (m_model.is_true(f2)) m_todo.push_back(f2); - else if (m_mev.is_false(f1)) + else if (m_model.is_false(f1)) m_todo.push_back(f1); } else m_todo.append(a->get_num_args(), a->get_args()); } else { - IF_VERBOSE(0, verbose_stream () << "Unexpected expression: " << mk_pp(a, m) << "\n"); + IF_VERBOSE(0, + verbose_stream() << "Unexpected expression: " + << mk_pp(a, m) << "\n"); UNREACHABLE(); } } @@ -574,70 +579,72 @@ namespace { m_todo.pop_back(); process_app(a, out); m_visited.mark(a, true); - } while (!m_todo.empty()); + } while(!m_todo.empty()); } bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) { m_visited.reset(); - bool is_true = m_mev.is_true (in); + bool is_true = m_model.is_true(in); - for (expr* e : in) { - if (is_true || m_mev.is_true(e)) { + for(expr* e : in) { + if (is_true || m_model.is_true(e)) { pick_literals(e, out); } } - m_visited.reset (); + m_visited.reset(); return is_true; } public: - implicant_picker (model_evaluator_util &mev) : - m_mev (mev), m (m_mev.get_ast_manager ()), m_arith(m), m_todo(m) {} + implicant_picker(model &mdl) : + m_model(mdl), m(m_model.get_manager()), m_arith(m), m_todo(m) {} - void operator() (expr_ref_vector &in, expr_ref_vector& out) { - pick_implicant (in, out); + void operator()(expr_ref_vector &in, expr_ref_vector& out) { + model::scoped_model_completion _sc_(m_model, false); + pick_implicant(in, out); } }; } - void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, + void compute_implicant_literals(model &mdl, + expr_ref_vector &formula, expr_ref_vector &res) { // flatten the formula and remove all trivial literals - // TBD: not clear why there is a dependence on it (other than + // TBD: not clear why there is a dependence on it(other than // not handling of Boolean constants by implicant_picker), however, // it was a source of a problem on a benchmark flatten_and(formula); if (formula.empty()) {return;} - implicant_picker ipick (mev); - ipick (formula, res); + implicant_picker ipick(mdl); + ipick(formula, res); } void simplify_bounds_old(expr_ref_vector& cube) { ast_manager& m = cube.m(); scoped_no_proof _no_pf_(m); goal_ref g(alloc(goal, m, false, false, false)); - for (expr* c : cube) + for(expr* c : cube) g->assert_expr(c); goal_ref_buffer result; tactic_ref simplifier = mk_arith_bounds_tactic(m); - (*simplifier)(g, result); + (*simplifier)(g, result); SASSERT(result.size() == 1); goal* r = result[0]; cube.reset(); - for (unsigned i = 0; i < r->size(); ++i) { + for(unsigned i = 0; i < r->size(); ++i) { cube.push_back(r->form(i)); } } - void simplify_bounds_new (expr_ref_vector &cube) { + void simplify_bounds_new(expr_ref_vector &cube) { ast_manager &m = cube.m(); scoped_no_proof _no_pf_(m); goal_ref g(alloc(goal, m, false, false, false)); - for (expr* c : cube) + for(expr* c : cube) g->assert_expr(c); goal_ref_buffer goals; @@ -645,12 +652,12 @@ namespace { tactic_ref prop_bounds = mk_propagate_ineqs_tactic(m); tactic_ref t = and_then(prop_values.get(), prop_bounds.get()); - (*t)(g, goals); + (*t)(g, goals); SASSERT(goals.size() == 1); g = goals[0]; cube.reset(); - for (unsigned i = 0; i < g->size(); ++i) { + for(unsigned i = 0; i < g->size(); ++i) { cube.push_back(g->form(i)); } } @@ -664,86 +671,86 @@ namespace { ast_manager &m; arith_util m_util; - adhoc_rewriter_cfg (ast_manager &manager) : m(manager), m_util(m) {} + adhoc_rewriter_cfg(ast_manager &manager) : m(manager), m_util(m) {} bool is_le(func_decl const * n) const { return m_util.is_le(n); } bool is_ge(func_decl const * n) const { return m_util.is_ge(n); } - br_status reduce_app (func_decl * f, unsigned num, expr * const * args, + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { expr * e; if (is_le(f)) - return mk_le_core (args[0], args[1], result); + return mk_le_core(args[0], args[1], result); if (is_ge(f)) - return mk_ge_core (args[0], args[1], result); - if (m.is_not(f) && m.is_not (args[0], e)) { + return mk_ge_core(args[0], args[1], result); + if (m.is_not(f) && m.is_not(args[0], e)) { result = e; return BR_DONE; } return BR_FAILED; } - br_status mk_le_core (expr *arg1, expr * arg2, expr_ref & result) { - // t <= -1 ==> t < 0 ==> ! (t >= 0) - if (m_util.is_int (arg1) && m_util.is_minus_one (arg2)) { - result = m.mk_not (m_util.mk_ge (arg1, mk_zero ())); + br_status mk_le_core(expr *arg1, expr * arg2, expr_ref & result) { + // t <= -1 ==> t < 0 ==> !(t >= 0) + if (m_util.is_int(arg1) && m_util.is_minus_one(arg2)) { + result = m.mk_not(m_util.mk_ge(arg1, mk_zero())); return BR_DONE; } return BR_FAILED; } - br_status mk_ge_core (expr * arg1, expr * arg2, expr_ref & result) { - // t >= 1 ==> t > 0 ==> ! (t <= 0) - if (m_util.is_int (arg1) && is_one (arg2)) { + br_status mk_ge_core(expr * arg1, expr * arg2, expr_ref & result) { + // t >= 1 ==> t > 0 ==> !(t <= 0) + if (m_util.is_int(arg1) && is_one(arg2)) { - result = m.mk_not (m_util.mk_le (arg1, mk_zero ())); + result = m.mk_not(m_util.mk_le(arg1, mk_zero())); return BR_DONE; } return BR_FAILED; } - expr * mk_zero () {return m_util.mk_numeral (rational (0), true);} - bool is_one (expr const * n) const { - rational val; return m_util.is_numeral (n, val) && val.is_one (); + expr * mk_zero() {return m_util.mk_numeral(rational(0), true);} + bool is_one(expr const * n) const { + rational val; return m_util.is_numeral(n, val) && val.is_one(); } }; - void normalize (expr *e, expr_ref &out, + void normalize(expr *e, expr_ref &out, bool use_simplify_bounds, bool use_factor_eqs) { params_ref params; // arith_rewriter - params.set_bool ("sort_sums", true); - params.set_bool ("gcd_rounding", true); - params.set_bool ("arith_lhs", true); + params.set_bool("sort_sums", true); + params.set_bool("gcd_rounding", true); + params.set_bool("arith_lhs", true); // poly_rewriter - params.set_bool ("som", true); - params.set_bool ("flat", true); + params.set_bool("som", true); + params.set_bool("flat", true); // apply rewriter th_rewriter rw(out.m(), params); - rw (e, out); + rw(e, out); - adhoc_rewriter_cfg adhoc_cfg(out.m ()); - rewriter_tpl adhoc_rw (out.m (), false, adhoc_cfg); - adhoc_rw (out.get (), out); + adhoc_rewriter_cfg adhoc_cfg(out.m()); + rewriter_tpl adhoc_rw(out.m(), false, adhoc_cfg); + adhoc_rw(out.get(), out); if (out.m().is_and(out)) { expr_ref_vector v(out.m()); - flatten_and (out, v); + flatten_and(out, v); if (v.size() > 1) { // sort arguments of the top-level and - std::stable_sort (v.c_ptr(), v.c_ptr() + v.size(), ast_lt_proc()); + std::stable_sort(v.c_ptr(), v.c_ptr() + v.size(), ast_lt_proc()); if (use_simplify_bounds) { // remove redundant inequalities - simplify_bounds (v); + simplify_bounds(v); } if (use_factor_eqs) { // -- refactor equivalence classes and choose a representative qe::term_graph egraph(out.m()); - egraph.add_lits (v); + egraph.add_lits(v); v.reset(); egraph.to_lits(v); } @@ -755,10 +762,10 @@ namespace { << mk_and(v) << "\n";); TRACE("spacer_normalize", qe::term_graph egraph(out.m()); - for (expr* e : v) egraph.add_lit (to_app(e)); + for(expr* e : v) egraph.add_lit(to_app(e)); tout << "Reduced app:\n" << mk_pp(egraph.to_app(), out.m()) << "\n";); - out = mk_and (v); + out = mk_and(v); } } } @@ -768,34 +775,34 @@ namespace { ast_manager &m; arith_util m_arith; - adhoc_rewriter_rpp (ast_manager &manager) : m(manager), m_arith(m) {} + adhoc_rewriter_rpp(ast_manager &manager) : m(manager), m_arith(m) {} bool is_le(func_decl const * n) const { return m_arith.is_le(n); } bool is_ge(func_decl const * n) const { return m_arith.is_ge(n); } bool is_lt(func_decl const * n) const { return m_arith.is_lt(n); } bool is_gt(func_decl const * n) const { return m_arith.is_gt(n); } - bool is_zero (expr const * n) const {rational val; return m_arith.is_numeral(n, val) && val.is_zero();} + bool is_zero(expr const * n) const {rational val; return m_arith.is_numeral(n, val) && val.is_zero();} - br_status reduce_app (func_decl * f, unsigned num, expr * const * args, + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { br_status st = BR_FAILED; expr *e1, *e2, *e3, *e4; - // rewrites (= (+ A (* -1 B)) 0) into (= A B) - if (m.is_eq (f) && is_zero (args [1]) && - m_arith.is_add (args[0], e1, e2) && - m_arith.is_mul (e2, e3, e4) && m_arith.is_minus_one (e3)) { - result = m.mk_eq (e1, e4); + // rewrites(=(+ A(* -1 B)) 0) into(= A B) + if (m.is_eq(f) && is_zero(args [1]) && + m_arith.is_add(args[0], e1, e2) && + m_arith.is_mul(e2, e3, e4) && m_arith.is_minus_one(e3)) { + result = m.mk_eq(e1, e4); return BR_DONE; } // simplify normalized leq, where right side is different from 0 - // rewrites (<= (+ A (* -1 B)) C) into (<= A B+C) + // rewrites(<=(+ A(* -1 B)) C) into(<= A B+C) else if ((is_le(f) || is_lt(f) || is_ge(f) || is_gt(f)) && - m_arith.is_add (args[0], e1, e2) && - m_arith.is_mul (e2, e3, e4) && m_arith.is_minus_one (e3)) { + m_arith.is_add(args[0], e1, e2) && + m_arith.is_mul(e2, e3, e4) && m_arith.is_minus_one(e3)) { expr_ref rhs(m); - rhs = is_zero (args[1]) ? e4 : m_arith.mk_add(e4, args[1]); + rhs = is_zero(args[1]) ? e4 : m_arith.mk_add(e4, args[1]); if (is_le(f)) { result = m_arith.mk_le(e1, rhs); @@ -813,7 +820,7 @@ namespace { { UNREACHABLE(); } } // simplify negation of ordering predicate - else if (m.is_not (f)) { + else if (m.is_not(f)) { if (m_arith.is_lt(args[0], e1, e2)) { result = m_arith.mk_ge(e1, e2); st = BR_DONE; @@ -834,11 +841,11 @@ namespace { mk_epp::mk_epp(ast *t, ast_manager &m, unsigned indent, unsigned num_vars, char const * var_prefix) : - mk_pp (t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { + mk_pp(t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { m_epp_params.set_uint("min_alias_size", UINT_MAX); m_epp_params.set_uint("max_depth", UINT_MAX); - if (is_expr (m_ast)) { + if (is_expr(m_ast)) { rw(to_expr(m_ast), m_epp_expr); m_ast = m_epp_expr; } @@ -858,11 +865,11 @@ namespace { if (vars.size() < fv.size()) { vars.resize(fv.size()); } - for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { + for(unsigned i = 0, sz = fv.size(); i < sz; ++i) { sort *s = fv[i] ? fv[i] : m.mk_bool_sort(); vars[i] = mk_zk_const(m, i, s); var_subst vs(m, false); - vs(e, vars.size(), (expr * *) vars.c_ptr(), out); + vs(e, vars.size(),(expr * *) vars.c_ptr(), out); } } @@ -872,75 +879,75 @@ namespace { app_ref m_var; expr_ref_vector &m_res; - index_term_finder (ast_manager &mgr, app* v, expr_ref_vector &res) : m(mgr), m_array (m), m_var (v, m), m_res (res) {} - void operator() (var *n) {} - void operator() (quantifier *n) {} - void operator() (app *n) { - if (m_array.is_select (n) || m.is_eq(n)) { + index_term_finder(ast_manager &mgr, app* v, expr_ref_vector &res) : m(mgr), m_array(m), m_var(v, m), m_res(res) {} + void operator()(var *n) {} + void operator()(quantifier *n) {} + void operator()(app *n) { + if (m_array.is_select(n) || m.is_eq(n)) { unsigned i = 0; - for (expr * arg : *n) { - if ((m.is_eq(n) || i > 0) && m_var != arg) m_res.push_back (arg); + for(expr * arg : *n) { + if ((m.is_eq(n) || i > 0) && m_var != arg) m_res.push_back(arg); ++i; } } } }; - bool mbqi_project_var (model_evaluator_util &mev, app* var, expr_ref &fml) { - ast_manager &m = fml.get_manager (); + bool mbqi_project_var(model_evaluator_util &mev, app* var, expr_ref &fml) { + ast_manager &m = fml.get_manager(); expr_ref val(m); - mev.eval (var, val, false); + mev.eval(var, val, false); - TRACE ("mbqi_project_verbose", - tout << "MBQI: var: " << mk_pp (var, m) << "\n" + TRACE("mbqi_project_verbose", + tout << "MBQI: var: " << mk_pp(var, m) << "\n" << "fml: " << fml << "\n";); - expr_ref_vector terms (m); - index_term_finder finder (m, var, terms); - for_each_expr (finder, fml); + expr_ref_vector terms(m); + index_term_finder finder(m, var, terms); + for_each_expr(finder, fml); - TRACE ("mbqi_project_verbose", + TRACE("mbqi_project_verbose", tout << "terms:\n" << terms << "\n";); - for (expr * term : terms) { - expr_ref tval (m); - mev.eval (term, tval, false); + for(expr * term : terms) { + expr_ref tval(m); + mev.eval(term, tval, false); - TRACE ("mbqi_project_verbose", - tout << "term: " << mk_pp (term, m) + TRACE("mbqi_project_verbose", + tout << "term: " << mk_pp(term, m) << " tval: " << tval - << " val: " << mk_pp (val, m) << "\n";); + << " val: " << mk_pp(val, m) << "\n";); // -- if the term does not contain an occurrence of var // -- and is in the same equivalence class in the model - if (tval == val && !occurs (var, term)) { - TRACE ("mbqi_project", - tout << "MBQI: replacing " << mk_pp (var, m) << " with " << mk_pp (term, m) << "\n";); + if (tval == val && !occurs(var, term)) { + TRACE("mbqi_project", + tout << "MBQI: replacing " << mk_pp(var, m) << " with " << mk_pp(term, m) << "\n";); expr_safe_replace sub(m); - sub.insert (var, term); - sub (fml); + sub.insert(var, term); + sub(fml); return true; } } - TRACE ("mbqi_project", - tout << "MBQI: failed to eliminate " << mk_pp (var, m) << " from " << fml << "\n";); + TRACE("mbqi_project", + tout << "MBQI: failed to eliminate " << mk_pp(var, m) << " from " << fml << "\n";); return false; } - void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml) { - ast_manager &m = fml.get_manager (); + void mbqi_project(model &M, app_ref_vector &vars, expr_ref &fml) { + ast_manager &m = fml.get_manager(); model_evaluator_util mev(m); - mev.set_model (M); + mev.set_model(M); expr_ref tmp(m); // -- evaluate to initialize mev cache - mev.eval (fml, tmp, false); - tmp.reset (); + mev.eval(fml, tmp, false); + tmp.reset(); unsigned j = 0; - for (app* v : vars) - if (!mbqi_project_var (mev, v, fml)) + for(app* v : vars) + if (!mbqi_project_var(mev, v, fml)) vars[j++] = v; vars.shrink(j); } @@ -959,7 +966,7 @@ namespace { for_each_expr(cs, fml); return false; } - catch (found) { + catch(found) { return true; } } @@ -970,8 +977,8 @@ namespace { collect_indices(app_ref_vector& indices): m_indices(indices), a(indices.get_manager()) {} void operator()(expr* n) {} void operator()(app* n) { - if (a.is_select (n)) - for (unsigned i = 1; i < n->get_num_args(); ++i) + if (a.is_select(n)) + for(unsigned i = 1; i < n->get_num_args(); ++i) if (is_app(n->get_arg(i))) m_indices.push_back(to_app(n->get_arg(i))); } diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index f43195a97..a66d2dd5a 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -48,17 +48,17 @@ namespace spacer { return UINT_MAX; } - inline bool is_infty_level(unsigned lvl) { - return lvl == infty_level (); + inline bool is_infty_level(unsigned lvl) { + return lvl == infty_level (); } - inline unsigned next_level(unsigned lvl) { - return is_infty_level(lvl)?lvl:(lvl+1); + inline unsigned next_level(unsigned lvl) { + return is_infty_level(lvl)?lvl:(lvl+1); } inline unsigned prev_level (unsigned lvl) { - if (is_infty_level(lvl)) return infty_level(); - if (lvl == 0) return 0; + if (is_infty_level(lvl)) return infty_level(); + if (lvl == 0) return 0; return lvl - 1; } @@ -78,28 +78,28 @@ namespace spacer { typedef ptr_vector app_vector; typedef ptr_vector decl_vector; typedef obj_hashtable func_decl_set; - + // TBD: deprecate class model_evaluator_util { ast_manager& m; model_ref m_model; model_evaluator* m_mev; - + /// initialize with a given model. All previous state is lost. model can be NULL void reset (model *model); public: model_evaluator_util(ast_manager& m); ~model_evaluator_util(); - + void set_model(model &model) {reset (&model);} model_ref &get_model() {return m_model;} ast_manager& get_ast_manager() const {return m;} - + public: bool is_true (const expr_ref_vector &v); bool is_false(expr* x); bool is_true(expr* x); - + bool eval (const expr_ref_vector &v, expr_ref &result, bool model_completion); /// evaluates an expression bool eval (expr *e, expr_ref &result, bool model_completion); @@ -109,10 +109,10 @@ namespace spacer { /** \brief hoist non-boolean if expressions. */ - + void to_mbp_benchmark(std::ostream &out, const expr* fml, const app_ref_vector &vars); - + // TBD: deprecate by qe::mbp /** * do the following in sequence @@ -126,27 +126,29 @@ namespace spacer { bool dont_sub=false); void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map); - + // TBD: sort out void expand_literals(ast_manager &m, expr_ref_vector& conjs); - void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, expr_ref_vector &res); + void compute_implicant_literals(model &mdl, + expr_ref_vector &formula, + expr_ref_vector &res); void simplify_bounds (expr_ref_vector &lemmas); void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false); - - /** + + /** * Ground expression by replacing all free variables by skolem * constants. On return, out is the resulting expression, and vars is * a map from variable ids to corresponding skolem constants. */ void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); - + void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml); - + bool contains_selects (expr* fml, ast_manager& m); void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m); - + void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix); - + /** * extended pretty-printer * used for debugging @@ -156,7 +158,7 @@ namespace spacer { params_ref m_epp_params; expr_ref m_epp_expr; mk_epp(ast *t, ast_manager &m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); - void rw(expr *e, expr_ref &out); + void rw(expr *e, expr_ref &out); }; } From 5e65b37f25d4a536adc99ec25a561d9100d1c68b Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 16 Jun 2018 13:58:58 -0700 Subject: [PATCH 336/364] Switch spacer::qe_project to new model API --- src/api/api_qe.cpp | 2 +- src/muz/spacer/spacer_context.cpp | 12 ++-- src/muz/spacer/spacer_context.h | 2 +- src/muz/spacer/spacer_util.cpp | 95 +++++++++++++------------------ src/muz/spacer/spacer_util.h | 10 +++- 5 files changed, 54 insertions(+), 67 deletions(-) diff --git a/src/api/api_qe.cpp b/src/api/api_qe.cpp index 0073ef274..92517b02b 100644 --- a/src/api/api_qe.cpp +++ b/src/api/api_qe.cpp @@ -62,7 +62,7 @@ extern "C" expr_ref result (mk_c(c)->m ()); result = to_expr (body); model_ref model (to_model_ref (m)); - spacer::qe_project (mk_c(c)->m (), vars, result, model); + spacer::qe_project (mk_c(c)->m (), vars, result, *model); mk_c(c)->save_ast_trail (result.get ()); return of_expr (result.get ()); diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index db1153de3..8853a2e45 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -265,7 +265,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) verbose_stream ()); vars.append(m_evars); m_evars.reset(); - pt().mbp(vars, m_trans, mev.get_model(), + pt().mbp(vars, m_trans, *mev.get_model(), true, pt().get_context().use_ground_pob()); m_evars.append (vars); vars.reset(); @@ -294,7 +294,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) verbose_stream ()); // include m_evars in case they can eliminated now as well vars.append(m_evars); - pt().mbp(vars, post, mev.get_model(), + pt().mbp(vars, post, *mev.get_model(), true, pt().get_context().use_ground_pob()); //qe::reduce_array_selects (*mev.get_model (), post); } @@ -398,7 +398,7 @@ pob *derivation::create_next_child () if (!vars.empty ()) { vars.append(m_evars); m_evars.reset(); - this->pt().mbp(vars, m_trans, mev.get_model(), + this->pt().mbp(vars, m_trans, *mev.get_model(), true, this->pt().get_context().use_ground_pob()); // keep track of implicitly quantified variables m_evars.append (vars); @@ -1267,7 +1267,7 @@ bool pred_transformer::is_qblocked (pob &n) { } -void pred_transformer::mbp(app_ref_vector &vars, expr_ref &fml, const model_ref &mdl, +void pred_transformer::mbp(app_ref_vector &vars, expr_ref &fml, model &mdl, bool reduce_all_selects, bool force) { scoped_watch _t_(m_mbp_watch); qe_project(m, vars, fml, mdl, reduce_all_selects, use_native_mbp(), !force); @@ -3617,7 +3617,7 @@ reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, timeit _timer1 (is_trace_enabled("spacer_timeit"), "mk_rf::qe_project", verbose_stream ()); - mbp(vars, res, mev.get_model(), false, true /* force or skolemize */); + mbp(vars, res, *mev.get_model(), false, true /* force or skolemize */); } @@ -3685,7 +3685,7 @@ bool context::create_children(pob& n, datalog::rule const& r, // skolems of the pob n.get_skolems(vars); - n.pt().mbp(vars, phi, mev.get_model (), true, use_ground_pob()); + n.pt().mbp(vars, phi, *mev.get_model (), true, use_ground_pob()); //qe::reduce_array_selects (*mev.get_model (), phi1); SASSERT (!m_ground_pob || vars.empty ()); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 760f38f69..c502baa63 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -530,7 +530,7 @@ public: bool is_qblocked (pob &n); /// \brief interface to Model Based Projection - void mbp(app_ref_vector &vars, expr_ref &fml, const model_ref &mdl, + void mbp(app_ref_vector &vars, expr_ref &fml, model &mdl, bool reduce_all_selects, bool force = false); void updt_solver(prop_solver *solver); diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 3eb18b0e5..b9768f4be 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -128,17 +128,11 @@ namespace spacer { } void subst_vars(ast_manager& m, - app_ref_vector const& vars, - model* M, expr_ref& fml) { - expr_safe_replace sub (m); - model_evaluator_util mev (m); - mev.set_model(*M); - for (app * v : vars) { - expr_ref val (m); - VERIFY(mev.eval (v, val, true)); - sub.insert (v, val); - } - sub (fml); + app_ref_vector const& vars, model& mdl, expr_ref& fml) { + model::scoped_model_completion _sc_(mdl, true); + expr_safe_replace sub(m); + for (app * v : vars) sub.insert (v, mdl(v)); + sub(fml); } void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) { @@ -161,16 +155,14 @@ namespace spacer { } void qe_project_z3 (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects, bool use_native_mbp, + model & mdl, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { params_ref p; p.set_bool("reduce_all_selects", reduce_all_selects); p.set_bool("dont_sub", dont_sub); qe::mbp mbp(m, p); - // TODO: deal with const - model *mdl = const_cast(M.get()); - mbp.spacer(vars, *mdl, fml); + mbp.spacer(vars, mdl, fml); } /* @@ -178,7 +170,7 @@ namespace spacer { * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays */ void qe_project_spacer (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects, bool use_native_mbp, + model& mdl, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { th_rewriter rw (m); TRACE ("spacer_mbp", @@ -221,30 +213,29 @@ namespace spacer { // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { if (m.is_bool (v)) { - // obtain the interpretation of the ith var using model completion - VERIFY (M->eval (v, bval, true)); - bool_sub.insert (v, bval); + // obtain the interpretation of the ith var + // using model completion + model::scoped_model_completion _sc_(mdl, true); + bool_sub.insert (v, mdl(v)); } else if (arr_u.is_array(v)) { - array_vars.push_back (v); + array_vars.push_back(v); } else { - SASSERT (ari_u.is_int (v) || ari_u.is_real (v)); - arith_vars.push_back (v); + SASSERT (ari_u.is_int(v) || ari_u.is_real(v)); + arith_vars.push_back(v); } } // substitute Booleans if (!bool_sub.empty()) { - bool_sub (fml); + bool_sub(fml); // -- bool_sub is not simplifying rw (fml); - SASSERT (!m.is_false (fml)); - TRACE ("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); - bool_sub.reset (); + SASSERT(!m.is_false (fml)); + TRACE("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); + bool_sub.reset(); } - TRACE ("spacer_mbp", - tout << "Array vars:\n"; - tout << array_vars;); + TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); vars.reset (); @@ -253,7 +244,7 @@ namespace spacer { scoped_no_proof _sp (m); // -- local rewriter that is aware of current proof mode th_rewriter srw(m); - spacer_qe::array_project (*M.get (), array_vars, fml, vars, reduce_all_selects); + spacer_qe::array_project (mdl, array_vars, fml, vars, reduce_all_selects); SASSERT (array_vars.empty ()); srw (fml); SASSERT (!m.is_false (fml)); @@ -261,10 +252,9 @@ namespace spacer { TRACE ("spacer_mbp", tout << "extended model:\n"; - model_pp (tout, *M); + model_pp (tout, mdl); tout << "Auxiliary variables of index and value sorts:\n"; - tout << vars; - ); + tout << vars;); if (vars.empty()) { break; } } @@ -273,39 +263,32 @@ namespace spacer { if (!arith_vars.empty ()) { TRACE ("spacer_mbp", tout << "Arith vars:\n" << arith_vars;); - // XXX Does not seem to have an effect - // qe_lite qe(m); - // qe (arith_vars, fml); - // TRACE ("spacer_mbp", - // tout << "After second qelite: " << - // mk_pp (fml, m) << "\n";); - if (use_native_mbp) { qe::mbp mbp (m); expr_ref_vector fmls(m); flatten_and (fml, fmls); - mbp (true, arith_vars, *M.get (), fmls); - fml = mk_and (fmls); - SASSERT (arith_vars.empty ()); + mbp (true, arith_vars, mdl, fmls); + fml = mk_and(fmls); + SASSERT(arith_vars.empty ()); } else { scoped_no_proof _sp (m); - spacer_qe::arith_project (*M.get (), arith_vars, fml); - } + spacer_qe::arith_project (mdl, arith_vars, fml); + } TRACE ("spacer_mbp", - tout << "Projected arith vars:\n" << mk_pp (fml, m) << "\n"; + tout << "Projected arith vars:\n" << fml << "\n"; tout << "Remaining arith vars:\n" << arith_vars << "\n";); SASSERT (!m.is_false (fml)); } if (!arith_vars.empty ()) { - mbqi_project (*M.get(), arith_vars, fml); + mbqi_project (mdl, arith_vars, fml); } // substitute any remaining arith vars if (!dont_sub && !arith_vars.empty ()) { - subst_vars (m, arith_vars, M.get(), fml); + subst_vars (m, arith_vars, mdl, fml); TRACE ("spacer_mbp", tout << "After substituting remaining arith vars:\n"; tout << mk_pp (fml, m) << "\n"; @@ -315,11 +298,9 @@ namespace spacer { } DEBUG_CODE ( - model_evaluator_util mev (m); - expr_ref v(m); - mev.set_model(*M.get()); - SASSERT (mev.eval (fml, v, false)); - SASSERT (m.is_true (v)); + model_evaluator mev(mdl); + mev.set_model_completion(false); + SASSERT(mev.is_true(fml)); ); vars.reset (); @@ -343,12 +324,14 @@ namespace spacer { } void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects, bool use_native_mbp, + model &mdl, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { if (use_native_mbp) - qe_project_z3(m, vars, fml, M, reduce_all_selects, use_native_mbp, dont_sub); + qe_project_z3(m, vars, fml, mdl, + reduce_all_selects, use_native_mbp, dont_sub); else - qe_project_spacer(m, vars, fml, M, reduce_all_selects, use_native_mbp, dont_sub); + qe_project_spacer(m, vars, fml, mdl, + reduce_all_selects, use_native_mbp, dont_sub); } void expand_literals(ast_manager &m, expr_ref_vector& conjs) { diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index a66d2dd5a..c3dbbd042 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -121,11 +121,15 @@ namespace spacer { * 3. use MBP for remaining array and arith variables * 4. for any remaining arith variables, substitute using M */ - void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects=false, bool native_mbp=false, + void qe_project (ast_manager& m, app_ref_vector& vars, + expr_ref& fml, model &mdl, + bool reduce_all_selects=false, + bool native_mbp=false, bool dont_sub=false); - void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map); + // deprecate + void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + model_ref& M, expr_map& map); // TBD: sort out void expand_literals(ast_manager &m, expr_ref_vector& conjs); From f226c6682bbf1545cc666177f7696af937d3971d Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 16 Jun 2018 14:09:24 -0700 Subject: [PATCH 337/364] Switched derivation to new model API --- src/muz/spacer/spacer_context.cpp | 28 ++++++++++++++-------------- src/muz/spacer/spacer_context.h | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 8853a2e45..496672b96 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -195,10 +195,10 @@ void derivation::add_premise (pred_transformer &pt, -pob *derivation::create_first_child (model_evaluator_util &mev) { +pob *derivation::create_first_child (model &mdl) { if (m_premises.empty()) { return nullptr; } m_active = 0; - return create_next_child(mev); + return create_next_child(mdl); } void derivation::exist_skolemize(expr* fml, app_ref_vector &vars, expr_ref &res) { @@ -236,7 +236,7 @@ void derivation::exist_skolemize(expr* fml, app_ref_vector &vars, expr_ref &res) sub(fml, res); } -pob *derivation::create_next_child (model_evaluator_util &mev) +pob *derivation::create_next_child(model &mdl) { timeit _timer (is_trace_enabled("spacer_timeit"), "spacer::derivation::create_next_child", @@ -265,13 +265,13 @@ pob *derivation::create_next_child (model_evaluator_util &mev) verbose_stream ()); vars.append(m_evars); m_evars.reset(); - pt().mbp(vars, m_trans, *mev.get_model(), + pt().mbp(vars, m_trans, mdl, true, pt().get_context().use_ground_pob()); m_evars.append (vars); vars.reset(); } - if (!mev.is_true (m_premises[m_active].get_summary())) { + if (!mdl.is_true(m_premises[m_active].get_summary())) { IF_VERBOSE(1, verbose_stream() << "Summary unexpectendly not true\n";); return nullptr; } @@ -294,7 +294,7 @@ pob *derivation::create_next_child (model_evaluator_util &mev) verbose_stream ()); // include m_evars in case they can eliminated now as well vars.append(m_evars); - pt().mbp(vars, post, *mev.get_model(), + pt().mbp(vars, post, mdl, true, pt().get_context().use_ground_pob()); //qe::reduce_array_selects (*mev.get_model (), post); } @@ -337,7 +337,6 @@ pob *derivation::create_next_child () // construct a new model consistent with the must summary of m_active premise pred_transformer &pt = m_premises[m_active].pt (); - model_ref model; ast_manager &m = get_ast_manager (); manager &pm = get_manager (); @@ -355,18 +354,19 @@ pob *derivation::create_next_child () // if not true, bail out, the must summary of m_active is not strong enough // this is possible if m_post was weakened for some reason - if (!pt.is_must_reachable(mk_and(summaries), &model)) { return nullptr; } + model_ref mdl; + if (!pt.is_must_reachable(mk_and(summaries), &mdl)) { return nullptr; } model_evaluator_util mev (m); - mev.set_model (*model); + mev.set_model (*mdl); // find must summary used reach_fact *rf = pt.get_used_rf (mev, true); // get an implicant of the summary - expr_ref_vector u(m), lits (m); + expr_ref_vector u(m), lits(m); u.push_back (rf->get ()); - compute_implicant_literals (*model, u, lits); + compute_implicant_literals (*mdl, u, lits); expr_ref v(m); v = mk_and (lits); @@ -398,7 +398,7 @@ pob *derivation::create_next_child () if (!vars.empty ()) { vars.append(m_evars); m_evars.reset(); - this->pt().mbp(vars, m_trans, *mev.get_model(), + this->pt().mbp(vars, m_trans, *mdl, true, this->pt().get_context().use_ground_pob()); // keep track of implicitly quantified variables m_evars.append (vars); @@ -408,7 +408,7 @@ pob *derivation::create_next_child () m_active++; - return create_next_child (mev); + return create_next_child (*mdl); } /// derivation::premise @@ -3732,7 +3732,7 @@ bool context::create_children(pob& n, datalog::rule const& r, } // create post for the first child and add to queue - pob* kid = deriv->create_first_child (mev); + pob* kid = deriv->create_first_child (*mev.get_model()); // -- failed to create derivation, cleanup and bail out if (!kid) { diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index c502baa63..93704aefc 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -740,7 +740,7 @@ class derivation { app_ref_vector m_evars; /// -- create next child using given model as the guide /// -- returns NULL if there is no next child - pob* create_next_child (model_evaluator_util &mev); + pob* create_next_child (model &mdl); /// existentially quantify vars and skolemize the result void exist_skolemize(expr *fml, app_ref_vector &vars, expr_ref &res); public: @@ -752,7 +752,7 @@ public: /// creates the first child. Must be called after all the premises /// are added. The model must be valid for the premises /// Returns NULL if no child exits - pob *create_first_child (model_evaluator_util &mev); + pob *create_first_child (model &mdl); /// Create the next child. Must summary of the currently active /// premise must be consistent with the transition relation From a222b6d41f1a8e5573eae52d8fb984ec47640821 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 16 Jun 2018 14:17:33 -0700 Subject: [PATCH 338/364] Switch reach_fact to new model API --- src/muz/spacer/spacer_context.cpp | 43 ++++++++++++++----------------- src/muz/spacer/spacer_context.h | 9 +++---- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 496672b96..89d438b73 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -361,7 +361,7 @@ pob *derivation::create_next_child () mev.set_model (*mdl); // find must summary used - reach_fact *rf = pt.get_used_rf (mev, true); + reach_fact *rf = pt.get_used_rf (*mev.get_model(), true); // get an implicant of the summary expr_ref_vector u(m), lits(m); @@ -795,27 +795,24 @@ bool pred_transformer::is_must_reachable(expr* state, model_ref* model) -reach_fact* pred_transformer::get_used_rf (model_evaluator_util& mev, - bool all) { +reach_fact* pred_transformer::get_used_rf (model& mdl, bool all) { expr_ref v (m); + model::scoped_model_completion _sc_(mdl, false); for (auto *rf : m_reach_facts) { if (!all && rf->is_init()) continue; - VERIFY(mev.eval (rf->tag(), v, false)); - if (m.is_false(v)) return rf; + if (mdl.is_false(rf->tag())) return rf; } UNREACHABLE(); return nullptr; } -reach_fact *pred_transformer::get_used_origin_rf (model_evaluator_util& mev, - unsigned oidx) { +reach_fact *pred_transformer::get_used_origin_rf(model& mdl, unsigned oidx) { expr_ref b(m), v(m); - + model::scoped_model_completion _sc_(mdl, false); for (auto *rf : m_reach_facts) { pm.formula_n2o (rf->tag(), v, oidx); - VERIFY(mev.eval (v, b, false)); - if (m.is_false (b)) return rf; + if (mdl.is_false(v)) return rf; } UNREACHABLE(); return nullptr; @@ -1139,12 +1136,13 @@ expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) * * returns an implicant of the summary */ -expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, +expr_ref pred_transformer::get_origin_summary (model &mdl, unsigned level, unsigned oidx, bool must, const ptr_vector **aux) { + model::scoped_model_completion _sc_(mdl, false); expr_ref_vector summary (m); expr_ref v(m); @@ -1153,7 +1151,7 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, // -- no auxiliary variables in lemmas *aux = nullptr; } else { // find must summary to use - reach_fact *f = get_used_origin_rf (mev, oidx); + reach_fact *f = get_used_origin_rf(mdl, oidx); summary.push_back (f->get ()); *aux = &f->aux_vars (); } @@ -1167,13 +1165,11 @@ expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, } // bail out of if the model is insufficient - if (!mev.is_true(summary)) - return expr_ref(m); + if (!mdl.is_true(summary)) return expr_ref(m); // -- pick an implicant expr_ref_vector lits(m); - compute_implicant_literals (*mev.get_model(), summary, lits); - + compute_implicant_literals (mdl, summary, lits); return mk_and(lits); } @@ -3199,7 +3195,7 @@ bool context::is_reachable(pob &n) mev.set_model(*model); // -- update must summary if (r && r->get_uninterpreted_tail_size () > 0) { - reach_fact_ref rf = n.pt().mk_rf (n, mev, *r); + reach_fact_ref rf = n.pt().mk_rf (n, *mev.get_model(), *r); n.pt ().add_rf (rf.get ()); } @@ -3340,7 +3336,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) if (is_concrete) { // -- update must summary if (r && r->get_uninterpreted_tail_size() > 0) { - reach_fact_ref rf = n.pt().mk_rf (n, mev, *r); + reach_fact_ref rf = n.pt().mk_rf (n, *mev.get_model(), *r); checkpoint (); n.pt ().add_rf (rf.get ()); checkpoint (); @@ -3554,8 +3550,7 @@ bool context::propagate(unsigned min_prop_lvl, return false; } -reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, - const datalog::rule& r) +reach_fact *pred_transformer::mk_rf(pob& n, model &mdl, const datalog::rule& r) { SASSERT(&n.pt() == this); timeit _timer1 (is_trace_enabled("spacer_timeit"), @@ -3576,7 +3571,7 @@ reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, pred_transformer& ch_pt = ctx.get_pred_transformer (pred); // get a reach fact of body preds used in the model expr_ref o_ch_reach (m); - reach_fact *kid = ch_pt.get_used_origin_rf (mev, i); + reach_fact *kid = ch_pt.get_used_origin_rf(mdl, i); child_reach_facts.push_back (kid); pm.formula_n2o (kid->get (), o_ch_reach, i); path_cons.push_back (o_ch_reach); @@ -3599,7 +3594,7 @@ reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, if (ctx.reach_dnf()) { expr_ref_vector u(m), lits(m); u.push_back (res); - compute_implicant_literals (*mev.get_model(), u, lits); + compute_implicant_literals (mdl, u, lits); res = mk_and (lits); } @@ -3617,7 +3612,7 @@ reach_fact *pred_transformer::mk_rf (pob& n, model_evaluator_util &mev, timeit _timer1 (is_trace_enabled("spacer_timeit"), "mk_rf::qe_project", verbose_stream ()); - mbp(vars, res, *mev.get_model(), false, true /* force or skolemize */); + mbp(vars, res, mdl, false, true /* force or skolemize */); } @@ -3722,7 +3717,7 @@ bool context::create_children(pob& n, datalog::rule const& r, const ptr_vector *aux = nullptr; expr_ref sum(m); - sum = pt.get_origin_summary (mev, prev_level(n.level()), + sum = pt.get_origin_summary (*mev.get_model(), prev_level(n.level()), j, reach_pred_used[j], &aux); if (!sum) { dealloc(deriv); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 93704aefc..394dbf7e2 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -440,10 +440,10 @@ public: bool is_must_reachable(expr* state, model_ref* model = nullptr); /// \brief Returns reachability fact active in the given model /// all determines whether initial reachability facts are included as well - reach_fact *get_used_rf(model_evaluator_util& mev, bool all = true); + reach_fact *get_used_rf(model& mdl, bool all = true); /// \brief Returns reachability fact active in the origin of the given model - reach_fact* get_used_origin_rf(model_evaluator_util &mev, unsigned oidx); - expr_ref get_origin_summary(model_evaluator_util &mev, + reach_fact* get_used_origin_rf(model &mdl, unsigned oidx); + expr_ref get_origin_summary(model &mdl, unsigned level, unsigned oidx, bool must, const ptr_vector **aux); @@ -472,8 +472,7 @@ public: /// initialize reachability facts using initial rules void init_rfs (); - reach_fact *mk_rf(pob &n, model_evaluator_util &mev, - const datalog::rule &r); + reach_fact *mk_rf(pob &n, model &mdl, const datalog::rule &r); void add_rf (reach_fact *fact); // add reachability fact reach_fact* get_last_rf () const { return m_reach_facts.back (); } expr* get_last_rf_tag () const; From 4204b6ede25e227574e431131e9fbdf2008fefbd Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Sat, 16 Jun 2018 14:40:17 -0700 Subject: [PATCH 339/364] Switch rest of spacer to new model API and remove mev_util --- src/muz/spacer/spacer_context.cpp | 50 +++++++---------- src/muz/spacer/spacer_context.h | 4 +- src/muz/spacer/spacer_pdr.cpp | 4 +- src/muz/spacer/spacer_util.cpp | 90 ++++++------------------------- src/muz/spacer/spacer_util.h | 30 +---------- 5 files changed, 41 insertions(+), 137 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 89d438b73..5d3ecb3fe 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -36,7 +36,6 @@ Notes: #include "muz/base/dl_rule_set.h" #include "smt/tactic/unit_subsumption_tactic.h" #include "model/model_smt2_pp.h" -#include "model/model_evaluator.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" @@ -356,12 +355,10 @@ pob *derivation::create_next_child () // this is possible if m_post was weakened for some reason model_ref mdl; if (!pt.is_must_reachable(mk_and(summaries), &mdl)) { return nullptr; } + mdl->set_model_completion(false); - model_evaluator_util mev (m); - mev.set_model (*mdl); // find must summary used - - reach_fact *rf = pt.get_used_rf (*mev.get_model(), true); + reach_fact *rf = pt.get_used_rf (*mdl, true); // get an implicant of the summary expr_ref_vector u(m), lits(m); @@ -2883,7 +2880,6 @@ expr_ref context::get_ground_sat_answer() // smt context to obtain local cexes ref cex_ctx = mk_smt_solver(m, params_ref::get_empty(), symbol::null); - model_evaluator_util mev (m); // preorder traversal of the query derivation tree for (unsigned curr = 0; curr < pts.size (); curr++) { @@ -2936,8 +2932,7 @@ expr_ref context::get_ground_sat_answer() model_ref local_mdl; cex_ctx->get_model (local_mdl); cex_ctx->pop (1); - - model_evaluator mev(*local_mdl); + local_mdl->set_model_completion(true); for (unsigned i = 0; i < child_pts.size(); i++) { pred_transformer& ch_pt = *(child_pts.get(i)); unsigned sig_size = ch_pt.sig_size(); @@ -2946,7 +2941,7 @@ expr_ref context::get_ground_sat_answer() for (unsigned j = 0; j < sig_size; j++) { expr_ref sig_arg(m), sig_val(m); sig_arg = m.mk_const (m_pm.o2o(ch_pt.sig(j), 0, i)); - VERIFY(mev.eval (sig_arg, sig_val, true)); + sig_val = (*local_mdl)(sig_arg); ground_fact_conjs.push_back(m.mk_eq(sig_arg, sig_val)); ground_arg_vals.push_back(sig_val); } @@ -3166,7 +3161,7 @@ bool context::is_reachable(pob &n) // used in case n is unreachable unsigned uses_level = infty_level (); - model_ref model; + model_ref mdl; // used in case n is reachable bool is_concrete; @@ -3177,7 +3172,7 @@ bool context::is_reachable(pob &n) unsigned saved = n.level (); n.m_level = infty_level (); - lbool res = n.pt().is_reachable(n, nullptr, &model, + lbool res = n.pt().is_reachable(n, nullptr, &mdl, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); n.m_level = saved; @@ -3191,11 +3186,9 @@ bool context::is_reachable(pob &n) SASSERT(res == l_true); SASSERT(is_concrete); - model_evaluator_util mev (m); - mev.set_model(*model); // -- update must summary if (r && r->get_uninterpreted_tail_size () > 0) { - reach_fact_ref rf = n.pt().mk_rf (n, *mev.get_model(), *r); + reach_fact_ref rf = n.pt().mk_rf (n, *mdl, *r); n.pt ().add_rf (rf.get ()); } @@ -3322,6 +3315,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) lbool res = n.pt ().is_reachable (n, &cube, &model, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); + if (model) model->set_model_completion(false); checkpoint (); IF_VERBOSE (1, verbose_stream () << "." << std::flush;); switch (res) { @@ -3330,13 +3324,11 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) // update stats m_stats.m_num_reuse_reach += num_reuse_reach; - model_evaluator_util mev (m); - mev.set_model (*model); // must-reachable if (is_concrete) { // -- update must summary if (r && r->get_uninterpreted_tail_size() > 0) { - reach_fact_ref rf = n.pt().mk_rf (n, *mev.get_model(), *r); + reach_fact_ref rf = n.pt().mk_rf (n, *model, *r); checkpoint (); n.pt ().add_rf (rf.get ()); checkpoint (); @@ -3374,7 +3366,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) // create a child of n out.push_back(&n); - VERIFY(create_children (n, *r, mev, reach_pred_used, out)); + VERIFY(create_children (n, *r, *model, reach_pred_used, out)); IF_VERBOSE(1, verbose_stream () << " U " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); @@ -3449,12 +3441,10 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) SASSERT(m_weak_abs); m_stats.m_expand_pob_undef++; if (r && r->get_uninterpreted_tail_size() > 0) { - model_evaluator_util mev(m); - mev.set_model(*model); // do not trust reach_pred_used for (unsigned i = 0, sz = reach_pred_used.size(); i < sz; ++i) { reach_pred_used[i] = false; } - has_new_child = create_children(n,*r,mev,reach_pred_used, out); + has_new_child = create_children(n, *r, *model, reach_pred_used, out); } IF_VERBOSE(1, verbose_stream() << " UNDEF " << std::fixed << std::setprecision(2) @@ -3640,7 +3630,7 @@ reach_fact *pred_transformer::mk_rf(pob& n, model &mdl, const datalog::rule& r) \brief create children states from model cube. */ bool context::create_children(pob& n, datalog::rule const& r, - model_evaluator_util &mev, + model &mdl, const vector &reach_pred_used, pob_ref_buffer &out) { @@ -3649,7 +3639,7 @@ bool context::create_children(pob& n, datalog::rule const& r, TRACE("spacer", tout << "Model:\n"; - model_smt2_pp(tout, m, *mev.get_model (), 0); + model_smt2_pp(tout, m, mdl, 0); tout << "\n"; tout << "Transition:\n" << mk_pp(pt.get_transition(r), m) << "\n"; tout << "Pob:\n" << mk_pp(n.post(), m) << "\n";); @@ -3665,7 +3655,7 @@ bool context::create_children(pob& n, datalog::rule const& r, forms.push_back(pt.get_transition(r)); forms.push_back(n.post()); - compute_implicant_literals (*mev.get_model(), forms, lits); + compute_implicant_literals (mdl, forms, lits); expr_ref phi = mk_and (lits); // primed variables of the head @@ -3680,7 +3670,7 @@ bool context::create_children(pob& n, datalog::rule const& r, // skolems of the pob n.get_skolems(vars); - n.pt().mbp(vars, phi, *mev.get_model (), true, use_ground_pob()); + n.pt().mbp(vars, phi, mdl, true, use_ground_pob()); //qe::reduce_array_selects (*mev.get_model (), phi1); SASSERT (!m_ground_pob || vars.empty ()); @@ -3694,7 +3684,7 @@ bool context::create_children(pob& n, datalog::rule const& r, if (m_use_gpdr && preds.size() > 1) { SASSERT(vars.empty()); - return gpdr_create_split_children(n, r, phi, mev.get_model(), out); + return gpdr_create_split_children(n, r, phi, mdl, out); } derivation *deriv = alloc(derivation, n, r, phi, vars); @@ -3717,7 +3707,7 @@ bool context::create_children(pob& n, datalog::rule const& r, const ptr_vector *aux = nullptr; expr_ref sum(m); - sum = pt.get_origin_summary (*mev.get_model(), prev_level(n.level()), + sum = pt.get_origin_summary (mdl, prev_level(n.level()), j, reach_pred_used[j], &aux); if (!sum) { dealloc(deriv); @@ -3727,7 +3717,7 @@ bool context::create_children(pob& n, datalog::rule const& r, } // create post for the first child and add to queue - pob* kid = deriv->create_first_child (*mev.get_model()); + pob* kid = deriv->create_first_child (mdl); // -- failed to create derivation, cleanup and bail out if (!kid) { @@ -3744,8 +3734,8 @@ bool context::create_children(pob& n, datalog::rule const& r, // -- not satisfy 'T && phi'. It is possible to recover from // -- that more gracefully. For now, we just remove the // -- derivation completely forcing it to be recomputed - if (m_weak_abs && (!mev.is_true(pt.get_transition(r)) || - !mev.is_true(n.post()))) + if (m_weak_abs && (!mdl.is_true(pt.get_transition(r)) || + !mdl.is_true(n.post()))) { kid->reset_derivation(); } out.push_back(kid); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 394dbf7e2..37b035b98 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -940,7 +940,7 @@ class context { bool gpdr_check_reachability(unsigned lvl, model_search &ms); bool gpdr_create_split_children(pob &n, const datalog::rule &r, expr *trans, - model_ref &mdl, + model &mdl, pob_ref_buffer &out); // Functions used by search. @@ -952,7 +952,7 @@ class context { bool is_reachable(pob &n); lbool expand_pob(pob &n, pob_ref_buffer &out); bool create_children(pob& n, const datalog::rule &r, - model_evaluator_util &mdl, + model &mdl, const vector& reach_pred_used, pob_ref_buffer &out); diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index ad2b6200d..4e6b37a0a 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -306,7 +306,7 @@ bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, expr *trans, - model_ref &mdl, + model &mdl, pob_ref_buffer &out) { pred_transformer &pt = n.pt(); ptr_vector preds; @@ -330,7 +330,7 @@ bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, expr_ref_vector lits(m); flatten_and(trans, lits); vector res(preds.size(), expr_ref_vector(m)); - _mbc(pmap, lits, *mdl.get(), res); + _mbc(pmap, lits, mdl, res); // pick an order to process children unsigned_vector kid_order; diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index b9768f4be..f63fc02d0 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -69,64 +69,6 @@ Notes: namespace spacer { - ///////////////////////// - // model_evaluator_util - // - - model_evaluator_util::model_evaluator_util(ast_manager& m) : - m(m), m_mev(nullptr) { - reset (nullptr); - } - - model_evaluator_util::~model_evaluator_util() {reset (nullptr);} - - - void model_evaluator_util::reset(model* model) { - if (m_mev) { - dealloc(m_mev); - m_mev = nullptr; - } - m_model = model; - if (m_model) - m_mev = alloc(model_evaluator, *m_model); - } - - bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { - m_mev->set_model_completion (model_completion); - try { - m_mev->operator() (e, result); - return true; - } - catch (model_evaluator_exception &ex) { - (void)ex; - TRACE("spacer_model_evaluator", tout << ex.msg () << "\n";); - return false; - } - } - - bool model_evaluator_util::eval(const expr_ref_vector &v, - expr_ref& res, bool model_completion) { - expr_ref e(m); - e = mk_and (v); - return eval(e, res, model_completion); - } - - - bool model_evaluator_util::is_true(const expr_ref_vector &v) { - expr_ref res(m); - return eval (v, res, false) && m.is_true (res); - } - - bool model_evaluator_util::is_false(expr *x) { - expr_ref res(m); - return eval(x, res, false) && m.is_false (res); - } - - bool model_evaluator_util::is_true(expr *x) { - expr_ref res(m); - return eval(x, res, false) && m.is_true (res); - } - void subst_vars(ast_manager& m, app_ref_vector const& vars, model& mdl, expr_ref& fml) { model::scoped_model_completion _sc_(mdl, true); @@ -876,36 +818,36 @@ namespace { } }; - bool mbqi_project_var(model_evaluator_util &mev, app* var, expr_ref &fml) { + bool mbqi_project_var(model &mdl, app* var, expr_ref &fml) { ast_manager &m = fml.get_manager(); + model::scoped_model_completion _sc_(mdl, false); expr_ref val(m); - mev.eval(var, val, false); + val = mdl(var); TRACE("mbqi_project_verbose", - tout << "MBQI: var: " << mk_pp(var, m) << "\n" - << "fml: " << fml << "\n";); + tout << "MBQI: var: " << mk_pp(var, m) << "\n" + << "fml: " << fml << "\n";); expr_ref_vector terms(m); index_term_finder finder(m, var, terms); for_each_expr(finder, fml); - TRACE("mbqi_project_verbose", - tout << "terms:\n" << terms << "\n";); + TRACE("mbqi_project_verbose", tout << "terms:\n" << terms << "\n";); for(expr * term : terms) { expr_ref tval(m); - mev.eval(term, tval, false); + tval = mdl(term); TRACE("mbqi_project_verbose", tout << "term: " << mk_pp(term, m) - << " tval: " << tval - << " val: " << mk_pp(val, m) << "\n";); + << " tval: " << tval << " val: " << val << "\n";); // -- if the term does not contain an occurrence of var // -- and is in the same equivalence class in the model if (tval == val && !occurs(var, term)) { TRACE("mbqi_project", - tout << "MBQI: replacing " << mk_pp(var, m) << " with " << mk_pp(term, m) << "\n";); + tout << "MBQI: replacing " << mk_pp(var, m) + << " with " << mk_pp(term, m) << "\n";); expr_safe_replace sub(m); sub.insert(var, term); sub(fml); @@ -914,23 +856,23 @@ namespace { } TRACE("mbqi_project", - tout << "MBQI: failed to eliminate " << mk_pp(var, m) << " from " << fml << "\n";); + tout << "MBQI: failed to eliminate " << mk_pp(var, m) + << " from " << fml << "\n";); return false; } - void mbqi_project(model &M, app_ref_vector &vars, expr_ref &fml) { + void mbqi_project(model &mdl, app_ref_vector &vars, expr_ref &fml) { ast_manager &m = fml.get_manager(); - model_evaluator_util mev(m); - mev.set_model(M); expr_ref tmp(m); + model::scoped_model_completion _sc_(mdl, false); // -- evaluate to initialize mev cache - mev.eval(fml, tmp, false); + tmp = mdl(fml); tmp.reset(); unsigned j = 0; for(app* v : vars) - if (!mbqi_project_var(mev, v, fml)) + if (!mbqi_project_var(mdl, v, fml)) vars[j++] = v; vars.shrink(j); } diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index c3dbbd042..9912769c5 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -40,7 +40,6 @@ Revision History: class model; class model_core; -class model_evaluator; namespace spacer { @@ -79,33 +78,6 @@ namespace spacer { typedef ptr_vector decl_vector; typedef obj_hashtable func_decl_set; - // TBD: deprecate - class model_evaluator_util { - ast_manager& m; - model_ref m_model; - model_evaluator* m_mev; - - /// initialize with a given model. All previous state is lost. model can be NULL - void reset (model *model); - public: - model_evaluator_util(ast_manager& m); - ~model_evaluator_util(); - - void set_model(model &model) {reset (&model);} - model_ref &get_model() {return m_model;} - ast_manager& get_ast_manager() const {return m;} - - public: - bool is_true (const expr_ref_vector &v); - bool is_false(expr* x); - bool is_true(expr* x); - - bool eval (const expr_ref_vector &v, expr_ref &result, bool model_completion); - /// evaluates an expression - bool eval (expr *e, expr_ref &result, bool model_completion); - // expr_ref eval(expr* e, bool complete=true); - }; - /** \brief hoist non-boolean if expressions. */ @@ -146,7 +118,7 @@ namespace spacer { */ void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); - void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml); + void mbqi_project(model &mdl, app_ref_vector &vars, expr_ref &fml); bool contains_selects (expr* fml, ast_manager& m); void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m); From 0adf66dc0acda02437b3d25608a1fc660ebd02bc Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Sun, 17 Jun 2018 13:16:36 +0200 Subject: [PATCH 340/364] python: fix usage of fpa_get_numeral_significand_uint64 --- src/api/python/z3/z3.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index bd954d8ff..5e83a4181 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -8858,7 +8858,10 @@ class FPNumRef(FPRef): 1.25 """ def significand_as_long(self): - return Z3_fpa_get_numeral_significand_uint64(self.ctx.ref(), self.as_ast()) + ptr = (ctypes.c_ulonglong * 1)() + if not Z3_fpa_get_numeral_significand_uint64(self.ctx.ref(), self.as_ast(), ptr): + raise Z3Exception("error retrieving the significand of a numeral.") + return ptr[0] """The significand of the numeral as a bit-vector expression. From 035baf7cb9de60a3f0e6e2e6c29316c8ef0bb191 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 17 Jun 2018 09:43:40 -0700 Subject: [PATCH 341/364] align use of spaces before for/if/while Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_context.cpp | 16 ++++++------ src/muz/spacer/spacer_iuc_proof.cpp | 6 ++--- src/muz/spacer/spacer_unsat_core_plugin.cpp | 4 +-- src/muz/spacer/spacer_util.cpp | 29 +++++++++++---------- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 5d3ecb3fe..c926017b6 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -68,7 +68,7 @@ pob::pob (pob* parent, pred_transformer& pt, m_level (level), m_depth (depth), m_open (true), m_use_farkas (true), m_weakness(0), m_blocked_lvl(0) { - if(add_to_parent && m_parent) { + if (add_to_parent && m_parent) { m_parent->add_child(*this); } } @@ -106,14 +106,14 @@ void pob::inherit(pob const &p) { } void pob::clean () { - if(m_new_post) { + if (m_new_post) { m_post = m_new_post; m_new_post.reset(); } } void pob::close () { - if(!m_open) { return; } + if (!m_open) { return; } reset (); m_open = false; @@ -537,7 +537,7 @@ void lemma::mk_cube_core() { if (!m_cube.empty()) {return;} expr_ref cube(m); if (m_pob || m_body) { - if(m_pob) {cube = m_pob->post();} + if (m_pob) {cube = m_pob->post();} else if (m_body) { // no quantifiers for now SASSERT(!is_quantifier(m_body)); @@ -637,7 +637,7 @@ void lemma::instantiate(expr * const * exprs, expr_ref &result, expr *e) { } void lemma::set_level (unsigned lvl) { - if(m_pob){m_pob->blocked_at(lvl);} + if (m_pob){m_pob->blocked_at(lvl);} m_lvl = lvl; } @@ -1920,7 +1920,7 @@ bool pred_transformer::frames::add_lemma(lemma *new_lemma) if (!new_lemma->get_bindings().empty()) { m_pt.add_lemma_core(old_lemma, true); } - if(is_infty_level(old_lemma->level())) { + if (is_infty_level(old_lemma->level())) { old_lemma->bump(); if (old_lemma->get_bumped() >= 100) { IF_VERBOSE(1, verbose_stream() << "Adding lemma to oo " @@ -3230,7 +3230,7 @@ bool context::is_reachable(pob &n) void context::dump_json() { - if(m_params.spacer_print_json().size()) { + if (m_params.spacer_print_json().size()) { std::ofstream of; of.open(m_params.spacer_print_json().bare_str()); m_json_marshaller.marshal(of); @@ -3241,7 +3241,7 @@ void context::dump_json() void context::predecessor_eh() { for (unsigned i = 0; i < m_callbacks.size(); i++) { - if(m_callbacks[i]->predecessor()) + if (m_callbacks[i]->predecessor()) m_callbacks[i]->predecessor_eh(); } } diff --git a/src/muz/spacer/spacer_iuc_proof.cpp b/src/muz/spacer/spacer_iuc_proof.cpp index 6033c744f..b6a522b76 100644 --- a/src/muz/spacer/spacer_iuc_proof.cpp +++ b/src/muz/spacer/spacer_iuc_proof.cpp @@ -137,7 +137,7 @@ void iuc_proof::compute_marks() // if current node is application of a lemma, then all // active hypotheses are removed - if(cur->get_decl_kind() == PR_LEMMA) need_to_mark_h = false; + if (cur->get_decl_kind() == PR_LEMMA) need_to_mark_h = false; // save results m_a_mark.mark(cur, need_to_mark_a); @@ -212,9 +212,9 @@ void iuc_proof::display_dot(std::ostream& out) { std::string color = "white"; if (this->is_a_marked(curr) && !this->is_b_marked(curr)) color = "red"; - else if(!this->is_a_marked(curr) && this->is_b_marked(curr)) + else if (!this->is_a_marked(curr) && this->is_b_marked(curr)) color = "blue"; - else if(this->is_a_marked(curr) && this->is_b_marked(curr) ) + else if (this->is_a_marked(curr) && this->is_b_marked(curr) ) color = "purple"; // compute node label diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index eff05762b..b96ef3037 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -240,7 +240,7 @@ namespace spacer { func_decl* d = step->get_decl(); symbol sym; - if(!m_learner.is_closed(step) && // if step is not already interpolated + if (!m_learner.is_closed(step) && // if step is not already interpolated is_farkas_lemma(m, step)) { SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); SASSERT(m.has_fact(step)); @@ -415,7 +415,7 @@ namespace spacer { } // 4. find smallest n using guess and check algorithm - for(unsigned n = 1; true; ++n) + for (unsigned n = 1; true; ++n) { params_ref p; p.set_bool("model", true); diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index f63fc02d0..246410921 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -420,7 +420,7 @@ namespace { m_todo.append(a->get_num_args(), a->get_args()); } else { - for(expr* e : *a) { + for (expr* e : *a) { if (m_model.is_false(e)) { m_todo.push_back(e); break; @@ -432,7 +432,7 @@ namespace { if (!is_true) m_todo.append(a->get_num_args(), a->get_args()); else { - for(expr * e : *a) { + for (expr * e : *a) { if (m_model.is_true(e)) { m_todo.push_back(e); break; @@ -504,14 +504,15 @@ namespace { m_todo.pop_back(); process_app(a, out); m_visited.mark(a, true); - } while(!m_todo.empty()); + } + while (!m_todo.empty()); } bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) { m_visited.reset(); bool is_true = m_model.is_true(in); - for(expr* e : in) { + for (expr* e : in) { if (is_true || m_model.is_true(e)) { pick_literals(e, out); } @@ -551,7 +552,7 @@ namespace { ast_manager& m = cube.m(); scoped_no_proof _no_pf_(m); goal_ref g(alloc(goal, m, false, false, false)); - for(expr* c : cube) + for (expr* c : cube) g->assert_expr(c); goal_ref_buffer result; @@ -560,7 +561,7 @@ namespace { SASSERT(result.size() == 1); goal* r = result[0]; cube.reset(); - for(unsigned i = 0; i < r->size(); ++i) { + for (unsigned i = 0; i < r->size(); ++i) { cube.push_back(r->form(i)); } } @@ -569,7 +570,7 @@ namespace { ast_manager &m = cube.m(); scoped_no_proof _no_pf_(m); goal_ref g(alloc(goal, m, false, false, false)); - for(expr* c : cube) + for (expr* c : cube) g->assert_expr(c); goal_ref_buffer goals; @@ -582,7 +583,7 @@ namespace { g = goals[0]; cube.reset(); - for(unsigned i = 0; i < g->size(); ++i) { + for (unsigned i = 0; i < g->size(); ++i) { cube.push_back(g->form(i)); } } @@ -687,7 +688,7 @@ namespace { << mk_and(v) << "\n";); TRACE("spacer_normalize", qe::term_graph egraph(out.m()); - for(expr* e : v) egraph.add_lit(to_app(e)); + for (expr* e : v) egraph.add_lit(to_app(e)); tout << "Reduced app:\n" << mk_pp(egraph.to_app(), out.m()) << "\n";); out = mk_and(v); @@ -790,7 +791,7 @@ namespace { if (vars.size() < fv.size()) { vars.resize(fv.size()); } - for(unsigned i = 0, sz = fv.size(); i < sz; ++i) { + for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { sort *s = fv[i] ? fv[i] : m.mk_bool_sort(); vars[i] = mk_zk_const(m, i, s); var_subst vs(m, false); @@ -810,7 +811,7 @@ namespace { void operator()(app *n) { if (m_array.is_select(n) || m.is_eq(n)) { unsigned i = 0; - for(expr * arg : *n) { + for (expr * arg : *n) { if ((m.is_eq(n) || i > 0) && m_var != arg) m_res.push_back(arg); ++i; } @@ -834,7 +835,7 @@ namespace { TRACE("mbqi_project_verbose", tout << "terms:\n" << terms << "\n";); - for(expr * term : terms) { + for (expr * term : terms) { expr_ref tval(m); tval = mdl(term); @@ -871,7 +872,7 @@ namespace { tmp.reset(); unsigned j = 0; - for(app* v : vars) + for (app* v : vars) if (!mbqi_project_var(mdl, v, fml)) vars[j++] = v; vars.shrink(j); @@ -903,7 +904,7 @@ namespace { void operator()(expr* n) {} void operator()(app* n) { if (a.is_select(n)) - for(unsigned i = 1; i < n->get_num_args(); ++i) + for (unsigned i = 1; i < n->get_num_args(); ++i) if (is_app(n->get_arg(i))) m_indices.push_back(to_app(n->get_arg(i))); } From c81f25a1c8309fe72e358e78d42f19a2ab63064c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 17 Jun 2018 09:59:03 -0700 Subject: [PATCH 342/364] fix build issue Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_context.cpp | 2 +- src/muz/spacer/spacer_util.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index c926017b6..b917f8cc1 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -3098,7 +3098,7 @@ bool context::check_reachability () node = m_pob_queue.top (); m_pob_queue.pop(); - unsigned old_sz = m_pob_queue.size(); + size_t old_sz = m_pob_queue.size(); (void)old_sz; SASSERT (node->level () <= m_pob_queue.max_level ()); switch (expand_pob(*node, new_pobs)) { diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 246410921..aaa203c5e 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -358,16 +358,16 @@ namespace { } } - expr *nres, *f1, *f2; + expr *nres = nullptr, *f1 = nullptr, *f2 = nullptr; if (m.is_not(res, nres)) { // --(not (xor a b)) == (= a b) if (m.is_xor(nres, f1, f2)) res = m.mk_eq(f1, f2); // -- split arithmetic inequality else if (m.is_eq(nres, f1, f2) && m_arith.is_int_real(f1)) { - expr_ref u(m); - u = m_arith.mk_lt(f1, f2); - res = m_model.is_true(u) ? u : m_arith.mk_lt(f2, f1); + res = m_arith.mk_lt(f1, f2); + if (!m_model.is_true(res)) + res = m_arith.mk_lt(f2, f1); } } @@ -387,7 +387,7 @@ namespace { if (!is_true && !m.is_false(v)) return; - expr *na, *f1, *f2, *f3; + expr *na = nullptr, *f1 = nullptr, *f2 = nullptr, *f3 = nullptr; SASSERT(!m.is_false(a)); if (m.is_true(a)) { From cd890bd993ccc534df415f1335bb8ec2011754e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 09:34:53 -0700 Subject: [PATCH 343/364] fix bug in order for model conversion in normalize_bounds Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 3 ++- src/tactic/arith/lia2pb_tactic.cpp | 18 +++++------------- src/tactic/arith/normalize_bounds_tactic.cpp | 2 +- src/tactic/generic_model_converter.cpp | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 0bb4b091e..6dd7fbcaa 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1826,8 +1826,9 @@ void cmd_context::validate_model() { catch (contains_underspecified_op_proc::found) { continue; } - TRACE("model_validate", model_smt2_pp(tout, *this, *(md.get()), 0);); + TRACE("model_validate", model_smt2_pp(tout, *this, *md, 0);); IF_VERBOSE(10, verbose_stream() << "model check failed on: " << mk_pp(a, m()) << "\n";); + IF_VERBOSE(11, model_smt2_pp(verbose_stream(), *this, *md, 0);); invalid_model = true; } } diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index 178ace7fd..db1c22866 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -162,10 +162,8 @@ class lia2pb_tactic : public tactic { } bool has_target() { - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { - if (is_target(*it)) + for (expr * x : m_bm) { + if (is_target(x)) return true; } return false; @@ -174,10 +172,7 @@ class lia2pb_tactic : public tactic { bool check_num_bits() { unsigned num_bits = 0; rational u; - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { - expr * x = *it; + for (expr * x : m_bm) { if (is_target_core(x, u) && u > rational(1)) { num_bits += u.get_num_bits(); if (num_bits > m_total_bits) @@ -234,10 +229,7 @@ class lia2pb_tactic : public tactic { expr_substitution subst(m, m_produce_unsat_cores, false); rational u; ptr_buffer def_args; - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { - expr * x = *it; + for (expr * x : m_bm) { if (is_target_core(x, u) && u > rational(1)) { num_converted++; def_args.reset(); @@ -251,7 +243,7 @@ class lia2pb_tactic : public tactic { def_args.push_back(x_prime); else def_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), x_prime)); - if (m_produce_models) + if (m_produce_models) gmc->hide(x_prime->get_decl()); a *= rational(2); } diff --git a/src/tactic/arith/normalize_bounds_tactic.cpp b/src/tactic/arith/normalize_bounds_tactic.cpp index 907c7af8c..60d583798 100644 --- a/src/tactic/arith/normalize_bounds_tactic.cpp +++ b/src/tactic/arith/normalize_bounds_tactic.cpp @@ -109,8 +109,8 @@ class normalize_bounds_tactic : public tactic { expr * def = m_util.mk_add(x_prime, m_util.mk_numeral(val, s)); subst.insert(x, def); if (produce_models) { - gmc->add(to_app(x)->get_decl(), def); gmc->hide(x_prime->get_decl()); + gmc->add(to_app(x)->get_decl(), def); } } } diff --git a/src/tactic/generic_model_converter.cpp b/src/tactic/generic_model_converter.cpp index 9e2a7b9b2..1911edf3b 100644 --- a/src/tactic/generic_model_converter.cpp +++ b/src/tactic/generic_model_converter.cpp @@ -41,7 +41,7 @@ void generic_model_converter::add(func_decl * d, expr* e) { void generic_model_converter::operator()(model_ref & md) { TRACE("model_converter", tout << "before generic_model_converter\n"; model_v2_pp(tout, *md); display(tout);); - // IF_VERBOSE(0, verbose_stream() << "Apply converter " << m_orig << "\n";); + model_evaluator ev(*(md.get())); ev.set_model_completion(true); ev.set_expand_array_equalities(false); From 55ebf696489537565a10359b084abd9d1acc971a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 09:42:05 -0700 Subject: [PATCH 344/364] move comment to fix #1682 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 54fd1cb2b..b2adfff8d 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -2647,10 +2647,11 @@ namespace smt { } TRACE("simplify_clauses_detail", tout << "before:\n"; display_clauses(tout, m_lemmas);); + IF_VERBOSE(2, verbose_stream() << "(smt.simplifying-clause-set"; verbose_stream().flush();); + SASSERT(check_clauses(m_lemmas)); SASSERT(check_clauses(m_aux_clauses)); - IF_VERBOSE(2, verbose_stream() << "(smt.simplifying-clause-set"; verbose_stream().flush();); // m_simp_counter is used to balance the cost of simplify_clause. // From c3b27903f8c13073e2bcb66b81a699599ee86a60 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 11:22:01 -0700 Subject: [PATCH 345/364] fix #1677 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 6 ++---- src/smt/theory_seq.cpp | 9 ++++++--- src/smt/theory_str.cpp | 7 +++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 3ead59833..8f7b56381 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -1206,8 +1206,7 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) { e = todo.back(); todo.pop_back(); if (m_util.str.is_string(e, s)) { - for (unsigned i = s.length(); i > 0; ) { - --i; + for (unsigned i = 0; i < s.length(); ++i) { seq.push_back(m_util.str.mk_char(s, i)); } } @@ -1218,14 +1217,13 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) { seq.push_back(e1); } else if (m_util.str.is_concat(e, e1, e2)) { - todo.push_back(e1); todo.push_back(e2); + todo.push_back(e1); } else { return false; } } - seq.reverse(); return true; } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index b704795b2..2b2c35466 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2251,7 +2251,9 @@ bool theory_seq::internalize_term(app* term) { e = ctx.mk_enode(term, false, m.is_bool(term), true); } mk_var(e); - + if (m_util.str.is_string(term)) { + add_elim_string_axiom(term); + } return true; } @@ -3086,6 +3088,7 @@ void theory_seq::propagate() { void theory_seq::enque_axiom(expr* e) { if (!m_axiom_set.contains(e)) { + TRACE("seq", tout << "add axiom " << mk_pp(e, m) << "\n";); m_axioms.push_back(e); m_axiom_set.insert(e); m_trail_stack.push(push_back_vector(m_axioms)); @@ -3285,13 +3288,13 @@ void theory_seq::add_replace_axiom(expr* r) { void theory_seq::add_elim_string_axiom(expr* n) { zstring s; + TRACE("seq", tout << mk_pp(n, m) << "\n";); VERIFY(m_util.str.is_string(n, s)); if (s.length() == 0) { return; } expr_ref result(m_util.str.mk_unit(m_util.str.mk_char(s, s.length()-1)), m); - for (unsigned i = s.length()-1; i > 0; ) { - --i; + for (unsigned i = s.length()-1; i-- > 0; ) { result = mk_concat(m_util.str.mk_unit(m_util.str.mk_char(s, i)), result); } add_axiom(mk_eq(n, result, false)); diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 0ef008927..f2432b4fb 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -8388,13 +8388,12 @@ namespace smt { lbool theory_str::validate_unsat_core(expr_ref_vector & unsat_core) { app * target_term = to_app(get_manager().mk_not(m_theoryStrOverlapAssumption_term)); get_context().internalize(target_term, false); + enode* e1 = get_context().get_enode(target_term); for (unsigned i = 0; i < unsat_core.size(); ++i) { app * core_term = to_app(unsat_core.get(i)); // not sure if this is the correct way to compare terms in this context - enode * e1; - enode * e2; - e1 = get_context().get_enode(target_term); - e2 = get_context().get_enode(core_term); + if (!get_context().e_internalized(core_term)) continue; + enode *e2 = get_context().get_enode(core_term); if (e1 == e2) { TRACE("str", tout << "overlap detected in unsat core, changing UNSAT to UNKNOWN" << std::endl;); return l_undef; From 8040eddf6580be69ea02880fbf918978211ab40f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 16:41:04 -0700 Subject: [PATCH 346/364] fix #1658 fix #1689 Signed-off-by: Nikolaj Bjorner --- src/nlsat/nlsat_explain.cpp | 47 ++++++++----- src/nlsat/nlsat_scoped_literal_vector.h | 7 +- src/nlsat/nlsat_solver.cpp | 87 ++++++++++++++----------- src/nlsat/nlsat_solver.h | 10 +-- src/qe/nlqsat.cpp | 37 +++++------ 5 files changed, 105 insertions(+), 83 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 2278e53dd..420174506 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -574,7 +574,7 @@ namespace nlsat { if (is_const(p)) return; if (m_factor) { - TRACE("nlsat_explain", tout << "adding factors of\n"; display(tout, p); tout << "\n";); + TRACE("nlsat_explain", display(tout << "adding factors of\n", p); tout << "\n";); factor(p, m_factors); polynomial_ref f(m_pm); for (unsigned i = 0; i < m_factors.size(); i++) { @@ -1457,7 +1457,7 @@ namespace nlsat { process(num, ls); reset_already_added(); m_result = nullptr; - TRACE("nlsat_explain", tout << "[explain] result\n"; display(tout, result);); + TRACE("nlsat_explain", display(tout << "[explain] result\n", result);); CASSERT("nlsat", check_already_added()); } @@ -1466,7 +1466,12 @@ namespace nlsat { m_result = &result; svector lits; - TRACE("nlsat", tout << "project x" << x << "\n"; m_solver.display(tout);); + TRACE("nlsat", tout << "project x" << x << "\n"; + for (unsigned i = 0; i < num; ++i) { + m_solver.display(tout, ls[i]) << " "; + } + tout << "\n"; + m_solver.display(tout);); DEBUG_CODE( for (unsigned i = 0; i < num; ++i) { @@ -1509,8 +1514,15 @@ namespace nlsat { result.set(i, ~result[i]); } DEBUG_CODE( - for (unsigned i = 0; i < result.size(); ++i) { - SASSERT(l_true == m_solver.value(result[i])); + TRACE("nlsat", + for (literal l : result) { + m_solver.display(tout << " ", l); + } + tout << "\n"; + ); + for (literal l : result) { + CTRACE("nlsat", l_true != m_solver.value(l), m_solver.display(tout, l) << " " << m_solver.value(l) << "\n";); + SASSERT(l_true == m_solver.value(l)); }); } @@ -1621,21 +1633,21 @@ namespace nlsat { roots.reset(); m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots); bool glb_valid = false, lub_valid = false; - for (unsigned j = 0; j < roots.size(); ++j) { - int s = m_am.compare(x_val, roots[j]); + for (auto const& r : roots) { + int s = m_am.compare(x_val, r); SASSERT(s != 0); + + if (s < 0 && (!lub_valid || m_am.lt(r, lub))) { + lub_index = i; + m_am.set(lub, r); + } + + if (s > 0 && (!glb_valid || m_am.lt(glb, r))) { + glb_index = i; + m_am.set(glb, r); + } lub_valid |= s < 0; glb_valid |= s > 0; - - if (s < 0 && m_am.lt(roots[j], lub)) { - lub_index = i; - m_am.set(lub, roots[j]); - } - - if (s > 0 && m_am.lt(glb, roots[j])) { - glb_index = i; - m_am.set(glb, roots[j]); - } } if (glb_valid) { ++num_glb; @@ -1701,6 +1713,7 @@ namespace nlsat { } void project_pairs(var x, unsigned idx, polynomial_ref_vector const& ps) { + TRACE("nlsat_explain", tout << "project pairs\n";); polynomial_ref p(m_pm); p = ps.get(idx); for (unsigned i = 0; i < ps.size(); ++i) { diff --git a/src/nlsat/nlsat_scoped_literal_vector.h b/src/nlsat/nlsat_scoped_literal_vector.h index 61760f06d..dffaa169f 100644 --- a/src/nlsat/nlsat_scoped_literal_vector.h +++ b/src/nlsat/nlsat_scoped_literal_vector.h @@ -34,9 +34,8 @@ namespace nlsat { bool empty() const { return m_lits.empty(); } literal operator[](unsigned i) const { return m_lits[i]; } void reset() { - unsigned sz = m_lits.size(); - for (unsigned i = 0; i < sz; i++) { - m_solver.dec_ref(m_lits[i]); + for (literal l : m_lits) { + m_solver.dec_ref(l); } m_lits.reset(); } @@ -50,6 +49,8 @@ namespace nlsat { m_lits[i] = l; } literal const * c_ptr() const { return m_lits.c_ptr(); } + literal const * begin() const { return m_lits.begin(); } + literal const * end() const { return m_lits.end(); } void shrink(unsigned new_sz) { SASSERT(new_sz <= m_lits.size()); unsigned sz = m_lits.size(); diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 9767448b1..f430f3a0c 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -270,7 +270,7 @@ namespace nlsat { TRACE("ref", tout << "inc: " << b << "\n";); if (b == null_bool_var) return; - if (m_atoms[b] == 0) + if (m_atoms[b] == nullptr) return; m_atoms[b]->inc_ref(); } @@ -395,7 +395,7 @@ namespace nlsat { bool_var mk_bool_var_core() { bool_var b = m_bid_gen.mk(); m_num_bool_vars++; - m_atoms .setx(b, 0, 0); + m_atoms .setx(b, nullptr, nullptr); m_bvalues .setx(b, l_undef, l_undef); m_levels .setx(b, UINT_MAX, UINT_MAX); m_justifications.setx(b, null_justification, null_justification); @@ -469,7 +469,7 @@ namespace nlsat { //SASSERT(m_bvalues[b] == l_undef); m_num_bool_vars--; m_dead[b] = true; - m_atoms[b] = 0; + m_atoms[b] = nullptr; m_bid_gen.recycle(b); } @@ -494,6 +494,7 @@ namespace nlsat { void del(atom * a) { if (a == nullptr) return ; + TRACE("nlsat", display(tout << "del: b" << a->m_bool_var << " ", *a) << "\n";); if (a->is_ineq_atom()) del(to_ineq_atom(a)); else @@ -555,6 +556,7 @@ namespace nlsat { bool_var b = mk_bool_var_core(); m_atoms[b] = atom; atom->m_bool_var = b; + TRACE("nlsat", display(tout << "create: b" << atom->m_bool_var << " ", *atom) << "\n";); return b; } } @@ -749,7 +751,7 @@ namespace nlsat { m_levels[b] = UINT_MAX; del_jst(m_allocator, m_justifications[b]); m_justifications[b] = null_justification; - if (m_atoms[b] == 0 && b < m_bk) + if (m_atoms[b] == nullptr && b < m_bk) m_bk = b; } @@ -895,7 +897,7 @@ namespace nlsat { \brief Assign literal using the given justification */ void assign(literal l, justification j) { - TRACE("nlsat", tout << "assigning literal:\n"; display(tout, l); + TRACE("nlsat", tout << "assigning literal: "; display(tout, l); tout << "\njustification kind: " << j.get_kind() << "\n";); SASSERT(assigned_value(l) == l_undef); SASSERT(j != null_justification); @@ -926,6 +928,7 @@ namespace nlsat { */ lbool value(literal l) { lbool val = assigned_value(l); + TRACE("nlsat_verbose", display(tout << " assigned value " << val << " for ", l) << "\n";); if (val != l_undef) { return val; } @@ -941,7 +944,7 @@ namespace nlsat { val = to_lbool(m_evaluator.eval(a, l.sign())); TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n"; tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; - display_assignment(tout);); + display_assignment(tout);); return val; } @@ -2011,9 +2014,9 @@ namespace nlsat { } bool can_reorder() const { - for (unsigned i = 0; i < m_atoms.size(); ++i) { - if (m_atoms[i]) { - if (m_atoms[i]->is_root_atom()) return false; + for (atom * a : m_atoms) { + if (a) { + if (a->is_root_atom()) return false; } } return true; @@ -2100,16 +2103,13 @@ namespace nlsat { \brief After variable reordering some lemmas containing root atoms may be ill-formed. */ void del_ill_formed_lemmas() { - unsigned sz = m_learned.size(); unsigned j = 0; - for (unsigned i = 0; i < sz; i++) { - clause * c = m_learned[i]; + for (clause* c : m_learned) { if (ill_formed(*c)) { del_clause(c); } else { - m_learned[j] = c; - j++; + m_learned[j++] = c; } } m_learned.shrink(j); @@ -2119,9 +2119,8 @@ namespace nlsat { \brief Return true if the clause contains an ill formed root atom */ bool ill_formed(clause const & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - bool_var b = c[i].var(); + for (literal lit : c) { + bool_var b = lit.var(); atom * a = m_atoms[b]; if (a == nullptr) continue; @@ -2139,19 +2138,16 @@ namespace nlsat { void reinit_cache() { reinit_cache(m_clauses); reinit_cache(m_learned); - for (unsigned i = 0; i < m_atoms.size(); ++i) { - reinit_cache(m_atoms[i]); - } + for (atom* a : m_atoms) + reinit_cache(a); } void reinit_cache(clause_vector const & cs) { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) - reinit_cache(*(cs[i])); + for (clause* c : cs) + reinit_cache(*c); } void reinit_cache(clause const & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) - reinit_cache(c[i]); + for (literal l : c) + reinit_cache(l); } void reinit_cache(literal l) { bool_var b = l.var(); @@ -2566,9 +2562,12 @@ namespace nlsat { std::ostream& display_bool_assignment(std::ostream & out) const { unsigned sz = m_atoms.size(); for (bool_var b = 0; b < sz; b++) { - if (m_atoms[b] == 0 && m_bvalues[b] != l_undef) { + if (m_atoms[b] == nullptr && m_bvalues[b] != l_undef) { out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; } + else if (m_atoms[b] != nullptr && m_bvalues[b] != l_undef) { + display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; + } } TRACE("nlsat_bool_assignment", for (bool_var b = 0; b < sz; b++) { @@ -3035,7 +3034,7 @@ namespace nlsat { std::ostream& display_smt2_bool_decls(std::ostream & out) const { unsigned sz = m_atoms.size(); for (unsigned i = 0; i < sz; i++) { - if (m_atoms[i] == 0) + if (m_atoms[i] == nullptr) out << "(declare-fun b" << i << " () Bool)\n"; } return out; @@ -3162,13 +3161,24 @@ namespace nlsat { void solver::get_bvalues(svector& vs) { vs.reset(); - vs.append(m_imp->m_bvalues); + unsigned sz = m_imp->m_bvalues.size(); + for (bool_var b = 0; b < sz; ++b) { + if (m_imp->m_atoms[b] == nullptr) { + vs.push_back(m_imp->m_bvalues[b]); + } + else { + vs.push_back(l_undef); // don't save values from atoms. + } + } + TRACE("nlsat", display(tout);); } void solver::set_bvalues(svector const& vs) { + TRACE("nlsat", display(tout);); m_imp->m_bvalues.reset(); m_imp->m_bvalues.append(vs); m_imp->m_bvalues.resize(m_imp->m_atoms.size(), l_undef); + TRACE("nlsat", display(tout);); } var solver::mk_var(bool is_int) { @@ -3199,27 +3209,28 @@ namespace nlsat { return m_imp->mk_clause(num_lits, lits, a); } - void solver::display(std::ostream & out) const { - m_imp->display(out); + std::ostream& solver::display(std::ostream & out) const { + return m_imp->display(out); } - void solver::display(std::ostream & out, literal l) const { - m_imp->display(out, l); + std::ostream& solver::display(std::ostream & out, literal l) const { + return m_imp->display(out, l); } - void solver::display(std::ostream & out, unsigned n, literal const* ls) const { + std::ostream& solver::display(std::ostream & out, unsigned n, literal const* ls) const { for (unsigned i = 0; i < n; ++i) { display(out, ls[i]); out << "; "; } + return out; } - void solver::display(std::ostream & out, var x) const { - m_imp->m_display_var(out, x); + std::ostream& solver::display(std::ostream & out, var x) const { + return m_imp->m_display_var(out, x); } - void solver::display(std::ostream & out, atom const& a) const { - m_imp->display(out, a, m_imp->m_display_var); + std::ostream& solver::display(std::ostream & out, atom const& a) const { + return m_imp->display(out, a, m_imp->m_display_var); } display_var_proc const & solver::display_proc() const { diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index e7b250f64..4ba1225bd 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -225,21 +225,21 @@ namespace nlsat { /** \brief Display solver's state. */ - void display(std::ostream & out) const; + std::ostream& display(std::ostream & out) const; /** \brief Display literal */ - void display(std::ostream & out, literal l) const; + std::ostream& display(std::ostream & out, literal l) const; - void display(std::ostream & out, unsigned n, literal const* ls) const; + std::ostream& display(std::ostream & out, unsigned n, literal const* ls) const; - void display(std::ostream & out, atom const& a) const; + std::ostream& display(std::ostream & out, atom const& a) const; /** \brief Display variable */ - void display(std::ostream & out, var x) const; + std::ostream& display(std::ostream & out, var x) const; display_var_proc const & display_proc() const; }; diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index d8d6705fe..95f6b9b21 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -205,8 +205,7 @@ namespace qe { nlsat::scoped_literal_vector new_result(m_solver); result.reset(); // project quantified Boolean variables. - for (unsigned i = 0; i < m_asms.size(); ++i) { - nlsat::literal lit = m_asms[i]; + for (nlsat::literal lit : m_asms) { if (!m_b2a.contains(lit.var()) || fvars.contains(lit.var())) { result.push_back(lit); } @@ -215,12 +214,13 @@ namespace qe { // project quantified real variables. // They are sorted by size, so we project the largest variables first to avoid // renaming variables. - for (unsigned i = vars.size(); i > 0;) { - --i; + for (unsigned i = vars.size(); i-- > 0;) { new_result.reset(); + TRACE("qe", m_solver.display(tout << "project: ", vars[i]) << "\n";); ex.project(vars[i], result.size(), result.c_ptr(), new_result); result.swap(new_result); - TRACE("qe", m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";); + TRACE("qe", m_solver.display(tout, vars[i]) << ": "; + m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";); } negate_clause(result); } @@ -596,6 +596,7 @@ namespace qe { } void display(std::ostream& out) { + out << "level " << level() << "\n"; display_preds(out); display_assumptions(out); m_solver.display(out << "solver:\n"); @@ -682,7 +683,7 @@ namespace qe { } else if (m_t2x.is_var(v)) { nlsat::var w = m_t2x.to_var(v); - TRACE("qe", tout << mk_pp(v, m) << " |-> " << w << "\n";); + TRACE("qe", tout << mk_pp(v, m) << " |-> x" << w << "\n";); m_bound_rvars.back().push_back(w); m_rvar2level.setx(w, lvl, max_level()); } @@ -724,13 +725,11 @@ namespace qe { } void init_var2expr() { - expr2var::iterator it = m_t2x.begin(), end = m_t2x.end(); - for (; it != end; ++it) { - m_x2t.insert(it->m_value, it->m_key); + for (auto const& kv : m_t2x) { + m_x2t.insert(kv.m_value, kv.m_key); } - it = m_a2b.begin(), end = m_a2b.end(); - for (; it != end; ++it) { - m_b2a.insert(it->m_value, it->m_key); + for (auto const& kv : m_a2b) { + m_b2a.insert(kv.m_value, kv.m_key); } } @@ -741,10 +740,9 @@ namespace qe { bool ok = true; model_ref md = alloc(model, m); arith_util util(m); - expr2var::iterator it = m_t2x.begin(), end = m_t2x.end(); - for (; it != end; ++it) { - nlsat::var x = it->m_value; - expr * t = it->m_key; + for (auto const& kv : m_t2x) { + nlsat::var x = kv.m_value; + expr * t = kv.m_key; if (!is_uninterp_const(t) || !m_free_vars.contains(t) || m_aux_vars.contains(t)) continue; expr * v; @@ -760,10 +758,9 @@ namespace qe { } md->register_decl(to_app(t)->get_decl(), v); } - it = m_a2b.begin(), end = m_a2b.end(); - for (; it != end; ++it) { - expr * a = it->m_key; - nlsat::bool_var b = it->m_value; + for (auto const& kv : m_a2b) { + expr * a = kv.m_key; + nlsat::bool_var b = kv.m_value; if (a == nullptr || !is_uninterp_const(a) || b == m_is_true.var() || !m_free_vars.contains(a) || m_aux_vars.contains(a)) continue; lbool val = m_bmodel0.get(b, l_undef); From 4634d1daed5a173b5bca26995dba51256bad521f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 20:37:39 -0700 Subject: [PATCH 347/364] selective expansion of strings for canonizer to fix #1690 regression Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 14 ++++++++------ src/smt/theory_seq.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 2b2c35466..91da5a078 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2251,9 +2251,6 @@ bool theory_seq::internalize_term(app* term) { e = ctx.mk_enode(term, false, m.is_bool(term), true); } mk_var(e); - if (m_util.str.is_string(term)) { - add_elim_string_axiom(term); - } return true; } @@ -2901,12 +2898,16 @@ expr_ref theory_seq::expand(expr* e, dependency*& eqs) { expr_ref theory_seq::try_expand(expr* e, dependency*& eqs){ expr_ref result(m); expr_dep ed; + zstring s; if (m_rep.find_cache(e, ed)) { if (e != ed.first) { eqs = m_dm.mk_join(eqs, ed.second); } result = ed.first; } + else if (m_util.str.is_string(e, s) && s.length() > 0) { + result = add_elim_string_axiom(e); + } else { m_expand_todo.push_back(e); } @@ -2929,7 +2930,7 @@ expr_ref theory_seq::expand1(expr* e0, dependency*& eqs) { } else if (m_util.str.is_empty(e) || m_util.str.is_string(e)) { result = e; - } + } else if (m_util.str.is_prefix(e, e1, e2)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); @@ -3286,12 +3287,12 @@ void theory_seq::add_replace_axiom(expr* r) { tightest_prefix(s, x); } -void theory_seq::add_elim_string_axiom(expr* n) { +expr_ref theory_seq::add_elim_string_axiom(expr* n) { zstring s; TRACE("seq", tout << mk_pp(n, m) << "\n";); VERIFY(m_util.str.is_string(n, s)); if (s.length() == 0) { - return; + return expr_ref(n, m); } expr_ref result(m_util.str.mk_unit(m_util.str.mk_char(s, s.length()-1)), m); for (unsigned i = s.length()-1; i-- > 0; ) { @@ -3300,6 +3301,7 @@ void theory_seq::add_elim_string_axiom(expr* n) { add_axiom(mk_eq(n, result, false)); m_rep.update(n, result, nullptr); m_new_solution = true; + return result; } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index b6b553dec..3cacf3326 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -504,7 +504,7 @@ namespace smt { void add_int_string(expr* e); bool check_int_string(); - void add_elim_string_axiom(expr* n); + expr_ref add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); void add_in_re_axiom(expr* n); void add_itos_axiom(expr* n); From 6a0b70ee5cb3664fafee08068626b9a68778bf78 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 20:42:53 -0700 Subject: [PATCH 348/364] selective expansion of strings for canonizer to fix #1690 regression Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 91da5a078..03bf81d45 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2898,14 +2898,13 @@ expr_ref theory_seq::expand(expr* e, dependency*& eqs) { expr_ref theory_seq::try_expand(expr* e, dependency*& eqs){ expr_ref result(m); expr_dep ed; - zstring s; if (m_rep.find_cache(e, ed)) { if (e != ed.first) { eqs = m_dm.mk_join(eqs, ed.second); } result = ed.first; } - else if (m_util.str.is_string(e, s) && s.length() > 0) { + else if (m_util.str.is_string(e)) { result = add_elim_string_axiom(e); } else { From c15eca66d6c0df6b51ac3e7a5a83dbcdd2e0687b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 20:53:33 -0700 Subject: [PATCH 349/364] fix #1685 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_simplifier.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 4fd9b08be..d19cd14d4 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -338,8 +338,7 @@ namespace sat { if (sz == 0) { s.set_conflict(justification()); for (; it != end; ++it, ++it2) { - *it2 = *it; - ++it2; + *it2 = *it; } break; } From b4aac1ab5594419346d18278fcccbb24f02a8a5a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 21:23:13 -0700 Subject: [PATCH 350/364] revert fix to #1677 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context_inv.cpp | 83 ++++++++++++------------------------- src/smt/theory_seq.cpp | 2 +- 2 files changed, 27 insertions(+), 58 deletions(-) diff --git a/src/smt/smt_context_inv.cpp b/src/smt/smt_context_inv.cpp index cf09c996a..7f409622b 100644 --- a/src/smt/smt_context_inv.cpp +++ b/src/smt/smt_context_inv.cpp @@ -43,13 +43,9 @@ namespace smt { } bool context::check_clauses(clause_vector const & v) const { - clause_vector::const_iterator it = v.begin(); - clause_vector::const_iterator end = v.end(); - for (; it != end; ++it) { - clause * cls = *it; + for (clause* cls : v) if (!cls->deleted()) check_clause(cls); - } return true; } @@ -92,10 +88,7 @@ namespace smt { bool context::check_lit_occs(literal l) const { clause_set const & v = m_lit_occs[l.index()]; - clause_set::iterator it = v.begin(); - clause_set::iterator end = v.end(); - for (; it != end; ++it) { - clause * cls = *it; + for (clause * cls : v) { unsigned num = cls->get_num_literals(); unsigned i = 0; for (; i < num; i++) @@ -138,10 +131,8 @@ namespace smt { } bool context::check_enodes() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - check_enode(*it); + for (enode* n : m_enodes) { + check_enode(n); } return true; } @@ -157,11 +148,9 @@ namespace smt { } bool context::check_missing_clause_propagation(clause_vector const & v) const { - clause_vector::const_iterator it = v.begin(); - clause_vector::const_iterator end = v.end(); - for (; it != end; ++it) { - CTRACE("missing_propagation", is_unit_clause(*it), display_clause_detail(tout, *it); tout << "\n";); - SASSERT(!is_unit_clause(*it)); + for (clause * cls : v) { + CTRACE("missing_propagation", is_unit_clause(cls), display_clause_detail(tout, cls); tout << "\n";); + SASSERT(!is_unit_clause(cls)); } return true; } @@ -188,10 +177,7 @@ namespace smt { } bool context::check_missing_eq_propagation() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode* n : m_enodes) { SASSERT(!n->is_true_eq() || get_assignment(n) == l_true); if (n->is_eq() && get_assignment(n) == l_true) { SASSERT(n->is_true_eq()); @@ -201,13 +187,8 @@ namespace smt { } bool context::check_missing_congruence() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * n = *it; - ptr_vector::const_iterator it2 = m_enodes.begin(); - for (; it2 != end; ++it2) { - enode * n2 = *it2; + for (enode* n : m_enodes) { + for (enode* n2 : m_enodes) { if (n->get_root() != n2->get_root()) { if (n->is_true_eq() && n2->is_true_eq()) continue; @@ -222,10 +203,7 @@ namespace smt { } bool context::check_missing_bool_enode_propagation() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode* n : m_enodes) { if (m_manager.is_bool(n->get_owner()) && get_assignment(n) == l_undef) { enode * first = n; do { @@ -286,10 +264,7 @@ namespace smt { For all a, b. root(a) == root(b) ==> get_assignment(a) == get_assignment(b) */ bool context::check_eqc_bool_assignment() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * e = *it; + for (enode* e : m_enodes) { if (m_manager.is_bool(e->get_owner())) { enode * r = e->get_root(); CTRACE("eqc_bool", get_assignment(e) != get_assignment(r), @@ -343,24 +318,24 @@ namespace smt { TRACE("check_th_diseq_propagation", tout << "checking theory: " << m_manager.get_family_name(th_id) << "\n";); // if the theory doesn't use diseqs, then the diseqs are not propagated. if (th->use_diseqs() && rhs->get_th_var(th_id) != null_theory_var) { + bool found = false; // lhs and rhs are attached to theory th_id - svector::const_iterator it = m_propagated_th_diseqs.begin(); - svector::const_iterator end = m_propagated_th_diseqs.end(); - for (; it != end; ++it) { - if (it->m_th_id == th_id) { - enode * lhs_prime = th->get_enode(it->m_lhs)->get_root(); - enode * rhs_prime = th->get_enode(it->m_rhs)->get_root(); + for (new_th_eq const& eq : m_propagated_th_diseqs) { + if (eq.m_th_id == th_id) { + enode * lhs_prime = th->get_enode(eq.m_lhs)->get_root(); + enode * rhs_prime = th->get_enode(eq.m_rhs)->get_root(); TRACE("check_th_diseq_propagation", - tout << m_manager.get_family_name(it->m_th_id) << "\n";); + tout << m_manager.get_family_name(eq.m_th_id) << "\n";); if ((lhs == lhs_prime && rhs == rhs_prime) || (rhs == lhs_prime && lhs == rhs_prime)) { TRACE("check_th_diseq_propagation", tout << "ok v" << v << " " << get_assignment(v) << "\n";); + found = true; break; } } } - if (it == end) { + if (!found) { // missed theory diseq propagation display(std::cout); std::cout << "checking theory: " << m_manager.get_family_name(th_id) << "\n"; @@ -369,8 +344,7 @@ namespace smt { std::cout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << "\n"; std::cout << mk_bounded_pp(lhs->get_owner(), m_manager) << " " << mk_bounded_pp(rhs->get_owner(), m_manager) << "\n"; } - - SASSERT(it != end); + VERIFY(found); } l = l->get_next(); } @@ -381,11 +355,9 @@ namespace smt { } bool context::check_missing_diseq_conflict() const { - svector::const_iterator it = m_diseq_vector.begin(); - svector::const_iterator end = m_diseq_vector.end(); - for (; it != end; ++it) { - enode * n1 = it->first; - enode * n2 = it->second; + for (enode_pair const& p : m_diseq_vector) { + enode * n1 = p.first; + enode * n2 = p.second; if (n1->get_root() == n2->get_root()) { TRACE("diseq_bug", tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << @@ -420,10 +392,7 @@ namespace smt { return true; } ast_manager& m = m_manager; - literal_vector::const_iterator it = m_assigned_literals.begin(); - literal_vector::const_iterator end = m_assigned_literals.end(); - for (; it != end; ++it) { - literal lit = *it; + for (literal lit : m_assigned_literals) { if (!is_relevant(lit)) { continue; } @@ -435,7 +404,7 @@ namespace smt { if (is_quantifier(n) && m.is_rec_fun_def(to_quantifier(n))) { continue; } - switch (get_assignment(*it)) { + switch (get_assignment(lit)) { case l_undef: break; case l_true: diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 03bf81d45..3b81a35dc 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2904,7 +2904,7 @@ expr_ref theory_seq::try_expand(expr* e, dependency*& eqs){ } result = ed.first; } - else if (m_util.str.is_string(e)) { + else if (false && m_util.str.is_string(e)) { result = add_elim_string_axiom(e); } else { From 86c39c971d12be7e063e05d26582a53e7f10194a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Jun 2018 21:53:45 -0700 Subject: [PATCH 351/364] fix #1681 Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/proto_model.cpp | 16 +++++++++++----- src/smt/proto_model/proto_model.h | 4 ++-- src/smt/smt_context.cpp | 2 +- src/smt/smt_model_finder.cpp | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 688ded834..493948f32 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -342,10 +342,16 @@ void proto_model::compress() { \brief Complete the interpretation fi of f if it is partial. If f does not have an interpretation in the given model, then this is a noop. */ -void proto_model::complete_partial_func(func_decl * f) { +void proto_model::complete_partial_func(func_decl * f, bool use_fresh) { func_interp * fi = get_func_interp(f); if (fi && fi->is_partial()) { - expr * else_value = fi->get_max_occ_result(); + expr * else_value; + if (use_fresh) { + else_value = get_fresh_value(f->get_range()); + } + else { + else_value = fi->get_max_occ_result(); + } if (else_value == nullptr) else_value = get_some_value(f->get_range()); fi->set_else(else_value); @@ -355,14 +361,14 @@ void proto_model::complete_partial_func(func_decl * f) { /** \brief Set the (else) field of function interpretations... */ -void proto_model::complete_partial_funcs() { +void proto_model::complete_partial_funcs(bool use_fresh) { if (m_model_partial) return; // m_func_decls may be "expanded" when we invoke get_some_value. // So, we must not use iterators to traverse it. - for (unsigned i = 0; i < m_func_decls.size(); i++) { - complete_partial_func(m_func_decls[i]); + for (func_decl* f : m_func_decls) { + complete_partial_func(f, use_fresh); } } diff --git a/src/smt/proto_model/proto_model.h b/src/smt/proto_model/proto_model.h index d92d459e4..04e3a90fe 100644 --- a/src/smt/proto_model/proto_model.h +++ b/src/smt/proto_model/proto_model.h @@ -100,8 +100,8 @@ public: // // Complete partial function interps // - void complete_partial_func(func_decl * f); - void complete_partial_funcs(); + void complete_partial_func(func_decl * f, bool use_fresh); + void complete_partial_funcs(bool use_fresh); // // Create final model object. diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index b2adfff8d..48033f9b5 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4364,7 +4364,7 @@ namespace smt { m_proto_model = m_model_generator->mk_model(); m_qmanager->adjust_model(m_proto_model.get()); TRACE("mbqi_bug", tout << "before complete_partial_funcs:\n"; model_pp(tout, *m_proto_model);); - m_proto_model->complete_partial_funcs(); + m_proto_model->complete_partial_funcs(false); TRACE("mbqi_bug", tout << "before cleanup:\n"; model_pp(tout, *m_proto_model);); m_proto_model->cleanup(); if (m_fparams.m_model_compact) diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 4307d3fdf..73f85faf6 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -1028,7 +1028,7 @@ namespace smt { void complete_partial_funcs(func_decl_set const & partial_funcs) { for (func_decl * f : partial_funcs) { // Complete the current interpretation - m_model->complete_partial_func(f); + m_model->complete_partial_func(f, true); unsigned arity = f->get_arity(); func_interp * fi = m_model->get_func_interp(f); From 8a29c2803c1852683bdbfc4bf5ff170b92b0ce91 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Jun 2018 07:04:39 -0700 Subject: [PATCH 352/364] improvements to arithmetic preprocessing simplificaiton and axiom generation for #1683 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/arith_rewriter.cpp | 129 ++++++++++++++++++++-------- src/ast/rewriter/arith_rewriter.h | 5 +- src/smt/mam.cpp | 32 ++----- src/smt/theory_arith_core.h | 28 +++++- 4 files changed, 131 insertions(+), 63 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 9824884a4..379020331 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -800,52 +800,105 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } - expr_ref quot(m()); - if (divides(arg1, arg2, quot)) { - result = m_util.mk_mul(quot, m_util.mk_idiv(arg1, arg1)); - return BR_REWRITE2; + if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { + result = arg1; + return BR_DONE; + } + if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { + return BR_FAILED; + } + if (arg1 == arg2) { + expr_ref zero(m_util.mk_int(0), m()); + result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); + return BR_REWRITE3; + } + if (divides(arg1, arg2, result)) { + return BR_REWRITE_FULL; } return BR_FAILED; } -bool arith_rewriter::divides(expr* d, expr* n, expr_ref& quot) { - if (d == n) { - quot = m_util.mk_numeral(rational(1), m_util.is_int(d)); + +// +// implement div ab ac = floor( ab / ac) = floor (b / c) = div b c +// +bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) { + expr_fast_mark1 mark; + rational num_r(1), den_r(1); + expr* num_e = nullptr, *den_e = nullptr; + ptr_buffer args1, args2; + flat_mul(num, args1); + flat_mul(den, args2); + for (expr * arg : args1) { + mark.mark(arg); + if (m_util.is_numeral(arg, num_r)) num_e = arg; + } + for (expr* arg : args2) { + if (mark.is_marked(arg)) { + result = remove_divisor(arg, num, den); + return true; + } + if (m_util.is_numeral(arg, den_r)) den_e = arg; + } + rational g = gcd(num_r, den_r); + if (!g.is_one()) { + SASSERT(g.is_pos()); + // replace num_e, den_e by their gcd reduction. + for (unsigned i = 0; i < args1.size(); ++i) { + if (args1[i] == num_e) { + args1[i] = m_util.mk_numeral(num_r / g, true); + break; + } + } + for (unsigned i = 0; i < args2.size(); ++i) { + if (args2[i] == den_e) { + args2[i] = m_util.mk_numeral(den_r / g, true); + break; + } + } + + num = m_util.mk_mul(args1.size(), args1.c_ptr()); + den = m_util.mk_mul(args2.size(), args2.c_ptr()); + result = m_util.mk_idiv(num, den); return true; } - if (m_util.is_mul(n)) { - expr_ref_vector muls(m()); - muls.push_back(n); - expr* n1, *n2; - rational r1, r2; - for (unsigned i = 0; i < muls.size(); ++i) { - if (m_util.is_mul(muls[i].get(), n1, n2)) { - muls[i] = n1; - muls.push_back(n2); - --i; - } - } - if (m_util.is_numeral(d, r1) && !r1.is_zero()) { - for (unsigned i = 0; i < muls.size(); ++i) { - if (m_util.is_numeral(muls[i].get(), r2) && (r2 / r1).is_int()) { - muls[i] = m_util.mk_numeral(r2 / r1, m_util.is_int(d)); - quot = m_util.mk_mul(muls.size(), muls.c_ptr()); - return true; - } - } - } - else { - for (unsigned i = 0; i < muls.size(); ++i) { - if (d == muls[i].get()) { - muls[i] = muls.back(); - muls.pop_back(); - quot = m_util.mk_mul(muls.size(), muls.c_ptr()); - return true; - } - } + return false; +} + +expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) { + ptr_buffer args1, args2; + flat_mul(num, args1); + flat_mul(den, args2); + remove_divisor(arg, args1); + remove_divisor(arg, args2); + expr_ref zero(m_util.mk_int(0), m()); + num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.c_ptr()); + den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.c_ptr()); + return expr_ref(m().mk_ite(m().mk_eq(zero, arg), m_util.mk_idiv(zero, zero), m_util.mk_idiv(num, den)), m()); +} + +void arith_rewriter::flat_mul(expr* e, ptr_buffer& args) { + args.push_back(e); + for (unsigned i = 0; i < args.size(); ++i) { + e = args[i]; + if (m_util.is_mul(e)) { + args.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + args[i] = args.back(); + args.shrink(args.size()-1); + --i; + } + } +} + +void arith_rewriter::remove_divisor(expr* d, ptr_buffer& args) { + for (unsigned i = 0; i < args.size(); ++i) { + if (args[i] == d) { + args[i] = args.back(); + args.shrink(args.size()-1); + return; } } - return false; + UNREACHABLE(); } br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) { diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index 65fbfb43f..fa8677941 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -95,7 +95,10 @@ class arith_rewriter : public poly_rewriter { expr_ref neg_monomial(expr * e) const; expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); - bool divides(expr* d, expr* n, expr_ref& quot); + bool divides(expr* d, expr* n, expr_ref& result); + expr_ref remove_divisor(expr* arg, expr* num, expr* den); + void flat_mul(expr* e, ptr_buffer& args); + void remove_divisor(expr* d, ptr_buffer& args); public: arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index d31cbc905..ff62dacec 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -865,9 +865,7 @@ namespace smt { That is, during execution time, the variables will be already bound */ bool all_args_are_bound_vars(app * n) { - unsigned num_args = n->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = n->get_arg(i); + for (expr* arg : *n) { if (!is_var(arg)) return false; if (m_vars[to_var(arg)->get_idx()] == -1) @@ -884,9 +882,7 @@ namespace smt { if (n->is_ground()) { return; } - unsigned num_args = n->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = n->get_arg(i); + for (expr* arg : *n) { if (is_var(arg)) { sz++; unsigned var_id = to_var(arg)->get_idx(); @@ -928,10 +924,7 @@ namespace smt { unsigned first_app_sz; unsigned first_app_num_unbound_vars; // generate first the non-BIND operations - unsigned_vector::iterator it = m_todo.begin(); - unsigned_vector::iterator end = m_todo.end(); - for (; it != end; ++it) { - unsigned reg = *it; + for (unsigned reg : m_todo) { expr * p = m_registers[reg]; SASSERT(!is_quantifier(p)); if (is_var(p)) { @@ -1249,10 +1242,7 @@ namespace smt { SASSERT(head->m_next == 0); m_seq.push_back(m_ct_manager.mk_yield(m_qa, m_mp, m_qa->get_num_decls(), reinterpret_cast(m_vars.begin()))); - ptr_vector::iterator it = m_seq.begin(); - ptr_vector::iterator end = m_seq.end(); - for (; it != end; ++it) { - instruction * curr = *it; + for (instruction * curr : m_seq) { head->m_next = curr; head = curr; } @@ -1495,10 +1485,8 @@ namespace smt { } if (num_instr > SIMPLE_SEQ_THRESHOLD || (curr != nullptr && curr->m_opcode == CHOOSE)) simple = false; - unsigned_vector::iterator it = m_to_reset.begin(); - unsigned_vector::iterator end = m_to_reset.end(); - for (; it != end; ++it) - m_registers[*it] = 0; + for (unsigned reg : m_to_reset) + m_registers[reg] = 0; return weight; } @@ -1716,11 +1704,9 @@ namespace smt { m_num_choices++; // set: head -> c1 -> c2 -> c3 -> new_child_head1 curr = head; - ptr_vector::iterator it1 = m_compatible.begin(); - ptr_vector::iterator end1 = m_compatible.end(); - for (; it1 != end1; ++it1) { - set_next(curr, *it1); - curr = *it1; + for (instruction* instr : m_compatible) { + set_next(curr, instr); + curr = instr; } set_next(curr, new_child_head1); // set: new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index af761eecf..a052d1f98 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -508,13 +508,14 @@ namespace smt { mk_axiom(eqz, lower, !is_numeral); mk_axiom(eqz, upper, !is_numeral); rational k; + context& ctx = get_context(); + (void)ctx; if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && k.is_pos() && k < rational(8)) { rational j(0); #if 1 literal_buffer lits; expr_ref mod_j(m); - context& ctx = get_context(); while(j < k) { mod_j = m.mk_eq(mod, m_util.mk_numeral(j, true)); ctx.internalize(mod_j, false); @@ -542,6 +543,31 @@ namespace smt { } #endif } + if (!m_util.is_numeral(divisor)) { + // + // forall x . (or (= y 0) (= (div (* x y) y) x)) + // forall x . (=> (= y 0) (= (div (* x y) y) (div 0 0))) + // + sort* intS = m_util.mk_int(); + var_ref v(m.mk_var(0, intS), m); + app_ref mul(m_util.mk_mul(divisor, v), m); + app_ref div(m_util.mk_idiv(mul, divisor), m); + expr_ref divp1(m.mk_pattern(div), m); + app_ref mul2(m_util.mk_mul(v, divisor), m); + app_ref div2(m_util.mk_idiv(mul2, divisor), m); + expr_ref divp2(m.mk_pattern(div2), m); + expr_ref fml1(m.mk_or(m.mk_not(eqz), m.mk_eq(div, m_util.mk_idiv(zero, zero))), m); + expr_ref fml2(m.mk_or(eqz, m.mk_eq(div, v)), m); + symbol name("?x"); + expr* pats[2] = { divp1, divp2 }; + expr_ref fml(m); + fml = m.mk_forall(1, &intS, &name, fml1, 0, symbol::null, symbol::null, 2, pats, 0, nullptr); + proof_ref pr(m.mk_asserted(fml), m); + ctx.internalize_assertion(fml, pr, 0); + fml = m.mk_forall(1, &intS, &name, fml2, 0, symbol::null, symbol::null, 2, pats, 0, nullptr); + pr = m.mk_asserted(fml); + ctx.internalize_assertion(fml, pr, 0); + } } } From 9b6a99794bc394adb29b4f4347f6de8df93bda8c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Jun 2018 10:02:31 -0700 Subject: [PATCH 353/364] add default method for fresh fp value, try to address OsX build Signed-off-by: Nikolaj Bjorner --- cmake/modules/FindDotNetToolchain.cmake | 2 +- src/smt/theory_fpa.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/FindDotNetToolchain.cmake b/cmake/modules/FindDotNetToolchain.cmake index abc4ff21c..7f86534c3 100644 --- a/cmake/modules/FindDotNetToolchain.cmake +++ b/cmake/modules/FindDotNetToolchain.cmake @@ -39,7 +39,7 @@ if (DOTNET_CSC_EXECUTABLE) set(DOTNET_TOOLCHAIN_IS_WINDOWS TRUE) message(STATUS ".NET toolchain is Windows native") else() - message(STATUS ".NET toolchain is unknown") + message(STATUS ".NET toolchain is unknown: ${CSC_STD_OUT}") endif() endif() endif() diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h index 8cd01d2e9..75bcec13c 100644 --- a/src/smt/theory_fpa.h +++ b/src/smt/theory_fpa.h @@ -62,7 +62,7 @@ namespace smt { return true; } - expr * get_fresh_value(sort * s) override { NOT_IMPLEMENTED_YET(); } + expr * get_fresh_value(sort * s) override { return get_some_value(s); } void register_value(expr * n) override { /* Ignore */ } app * mk_value(mpf const & x) { From 245651305356de89c61e919257fcc747b9e6c743 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Jun 2018 10:43:51 -0700 Subject: [PATCH 354/364] sometimes comments are worth reading Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/proto_model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 493948f32..8af3af277 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -367,8 +367,8 @@ void proto_model::complete_partial_funcs(bool use_fresh) { // m_func_decls may be "expanded" when we invoke get_some_value. // So, we must not use iterators to traverse it. - for (func_decl* f : m_func_decls) { - complete_partial_func(f, use_fresh); + for (unsigned i = 0; i < m_func_decls.size(); ++i) { + complete_partial_func(m_func_decls.get(i), use_fresh); } } From eeba30a27756f64c1dcdbbe239d738d6427a1458 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Jun 2018 10:56:45 -0700 Subject: [PATCH 355/364] fix #1677 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 47 ++++++++++++++++++++++++++++++------------ src/smt/theory_seq.h | 1 + 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 3b81a35dc..d5fe54fae 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -712,12 +712,30 @@ unsigned theory_seq::find_branch_start(unsigned k) { return 0; } -bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_ref_vector const& ls, expr_ref_vector const& rs) { +expr_ref_vector theory_seq::expand_strings(expr_ref_vector const& es) { + expr_ref_vector ls(m); + for (expr* e : es) { + zstring s; + if (m_util.str.is_string(e, s)) { + for (unsigned i = 0; i < s.length(); ++i) { + ls.push_back(m_util.str.mk_unit(m_util.str.mk_char(s, i))); + } + } + else { + ls.push_back(e); + } + } + return ls; +} + +bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_ref_vector const& _ls, expr_ref_vector const& _rs) { + expr_ref_vector ls = expand_strings(_ls); + expr_ref_vector rs = expand_strings(_rs); if (ls.empty()) { return false; } - expr* l = ls[0]; + expr* l = ls.get(0); if (!is_var(l)) { return false; @@ -735,9 +753,9 @@ bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_re } for (; start < rs.size(); ++start) { unsigned j = start; - SASSERT(!m_util.str.is_concat(rs[j])); - SASSERT(!m_util.str.is_string(rs[j])); - if (l == rs[j]) { + SASSERT(!m_util.str.is_concat(rs.get(j))); + SASSERT(!m_util.str.is_string(rs.get(j))); + if (l == rs.get(j)) { return false; } if (!can_be_equal(ls.size() - 1, ls.c_ptr() + 1, rs.size() - j - 1, rs.c_ptr() + j + 1)) { @@ -752,8 +770,11 @@ bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_re } bool all_units = true; - for (unsigned j = 0; all_units && j < rs.size(); ++j) { - all_units &= m_util.str.is_unit(rs[j]); + for (expr* r : rs) { + if (!m_util.str.is_unit(r)) { + all_units = false; + break; + } } if (all_units) { context& ctx = get_context(); @@ -765,20 +786,20 @@ bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_re lits.push_back(~mk_eq(l, v0, false)); } } - for (unsigned i = 0; i < lits.size(); ++i) { - switch (ctx.get_assignment(lits[i])) { + for (literal lit : lits) { + switch (ctx.get_assignment(lit)) { case l_true: break; case l_false: start = 0; return true; - case l_undef: ctx.force_phase(~lits[i]); start = 0; return true; + case l_undef: ctx.force_phase(~lit); start = 0; return true; } } set_conflict(dep, lits); TRACE("seq", tout << "start: " << start << "\n"; - for (unsigned i = 0; i < lits.size(); ++i) { - ctx.display_literal_verbose(tout << lits[i] << ": ", lits[i]); + for (literal lit : lits) { + ctx.display_literal_verbose(tout << lit << ": ", lit); tout << "\n"; - ctx.display(tout, ctx.get_justification(lits[i].var())); + ctx.display(tout, ctx.get_justification(lit.var())); tout << "\n"; }); return true; diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 3cacf3326..2f7cfd2b3 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -447,6 +447,7 @@ namespace smt { void insert_branch_start(unsigned k, unsigned s); unsigned find_branch_start(unsigned k); bool find_branch_candidate(unsigned& start, dependency* dep, expr_ref_vector const& ls, expr_ref_vector const& rs); + expr_ref_vector expand_strings(expr_ref_vector const& es); bool can_be_equal(unsigned szl, expr* const* ls, unsigned szr, expr* const* rs) const; lbool assume_equality(expr* l, expr* r); From b5614bc93e09d857ee2b1b96c28240d16ffdeab7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Jun 2018 10:58:43 -0700 Subject: [PATCH 356/364] going Turbo Signed-off-by: Nikolaj Bjorner --- cmake/modules/FindDotNetToolchain.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/modules/FindDotNetToolchain.cmake b/cmake/modules/FindDotNetToolchain.cmake index 7f86534c3..6e8cc7610 100644 --- a/cmake/modules/FindDotNetToolchain.cmake +++ b/cmake/modules/FindDotNetToolchain.cmake @@ -33,6 +33,11 @@ if (DOTNET_CSC_EXECUTABLE) set(DOTNET_TOOLCHAIN_IS_MONO TRUE) set(DOTNET_TOOLCHAIN_IS_WINDOWS FALSE) message(STATUS ".NET toolchain is Mono") + elseif ("${CSC_STD_OUT}" MATCHES "^Turbo[ ]+C#") + set(DOTNET_DETERMINED_VENDOR TRUE) + set(DOTNET_TOOLCHAIN_IS_MONO TRUE) + set(DOTNET_TOOLCHAIN_IS_WINDOWS FALSE) + message(STATUS ".NET toolchain is Mono") elseif ("${CSC_STD_OUT}" MATCHES "^Microsoft.+Visual[ ]+C#") set(DOTNET_DETERMINED_VENDOR TRUE) set(DOTNET_TOOLCHAIN_IS_MONO FALSE) From 341f7ceb1732c8abd617b158087821c396c0c8ec Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Jun 2018 13:19:48 -0700 Subject: [PATCH 357/364] remove quantified lemmas for idiv/mod Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith_core.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index a052d1f98..cbd41f08c 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -543,6 +543,10 @@ namespace smt { } #endif } +#if 0 + // e-matching is too restrictive for multiplication. + // also suffers from use-after free so formulas have to be pinned in solver. + // if (!m_util.is_numeral(divisor)) { // // forall x . (or (= y 0) (= (div (* x y) y) x)) @@ -568,6 +572,7 @@ namespace smt { pr = m.mk_asserted(fml); ctx.internalize_assertion(fml, pr, 0); } +#endif } } From 26e9321517e1ca68a1c73ccd5522ea3f14e0e1e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Jun 2018 14:58:21 -0700 Subject: [PATCH 358/364] disable dot-net for osx Signed-off-by: Nikolaj Bjorner --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a3ffb221e..9ec6132b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,7 +84,7 @@ matrix: - os: osx osx_image: xcode8.3 # Note: Apple Clang does not support OpenMP - env: Z3_BUILD_TYPE=RelWithDebInfo USE_OPENMP=0 + env: Z3_BUILD_TYPE=RelWithDebInfo USE_OPENMP=0 DOTNET_BINDINGS=0 script: # Use `travis_wait` when doing LTO builds because this configuration will # have long link times during which it will not show any output which From 335d672bf1f923b8782d89da2b60339a29c742e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Jun 2018 23:23:19 -0700 Subject: [PATCH 359/364] fix #1675, regression in core processing in maxres Signed-off-by: Nikolaj Bjorner --- src/api/api_model.cpp | 3 +- src/cmd_context/basic_cmds.cpp | 4 +- src/model/model.cpp | 8 +- src/model/model.h | 5 +- src/model/model_core.h | 2 +- src/model/model_implicant.cpp | 18 ++- src/model/model_smt2_pp.cpp | 5 + src/model/model_smt2_pp.h | 1 + src/muz/bmc/dl_bmc_engine.cpp | 25 ++-- src/muz/spacer/spacer_context.cpp | 6 +- src/muz/spacer/spacer_legacy_mbp.cpp | 5 +- src/muz/spacer/spacer_legacy_mev.cpp | 17 ++- src/muz/spacer/spacer_qe_project.cpp | 18 ++- src/muz/spacer/spacer_sat_answer.cpp | 7 +- src/muz/spacer/spacer_unsat_core_plugin.cpp | 2 +- src/muz/tab/tab_context.cpp | 3 +- src/nlsat/tactic/nlsat_tactic.cpp | 5 +- src/opt/maxres.cpp | 146 ++++++++------------ src/opt/maxsmt.cpp | 15 +- src/opt/opt_context.cpp | 63 ++++----- src/opt/optsmt.cpp | 2 +- src/opt/pb_sls.cpp | 10 +- src/opt/sortmax.cpp | 6 +- src/opt/wmax.cpp | 8 +- src/parsers/smt2/smt2parser.cpp | 4 +- src/qe/qe_arrays.cpp | 7 +- src/qe/qe_datatypes.cpp | 3 +- src/qe/qe_mbp.cpp | 6 +- src/qe/qsat.cpp | 20 +-- src/sat/tactic/goal2sat.cpp | 4 +- src/sat/tactic/goal2sat.h | 2 +- src/shell/opt_frontend.cpp | 6 +- src/smt/mam.cpp | 38 ++--- src/smt/smt_consequences.cpp | 7 +- src/smt/smt_implied_equalities.cpp | 2 +- src/solver/mus.cpp | 3 +- src/solver/solver.cpp | 3 +- src/tactic/generic_model_converter.cpp | 15 +- src/tactic/generic_model_converter.h | 2 +- src/tactic/horn_subsume_model_converter.cpp | 3 +- src/tactic/model_converter.cpp | 21 ++- src/tactic/model_converter.h | 2 +- src/tactic/sls/sls_engine.cpp | 35 ++--- 43 files changed, 246 insertions(+), 321 deletions(-) diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 6318283c6..7eb7d2fdd 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -166,7 +166,8 @@ extern "C" { CHECK_IS_EXPR(t, Z3_FALSE); model * _m = to_model_ref(m); expr_ref result(mk_c(c)->m()); - _m->eval(to_expr(t), result, model_completion == Z3_TRUE); + model::scoped_model_completion _scm(*_m, model_completion == Z3_TRUE); + result = (*_m)(to_expr(t)); mk_c(c)->save_ast_trail(result.get()); *v = of_ast(result.get()); RETURN_Z3_model_eval Z3_TRUE; diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 4e5202fd8..846fb1ded 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -138,8 +138,8 @@ ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { macro_decls const & _m = kv.m_value; for (auto md : _m) { if (md.m_domain.size() == 0 && ctx.m().is_bool(md.m_body)) { - expr_ref val(ctx.m()); - m->eval(md.m_body, val, true); + model::scoped_model_completion _scm(*m, true); + expr_ref val = (*m)(md.m_body); if (ctx.m().is_true(val) || ctx.m().is_false(val)) { if (first) first = false; diff --git a/src/model/model.cpp b/src/model/model.cpp index 9ac971afe..6ddf9765c 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -66,12 +66,10 @@ model * model::copy() const { return m; } -// Remark: eval is for backward compatibility. We should use model_evaluator. -bool model::eval(expr * e, expr_ref & result, bool model_completion) { - model_evaluator ev(*this); - ev.set_model_completion(model_completion); +bool model::eval_expr(expr * e, expr_ref & result, bool model_completion) { + scoped_model_completion _smc(*this, model_completion); try { - ev(e, result); + result = (*this)(e); return true; } catch (model_evaluator_exception & ex) { diff --git a/src/model/model.h b/src/model/model.h index 429e7b51a..9399be285 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -46,8 +46,7 @@ public: model * copy() const; - bool eval(func_decl * f, expr_ref & r) const { return model_core::eval(f, r); } - bool eval(expr * e, expr_ref & result, bool model_completion = false); + bool eval_expr(expr * e, expr_ref & result, bool model_completion = false); expr * get_some_value(sort * s) override; ptr_vector const & get_universe(sort * s) const override; @@ -93,8 +92,6 @@ public: m_model.set_model_completion(m_old_completion); } }; - - }; diff --git a/src/model/model_core.h b/src/model/model_core.h index 400b2fcab..3f1e92bad 100644 --- a/src/model/model_core.h +++ b/src/model/model_core.h @@ -40,7 +40,7 @@ public: virtual ~model_core(); ast_manager & get_manager() const { return m_manager; } - ast_manager& m() { return m_manager; } + ast_manager& m() const { return m_manager; } unsigned get_num_decls() const { return m_decls.size(); } func_decl * get_decl(unsigned i) const { return m_decls[i]; } diff --git a/src/model/model_implicant.cpp b/src/model/model_implicant.cpp index 0cdd80c11..375834477 100644 --- a/src/model/model_implicant.cpp +++ b/src/model/model_implicant.cpp @@ -535,9 +535,8 @@ bool model_implicant::extract_array_func_interp(expr* a, vector */ void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) { TRACE("pdr", tout << "array equality: " << mk_pp(e, m) << "\n";); - expr_ref v1(m), v2(m); - m_model->eval(arg1, v1); - m_model->eval(arg2, v2); + expr_ref v1 = (*m_model)(arg1); + expr_ref v2 = (*m_model)(arg2); if (v1 == v2) { set_true(e); return; @@ -587,8 +586,8 @@ void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) { args2.append(store[i].size()-1, store[i].c_ptr()); s1 = m_array.mk_select(args1.size(), args1.c_ptr()); s2 = m_array.mk_select(args2.size(), args2.c_ptr()); - m_model->eval(s1, w1); - m_model->eval(s2, w2); + w1 = (*m_model)(s1); + w2 = (*m_model)(s2); if (w1 == w2) { continue; } @@ -621,9 +620,9 @@ void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) { eval_array_eq(e, arg1, arg2); } else if (is_x(arg1) || is_x(arg2)) { - expr_ref eq(m), vl(m); + expr_ref eq(m); eq = m.mk_eq(arg1, arg2); - m_model->eval(eq, vl); + expr_ref vl = (*m_model)(eq); if (m.is_true(vl)) { set_bool(e, true); } @@ -837,8 +836,7 @@ bool model_implicant::check_model(ptr_vector const& formulas) { eval_basic(curr); } else { - expr_ref vl(m); - m_model->eval(curr, vl); + expr_ref vl = (*m_model)(curr); assign_value(curr, vl); } @@ -884,7 +882,7 @@ expr_ref model_implicant::eval(model_ref& model, func_decl* d) { expr_ref model_implicant::eval(model_ref& model, expr* e) { expr_ref result(m); m_model = model; - VERIFY(m_model->eval(e, result, true)); + result = (*m_model)(e); if (m_array.is_array(e)) { vector stores; expr_ref_vector args(m); diff --git a/src/model/model_smt2_pp.cpp b/src/model/model_smt2_pp.cpp index 152cbc8d3..bc1900ba7 100644 --- a/src/model/model_smt2_pp.cpp +++ b/src/model/model_smt2_pp.cpp @@ -302,3 +302,8 @@ void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, u pp_consts(out, *(ctx.get()), md, indent); pp_funs(out, *(ctx.get()), md, indent); } + +std::ostream& operator<<(std::ostream& out, model_core const& m) { + model_smt2_pp(out, m.m(), m, 0); + return out; +} diff --git a/src/model/model_smt2_pp.h b/src/model/model_smt2_pp.h index c43e26ca6..b38bd631d 100644 --- a/src/model/model_smt2_pp.h +++ b/src/model/model_smt2_pp.h @@ -25,5 +25,6 @@ Revision History: void model_smt2_pp(std::ostream & out, ast_printer_context & ctx, model_core const & m, unsigned indent); void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, unsigned indent); +std::ostream& operator<<(std::ostream& out, model_core const& m); #endif diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index c7657a0d7..97432f544 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -228,10 +228,9 @@ namespace datalog { expr_ref eval_q(model_ref& model, func_decl* f, unsigned i) { func_decl_ref fn = mk_q_func_decl(f); - expr_ref t(m), result(m); + expr_ref t(m); t = m.mk_app(mk_q_func_decl(f).get(), mk_q_num(i)); - model->eval(t, result); - return result; + return (*model)(t); } expr_ref eval_q(model_ref& model, expr* t, unsigned i) { @@ -240,8 +239,7 @@ namespace datalog { num = mk_q_num(i); expr* nums[1] = { num }; vs(t, 1, nums, tmp); - model->eval(tmp, result); - return result; + return (*model)(tmp); } lbool get_model() { @@ -258,7 +256,7 @@ namespace datalog { func_decl* pred = b.m_query_pred; dl_decl_util util(m); T = m.mk_const(symbol("T"), mk_index_sort()); - md->eval(T, vl); + vl = (*md)(T); VERIFY (m_bv.is_numeral(vl, num, bv_size)); SASSERT(num.is_unsigned()); level = num.get_unsigned(); @@ -505,8 +503,7 @@ namespace datalog { func_decl_ref rule_i = mk_level_rule(pred, i, level); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); prop_r = m.mk_app(rule_i, prop->get_num_args(), prop->get_args()); - md->eval(prop_r, prop_v); - if (m.is_true(prop_v)) { + if (md->is_true(prop_r)) { r = rls[i]; break; } @@ -527,8 +524,7 @@ namespace datalog { return pr; } for (unsigned j = 0; j < sub.size(); ++j) { - md->eval(sub[j].get(), tmp); - sub[j] = tmp; + sub[j] = (*md)(sub[j].get()); } svector > positions; @@ -1062,8 +1058,7 @@ namespace datalog { return pr; } for (unsigned j = 0; j < sub.size(); ++j) { - md->eval(sub[j].get(), tmp); - sub[j] = tmp; + sub[j] = (*md)(sub.get(j)); } rule_ref rl(b.m_ctx.get_rule_manager()); rl = rules[i]; @@ -1116,7 +1111,7 @@ namespace datalog { bool check_model(model_ref& md, expr* trace) { expr_ref trace_val(m), eq(m); - md->eval(trace, trace_val); + trace_val = (*md)(trace); eq = m.mk_eq(trace, trace_val); b.m_solver.push(); b.m_solver.assert_expr(eq); @@ -1135,8 +1130,8 @@ namespace datalog { void mk_answer(model_ref& md, expr_ref& trace, expr_ref& path) { IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); - md->eval(trace, trace); - md->eval(path, path); + trace = (*md)(trace); + path = (*md)(path); IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n"; for (unsigned i = 0; i < b.m_solver.size(); ++i) { verbose_stream() << mk_pp(b.m_solver.get_formula(i), m) << "\n"; diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index b917f8cc1..f3ceee9c8 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1398,7 +1398,7 @@ bool pred_transformer::is_ctp_blocked(lemma *lem) { expr_ref lemmas(m), val(m); lemmas = pt.get_formulas(lem->level()); pm.formula_n2o(lemmas.get(), lemmas, i); - if (ctp->eval(lemmas, val) && m.is_false(val)) {return false;} + if (ctp->is_false(lemmas)) return false; } // lem is blocked by ctp since none of the lemmas at the previous @@ -2440,13 +2440,13 @@ bool context::validate() get_rule_manager (). display_smt2(r, tout) << "\n";); - model->eval(r.get_head(), tmp); + tmp = (*model)(r.get_head()); expr_ref_vector fmls(m); fmls.push_back(m.mk_not(tmp)); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { - model->eval(r.get_tail(j), tmp); + tmp = (*model)(r.get_tail(j)); fmls.push_back(tmp); } for (unsigned j = utsz; j < tsz; ++j) { diff --git a/src/muz/spacer/spacer_legacy_mbp.cpp b/src/muz/spacer/spacer_legacy_mbp.cpp index d52228d8a..76b543f04 100644 --- a/src/muz/spacer/spacer_legacy_mbp.cpp +++ b/src/muz/spacer/spacer_legacy_mbp.cpp @@ -71,10 +71,11 @@ void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& expr_substitution sub(m); proof_ref pr(m.mk_asserted(m.mk_true()), m); expr_ref bval(m); + model::scoped_model_completion _scm(*M, true); for (unsigned i = 0; i < vars.size(); i++) { if (m.is_bool(vars.get(i))) { // obtain the interpretation of the ith var using model completion - VERIFY(M->eval(vars.get(i), bval, true)); + bval = (*M)(vars.get(i)); sub.insert(vars.get(i), bval, pr); } else { arith_vars.push_back(vars.get(i)); @@ -106,7 +107,7 @@ void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& tout << "Projected arith vars:\n" << mk_pp(fml, m) << "\n"; ); } - SASSERT(M->eval(fml, bval, true) && m.is_true(bval)); // M |= fml + SASSERT(M->is_true(fml)); vars.reset(); vars.append(arith_vars); } diff --git a/src/muz/spacer/spacer_legacy_mev.cpp b/src/muz/spacer/spacer_legacy_mev.cpp index 5feaed1fa..b62b24b0d 100644 --- a/src/muz/spacer/spacer_legacy_mev.cpp +++ b/src/muz/spacer/spacer_legacy_mev.cpp @@ -461,8 +461,8 @@ void model_evaluator::eval_array_eq(app* e, expr* arg1, expr* arg2) { TRACE("old_spacer", tout << "array equality: " << mk_pp(e, m) << "\n";); expr_ref v1(m), v2(m); - m_model->eval(arg1, v1); - m_model->eval(arg2, v2); + v1 = (*m_model)(arg1); + v2 = (*m_model)(arg2); if (v1 == v2) { set_true(e); return; @@ -510,8 +510,8 @@ void model_evaluator::eval_array_eq(app* e, expr* arg1, expr* arg2) args2.append(store[i].size() - 1, store[i].c_ptr()); s1 = m_array.mk_select(args1.size(), args1.c_ptr()); s2 = m_array.mk_select(args2.size(), args2.c_ptr()); - m_model->eval(s1, w1); - m_model->eval(s2, w2); + w1 = (*m_model)(s1); + w2 = (*m_model)(s2); if (w1 == w2) { continue; } @@ -729,7 +729,7 @@ void model_evaluator::eval_fmls(ptr_vector const& formulas) eval_basic(curr); } else { expr_ref vl(m); - m_model->eval(curr, vl); + vl = eval(m_model, curr); assign_value(curr, vl); } @@ -798,11 +798,10 @@ expr_ref model_evaluator::eval(const model_ref& model, func_decl* d) return result; } -expr_ref model_evaluator::eval(const model_ref& model, expr* e) -{ - expr_ref result(m); +expr_ref model_evaluator::eval(const model_ref& model, expr* e){ m_model = model.get(); - VERIFY(m_model->eval(e, result, true)); + model::scoped_model_completion _scm(m_model, true); + expr_ref result = (*m_model)(e); if (m_array.is_array(e)) { vector stores; expr_ref_vector args(m); diff --git a/src/muz/spacer/spacer_qe_project.cpp b/src/muz/spacer/spacer_qe_project.cpp index 1bf670a7a..ed5a0215c 100644 --- a/src/muz/spacer/spacer_qe_project.cpp +++ b/src/muz/spacer/spacer_qe_project.cpp @@ -378,7 +378,7 @@ namespace spacer_qe { rational r; cx = mk_mul (c, m_var->x()); cxt = mk_add (cx, t); - VERIFY(mdl.eval(cxt, val, true)); + val = mdl(cxt); VERIFY(a.is_numeral(val, r)); SASSERT (r > rational::zero () || r < rational::zero ()); if (r > rational::zero ()) { @@ -464,8 +464,7 @@ namespace spacer_qe { m_strict.reset(); m_eq.reset (); - expr_ref var_val (m); - VERIFY (mdl.eval (m_var->x(), var_val, true)); + expr_ref var_val = mdl(m_var->x()); unsigned eq_idx = lits.size (); for (unsigned i = 0; i < lits.size(); ++i) { @@ -492,7 +491,7 @@ namespace spacer_qe { rational r; cx = mk_mul (c, m_var->x()); cxt = mk_add (cx, t); - VERIFY(mdl.eval(cxt, val, true)); + val = mdl(cxt); VERIFY(a.is_numeral(val, r)); if (is_eq) { @@ -738,7 +737,7 @@ namespace spacer_qe { rational r; cx = mk_mul (m_coeffs[max_t], m_var->x()); cxt = mk_add (cx, m_terms.get (max_t)); - VERIFY(mdl.eval(cxt, val, true)); + val = mdl(cxt); VERIFY(a.is_numeral(val, r)); // get the offset from the smallest/largest possible value for x @@ -796,13 +795,13 @@ namespace spacer_qe { // evaluate x in mdl rational r_x; - VERIFY(mdl.eval(m_var->x (), val, true)); + val = mdl(m_var->x ()); VERIFY(a.is_numeral (val, r_x)); for (unsigned i = 0; i < m_terms.size(); ++i) { rational const& ac = m_coeffs[i]; if (!m_eq[i] && ac.is_pos() == do_pos) { - VERIFY(mdl.eval(m_terms.get (i), val, true)); + val = mdl(m_terms.get (i)); VERIFY(a.is_numeral(val, r)); r /= abs(ac); // skip the literal if false in the model @@ -955,8 +954,7 @@ namespace spacer_qe { new_var = m.mk_fresh_const ("mod_var", d->get_range ()); eqs.push_back (m.mk_eq (new_var, new_term)); // obtain value of new_term in mdl - expr_ref val (m); - mdl.eval (new_term, val, true); + expr_ref val = mdl(new_term); // use the variable from now on new_term = new_var; changed = true; @@ -1413,7 +1411,7 @@ namespace spacer_qe { app_ref val_const (m.mk_fresh_const ("sel", val_sort), m); m_aux_vars.push_back (val_const); // extend M to include val_const - expr_ref val (m); + expr_ref val(m); m_mev.eval (*M, a_new, val); M->register_decl (val_const->get_decl (), val); // add equality diff --git a/src/muz/spacer/spacer_sat_answer.cpp b/src/muz/spacer/spacer_sat_answer.cpp index 09553de89..30241230f 100644 --- a/src/muz/spacer/spacer_sat_answer.cpp +++ b/src/muz/spacer/spacer_sat_answer.cpp @@ -65,10 +65,11 @@ proof_ref ground_sat_answer_op::operator()(pred_transformer &query) { SASSERT(res == l_true); model_ref mdl; m_solver->get_model(mdl); + model::scoped_model_completion _scm(mdl, true); for (unsigned i = 0, sz = query.sig_size(); i < sz; ++i) { expr_ref arg(m), val(m); arg = m.mk_const(m_pm.o2n(query.sig(i), 0)); - mdl->eval(arg, val, true); + val = (*mdl)(arg); qsubst.push_back(val); } } @@ -141,11 +142,13 @@ void ground_sat_answer_op::mk_children(frame &fr, vector &todo) { void ground_sat_answer_op::mk_child_subst_from_model(func_decl *pred, unsigned j, model_ref &mdl, expr_ref_vector &subst) { + + model::scoped_model_completion _scm(mdl, true); pred_transformer &pt = m_ctx.get_pred_transformer(pred); for (unsigned i = 0, sz = pt.sig_size(); i < sz; ++i) { expr_ref arg(m), val(m); arg = m.mk_const(m_pm.o2o(pt.sig(i), 0, j)); - mdl->eval(arg, val, true); + val = (*mdl)(arg); subst.push_back(val); } } diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index b96ef3037..8fb5feeef 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -472,7 +472,7 @@ namespace spacer { for (unsigned j = 0; j < matrix.num_cols(); ++j) { expr_ref evaluation(m); - model.get()->eval(bounded_vectors[j][k].get(), evaluation, false); + evaluation = (*model)(bounded_vectors[j][k].get()); if (!util.is_zero(evaluation)) { coeff_lits.push_back(std::make_pair(rational(1), ordered_basis[j])); } diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index 2fb329ff4..7200b3522 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -693,13 +693,12 @@ namespace tb { m_solver.assert_expr(postcond); lbool is_sat = m_solver.check(); if (is_sat == l_true) { - expr_ref tmp(m); expr* n; model_ref mdl; m_solver.get_model(mdl); for (unsigned i = 0; i < fmls.size(); ++i) { n = fmls[i].get(); - if (mdl->eval(n, tmp) && m.is_false(tmp)) { + if (mdl->is_false(n)) { m_refs.push_back(normalize(n)); m_sat_lits.insert(m_refs.back()); } diff --git a/src/nlsat/tactic/nlsat_tactic.cpp b/src/nlsat/tactic/nlsat_tactic.cpp index 1392df3f6..5e536bbe6 100644 --- a/src/nlsat/tactic/nlsat_tactic.cpp +++ b/src/nlsat/tactic/nlsat_tactic.cpp @@ -83,9 +83,8 @@ class nlsat_tactic : public tactic { bool eval_model(model& model, goal& g) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { - expr_ref val(m); - if (model.eval(g.form(i), val) && !m.is_true(val)) { - TRACE("nlsat", tout << mk_pp(g.form(i), m) << " -> " << val << "\n";); + if (!model.is_true(g.form(i))) { + TRACE("nlsat", tout << mk_pp(g.form(i), m) << " -> " << model(g.form(i)) << "\n";); return false; } } diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index 6c71c6238..912a64038 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -175,10 +175,11 @@ public: void new_assumption(expr* e, rational const& w) { IF_VERBOSE(13, verbose_stream() << "new assumption " << mk_pp(e, m) << " " << w << "\n";); - TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n";); m_asm2weight.insert(e, w); m_asms.push_back(e); m_trail.push_back(e); + TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n"; + tout << m_asms << " " << "\n"; ); } void trace() { @@ -192,7 +193,7 @@ public: trace(); if (is_sat != l_true) return is_sat; while (m_lower < m_upper) { - TRACE("opt", + TRACE("opt_verbose", display_vec(tout, m_asms); s().display(tout); tout << "\n"; @@ -204,7 +205,12 @@ public: } switch (is_sat) { case l_true: - SASSERT(is_true(m_asms)); + CTRACE("opt", !m_model->is_true(m_asms), + tout << *m_model; + tout << "assumptions: "; + for (expr* a : m_asms) tout << mk_pp(a, m) << " -> " << (*m_model)(a) << " "; + tout << "\n";); + SASSERT(m_model->is_true(m_asms)); found_optimum(); return l_true; case l_false: @@ -276,8 +282,7 @@ public: /** Give preference to cores that have large minmal values. */ - sort_assumptions(asms); - + sort_assumptions(asms); m_last_index = std::min(m_last_index, asms.size()-1); m_last_index = 0; unsigned index = m_last_index>0?m_last_index-1:0; @@ -290,8 +295,6 @@ public: index = next_index(asms, index); } first = false; - IF_VERBOSE(3, verbose_stream() << "hill climb " << index << "\n";); - // IF_VERBOSE(3, verbose_stream() << "weight: " << get_weight(asms[0].get()) << " " << get_weight(asms[index-1].get()) << " num soft: " << index << "\n";); m_last_index = index; is_sat = check_sat(index, asms.c_ptr()); } @@ -307,8 +310,9 @@ public: if (r == l_true) { model_ref mdl; s().get_model(mdl); + TRACE("opt", tout << *mdl;); if (mdl.get()) { - update_assignment(mdl.get()); + update_assignment(mdl); } } return r; @@ -318,7 +322,7 @@ public: IF_VERBOSE(1, verbose_stream() << "found optimum\n";); m_lower.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment[i] = is_true(m_soft[i]); + m_assignment[i] = m_model->is_true(m_soft[i]); if (!m_assignment[i]) { m_lower += m_weights[i]; } @@ -347,7 +351,6 @@ public: lbool get_cores(vector& cores) { // assume m_s is unsat. lbool is_sat = l_false; - expr_ref_vector asms(m_asms); cores.reset(); exprs core; while (is_sat == l_false) { @@ -370,6 +373,10 @@ public: m_lower = m_upper; return l_true; } + // 1. remove all core literals from m_asms + // 2. re-add literals of higher weight than min-weight. + // 3. 'core' stores the core literals that are split afterwards + remove_soft(core, m_asms); split_core(core); cores.push_back(core); if (core.size() >= m_max_core_size) { @@ -378,15 +385,14 @@ public: if (cores.size() >= m_max_num_cores) { break; } - remove_soft(core, asms); - is_sat = check_sat_hill_climb(asms); + is_sat = check_sat_hill_climb(m_asms); } TRACE("opt", tout << "num cores: " << cores.size() << "\n"; for (auto const& c : cores) { display_vec(tout, c); } - tout << "num satisfying: " << asms.size() << "\n";); + tout << "num satisfying: " << m_asms.size() << "\n";); return is_sat; } @@ -394,7 +400,7 @@ public: void get_current_correction_set(exprs& cs) { model_ref mdl; s().get_model(mdl); - update_assignment(mdl.get()); + update_assignment(mdl); get_current_correction_set(mdl.get(), cs); } @@ -402,10 +408,10 @@ public: cs.reset(); if (!mdl) return; for (expr* a : m_asms) { - if (is_false(mdl, a)) { + if (mdl->is_false(a)) { cs.push_back(a); } - TRACE("opt", expr_ref tmp(m); mdl->eval(a, tmp, true); tout << mk_pp(a, m) << ": " << tmp << "\n";); + // TRACE("opt", tout << mk_pp(a, m) << ": " << (*mdl)(a) << "\n";); } TRACE("opt", display_vec(tout << "new correction set: ", cs);); } @@ -444,7 +450,7 @@ public: ++m_stats.m_num_cs; expr_ref fml(m), tmp(m); TRACE("opt", display_vec(tout << "corr_set: ", corr_set);); - remove_core(corr_set); + remove_soft(corr_set, m_asms); rational w = split_core(corr_set); cs_max_resolve(corr_set, w); IF_VERBOSE(2, verbose_stream() << "(opt.maxres.correction-set " << corr_set.size() << ")\n";); @@ -484,18 +490,13 @@ public: void update_model(expr* def, expr* value) { SASSERT(is_uninterp_const(def)); if (m_csmodel) { - expr_ref val(m); - SASSERT(m_csmodel.get()); - if (m_csmodel->eval(value, val, true)) { - m_csmodel->register_decl(to_app(def)->get_decl(), val); - } + m_csmodel->register_decl(to_app(def)->get_decl(), (*m_csmodel)(value)); } } void process_unsat(exprs const& core) { IF_VERBOSE(3, verbose_stream() << "(maxres cs model valid: " << (m_csmodel.get() != nullptr) << " cs size:" << m_correction_set_size << " core: " << core.size() << ")\n";); expr_ref fml(m); - remove_core(core); SASSERT(!core.empty()); rational w = core_weight(core); TRACE("opt", display_vec(tout << "minimized core: ", core);); @@ -536,7 +537,7 @@ public: w = m_mus.get_best_model(mdl); } if (mdl.get() && w < m_upper) { - update_assignment(mdl.get()); + update_assignment(mdl); } return nullptr != mdl.get(); } @@ -707,10 +708,11 @@ public: s().assert_expr(fml); } - void update_assignment(model* mdl) { + void update_assignment(model_ref & mdl) { + mdl->set_model_completion(true); unsigned correction_set_size = 0; for (expr* a : m_asms) { - if (is_false(mdl, a)) { + if (mdl->is_false(a)) { ++correction_set_size; } } @@ -719,41 +721,45 @@ public: m_correction_set_size = correction_set_size; } + TRACE("opt", tout << *mdl;); + rational upper(0); - expr_ref tmp(m); + unsigned i = 0; for (expr* s : m_soft) { - if (!is_true(mdl, s)) { + TRACE("opt", tout << mk_pp(s, m) << ": " << (*mdl)(s) << " " << m_weights[i] << "\n";); + if (!mdl->is_true(s)) { upper += m_weights[i]; } ++i; } if (upper > m_upper) { + TRACE("opt", tout << "new upper: " << upper << " vs existing upper: " << m_upper << "\n";); return; } - if (!m_c.verify_model(m_index, mdl, upper)) { + if (!m_c.verify_model(m_index, mdl.get(), upper)) { return; } m_model = mdl; - m_c.model_updated(mdl); + m_c.model_updated(mdl.get()); - TRACE("opt", model_smt2_pp(tout << "updated model\n", m, *m_model, 0);); + TRACE("opt", tout << "updated upper: " << upper << "\nmodel\n" << *m_model;); i = 0; for (expr* s : m_soft) { - m_assignment[i++] = is_true(s); + m_assignment[i++] = m_model->is_true(s); } // DEBUG_CODE(verify_assignment();); m_upper = upper; + trace(); add_upper_bound_block(); - } void add_upper_bound_block() { @@ -769,54 +775,28 @@ public: s().assert_expr(fml); } - bool is_true(model* mdl, expr* e) { - expr_ref tmp(m); - return mdl->eval(e, tmp, true) && m.is_true(tmp); - } - - bool is_false(model* mdl, expr* e) { - expr_ref tmp(m); - return mdl->eval(e, tmp, true) && m.is_false(tmp); - } - - bool is_true(expr* e) { - return is_true(m_model.get(), e); - } - - bool is_true(expr_ref_vector const& es) { - unsigned i = 0; - for (; i < es.size() && is_true(es[i]); ++i) { } - CTRACE("opt_bug", i < es.size(), tout << mk_pp(es[i], m) << "\n"; - model_smt2_pp(tout, m, *m_model, 0);); - return i == es.size(); - } - void remove_soft(exprs const& core, expr_ref_vector& asms) { - for (unsigned i = 0; i < asms.size(); ++i) { - if (core.contains(asms[i].get())) { - asms[i] = asms.back(); - asms.pop_back(); - --i; - } - } + TRACE("opt", tout << "before remove: " << asms << "\n";); + unsigned j = 0; + for (expr* a : asms) + if (!core.contains(a)) + asms[j++] = a; + asms.shrink(j); + TRACE("opt", tout << "after remove: " << asms << "\n";); } - void remove_core(exprs const& core) { - remove_soft(core, m_asms); - } - - virtual void updt_params(params_ref& p) { - maxsmt_solver_base::updt_params(p); - opt_params _p(p); - m_hill_climb = _p.maxres_hill_climb(); - m_add_upper_bound_block = _p.maxres_add_upper_bound_block(); - m_max_num_cores = _p.maxres_max_num_cores(); - m_max_core_size = _p.maxres_max_core_size(); - m_maximize_assignment = _p.maxres_maximize_assignment(); - m_max_correction_set_size = _p.maxres_max_correction_set_size(); - m_pivot_on_cs = _p.maxres_pivot_on_correction_set(); - m_wmax = _p.maxres_wmax(); - m_dump_benchmarks = _p.dump_benchmarks(); + virtual void updt_params(params_ref& _p) { + maxsmt_solver_base::updt_params(_p); + opt_params p(_p); + m_hill_climb = p.maxres_hill_climb(); + m_add_upper_bound_block = p.maxres_add_upper_bound_block(); + m_max_num_cores = p.maxres_max_num_cores(); + m_max_core_size = p.maxres_max_core_size(); + m_maximize_assignment = p.maxres_maximize_assignment(); + m_max_correction_set_size = p.maxres_max_correction_set_size(); + m_pivot_on_cs = p.maxres_pivot_on_correction_set(); + m_wmax = p.maxres_wmax(); + m_dump_benchmarks = p.dump_benchmarks(); } lbool init_local() { @@ -828,9 +808,8 @@ public: if (is_sat != l_true) { return is_sat; } - obj_map::iterator it = new_soft.begin(), end = new_soft.end(); - for (; it != end; ++it) { - add_soft(it->m_key, it->m_value); + for (auto const& kv : new_soft) { + add_soft(kv.m_key, kv.m_value); } m_max_upper = m_upper; m_found_feasible_optimum = false; @@ -843,10 +822,7 @@ public: virtual void commit_assignment() { if (m_found_feasible_optimum) { - TRACE("opt", tout << "Committing feasible solution\n"; - tout << m_defs; - tout << m_asms; - ); + TRACE("opt", tout << "Committing feasible solution\n" << m_defs << " " << m_asms;); s().assert_expr(m_defs); s().assert_expr(m_asms); } diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index fc5bd6bbb..c97b818cf 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -76,9 +76,7 @@ namespace opt { m_upper.reset(); m_assignment.reset(); for (unsigned i = 0; i < m_weights.size(); ++i) { - expr_ref val(m); - if (!m_model->eval(m_soft[i], val)) return false; - m_assignment.push_back(m.is_true(val)); + m_assignment.push_back(m.is_true(m_soft[i])); if (!m_assignment.back()) { m_upper += m_weights[i]; } @@ -232,9 +230,7 @@ namespace opt { m_msolver = nullptr; symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); - TRACE("opt", tout << "maxsmt\n"; - s().display(tout); tout << "\n"; - ); + TRACE("opt_verbose", s().display(tout << "maxsmt\n") << "\n";); if (m_soft_constraints.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) { m_msolver = mk_maxres(m_c, m_index, m_weights, m_soft_constraints); } @@ -455,10 +451,9 @@ namespace opt { maxsmt.get_model(m_model, labels); // TBD: is m_fm applied or not? unsigned j = 0; - expr_ref tmp(m); - for (unsigned i = 0; i < soft.size(); ++i) { - if (m_model->eval(soft[i].first, tmp) && m.is_true(tmp)) { - soft[j++] = soft[i]; + for (auto const& p : soft) { + if (m_model->is_true(p.first)) { + soft[j++] = p; } } soft.shrink(j); diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 37dbd0817..af28d2254 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -341,6 +341,7 @@ namespace opt { void context::fix_model(model_ref& mdl) { if (mdl && !m_model_fixed.contains(mdl.get())) { + TRACE("opt", tout << "fix-model\n";); (*m_fm)(mdl); apply(m_model_converter, mdl); m_model_fixed.push_back(mdl.get()); @@ -350,7 +351,7 @@ namespace opt { void context::get_model_core(model_ref& mdl) { mdl = m_model; fix_model(mdl); - TRACE("opt", model_smt2_pp(tout, m, *mdl.get(), 0);); + TRACE("opt", tout << *mdl;); } void context::get_box_model(model_ref& mdl, unsigned index) { @@ -502,7 +503,8 @@ namespace opt { case O_MINIMIZE: is_ge = !is_ge; case O_MAXIMIZE: - if (mdl->eval(obj.m_term, val, true) && is_numeral(val, k)) { + val = (*mdl)(obj.m_term); + if (is_numeral(val, k)) { if (is_ge) { result = mk_ge(obj.m_term, val); } @@ -522,7 +524,7 @@ namespace opt { for (unsigned i = 0; i < sz; ++i) { terms.push_back(obj.m_terms[i]); coeffs.push_back(obj.m_weights[i]); - if (mdl->eval(obj.m_terms[i], val, true) && m.is_true(val)) { + if (mdl->is_true(obj.m_terms[i])) { k += obj.m_weights[i]; } else { @@ -1036,7 +1038,7 @@ namespace opt { buffer << prefix << (m_model_counter++) << ".smt2"; std::ofstream out(buffer.str()); if (out) { - model_smt2_pp(out, m, *mdl, 0); + out << *mdl; out.close(); } } @@ -1052,11 +1054,7 @@ namespace opt { expr_ref val(m); model_ref mdl = md->copy(); fix_model(mdl); - - if (!mdl->eval(term, val, true)) { - TRACE("opt", tout << "Term does not evaluate " << term << "\n";); - return false; - } + val = (*mdl)(term); unsigned bvsz; if (!m_arith.is_numeral(val, r) && !m_bv.is_numeral(val, r, bvsz)) { TRACE("opt", tout << "model does not evaluate objective to a value\n";); @@ -1195,9 +1193,9 @@ namespace opt { rational r; switch(obj.m_type) { case O_MINIMIZE: { - bool evaluated = m_model->eval(obj.m_term, val, true); - TRACE("opt", tout << obj.m_term << " " << val << " " << evaluated << " " << is_numeral(val, r) << "\n";); - if (evaluated && is_numeral(val, r)) { + val = (*m_model)(obj.m_term); + TRACE("opt", tout << obj.m_term << " " << val << " " << is_numeral(val, r) << "\n";); + if (is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); TRACE("opt", tout << "adjusted value: " << val << "\n";); if (is_lower) { @@ -1210,9 +1208,9 @@ namespace opt { break; } case O_MAXIMIZE: { - bool evaluated = m_model->eval(obj.m_term, val, true); + val = (*m_model)(obj.m_term); TRACE("opt", tout << obj.m_term << " " << val << "\n";); - if (evaluated && is_numeral(val, r)) { + if (is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); TRACE("opt", tout << "adjusted value: " << val << "\n";); if (is_lower) { @@ -1227,15 +1225,10 @@ namespace opt { case O_MAXSMT: { bool ok = true; for (unsigned j = 0; ok && j < obj.m_terms.size(); ++j) { - bool evaluated = m_model->eval(obj.m_terms[j], val, true); + val = (*m_model)(obj.m_terms[j]); TRACE("opt", tout << mk_pp(obj.m_terms[j], m) << " " << val << "\n";); - if (evaluated) { - if (!m.is_true(val)) { - r += obj.m_weights[j]; - } - } - else { - ok = false; + if (!m.is_true(val)) { + r += obj.m_weights[j]; } } if (ok) { @@ -1485,7 +1478,7 @@ namespace opt { } if (is_internal && mc) { - mc->collect(visitor); + mc->set_env(&visitor); } param_descrs descrs; @@ -1531,7 +1524,9 @@ namespace opt { if (is_internal && mc) { mc->display(out); } - + if (is_internal && mc) { + mc->set_env(nullptr); + } out << "(check-sat)\n"; return out.str(); } @@ -1545,7 +1540,7 @@ namespace opt { model_ref mdl; get_model(mdl); for (expr * f : fmls) { - if (!mdl->eval(f, tmp) || !m.is_true(tmp)) { + if (!mdl->is_true(f)) { //IF_VERBOSE(0, m_fm->display(verbose_stream() << "fm\n")); IF_VERBOSE(0, m_model_converter->display(verbose_stream() << "mc\n")); IF_VERBOSE(0, verbose_stream() << "Failed to validate " << mk_pp(f, m) << "\n" << tmp << "\n"); @@ -1559,18 +1554,14 @@ namespace opt { void context::validate_maxsat(symbol const& id) { maxsmt& ms = *m_maxsmts.find(id); TRACE("opt", tout << "Validate: " << id << "\n";); - for (unsigned i = 0; i < m_objectives.size(); ++i) { - objective const& obj = m_objectives[i]; + for (objective const& obj : m_objectives) { if (obj.m_id == id && obj.m_type == O_MAXSMT) { SASSERT(obj.m_type == O_MAXSMT); rational value(0); expr_ref val(m); for (unsigned i = 0; i < obj.m_terms.size(); ++i) { - bool evaluated = m_model->eval(obj.m_terms[i], val); - SASSERT(evaluated); - CTRACE("opt", evaluated && !m.is_true(val) && !m.is_false(val), tout << mk_pp(obj.m_terms[i], m) << " " << val << "\n";); - CTRACE("opt", !evaluated, tout << mk_pp(obj.m_terms[i], m) << "\n";); - if (evaluated && !m.is_true(val)) { + auto const& t = obj.m_terms[i]; + if (!m_model->is_true(t)) { value += obj.m_weights[i]; } // TBD: check that optimal was not changed. @@ -1595,14 +1586,13 @@ namespace opt { if (m_optsmt.objective_is_model_valid(obj.m_index) && n.get_infinity().is_zero() && n.get_infinitesimal().is_zero() && - m_model->eval(obj.m_term, val) && - is_numeral(val, r1)) { + is_numeral((*m_model)(obj.m_term), r1)) { rational r2 = n.get_rational(); if (obj.m_type == O_MINIMIZE) { r1.neg(); } CTRACE("opt", r1 != r2, tout << obj.m_term << " evaluates to " << r1 << " but has objective " << r2 << "\n";); - CTRACE("opt", r1 != r2, model_smt2_pp(tout, m, *m_model, 0);); + CTRACE("opt", r1 != r2, tout << *m_model;); SASSERT(r1 == r2); } break; @@ -1610,8 +1600,7 @@ namespace opt { case O_MAXSMT: { rational value(0); for (unsigned i = 0; i < obj.m_terms.size(); ++i) { - bool evaluated = m_model->eval(obj.m_terms[i], val); - if (evaluated && !m.is_true(val)) { + if (!m_model->is_true(obj.m_terms[i])) { value += obj.m_weights[i]; } // TBD: check that optimal was not changed. diff --git a/src/opt/optsmt.cpp b/src/opt/optsmt.cpp index 702702ef4..a461d4a22 100644 --- a/src/opt/optsmt.cpp +++ b/src/opt/optsmt.cpp @@ -318,7 +318,7 @@ namespace opt { m_s->get_labels(m_labels); for (unsigned i = 0; i < ors.size(); ++i) { expr_ref tmp(m); - if (m_model->eval(ors[i].get(), tmp) && m.is_true(tmp)) { + if (m_model->is_true(ors[i].get())) { m_lower[i] = m_upper[i]; ors[i] = m.mk_false(); disj[i] = m.mk_false(); diff --git a/src/opt/pb_sls.cpp b/src/opt/pb_sls.cpp index e28c3cd3d..9054e2b00 100644 --- a/src/opt/pb_sls.cpp +++ b/src/opt/pb_sls.cpp @@ -179,7 +179,7 @@ namespace smt { m_orig_model = mdl; for (unsigned i = 0; i < m_var2decl.size(); ++i) { expr_ref tmp(m); - m_assignment[i] = mdl->eval(m_var2decl[i], tmp) && m.is_true(tmp); + m_assignment[i] = mdl->is_true(m_var2decl[i]); } } @@ -343,10 +343,7 @@ namespace smt { for (unsigned i = 0; i < m_clauses.size(); ++i) { if (!eval(m_clauses[i])) { m_hard_false.insert(i); - expr_ref tmp(m); - if (!m_orig_model->eval(m_orig_clauses[i].get(), tmp)) { - return; - } + expr_ref tmp = (*m_orig_model)(m_orig_clauses[i].get()); IF_VERBOSE(0, verbose_stream() << "original evaluation: " << tmp << "\n"; verbose_stream() << mk_pp(m_orig_clauses[i].get(), m) << "\n"; @@ -521,14 +518,13 @@ namespace smt { literal mk_aux_literal(expr* f) { unsigned var; - expr_ref tmp(m); if (!m_decl2var.find(f, var)) { var = m_hard_occ.size(); SASSERT(m_var2decl.size() == var); SASSERT(m_soft_occ.size() == var); m_hard_occ.push_back(unsigned_vector()); m_soft_occ.push_back(unsigned_vector()); - m_assignment.push_back(m_orig_model->eval(f, tmp) && m.is_true(tmp)); + m_assignment.push_back(m_orig_model->is_true(f)); m_decl2var.insert(f, var); m_var2decl.push_back(f); } diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp index 9c45e42a2..6e7e6ec78 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -73,8 +73,7 @@ namespace opt { unsigned first = 0; it = soft.begin(); for (; it != end; ++it) { - expr_ref tmp(m); - if (m_model->eval(it->m_key, tmp) && m.is_true(tmp)) { + if (m_model->is_true(it->m_key)) { unsigned n = it->m_value.get_unsigned(); while (n > 0) { s().assert_expr(out[first]); @@ -121,8 +120,7 @@ namespace opt { } bool is_true(expr* e) { - expr_ref tmp(m); - return m_model->eval(e, tmp) && m.is_true(tmp); + return m_model->is_true(e); } // definitions used for sorting network diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index 513c68d6e..97231f69e 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -120,8 +120,7 @@ namespace opt { } bool is_true(expr* e) { - expr_ref tmp(m); - return m_model->eval(e, tmp) && m.is_true(tmp); + return m_model->is_true(e); } void update_assignment() { @@ -307,9 +306,8 @@ namespace opt { } void update_model(expr* def, expr* value) { - expr_ref val(m); - if (m_model && m_model->eval(value, val, true)) { - m_model->register_decl(to_app(def)->get_decl(), val); + if (m_model) { + m_model->register_decl(to_app(def)->get_decl(), (*m_model)(value)); } } diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 9276b60cd..016c6f4e5 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -2611,8 +2611,8 @@ namespace smt2 { expr ** expr_it = expr_stack().c_ptr() + spos; expr ** expr_end = expr_it + m_cached_strings.size(); for (unsigned i = 0; expr_it < expr_end; expr_it++, i++) { - expr_ref v(m()); - md->eval(*expr_it, v, true); + model::scoped_model_completion _scm(md, true); + expr_ref v = (*md)(*expr_it); if (i > 0) m_ctx.regular_stream() << "\n "; m_ctx.regular_stream() << "(" << m_cached_strings[i] << " "; diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index a4da829a0..4c81418b6 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -1181,9 +1181,8 @@ namespace qe { indices(ast_manager& m, model& model, unsigned n, expr* const* vars): m_values(m), m_vars(vars) { expr_ref val(m); - for (unsigned i = 0; i < n; ++i) { - VERIFY(model.eval(vars[i], val)); - m_values.push_back(val); + for (unsigned i = 0; i < n; ++i) { + m_values.push_back(model(vars[i])); } } }; @@ -1269,7 +1268,7 @@ namespace qe { args.push_back (s); args.append(idxs[i].m_values.size(), idxs[i].m_vars); sel = a.mk_select (args.size (), args.c_ptr ()); - VERIFY (model.eval (sel, val)); + val = model(sel); model.register_decl (var->get_decl (), val); args[0] = result; diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp index 87ae97857..5499d638d 100644 --- a/src/qe/qe_datatypes.cpp +++ b/src/qe/qe_datatypes.cpp @@ -42,8 +42,7 @@ namespace qe { } bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - expr_ref val(m); - VERIFY(model.eval(var, val)); + expr_ref val = model(var); SASSERT(is_app(val)); TRACE("qe", tout << mk_pp(var, m) << " := " << val << "\n";); m_val = to_app(val); diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 438f1c061..78dcbfd16 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -66,7 +66,7 @@ expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { app* alit = to_app(t); for (expr * e1 : *alit) { expr *e2; - VERIFY(model.eval(e1, val)); + val = model(e1); if (val2expr.find(val, e2)) { return expr_ref(m.mk_eq(e1, e2), m); } @@ -501,7 +501,7 @@ public: expr_ref val(m); model_evaluator eval(model); for (expr * f : fmls) { - VERIFY(model.eval(f, val) && m.is_true(val)); + VERIFY(model.is_true(f)); } return true; } @@ -538,7 +538,7 @@ public: var = new_vars.back(); new_vars.pop_back(); expr_safe_replace sub(m); - VERIFY(model.eval(var, val)); + val = model(var); sub.insert(var, val); for (unsigned i = 0; i < fmls.size(); ++i) { sub(fmls[i].get(), tmp); diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 64cde4b1d..a2e8fd8b1 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -516,8 +516,8 @@ namespace qe { expr_ref val_a(m), val_b(m); expr* a = it->m_key; expr* b = it->m_value; - VERIFY(model.eval(a, val_a)); - VERIFY(model.eval(b, val_b)); + val_a = model(a); + val_b = model(b); if (val_a != val_b) { TRACE("qe", tout << mk_pp(a, m) << " := " << val_a << "\n"; @@ -1060,11 +1060,9 @@ namespace qe { } bool validate_assumptions(model& mdl, expr_ref_vector const& core) { - for (unsigned i = 0; i < core.size(); ++i) { - expr_ref val(m); - VERIFY(mdl.eval(core[i], val)); - if (!m.is_true(val)) { - TRACE("qe", tout << "component of core is not true: " << mk_pp(core[i], m) << "\n";); + for (expr* c : core) { + if (!mdl.is_true(c)) { + TRACE("qe", tout << "component of core is not true: " << mk_pp(c, m) << "\n";); return false; } } @@ -1111,14 +1109,10 @@ namespace qe { bool validate_model(model& mdl, unsigned sz, expr* const* fmls) { expr_ref val(m); for (unsigned i = 0; i < sz; ++i) { - if (!m_model->eval(fmls[i], val) && !m.canceled()) { - TRACE("qe", tout << "Formula does not evaluate in model: " << mk_pp(fmls[i], m) << "\n";); + if (!m_model->is_true(fmls[i]) && !m.canceled()) { + TRACE("qe", tout << "Formula does not evaluate to true in model: " << mk_pp(fmls[i], m) << "\n";); return false; } - if (!m.is_true(val)) { - TRACE("qe", tout << mk_pp(fmls[i], m) << " evaluates to " << val << " in model\n";); - return false; - } } return true; } diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 75290946a..5f0fbec16 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -968,9 +968,9 @@ model_converter* sat2goal::mc::translate(ast_translation& translator) { return result; } -void sat2goal::mc::collect(ast_pp_util& visitor) { +void sat2goal::mc::set_env(ast_pp_util* visitor) { flush_gmc(); - if (m_gmc) m_gmc->collect(visitor); + if (m_gmc) m_gmc->set_env(visitor); } void sat2goal::mc::display(std::ostream& out) { diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index 32b89fe5d..d86b2d7e4 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -92,7 +92,7 @@ public: void operator()(model_ref& md) override; void operator()(expr_ref& fml) override; model_converter* translate(ast_translation& translator) override; - void collect(ast_pp_util& visitor) override; + void set_env(ast_pp_util* visitor) override; void display(std::ostream& out) override; void get_units(obj_map& units) override; app* var2expr(sat::bool_var v) const { return m_var2expr.get(v, nullptr); } diff --git a/src/shell/opt_frontend.cpp b/src/shell/opt_frontend.cpp index 5af5bfdd7..0a2a8f4a1 100644 --- a/src/shell/opt_frontend.cpp +++ b/src/shell/opt_frontend.cpp @@ -121,10 +121,8 @@ static unsigned parse_opt(std::istream& in, opt_format f) { expr_ref_vector hard(m); opt.get_hard_constraints(hard); for (expr* h : hard) { - expr_ref tmp(m); - VERIFY(mdl->eval(h, tmp)); - if (!m.is_true(tmp)) { - std::cout << mk_pp(h, m) << " " << tmp << "\n"; + if (!mdl->is_true(h)) { + std::cout << mk_pp(h, m) << " evaluates to: " << (*mdl)(h) << "\n"; } } } diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index ff62dacec..c35a0b180 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -569,10 +569,9 @@ namespace smt { if (m_context) { ast_manager & m = m_context->get_manager(); out << "patterns:\n"; - ptr_vector::const_iterator it = m_patterns.begin(); - ptr_vector::const_iterator end = m_patterns.end(); - for (; it != end; ++it) - out << mk_pp(*it, m) << "\n"; + for (expr* p : m_patterns) { + out << mk_pp(p, m) << "\n"; + } } #endif out << "function: " << m_root_lbl->get_name(); @@ -831,10 +830,8 @@ namespace smt { void init(code_tree * t, quantifier * qa, app * mp, unsigned first_idx) { SASSERT(m_ast_manager.is_pattern(mp)); #ifdef Z3DEBUG - svector::iterator it = m_mark.begin(); - svector::iterator end = m_mark.end(); - for (; it != end; ++it) { - SASSERT(*it == NOT_CHECKED); + for (auto cm : m_mark) { + SASSERT(cm == NOT_CHECKED); } #endif m_tree = t; @@ -1711,14 +1708,12 @@ namespace smt { set_next(curr, new_child_head1); // set: new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head curr = new_child_head1; - ptr_vector::iterator it2 = m_incompatible.begin(); - ptr_vector::iterator end2 = m_incompatible.end(); - for (; it2 != end2; ++it2) { + for (instruction* inc : m_incompatible) { if (curr == new_child_head1) - curr->m_next = *it2; // new_child_head1 is a new node, I don't need to save trail + curr->m_next = inc; // new_child_head1 is a new node, I don't need to save trail else - set_next(curr, *it2); - curr = *it2; + set_next(curr, inc); + curr = inc; } set_next(curr, first_child_head); // build new_child_head2:NOOP -> linearise() @@ -3353,10 +3348,7 @@ namespace smt { void update_vars(unsigned short var_id, path * p, quantifier * qa, app * mp) { paths & var_paths = m_var_paths[var_id]; bool found = false; - paths::iterator it = var_paths.begin(); - paths::iterator end = var_paths.end(); - for (; it != end; ++it) { - path * curr_path = *it; + for (path* curr_path : var_paths) { if (is_equal(p, curr_path)) found = true; func_decl * lbl1 = curr_path->m_label; @@ -3647,18 +3639,12 @@ namespace smt { TRACE("incremental_matcher", tout << "pp: plbls1: " << plbls1 << ", plbls2: " << plbls2 << "\n";); TRACE("mam_info", tout << "pp: " << plbls1.size() * plbls2.size() << "\n";); if (!plbls1.empty() && !plbls2.empty()) { - approx_set::iterator it1 = plbls1.begin(); - approx_set::iterator end1 = plbls1.end(); - for (; it1 != end1; ++it1) { + for (unsigned plbl1 : plbls1) { if (m_context.get_cancel_flag()) { break; } - unsigned plbl1 = *it1; SASSERT(plbls1.may_contain(plbl1)); - approx_set::iterator it2 = plbls2.begin(); - approx_set::iterator end2 = plbls2.end(); - for (; it2 != end2; ++it2) { - unsigned plbl2 = *it2; + for (unsigned plbl2 : plbls2) { SASSERT(plbls2.may_contain(plbl2)); unsigned n_plbl1 = plbl1; unsigned n_plbl2 = plbl2; diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp index 13fd9e6ea..4cb331661 100644 --- a/src/smt/smt_consequences.cpp +++ b/src/smt/smt_consequences.cpp @@ -637,15 +637,14 @@ namespace smt { model_ref mdl; for (unsigned i = 0; i < unfixed.size(); ++i) { push(); - for (unsigned j = 0; j < assumptions.size(); ++j) { - assert_expr(assumptions[j]); - } + for (expr* a : assumptions) + assert_expr(a); TRACE("context", tout << "checking unfixed: " << mk_pp(unfixed[i], m) << "\n";); lbool is_sat = check(); SASSERT(is_sat != l_false); if (is_sat == l_true) { get_model(mdl); - mdl->eval(unfixed[i], tmp); + tmp = (*mdl)(unfixed[i]); if (m.is_value(tmp)) { tmp = m.mk_not(m.mk_eq(unfixed[i], tmp)); assert_expr(tmp); diff --git a/src/smt/smt_implied_equalities.cpp b/src/smt/smt_implied_equalities.cpp index 0b944c145..9119119d3 100644 --- a/src/smt/smt_implied_equalities.cpp +++ b/src/smt/smt_implied_equalities.cpp @@ -199,7 +199,7 @@ namespace smt { for (unsigned i = 0; i < terms.size(); ++i) { expr* t = terms[i].term; - model->eval(t, vl); + vl = (*model)(t); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " |-> " << mk_pp(vl, m) << "\n";); reduce_value(model, vl); if (!m.is_value(vl)) { diff --git a/src/solver/mus.cpp b/src/solver/mus.cpp index b3d6c9ad9..094b27ed3 100644 --- a/src/solver/mus.cpp +++ b/src/solver/mus.cpp @@ -332,8 +332,7 @@ struct mus::imp { m_solver.get_model(mdl); rational w; for (unsigned i = 0; i < m_soft.size(); ++i) { - mdl->eval(m_soft[i].get(), tmp); - if (!m.is_true(tmp)) { + if (!mdl->is_true(m_soft.get(i))) { w += m_weights[i]; } } diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 84b5eb588..186449545 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -44,7 +44,7 @@ std::ostream& solver::display(std::ostream & out, unsigned n, expr* const* assum ast_pp_util visitor(get_manager()); model_converter_ref mc = get_model_converter(); if (mc.get()) { - mc->collect(visitor); + mc->set_env(&visitor); } visitor.collect(fmls); visitor.collect(n, assumptions); @@ -52,6 +52,7 @@ std::ostream& solver::display(std::ostream & out, unsigned n, expr* const* assum visitor.display_asserts(out, fmls, true); if (mc.get()) { mc->display(out); + mc->set_env(nullptr); } return out; } diff --git a/src/tactic/generic_model_converter.cpp b/src/tactic/generic_model_converter.cpp index 1911edf3b..0790c962a 100644 --- a/src/tactic/generic_model_converter.cpp +++ b/src/tactic/generic_model_converter.cpp @@ -114,11 +114,16 @@ model_converter * generic_model_converter::translate(ast_translation & translato return res; } -void generic_model_converter::collect(ast_pp_util& visitor) { - m_env = &visitor.env(); - for (entry const& e : m_entries) { - visitor.coll.visit_func(e.m_f); - if (e.m_def) visitor.coll.visit(e.m_def); +void generic_model_converter::set_env(ast_pp_util* visitor) { + if (!visitor) { + m_env = nullptr; + } + else { + m_env = &visitor->env(); + for (entry const& e : m_entries) { + visitor->coll.visit_func(e.m_f); + if (e.m_def) visitor->coll.visit(e.m_def); + } } } diff --git a/src/tactic/generic_model_converter.h b/src/tactic/generic_model_converter.h index d303fb2e3..a190b8525 100644 --- a/src/tactic/generic_model_converter.h +++ b/src/tactic/generic_model_converter.h @@ -68,7 +68,7 @@ public: model_converter * translate(ast_translation & translator) override; - void collect(ast_pp_util& visitor) override; + void set_env(ast_pp_util* visitor) override; void operator()(expr_ref& fml) override; diff --git a/src/tactic/horn_subsume_model_converter.cpp b/src/tactic/horn_subsume_model_converter.cpp index 0d49a769e..eeb2967f2 100644 --- a/src/tactic/horn_subsume_model_converter.cpp +++ b/src/tactic/horn_subsume_model_converter.cpp @@ -195,8 +195,7 @@ void horn_subsume_model_converter::operator()(model_ref& mr) { SASSERT(m.is_bool(body)); TRACE("mc", tout << "eval: " << h->get_name() << "\n" << body << "\n";); - expr_ref tmp(body); - mr->eval(tmp, body); + body = (*mr)(body); TRACE("mc", tout << "to:\n" << body << "\n";); diff --git a/src/tactic/model_converter.cpp b/src/tactic/model_converter.cpp index b39940366..89e2b6602 100644 --- a/src/tactic/model_converter.cpp +++ b/src/tactic/model_converter.cpp @@ -43,6 +43,16 @@ void model_converter::display_del(std::ostream& out, func_decl* f) const { } } +void model_converter::set_env(ast_pp_util* visitor) { + if (visitor) { + m_env = &visitor->env(); + } + else { + m_env = nullptr; + } +} + + void model_converter::display_add(std::ostream& out, ast_manager& m) { // default printer for converter that adds entries model_ref mdl = alloc(model, m); @@ -91,9 +101,9 @@ public: return this->translate_core(translator); } - void collect(ast_pp_util& visitor) override { - this->m_c1->collect(visitor); - this->m_c2->collect(visitor); + void set_env(ast_pp_util* visitor) override { + this->m_c1->set_env(visitor); + this->m_c2->set_env(visitor); } }; @@ -125,9 +135,8 @@ public: } void operator()(expr_ref& fml) override { - expr_ref r(m_model->get_manager()); - m_model->eval(fml, r, false); - fml = r; + model::scoped_model_completion _scm(m_model, false); + fml = (*m_model)(fml); } void get_units(obj_map& fmls) override { diff --git a/src/tactic/model_converter.h b/src/tactic/model_converter.h index cd8fb5ee3..9c5b72830 100644 --- a/src/tactic/model_converter.h +++ b/src/tactic/model_converter.h @@ -80,7 +80,7 @@ public: virtual model_converter * translate(ast_translation & translator) = 0; - virtual void collect(ast_pp_util& visitor) { m_env = &visitor.env(); } + virtual void set_env(ast_pp_util* visitor); /** \brief we are adding a formula to the context of the model converter. diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index 7836ab70f..f5b5ec1b2 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -100,36 +100,27 @@ void sls_engine::checkpoint() { } bool sls_engine::full_eval(model & mdl) { - bool res = true; - - unsigned sz = m_assertions.size(); - for (unsigned i = 0; i < sz && res; i++) { - checkpoint(); - expr_ref o(m_manager); - - if (!mdl.eval(m_assertions[i], o, true)) - exit(ERR_INTERNAL_FATAL); - - res = m_manager.is_true(o.get()); - } - - TRACE("sls", tout << "Evaluation: " << res << std::endl;); - - return res; + model::scoped_model_completion _scm(mdl, true); + for (expr* a : m_assertions) { + checkpoint(); + if (!mdl.is_true(a)) { + TRACE("sls", tout << "Evaluation: false\n";); + return false; + } + } + return true; } double sls_engine::top_score() { double top_sum = 0.0; - unsigned sz = m_assertions.size(); - for (unsigned i = 0; i < sz; i++) { - expr * e = m_assertions[i]; + for (expr* e : m_assertions) { top_sum += m_tracker.get_score(e); } TRACE("sls_top", tout << "Score distribution:"; - for (unsigned i = 0; i < sz; i++) - tout << " " << m_tracker.get_score(m_assertions[i]); - tout << " AVG: " << top_sum / (double)sz << std::endl;); + for (expr* e : m_assertions) + tout << " " << m_tracker.get_score(e); + tout << " AVG: " << top_sum / (double)m_assertions.size() << std::endl;); m_tracker.set_top_sum(top_sum); From 792bf6c10b3fba2a77001e930d1d5796f15a105d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Jun 2018 08:22:15 -0700 Subject: [PATCH 360/364] fix tests Signed-off-by: Nikolaj Bjorner --- src/opt/maxres.cpp | 72 +++++++++++++++++++------------------------ src/opt/maxsmt.cpp | 43 ++++++++++++++------------ src/opt/maxsmt.h | 19 +++++++++--- src/opt/sortmax.cpp | 4 +-- src/opt/wmax.cpp | 5 +-- src/solver/solver.cpp | 7 +++++ src/solver/solver.h | 2 ++ src/test/qe_arith.cpp | 3 +- 8 files changed, 81 insertions(+), 74 deletions(-) diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index 912a64038..f52c56a60 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -321,10 +321,10 @@ public: void found_optimum() { IF_VERBOSE(1, verbose_stream() << "found optimum\n";); m_lower.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment[i] = m_model->is_true(m_soft[i]); - if (!m_assignment[i]) { - m_lower += m_weights[i]; + for (soft& s : m_soft) { + s.is_true = m_model->is_true(s.s); + if (!s.is_true) { + m_lower += s.weight; } } m_upper = m_lower; @@ -375,24 +375,22 @@ public: } // 1. remove all core literals from m_asms // 2. re-add literals of higher weight than min-weight. - // 3. 'core' stores the core literals that are split afterwards + // 3. 'core' stores the core literals that are + // re-encoded as assumptions, afterwards remove_soft(core, m_asms); split_core(core); cores.push_back(core); - if (core.size() >= m_max_core_size) { - break; - } - if (cores.size() >= m_max_num_cores) { - break; - } + + if (core.size() >= m_max_core_size) break; + if (cores.size() >= m_max_num_cores) break; + is_sat = check_sat_hill_climb(m_asms); } + TRACE("opt", - tout << "num cores: " << cores.size() << "\n"; - for (auto const& c : cores) { - display_vec(tout, c); - } - tout << "num satisfying: " << m_asms.size() << "\n";); + tout << "sat: " << is_sat << " num cores: " << cores.size() << "\n"; + for (auto const& c : cores) display_vec(tout, c); + tout << "num assumptions: " << m_asms.size() << "\n";); return is_sat; } @@ -411,7 +409,6 @@ public: if (mdl->is_false(a)) { cs.push_back(a); } - // TRACE("opt", tout << mk_pp(a, m) << ": " << (*mdl)(a) << "\n";); } TRACE("opt", display_vec(tout << "new correction set: ", cs);); } @@ -475,8 +472,8 @@ public: unsigned max_core_size(vector const& cores) { unsigned result = 0; - for (unsigned i = 0; i < cores.size(); ++i) { - result = std::max(cores[i].size(), result); + for (auto const& c : cores) { + result = std::max(c.size(), result); } return result; } @@ -725,13 +722,11 @@ public: rational upper(0); - unsigned i = 0; - for (expr* s : m_soft) { - TRACE("opt", tout << mk_pp(s, m) << ": " << (*mdl)(s) << " " << m_weights[i] << "\n";); - if (!mdl->is_true(s)) { - upper += m_weights[i]; + for (soft& s : m_soft) { + TRACE("opt", tout << s.s << ": " << (*mdl)(s.s) << " " << s.weight << "\n";); + if (!mdl->is_true(s.s)) { + upper += s.weight; } - ++i; } if (upper > m_upper) { @@ -748,9 +743,8 @@ public: TRACE("opt", tout << "updated upper: " << upper << "\nmodel\n" << *m_model;); - i = 0; - for (expr* s : m_soft) { - m_assignment[i++] = m_model->is_true(s); + for (soft& s : m_soft) { + s.is_true = m_model->is_true(s.s); } // DEBUG_CODE(verify_assignment();); @@ -766,11 +760,13 @@ public: if (!m_add_upper_bound_block) return; pb_util u(m); expr_ref_vector nsoft(m); + vector weights; expr_ref fml(m); - for (expr* s : m_soft) { - nsoft.push_back(mk_not(m, s)); + for (soft& s : m_soft) { + nsoft.push_back(mk_not(m, s.s)); + weights.push_back(s.weight); } - fml = u.mk_lt(nsoft.size(), m_weights.c_ptr(), nsoft.c_ptr(), m_upper); + fml = u.mk_lt(nsoft.size(), weights.c_ptr(), nsoft.c_ptr(), m_upper); TRACE("opt", tout << "block upper bound " << fml << "\n";);; s().assert_expr(fml); } @@ -832,9 +828,7 @@ public: void verify_core(exprs const& core) { IF_VERBOSE(3, verbose_stream() << "verify core\n";); ref smt_solver = mk_smt_solver(m, m_params, symbol()); - for (unsigned i = 0; i < s().get_num_assertions(); ++i) { - smt_solver->assert_expr(s().get_assertion(i)); - } + smt_solver->assert_expr(s().get_assertions()); smt_solver->assert_expr(core); lbool is_sat = smt_solver->check_sat(0, nullptr); if (is_sat == l_true) { @@ -845,13 +839,11 @@ public: void verify_assignment() { IF_VERBOSE(1, verbose_stream() << "verify assignment\n";); ref smt_solver = mk_smt_solver(m, m_params, symbol()); - for (unsigned i = 0; i < s().get_num_assertions(); ++i) { - smt_solver->assert_expr(s().get_assertion(i)); - } + smt_solver->assert_expr(s().get_assertions()); expr_ref n(m); - for (unsigned i = 0; i < m_soft.size(); ++i) { - n = m_soft[i]; - if (!m_assignment[i]) { + for (soft& s : m_soft) { + n = s.s; + if (!s.is_true) { n = mk_not(m, n); } smt_solver->assert_expr(n); diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index c97b818cf..1b44b578b 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -34,16 +34,17 @@ Notes: namespace opt { maxsmt_solver_base::maxsmt_solver_base( - maxsat_context& c, vector const& ws, expr_ref_vector const& soft): + maxsat_context& c, vector const& ws, expr_ref_vector const& softs): m(c.get_manager()), m_c(c), - m_soft(soft), - m_weights(ws), m_assertions(m), m_trail(m) { c.get_base_model(m_model); SASSERT(m_model); updt_params(c.params()); + for (unsigned i = 0; i < ws.size(); ++i) { + m_soft.push_back(soft(expr_ref(softs.get(i), m), ws[i], false)); + } } void maxsmt_solver_base::updt_params(params_ref& p) { @@ -56,17 +57,21 @@ namespace opt { void maxsmt_solver_base::commit_assignment() { expr_ref tmp(m); + expr_ref_vector fmls(m); rational k(0), cost(0); - for (unsigned i = 0; i < m_soft.size(); ++i) { - if (get_assignment(i)) { - k += m_weights[i]; + vector weights; + for (soft const& s : m_soft) { + if (s.is_true) { + k += s.weight; } else { - cost += m_weights[i]; + cost += s.weight; } + weights.push_back(s.weight); + fmls.push_back(s.s); } pb_util pb(m); - tmp = pb.mk_ge(m_weights.size(), m_weights.c_ptr(), m_soft.c_ptr(), k); + tmp = pb.mk_ge(weights.size(), weights.c_ptr(), fmls.c_ptr(), k); TRACE("opt", tout << "cost: " << cost << "\n" << tmp << "\n";); s().assert_expr(tmp); } @@ -74,19 +79,14 @@ namespace opt { bool maxsmt_solver_base::init() { m_lower.reset(); m_upper.reset(); - m_assignment.reset(); - for (unsigned i = 0; i < m_weights.size(); ++i) { - m_assignment.push_back(m.is_true(m_soft[i])); - if (!m_assignment.back()) { - m_upper += m_weights[i]; - } + for (soft& s : m_soft) { + s.is_true = m.is_true(s.s); + if (!s.is_true) m_upper += s.weight; } TRACE("opt", tout << "upper: " << m_upper << " assignments: "; - for (unsigned i = 0; i < m_weights.size(); ++i) { - tout << (m_assignment[i]?"T":"F"); - } + for (soft& s : m_soft) tout << (s.is_true?"T":"F"); tout << "\n";); return true; } @@ -141,6 +141,7 @@ namespace opt { maxsmt_solver_base::scoped_ensure_theory::scoped_ensure_theory(maxsmt_solver_base& s) { m_wth = s.ensure_wmax_theory(); } + maxsmt_solver_base::scoped_ensure_theory::~scoped_ensure_theory() { if (m_wth) { m_wth->reset_local(); @@ -159,11 +160,13 @@ namespace opt { lbool maxsmt_solver_base::find_mutexes(obj_map& new_soft) { m_lower.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - new_soft.insert(m_soft[i], m_weights[i]); + expr_ref_vector fmls(m); + for (soft& s : m_soft) { + new_soft.insert(s.s, s.weight); + fmls.push_back(s.s); } vector mutexes; - lbool is_sat = s().find_mutexes(m_soft, mutexes); + lbool is_sat = s().find_mutexes(fmls, mutexes); if (is_sat != l_true) { return is_sat; } diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index 422cf26b9..de38baf32 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -56,17 +56,26 @@ namespace opt { // class maxsmt_solver_base : public maxsmt_solver { protected: + struct soft { + expr_ref s; + rational weight; + bool is_true; + soft(expr_ref& s, rational const& w, bool t): s(s), weight(w), is_true(t) {} + soft(soft const& other):s(other.s), weight(other.weight), is_true(other.is_true) {} + soft& operator=(soft const& other) { s = other.s; weight = other.weight; is_true = other.is_true; return *this; } + }; ast_manager& m; - maxsat_context& m_c; - const expr_ref_vector m_soft; - vector m_weights; + maxsat_context& m_c; + vector m_soft; expr_ref_vector m_assertions; expr_ref_vector m_trail; rational m_lower; rational m_upper; model_ref m_model; svector m_labels; - svector m_assignment; // truth assignment to soft constraints + //const expr_ref_vector m_soft; + //vector m_weights; + //svector m_assignment; // truth assignment to soft constraints params_ref m_params; // config public: @@ -75,7 +84,7 @@ namespace opt { ~maxsmt_solver_base() override {} rational get_lower() const override { return m_lower; } rational get_upper() const override { return m_upper; } - bool get_assignment(unsigned index) const override { return m_assignment[index]; } + bool get_assignment(unsigned index) const override { return m_soft[index].is_true; } void collect_statistics(statistics& st) const override { } void get_model(model_ref& mdl, svector& labels) override { mdl = m_model.get(); labels = m_labels;} virtual void commit_assignment(); diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp index 6e7e6ec78..4313cfbec 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -114,9 +114,7 @@ namespace opt { } void update_assignment() { - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment[i] = is_true(m_soft[i]); - } + for (soft& s : m_soft) s.is_true = is_true(s.s); } bool is_true(expr* e) { diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index 97231f69e..e4eb7e06b 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -124,10 +124,7 @@ namespace opt { } void update_assignment() { - m_assignment.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment.push_back(is_true(m_soft[i])); - } + for (soft& s : m_soft) s.is_true = is_true(s.s); } struct compare_asm { diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 186449545..4044c4a85 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -64,6 +64,13 @@ void solver::get_assertions(expr_ref_vector& fmls) const { } } +expr_ref_vector solver::get_assertions() const { + expr_ref_vector result(get_manager()); + get_assertions(result); + return result; +} + + struct scoped_assumption_push { expr_ref_vector& m_vec; scoped_assumption_push(expr_ref_vector& v, expr* e): m_vec(v) { v.push_back(e); } diff --git a/src/solver/solver.h b/src/solver/solver.h index c4df362e4..c371be284 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -179,6 +179,8 @@ public: */ void get_assertions(expr_ref_vector& fmls) const; + expr_ref_vector get_assertions() const; + /** \brief The number of tracked assumptions (see assert_expr(t, a)). */ diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index d18e0f717..031912c46 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -88,8 +88,7 @@ static void test(app* var, expr_ref& fml) { std::cout << "projected: " << mk_pp(pr, m) << "\n"; // projection is consistent with model. - expr_ref tmp(m); - VERIFY(md->eval(pr, tmp) && m.is_true(tmp)); + VERIFY(md->is_true(pr)); // projection implies E x. fml { From 0c32989144709ca7fddff62737990a05b4d22242 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Jun 2018 15:07:21 -0700 Subject: [PATCH 361/364] change to const qualifier on constructor Signed-off-by: Nikolaj Bjorner --- src/opt/maxsmt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index de38baf32..b61d876b3 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -60,7 +60,7 @@ namespace opt { expr_ref s; rational weight; bool is_true; - soft(expr_ref& s, rational const& w, bool t): s(s), weight(w), is_true(t) {} + soft(expr_ref const& s, rational const& w, bool t): s(s), weight(w), is_true(t) {} soft(soft const& other):s(other.s), weight(other.weight), is_true(other.is_true) {} soft& operator=(soft const& other) { s = other.s; weight = other.weight; is_true = other.is_true; return *this; } }; From 19e2f8c9d5281772e75f087dfde7c0883418852b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Jun 2018 17:35:41 -0700 Subject: [PATCH 362/364] fix #1694 Signed-off-by: Nikolaj Bjorner --- src/nlsat/nlsat_explain.cpp | 23 ++++++++++-------- src/qe/nlqsat.cpp | 10 ++------ src/tactic/core/tseitin_cnf_tactic.cpp | 33 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 420174506..a93935fb6 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -216,9 +216,10 @@ namespace nlsat { max_var(p) must be assigned in the current interpretation. */ int sign(polynomial_ref const & p) { - TRACE("nlsat_explain", tout << "p: " << p << " var: " << max_var(p) << "\n";); SASSERT(max_var(p) == null_var || m_assignment.is_assigned(max_var(p))); - return m_am.eval_sign_at(p, m_assignment); + int s = m_am.eval_sign_at(p, m_assignment); + TRACE("nlsat_explain", tout << "p: " << p << " var: " << max_var(p) << " sign: " << s << "\n";); + return s; } /** @@ -1452,7 +1453,6 @@ namespace nlsat { SASSERT(check_already_added()); SASSERT(num > 0); TRACE("nlsat_explain", tout << "[explain] set of literals is infeasible in the current interpretation\n"; display(tout, num, ls);); - // exit(0); m_result = &result; process(num, ls); reset_already_added(); @@ -1738,11 +1738,13 @@ namespace nlsat { void solve_eq(var x, unsigned idx, polynomial_ref_vector const& ps) { polynomial_ref p(m_pm), A(m_pm), B(m_pm), C(m_pm), D(m_pm), E(m_pm), q(m_pm), r(m_pm); - polynomial_ref_vector qs(m_pm); + polynomial_ref_vector As(m_pm), Bs(m_pm); p = ps.get(idx); SASSERT(degree(p, x) == 1); A = m_pm.coeff(p, x, 1); B = m_pm.coeff(p, x, 0); + As.push_back(m_pm.mk_const(rational(1))); + Bs.push_back(m_pm.mk_const(rational(1))); B = neg(B); TRACE("nlsat_explain", tout << "p: " << p << " A: " << A << " B: " << B << "\n";); // x = B/A @@ -1753,20 +1755,21 @@ namespace nlsat { D = m_pm.mk_const(rational(1)); E = D; r = m_pm.mk_zero(); - for (unsigned j = 0; j <= d; ++j) { - qs.push_back(D); - D = D*A; + for (unsigned j = As.size(); j <= d; ++j) { + D = As.back(); As.push_back(A * D); + D = Bs.back(); Bs.push_back(B * D); } for (unsigned j = 0; j <= d; ++j) { // A^d*p0 + A^{d-1}*B*p1 + ... + B^j*A^{d-j}*pj + ... + B^d*p_d C = m_pm.coeff(q, x, j); + TRACE("nlsat_explain", tout << "coeff: q" << j << ": " << C << "\n";); if (!is_zero(C)) { - D = qs.get(d-j); + D = As.get(d - j); + E = Bs.get(j); r = r + D*E*C; } - E = E*B; } - TRACE("nlsat_explain", tout << "q: " << q << " r: " << r << "\n";); + TRACE("nlsat_explain", tout << "p: " << p << " q: " << q << " r: " << r << "\n";); ensure_sign(r); } else { diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index 95f6b9b21..1ae9723c9 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -646,12 +646,8 @@ namespace qe { while (!vars.empty()); SASSERT(qvars.size() >= 2); SASSERT(qvars.back().empty()); - ackermanize_div(is_forall, qvars, fml); - init_expr2var(qvars); - - goal2nlsat g2s; expr_ref is_true(m), fml1(m), fml2(m); @@ -672,9 +668,7 @@ namespace qe { m_bound_rvars.push_back(nlsat::var_vector()); max_level lvl; if (is_exists(i)) lvl.m_ex = i; else lvl.m_fa = i; - for (unsigned j = 0; j < qvars[i].size(); ++j) { - app* v = qvars[i][j].get(); - + for (app* v : qvars[i]) { if (m_a2b.is_var(v)) { SASSERT(m.is_bool(v)); nlsat::bool_var b = m_a2b.to_var(v); @@ -696,7 +690,7 @@ namespace qe { m_is_true = nlsat::literal(m_a2b.to_var(is_true), false); // insert literals from arithmetical sub-formulas nlsat::atom_vector const& atoms = m_solver.get_atoms(); - TRACE("qe", m_solver.display(tout); ); + TRACE("qe", m_solver.display(tout);); for (unsigned i = 0; i < atoms.size(); ++i) { if (atoms[i]) { get_level(nlsat::literal(i, false)); diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index 9c57fe791..b29a80927 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -466,6 +466,38 @@ class tseitin_cnf_tactic : public tactic { } return NO; } + + mres match_iff_or(app * t, bool first, bool root) { + expr * a = nullptr, * _b = nullptr; + if (!root) return NO; + if (!is_iff(m, t, a, _b)) return NO; + bool sign = m.is_not(_b, _b); + if (!m.is_or(_b)) return NO; + app* b = to_app(_b); + unsigned num = b->get_num_args(); + if (first) { + bool visited = true; + visit(a, visited); + for (expr* arg : *b) { + visit(arg, visited); + } + if (!visited) + return CONT; + } + expr_ref la(m), nla(m), nlb(m), lb(m); + get_lit(a, sign, la); + inv(la, nla); + expr_ref_buffer lits(m); + lits.push_back(nla); + for (expr* arg : *b) { + get_lit(arg, false, lb); + lits.push_back(lb); + inv(lb, nlb); + mk_clause(la, nlb); + } + mk_clause(lits.size(), lits.c_ptr()); + return DONE; + } mres match_iff(app * t, bool first, bool root) { expr * a, * b; @@ -784,6 +816,7 @@ class tseitin_cnf_tactic : public tactic { TRY(match_or_3and); TRY(match_or); TRY(match_iff3); + // TRY(match_iff_or); TRY(match_iff); TRY(match_ite); TRY(match_not); From fc8b1d9a7dab2489f593379f398f057ab2289281 Mon Sep 17 00:00:00 2001 From: rainoftime Date: Fri, 22 Jun 2018 16:46:47 +0800 Subject: [PATCH 363/364] Refine default_tactic: if the constraint is an SAT instance and proof is not enabled, then use the qffd tactic --- src/tactic/portfolio/default_tactic.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tactic/portfolio/default_tactic.cpp b/src/tactic/portfolio/default_tactic.cpp index 51cda17cd..7f71114f4 100644 --- a/src/tactic/portfolio/default_tactic.cpp +++ b/src/tactic/portfolio/default_tactic.cpp @@ -31,9 +31,11 @@ Notes: #include "tactic/fpa/qffplra_tactic.h" #include "tactic/smtlogics/qfaufbv_tactic.h" #include "tactic/smtlogics/qfauflia_tactic.h" +#include "tactic/portfolio/fd_solver.h" tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), + cond(mk_is_propositional_probe(), if_no_proofs(mk_fd_tactic(m, p)), cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), cond(mk_is_qfaufbv_probe(), mk_qfaufbv_tactic(m), cond(mk_is_qflia_probe(), mk_qflia_tactic(m), @@ -46,7 +48,7 @@ tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { cond(mk_is_qffp_probe(), mk_qffp_tactic(m, p), cond(mk_is_qffplra_probe(), mk_qffplra_tactic(m, p), //cond(mk_is_qfufnra_probe(), mk_qfufnra_tactic(m, p), - mk_smt_tactic())))))))))))), + mk_smt_tactic()))))))))))))), p); return st; } From e1870233041a5a8cfb4fc7a0ab0c406e80d56eef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Jun 2018 21:57:10 -0700 Subject: [PATCH 364/364] fix #1699 Signed-off-by: Nikolaj Bjorner --- src/opt/opt_context.cpp | 4 +++- src/opt/opt_pareto.cpp | 2 ++ src/tactic/sine_filter.cpp | 26 ++++++++------------------ 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index af28d2254..566aaa1f6 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -351,6 +351,7 @@ namespace opt { void context::get_model_core(model_ref& mdl) { mdl = m_model; fix_model(mdl); + mdl->set_model_completion(true); TRACE("opt", tout << *mdl;); } @@ -528,7 +529,7 @@ namespace opt { k += obj.m_weights[i]; } else { - TRACE("opt", tout << val << "\n";); + TRACE("opt", tout << (*mdl)(obj.m_terms[i]) << "\n";); } } if (is_ge) { @@ -1539,6 +1540,7 @@ namespace opt { expr_ref tmp(m); model_ref mdl; get_model(mdl); + mdl->set_model_completion(true); for (expr * f : fmls) { if (!mdl->is_true(f)) { //IF_VERBOSE(0, m_fm->display(verbose_stream() << "fm\n")); diff --git a/src/opt/opt_pareto.cpp b/src/opt/opt_pareto.cpp index 56eed72ac..fe92c3ba6 100644 --- a/src/opt/opt_pareto.cpp +++ b/src/opt/opt_pareto.cpp @@ -40,6 +40,7 @@ namespace opt { } m_solver->get_model(m_model); m_solver->get_labels(m_labels); + m_model->set_model_completion(true); IF_VERBOSE(1, model_ref mdl(m_model); cb.fix_model(mdl); @@ -99,6 +100,7 @@ namespace opt { if (is_sat == l_true) { m_solver->get_model(m_model); m_solver->get_labels(m_labels); + m_model->set_model_completion(true); mk_not_dominated_by(); } return is_sat; diff --git a/src/tactic/sine_filter.cpp b/src/tactic/sine_filter.cpp index 0ac726986..647791784 100644 --- a/src/tactic/sine_filter.cpp +++ b/src/tactic/sine_filter.cpp @@ -93,11 +93,8 @@ private: obj_hashtable const & consts, ptr_vector & next_consts) { TRACE("sine", - tout << "size of consts is "; tout << consts.size(); tout << "\n"; - obj_hashtable::iterator it = consts.begin(); - obj_hashtable::iterator end = consts.end(); - for (; it != end; it++) - tout << *it << "\n"; ); + tout << "size of consts is "; tout << consts.size(); tout << "\n"; + for (func_decl* f : consts) tout << f->get_name() << "\n";); bool matched = false; for (unsigned i = 0; i < q->get_num_patterns(); i++) { @@ -156,10 +153,8 @@ private: if (!consts.contains(f)) { consts.insert(f); if (const2quantifier.contains(f)) { - obj_pair_hashtable::iterator it = const2quantifier[f].begin(); - obj_pair_hashtable::iterator end = const2quantifier[f].end(); - for (; it != end; it++) - stack.push_back(*it); + for (auto const& p : const2quantifier[f]) + stack.push_back(p); const2quantifier.remove(f); } } @@ -220,16 +215,11 @@ private: visiting = to_visit.back(); to_visit.pop_back(); visited.insert(visiting); - obj_hashtable::iterator it = exp2const[visiting].begin(); - obj_hashtable::iterator end = exp2const[visiting].end(); - for (; it != end; it++) { - obj_hashtable::iterator exprit = const2exp[*it].begin(); - obj_hashtable::iterator exprend = const2exp[*it].end(); - for (; exprit != exprend; exprit++) { - if (!visited.contains(*exprit)) - to_visit.push_back(*exprit); + for (func_decl* f : exp2const[visiting]) + for (expr* e : const2exp[f]) { + if (!visited.contains(e)) + to_visit.push_back(e); } - } } for (unsigned i = 0; i < g->size(); i++) {