diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..b86ed5df7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index ab5d0132c..77f7702f2 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -906,6 +906,72 @@ void enum_sort_example() { std::cout << "2: " << result_goal.as_expr() << std::endl; } +void expr_vector_example() { + std::cout << "expr_vector example\n"; + context c; + const unsigned N = 10; + + expr_vector x(c); + + for (unsigned i = 0; i < N; i++) { + std::stringstream x_name; + x_name << "x_" << i; + x.push_back(c.int_const(x_name.str().c_str())); + } + + solver s(c); + for (unsigned i = 0; i < N; i++) { + s.add(x[i] >= 1); + } + + std::cout << s << "\n" << "solving...\n" << s.check() << "\n"; + model m = s.get_model(); + std::cout << "solution\n" << m; +} + +void exists_expr_vector_example() { + std::cout << "exists expr_vector example\n"; + context c; + const unsigned N = 10; + + expr_vector xs(c); + expr x(c); + expr b(c); + b = c.bool_val(true); + + for (unsigned i = 0; i < N; i++) { + std::stringstream x_name; + x_name << "x_" << i; + x = c.int_const(x_name.str().c_str()); + xs.push_back(x); + b = b && x >= 0; + } + + expr ex(c); + ex = exists(xs, b); + std::cout << ex << std::endl; +} + +void substitute_example() { + std::cout << "substitute example\n"; + context c; + expr x(c); + x = c.int_const("x"); + expr f(c); + f = (x == 2) || (x == 1); + std::cout << f << std::endl; + + expr two(c), three(c); + two = c.int_val(2); + three = c.int_val(3); + Z3_ast from[] = { two }; + Z3_ast to[] = { three }; + expr new_f(c); + new_f = to_expr(c, Z3_substitute(c, f, 1, from, to)); + + std::cout << new_f << std::endl; +} + int main() { try { demorgan(); std::cout << "\n"; @@ -937,10 +1003,13 @@ int main() { tactic_example9(); std::cout << "\n"; tactic_qe(); std::cout << "\n"; tst_visit(); std::cout << "\n"; - incremental_example1(); std::cout << "\n"; - incremental_example2(); std::cout << "\n"; - incremental_example3(); std::cout << "\n"; + incremental_example1(); std::cout << "\n"; + incremental_example2(); std::cout << "\n"; + incremental_example3(); std::cout << "\n"; enum_sort_example(); std::cout << "\n"; + expr_vector_example(); std::cout << "\n"; + exists_expr_vector_example(); std::cout << "\n"; + substitute_example(); std::cout << "\n"; std::cout << "done\n"; } catch (exception & ex) { diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 6ea794040..e6e7d5dc8 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -54,7 +54,7 @@ def init_project_def(): add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') # TODO: split muz_qe into muz, qe. Perhaps, we should also consider breaking muz into muz and pdr. - add_lib('muz_qe', ['smt', 'sat', 'smt2parser']) + add_lib('muz_qe', ['smt', 'sat', 'smt2parser', 'aig_tactic']) add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'muz_qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 680b59c68..6855f6209 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -46,7 +46,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_int_symbol(c, i); RESET_ERROR_CODE(); - if (i < 0 || (unsigned)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { + if (i < 0 || (size_t)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { SET_ERROR_CODE(Z3_IOB); return 0; } @@ -1070,6 +1070,10 @@ extern "C" { case OP_BV2INT: return Z3_OP_BV2INT; case OP_CARRY: return Z3_OP_CARRY; case OP_XOR3: return Z3_OP_XOR3; + case OP_BSMUL_NO_OVFL: + case OP_BUMUL_NO_OVFL: + case OP_BSMUL_NO_UDFL: + return Z3_OP_UNINTERPRETED; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 8126c8e2a..07d4fda18 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -121,10 +121,20 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ unsigned sz = Z3_get_bv_sort_size(c, s); rational max_bound = power(rational(2), sz); Z3_ast bound = Z3_mk_numeral(c, max_bound.to_string().c_str(), int_s); - Z3_ast pred = Z3_mk_bvslt(c, n, Z3_mk_int(c, 0, s)); + Z3_inc_ref(c, bound); + Z3_ast zero = Z3_mk_int(c, 0, s); + Z3_inc_ref(c, zero); + Z3_ast pred = Z3_mk_bvslt(c, n, zero); + Z3_inc_ref(c, pred); // if n <_sigend 0 then r - s^sz else r Z3_ast args[2] = { r, bound }; - Z3_ast res = Z3_mk_ite(c, pred, Z3_mk_sub(c, 2, args), r); + Z3_ast sub = Z3_mk_sub(c, 2, args); + Z3_inc_ref(c, sub); + Z3_ast res = Z3_mk_ite(c, pred, sub, r); + Z3_dec_ref(c, bound); + Z3_dec_ref(c, pred); + Z3_dec_ref(c, sub); + Z3_dec_ref(c, zero); RETURN_Z3(res); } else { @@ -156,7 +166,14 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } - return Z3_mk_bvshl(c, Z3_mk_int64(c, 1, s), Z3_mk_int64(c, sz - 1, s)); + Z3_ast x = Z3_mk_int64(c, 1, s); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_int64(c, sz - 1, s); + Z3_inc_ref(c, y); + Z3_ast result = Z3_mk_bvshl(c, x, y); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + return result; Z3_CATCH_RETURN(0); } @@ -177,17 +194,40 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); + Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - Z3_ast args[2] = { Z3_mk_bvslt(c, zero, t1), Z3_mk_bvslt(c, zero, t2) }; + Z3_inc_ref(c, r); + Z3_ast l1 = Z3_mk_bvslt(c, zero, t1); + Z3_inc_ref(c, l1); + Z3_ast l2 = Z3_mk_bvslt(c, zero, t2); + Z3_inc_ref(c, l2); + Z3_ast args[2] = { l1, l2 }; Z3_ast args_pos = Z3_mk_and(c, 2, args); - return Z3_mk_implies(c, args_pos, Z3_mk_bvslt(c, zero, r)); + Z3_inc_ref(c, args_pos); + Z3_ast result = Z3_mk_implies(c, args_pos, Z3_mk_bvslt(c, zero, r)); + Z3_dec_ref(c, r); + Z3_dec_ref(c, l1); + Z3_dec_ref(c, l2); + Z3_dec_ref(c, args_pos); + Z3_dec_ref(c, zero); + return result; } else { unsigned sz = Z3_get_bv_sort_size(c, Z3_get_sort(c, t1)); t1 = Z3_mk_zero_ext(c, 1, t1); + Z3_inc_ref(c, t1); t2 = Z3_mk_zero_ext(c, 1, t2); + Z3_inc_ref(c, t2); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - return Z3_mk_eq(c, Z3_mk_extract(c, sz, sz, r), Z3_mk_int(c, 0, Z3_mk_bv_sort(c, 1))); + Z3_inc_ref(c, r); + Z3_ast ex = Z3_mk_extract(c, sz, sz, r); + Z3_inc_ref(c, ex); + Z3_ast result = Z3_mk_eq(c, ex, Z3_mk_int(c, 0, Z3_mk_bv_sort(c, 1))); + Z3_dec_ref(c, t1); + Z3_dec_ref(c, t2); + Z3_dec_ref(c, ex); + Z3_dec_ref(c, r); + return result; } Z3_CATCH_RETURN(0); } @@ -197,10 +237,26 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; RESET_ERROR_CODE(); Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); + Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - Z3_ast args[2] = { Z3_mk_bvslt(c, t1, zero), Z3_mk_bvslt(c, t2, zero) }; + Z3_inc_ref(c, r); + Z3_ast l1 = Z3_mk_bvslt(c, t1, zero); + Z3_inc_ref(c, l1); + Z3_ast l2 = Z3_mk_bvslt(c, t2, zero); + Z3_inc_ref(c, l2); + Z3_ast args[2] = { l1, l2 }; Z3_ast args_neg = Z3_mk_and(c, 2, args); - return Z3_mk_implies(c, args_neg, Z3_mk_bvslt(c, r, zero)); + Z3_inc_ref(c, args_neg); + Z3_ast lt = Z3_mk_bvslt(c, r, zero); + Z3_inc_ref(c, lt); + Z3_ast result = Z3_mk_implies(c, args_neg, lt); + Z3_dec_ref(c, lt); + Z3_dec_ref(c, l1); + Z3_dec_ref(c, l2); + Z3_dec_ref(c, r); + Z3_dec_ref(c, args_neg); + Z3_dec_ref(c, zero); + return result; Z3_CATCH_RETURN(0); } @@ -208,12 +264,28 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_ast Z3_API Z3_mk_bvsub_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); - Z3_sort s = Z3_get_sort(c, t2); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); + Z3_inc_ref(c, minus_t2); + Z3_sort s = Z3_get_sort(c, t2); Z3_ast min = Z3_mk_bvsmin(c, s); - return Z3_mk_ite(c, Z3_mk_eq(c, t2, min), - Z3_mk_bvslt(c, t1, Z3_mk_int(c, 0, s)), - Z3_mk_bvadd_no_overflow(c, t1, minus_t2, true)); + Z3_inc_ref(c, min); + Z3_ast x = Z3_mk_eq(c, t2, min); + Z3_inc_ref(c, x); + Z3_ast zero = Z3_mk_int(c, 0, s); + Z3_inc_ref(c, zero); + Z3_ast y = Z3_mk_bvslt(c, t1, zero); + Z3_inc_ref(c, y); + Z3_ast z = Z3_mk_bvadd_no_overflow(c, t1, minus_t2, true); + Z3_inc_ref(c, z); + Z3_ast result = Z3_mk_ite(c, x, y, z); + mk_c(c)->save_ast_trail(to_app(result)); + Z3_dec_ref(c, minus_t2); + Z3_dec_ref(c, min); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + Z3_dec_ref(c, z); + Z3_dec_ref(c, zero); + return result; Z3_CATCH_RETURN(0); } @@ -222,10 +294,19 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); - if (Z3_get_error_code(c) != Z3_OK) return 0; + Z3_inc_ref(c, zero); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); - if (Z3_get_error_code(c) != Z3_OK) return 0; - return Z3_mk_implies(c, Z3_mk_bvslt(c, zero, t2), Z3_mk_bvadd_no_underflow(c, t1, minus_t2)); + Z3_inc_ref(c, minus_t2); + Z3_ast x = Z3_mk_bvslt(c, zero, t2); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_bvadd_no_underflow(c, t1, minus_t2); + Z3_inc_ref(c, y); + Z3_ast result = Z3_mk_implies(c, x, y); + Z3_dec_ref(c, zero); + Z3_dec_ref(c, minus_t2); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + return result; } else { return Z3_mk_bvule(c, t2, t1); @@ -267,12 +348,24 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; RESET_ERROR_CODE(); Z3_sort s = Z3_get_sort(c, t1); - if (Z3_get_error_code(c) != Z3_OK) return 0; Z3_ast min = Z3_mk_bvmsb(c, s); - if (Z3_get_error_code(c) != Z3_OK) return 0; - Z3_ast args[2] = { Z3_mk_eq(c, t1, min), - Z3_mk_eq(c, t2, Z3_mk_int(c, -1, s)) }; - return Z3_mk_not(c, Z3_mk_and(c, 2, args)); + Z3_inc_ref(c, min); + Z3_ast x = Z3_mk_eq(c, t1, min); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_int(c, -1, s); + Z3_inc_ref(c, y); + Z3_ast z = Z3_mk_eq(c, t2, y); + Z3_inc_ref(c, z); + Z3_ast args[2] = { x, z }; + Z3_ast u = Z3_mk_and(c, 2, args); + Z3_inc_ref(c, u); + Z3_ast result = Z3_mk_not(c, u); + Z3_dec_ref(c, min); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + Z3_dec_ref(c, z); + Z3_dec_ref(c, u); + return result; Z3_CATCH_RETURN(0); } diff --git a/src/api/api_util.h b/src/api/api_util.h index 58abf97bf..b49ef8315 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -29,7 +29,6 @@ Revision History: #define Z3_CATCH_RETURN_NO_HANDLE(VAL) } catch (z3_exception &) { return VAL; } #define CHECK_REF_COUNT(a) (reinterpret_cast(a)->get_ref_count() > 0) -#define VALIDATE(a) SASSERT(!a || CHECK_REF_COUNT(a)) namespace api { // Generic wrapper for ref-count objects exposed by the API @@ -44,30 +43,30 @@ namespace api { }; }; -inline ast * to_ast(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline ast * to_ast(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_ast(ast* a) { return reinterpret_cast(a); } -inline expr * to_expr(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline expr * to_expr(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_expr(expr* e) { return reinterpret_cast(e); } inline expr * const * to_exprs(Z3_ast const* a) { return reinterpret_cast(a); } inline Z3_ast * const * of_exprs(expr* const* e) { return reinterpret_cast(e); } -inline app * to_app(Z3_app a) { VALIDATE(a); return reinterpret_cast(a); } -inline app * to_app(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline app * to_app(Z3_app a) { return reinterpret_cast(a); } +inline app * to_app(Z3_ast a) { return reinterpret_cast(a); } inline Z3_app of_app(app* a) { return reinterpret_cast(a); } -inline app * const* to_apps(Z3_ast const* a) { VALIDATE(a); return reinterpret_cast(a); } +inline app * const* to_apps(Z3_ast const* a) { return reinterpret_cast(a); } inline ast * const * to_asts(Z3_ast const* a) { return reinterpret_cast(a); } -inline sort * to_sort(Z3_sort a) { VALIDATE(a); return reinterpret_cast(a); } +inline sort * to_sort(Z3_sort a) { return reinterpret_cast(a); } inline Z3_sort of_sort(sort* s) { return reinterpret_cast(s); } inline sort * const * to_sorts(Z3_sort const* a) { return reinterpret_cast(a); } inline Z3_sort const * of_sorts(sort* const* s) { return reinterpret_cast(s); } -inline func_decl * to_func_decl(Z3_func_decl a) { VALIDATE(a); return reinterpret_cast(a); } +inline func_decl * to_func_decl(Z3_func_decl a) { return reinterpret_cast(a); } inline Z3_func_decl of_func_decl(func_decl* f) { return reinterpret_cast(f); } inline func_decl * const * to_func_decls(Z3_func_decl const* f) { return reinterpret_cast(f); } @@ -75,7 +74,7 @@ inline func_decl * const * to_func_decls(Z3_func_decl const* f) { return reinter inline symbol to_symbol(Z3_symbol s) { return symbol::mk_symbol_from_c_ptr(reinterpret_cast(s)); } inline Z3_symbol of_symbol(symbol s) { return reinterpret_cast(const_cast(s.c_ptr())); } -inline Z3_pattern of_pattern(ast* a) { VALIDATE(a); return reinterpret_cast(a); } +inline Z3_pattern of_pattern(ast* a) { return reinterpret_cast(a); } inline app* to_pattern(Z3_pattern p) { return reinterpret_cast(p); } inline Z3_lbool of_lbool(lbool b) { return static_cast(b); } diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 43975fd04..a255c2d97 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -249,6 +249,8 @@ namespace z3 { array & operator=(array const & s); public: array(unsigned sz):m_size(sz) { m_array = new T[sz]; } + template + array(ast_vector_tpl const & v); ~array() { delete[] m_array; } unsigned size() const { return m_size; } T & operator[](int i) { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } @@ -872,7 +874,18 @@ namespace z3 { \brief Return a simplified version of this expression. The parameter \c p is a set of parameters for the Z3 simplifier. */ expr simplify(params const & p) const { Z3_ast r = Z3_simplify_ex(ctx(), m_ast, p); check_error(); return expr(ctx(), r); } - }; + + /** + \brief Apply substitution. Replace src expressions by dst. + */ + expr substitute(expr_vector const& src, expr_vector const& dst); + + /** + \brief Apply substitution. Replace bound variables by expressions. + */ + expr substitute(expr_vector const& dst); + + }; /** \brief Wraps a Z3_ast as an expr object. It also checks for errors. @@ -928,49 +941,6 @@ namespace z3 { inline expr udiv(expr const & a, int b) { return udiv(a, a.ctx().num_val(b, a.get_sort())); } inline expr udiv(int a, expr const & b) { return udiv(b.ctx().num_val(a, b.get_sort()), b); } - // Basic functions for creating quantified formulas. - // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. - inline expr forall(expr const & x, expr const & b) { - check_context(x, b); - Z3_app vars[] = {(Z3_app) x}; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & b) { - check_context(x1, b); check_context(x2, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x, expr const & b) { - check_context(x, b); - Z3_app vars[] = {(Z3_app) x}; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & b) { - check_context(x1, b); check_context(x2, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - template class cast_ast; template<> class cast_ast { @@ -1032,6 +1002,67 @@ namespace z3 { friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } }; + template + template + array::array(ast_vector_tpl const & v) { + m_array = new T[v.size()]; + m_size = v.size(); + for (unsigned i = 0; i < m_size; i++) { + m_array[i] = v[i]; + } + } + + // Basic functions for creating quantified formulas. + // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. + inline expr forall(expr const & x, expr const & b) { + check_context(x, b); + Z3_app vars[] = {(Z3_app) x}; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & b) { + check_context(x1, b); check_context(x2, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr_vector const & xs, expr const & b) { + array vars(xs); + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x, expr const & b) { + check_context(x, b); + Z3_app vars[] = {(Z3_app) x}; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & b) { + check_context(x1, b); check_context(x2, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr_vector const & xs, expr const & b) { + array vars(xs); + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + class func_entry : public object { Z3_func_entry m_entry; void init(Z3_func_entry e) { @@ -1680,6 +1711,30 @@ namespace z3 { d.check_error(); return expr(d.ctx(), r); } + + inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) { + assert(src.size() == dst.size()); + array _src(src.size()); + array _dst(dst.size()); + for (unsigned i = 0; i < src.size(); ++i) { + _src[i] = src[i]; + _dst[i] = dst[i]; + } + Z3_ast r = Z3_substitute(ctx(), m_ast, src.size(), _src.ptr(), _dst.ptr()); + check_error(); + return expr(ctx(), r); + } + + inline expr expr::substitute(expr_vector const& dst) { + array _dst(dst.size()); + for (unsigned i = 0; i < dst.size(); ++i) { + _dst[i] = dst[i]; + } + Z3_ast r = Z3_substitute_vars(ctx(), m_ast, dst.size(), _dst.ptr()); + check_error(); + return expr(ctx(), r); + } + }; diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 35fe6611d..68cca046e 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -44,6 +44,21 @@ namespace Microsoft.Z3 /// /// Constructor. /// + /// + /// The following parameters can be set: + /// - proof (Boolean) Enable proof generation + /// - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting + /// - trace (Boolean) Tracing support for VCC + /// - trace_file_name (String) Trace out file for VCC traces + /// - timeout (unsigned) default timeout (in milliseconds) used for solvers + /// - well_sorted_check type checker + /// - auto_config use heuristics to automatically select solver and configure it + /// - model model generation for solvers, this parameter can be overwritten when creating a solver + /// - model_validate validate models produced by solvers + /// - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver + /// Note that in previous versions of Z3, this constructor was also used to set global and module parameters. + /// For this purpose we should now use + /// public Context(Dictionary settings) : base() { diff --git a/src/api/dotnet/Microsoft.Z3.csproj b/src/api/dotnet/Microsoft.Z3.csproj index 8f6e34517..9eb9eb660 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj +++ b/src/api/dotnet/Microsoft.Z3.csproj @@ -399,4 +399,4 @@ --> - \ No newline at end of file + diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 5277dab38..6b6c63ac3 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -27,6 +27,21 @@ public class Context extends IDisposable /** * Constructor. + * + * The following parameters can be set: + * - proof (Boolean) Enable proof generation + * - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting + * - trace (Boolean) Tracing support for VCC + * - trace_file_name (String) Trace out file for VCC traces + * - timeout (unsigned) default timeout (in milliseconds) used for solvers + * - well_sorted_check type checker + * - auto_config use heuristics to automatically select solver and configure it + * - model model generation for solvers, this parameter can be overwritten when creating a solver + * - model_validate validate models produced by solvers + * - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver + * Note that in previous versions of Z3, this constructor was also used to set global and + * module parameters. For this purpose we should now use + * **/ public Context(Map settings) throws Z3Exception { diff --git a/src/api/ml/README-test-win b/src/api/ml/README-test-win index d9be0174b..0e8b73ccd 100644 --- a/src/api/ml/README-test-win +++ b/src/api/ml/README-test-win @@ -1,23 +1,23 @@ -This directory contains scripts to build the test application using -OCaml. You also need CamlIDL to be able to generate the OCaml API. - -- To download OCaml: - http://caml.inria.fr/ocaml/ - -- To download CamlIDL: - http://forge.ocamlcore.org/projects/camlidl/ - -- One must build the OCaml library before compiling the example. - Go to directory ../ocaml - -- Use 'build-test.cmd' to build the test application using the OCaml compiler. - -Remark: The OCaml and C compiler tool chains must be configured in your environment. -Running from the Visual Studio Command Prompt configures the Microsoft C compiler. - -- The script 'exec.cmd' adds the bin directory to the path. So, - test_mlapi.exe can find z3.dll. - - - - +This directory contains scripts to build the test application using +OCaml. You also need CamlIDL to be able to generate the OCaml API. + +- To download OCaml: + http://caml.inria.fr/ocaml/ + +- To download CamlIDL: + http://forge.ocamlcore.org/projects/camlidl/ + +- One must build the OCaml library before compiling the example. + Go to directory ../ocaml + +- Use 'build-test.cmd' to build the test application using the OCaml compiler. + +Remark: The OCaml and C compiler tool chains must be configured in your environment. +Running from the Visual Studio Command Prompt configures the Microsoft C compiler. + +- The script 'exec.cmd' adds the bin directory to the path. So, + test_mlapi.exe can find z3.dll. + + + + diff --git a/src/api/ml/README-win b/src/api/ml/README-win index 91d2faa4f..82ddc2652 100644 --- a/src/api/ml/README-win +++ b/src/api/ml/README-win @@ -1,23 +1,23 @@ -The OCaml API for Z3 was tested using OCaml 3.12.1. -You also need CamlIDL to be able to generate the OCaml API. - -- To download OCaml: - http://caml.inria.fr/ocaml/ - -- To download CamlIDL: - http://forge.ocamlcore.org/projects/camlidl/ - -- To build the OCaml API for Z3: - .\build-lib.cmd - -Remark: The OCaml and C compiler tool chains must be configured in your environment. -Running from the Visual Studio Command Prompt configures the Microsoft C compiler. - -Remark: Building the OCaml API copies some pathnames into files, -so the OCaml API must be recompiled if the Z3 library files are moved. - -See ..\examples\ocaml\build-test.cmd for an example of how to compile and link with Z3. - -Acknowledgements: -The OCaml interface for Z3 was written by Josh Berdine and Jakob Lichtenberg. -Many thanks to them! +The OCaml API for Z3 was tested using OCaml 3.12.1. +You also need CamlIDL to be able to generate the OCaml API. + +- To download OCaml: + http://caml.inria.fr/ocaml/ + +- To download CamlIDL: + http://forge.ocamlcore.org/projects/camlidl/ + +- To build the OCaml API for Z3: + .\build-lib.cmd + +Remark: The OCaml and C compiler tool chains must be configured in your environment. +Running from the Visual Studio Command Prompt configures the Microsoft C compiler. + +Remark: Building the OCaml API copies some pathnames into files, +so the OCaml API must be recompiled if the Z3 library files are moved. + +See ..\examples\ocaml\build-test.cmd for an example of how to compile and link with Z3. + +Acknowledgements: +The OCaml interface for Z3 was written by Josh Berdine and Jakob Lichtenberg. +Many thanks to them! diff --git a/src/api/ml/build-lib.cmd b/src/api/ml/build-lib.cmd index 93d667a34..7cf1bbcbd 100755 --- a/src/api/ml/build-lib.cmd +++ b/src/api/ml/build-lib.cmd @@ -1,3 +1,3 @@ -@echo off - -call .\compile_mlapi.cmd ..\include ..\bin ..\bin +@echo off + +call .\compile_mlapi.cmd ..\include ..\bin ..\bin diff --git a/src/api/ml/build-test.cmd b/src/api/ml/build-test.cmd index 7b80dc795..13a752dbb 100755 --- a/src/api/ml/build-test.cmd +++ b/src/api/ml/build-test.cmd @@ -1,19 +1,19 @@ -@echo off - -if not exist ..\..\ocaml\z3.cmxa ( - echo "YOU MUST BUILD OCAML API! Go to directory ..\ocaml" - goto :EOF -) - -REM ocaml (>= 3.11) calls the linker through flexlink -ocamlc -version >> ocaml_version -set /p OCAML_VERSION= = 3.11) calls the linker through flexlink +ocamlc -version >> ocaml_version +set /p OCAML_VERSION= get_num_args(); #ifdef Z3DEBUG - vector found; + svector found; #endif for (unsigned i = 0; i < num_args; i++) { expr * lit = cls->get_arg(i); diff --git a/src/ast/ast.h b/src/ast/ast.h index 4c924691c..ef4ab3b55 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1193,7 +1193,6 @@ enum pattern_op_kind { heurisitic quantifier instantiation. */ class pattern_decl_plugin : public decl_plugin { - sort * m_list; public: virtual decl_plugin * mk_fresh() { return alloc(pattern_decl_plugin); } diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 5819e3930..f684879ae 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -260,6 +260,7 @@ class smt_printer { else { m_out << sym << "["; } + for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (p.is_ast()) { @@ -642,9 +643,7 @@ class smt_printer { m_out << m_var_names[m_num_var_names - idx - 1]; } else { - if (!m_is_smt2) { - m_out << "?" << idx; - } + m_out << "?" << idx; } } diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 8ac19c11c..4f0c9a75d 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -326,6 +326,7 @@ namespace datalog { } unsigned index0; sort* last_sort = 0; + SASSERT(num_params > 0); for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (!p.is_int()) { @@ -636,9 +637,13 @@ namespace datalog { app* dl_decl_util::mk_numeral(uint64 value, sort* s) { if (is_finite_sort(s)) { + uint64 sz = 0; + if (try_get_size(s, sz) && sz <= value) { + m.raise_exception("value is out of bounds"); + } parameter params[2] = { parameter(rational(value, rational::ui64())), parameter(s) }; return m.mk_const(m.mk_func_decl(m_fid, OP_DL_CONSTANT, 2, params, 0, (sort*const*)0)); - } + } if (m_arith.is_int(s) || m_arith.is_real(s)) { return m_arith.mk_numeral(rational(value, rational::ui64()), s); } @@ -656,9 +661,9 @@ namespace datalog { return 0; } - bool dl_decl_util::is_numeral(expr* e, uint64& v) const { + bool dl_decl_util::is_numeral(const expr* e, uint64& v) const { if (is_numeral(e)) { - app* c = to_app(e); + const app* c = to_app(e); SASSERT(c->get_decl()->get_num_parameters() == 2); parameter const& p = c->get_decl()->get_parameter(0); SASSERT(p.is_rational()); diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index 559aff7bd..a662de578 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -169,11 +169,11 @@ namespace datalog { app* mk_le(expr* a, expr* b); - bool is_lt(expr* a) { return is_app_of(a, m_fid, OP_DL_LT); } + bool is_lt(const expr* a) const { return is_app_of(a, m_fid, OP_DL_LT); } - bool is_numeral(expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } + bool is_numeral(const expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } - bool is_numeral(expr* e, uint64& v) const; + bool is_numeral(const expr* e, uint64& v) const; // // Utilities for extracting constants diff --git a/src/ast/expr_abstract.cpp b/src/ast/expr_abstract.cpp index 6deb4bf45..0569eb360 100644 --- a/src/ast/expr_abstract.cpp +++ b/src/ast/expr_abstract.cpp @@ -20,52 +20,50 @@ Notes: #include "expr_abstract.h" #include "map.h" -void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { - ast_ref_vector pinned(m); - ptr_vector stack; - obj_map map; +void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { + expr * curr = 0, *b = 0; SASSERT(n->get_ref_count() > 0); - stack.push_back(n); + m_stack.push_back(n); for (unsigned i = 0; i < num_bound; ++i) { b = bound[i]; expr* v = m.mk_var(base + num_bound - i - 1, m.get_sort(b)); - pinned.push_back(v); - map.insert(b, v); + m_pinned.push_back(v); + m_map.insert(b, v); } - while(!stack.empty()) { - curr = stack.back(); - if (map.contains(curr)) { - stack.pop_back(); + while(!m_stack.empty()) { + curr = m_stack.back(); + if (m_map.contains(curr)) { + m_stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_VAR: { - map.insert(curr, curr); - stack.pop_back(); + m_map.insert(curr, curr); + m_stack.pop_back(); break; } case AST_APP: { app* a = to_app(curr); bool all_visited = true; - ptr_vector args; + m_args.reset(); for (unsigned i = 0; i < a->get_num_args(); ++i) { - if (!map.find(a->get_arg(i), b)) { - stack.push_back(a->get_arg(i)); + if (!m_map.find(a->get_arg(i), b)) { + m_stack.push_back(a->get_arg(i)); all_visited = false; } else { - args.push_back(b); + m_args.push_back(b); } } if (all_visited) { - b = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); - pinned.push_back(b); - map.insert(curr, b); - stack.pop_back(); + b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); + m_pinned.push_back(b); + m_map.insert(curr, b); + m_stack.pop_back(); } break; } @@ -81,17 +79,24 @@ void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* cons } expr_abstract(m, new_base, num_bound, bound, q->get_expr(), result1); b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get()); - pinned.push_back(b); - map.insert(curr, b); - stack.pop_back(); + m_pinned.push_back(b); + m_map.insert(curr, b); + m_stack.pop_back(); break; } default: UNREACHABLE(); } } - if (!map.find(n, b)) { - UNREACHABLE(); - } + VERIFY (m_map.find(n, b)); result = b; + m_pinned.reset(); + m_map.reset(); + m_stack.reset(); + m_args.reset(); +} + +void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { + expr_abstractor abs(m); + abs(base, num_bound, bound, n, result); } diff --git a/src/ast/expr_abstract.h b/src/ast/expr_abstract.h index c6ec7973b..3d9f3960f 100644 --- a/src/ast/expr_abstract.h +++ b/src/ast/expr_abstract.h @@ -21,6 +21,17 @@ Notes: #include"ast.h" +class expr_abstractor { + ast_manager& m; + expr_ref_vector m_pinned; + ptr_vector m_stack, m_args; + obj_map m_map; + +public: + expr_abstractor(ast_manager& m): m(m), m_pinned(m) {} + void operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); +}; + void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); #endif diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 26131bc28..2a090fc39 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -200,6 +200,7 @@ func_decl * float_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_par } else { m_manager->raise_exception("sort of floating point constant was not specified"); + UNREACHABLE(); } SASSERT(is_sort_of(s, m_family_id, FLOAT_SORT)); diff --git a/src/ast/func_decl_dependencies.cpp b/src/ast/func_decl_dependencies.cpp index 5e054d5d9..162efb0dd 100644 --- a/src/ast/func_decl_dependencies.cpp +++ b/src/ast/func_decl_dependencies.cpp @@ -79,7 +79,6 @@ void func_decl_dependencies::collect_ng_func_decls(expr * n, func_decl_set * s) */ class func_decl_dependencies::top_sort { enum color { OPEN, IN_PROGRESS, CLOSED }; - ast_manager & m_manager; dependency_graph & m_deps; typedef obj_map color_map; @@ -177,7 +176,7 @@ class func_decl_dependencies::top_sort { } public: - top_sort(ast_manager & m, dependency_graph & deps):m_manager(m), m_deps(deps) {} + top_sort(dependency_graph & deps) : m_deps(deps) {} bool operator()(func_decl * new_decl) { @@ -198,7 +197,7 @@ bool func_decl_dependencies::insert(func_decl * f, func_decl_set * s) { m_deps.insert(f, s); - top_sort cycle_detector(m_manager, m_deps); + top_sort cycle_detector(m_deps); if (cycle_detector(f)) { m_deps.erase(f); dealloc(s); diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp index 1b0f0b621..084a33ebf 100644 --- a/src/ast/macros/quasi_macros.cpp +++ b/src/ast/macros/quasi_macros.cpp @@ -22,10 +22,9 @@ Revision History: #include"uint_set.h" #include"var_subst.h" -quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s) : +quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s) : m_manager(m), m_macro_manager(mm), - m_bsimp(p), m_simplifier(s), m_new_vars(m), m_new_eqs(m), diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h index 1731774b2..c5e6b6d4f 100644 --- a/src/ast/macros/quasi_macros.h +++ b/src/ast/macros/quasi_macros.h @@ -32,7 +32,6 @@ class quasi_macros { ast_manager & m_manager; macro_manager & m_macro_manager; - basic_simplifier_plugin & m_bsimp; simplifier & m_simplifier; occurrences_map m_occurrences; ptr_vector m_todo; @@ -57,7 +56,7 @@ class quasi_macros { void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); public: - quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s); + quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s); ~quasi_macros(); /** diff --git a/src/ast/pattern/database.smt2 b/src/ast/pattern/database.smt2 index 9021e44f0..c42c9c25f 100644 --- a/src/ast/pattern/database.smt2 +++ b/src/ast/pattern/database.smt2 @@ -119,13 +119,13 @@ :pattern (?select (?select (?asElems e) a) i)))) (assert (forall ((x Int) (f Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) + (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (= (?isAllocated x a0) 1)) (= (?isAllocated (?select f x) a0) 1)) :pattern (?isAllocated (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?eClosedTime e))) 0) + (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (= (?isAllocated a a0) 1)) (= (?isAllocated (?select (?select e a) i) a0) 1)) :pattern (?isAllocated (?select (?select e a) i) a0)))) @@ -281,13 +281,13 @@ :pattern (IntsAllocated h (?StructGet_ s f))))) (assert (forall ((x Int) (f Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) + (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (?isAllocated_ x a0)) (?isAllocated_ (?select f x) a0)) :pattern (?isAllocated_ (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?eClosedTime e))) 0) + (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (?isAllocated_ a a0)) (?isAllocated_ (?select (?select e a) i) a0)) :pattern (?isAllocated_ (?select (?select e a) i) a0)))) diff --git a/src/ast/recurse_expr.h b/src/ast/recurse_expr.h index 9cb71872b..6b3220d40 100644 --- a/src/ast/recurse_expr.h +++ b/src/ast/recurse_expr.h @@ -30,7 +30,7 @@ class recurse_expr : public Visitor { vector m_results2; bool is_cached(expr * n) const { T c; return m_cache.find(n, c); } - T get_cached(expr * n) const { T c; m_cache.find(n, c); return c; } + T get_cached(expr * n) const { return m_cache.find(n); } void cache_result(expr * n, T c) { m_cache.insert(n, c); } void visit(expr * n, bool & visited); diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index 0bb7378f4..1f4418fd5 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -25,6 +25,7 @@ void array_rewriter::updt_params(params_ref const & _p) { array_rewriter_params p(_p); m_sort_store = p.sort_store(); m_expand_select_store = p.expand_select_store(); + m_expand_store_eq = p.expand_store_eq(); } void array_rewriter::get_param_descrs(param_descrs & r) { @@ -365,3 +366,40 @@ br_status array_rewriter::mk_set_subset(expr * arg1, expr * arg2, expr_ref & res return BR_REWRITE3; } +br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { + if (!m_expand_store_eq) { + return BR_FAILED; + } + expr* lhs1 = lhs; + while (m_util.is_store(lhs1)) { + lhs1 = to_app(lhs1)->get_arg(0); + } + expr* rhs1 = rhs; + while (m_util.is_store(rhs1)) { + rhs1 = to_app(rhs1)->get_arg(0); + } + if (lhs1 != rhs1) { + return BR_FAILED; + } + ptr_buffer fmls, args; + expr* e; + expr_ref tmp1(m()), tmp2(m()); +#define MK_EQ() \ + while (m_util.is_store(e)) { \ + args.push_back(lhs); \ + args.append(to_app(e)->get_num_args()-2,to_app(e)->get_args()+1); \ + mk_select(args.size(), args.c_ptr(), tmp1); \ + args[0] = rhs; \ + mk_select(args.size(), args.c_ptr(), tmp2); \ + fmls.push_back(m().mk_eq(tmp1, tmp2)); \ + e = to_app(e)->get_arg(0); \ + args.reset(); \ + } \ + + e = lhs; + MK_EQ(); + e = rhs; + MK_EQ(); + result = m().mk_and(fmls.size(), fmls.c_ptr()); + return BR_REWRITE_FULL; +} diff --git a/src/ast/rewriter/array_rewriter.h b/src/ast/rewriter/array_rewriter.h index f1362802d..5f20f61ba 100644 --- a/src/ast/rewriter/array_rewriter.h +++ b/src/ast/rewriter/array_rewriter.h @@ -31,12 +31,14 @@ class array_rewriter { array_util m_util; bool m_sort_store; bool m_expand_select_store; + bool m_expand_store_eq; template lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2); public: array_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m) { updt_params(p); + } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } @@ -60,6 +62,7 @@ public: br_status mk_set_complement(expr * arg, expr_ref & result); br_status mk_set_difference(expr * arg1, expr * arg2, expr_ref & result); br_status mk_set_subset(expr * arg1, expr * arg2, expr_ref & result); + br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); }; #endif diff --git a/src/ast/rewriter/array_rewriter_params.pyg b/src/ast/rewriter/array_rewriter_params.pyg index 2e3ae9f6a..a43fadecf 100644 --- a/src/ast/rewriter/array_rewriter_params.pyg +++ b/src/ast/rewriter/array_rewriter_params.pyg @@ -2,4 +2,5 @@ def_module_params(module_name='rewriter', class_name='array_rewriter_params', export=True, params=(("expand_select_store", BOOL, False, "replace a (select (store ...) ...) term by an if-then-else term"), + ("expand_store_eq", BOOL, False, "reduce (store ...) = (store ...) with a common base into selects"), ("sort_store", BOOL, False, "sort nested stores when the indices are known to be different"))) diff --git a/src/ast/rewriter/ast_counter.cpp b/src/ast/rewriter/ast_counter.cpp index c542abb60..a807237c5 100644 --- a/src/ast/rewriter/ast_counter.cpp +++ b/src/ast/rewriter/ast_counter.cpp @@ -93,7 +93,9 @@ void var_counter::count_vars(ast_manager & m, const app * pred, int coef) { unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { m_sorts.reset(); - ::get_free_vars(pred->get_arg(i), m_sorts); + m_todo.reset(); + m_mark.reset(); + ::get_free_vars(m_mark, m_todo, pred->get_arg(i), m_sorts); for (unsigned j = 0; j < m_sorts.size(); ++j) { if (m_sorts[j]) { update(j, coef); @@ -108,24 +110,27 @@ unsigned var_counter::get_max_var(bool& has_var) { unsigned max_var = 0; while (!m_todo.empty()) { expr* e = m_todo.back(); - unsigned scope = m_scopes.back(); m_todo.pop_back(); - m_scopes.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e, true); switch(e->get_kind()) { case AST_QUANTIFIER: { + var_counter aux_counter; quantifier* q = to_quantifier(e); - m_todo.push_back(q->get_expr()); - m_scopes.push_back(scope + q->get_num_decls()); + bool has_var1 = false; + unsigned max_v = aux_counter.get_max_var(has_var1); + if (max_v > max_var + q->get_num_decls()) { + max_var = max_v - q->get_num_decls(); + has_var = true; + } break; } case AST_VAR: { - if (to_var(e)->get_idx() >= scope + max_var) { + if (to_var(e)->get_idx() >= max_var) { has_var = true; - max_var = to_var(e)->get_idx() - scope; + max_var = to_var(e)->get_idx(); } break; } @@ -133,7 +138,6 @@ unsigned var_counter::get_max_var(bool& has_var) { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { m_todo.push_back(a->get_arg(i)); - m_scopes.push_back(scope); } break; } @@ -150,14 +154,12 @@ unsigned var_counter::get_max_var(bool& has_var) { unsigned var_counter::get_max_var(expr* e) { bool has_var = false; m_todo.push_back(e); - m_scopes.push_back(0); return get_max_var(has_var); } unsigned var_counter::get_next_var(expr* e) { bool has_var = false; m_todo.push_back(e); - m_scopes.push_back(0); unsigned mv = get_max_var(has_var); if (has_var) mv++; return mv; diff --git a/src/ast/rewriter/ast_counter.h b/src/ast/rewriter/ast_counter.h index 2a581c302..e7251079f 100644 --- a/src/ast/rewriter/ast_counter.h +++ b/src/ast/rewriter/ast_counter.h @@ -38,6 +38,7 @@ public: counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} + void reset() { m_data.reset(); } iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } void update(unsigned el, int delta); @@ -71,6 +72,7 @@ protected: ptr_vector m_sorts; expr_fast_mark1 m_visited; ptr_vector m_todo; + ast_mark m_mark; unsigned_vector m_scopes; unsigned get_max_var(bool & has_var); public: diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 70ba09581..b43f07b65 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -242,13 +242,13 @@ br_status float_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) { return BR_DONE; } // expand as using ite's - result = m().mk_ite(mk_eq_nan(arg1), + result = m().mk_ite(m().mk_or(mk_eq_nan(arg1), m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2))), arg2, m().mk_ite(mk_eq_nan(arg2), arg1, m().mk_ite(m_util.mk_lt(arg1, arg2), - arg1, - arg2))); + arg1, + arg2))); return BR_REWRITE_FULL; } @@ -262,7 +262,7 @@ br_status float_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { return BR_DONE; } // expand as using ite's - result = m().mk_ite(mk_eq_nan(arg1), + result = m().mk_ite(m().mk_or(mk_eq_nan(arg1), m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2))), arg2, m().mk_ite(mk_eq_nan(arg2), arg1, diff --git a/src/ast/rewriter/mk_simplified_app.cpp b/src/ast/rewriter/mk_simplified_app.cpp index da615e195..a46e71582 100644 --- a/src/ast/rewriter/mk_simplified_app.cpp +++ b/src/ast/rewriter/mk_simplified_app.cpp @@ -62,6 +62,8 @@ struct mk_simplified_app::imp { st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); + else if (s_fid == m_ar_rw.get_fid()) + st = m_ar_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) return st; diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index 290144c75..036221f96 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -1,932 +1,932 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - poly_rewriter_def.h - -Abstract: - - Basic rewriting rules for Polynomials. - -Author: - - Leonardo (leonardo) 2011-04-08 - -Notes: - ---*/ -#include"poly_rewriter.h" -#include"poly_rewriter_params.hpp" -#include"ast_lt.h" -#include"ast_ll_pp.h" -#include"ast_smt2_pp.h" - -template -char const * poly_rewriter::g_ste_blowup_msg = "sum of monomials blowup"; - - -template -void poly_rewriter::updt_params(params_ref const & _p) { - poly_rewriter_params p(_p); - m_flat = p.flat(); - m_som = p.som(); - m_hoist_mul = p.hoist_mul(); - m_hoist_cmul = p.hoist_cmul(); - m_som_blowup = p.som_blowup(); -} - -template -void poly_rewriter::get_param_descrs(param_descrs & r) { - poly_rewriter_params::collect_param_descrs(r); -} - -template -expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) { - switch (num_args) { - case 0: return mk_numeral(numeral(0)); - case 1: return args[0]; - default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); - } -} - -// t = (^ x y) --> return x, and set k = y if k is an integer >= 1 -// Otherwise return t and set k = 1 -template -expr * poly_rewriter::get_power_body(expr * t, rational & k) { - if (!is_power(t)) { - k = rational(1); - return t; - } - if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) { - return to_app(t)->get_arg(0); - } - k = rational(1); - return t; -} - -template -expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) { - switch (num_args) { - case 0: - return mk_numeral(numeral(1)); - case 1: - return args[0]; - default: - if (use_power()) { - rational k_prev; - expr * prev = get_power_body(args[0], k_prev); - rational k; - ptr_buffer new_args; -#define PUSH_POWER() { \ - if (k_prev.is_one()) { \ - new_args.push_back(prev); \ - } \ - else { \ - expr * pargs[2] = { prev, mk_numeral(k_prev) }; \ - new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \ - } \ - } - - for (unsigned i = 1; i < num_args; i++) { - expr * arg = get_power_body(args[i], k); - if (arg == prev) { - k_prev += k; - } - else { - PUSH_POWER(); - prev = arg; - k_prev = k; - } - } - PUSH_POWER(); - SASSERT(new_args.size() > 0); - if (new_args.size() == 1) { - return new_args[0]; - } - else { - return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr()); - } - } - else { - return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); - } - } -} - -template -expr * poly_rewriter::mk_mul_app(numeral const & c, expr * arg) { - if (c.is_one()) { - return arg; - } - else { - expr * new_args[2] = { mk_numeral(c), arg }; - return mk_mul_app(2, new_args); - } -} - -template -br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - // only try to apply flattening if it is not already in one of the flat monomial forms - // - (* c x) - // - (* c (* x_1 ... x_n)) - if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) { - unsigned i; - for (i = 0; i < num_args; i++) { - if (is_mul(args[i])) - break; - } - if (i < num_args) { - // input has nested monomials. - ptr_buffer flat_args; - // we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n))) - ptr_buffer todo; - flat_args.append(i, args); - for (unsigned j = i; j < num_args; j++) { - if (is_mul(args[j])) { - todo.push_back(args[j]); - while (!todo.empty()) { - expr * curr = todo.back(); - todo.pop_back(); - if (is_mul(curr)) { - unsigned k = to_app(curr)->get_num_args(); - while (k > 0) { - --k; - todo.push_back(to_app(curr)->get_arg(k)); - } - } - else { - flat_args.push_back(curr); - } - } - } - else { - flat_args.push_back(args[j]); - } - } - TRACE("poly_rewriter", - tout << "flat mul:\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; - tout << "---->\n"; - for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";); - br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result); - if (st == BR_FAILED) { - result = mk_mul_app(flat_args.size(), flat_args.c_ptr()); - return BR_DONE; - } - return st; - } - } - return mk_nflat_mul_core(num_args, args, result); -} - - -template -struct poly_rewriter::mon_pw_lt { - poly_rewriter & m_owner; - mon_pw_lt(poly_rewriter & o):m_owner(o) {} - - bool operator()(expr * n1, expr * n2) const { - rational k; - return lt(m_owner.get_power_body(n1, k), - m_owner.get_power_body(n2, k)); - } -}; - - -template -br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - // cheap case - numeral a; - if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() && - (is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid())) - return BR_FAILED; - numeral c(1); - unsigned num_coeffs = 0; - unsigned num_add = 0; - expr * var = 0; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg, a)) { - num_coeffs++; - c *= a; - } - else { - var = arg; - if (is_add(arg)) - num_add++; - } - } - - normalize(c); - // (* c_1 ... c_n) --> c_1*...*c_n - if (num_coeffs == num_args) { - result = mk_numeral(c); - return BR_DONE; - } - - // (* s ... 0 ... r) --> 0 - if (c.is_zero()) { - result = mk_numeral(c); - return BR_DONE; - } - - if (num_coeffs == num_args - 1) { - SASSERT(var != 0); - // (* c_1 ... c_n x) --> x if c_1*...*c_n == 1 - if (c.is_one()) { - result = var; - return BR_DONE; - } - - numeral c_prime; - if (is_mul(var)) { - // apply basic simplification even when flattening is not enabled. - // (* c1 (* c2 x)) --> (* c1*c2 x) - if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) { - c *= c_prime; - normalize(c); - result = mk_mul_app(c, to_app(var)->get_arg(1)); - return BR_REWRITE1; - } - else { - // var is a power-product - return BR_FAILED; - } - } - - if (num_add == 0 || m_hoist_cmul) { - SASSERT(!is_add(var) || m_hoist_cmul); - if (num_args == 2 && args[1] == var) { - DEBUG_CODE({ - numeral c_prime; - SASSERT(is_numeral(args[0], c_prime) && c == c_prime); - }); - // it is already simplified - return BR_FAILED; - } - - // (* c_1 ... c_n x) --> (* c_1*...*c_n x) - result = mk_mul_app(c, var); - return BR_DONE; - } - else { - SASSERT(is_add(var)); - // (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m)) - ptr_buffer new_add_args; - unsigned num = to_app(var)->get_num_args(); - for (unsigned i = 0; i < num; i++) { - new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); - } - result = mk_add_app(new_add_args.size(), new_add_args.c_ptr()); - TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); - return BR_REWRITE2; - } - } - - SASSERT(num_coeffs <= num_args - 2); - - if (!m_som || num_add == 0) { - ptr_buffer new_args; - expr * prev = 0; - bool ordered = true; - for (unsigned i = 0; i < num_args; i++) { - expr * curr = args[i]; - if (is_numeral(curr)) - continue; - if (prev != 0 && lt(curr, prev)) - ordered = false; - new_args.push_back(curr); - prev = curr; - } - TRACE("poly_rewriter", - for (unsigned i = 0; i < new_args.size(); i++) { - if (i > 0) - tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m()); - } - tout << "\nordered: " << ordered << "\n";); - if (ordered && num_coeffs == 0 && !use_power()) - return BR_FAILED; - if (!ordered) { - if (use_power()) - std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this)); - else - std::sort(new_args.begin(), new_args.end(), ast_to_lt()); - TRACE("poly_rewriter", - tout << "after sorting:\n"; - for (unsigned i = 0; i < new_args.size(); i++) { - if (i > 0) - tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m()); - } - tout << "\n";); - } - SASSERT(new_args.size() >= 2); - result = mk_mul_app(new_args.size(), new_args.c_ptr()); - result = mk_mul_app(c, result); - TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); - return BR_DONE; - } - - SASSERT(m_som && num_add > 0); - - sbuffer szs; - sbuffer it; - sbuffer sums; - for (unsigned i = 0; i < num_args; i ++) { - it.push_back(0); - expr * arg = args[i]; - if (is_add(arg)) { - sums.push_back(const_cast(to_app(arg)->get_args())); - szs.push_back(to_app(arg)->get_num_args()); - } - else { - sums.push_back(const_cast(args + i)); - szs.push_back(1); - SASSERT(sums.back()[0] == arg); - } - } - expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception - ptr_buffer m_args; - TRACE("som", tout << "starting som...\n";); - do { - TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; - tout << "\n";); - if (sum.size() > m_som_blowup) - throw rewriter_exception(g_ste_blowup_msg); - m_args.reset(); - for (unsigned i = 0; i < num_args; i++) { - expr * const * v = sums[i]; - expr * arg = v[it[i]]; - m_args.push_back(arg); - } - sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr())); - } - while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); - result = mk_add_app(sum.size(), sum.c_ptr()); - return BR_REWRITE2; -} - -template -br_status poly_rewriter::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { - unsigned i; - for (i = 0; i < num_args; i++) { - if (is_add(args[i])) - break; - } - if (i < num_args) { - // has nested ADDs - ptr_buffer flat_args; - flat_args.append(i, args); - for (; i < num_args; i++) { - expr * arg = args[i]; - // Remark: all rewrites are depth 1. - if (is_add(arg)) { - unsigned num = to_app(arg)->get_num_args(); - for (unsigned j = 0; j < num; j++) - flat_args.push_back(to_app(arg)->get_arg(j)); - } - else { - flat_args.push_back(arg); - } - } - br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result); - if (st == BR_FAILED) { - result = mk_add_app(flat_args.size(), flat_args.c_ptr()); - return BR_DONE; - } - return st; - } - return mk_nflat_add_core(num_args, args, result); -} - -template -inline expr * poly_rewriter::get_power_product(expr * t) { - if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0))) - return to_app(t)->get_arg(1); - return t; -} - -template -inline expr * poly_rewriter::get_power_product(expr * t, numeral & a) { - if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a)) - return to_app(t)->get_arg(1); - a = numeral(1); - return t; -} - -template -bool poly_rewriter::is_mul(expr * t, numeral & c, expr * & pp) { - if (!is_mul(t) || to_app(t)->get_num_args() != 2) - return false; - if (!is_numeral(to_app(t)->get_arg(0), c)) - return false; - pp = to_app(t)->get_arg(1); - return true; -} - -template -struct poly_rewriter::hoist_cmul_lt { - poly_rewriter & m_r; - hoist_cmul_lt(poly_rewriter & r):m_r(r) {} - - bool operator()(expr * t1, expr * t2) const { - expr * pp1, * pp2; - numeral c1, c2; - bool is_mul1 = m_r.is_mul(t1, c1, pp1); - bool is_mul2 = m_r.is_mul(t2, c2, pp2); - if (!is_mul1 && is_mul2) - return true; - if (is_mul1 && !is_mul2) - return false; - if (!is_mul1 && !is_mul2) - return t1->get_id() < t2->get_id(); - if (c1 < c2) - return true; - if (c1 > c2) - return false; - return pp1->get_id() < pp2->get_id(); - } -}; - -template -void poly_rewriter::hoist_cmul(expr_ref_buffer & args) { - unsigned sz = args.size(); - std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this)); - numeral c, c_prime; - ptr_buffer pps; - expr * pp, * pp_prime; - unsigned j = 0; - unsigned i = 0; - while (i < sz) { - expr * mon = args[i]; - if (is_mul(mon, c, pp) && i < sz - 1) { - expr * mon_prime = args[i+1]; - if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) { - // found target - pps.reset(); - pps.push_back(pp); - pps.push_back(pp_prime); - i += 2; - while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) { - pps.push_back(pp_prime); - i++; - } - SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime); - expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) }; - args.set(j, mk_mul_app(2, mul_args)); - j++; - continue; - } - } - args.set(j, mon); - j++; - i++; - } - args.resize(j); -} - -template -br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - numeral c; - unsigned num_coeffs = 0; - numeral a; - expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args - expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once - bool has_multiple = false; - expr * prev = 0; - bool ordered = true; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg, a)) { - num_coeffs++; - c += a; - } - else { - // arg is not a numeral - if (m_sort_sums && ordered) { - if (prev != 0 && lt(arg, prev)) - ordered = false; - prev = arg; - } - } - - arg = get_power_product(arg); - if (visited.is_marked(arg)) { - multiple.mark(arg); - has_multiple = true; - } - else { - visited.mark(arg); - } - } - normalize(c); - SASSERT(m_sort_sums || ordered); - TRACE("sort_sums", - tout << "ordered: " << ordered << "\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); - - if (has_multiple) { - // expensive case - buffer coeffs; - m_expr2pos.reset(); - // compute the coefficient of power products that occur multiple times. - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos; - if (m_expr2pos.find(pp, pos)) { - coeffs[pos] += a; - } - else { - m_expr2pos.insert(pp, coeffs.size()); - coeffs.push_back(a); - } - } - expr_ref_buffer new_args(m()); - if (!c.is_zero()) { - new_args.push_back(mk_numeral(c)); - } - // copy power products with non zero coefficients to new_args - visited.reset(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg); - if (!multiple.is_marked(pp)) { - new_args.push_back(arg); - } - else if (!visited.is_marked(pp)) { - visited.mark(pp); - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - a = coeffs[pos]; - normalize(a); - if (!a.is_zero()) - new_args.push_back(mk_mul_app(a, pp)); - } - } - if (m_hoist_cmul) { - hoist_cmul(new_args); - } - else if (m_sort_sums) { - TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";); - if (c.is_zero()) - std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); - else - std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - if (hoist_multiplication(result)) { - return BR_REWRITE_FULL; - } - return BR_DONE; - } - else { - SASSERT(!has_multiple); - if (ordered && !m_hoist_mul && !m_hoist_cmul) { - if (num_coeffs == 0) - return BR_FAILED; - if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) - return BR_FAILED; - } - expr_ref_buffer new_args(m()); - if (!c.is_zero()) - new_args.push_back(mk_numeral(c)); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - new_args.push_back(arg); - } - if (m_hoist_cmul) { - hoist_cmul(new_args); - } - else if (!ordered) { - if (c.is_zero()) - std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); - else - std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - if (hoist_multiplication(result)) { - return BR_REWRITE_FULL; - } - return BR_DONE; - } -} - - -template -br_status poly_rewriter::mk_uminus(expr * arg, expr_ref & result) { - numeral a; - set_curr_sort(m().get_sort(arg)); - if (is_numeral(arg, a)) { - a.neg(); - normalize(a); - result = mk_numeral(a); - return BR_DONE; - } - else { - result = mk_mul_app(numeral(-1), arg); - return BR_REWRITE1; - } -} - -template -br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args > 0); - if (num_args == 1) { - result = args[0]; - return BR_DONE; - } - set_curr_sort(m().get_sort(args[0])); - expr * minus_one = mk_numeral(numeral(-1)); - ptr_buffer new_args; - new_args.push_back(args[0]); - for (unsigned i = 1; i < num_args; i++) { - expr * aux_args[2] = { minus_one, args[i] }; - new_args.push_back(mk_mul_app(2, aux_args)); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - return BR_REWRITE2; -} - -/** - \brief Cancel/Combine monomials that occur is the left and right hand sides. - - \remark If move = true, then all non-constant monomials are moved to the left-hand-side. -*/ -template -br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) { - set_curr_sort(m().get_sort(lhs)); - unsigned lhs_sz; - expr * const * lhs_monomials = get_monomials(lhs, lhs_sz); - unsigned rhs_sz; - expr * const * rhs_monomials = get_monomials(rhs, rhs_sz); - - expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs - expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once - bool has_multiple = false; - - numeral c(0); - numeral a; - unsigned num_coeffs = 0; - - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg, a)) { - c += a; - num_coeffs++; - } - else { - visited.mark(get_power_product(arg)); - } - } - - if (move && num_coeffs == 0 && is_numeral(rhs)) - return BR_FAILED; - - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg, a)) { - c -= a; - num_coeffs++; - } - else { - expr * pp = get_power_product(arg); - if (visited.is_marked(pp)) { - multiple.mark(pp); - has_multiple = true; - } - } - } - - normalize(c); - - if (!has_multiple && num_coeffs <= 1) { - if (move) { - if (is_numeral(rhs)) - return BR_FAILED; - } - else { - if (num_coeffs == 0 || is_numeral(rhs)) - return BR_FAILED; - } - } - - buffer coeffs; - m_expr2pos.reset(); - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos; - if (m_expr2pos.find(pp, pos)) { - coeffs[pos] += a; - } - else { - m_expr2pos.insert(pp, coeffs.size()); - coeffs.push_back(a); - } - } - - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - coeffs[pos] -= a; - } - - - ptr_buffer new_lhs_monomials; - new_lhs_monomials.push_back(0); // save space for coefficient if needed - // copy power products with non zero coefficients to new_lhs_monomials - visited.reset(); - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg); - if (!multiple.is_marked(pp)) { - new_lhs_monomials.push_back(arg); - } - else if (!visited.is_marked(pp)) { - visited.mark(pp); - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - a = coeffs[pos]; - if (!a.is_zero()) - new_lhs_monomials.push_back(mk_mul_app(a, pp)); - } - } - - ptr_buffer new_rhs_monomials; - new_rhs_monomials.push_back(0); // save space for coefficient if needed - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) { - if (move) { - if (!a.is_zero()) { - if (a.is_minus_one()) { - new_lhs_monomials.push_back(pp); - } - else { - a.neg(); - SASSERT(!a.is_one()); - expr * args[2] = { mk_numeral(a), pp }; - new_lhs_monomials.push_back(mk_mul_app(2, args)); - } - } - } - else { - new_rhs_monomials.push_back(arg); - } - } - } - - bool c_at_rhs = false; - if (move) { - if (m_sort_sums) { - // + 1 to skip coefficient - std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt()); - } - c_at_rhs = true; - } - else if (new_rhs_monomials.size() == 1) { // rhs is empty - c_at_rhs = true; - } - else if (new_lhs_monomials.size() > 1) { - c_at_rhs = true; - } - - if (c_at_rhs) { - c.neg(); - normalize(c); - new_rhs_monomials[0] = mk_numeral(c); - lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1); - rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr()); - } - else { - new_lhs_monomials[0] = mk_numeral(c); - lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr()); - rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1); - } - return BR_DONE; -} - -#define TO_BUFFER(_tester_, _buffer_, _e_) \ - _buffer_.push_back(_e_); \ - for (unsigned _i = 0; _i < _buffer_.size(); ) { \ - expr* _e = _buffer_[_i]; \ - if (_tester_(_e)) { \ - app* a = to_app(_e); \ - _buffer_[_i] = a->get_arg(0); \ - for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \ - _buffer_.push_back(a->get_arg(_j)); \ - } \ - } \ - else { \ - ++_i; \ - } \ - } \ - -template -bool poly_rewriter::hoist_multiplication(expr_ref& som) { - if (!m_hoist_mul) { - return false; - } - ptr_buffer adds, muls; - TO_BUFFER(is_add, adds, som); - buffer valid(adds.size(), true); - obj_map mul_map; - unsigned j; - bool change = false; - for (unsigned k = 0; k < adds.size(); ++k) { - expr* e = adds[k]; - muls.reset(); - TO_BUFFER(is_mul, muls, e); - for (unsigned i = 0; i < muls.size(); ++i) { - e = muls[i]; - if (is_numeral(e)) { - continue; - } - if (mul_map.find(e, j) && valid[j] && j != k) { - m_curr_sort = m().get_sort(adds[k]); - adds[j] = merge_muls(adds[j], adds[k]); - adds[k] = mk_numeral(rational(0)); - valid[j] = false; - valid[k] = false; - change = true; - break; - } - else { - mul_map.insert(e, k); - } - } - } - if (!change) { - return false; - } - - som = mk_add_app(adds.size(), adds.c_ptr()); - - - return true; -} - -template -expr* poly_rewriter::merge_muls(expr* x, expr* y) { - ptr_buffer m1, m2; - TO_BUFFER(is_mul, m1, x); - TO_BUFFER(is_mul, m2, y); - unsigned k = 0; - for (unsigned i = 0; i < m1.size(); ++i) { - x = m1[i]; - bool found = false; - unsigned j; - for (j = k; j < m2.size(); ++j) { - found = m2[j] == x; - if (found) break; - } - if (found) { - std::swap(m1[i],m1[k]); - std::swap(m2[j],m2[k]); - ++k; - } - } - m_curr_sort = m().get_sort(x); - SASSERT(k > 0); - SASSERT(m1.size() >= k); - SASSERT(m2.size() >= k); - expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k), - mk_mul_app(m2.size()-k, m2.c_ptr()+k) }; - if (k == m1.size()) { - m1.push_back(0); - } - m1[k] = mk_add_app(2, args); - return mk_mul_app(k+1, m1.c_ptr()); -} +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + poly_rewriter_def.h + +Abstract: + + Basic rewriting rules for Polynomials. + +Author: + + Leonardo (leonardo) 2011-04-08 + +Notes: + +--*/ +#include"poly_rewriter.h" +#include"poly_rewriter_params.hpp" +#include"ast_lt.h" +#include"ast_ll_pp.h" +#include"ast_smt2_pp.h" + +template +char const * poly_rewriter::g_ste_blowup_msg = "sum of monomials blowup"; + + +template +void poly_rewriter::updt_params(params_ref const & _p) { + poly_rewriter_params p(_p); + m_flat = p.flat(); + m_som = p.som(); + m_hoist_mul = p.hoist_mul(); + m_hoist_cmul = p.hoist_cmul(); + m_som_blowup = p.som_blowup(); +} + +template +void poly_rewriter::get_param_descrs(param_descrs & r) { + poly_rewriter_params::collect_param_descrs(r); +} + +template +expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) { + switch (num_args) { + case 0: return mk_numeral(numeral(0)); + case 1: return args[0]; + default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); + } +} + +// t = (^ x y) --> return x, and set k = y if k is an integer >= 1 +// Otherwise return t and set k = 1 +template +expr * poly_rewriter::get_power_body(expr * t, rational & k) { + if (!is_power(t)) { + k = rational(1); + return t; + } + if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) { + return to_app(t)->get_arg(0); + } + k = rational(1); + return t; +} + +template +expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) { + switch (num_args) { + case 0: + return mk_numeral(numeral(1)); + case 1: + return args[0]; + default: + if (use_power()) { + rational k_prev; + expr * prev = get_power_body(args[0], k_prev); + rational k; + ptr_buffer new_args; +#define PUSH_POWER() { \ + if (k_prev.is_one()) { \ + new_args.push_back(prev); \ + } \ + else { \ + expr * pargs[2] = { prev, mk_numeral(k_prev) }; \ + new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \ + } \ + } + + for (unsigned i = 1; i < num_args; i++) { + expr * arg = get_power_body(args[i], k); + if (arg == prev) { + k_prev += k; + } + else { + PUSH_POWER(); + prev = arg; + k_prev = k; + } + } + PUSH_POWER(); + SASSERT(new_args.size() > 0); + if (new_args.size() == 1) { + return new_args[0]; + } + else { + return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr()); + } + } + else { + return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); + } + } +} + +template +expr * poly_rewriter::mk_mul_app(numeral const & c, expr * arg) { + if (c.is_one()) { + return arg; + } + else { + expr * new_args[2] = { mk_numeral(c), arg }; + return mk_mul_app(2, new_args); + } +} + +template +br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + // only try to apply flattening if it is not already in one of the flat monomial forms + // - (* c x) + // - (* c (* x_1 ... x_n)) + if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) { + unsigned i; + for (i = 0; i < num_args; i++) { + if (is_mul(args[i])) + break; + } + if (i < num_args) { + // input has nested monomials. + ptr_buffer flat_args; + // we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n))) + ptr_buffer todo; + flat_args.append(i, args); + for (unsigned j = i; j < num_args; j++) { + if (is_mul(args[j])) { + todo.push_back(args[j]); + while (!todo.empty()) { + expr * curr = todo.back(); + todo.pop_back(); + if (is_mul(curr)) { + unsigned k = to_app(curr)->get_num_args(); + while (k > 0) { + --k; + todo.push_back(to_app(curr)->get_arg(k)); + } + } + else { + flat_args.push_back(curr); + } + } + } + else { + flat_args.push_back(args[j]); + } + } + TRACE("poly_rewriter", + tout << "flat mul:\n"; + for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; + tout << "---->\n"; + for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";); + br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result); + if (st == BR_FAILED) { + result = mk_mul_app(flat_args.size(), flat_args.c_ptr()); + return BR_DONE; + } + return st; + } + } + return mk_nflat_mul_core(num_args, args, result); +} + + +template +struct poly_rewriter::mon_pw_lt { + poly_rewriter & m_owner; + mon_pw_lt(poly_rewriter & o):m_owner(o) {} + + bool operator()(expr * n1, expr * n2) const { + rational k; + return lt(m_owner.get_power_body(n1, k), + m_owner.get_power_body(n2, k)); + } +}; + + +template +br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + // cheap case + numeral a; + if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() && + (is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid())) + return BR_FAILED; + numeral c(1); + unsigned num_coeffs = 0; + unsigned num_add = 0; + expr * var = 0; + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg, a)) { + num_coeffs++; + c *= a; + } + else { + var = arg; + if (is_add(arg)) + num_add++; + } + } + + normalize(c); + // (* c_1 ... c_n) --> c_1*...*c_n + if (num_coeffs == num_args) { + result = mk_numeral(c); + return BR_DONE; + } + + // (* s ... 0 ... r) --> 0 + if (c.is_zero()) { + result = mk_numeral(c); + return BR_DONE; + } + + if (num_coeffs == num_args - 1) { + SASSERT(var != 0); + // (* c_1 ... c_n x) --> x if c_1*...*c_n == 1 + if (c.is_one()) { + result = var; + return BR_DONE; + } + + numeral c_prime; + if (is_mul(var)) { + // apply basic simplification even when flattening is not enabled. + // (* c1 (* c2 x)) --> (* c1*c2 x) + if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) { + c *= c_prime; + normalize(c); + result = mk_mul_app(c, to_app(var)->get_arg(1)); + return BR_REWRITE1; + } + else { + // var is a power-product + return BR_FAILED; + } + } + + if (num_add == 0 || m_hoist_cmul) { + SASSERT(!is_add(var) || m_hoist_cmul); + if (num_args == 2 && args[1] == var) { + DEBUG_CODE({ + numeral c_prime; + SASSERT(is_numeral(args[0], c_prime) && c == c_prime); + }); + // it is already simplified + return BR_FAILED; + } + + // (* c_1 ... c_n x) --> (* c_1*...*c_n x) + result = mk_mul_app(c, var); + return BR_DONE; + } + else { + SASSERT(is_add(var)); + // (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m)) + ptr_buffer new_add_args; + unsigned num = to_app(var)->get_num_args(); + for (unsigned i = 0; i < num; i++) { + new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); + } + result = mk_add_app(new_add_args.size(), new_add_args.c_ptr()); + TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); + return BR_REWRITE2; + } + } + + SASSERT(num_coeffs <= num_args - 2); + + if (!m_som || num_add == 0) { + ptr_buffer new_args; + expr * prev = 0; + bool ordered = true; + for (unsigned i = 0; i < num_args; i++) { + expr * curr = args[i]; + if (is_numeral(curr)) + continue; + if (prev != 0 && lt(curr, prev)) + ordered = false; + new_args.push_back(curr); + prev = curr; + } + TRACE("poly_rewriter", + for (unsigned i = 0; i < new_args.size(); i++) { + if (i > 0) + tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); + tout << mk_ismt2_pp(new_args[i], m()); + } + tout << "\nordered: " << ordered << "\n";); + if (ordered && num_coeffs == 0 && !use_power()) + return BR_FAILED; + if (!ordered) { + if (use_power()) + std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this)); + else + std::sort(new_args.begin(), new_args.end(), ast_to_lt()); + TRACE("poly_rewriter", + tout << "after sorting:\n"; + for (unsigned i = 0; i < new_args.size(); i++) { + if (i > 0) + tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); + tout << mk_ismt2_pp(new_args[i], m()); + } + tout << "\n";); + } + SASSERT(new_args.size() >= 2); + result = mk_mul_app(new_args.size(), new_args.c_ptr()); + result = mk_mul_app(c, result); + TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); + return BR_DONE; + } + + SASSERT(m_som && num_add > 0); + + sbuffer szs; + sbuffer it; + sbuffer sums; + for (unsigned i = 0; i < num_args; i ++) { + it.push_back(0); + expr * arg = args[i]; + if (is_add(arg)) { + sums.push_back(const_cast(to_app(arg)->get_args())); + szs.push_back(to_app(arg)->get_num_args()); + } + else { + sums.push_back(const_cast(args + i)); + szs.push_back(1); + SASSERT(sums.back()[0] == arg); + } + } + expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception + ptr_buffer m_args; + TRACE("som", tout << "starting som...\n";); + do { + TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; + tout << "\n";); + if (sum.size() > m_som_blowup) + throw rewriter_exception(g_ste_blowup_msg); + m_args.reset(); + for (unsigned i = 0; i < num_args; i++) { + expr * const * v = sums[i]; + expr * arg = v[it[i]]; + m_args.push_back(arg); + } + sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr())); + } + while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); + result = mk_add_app(sum.size(), sum.c_ptr()); + return BR_REWRITE2; +} + +template +br_status poly_rewriter::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { + unsigned i; + for (i = 0; i < num_args; i++) { + if (is_add(args[i])) + break; + } + if (i < num_args) { + // has nested ADDs + ptr_buffer flat_args; + flat_args.append(i, args); + for (; i < num_args; i++) { + expr * arg = args[i]; + // Remark: all rewrites are depth 1. + if (is_add(arg)) { + unsigned num = to_app(arg)->get_num_args(); + for (unsigned j = 0; j < num; j++) + flat_args.push_back(to_app(arg)->get_arg(j)); + } + else { + flat_args.push_back(arg); + } + } + br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result); + if (st == BR_FAILED) { + result = mk_add_app(flat_args.size(), flat_args.c_ptr()); + return BR_DONE; + } + return st; + } + return mk_nflat_add_core(num_args, args, result); +} + +template +inline expr * poly_rewriter::get_power_product(expr * t) { + if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0))) + return to_app(t)->get_arg(1); + return t; +} + +template +inline expr * poly_rewriter::get_power_product(expr * t, numeral & a) { + if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a)) + return to_app(t)->get_arg(1); + a = numeral(1); + return t; +} + +template +bool poly_rewriter::is_mul(expr * t, numeral & c, expr * & pp) { + if (!is_mul(t) || to_app(t)->get_num_args() != 2) + return false; + if (!is_numeral(to_app(t)->get_arg(0), c)) + return false; + pp = to_app(t)->get_arg(1); + return true; +} + +template +struct poly_rewriter::hoist_cmul_lt { + poly_rewriter & m_r; + hoist_cmul_lt(poly_rewriter & r):m_r(r) {} + + bool operator()(expr * t1, expr * t2) const { + expr * pp1, * pp2; + numeral c1, c2; + bool is_mul1 = m_r.is_mul(t1, c1, pp1); + bool is_mul2 = m_r.is_mul(t2, c2, pp2); + if (!is_mul1 && is_mul2) + return true; + if (is_mul1 && !is_mul2) + return false; + if (!is_mul1 && !is_mul2) + return t1->get_id() < t2->get_id(); + if (c1 < c2) + return true; + if (c1 > c2) + return false; + return pp1->get_id() < pp2->get_id(); + } +}; + +template +void poly_rewriter::hoist_cmul(expr_ref_buffer & args) { + unsigned sz = args.size(); + std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this)); + numeral c, c_prime; + ptr_buffer pps; + expr * pp, * pp_prime; + unsigned j = 0; + unsigned i = 0; + while (i < sz) { + expr * mon = args[i]; + if (is_mul(mon, c, pp) && i < sz - 1) { + expr * mon_prime = args[i+1]; + if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) { + // found target + pps.reset(); + pps.push_back(pp); + pps.push_back(pp_prime); + i += 2; + while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) { + pps.push_back(pp_prime); + i++; + } + SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime); + expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) }; + args.set(j, mk_mul_app(2, mul_args)); + j++; + continue; + } + } + args.set(j, mon); + j++; + i++; + } + args.resize(j); +} + +template +br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + numeral c; + unsigned num_coeffs = 0; + numeral a; + expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args + expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once + bool has_multiple = false; + expr * prev = 0; + bool ordered = true; + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg, a)) { + num_coeffs++; + c += a; + } + else { + // arg is not a numeral + if (m_sort_sums && ordered) { + if (prev != 0 && lt(arg, prev)) + ordered = false; + prev = arg; + } + } + + arg = get_power_product(arg); + if (visited.is_marked(arg)) { + multiple.mark(arg); + has_multiple = true; + } + else { + visited.mark(arg); + } + } + normalize(c); + SASSERT(m_sort_sums || ordered); + TRACE("sort_sums", + tout << "ordered: " << ordered << "\n"; + for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); + + if (has_multiple) { + // expensive case + buffer coeffs; + m_expr2pos.reset(); + // compute the coefficient of power products that occur multiple times. + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos; + if (m_expr2pos.find(pp, pos)) { + coeffs[pos] += a; + } + else { + m_expr2pos.insert(pp, coeffs.size()); + coeffs.push_back(a); + } + } + expr_ref_buffer new_args(m()); + if (!c.is_zero()) { + new_args.push_back(mk_numeral(c)); + } + // copy power products with non zero coefficients to new_args + visited.reset(); + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg); + if (!multiple.is_marked(pp)) { + new_args.push_back(arg); + } + else if (!visited.is_marked(pp)) { + visited.mark(pp); + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + a = coeffs[pos]; + normalize(a); + if (!a.is_zero()) + new_args.push_back(mk_mul_app(a, pp)); + } + } + if (m_hoist_cmul) { + hoist_cmul(new_args); + } + else if (m_sort_sums) { + TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";); + if (c.is_zero()) + std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); + else + std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + if (hoist_multiplication(result)) { + return BR_REWRITE_FULL; + } + return BR_DONE; + } + else { + SASSERT(!has_multiple); + if (ordered && !m_hoist_mul && !m_hoist_cmul) { + if (num_coeffs == 0) + return BR_FAILED; + if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) + return BR_FAILED; + } + expr_ref_buffer new_args(m()); + if (!c.is_zero()) + new_args.push_back(mk_numeral(c)); + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + new_args.push_back(arg); + } + if (m_hoist_cmul) { + hoist_cmul(new_args); + } + else if (!ordered) { + if (c.is_zero()) + std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); + else + std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + if (hoist_multiplication(result)) { + return BR_REWRITE_FULL; + } + return BR_DONE; + } +} + + +template +br_status poly_rewriter::mk_uminus(expr * arg, expr_ref & result) { + numeral a; + set_curr_sort(m().get_sort(arg)); + if (is_numeral(arg, a)) { + a.neg(); + normalize(a); + result = mk_numeral(a); + return BR_DONE; + } + else { + result = mk_mul_app(numeral(-1), arg); + return BR_REWRITE1; + } +} + +template +br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args > 0); + if (num_args == 1) { + result = args[0]; + return BR_DONE; + } + set_curr_sort(m().get_sort(args[0])); + expr * minus_one = mk_numeral(numeral(-1)); + ptr_buffer new_args; + new_args.push_back(args[0]); + for (unsigned i = 1; i < num_args; i++) { + expr * aux_args[2] = { minus_one, args[i] }; + new_args.push_back(mk_mul_app(2, aux_args)); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + return BR_REWRITE2; +} + +/** + \brief Cancel/Combine monomials that occur is the left and right hand sides. + + \remark If move = true, then all non-constant monomials are moved to the left-hand-side. +*/ +template +br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) { + set_curr_sort(m().get_sort(lhs)); + unsigned lhs_sz; + expr * const * lhs_monomials = get_monomials(lhs, lhs_sz); + unsigned rhs_sz; + expr * const * rhs_monomials = get_monomials(rhs, rhs_sz); + + expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs + expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once + bool has_multiple = false; + + numeral c(0); + numeral a; + unsigned num_coeffs = 0; + + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg, a)) { + c += a; + num_coeffs++; + } + else { + visited.mark(get_power_product(arg)); + } + } + + if (move && num_coeffs == 0 && is_numeral(rhs)) + return BR_FAILED; + + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg, a)) { + c -= a; + num_coeffs++; + } + else { + expr * pp = get_power_product(arg); + if (visited.is_marked(pp)) { + multiple.mark(pp); + has_multiple = true; + } + } + } + + normalize(c); + + if (!has_multiple && num_coeffs <= 1) { + if (move) { + if (is_numeral(rhs)) + return BR_FAILED; + } + else { + if (num_coeffs == 0 || is_numeral(rhs)) + return BR_FAILED; + } + } + + buffer coeffs; + m_expr2pos.reset(); + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos; + if (m_expr2pos.find(pp, pos)) { + coeffs[pos] += a; + } + else { + m_expr2pos.insert(pp, coeffs.size()); + coeffs.push_back(a); + } + } + + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + coeffs[pos] -= a; + } + + + ptr_buffer new_lhs_monomials; + new_lhs_monomials.push_back(0); // save space for coefficient if needed + // copy power products with non zero coefficients to new_lhs_monomials + visited.reset(); + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg); + if (!multiple.is_marked(pp)) { + new_lhs_monomials.push_back(arg); + } + else if (!visited.is_marked(pp)) { + visited.mark(pp); + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + a = coeffs[pos]; + if (!a.is_zero()) + new_lhs_monomials.push_back(mk_mul_app(a, pp)); + } + } + + ptr_buffer new_rhs_monomials; + new_rhs_monomials.push_back(0); // save space for coefficient if needed + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) { + if (move) { + if (!a.is_zero()) { + if (a.is_minus_one()) { + new_lhs_monomials.push_back(pp); + } + else { + a.neg(); + SASSERT(!a.is_one()); + expr * args[2] = { mk_numeral(a), pp }; + new_lhs_monomials.push_back(mk_mul_app(2, args)); + } + } + } + else { + new_rhs_monomials.push_back(arg); + } + } + } + + bool c_at_rhs = false; + if (move) { + if (m_sort_sums) { + // + 1 to skip coefficient + std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt()); + } + c_at_rhs = true; + } + else if (new_rhs_monomials.size() == 1) { // rhs is empty + c_at_rhs = true; + } + else if (new_lhs_monomials.size() > 1) { + c_at_rhs = true; + } + + if (c_at_rhs) { + c.neg(); + normalize(c); + new_rhs_monomials[0] = mk_numeral(c); + lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1); + rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr()); + } + else { + new_lhs_monomials[0] = mk_numeral(c); + lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr()); + rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1); + } + return BR_DONE; +} + +#define TO_BUFFER(_tester_, _buffer_, _e_) \ + _buffer_.push_back(_e_); \ + for (unsigned _i = 0; _i < _buffer_.size(); ) { \ + expr* _e = _buffer_[_i]; \ + if (_tester_(_e)) { \ + app* a = to_app(_e); \ + _buffer_[_i] = a->get_arg(0); \ + for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \ + _buffer_.push_back(a->get_arg(_j)); \ + } \ + } \ + else { \ + ++_i; \ + } \ + } \ + +template +bool poly_rewriter::hoist_multiplication(expr_ref& som) { + if (!m_hoist_mul) { + return false; + } + ptr_buffer adds, muls; + TO_BUFFER(is_add, adds, som); + buffer valid(adds.size(), true); + obj_map mul_map; + unsigned j; + bool change = false; + for (unsigned k = 0; k < adds.size(); ++k) { + expr* e = adds[k]; + muls.reset(); + TO_BUFFER(is_mul, muls, e); + for (unsigned i = 0; i < muls.size(); ++i) { + e = muls[i]; + if (is_numeral(e)) { + continue; + } + if (mul_map.find(e, j) && valid[j] && j != k) { + m_curr_sort = m().get_sort(adds[k]); + adds[j] = merge_muls(adds[j], adds[k]); + adds[k] = mk_numeral(rational(0)); + valid[j] = false; + valid[k] = false; + change = true; + break; + } + else { + mul_map.insert(e, k); + } + } + } + if (!change) { + return false; + } + + som = mk_add_app(adds.size(), adds.c_ptr()); + + + return true; +} + +template +expr* poly_rewriter::merge_muls(expr* x, expr* y) { + ptr_buffer m1, m2; + TO_BUFFER(is_mul, m1, x); + TO_BUFFER(is_mul, m2, y); + unsigned k = 0; + for (unsigned i = 0; i < m1.size(); ++i) { + x = m1[i]; + bool found = false; + unsigned j; + for (j = k; j < m2.size(); ++j) { + found = m2[j] == x; + if (found) break; + } + if (found) { + std::swap(m1[i],m1[k]); + std::swap(m2[j],m2[k]); + ++k; + } + } + m_curr_sort = m().get_sort(x); + SASSERT(k > 0); + SASSERT(m1.size() >= k); + SASSERT(m2.size() >= k); + expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k), + mk_mul_app(m2.size()-k, m2.c_ptr()+k) }; + if (k == m1.size()) { + m1.push_back(0); + } + m1[k] = mk_add_app(2, args); + return mk_mul_app(k+1, m1.c_ptr()); +} diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 966544b78..5d19c53e3 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -169,7 +169,9 @@ struct th_rewriter_cfg : public default_rewriter_cfg { st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); - + else if (s_fid == m_ar_rw.get_fid()) + st = m_ar_rw.mk_eq_core(args[0], args[1], result); + if (st != BR_FAILED) return st; } diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index 3ebc0b573..930267dad 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -17,7 +17,6 @@ Notes: --*/ #include"var_subst.h" -#include"used_vars.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"ast_smt2_pp.h" @@ -40,7 +39,7 @@ void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, exp tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";); } -void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { +void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { SASSERT(is_well_sorted(m, q)); if (is_ground(q->get_expr())) { // ignore patterns if the body is a ground formula. @@ -51,17 +50,17 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { result = q; return; } - used_vars used; - used.process(q->get_expr()); + m_used.reset(); + m_used.process(q->get_expr()); unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) - used.process(q->get_pattern(i)); + m_used.process(q->get_pattern(i)); unsigned num_no_patterns = q->get_num_no_patterns(); for (unsigned i = 0; i < num_no_patterns; i++) - used.process(q->get_no_pattern(i)); + m_used.process(q->get_no_pattern(i)); unsigned num_decls = q->get_num_decls(); - if (used.uses_all_vars(num_decls)) { + if (m_used.uses_all_vars(num_decls)) { q->set_no_unused_vars(); result = q; return; @@ -70,7 +69,7 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { ptr_buffer used_decl_sorts; buffer used_decl_names; for (unsigned i = 0; i < num_decls; ++i) { - if (used.contains(num_decls - i - 1)) { + if (m_used.contains(num_decls - i - 1)) { used_decl_sorts.push_back(q->get_decl_sort(i)); used_decl_names.push_back(q->get_decl_name(i)); } @@ -79,10 +78,10 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { unsigned num_removed = 0; expr_ref_buffer var_mapping(m); int next_idx = 0; - unsigned sz = used.get_max_found_var_idx_plus_1(); + unsigned sz = m_used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < num_decls; ++i) { - sort * s = used.contains(i); + sort * s = m_used.contains(i); if (s) { var_mapping.push_back(m.mk_var(next_idx, s)); next_idx++; @@ -95,7 +94,7 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { // (VAR 0) is in the first position of var_mapping. for (unsigned i = num_decls; i < sz; i++) { - sort * s = used.contains(i); + sort * s = m_used.contains(i); if (s) var_mapping.push_back(m.mk_var(i - num_removed, s)); else @@ -110,9 +109,8 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { std::reverse(var_mapping.c_ptr(), var_mapping.c_ptr() + var_mapping.size()); expr_ref new_expr(m); - var_subst subst(m); - subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr); + m_subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr); if (num_removed == num_decls) { result = new_expr; @@ -124,11 +122,11 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { expr_ref_buffer new_no_patterns(m); for (unsigned i = 0; i < num_patterns; i++) { - subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); + m_subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); new_patterns.push_back(tmp); } for (unsigned i = 0; i < num_no_patterns; i++) { - subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); + m_subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); new_no_patterns.push_back(tmp); } @@ -145,7 +143,12 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { num_no_patterns, new_no_patterns.c_ptr()); to_quantifier(result)->set_no_unused_vars(); - SASSERT(is_well_sorted(m, result)); + SASSERT(is_well_sorted(m, result)); +} + +void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { + unused_vars_eliminator el(m); + el(q, result); } void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result) { @@ -161,9 +164,7 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";); } -static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sorts) { - ast_mark mark; - ptr_vector todo; +static void get_free_vars_offset(ast_mark& mark, ptr_vector& todo, unsigned offset, expr* e, ptr_vector& sorts) { todo.push_back(e); while (!todo.empty()) { e = todo.back(); @@ -175,7 +176,9 @@ static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sor switch(e->get_kind()) { case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); - get_free_vars_offset(q->get_expr(), offset+q->get_num_decls(), sorts); + ast_mark mark1; + ptr_vector todo1; + get_free_vars_offset(mark1, todo1, offset+q->get_num_decls(), q->get_expr(), sorts); break; } case AST_VAR: { @@ -207,5 +210,11 @@ static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sor void get_free_vars(expr* e, ptr_vector& sorts) { - get_free_vars_offset(e, 0, sorts); + ast_mark mark; + ptr_vector todo; + get_free_vars_offset(mark, todo, 0, e, sorts); +} + +void get_free_vars(ast_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts) { + get_free_vars_offset(mark, todo, 0, e, sorts); } diff --git a/src/ast/rewriter/var_subst.h b/src/ast/rewriter/var_subst.h index 9f3c13c0c..ffc21e691 100644 --- a/src/ast/rewriter/var_subst.h +++ b/src/ast/rewriter/var_subst.h @@ -20,6 +20,7 @@ Notes: #define _VAR_SUBST_H_ #include"rewriter.h" +#include"used_vars.h" /** \brief Alias for var_shifter class. @@ -53,6 +54,15 @@ public: /** \brief Eliminate the unused variables from \c q. Store the result in \c r. */ +class unused_vars_eliminator { + ast_manager& m; + var_subst m_subst; + used_vars m_used; +public: + unused_vars_eliminator(ast_manager& m): m(m), m_subst(m) {} + void operator()(quantifier* q, expr_ref& r); +}; + void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & r); /** @@ -73,6 +83,8 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref */ void get_free_vars(expr* e, ptr_vector& sorts); +void get_free_vars(ast_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts); + #endif diff --git a/src/ast/simplifier/bv_simplifier_plugin.cpp b/src/ast/simplifier/bv_simplifier_plugin.cpp index 3ef55baba..8ee353a76 100644 --- a/src/ast/simplifier/bv_simplifier_plugin.cpp +++ b/src/ast/simplifier/bv_simplifier_plugin.cpp @@ -636,7 +636,31 @@ bool bv_simplifier_plugin::try_mk_extract(unsigned high, unsigned low, expr * ar if (!all_found) { return false; } - result = m_manager.mk_app(m_fid, a->get_decl_kind(), new_args.size(), new_args.c_ptr()); + // We should not use mk_app because it does not guarantee that the result would be in simplified form. + // result = m_manager.mk_app(m_fid, a->get_decl_kind(), new_args.size(), new_args.c_ptr()); + if (is_app_of(a, m_fid, OP_BAND)) + mk_bv_and(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BOR)) + mk_bv_or(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BXOR)) + mk_bv_xor(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNOR)) + mk_bv_nor(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNAND)) + mk_bv_nand(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNOT)) { + SASSERT(new_args.size() == 1); + mk_bv_not(new_args[0], result); + } + else if (is_app_of(a, m_fid, OP_BADD)) + mk_add(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BMUL)) + mk_mul(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BSUB)) + mk_sub(new_args.size(), new_args.c_ptr(), result); + else { + UNREACHABLE(); + } return true; } else if (m_manager.is_ite(a)) { @@ -747,16 +771,16 @@ void bv_simplifier_plugin::mk_bv_eq(expr* a1, expr* a2, expr_ref& result) { expr * arg1 = *it1; expr * arg2 = *it2; TRACE("expr_bv_util", tout << "low1: " << low1 << " low2: " << low2 << "\n"; - ast_ll_pp(tout, m_manager, arg1); - ast_ll_pp(tout, m_manager, arg2);); + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n";); unsigned sz1 = get_bv_size(arg1); unsigned sz2 = get_bv_size(arg2); SASSERT(low1 < sz1 && low2 < sz2); unsigned rsz1 = sz1 - low1; unsigned rsz2 = sz2 - low2; TRACE("expr_bv_util", tout << "rsz1: " << rsz1 << " rsz2: " << rsz2 << "\n"; - ast_ll_pp(tout, m_manager, arg1); ast_ll_pp(tout, m_manager, arg2);); - + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n";); if (rsz1 == rsz2) { mk_extract(sz1 - 1, low1, arg1, lhs); @@ -826,9 +850,9 @@ void bv_simplifier_plugin::mk_eq_core(expr * arg1, expr * arg2, expr_ref & resul } m_bsimp.mk_and(tmps.size(), tmps.c_ptr(), result); TRACE("mk_eq_bb", - ast_ll_pp(tout, m_manager, arg1); - ast_ll_pp(tout, m_manager, arg2); - ast_ll_pp(tout, m_manager, result);); + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n"; + tout << mk_pp(result, m_manager) << "\n";); return; } #endif diff --git a/src/ast/simplifier/poly_simplifier_plugin.cpp b/src/ast/simplifier/poly_simplifier_plugin.cpp index 402b078a8..d64123e7b 100644 --- a/src/ast/simplifier/poly_simplifier_plugin.cpp +++ b/src/ast/simplifier/poly_simplifier_plugin.cpp @@ -285,6 +285,7 @@ bool poly_simplifier_plugin::merge_monomials(bool inv, expr * n1, expr * n2, exp else result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k1), b); } + TRACE("merge_monomials", tout << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";); return true; } diff --git a/src/ast/substitution/matcher.cpp b/src/ast/substitution/matcher.cpp index a5560c6a2..ce9bdcb3e 100644 --- a/src/ast/substitution/matcher.cpp +++ b/src/ast/substitution/matcher.cpp @@ -18,10 +18,6 @@ Revision History: --*/ #include"matcher.h" -matcher::matcher(ast_manager & m): - m_manager(m) { -} - bool matcher::operator()(expr * e1, expr * e2, substitution & s) { reset(); m_subst = &s; diff --git a/src/ast/substitution/matcher.h b/src/ast/substitution/matcher.h index 1a71a51ed..84a62e874 100644 --- a/src/ast/substitution/matcher.h +++ b/src/ast/substitution/matcher.h @@ -30,7 +30,6 @@ class matcher { typedef pair_hash, obj_ptr_hash > expr_pair_hash; typedef hashtable > cache; - ast_manager & m_manager; substitution * m_subst; // cache m_cache; svector m_todo; @@ -38,7 +37,7 @@ class matcher { void reset(); public: - matcher(ast_manager & m); + matcher() {} /** \brief Return true if e2 is an instance of e1. diff --git a/src/ast/substitution/substitution.cpp b/src/ast/substitution/substitution.cpp index 11bca0133..be293c5a8 100644 --- a/src/ast/substitution/substitution.cpp +++ b/src/ast/substitution/substitution.cpp @@ -148,7 +148,7 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e expr * arg = to_app(e)->get_arg(i); expr * new_arg; - m_apply_cache.find(expr_offset(arg, off), new_arg); + VERIFY(m_apply_cache.find(expr_offset(arg, off), new_arg)); new_args.push_back(new_arg); if (arg != new_arg) has_new_args = true; diff --git a/src/cmd_context/README b/src/cmd_context/README index c44a00690..feb3abd20 100644 --- a/src/cmd_context/README +++ b/src/cmd_context/README @@ -1,2 +1,2 @@ Command context provides the infrastructure for executing commands in front-ends such as SMT-LIB 2.0. -It is also provides the solver abstraction to plugin solvers in this kind of front-end. \ No newline at end of file +It is also provides the solver abstraction to plugin solvers in this kind of front-end. diff --git a/src/math/euclid/README b/src/math/euclid/README index 7235cd76f..17d408fc9 100644 --- a/src/math/euclid/README +++ b/src/math/euclid/README @@ -1,2 +1,2 @@ Basic Euclidean solver for linear integer equations. -This solver generates "explanations". \ No newline at end of file +This solver generates "explanations". diff --git a/src/math/interval/README b/src/math/interval/README index 75aa2e9c6..06ca1ea7a 100644 --- a/src/math/interval/README +++ b/src/math/interval/README @@ -1,2 +1,2 @@ Template for interval arithmetic. The template can be instantiated using different numeral (integers/mpz, rationals/mpq, floating-point/mpf, etc) packages. -The class im_default_config defines a default configuration for the template that uses rationals. It also shows what is the expected signature used by the template. \ No newline at end of file +The class im_default_config defines a default configuration for the template that uses rationals. It also shows what is the expected signature used by the template. diff --git a/src/math/polynomial/README b/src/math/polynomial/README index 5d079eea0..2d2f9f0a0 100644 --- a/src/math/polynomial/README +++ b/src/math/polynomial/README @@ -1,3 +1,3 @@ Polynomial manipulation package. It contains support for univariate (upolynomial.*) and multivariate polynomials (polynomial.*). -Multivariate polynomial factorization does not work yet (polynomial_factorization.*), and it is disabled. \ No newline at end of file +Multivariate polynomial factorization does not work yet (polynomial_factorization.*), and it is disabled. diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 87b1ef8a4..84ba606fd 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -660,8 +660,7 @@ namespace realclosure { return; // interval was already saved. to_restore.push_back(v); inc_ref(v); - void * mem = allocator().allocate(sizeof(mpbqi)); - v->m_old_interval = new (mem) mpbqi(); + v->m_old_interval = new (allocator()) mpbqi(); set_interval(*(v->m_old_interval), v->m_interval); } void save_interval(value * v) { @@ -1237,8 +1236,7 @@ namespace realclosure { } sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) { - void * mem = allocator().allocate(sizeof(sign_condition)); - return new (mem) sign_condition(qidx, sign, prev_sc); + return new (allocator()) sign_condition(qidx, sign, prev_sc); } /** @@ -1246,7 +1244,7 @@ namespace realclosure { This method does not set the interval. It remains (-oo, oo) */ rational_function_value * mk_rational_function_value_core(extension * ext, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { - rational_function_value * r = alloc(rational_function_value, ext); + rational_function_value * r = new (allocator()) rational_function_value(ext); inc_ref(ext); set_p(r->num(), num_sz, num); if (ext->is_algebraic()) { @@ -1283,7 +1281,7 @@ namespace realclosure { */ void mk_infinitesimal(symbol const & n, symbol const & pp_n, numeral & r) { unsigned idx = next_infinitesimal_idx(); - infinitesimal * eps = alloc(infinitesimal, idx, n, pp_n); + infinitesimal * eps = new (allocator()) infinitesimal(idx, n, pp_n); m_extensions[extension::INFINITESIMAL].push_back(eps); set_lower(eps->interval(), mpbq(0)); @@ -1335,7 +1333,7 @@ namespace realclosure { void mk_transcendental(symbol const & n, symbol const & pp_n, mk_interval & proc, numeral & r) { unsigned idx = next_transcendental_idx(); - transcendental * t = alloc(transcendental, idx, n, pp_n, proc); + transcendental * t = new (allocator()) transcendental(idx, n, pp_n, proc); m_extensions[extension::TRANSCENDENTAL].push_back(t); while (contains_zero(t->interval())) { @@ -1798,8 +1796,7 @@ namespace realclosure { M and scs will be empty after this operation. */ sign_det * mk_sign_det(mpz_matrix & M_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { - void * mem = allocator().allocate(sizeof(sign_det)); - sign_det * r = new (mem) sign_det(); + sign_det * r = new (allocator()) sign_det(); r->M_s.swap(M_s); set_array_p(r->m_prs, prs); r->m_taqrs.set(allocator(), taqrs.size(), taqrs.c_ptr()); @@ -1814,8 +1811,7 @@ namespace realclosure { */ algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx) { unsigned idx = next_algebraic_idx(); - void * mem = allocator().allocate(sizeof(algebraic)); - algebraic * r = new (mem) algebraic(idx); + algebraic * r = new (allocator()) algebraic(idx); m_extensions[extension::ALGEBRAIC].push_back(r); set_p(r->m_p, p_sz, p); @@ -2561,8 +2557,7 @@ namespace realclosure { } rational_value * mk_rational() { - void * mem = allocator().allocate(sizeof(rational_value)); - return new (mem) rational_value(); + return new (allocator()) rational_value(); } /** diff --git a/src/math/subpaving/subpaving.cpp b/src/math/subpaving/subpaving.cpp index 0bbabc683..ad0819ad8 100644 --- a/src/math/subpaving/subpaving.cpp +++ b/src/math/subpaving/subpaving.cpp @@ -85,7 +85,6 @@ namespace subpaving { }; class context_mpf_wrapper : public context_wrapper { - f2n & m_fm; unsynch_mpq_manager & m_qm; scoped_mpf m_c; scoped_mpf_vector m_as; @@ -103,7 +102,6 @@ namespace subpaving { public: context_mpf_wrapper(f2n & fm, params_ref const & p, small_object_allocator * a): context_wrapper(fm, p, a), - m_fm(fm), m_qm(fm.m().mpq_manager()), m_c(fm.m()), m_as(fm.m()), @@ -145,7 +143,6 @@ namespace subpaving { }; class context_hwf_wrapper : public context_wrapper { - f2n & m_fm; unsynch_mpq_manager & m_qm; hwf m_c; svector m_as; @@ -166,7 +163,6 @@ namespace subpaving { public: context_hwf_wrapper(f2n & fm, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): context_wrapper(fm, p, a), - m_fm(fm), m_qm(qm) { } diff --git a/src/muz_qe/aig_exporter.cpp b/src/muz_qe/aig_exporter.cpp new file mode 100755 index 000000000..ca0030fc3 --- /dev/null +++ b/src/muz_qe/aig_exporter.cpp @@ -0,0 +1,328 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + aig_exporter.cpp + +Abstract: + + Export AIG files from horn clauses + +--*/ + +#include "aig_exporter.h" +#include "dl_context.h" +#include + +namespace datalog { + + aig_exporter::aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts) : + m_rules(rules), m_facts(facts), m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), + m_aigm(m), m_next_decl_id(1), m_next_aig_expr_id(2), m_num_and_gates(0), + m_latch_vars(m), m_latch_varsp(m), m_ruleid_var_set(m), m_ruleid_varp_set(m) + { + std::set predicates; + for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), + E = m_rules.end_grouped_rules(); I != E; ++I) { + predicates.insert(I->m_key); + } + + for (fact_vector::const_iterator I = facts->begin(), E = facts->end(); I != E; ++I) { + predicates.insert(I->first); + } + + // reserve pred id = 0 for initalization purposes + unsigned num_preds = (unsigned)predicates.size() + 1; + + // poor's man round-up log2 + unsigned preds_bitsize = log2(num_preds); + if ((1U << preds_bitsize) < num_preds) + ++preds_bitsize; + SASSERT((1U << preds_bitsize) >= num_preds); + + for (unsigned i = 0; i < preds_bitsize; ++i) { + m_ruleid_var_set.push_back(m.mk_fresh_const("rule_id", m.mk_bool_sort())); + m_ruleid_varp_set.push_back(m.mk_fresh_const("rule_id_p", m.mk_bool_sort())); + } + } + + void aig_exporter::mk_latch_vars(unsigned n) { + for (unsigned i = m_latch_vars.size(); i <= n; ++i) { + m_latch_vars.push_back(m.mk_fresh_const("latch_var", m.mk_bool_sort())); + m_latch_varsp.push_back(m.mk_fresh_const("latch_varp", m.mk_bool_sort())); + } + SASSERT(m_latch_vars.size() > n); + } + + expr* aig_exporter::get_latch_var(unsigned i, const expr_ref_vector& vars) { + mk_latch_vars(i); + return vars.get(i); + } + + void aig_exporter::assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs) { + unsigned id = 0; + if (decl && !m_decl_id_map.find(decl, id)) { + id = m_next_decl_id++; + SASSERT(id < (1U << vars.size())); + m_decl_id_map.insert(decl, id); + } + + for (unsigned i = 0; i < vars.size(); ++i) { + exprs.push_back((id & (1U << i)) ? vars[i] : m.mk_not(vars[i])); + } + } + + void aig_exporter::collect_var_substs(substitution& subst, const app *h, + const expr_ref_vector& vars, expr_ref_vector& eqs) { + for (unsigned i = 0; i < h->get_num_args(); ++i) { + expr *arg = h->get_arg(i); + expr *latchvar = get_latch_var(i, vars); + + if (is_var(arg)) { + var *v = to_var(arg); + expr_offset othervar; + if (subst.find(v, 0, othervar)) { + eqs.push_back(m.mk_eq(latchvar, othervar.get_expr())); + } else { + subst.insert(v, 0, expr_offset(latchvar, 0)); + } + } else { + eqs.push_back(m.mk_eq(latchvar, arg)); + } + } + } + + void aig_exporter::operator()(std::ostream& out) { + expr_ref_vector transition_function(m), output_preds(m); + var_ref_vector input_vars(m); + + rule_counter& vc = m_rm.get_counter(); + expr_ref_vector exprs(m); + substitution subst(m); + + for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), + E = m_rules.end_grouped_rules(); I != E; ++I) { + for (rule_vector::iterator II = I->get_value()->begin(), + EE = I->get_value()->end(); II != EE; ++II) { + rule *r = *II; + unsigned numqs = r->get_positive_tail_size(); + if (numqs > 1) { + std::cerr << "non-linear clauses not supported\n"; + exit(-1); + } + + if (numqs != r->get_uninterpreted_tail_size()) { + std::cerr << "negation of queries not supported\n"; + exit(-1); + } + + exprs.reset(); + assert_pred_id(numqs ? r->get_tail(0)->get_decl() : 0, m_ruleid_var_set, exprs); + assert_pred_id(r->get_head()->get_decl(), m_ruleid_varp_set, exprs); + + subst.reset(); + subst.reserve(1, vc.get_max_rule_var(*r)+1); + if (numqs) + collect_var_substs(subst, r->get_tail(0), m_latch_vars, exprs); + collect_var_substs(subst, r->get_head(), m_latch_varsp, exprs); + + for (unsigned i = numqs; i < r->get_tail_size(); ++i) { + expr_ref e(m); + subst.apply(r->get_tail(i), e); + exprs.push_back(e); + } + + transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + } + + // collect table facts + if (m_facts) { + for (fact_vector::const_iterator I = m_facts->begin(), E = m_facts->end(); I != E; ++I) { + exprs.reset(); + assert_pred_id(0, m_ruleid_var_set, exprs); + assert_pred_id(I->first, m_ruleid_varp_set, exprs); + + for (unsigned i = 0; i < I->second.size(); ++i) { + exprs.push_back(m.mk_eq(get_latch_var(i, m_latch_varsp), I->second[i])); + } + + transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + } + + expr *tr = m.mk_or(transition_function.size(), transition_function.c_ptr()); + aig_ref aig = m_aigm.mk_aig(tr); + expr_ref aig_expr(m); + m_aigm.to_formula(aig, aig_expr); + +#if 0 + std::cout << mk_pp(tr, m) << "\n\n"; + std::cout << mk_pp(aig_expr, m) << "\n\n"; +#endif + + // make rule_id vars latches + for (unsigned i = 0; i < m_ruleid_var_set.size(); ++i) { + m_latch_vars.push_back(m_ruleid_var_set.get(i)); + m_latch_varsp.push_back(m_ruleid_varp_set.get(i)); + } + + // create vars for latches + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + mk_var(m_latch_vars.get(i)); + mk_input_var(m_latch_varsp.get(i)); + } + + unsigned tr_id = expr_to_aig(aig_expr); + + // create latch next state variables: (ite tr varp var) + unsigned_vector latch_varp_ids; + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + unsigned in_val = mk_and(tr_id, get_var(m_latch_varsp.get(i))); + unsigned latch_val = mk_and(neg(tr_id), get_var(m_latch_vars.get(i))); + latch_varp_ids.push_back(mk_or(in_val, latch_val)); + } + m_latch_varsp.reset(); + + // create output variable (true iff an output predicate is derivable) + unsigned output_id = 0; + { + expr_ref_vector output(m); + const func_decl_set& preds = m_rules.get_output_predicates(); + + for (func_decl_set::iterator I = preds.begin(), E = preds.end(); I != E; ++I) { + exprs.reset(); + assert_pred_id(*I, m_ruleid_var_set, exprs); + output.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + + expr *out = m.mk_or(output.size(), output.c_ptr()); + aig = m_aigm.mk_aig(out); + m_aigm.to_formula(aig, aig_expr); + output_id = expr_to_aig(aig_expr); + +#if 0 + std::cout << "output formula\n"; + std::cout << mk_pp(out, m) << "\n\n"; + std::cout << mk_pp(aig_expr, m) << "\n\n"; +#endif + } + + // 1) print header + // aag var_index inputs latches outputs andgates + out << "aag " << (m_next_aig_expr_id-1)/2 << ' ' << m_input_vars.size() + << ' ' << m_latch_vars.size() << " 1 " << m_num_and_gates << '\n'; + + // 2) print inputs + for (unsigned i = 0; i < m_input_vars.size(); ++i) { + out << m_input_vars[i] << '\n'; + } + + // 3) print latches + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + out << get_var(m_latch_vars.get(i)) << ' ' << latch_varp_ids[i] << '\n'; + } + + // 4) print outputs (just one for now) + out << output_id << '\n'; + + // 5) print formula + out << m_buffer.str(); + } + + unsigned aig_exporter::expr_to_aig(const expr *e) { + unsigned id; + if (m_aig_expr_id_map.find(e, id)) + return id; + + if (is_uninterp_const(e)) + return get_var(e); + + switch (e->get_kind()) { + case AST_APP: { + const app *a = to_app(e); + switch (a->get_decl_kind()) { + case OP_OR: + SASSERT(a->get_num_args() > 0); + id = expr_to_aig(a->get_arg(0)); + for (unsigned i = 1; i < a->get_num_args(); ++i) { + id = mk_or(id, expr_to_aig(a->get_arg(i))); + } + m_aig_expr_id_map.insert(e, id); + return id; + + case OP_NOT: + return neg(expr_to_aig(a->get_arg(0))); + + case OP_FALSE: + return 0; + + case OP_TRUE: + return 1; + } + break;} + + case AST_VAR: + return get_var(e); + default: + UNREACHABLE(); + } + + UNREACHABLE(); + return 0; + } + + unsigned aig_exporter::neg(unsigned id) const { + return (id % 2) ? (id-1) : (id+1); + } + + unsigned aig_exporter::mk_and(unsigned id1, unsigned id2) { + if (id1 > id2) + std::swap(id1, id2); + + std::pair key(id1, id2); + and_gates_map::const_iterator I = m_and_gates_map.find(key); + if (I != m_and_gates_map.end()) + return I->second; + + unsigned id = mk_expr_id(); + m_buffer << id << ' ' << id1 << ' ' << id2 << '\n'; + m_and_gates_map[key] = id; + ++m_num_and_gates; + return id; + } + + unsigned aig_exporter::mk_or(unsigned id1, unsigned id2) { + return neg(mk_and(neg(id1), neg(id2))); + } + + unsigned aig_exporter::get_var(const expr *e) { + unsigned id; + if (m_aig_expr_id_map.find(e, id)) + return id; + return mk_input_var(e); + } + + unsigned aig_exporter::mk_var(const expr *e) { + SASSERT(!m_aig_expr_id_map.contains(e)); + unsigned id = mk_expr_id(); + m_aig_expr_id_map.insert(e, id); + return id; + } + + unsigned aig_exporter::mk_input_var(const expr *e) { + SASSERT(!m_aig_expr_id_map.contains(e)); + unsigned id = mk_expr_id(); + m_input_vars.push_back(id); + if (e) + m_aig_expr_id_map.insert(e, id); + return id; + } + + unsigned aig_exporter::mk_expr_id() { + unsigned id = m_next_aig_expr_id; + m_next_aig_expr_id += 2; + return id; + } +} diff --git a/src/muz_qe/aig_exporter.h b/src/muz_qe/aig_exporter.h new file mode 100755 index 000000000..20b31f01b --- /dev/null +++ b/src/muz_qe/aig_exporter.h @@ -0,0 +1,68 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + aig_exporter.h + +Abstract: + + Export AIG files from horn clauses + +--*/ + +#ifndef _AIG_EXPORTER_H_ +#define _AIG_EXPORTER_H_ + +#include "aig.h" +#include "dl_rule_set.h" +#include "rel_context.h" +#include +#include + +namespace datalog { + class aig_exporter { + public: + aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts = 0); + void operator()(std::ostream& out); + + private: + typedef obj_map decl_id_map; + typedef obj_map aig_expr_id_map; + typedef std::map, unsigned> and_gates_map; + + const rule_set& m_rules; + const fact_vector *m_facts; + ast_manager& m; + rule_manager& m_rm; + aig_manager m_aigm; + decl_id_map m_decl_id_map; + unsigned m_next_decl_id; + aig_expr_id_map m_aig_expr_id_map; + unsigned m_next_aig_expr_id; + and_gates_map m_and_gates_map; + unsigned m_num_and_gates; + + expr_ref_vector m_latch_vars, m_latch_varsp; + expr_ref_vector m_ruleid_var_set, m_ruleid_varp_set; + unsigned_vector m_input_vars; + + std::stringstream m_buffer; + + void mk_latch_vars(unsigned n); + expr* get_latch_var(unsigned i, const expr_ref_vector& vars); + void assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs); + void collect_var_substs(substitution& subst, const app *h, + const expr_ref_vector& vars, expr_ref_vector& eqs); + unsigned expr_to_aig(const expr *e); + unsigned neg(unsigned id) const; + unsigned mk_and(unsigned id1, unsigned id2); + unsigned mk_or(unsigned id1, unsigned id2); + unsigned get_var(const expr *e); + unsigned mk_var(const expr *e); + unsigned mk_input_var(const expr *e = 0); + unsigned mk_expr_id(); + }; +} + +#endif diff --git a/src/muz_qe/clp_context.cpp b/src/muz_qe/clp_context.cpp new file mode 100644 index 000000000..94a956eb9 --- /dev/null +++ b/src/muz_qe/clp_context.cpp @@ -0,0 +1,235 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + clp_context.cpp + +Abstract: + + Bounded CLP (symbolic simulation using Z3) context. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + +--*/ + +#include "clp_context.h" +#include "dl_context.h" +#include "unifier.h" +#include "var_subst.h" +#include "substitution.h" + +namespace datalog { + + class clp::imp { + struct stats { + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + unsigned m_num_unfold; + unsigned m_num_no_unfold; + unsigned m_num_subsumed; + }; + + context& m_ctx; + ast_manager& m; + rule_manager& rm; + smt_params m_fparams; + smt::kernel m_solver; + var_subst m_var_subst; + expr_ref_vector m_ground; + app_ref_vector m_goals; + volatile bool m_cancel; + stats m_stats; + public: + imp(context& ctx): + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_solver(m, m_fparams), // TBD: can be replaced by efficient BV solver. + m_var_subst(m, false), + m_ground(m), + m_goals(m), + m_cancel(false) + { + // m_fparams.m_relevancy_lvl = 0; + m_fparams.m_mbqi = false; + m_fparams.m_soft_timeout = 1000; + } + + ~imp() {} + + lbool query(expr* query) { + m_ctx.ensure_opened(); + m_solver.reset(); + m_goals.reset(); + rm.mk_query(query, m_ctx.get_rules()); + m_ctx.apply_default_transformation(); + func_decl *head_decl = m_ctx.get_rules().get_output_predicate(); + + expr_ref head(m_ctx.get_rules().get_predicate_rules(head_decl)[0]->get_head(), m); + ground(head); + m_goals.push_back(to_app(head)); + return search(20, 0); + } + + void cancel() { + m_cancel = true; + m_solver.cancel(); + } + + void cleanup() { + m_cancel = false; + m_goals.reset(); + m_solver.reset_cancel(); + } + + void reset_statistics() { + m_stats.reset(); + } + + void collect_statistics(statistics& st) const { + //st.update("tab.num_unfold", m_stats.m_num_unfold); + //st.update("tab.num_unfold_fail", m_stats.m_num_no_unfold); + //st.update("tab.num_subsumed", m_stats.m_num_subsumed); + } + + void display_certificate(std::ostream& out) const { + expr_ref ans = get_answer(); + out << mk_pp(ans, m) << "\n"; + + } + + expr_ref get_answer() const { + return expr_ref(m.mk_true(), m); + } + + private: + + void reset_ground() { + m_ground.reset(); + } + + void ground(expr_ref& e) { + ptr_vector sorts; + get_free_vars(e, sorts); + if (m_ground.size() < sorts.size()) { + m_ground.resize(sorts.size()); + } + for (unsigned i = 0; i < sorts.size(); ++i) { + if (sorts[i] && !m_ground[i].get()) { + m_ground[i] = m.mk_fresh_const("c",sorts[i]); + } + } + m_var_subst(e, m_ground.size(), m_ground.c_ptr(), e); + } + + static bool rule_sort_fn(const rule *r1, const rule *r2) { + return r1->get_uninterpreted_tail_size() < r2->get_uninterpreted_tail_size(); + } + + lbool search(unsigned depth, unsigned index) { + if (index == m_goals.size()) { + return l_true; + } + if (depth == 0) { + return l_undef; + } + IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";); + unsigned num_goals = m_goals.size(); + app* head = m_goals[index].get(); + + rule_vector rules(m_ctx.get_rules().get_predicate_rules(head->get_decl())); + std::stable_sort(rules.begin(), rules.end(), rule_sort_fn); + + lbool status = l_false; + for (unsigned i = 0; i < rules.size(); ++i) { + rule* r = rules[i]; + m_solver.push(); + reset_ground(); + expr_ref tmp(m); + tmp = r->get_head(); + IF_VERBOSE(2, verbose_stream() << index << " " << mk_pp(tmp, m) << "\n";); + ground(tmp); + for (unsigned j = 0; j < head->get_num_args(); ++j) { + expr_ref eq(m); + eq = m.mk_eq(head->get_arg(j), to_app(tmp)->get_arg(j)); + m_solver.assert_expr(eq); + } + for (unsigned j = r->get_uninterpreted_tail_size(); j < r->get_tail_size(); ++j) { + tmp = r->get_tail(j); + ground(tmp); + m_solver.assert_expr(tmp); + } + lbool is_sat = m_solver.check(); + switch (is_sat) { + case l_false: + break; + case l_true: + if (depth == 1 && (index+1 > m_goals.size() || r->get_uninterpreted_tail_size() > 0)) { + status = l_undef; + break; + } + for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { + tmp = r->get_tail(j); + ground(tmp); + m_goals.push_back(to_app(tmp)); + } + switch(search(depth-1, index+1)) { + case l_undef: + status = l_undef; + // fallthrough + case l_false: + m_goals.resize(num_goals); + break; + case l_true: + return l_true; + } + break; + case l_undef: + status = l_undef; + throw default_exception("undef"); + } + m_solver.pop(1); + } + return status; + } + + + proof_ref get_proof() const { + return proof_ref(0, m); + } + }; + + clp::clp(context& ctx): + m_imp(alloc(imp, ctx)) { + } + clp::~clp() { + dealloc(m_imp); + } + lbool clp::query(expr* query) { + return m_imp->query(query); + } + void clp::cancel() { + m_imp->cancel(); + } + void clp::cleanup() { + m_imp->cleanup(); + } + void clp::reset_statistics() { + m_imp->reset_statistics(); + } + void clp::collect_statistics(statistics& st) const { + m_imp->collect_statistics(st); + } + void clp::display_certificate(std::ostream& out) const { + m_imp->display_certificate(out); + } + expr_ref clp::get_answer() { + return m_imp->get_answer(); + } + +}; diff --git a/src/muz_qe/clp_context.h b/src/muz_qe/clp_context.h new file mode 100644 index 000000000..635891205 --- /dev/null +++ b/src/muz_qe/clp_context.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + clp_context.h + +Abstract: + + Bounded CLP (symbolic simulation using Z3) context. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + +--*/ +#ifndef _CLP_CONTEXT_H_ +#define _CLP_CONTEXT_H_ + +#include "ast.h" +#include "lbool.h" +#include "statistics.h" + +namespace datalog { + class context; + + class clp { + class imp; + imp* m_imp; + public: + clp(context& ctx); + ~clp(); + lbool query(expr* query); + void cancel(); + void cleanup(); + void reset_statistics(); + void collect_statistics(statistics& st) const; + void display_certificate(std::ostream& out) const; + expr_ref get_answer(); + }; +}; + +#endif diff --git a/src/muz_qe/dl_base.h b/src/muz_qe/dl_base.h index 6c53a4b27..200ce2d83 100644 --- a/src/muz_qe/dl_base.h +++ b/src/muz_qe/dl_base.h @@ -37,6 +37,8 @@ namespace datalog { ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm); context & get_context_from_rel_manager(const relation_manager & rm); + typedef func_decl_set decl_set; + #if DL_LEAK_HUNTING void leak_guard_check(const symbol & s); #endif @@ -329,6 +331,10 @@ namespace datalog { virtual mutator_fn * mk_filter_interpreted_fn(const base_object & t, app * condition) { return 0; } + virtual transformer_fn * mk_filter_interpreted_and_project_fn(const base_object & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) + { return 0; } + virtual transformer_fn * mk_select_equal_and_project_fn(const base_object & t, const element & value, unsigned col) { return 0; } @@ -452,8 +458,8 @@ namespace datalog { class convenient_join_fn : public join_fn { signature m_result_sig; protected: - const unsigned_vector m_cols1; - const unsigned_vector m_cols2; + unsigned_vector m_cols1; + unsigned_vector m_cols2; convenient_join_fn(const signature & o1_sig, const signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) @@ -468,8 +474,8 @@ namespace datalog { class convenient_join_project_fn : public join_fn { signature m_result_sig; protected: - const unsigned_vector m_cols1; - const unsigned_vector m_cols2; + unsigned_vector m_cols1; + unsigned_vector m_cols2; //it is non-const because it needs to be modified in sparse_table version of the join_project operator unsigned_vector m_removed_cols; @@ -496,7 +502,7 @@ namespace datalog { class convenient_project_fn : public convenient_transformer_fn { protected: - const unsigned_vector m_removed_cols; + unsigned_vector m_removed_cols; convenient_project_fn(const signature & orig_sig, unsigned col_cnt, const unsigned * removed_cols) : m_removed_cols(col_cnt, removed_cols) { @@ -654,6 +660,7 @@ namespace datalog { typedef sort * relation_sort; typedef ptr_vector relation_signature_base0; + typedef ptr_hash relation_sort_hash; typedef app * relation_element; typedef app_ref relation_element_ref; @@ -737,8 +744,8 @@ namespace datalog { struct hash { unsigned operator()(relation_signature const& s) const { - relation_sort const* sorts = s.c_ptr(); - return string_hash(reinterpret_cast(sorts), sizeof(*sorts)*s.size(), 12); } + return obj_vector_hash(s); + } }; struct eq { @@ -814,9 +821,11 @@ namespace datalog { typedef uint64 table_sort; typedef svector table_signature_base0; + typedef uint64_hash table_sort_hash; typedef uint64 table_element; typedef svector table_fact; + typedef uint64_hash table_element_hash; struct table_traits { typedef table_plugin plugin; @@ -879,8 +888,8 @@ namespace datalog { public: struct hash { unsigned operator()(table_signature const& s) const { - table_sort const* sorts = s.c_ptr(); - return string_hash(reinterpret_cast(sorts), sizeof(*sorts)*s.size(), 12); } + return svector_hash()(s); + } }; struct eq { diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz_qe/dl_bmc_engine.cpp index 959a11605..8e9fb510e 100644 --- a/src/muz_qe/dl_bmc_engine.cpp +++ b/src/muz_qe/dl_bmc_engine.cpp @@ -44,7 +44,6 @@ namespace datalog { public: qlinear(bmc& b): b(b), m(b.m), m_bv(m), m_bit_width(1) {} - lbool check() { setup(); m_bit_width = 4; @@ -298,6 +297,7 @@ namespace datalog { r->to_formula(fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); + proof_ref p(m); if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); @@ -307,7 +307,10 @@ namespace datalog { r1->to_formula(concl); scoped_proof _sp(m); - proof* p = r->get_proof(); + p = r->get_proof(); + if (!p) { + p = m.mk_asserted(fml); + } proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); @@ -320,13 +323,17 @@ namespace datalog { else { r2->to_formula(concl); scoped_proof _sp(m); - proof* p = r->get_proof(); + p = r->get_proof(); + if (!p) { + p = m.mk_asserted(fml); + } if (sub.empty()) { pr = p; } else { substs.push_back(sub); - pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); + proof* ps[1] = { p }; + pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } @@ -1005,7 +1012,6 @@ namespace datalog { symbol is_name(_name.str().c_str()); std::stringstream _name2; _name2 << "get_succ#" << i; - symbol acc_name(_name2.str().c_str()); ptr_vector accs; type_ref tr(0); accs.push_back(mk_accessor_decl(name, tr)); @@ -1213,6 +1219,15 @@ namespace datalog { r->to_formula(fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); + proof_ref p(m); + { + scoped_proof _sp(m); + p = r->get_proof(); + if (!p) { + p = m.mk_asserted(fml); + } + } + if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); @@ -1220,9 +1235,8 @@ namespace datalog { apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); r1->to_formula(concl); - scoped_proof _sp(m); - proof* p = r->get_proof(); + scoped_proof _sp(m); proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); @@ -1235,13 +1249,13 @@ namespace datalog { else { r2->to_formula(concl); scoped_proof _sp(m); - proof* p = r->get_proof(); if (sub.empty()) { pr = p; } else { substs.push_back(sub); - pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); + proof * ps[1] = { p }; + pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } @@ -1416,19 +1430,12 @@ namespace datalog { lbool bmc::query(expr* query) { m_solver.reset(); m_answer = 0; - m_ctx.ensure_opened(); m_rules.reset(); - datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); - datalog::rule_set old_rules(m_ctx.get_rules()); - datalog::rule_ref_vector query_rules(rule_manager); - datalog::rule_ref query_rule(rule_manager); - rule_manager.mk_query(query, m_query_pred, query_rules, query_rule); - m_ctx.add_rules(query_rules); - expr_ref bg_assertion = m_ctx.get_background_assertion(); - - m_ctx.set_output_predicate(m_query_pred); + datalog::rule_set old_rules(m_ctx.get_rules()); + rule_manager.mk_query(query, m_ctx.get_rules()); + expr_ref bg_assertion = m_ctx.get_background_assertion(); m_ctx.apply_default_transformation(); if (m_ctx.get_params().slice()) { @@ -1436,10 +1443,9 @@ namespace datalog { datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); - m_query_pred = slice->get_predicate(m_query_pred.get()); - m_ctx.set_output_predicate(m_query_pred); } - m_rules.add_rules(m_ctx.get_rules()); + m_query_pred = m_ctx.get_rules().get_output_predicate(); + m_rules.replace_rules(m_ctx.get_rules()); m_rules.close(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); diff --git a/src/muz_qe/dl_check_table.cpp b/src/muz_qe/dl_check_table.cpp index 5081654b5..ea4003e5f 100644 --- a/src/muz_qe/dl_check_table.cpp +++ b/src/muz_qe/dl_check_table.cpp @@ -82,6 +82,34 @@ namespace datalog { return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2); } + class check_table_plugin::join_project_fn : public table_join_fn { + scoped_ptr m_tocheck; + scoped_ptr m_checker; + public: + join_project_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + m_tocheck = p.get_manager().mk_join_project_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + } + + virtual table_base* operator()(const table_base & t1, const table_base & t2) { + table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); + table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); + check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_join_fn * check_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + } + class check_table_plugin::union_fn : public table_union_fn { scoped_ptr m_tocheck; scoped_ptr m_checker; @@ -120,7 +148,6 @@ namespace datalog { } table_base* operator()(table_base const& src) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); @@ -135,6 +162,31 @@ namespace datalog { return alloc(project_fn, *this, t, col_cnt, removed_cols); } + class check_table_plugin::select_equal_and_project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + select_equal_and_project_fn(check_table_plugin& p, const table_base & t, const table_element & value, unsigned col) { + m_checker = p.get_manager().mk_select_equal_and_project_fn(checker(t), value, col); + m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) { + if (!check_kind(t)) { + return 0; + } + return alloc(select_equal_and_project_fn, *this, t, value, col); + } + class check_table_plugin::rename_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; @@ -233,6 +285,33 @@ namespace datalog { return 0; } + class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_interpreted_and_project_fn(check_table_plugin& p, const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) + { + m_checker = p.get_manager().mk_filter_interpreted_and_project_fn(checker(t), condition, removed_col_cnt, removed_cols); + m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + if (check_kind(t)) { + return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols); + } + return 0; + } + class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; diff --git a/src/muz_qe/dl_check_table.h b/src/muz_qe/dl_check_table.h index 7126bde66..e4f439590 100644 --- a/src/muz_qe/dl_check_table.h +++ b/src/muz_qe/dl_check_table.h @@ -35,13 +35,16 @@ namespace datalog { unsigned m_count; protected: class join_fn; + class join_project_fn; class union_fn; class transformer_fn; class rename_fn; class project_fn; + class select_equal_and_project_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; + class filter_interpreted_and_project_fn; class filter_by_negation_fn; public: @@ -54,10 +57,15 @@ namespace datalog { virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta); virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols); + virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col); virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, @@ -65,6 +73,8 @@ namespace datalog { virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col); virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); + virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); virtual table_intersection_filter_fn * mk_filter_by_negation_fn( const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, @@ -89,15 +99,6 @@ namespace datalog { class check_table : public table_base { friend class check_table_plugin; - friend class check_table_plugin::join_fn; - friend class check_table_plugin::union_fn; - friend class check_table_plugin::transformer_fn; - friend class check_table_plugin::rename_fn; - friend class check_table_plugin::project_fn; - friend class check_table_plugin::filter_equal_fn; - friend class check_table_plugin::filter_identical_fn; - friend class check_table_plugin::filter_interpreted_fn; - friend class check_table_plugin::filter_by_negation_fn; table_base* m_checker; table_base* m_tocheck; diff --git a/src/muz_qe/dl_cmds.cpp b/src/muz_qe/dl_cmds.cpp index c1e3f85f9..b82225785 100644 --- a/src/muz_qe/dl_cmds.cpp +++ b/src/muz_qe/dl_cmds.cpp @@ -445,40 +445,8 @@ public: ctx.insert(var); m_dl_ctx->dlctx().register_variable(var); } - }; -class dl_push_cmd : public cmd { - ref m_ctx; -public: - dl_push_cmd(dl_context* ctx): - cmd("fixedpoint-push"), - m_ctx(ctx) - {} - - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "push context on the fixedpoint engine"; } - - virtual void execute(cmd_context& ctx) { - m_ctx->push(); - } -}; - -class dl_pop_cmd : public cmd { - ref m_ctx; -public: - dl_pop_cmd(dl_context* ctx): - cmd("fixedpoint-pop"), - m_ctx(ctx) - {} - - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "pop context on the fixedpoint engine"; } - - virtual void execute(cmd_context& ctx) { - m_ctx->pop(); - } -}; static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_cmds) { dl_context * dl_ctx = alloc(dl_context, ctx, collected_cmds); @@ -486,10 +454,6 @@ 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)); -#ifndef _EXTERNAL_RELEASE - ctx.insert(alloc(dl_push_cmd, dl_ctx)); // not exposed to keep command-extensions simple. - ctx.insert(alloc(dl_pop_cmd, dl_ctx)); -#endif } void install_dl_cmds(cmd_context & ctx) { diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index acedc3619..3f16d0dab 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -36,7 +36,7 @@ namespace datalog { } void compiler::ensure_predicate_loaded(func_decl * pred, instruction_block & acc) { - pred2idx::entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); + pred2idx::obj_map_entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); if(e->get_data().m_value!=UINT_MAX) { //predicate is already loaded return; @@ -61,17 +61,30 @@ namespace datalog { void compiler::make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { relation_signature aux_sig; - relation_signature::from_join(m_reg_signatures[t1], m_reg_signatures[t2], vars.size(), - vars.get_cols1(), vars.get_cols2(), aux_sig); + relation_signature sig1 = m_reg_signatures[t1]; + relation_signature sig2 = m_reg_signatures[t2]; + relation_signature::from_join(sig1, sig2, vars.size(), vars.get_cols1(), vars.get_cols2(), aux_sig); relation_signature res_sig; relation_signature::from_project(aux_sig, removed_cols.size(), removed_cols.c_ptr(), res_sig); - result = get_fresh_register(res_sig); + acc.push_back(instruction::mk_join_project(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); } + void compiler::make_filter_interpreted_and_project(reg_idx src, app_ref & cond, + const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { + SASSERT(!removed_cols.empty()); + relation_signature res_sig; + relation_signature::from_project(m_reg_signatures[src], removed_cols.size(), + removed_cols.c_ptr(), res_sig); + result = get_fresh_register(res_sig); + + acc.push_back(instruction::mk_filter_interpreted_and_project(src, cond, + removed_cols.size(), removed_cols.c_ptr(), result)); + } + void compiler::make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, reg_idx & result, instruction_block & acc) { relation_signature res_sig; @@ -145,7 +158,7 @@ namespace datalog { } void compiler::make_add_constant_column(func_decl* head_pred, reg_idx src, const relation_sort & s, const relation_element & val, - reg_idx & result, instruction_block & acc) { + reg_idx & result, bool & dealloc, instruction_block & acc) { reg_idx singleton_table; if(!m_constant_registers.find(s, val, singleton_table)) { singleton_table = get_single_column_register(s); @@ -154,16 +167,18 @@ namespace datalog { m_constant_registers.insert(s, val, singleton_table); } if(src==execution_context::void_register) { - make_clone(singleton_table, result, acc); + result = singleton_table; + dealloc = false; } else { variable_intersection empty_vars(m_context.get_manager()); make_join(src, singleton_table, empty_vars, result, acc); + dealloc = true; } } void compiler::make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result, - instruction_block & acc) { + bool & dealloc, instruction_block & acc) { TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); IF_VERBOSE(3, { @@ -172,25 +187,35 @@ namespace datalog { verbose_stream() << "Compiling unsafe rule column " << col_idx << "\n" << mk_ismt2_pp(e, m_context.get_manager()) << "\n"; }); - reg_idx total_table = get_single_column_register(s); - relation_signature sig; - sig.push_back(s); - acc.push_back(instruction::mk_total(sig, pred, total_table)); + reg_idx total_table; + if (!m_total_registers.find(s, pred, total_table)) { + total_table = get_single_column_register(s); + relation_signature sig; + sig.push_back(s); + m_top_level_code.push_back(instruction::mk_total(sig, pred, total_table)); + m_total_registers.insert(s, pred, total_table); + } if(src == execution_context::void_register) { result = total_table; + dealloc = false; } else { variable_intersection empty_vars(m_context.get_manager()); make_join(src, total_table, empty_vars, result, acc); - make_dealloc_non_void(total_table, acc); + dealloc = true; } } void compiler::make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc) { + SASSERT(sig.empty()); TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); + if (m_empty_tables_registers.find(pred, result)) + return; + result = get_fresh_register(sig); - acc.push_back(instruction::mk_total(sig, pred, result)); + m_top_level_code.push_back(instruction::mk_total(sig, pred, result)); + m_empty_tables_registers.insert(pred, result); } @@ -232,6 +257,7 @@ namespace datalog { reg_idx src, const svector & acis0, reg_idx & result, + bool & dealloc, instruction_block & acc) { TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); @@ -276,7 +302,9 @@ namespace datalog { if(!src_cols_to_remove.empty()) { reg_idx new_curr; make_projection(curr, src_cols_to_remove.size(), src_cols_to_remove.c_ptr(), new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; @@ -297,16 +325,19 @@ namespace datalog { unsigned bound_column_index; if(acis[i].kind!=ACK_UNBOUND_VAR || !handled_unbound.find(acis[i].var_index,bound_column_index)) { reg_idx new_curr; + bool new_dealloc; bound_column_index=curr_sig->size(); if(acis[i].kind==ACK_CONSTANT) { - make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, acc); + make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, new_dealloc, acc); } else { SASSERT(acis[i].kind==ACK_UNBOUND_VAR); - make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, acc); + make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, new_dealloc, acc); handled_unbound.insert(acis[i].var_index,bound_column_index); } - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = new_dealloc; curr=new_curr; curr_sig = & m_reg_signatures[curr]; SASSERT(bound_column_index==curr_sig->size()-1); @@ -327,7 +358,9 @@ namespace datalog { } reg_idx new_curr; make_duplicate_column(curr, col, new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; unsigned bound_column_index=curr_sig->size()-1; @@ -355,7 +388,9 @@ namespace datalog { reg_idx new_curr; make_rename(curr, permutation.size(), permutation.c_ptr(), new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; } @@ -364,6 +399,7 @@ namespace datalog { SASSERT(src==execution_context::void_register); SASSERT(acis0.size()==0); make_full_relation(head_pred, empty_signature, curr, acc); + dealloc = false; } result=curr; @@ -397,6 +433,7 @@ namespace datalog { void compiler::compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, reg_idx delta_reg, bool use_widening, instruction_block & acc) { + ast_manager & m = m_context.get_manager(); m_instruction_observer.start_rule(r); const app * h = r->get_head(); @@ -409,12 +446,15 @@ namespace datalog { SASSERT(pt_len<=2); //we require rules to be processed by the mk_simple_joins rule transformer plugin reg_idx single_res; - ptr_vector single_res_expr; + expr_ref_vector single_res_expr(m); //used to save on filter_identical instructions where the check is already done //by the join operation unsigned second_tail_arg_ofs; + // whether to dealloc the previous result + bool dealloc = true; + if(pt_len == 2) { reg_idx t1_reg=tail_regs[0]; reg_idx t2_reg=tail_regs[1]; @@ -471,8 +511,7 @@ namespace datalog { expr * arg = a->get_arg(i); if(is_app(arg)) { app * c = to_app(arg); //argument is a constant - SASSERT(c->get_num_args()==0); - SASSERT(m_context.get_decl_util().is_numeral_ext(arg)); + SASSERT(m.is_value(c)); reg_idx new_reg; make_select_equal_and_project(single_res, c, single_res_expr.size(), new_reg, acc); if(single_res!=t_reg) { @@ -487,8 +526,7 @@ namespace datalog { } } if(single_res==t_reg) { - //we may be modifying the register later, so we need a local copy - make_clone(t_reg, single_res, acc); + dealloc = false; } } @@ -499,7 +537,7 @@ namespace datalog { single_res=execution_context::void_register; } - add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, acc); + add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, dealloc, acc); int2ints var_indexes; @@ -510,11 +548,14 @@ namespace datalog { unsigned srlen=single_res_expr.size(); SASSERT((single_res==execution_context::void_register) ? (srlen==0) : (srlen==m_reg_signatures[single_res].size())); for(unsigned i=0; iget_tail_size(); // full tail + ptr_vector tail; + for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { + tail.push_back(r->get_tail(tail_index)); + } + + if (!tail.empty()) { + app_ref filter_cond(tail.size() == 1 ? to_app(tail.back()) : m.mk_and(tail.size(), tail.c_ptr()), m); + ptr_vector filter_vars; + get_free_vars(filter_cond, filter_vars); + + // create binding + expr_ref_vector binding(m); + binding.resize(filter_vars.size()+1); + + for (unsigned v = 0; v < filter_vars.size(); ++v) { + if (!filter_vars[v]) + continue; + + int2ints::entry * entry = var_indexes.find_core(v); + unsigned src_col; + if (entry) { + src_col = entry->get_data().m_value.back(); + } else { + // we have an unbound variable, so we add an unbound column for it + relation_sort unbound_sort = filter_vars[v]; + + reg_idx new_reg; + bool new_dealloc; + make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; + filtered_res = new_reg; + + src_col = single_res_expr.size(); + single_res_expr.push_back(m.mk_var(v, unbound_sort)); + + entry = var_indexes.insert_if_not_there2(v, unsigned_vector()); + entry->get_data().m_value.push_back(src_col); + } + relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; + binding[filter_vars.size()-v] = m.mk_var(src_col, var_sort); + } + + // check if there are any columns to remove + unsigned_vector remove_columns; + { + unsigned_vector var_idx_to_remove; + ptr_vector vars; + get_free_vars(r->get_head(), vars); + for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); + I != E; ++I) { + unsigned var_idx = I->m_key; + if (!vars.get(var_idx, 0)) { + unsigned_vector & cols = I->m_value; + for (unsigned i = 0; i < cols.size(); ++i) { + remove_columns.push_back(cols[i]); + } + var_idx_to_remove.push_back(var_idx); + } + } + + for (unsigned i = 0; i < var_idx_to_remove.size(); ++i) { + var_indexes.remove(var_idx_to_remove[i]); + } + + // update column idx for after projection state + if (!remove_columns.empty()) { + unsigned_vector offsets; + offsets.resize(single_res_expr.size(), 0); + + for (unsigned i = 0; i < remove_columns.size(); ++i) { + for (unsigned col = remove_columns[i]; col < offsets.size(); ++col) { + ++offsets[col]; + } + } + + for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); + I != E; ++I) { + unsigned_vector & cols = I->m_value; + for (unsigned i = 0; i < cols.size(); ++i) { + cols[i] -= offsets[cols[i]]; + } + } + } + } + + expr_ref renamed(m); + m_context.get_var_subst()(filter_cond, binding.size(), binding.c_ptr(), renamed); + app_ref app_renamed(to_app(renamed), m); + if (remove_columns.empty()) { + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); + acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); + } else { + reg_idx new_reg; + std::sort(remove_columns.begin(), remove_columns.end()); + make_filter_interpreted_and_project(filtered_res, app_renamed, remove_columns, new_reg, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + filtered_res = new_reg; + } + dealloc = true; + } + +#if 0 + // this version is potentially better for non-symbolic tables, + // since it constraints each unbound column at a time (reducing the + // size of intermediate results). unsigned ft_len=r->get_tail_size(); //full tail for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); - var_idx_set t_vars; - ast_manager & m = m_context.get_manager(); - collect_vars(m, t, t_vars); - + ptr_vector t_vars; + ::get_free_vars(t, t_vars); + if(t_vars.empty()) { expr_ref simplified(m); m_context.get_rewriter()(t, simplified); @@ -601,46 +760,32 @@ namespace datalog { } //determine binding size - unsigned max_var=0; - var_idx_set::iterator vit = t_vars.begin(); - var_idx_set::iterator vend = t_vars.end(); - for(; vit!=vend; ++vit) { - unsigned v = *vit; - if(v>max_var) { max_var = v; } + while (!t_vars.back()) { + t_vars.pop_back(); } + unsigned max_var = t_vars.size(); //create binding expr_ref_vector binding(m); binding.resize(max_var+1); - vit = t_vars.begin(); - for(; vit!=vend; ++vit) { - unsigned v = *vit; + + for(unsigned v = 0; v < t_vars.size(); ++v) { + if (!t_vars[v]) { + continue; + } int2ints::entry * e = var_indexes.find_core(v); if(!e) { //we have an unbound variable, so we add an unbound column for it - relation_sort unbound_sort = 0; - - for(unsigned hindex = 0; hindexget_arg(hindex); - if(!is_var(harg) || to_var(harg)->get_idx()!=v) { - continue; - } - unbound_sort = to_var(harg)->get_sort(); - } - if(!unbound_sort) { - // the variable in the interpreted tail is neither bound in the - // uninterpreted tail nor present in the head - std::stringstream sstm; - sstm << "rule with unbound variable #" << v << " in interpreted tail: "; - r->display(m_context, sstm); - throw default_exception(sstm.str()); - } + relation_sort unbound_sort = t_vars[v]; reg_idx new_reg; TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); - make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, acc); + bool new_dealloc; + make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); - make_dealloc_non_void(filtered_res, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; filtered_res = new_reg; // here filtered_res value gets changed !! unsigned unbound_column_index = single_res_expr.size(); @@ -658,8 +803,12 @@ namespace datalog { expr_ref renamed(m); m_context.get_var_subst()(t, binding.size(), binding.c_ptr(), renamed); app_ref app_renamed(to_app(renamed), m); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); + dealloc = true; } +#endif { //put together the columns of head relation @@ -703,19 +852,20 @@ namespace datalog { SASSERT(head_acis.size()==head_len); reg_idx new_head_reg; - make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, acc); + make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, dealloc, acc); //update the head relation make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); - make_dealloc_non_void(new_head_reg, acc); + if (dealloc) + make_dealloc_non_void(new_head_reg, acc); } - finish: +// finish: m_instruction_observer.finish_rule(); } - void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, ptr_vector& single_res_expr, - instruction_block & acc) { + void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, + bool & dealloc, instruction_block & acc) { uint_set pos_vars; u_map neg_vars; ast_manager& m = m_context.get_manager(); @@ -737,7 +887,7 @@ namespace datalog { } // populate positive variables: for (unsigned i = 0; i < single_res_expr.size(); ++i) { - expr* e = single_res_expr[i]; + expr* e = single_res_expr[i].get(); if (is_var(e)) { pos_vars.insert(to_var(e)->get_idx()); } @@ -749,7 +899,13 @@ namespace datalog { expr* e = it->m_value; if (!pos_vars.contains(v)) { single_res_expr.push_back(e); - make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), single_res, acc); + reg_idx new_single_res; + bool new_dealloc; + make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), new_single_res, new_dealloc, acc); + if (dealloc) + make_dealloc_non_void(single_res, acc); + dealloc = new_dealloc; + single_res = new_single_res; TRACE("dl", tout << "Adding unbound column: " << mk_pp(e, m) << "\n";); } } @@ -761,7 +917,7 @@ namespace datalog { typedef svector tail_delta_infos; unsigned rule_len = r->get_uninterpreted_tail_size(); - reg_idx head_reg = m_pred_regs.find(r->get_head()->get_decl()); + reg_idx head_reg = m_pred_regs.find(r->get_decl()); svector tail_regs; tail_delta_infos tail_deltas; @@ -804,13 +960,13 @@ namespace datalog { ast_mark m_visited; void traverse(T v) { - SASSERT(!m_stack_content.is_marked(v)); - if(m_visited.is_marked(v) || m_removed.contains(v)) { + SASSERT(!m_stack_content.is_marked(v)); + if(m_visited.is_marked(v) || m_removed.contains(v)) { return; } m_stack.push_back(v); - m_stack_content.mark(v, true); + m_stack_content.mark(v, true); m_visited.mark(v, true); const item_set & deps = m_deps.get_deps(v); @@ -818,7 +974,7 @@ namespace datalog { item_set::iterator end = deps.end(); for(; it!=end; ++it) { T d = *it; - if(m_stack_content.is_marked(d)) { + if(m_stack_content.is_marked(d)) { //TODO: find the best vertex to remove in the cycle m_removed.insert(v); break; @@ -828,8 +984,9 @@ namespace datalog { SASSERT(m_stack.back()==v); m_stack.pop_back(); - m_stack_content.mark(v, false); + m_stack_content.mark(v, false); } + public: cycle_breaker(rule_dependencies & deps, item_set & removed) : m_deps(deps), m_removed(removed) { SASSERT(removed.empty()); } @@ -885,13 +1042,47 @@ namespace datalog { rule_vector::const_iterator rend = pred_rules.end(); for(; rit!=rend; ++rit) { rule * r = *rit; - SASSERT(head_pred==r->get_head()->get_decl()); + SASSERT(head_pred==r->get_decl()); compile_rule_evaluation(r, input_deltas, d_head_reg, widen_predicate_in_loop, acc); } } } + void compiler::compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, + const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { + func_decl_vector::const_iterator hpit = head_preds.begin(); + func_decl_vector::const_iterator hpend = head_preds.end(); + reg_idx void_reg = execution_context::void_register; + for(; hpit!=hpend; ++hpit) { + func_decl * head_pred = *hpit; + const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); + rule_vector::const_iterator rit = pred_rules.begin(); + rule_vector::const_iterator rend = pred_rules.end(); + unsigned stratum = m_rule_set.get_predicate_strat(head_pred); + + for(; rit != rend; ++rit) { + rule * r = *rit; + SASSERT(head_pred==r->get_decl()); + + for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { + unsigned stratum1 = m_rule_set.get_predicate_strat(r->get_decl(i)); + if (stratum1 >= stratum) { + goto next_loop; + } + } + compile_rule_evaluation(r, input_deltas, void_reg, false, acc); + next_loop: + ; + } + + reg_idx d_head_reg; + if (output_deltas.find(head_pred, d_head_reg)) { + acc.push_back(instruction::mk_clone(m_pred_regs.find(head_pred), d_head_reg)); + } + } + } + void compiler::make_inloop_delta_transition(const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc) { //move global head deltas into tail ones @@ -942,7 +1133,7 @@ namespace datalog { const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc) { - if(!output_deltas.empty()) { + if (!output_deltas.empty()) { func_decl_set::iterator hpit = head_preds.begin(); func_decl_set::iterator hpend = head_preds.end(); for(; hpit!=hpend; ++hpit) { @@ -956,12 +1147,17 @@ namespace datalog { } func_decl_vector preds_vector; - func_decl_set global_deltas; + func_decl_set global_deltas_dummy; - detect_chains(head_preds, preds_vector, global_deltas); + detect_chains(head_preds, preds_vector, global_deltas_dummy); + /* + FIXME: right now we use all preds as global deltas for correctness purposes func_decl_set local_deltas(head_preds); set_difference(local_deltas, global_deltas); + */ + func_decl_set local_deltas; + func_decl_set global_deltas(head_preds); pred2idx d_global_src; //these deltas serve as sources of tuples for rule evaluation inside the loop get_fresh_registers(global_deltas, d_global_src); @@ -979,7 +1175,8 @@ namespace datalog { func_decl_set empty_func_decl_set; //generate code for the initial run - compile_preds(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); + // compile_preds(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); + compile_preds_init(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); if (compile_with_widening()) { compile_loop(preds_vector, global_deltas, d_global_tgt, d_global_src, d_local, acc); @@ -1040,7 +1237,7 @@ namespace datalog { rule_vector::const_iterator end = rules.end(); for (; it != end; ++it) { rule * r = *it; - SASSERT(r->get_head()->get_decl()==head_pred); + SASSERT(r->get_decl()==head_pred); compile_rule_evaluation(r, input_deltas, output_delta, false, acc); } @@ -1113,7 +1310,7 @@ namespace datalog { //load predicate data for(unsigned i=0;iget_head()->get_decl(), acc); + ensure_predicate_loaded(r->get_decl(), acc); unsigned rule_len = r->get_uninterpreted_tail_size(); for(unsigned j=0;j int_set; typedef u_map int2int; typedef u_map int2ints; - typedef map,ptr_eq > pred2idx; + typedef obj_map pred2idx; typedef unsigned_vector var_vector; typedef ptr_vector func_decl_vector; @@ -114,6 +114,8 @@ namespace datalog { reg_idx m_new_reg; vector m_reg_signatures; obj_pair_map m_constant_registers; + obj_pair_map m_total_registers; + obj_map m_empty_tables_registers; instruction_observer m_instruction_observer; /** @@ -143,6 +145,8 @@ namespace datalog { instruction_block & acc); void make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc); + void make_filter_interpreted_and_project(reg_idx src, app_ref & cond, + const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc); void make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, reg_idx & result, instruction_block & acc); /** @@ -163,20 +167,20 @@ namespace datalog { with empty signature. */ void make_assembling_code(rule* compiled_rule, func_decl* head_pred, reg_idx src, const svector & acis0, - reg_idx & result, instruction_block & acc); + reg_idx & result, bool & dealloc, instruction_block & acc); void make_dealloc_non_void(reg_idx r, instruction_block & acc); void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort & s, const relation_element & val, - reg_idx & result, instruction_block & acc); + reg_idx & result, bool & dealloc, instruction_block & acc); void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result, - instruction_block & acc); + bool & dealloc, instruction_block & acc); void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc); - void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, ptr_vector& single_res_expr, - instruction_block& acc); + void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, + bool & dealloc, instruction_block& acc); void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, instruction_block & acc); @@ -209,6 +213,12 @@ namespace datalog { void compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc); + /** + \brief Generate code to evaluate predicates in a stratum based on their non-recursive rules. + */ + void compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, + const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc); + void make_inloop_delta_transition(const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc); void compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds, diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 7a1e3b8fc..f8e572ab1 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -41,7 +41,6 @@ Revision History: #include"for_each_expr.h" #include"ast_smt_pp.h" #include"ast_smt2_pp.h" -#include"expr_functors.h" #include"dl_mk_partial_equiv.h" #include"dl_mk_bit_blast.h" #include"dl_mk_array_blast.h" @@ -49,7 +48,6 @@ Revision History: #include"dl_mk_quantifier_abstraction.h" #include"dl_mk_quantifier_instantiation.h" #include"datatype_decl_plugin.h" -#include"expr_abstract.h" namespace datalog { @@ -226,12 +224,17 @@ namespace datalog { m_rewriter(m), m_var_subst(m), m_rule_manager(*this), + m_elim_unused_vars(m), + m_abstractor(m), + m_contains_p(*this), + m_check_pred(m_contains_p, m), m_transf(*this), m_trail(*this), m_pinned(m), m_vars(m), m_rule_set(*this), m_transformed_rule_set(*this), + m_rule_fmls_head(0), m_rule_fmls(m), m_background(m), m_mc(0), @@ -241,8 +244,6 @@ namespace datalog { m_last_answer(m), m_engine(LAST_ENGINE), m_cancel(false) { - - //register plugins for builtin tables } context::~context() { @@ -252,6 +253,9 @@ namespace datalog { void context::reset() { m_trail.reset(); m_rule_set.reset(); + m_rule_fmls_head = 0; + m_rule_fmls.reset(); + m_rule_names.reset(); m_argument_var_names.reset(); m_preds.reset(); m_preds_by_name.reset(); @@ -270,15 +274,11 @@ namespace datalog { } context::sort_domain & context::get_sort_domain(relation_sort s) { - sort_domain * dom; - TRUSTME( m_sorts.find(s, dom) ); - return *dom; + return *m_sorts.find(s); } const context::sort_domain & context::get_sort_domain(relation_sort s) const { - sort_domain * dom; - TRUSTME( m_sorts.find(s, dom) ); - return *dom; + return *m_sorts.find(s); } void context::register_finite_sort(sort * s, sort_kind k) { @@ -298,10 +298,6 @@ namespace datalog { m_sorts.insert(s, dom); } - bool context::is_predicate(func_decl * pred) const { - return m_preds.contains(pred); - } - void context::register_variable(func_decl* var) { m_vars.push_back(m.mk_const(var)); } @@ -309,18 +305,19 @@ namespace datalog { expr_ref context::bind_variables(expr* fml, bool is_forall) { expr_ref result(m); app_ref_vector const & vars = m_vars; + rule_manager& rm = get_rule_manager(); if (vars.empty()) { result = fml; } else { - ptr_vector sorts; - expr_abstract(m, 0, vars.size(), reinterpret_cast(vars.c_ptr()), fml, result); - get_free_vars(result, sorts); + m_names.reset(); + m_abstractor(0, vars.size(), reinterpret_cast(vars.c_ptr()), fml, result); + rm.collect_vars(result); + ptr_vector& sorts = rm.get_var_sorts(); if (sorts.empty()) { result = fml; } else { - svector names; for (unsigned i = 0; i < sorts.size(); ++i) { if (!sorts[i]) { if (i < vars.size()) { @@ -331,29 +328,36 @@ namespace datalog { } } if (i < vars.size()) { - names.push_back(vars[i]->get_decl()->get_name()); + m_names.push_back(vars[i]->get_decl()->get_name()); } else { - names.push_back(symbol(i)); + m_names.push_back(symbol(i)); } } quantifier_ref q(m); sorts.reverse(); - q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), names.c_ptr(), result); - elim_unused_vars(m, q, result); + q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), m_names.c_ptr(), result); + m_elim_unused_vars(q, result); } } return result; } void context::register_predicate(func_decl * decl, bool named) { - if (m_preds.contains(decl)) { - return; + if (!is_predicate(decl)) { + m_pinned.push_back(decl); + m_preds.insert(decl); + if (named) { + m_preds_by_name.insert(decl->get_name(), decl); + } } - m_pinned.push_back(decl); - m_preds.insert(decl); - if (named) { - m_preds_by_name.insert(decl->get_name(), decl); + } + + void context::restrict_predicates(func_decl_set const& preds) { + m_preds.reset(); + func_decl_set::iterator it = preds.begin(), end = preds.end(); + for (; it != end; ++it) { + m_preds.insert(*it); } } @@ -447,21 +451,6 @@ namespace datalog { return new_pred; } - void context::set_output_predicate(func_decl * pred) { - ensure_rel(); - m_rel->set_output_predicate(pred); - } - - bool context::is_output_predicate(func_decl * pred) { - ensure_rel(); - return m_rel->is_output_predicate(pred); - } - - const decl_set & context::get_output_predicates() { - ensure_rel(); - return m_rel->get_output_predicates(); - } - void context::add_rule(expr* rl, symbol const& name) { m_rule_fmls.push_back(rl); m_rule_names.push_back(name); @@ -469,16 +458,19 @@ namespace datalog { void context::flush_add_rules() { datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rules(rm); scoped_proof_mode _scp(m, generate_proof_trace()?PGM_FINE:PGM_DISABLED); - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { - expr* fml = m_rule_fmls[i].get(); + while (m_rule_fmls_head < m_rule_fmls.size()) { + expr* fml = m_rule_fmls[m_rule_fmls_head].get(); proof* p = generate_proof_trace()?m.mk_asserted(fml):0; - rm.mk_rule(fml, p, rules, m_rule_names[i]); + rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]); + ++m_rule_fmls_head; + } + rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); + rule_ref r(m_rule_manager); + for (; it != end; ++it) { + r = *it; + check_rule(r); } - add_rules(rules); - m_rule_fmls.reset(); - m_rule_names.reset(); } // @@ -487,25 +479,28 @@ namespace datalog { // void context::update_rule(expr* rl, symbol const& name) { datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rules(rm); proof* p = 0; if (generate_proof_trace()) { p = m.mk_asserted(rl); } - rm.mk_rule(rl, p, rules, name); - if (rules.size() != 1) { + unsigned size_before = m_rule_set.get_num_rules(); + rm.mk_rule(rl, p, m_rule_set, name); + unsigned size_after = m_rule_set.get_num_rules(); + if (size_before + 1 != size_after) { std::stringstream strm; strm << "Rule " << name << " has a non-trivial body. It cannot be modified"; throw default_exception(strm.str()); } - rule_ref r(rules[0].get(), rm); + // The new rule is inserted last: + rule_ref r(m_rule_set.get_rule(size_before), rm); rule_ref_vector const& rls = m_rule_set.get_rules(); rule* old_rule = 0; - for (unsigned i = 0; i < rls.size(); ++i) { + for (unsigned i = 0; i < size_before; ++i) { if (rls[i]->name() == name) { 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]; @@ -518,11 +513,11 @@ namespace datalog { old_rule->display(*this, strm); strm << "does not subsume new rule "; r->display(*this, strm); + m_rule_set.del_rule(r); throw default_exception(strm.str()); } m_rule_set.del_rule(old_rule); } - m_rule_set.add_rule(r); } bool context::check_subsumes(rule const& stronger_rule, rule const& weaker_rule) { @@ -559,6 +554,8 @@ namespace datalog { throw default_exception("get_num_levels is not supported for bmc"); case TAB_ENGINE: throw default_exception("get_num_levels is not supported for tab"); + case CLP_ENGINE: + throw default_exception("get_num_levels is not supported for clp"); default: throw default_exception("unknown engine"); } @@ -577,6 +574,8 @@ namespace datalog { throw default_exception("operation is not supported for BMC engine"); case TAB_ENGINE: throw default_exception("operation is not supported for TAB engine"); + case CLP_ENGINE: + throw default_exception("operation is not supported for CLP engine"); default: throw default_exception("unknown engine"); } @@ -596,6 +595,8 @@ namespace datalog { throw default_exception("operation is not supported for BMC engine"); case TAB_ENGINE: throw default_exception("operation is not supported for TAB engine"); + case CLP_ENGINE: + throw default_exception("operation is not supported for CLP engine"); default: throw default_exception("unknown engine"); } @@ -622,28 +623,16 @@ namespace datalog { } } - class context::contains_pred : public i_expr_pred { - rule_manager const& m; - public: - contains_pred(rule_manager const& m): m(m) {} - virtual ~contains_pred() {} - - virtual bool operator()(expr* e) { - return is_app(e) && m.is_predicate(to_app(e)); - } - }; void context::check_existential_tail(rule_ref& r) { unsigned ut_size = r->get_uninterpreted_tail_size(); unsigned t_size = r->get_tail_size(); - contains_pred contains_p(get_rule_manager()); - check_pred check_pred(contains_p, get_manager()); TRACE("dl", r->display_smt2(get_manager(), tout); tout << "\n";); for (unsigned i = ut_size; i < t_size; ++i) { app* t = r->get_tail(i); TRACE("dl", tout << "checking: " << mk_ismt2_pp(t, get_manager()) << "\n";); - if (check_pred(t)) { + if (m_check_pred(t)) { std::ostringstream out; out << "interpreted body " << mk_ismt2_pp(t, get_manager()) << " contains recursive predicate"; throw default_exception(out.str()); @@ -662,8 +651,7 @@ namespace datalog { } } ast_manager& m = get_manager(); - datalog::rule_manager& rm = get_rule_manager(); - contains_pred contains_p(rm); + contains_pred contains_p(*this); check_pred check_pred(contains_p, get_manager()); for (unsigned i = ut_size; i < t_size; ++i) { @@ -676,7 +664,7 @@ namespace datalog { continue; } visited.mark(e, true); - if (rm.is_predicate(e)) { + if (is_predicate(e)) { } else if (m.is_and(e) || m.is_or(e)) { todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); @@ -736,6 +724,10 @@ namespace datalog { check_existential_tail(r); check_positive_predicates(r); break; + case CLP_ENGINE: + check_existential_tail(r); + check_positive_predicates(r); + break; default: UNREACHABLE(); break; @@ -749,14 +741,6 @@ namespace datalog { m_rule_set.add_rule(r); } - void context::add_rules(rule_ref_vector& rules) { - for (unsigned i = 0; i < rules.size(); ++i) { - rule_ref rule(rules[i].get(), rules.get_manager()); - add_rule(rule); - } - } - - void context::add_fact(func_decl * pred, const relation_fact & fact) { if (get_engine() == DATALOG_ENGINE) { ensure_rel(); @@ -771,10 +755,9 @@ namespace datalog { void context::add_fact(app * head) { SASSERT(is_fact(head)); - relation_fact fact(get_manager()); - unsigned n=head->get_num_args(); - for (unsigned i=0; iget_num_args(); + for (unsigned i = 0; i < n; i++) { fact.push_back(to_app(head->get_arg(i))); } add_fact(head->get_decl(), fact); @@ -838,21 +821,32 @@ namespace datalog { void context::transform_rules() { m_transf.reset(); - m_transf.register_plugin(alloc(mk_filter_rules,*this)); - m_transf.register_plugin(alloc(mk_simple_joins,*this)); - + m_transf.register_plugin(alloc(mk_coi_filter, *this)); + m_transf.register_plugin(alloc(mk_filter_rules, *this)); + m_transf.register_plugin(alloc(mk_simple_joins, *this)); if (unbound_compressor()) { - m_transf.register_plugin(alloc(mk_unbound_compressor,*this)); + m_transf.register_plugin(alloc(mk_unbound_compressor, *this)); } - if (similarity_compressor()) { - m_transf.register_plugin(alloc(mk_similarity_compressor, *this, - similarity_compressor_threshold())); + m_transf.register_plugin(alloc(mk_similarity_compressor, *this)); + } + m_transf.register_plugin(alloc(mk_partial_equivalence_transformer, *this)); + m_transf.register_plugin(alloc(mk_rule_inliner, *this)); + m_transf.register_plugin(alloc(mk_interp_tail_simplifier, *this)); + + if (get_params().bit_blast()) { + m_transf.register_plugin(alloc(mk_bit_blast, *this, 22000)); + m_transf.register_plugin(alloc(mk_interp_tail_simplifier, *this, 21000)); } - m_transf.register_plugin(alloc(datalog::mk_partial_equivalence_transformer, *this)); transform_rules(m_transf); } + + void context::transform_rules(rule_transformer::plugin* plugin) { + rule_transformer transformer(*this); + 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 @@ -866,15 +860,16 @@ namespace datalog { } } - void context::replace_rules(rule_set & rs) { + void context::replace_rules(rule_set const & rs) { SASSERT(!m_closed); - m_rule_set.reset(); - m_rule_set.add_rules(rs); + m_rule_set.replace_rules(rs); + if (m_rel) { + m_rel->restrict_predicates(get_predicates()); + } } void context::record_transformed_rules() { - m_transformed_rule_set.reset(); - m_transformed_rule_set.add_rules(m_rule_set); + m_transformed_rule_set.replace_rules(m_rule_set); } void context::apply_default_transformation() { @@ -925,16 +920,6 @@ namespace datalog { if (m_pdr.get()) m_pdr->updt_params(); } - void context::collect_predicates(decl_set& res) { - ensure_rel(); - m_rel->collect_predicates(res); - } - - void context::restrict_predicates(decl_set const& res) { - ensure_rel(); - m_rel->restrict_predicates(res); - } - expr_ref context::get_background_assertion() { expr_ref result(m); switch (m_background.size()) { @@ -980,18 +965,21 @@ namespace datalog { engine_type_proc(ast_manager& m): m(m), a(m), dt(m), m_engine(DATALOG_ENGINE) {} DL_ENGINE get_engine() const { return m_engine; } + void operator()(expr* e) { if (is_quantifier(e)) { m_engine = QPDR_ENGINE; } - else if (a.is_int_real(e) && m_engine != QPDR_ENGINE) { - m_engine = PDR_ENGINE; - } - else if (is_var(e) && m.is_bool(e)) { - m_engine = PDR_ENGINE; - } - else if (dt.is_datatype(m.get_sort(e))) { - m_engine = PDR_ENGINE; + else if (m_engine != QPDR_ENGINE) { + if (a.is_int_real(e)) { + m_engine = PDR_ENGINE; + } + else if (is_var(e) && m.is_bool(e)) { + m_engine = PDR_ENGINE; + } + else if (dt.is_datatype(m.get_sort(e))) { + m_engine = PDR_ENGINE; + } } } }; @@ -1017,11 +1005,14 @@ namespace datalog { else if (e == symbol("tab")) { m_engine = TAB_ENGINE; } + else if (e == symbol("clp")) { + m_engine = CLP_ENGINE; + } if (m_engine == LAST_ENGINE) { expr_fast_mark1 mark; engine_type_proc proc(m); - m_engine = DATALOG_ENGINE; + m_engine = DATALOG_ENGINE; for (unsigned i = 0; m_engine == 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()); @@ -1030,42 +1021,45 @@ namespace datalog { } m_engine = proc.get_engine(); } + for (unsigned i = m_rule_fmls_head; m_engine == DATALOG_ENGINE && i < m_rule_fmls.size(); ++i) { + expr* fml = m_rule_fmls[i].get(); + while (is_quantifier(fml)) { + fml = to_quantifier(fml)->get_expr(); + } + quick_for_each_expr(proc, mark, fml); + m_engine = proc.get_engine(); + } } } lbool context::query(expr* query) { - new_query(); - rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); - rule_ref r(m_rule_manager); - for (; it != end; ++it) { - r = *it; - check_rule(r); - } - switch(get_engine()) { + m_mc = mk_skip_model_converter(); + m_last_status = OK; + m_last_answer = 0; + switch (get_engine()) { case DATALOG_ENGINE: + flush_add_rules(); return rel_query(query); case PDR_ENGINE: case QPDR_ENGINE: + flush_add_rules(); return pdr_query(query); case BMC_ENGINE: case QBMC_ENGINE: + flush_add_rules(); return bmc_query(query); case TAB_ENGINE: + flush_add_rules(); return tab_query(query); + case CLP_ENGINE: + flush_add_rules(); + return clp_query(query); default: UNREACHABLE(); return rel_query(query); } } - void context::new_query() { - m_mc = mk_skip_model_converter(); - - flush_add_rules(); - m_last_status = OK; - m_last_answer = 0; - } - model_ref context::get_model() { switch(get_engine()) { case PDR_ENGINE: @@ -1116,11 +1110,22 @@ namespace datalog { } } + void context::ensure_clp() { + if (!m_clp.get()) { + m_clp = alloc(clp, *this); + } + } + lbool context::tab_query(expr* query) { ensure_tab(); return m_tab->query(query); } + lbool context::clp_query(expr* query) { + ensure_clp(); + return m_clp->query(query); + } + void context::ensure_rel() { if (!m_rel.get()) { m_rel = alloc(rel_context, *this); @@ -1161,6 +1166,10 @@ namespace datalog { ensure_tab(); m_last_answer = m_tab->get_answer(); return m_last_answer.get(); + case CLP_ENGINE: + ensure_clp(); + m_last_answer = m_clp->get_answer(); + return m_last_answer.get(); default: UNREACHABLE(); } @@ -1186,6 +1195,10 @@ namespace datalog { ensure_tab(); m_tab->display_certificate(out); return true; + case CLP_ENGINE: + ensure_clp(); + m_clp->display_certificate(out); + return true; default: return false; } @@ -1273,14 +1286,13 @@ namespace datalog { void context::get_rules_as_formulas(expr_ref_vector& rules, svector& names) { expr_ref fml(m); datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rule_refs(rm); // ensure that rules are all using bound variables. - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { + for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { ptr_vector sorts; get_free_vars(m_rule_fmls[i].get(), sorts); if (!sorts.empty()) { - rm.mk_rule(m_rule_fmls[i].get(), 0, rule_refs, m_rule_names[i]); + rm.mk_rule(m_rule_fmls[i].get(), 0, m_rule_set, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); m_rule_fmls.pop_back(); @@ -1288,14 +1300,13 @@ namespace datalog { --i; } } - add_rules(rule_refs); rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); for (; it != end; ++it) { (*it)->to_formula(fml); rules.push_back(fml); names.push_back((*it)->name()); } - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { + 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]); } diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index d23cf2c0c..e9abf7f23 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -45,6 +45,9 @@ Revision History: #include"model2expr.h" #include"smt_params.h" #include"dl_rule_transformer.h" +#include"expr_abstract.h" +#include"expr_functors.h" +#include"clp_context.h" namespace datalog { @@ -76,6 +79,18 @@ namespace datalog { typedef obj_map > pred2syms; typedef obj_map sort_domain_map; + class contains_pred : public i_expr_pred { + context const& ctx; + public: + contains_pred(context& ctx): ctx(ctx) {} + virtual ~contains_pred() {} + + virtual bool operator()(expr* e) { + return ctx.is_predicate(e); + } + }; + + ast_manager & m; smt_params & m_fparams; params_ref m_params_ref; @@ -84,16 +99,22 @@ namespace datalog { th_rewriter m_rewriter; var_subst m_var_subst; rule_manager m_rule_manager; + unused_vars_eliminator m_elim_unused_vars; + expr_abstractor m_abstractor; + contains_pred m_contains_p; + check_pred m_check_pred; rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; app_ref_vector m_vars; + svector m_names; sort_domain_map m_sorts; func_decl_set m_preds; sym2decl m_preds_by_name; pred2syms m_argument_var_names; rule_set m_rule_set; rule_set m_transformed_rule_set; + unsigned m_rule_fmls_head; expr_ref_vector m_rule_fmls; svector m_rule_names; expr_ref_vector m_background; @@ -104,6 +125,7 @@ namespace datalog { scoped_ptr m_bmc; scoped_ptr m_rel; scoped_ptr m_tab; + scoped_ptr m_clp; bool m_closed; bool m_saturation_was_run; @@ -185,7 +207,17 @@ namespace datalog { */ void register_predicate(func_decl * pred, bool named); - bool is_predicate(func_decl * pred) const; + /** + Restrict reltaions to set of predicates. + */ + void restrict_predicates(func_decl_set const& preds); + + /** + \brief Retrieve predicates + */ + func_decl_set const& get_predicates() const { return m_preds; } + bool is_predicate(func_decl* pred) const { return m_preds.contains(pred); } + bool is_predicate(expr * e) const { return is_app(e) && is_predicate(to_app(e)->get_decl()); } /** \brief If a predicate name has a \c func_decl object assigned, return pointer to it; @@ -238,11 +270,9 @@ namespace datalog { void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); - void set_output_predicate(func_decl * pred); - bool is_output_predicate(func_decl * pred); - const decl_set & get_output_predicates(); + void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); } - rule_set const & get_rules() { flush_add_rules(); return m_rule_set; } + rule_set & get_rules() { flush_add_rules(); return m_rule_set; } void get_rules_as_formulas(expr_ref_vector& fmls, svector& names); @@ -252,7 +282,6 @@ namespace datalog { bool has_facts(func_decl * pred) const; void add_rule(rule_ref& r); - void add_rules(rule_ref_vector& rs); void assert_expr(expr* e); expr_ref get_background_assertion(); @@ -330,7 +359,8 @@ namespace datalog { void transform_rules(); void transform_rules(rule_transformer& transf); - void replace_rules(rule_set & rs); + void transform_rules(rule_transformer::plugin* plugin); + void replace_rules(rule_set const& rs); void record_transformed_rules(); void apply_default_transformation(); @@ -339,14 +369,6 @@ namespace datalog { void updt_params(params_ref const& p); - void collect_predicates(decl_set & res); - /** - \brief Restrict the set of used predicates to \c res. - - The function deallocates unsused relations, it does not deal with rules. - */ - void restrict_predicates(const decl_set & res); - void display_rules(std::ostream & out) const { m_rule_set.display(out); } @@ -457,9 +479,9 @@ namespace datalog { void ensure_tab(); - void ensure_rel(); + void ensure_clp(); - void new_query(); + void ensure_rel(); lbool rel_query(expr* query); @@ -469,6 +491,8 @@ namespace datalog { lbool tab_query(expr* query); + lbool clp_query(expr* query); + void check_quantifier_free(rule_ref& r); void check_uninterpreted_free(rule_ref& r); void check_existential_tail(rule_ref& r); diff --git a/src/muz_qe/dl_finite_product_relation.cpp b/src/muz_qe/dl_finite_product_relation.cpp index 3070475ac..86fef433b 100644 --- a/src/muz_qe/dl_finite_product_relation.cpp +++ b/src/muz_qe/dl_finite_product_relation.cpp @@ -129,7 +129,7 @@ namespace datalog { for(unsigned i=0; i()(o.m_table_cols); } }; }; diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index 401a42e98..6b16968c2 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -359,21 +359,7 @@ namespace datalog { r2.get_signature().output(ctx.get_rel_context().get_manager(), tout); tout<<":"<\n";); - try { - ctx.set_reg(m_res, (*fn)(r1, r2)); - } - catch(...) - { - std::string annotation; - unsigned sz; - ctx.get_register_annotation(m_rel1, annotation); - sz = ctx.reg(m_rel1)?ctx.reg(m_rel1)->get_size_estimate_rows():0; - std::cout << m_rel1 << "\t" << sz << "\t" << annotation << "\n"; - ctx.get_register_annotation(m_rel2, annotation); - sz = ctx.reg(m_rel2)?ctx.reg(m_rel2)->get_size_estimate_rows():0; - std::cout << m_rel2 << "\t" << sz << "\t" << annotation << "\n"; - throw; - } + ctx.set_reg(m_res, (*fn)(r1, r2)); TRACE("dl", ctx.reg(m_res)->get_signature().output(ctx.get_rel_context().get_manager(), tout); @@ -452,7 +438,7 @@ namespace datalog { class instr_filter_identical : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; reg_idx m_reg; column_vector m_cols; public: @@ -540,6 +526,64 @@ namespace datalog { return alloc(instr_filter_interpreted, reg, condition); } + class instr_filter_interpreted_and_project : public instruction { + reg_idx m_src; + reg_idx m_res; + app_ref m_cond; + unsigned_vector m_cols; + public: + instr_filter_interpreted_and_project(reg_idx src, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result) + : m_src(src), m_cond(condition), m_cols(col_cnt, removed_cols), + m_res(result) {} + + virtual bool perform(execution_context & ctx) { + if (!ctx.reg(m_src)) { + ctx.make_empty(m_res); + return true; + } + + relation_transformer_fn * fn; + relation_base & reg = *ctx.reg(m_src); + if (!find_fn(reg, fn)) { + fn = reg.get_manager().mk_filter_interpreted_and_project_fn(reg, m_cond, m_cols.size(), m_cols.c_ptr()); + if (!fn) { + throw default_exception( + "trying to perform unsupported filter_interpreted_and_project operation on a relation of kind %s", + reg.get_plugin().get_name().bare_str()); + } + store_fn(reg, fn); + } + + ctx.set_reg(m_res, (*fn)(reg)); + + if (ctx.eager_emptiness_checking() && ctx.reg(m_res)->empty()) { + ctx.make_empty(m_res); + } + return true; + } + + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + out << "filter_interpreted_and_project " << m_src << " into " << m_res; + out << " using " << mk_pp(m_cond, m_cond.get_manager()); + out << " deleting columns "; + print_container(m_cols, out); + } + + virtual void make_annotations(execution_context & ctx) { + std::stringstream s; + std::string a = "rel_src"; + ctx.get_register_annotation(m_src, a); + s << "filter_interpreted_and_project " << mk_pp(m_cond, m_cond.get_manager()); + ctx.set_register_annotation(m_res, s.str()); + } + }; + + instruction * instruction::mk_filter_interpreted_and_project(reg_idx reg, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result) { + return alloc(instr_filter_interpreted_and_project, reg, condition, col_cnt, removed_cols, result); + } + class instr_union : public instruction { reg_idx m_src; @@ -606,6 +650,7 @@ namespace datalog { } } + SASSERT(r_src.get_signature().size() == r_tgt.get_signature().size()); TRACE("dl_verbose", r_tgt.display(tout <<"pre-union:");); (*fn)(r_tgt, r_src, r_delta); @@ -651,7 +696,7 @@ namespace datalog { class instr_project_rename : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; bool m_projection; reg_idx m_src; column_vector m_cols; @@ -723,7 +768,8 @@ namespace datalog { instr_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result) : m_rel1(rel1), m_rel2(rel2), m_cols1(joined_col_cnt, cols1), - m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) {} + m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) { + } virtual bool perform(execution_context & ctx) { ctx.make_empty(m_res); if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { @@ -830,7 +876,7 @@ namespace datalog { class instr_filter_by_negation : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; reg_idx m_tgt; reg_idx m_neg_rel; column_vector m_cols1; diff --git a/src/muz_qe/dl_instruction.h b/src/muz_qe/dl_instruction.h index ae6310ba6..97622c6f3 100644 --- a/src/muz_qe/dl_instruction.h +++ b/src/muz_qe/dl_instruction.h @@ -260,6 +260,8 @@ namespace datalog { static instruction * mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col); static instruction * mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols); static instruction * mk_filter_interpreted(reg_idx reg, app_ref & condition); + static instruction * mk_filter_interpreted_and_project(reg_idx src, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result); static instruction * mk_union(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_widen(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, diff --git a/src/muz_qe/dl_interval_relation.cpp b/src/muz_qe/dl_interval_relation.cpp index 3397f2db0..4c8171bc7 100644 --- a/src/muz_qe/dl_interval_relation.cpp +++ b/src/muz_qe/dl_interval_relation.cpp @@ -99,11 +99,9 @@ namespace datalog { } class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn { - interval_relation_plugin& m_plugin; public: - rename_fn(interval_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) - : convenient_relation_rename_fn(orig_sig, cycle_len, cycle), - m_plugin(p){ + rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { } virtual relation_base * operator()(const relation_base & _r) { @@ -120,7 +118,7 @@ namespace datalog { if(!check_kind(r)) { return 0; } - return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); + return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } interval interval_relation_plugin::unite(interval const& src1, interval const& src2) { @@ -194,11 +192,9 @@ namespace datalog { } class interval_relation_plugin::union_fn : public relation_union_fn { - interval_relation_plugin& m_plugin; bool m_is_widen; public: - union_fn(interval_relation_plugin& p, bool is_widen) : - m_plugin(p), + union_fn(bool is_widen) : m_is_widen(is_widen) { } @@ -223,7 +219,7 @@ namespace datalog { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } - return alloc(union_fn, *this, false); + return alloc(union_fn, false); } relation_union_fn * interval_relation_plugin::mk_widen_fn( @@ -232,7 +228,7 @@ namespace datalog { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } - return alloc(union_fn, *this, true); + return alloc(union_fn, true); } class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn { diff --git a/src/muz_qe/dl_mk_array_blast.cpp b/src/muz_qe/dl_mk_array_blast.cpp index 67bf52da8..c1f0912c0 100644 --- a/src/muz_qe/dl_mk_array_blast.cpp +++ b/src/muz_qe/dl_mk_array_blast.cpp @@ -145,7 +145,6 @@ namespace datalog { expr_ref_vector conjs(m), new_conjs(m); expr_ref tmp(m); expr_safe_replace sub(m); - uint_set lhs_vars, rhs_vars; bool change = false; bool inserted = false; @@ -161,10 +160,8 @@ namespace datalog { if (is_store_def(e, x, y)) { // enforce topological order consistency: - uint_set lhs; - collect_vars(m, x, lhs_vars); - collect_vars(m, y, rhs_vars); - lhs = lhs_vars; + uint_set lhs = rm.collect_vars(x); + uint_set rhs_vars = rm.collect_vars(y); lhs &= rhs_vars; if (!lhs.empty()) { TRACE("dl", tout << "unusable equality " << mk_pp(e, m) << "\n";); @@ -182,7 +179,6 @@ namespace datalog { } } - rule_ref_vector new_rules(rm); expr_ref fml1(m), fml2(m), body(m), head(m); r.to_formula(fml1); body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); @@ -199,12 +195,12 @@ namespace datalog { fml2 = m.mk_implies(body, head); proof_ref p(m); + rule_set new_rules(m_ctx); rm.mk_rule(fml2, p, new_rules, r.name()); - SASSERT(new_rules.size() == 1); - TRACE("dl", new_rules[0]->display(m_ctx, tout << "new rule\n");); + TRACE("dl", new_rules.last()->display(m_ctx, tout << "new rule\n");); rule_ref new_rule(rm); - if (m_simplifier.transform_rule(new_rules[0].get(), new_rule)) { + if (m_simplifier.transform_rule(new_rules.last(), new_rule)) { rules.add_rule(new_rule.get()); rm.mk_rule_rewrite_proof(r, *new_rule.get()); } @@ -214,6 +210,7 @@ namespace datalog { rule_set * mk_array_blast::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); + rules->inherit_predicates(source); rule_set::iterator it = source.begin(), end = source.end(); bool change = false; for (; !m_ctx.canceled() && it != end; ++it) { diff --git a/src/muz_qe/dl_mk_backwards.cpp b/src/muz_qe/dl_mk_backwards.cpp new file mode 100644 index 000000000..b1d8b7d36 --- /dev/null +++ b/src/muz_qe/dl_mk_backwards.cpp @@ -0,0 +1,78 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_backwards.cpp + +Abstract: + + Create Horn clauses for backwards flow. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-17 + +Revision History: + +--*/ + +#include"dl_mk_backwards.h" +#include"dl_context.h" + +namespace datalog { + + mk_backwards::mk_backwards(context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx) { + } + + mk_backwards::~mk_backwards() { } + + rule_set * mk_backwards::operator()(rule_set const & source) { + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + app_ref query(m); + query = m.mk_fresh_const("Q", m.mk_bool_sort()); + result->set_output_predicate(query->get_decl()); + m_ctx.register_predicate(query->get_decl(), false); + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + neg.reset(); + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + if (!source.is_output_predicate(r.get_decl())) { + tail.push_back(r.get_head()); + neg.push_back(false); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + for (unsigned j = 0; j <= utsz; ++j) { + if (j == utsz && j > 0) { + break; + } + if (j == utsz) { + head = query; + } + else { + head = r.get_tail(j); + } + new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + } + TRACE("dl", result->display(tout);); + return result; + } + +}; diff --git a/src/muz_qe/dl_mk_backwards.h b/src/muz_qe/dl_mk_backwards.h new file mode 100644 index 000000000..4e546c848 --- /dev/null +++ b/src/muz_qe/dl_mk_backwards.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_backwards.h + +Abstract: + + Create Horn clauses for backwards flow. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-17 + +Revision History: + +--*/ +#ifndef _DL_MK_BACKWARDS_H_ +#define _DL_MK_BACKWARDS_H_ + +#include"dl_rule_transformer.h" + +namespace datalog { + + class mk_backwards : public rule_transformer::plugin { + ast_manager& m; + context& m_ctx; + public: + mk_backwards(context & ctx, unsigned priority = 33000); + ~mk_backwards(); + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_BACKWARDS_H_ */ + diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp index 3d6c35bb3..271003fff 100644 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ b/src/muz_qe/dl_mk_bit_blast.cpp @@ -108,43 +108,50 @@ namespace datalog { class expand_mkbv_cfg : public default_rewriter_cfg { context& m_context; - rule_ref_vector& m_rules; ast_manager& m; bv_util m_util; expr_ref_vector m_args, m_f_vars, m_g_vars; func_decl_ref_vector m_old_funcs; func_decl_ref_vector m_new_funcs; + rule_set const* m_src; + rule_set* m_dst; obj_map m_pred2blast; public: - expand_mkbv_cfg(context& ctx, rule_ref_vector& rules): + expand_mkbv_cfg(context& ctx): m_context(ctx), - m_rules(rules), m(ctx.get_manager()), m_util(m), m_args(m), m_f_vars(m), m_g_vars(m), m_old_funcs(m), - m_new_funcs(m) + m_new_funcs(m), + m_src(0), + m_dst(0) {} ~expand_mkbv_cfg() {} + void set_src(rule_set const* src) { m_src = src; } + 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) { - rule_manager& rm = m_context.get_rule_manager(); - bool found = false; - for (unsigned j = 0; !found && j < num; ++j) { - found = m_util.is_mkbv(args[j]); - } - if (!found) { + if (num == 0) { + if (m_src->is_output_predicate(f)) + m_dst->set_output_predicate(f); return BR_FAILED; } + + for (unsigned i = 0; i < num; ++i) { + if (!m_util.is_mkbv(args[i])) + return BR_FAILED; + } + // // f(mk_bv(args),...) // @@ -183,14 +190,7 @@ namespace datalog { m_new_funcs.push_back(g); m_pred2blast.insert(f, g); - // Create rule f(mk_mkbv(args)) :- g(args) - - fml = m.mk_implies(m.mk_app(g, m_g_vars.size(), m_g_vars.c_ptr()), m.mk_app(f, m_f_vars.size(), m_f_vars.c_ptr())); - proof_ref pr(m); - if (m_context.generate_proof_trace()) { - pr = m.mk_asserted(fml); // or a def? - } - rm.mk_rule(fml, pr, m_rules, g->get_name()); + m_dst->inherit_predicate(*m_src, f, g); } result = m.mk_app(g, m_args.size(), m_args.c_ptr()); result_pr = 0; @@ -200,9 +200,9 @@ namespace datalog { struct expand_mkbv : public rewriter_tpl { expand_mkbv_cfg m_cfg; - expand_mkbv(ast_manager& m, context& ctx, rule_ref_vector& rules): + expand_mkbv(ast_manager& m, context& ctx): rewriter_tpl(m, m.proofs_enabled(), m_cfg), - m_cfg(ctx, rules) { + m_cfg(ctx) { } }; @@ -212,7 +212,6 @@ namespace datalog { context & m_context; ast_manager & m; params_ref m_params; - rule_ref_vector m_rules; mk_interp_tail_simplifier m_simplifier; bit_blaster_rewriter m_blaster; expand_mkbv m_rewriter; @@ -239,19 +238,14 @@ namespace datalog { } } - void reset() { - m_rules.reset(); - } - public: impl(context& ctx): m_context(ctx), m(ctx.get_manager()), m_params(ctx.get_params().p), - m_rules(ctx.get_rule_manager()), m_simplifier(ctx), m_blaster(ctx.get_manager(), m_params), - m_rewriter(ctx.get_manager(), ctx, m_rules) { + m_rewriter(ctx.get_manager(), ctx) { m_params.set_bool("blast_full", true); m_params.set_bool("blast_quant", true); m_blaster.updt_params(m_params); @@ -265,27 +259,34 @@ namespace datalog { rule_manager& rm = m_context.get_rule_manager(); unsigned sz = source.get_num_rules(); expr_ref fml(m); - reset(); - rule_set * result = alloc(rule_set, m_context); + 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) { rule * r = source.get_rule(i); - r->to_formula(fml); + r->to_formula(fml); if (blast(r, fml)) { proof_ref pr(m); if (m_context.generate_proof_trace()) { pr = m.mk_asserted(fml); // loses original proof of r. } - rm.mk_rule(fml, pr, m_rules, r->name()); + // TODO add logic for pc: + // 1. replace fresh predicates by non-bit-blasted predicates + // 2. replace pr by the proof of r. + rm.mk_rule(fml, pr, *result, r->name()); } else { - m_rules.push_back(r); + result->add_rule(r); } } - - for (unsigned i = 0; i < m_rules.size(); ++i) { - result->add_rule(m_rules.get(i)); - } + // copy output predicates without any rule (bit-blasting not really needed) + const func_decl_set& decls = source.get_output_predicates(); + for (func_decl_set::iterator I = decls.begin(), E = decls.end(); I != E; ++I) { + if (!source.contains(*I)) + result->set_output_predicate(*I); + } + if (m_context.get_model_converter()) { filter_model_converter* fmc = alloc(filter_model_converter, m); bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m); diff --git a/src/muz_qe/dl_mk_coalesce.cpp b/src/muz_qe/dl_mk_coalesce.cpp index 94c42e33b..8c7f1d5b4 100644 --- a/src/muz_qe/dl_mk_coalesce.cpp +++ b/src/muz_qe/dl_mk_coalesce.cpp @@ -173,6 +173,7 @@ namespace datalog { rule_set * mk_coalesce::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); + rules->inherit_predicates(source); rule_set::decl2rules::iterator it = source.begin_grouped_rules(), end = source.end_grouped_rules(); for (; it != end; ++it) { rule_ref_vector d_rules(rm); diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz_qe/dl_mk_coi_filter.cpp index 6aa688a3c..b253a0e20 100644 --- a/src/muz_qe/dl_mk_coi_filter.cpp +++ b/src/muz_qe/dl_mk_coi_filter.cpp @@ -35,7 +35,7 @@ namespace datalog { rule_set * mk_coi_filter::operator()(rule_set const & source) { - if (source.get_num_rules()==0) { + if (source.empty()) { return 0; } @@ -43,7 +43,7 @@ namespace datalog { decl_set pruned_preds; ptr_vector todo; { - const decl_set& output_preds = m_context.get_output_predicates(); + const decl_set& output_preds = source.get_output_predicates(); decl_set::iterator oend = output_preds.end(); for (decl_set::iterator it = output_preds.begin(); it!=oend; ++it) { todo.push_back(*it); @@ -70,6 +70,7 @@ namespace datalog { } scoped_ptr res = alloc(rule_set, m_context); + res->inherit_predicates(source); rule_set::iterator rend = source.end(); for (rule_set::iterator rit = source.begin(); rit!=rend; ++rit) { diff --git a/src/muz_qe/dl_mk_explanations.cpp b/src/muz_qe/dl_mk_explanations.cpp index 646a0bcbe..004b1823d 100644 --- a/src/muz_qe/dl_mk_explanations.cpp +++ b/src/muz_qe/dl_mk_explanations.cpp @@ -80,8 +80,8 @@ namespace datalog { virtual bool can_handle_signature(const relation_signature & s) { unsigned n=s.size(); - for(unsigned i=0; i(get_plugin().mk_empty(get_signature())); - if(empty()) { + if (empty()) { res->set_undefined(); } return res; } void display_explanation(app * expl, std::ostream & out) const { - if(expl) { + if (expl) { //TODO: some nice explanation output ast_smt_pp pp(get_plugin().get_ast_manager()); pp.display_expr_smt2(out, expl); @@ -249,13 +249,13 @@ namespace datalog { } virtual void display(std::ostream & out) const { - if(empty()) { + if (empty()) { out << "\n"; return; } unsigned sz = get_signature().size(); - for(unsigned i=0; i(plugin.mk_empty(get_result_signature())); - if(!r1.empty() && !r2.empty()) { + if (!r1.empty() && !r2.empty()) { res->m_empty = false; SASSERT(res->m_data.empty()); res->m_data.append(r1.m_data); @@ -317,10 +317,10 @@ namespace datalog { relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if(&r1.get_plugin()!=this || &r2.get_plugin()!=this) { + if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) { return 0; } - if(col_cnt!=0) { + if (col_cnt!=0) { return 0; } return alloc(join_fn, r1.get_signature(), r2.get_signature()); @@ -337,7 +337,7 @@ namespace datalog { explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if(!r.empty()) { + if (!r.empty()) { relation_fact proj_data = r.m_data; project_out_vector_columns(proj_data, m_removed_cols); res->assign_data(proj_data); @@ -348,7 +348,7 @@ namespace datalog { relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { - if(&r.get_plugin()!=this) { + if (&r.get_plugin()!=this) { return 0; } return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); @@ -365,7 +365,7 @@ namespace datalog { explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if(!r.empty()) { + if (!r.empty()) { relation_fact permutated_data = r.m_data; permutate_by_cycle(permutated_data, m_cycle); res->assign_data(permutated_data); @@ -389,16 +389,16 @@ namespace datalog { explanation_relation * delta = delta0 ? static_cast(delta0) : 0; explanation_relation_plugin & plugin = tgt.get_plugin(); - if(!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { + if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { UNREACHABLE(); } - if(src.empty()) { + if (src.empty()) { return; } - if(plugin.m_relation_level_explanations) { + if (plugin.m_relation_level_explanations) { tgt.unite_with_data(src.m_data); - if(delta) { - if(!m_delta_union_fun) { + if (delta) { + if (!m_delta_union_fun) { m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src); SASSERT(m_delta_union_fun); } @@ -406,9 +406,9 @@ namespace datalog { } } else { - if(tgt.empty()) { + if (tgt.empty()) { tgt.assign_data(src.m_data); - if(delta && delta->empty()) { + if (delta && delta->empty()) { delta->assign_data(src.m_data); } } @@ -423,11 +423,11 @@ namespace datalog { explanation_relation & tgt = static_cast(tgt0); explanation_relation * delta = delta0 ? static_cast(delta0) : 0; - if(src.empty()) { + if (src.empty()) { return; } tgt.set_undefined(); - if(delta) { + if (delta) { delta->set_undefined(); } } @@ -435,10 +435,10 @@ namespace datalog { relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { - if(!check_kind(tgt) || (delta && !check_kind(*delta))) { + if (!check_kind(tgt) || (delta && !check_kind(*delta))) { return 0; } - if(!check_kind(src)) { + if (!check_kind(src)) { //this is to handle the product relation return alloc(foreign_union_fn); } @@ -460,7 +460,7 @@ namespace datalog { virtual void operator()(relation_base & r0) { explanation_relation & r = static_cast(r0); - if(!r.is_undefined(m_col_idx)) { + if (!r.is_undefined(m_col_idx)) { UNREACHABLE(); } @@ -468,7 +468,7 @@ namespace datalog { ptr_vector subst_arg; subst_arg.resize(sz, 0); unsigned ofs = sz-1; - for(unsigned i=0; iget_arg(0); expr * arg2 = cond->get_arg(1); - if(is_var(arg2)) { + if (is_var(arg2)) { std::swap(arg1, arg2); } - if(!is_var(arg1) || !is_app(arg2)) { + if (!is_var(arg1) || !is_app(arg2)) { return 0; } var * col_var = to_var(arg1); app * new_rule = to_app(arg2); - if(!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { + if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { return 0; } unsigned col_idx = col_var->get_idx(); @@ -511,7 +511,7 @@ namespace datalog { class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { public: virtual void operator()(relation_base & r, const relation_base & neg) { - if(!neg.empty()) { + if (!neg.empty()) { r.reset(); } } @@ -520,43 +520,42 @@ namespace datalog { relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { - if(&r.get_plugin()!=this || &neg.get_plugin()!=this) { + if (&r.get_plugin()!=this || &neg.get_plugin()!=this) { return 0; } return alloc(negation_filter_fn); } class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn { - explanation_relation_plugin & m_plugin; func_decl_ref m_union_decl; public: intersection_filter_fn(explanation_relation_plugin & plugin) - : m_plugin(plugin), m_union_decl(plugin.m_union_decl) {} + : m_union_decl(plugin.m_union_decl) {} virtual void operator()(relation_base & tgt0, const relation_base & src0) { explanation_relation & tgt = static_cast(tgt0); const explanation_relation & src = static_cast(src0); - if(src.empty()) { + if (src.empty()) { tgt.reset(); return; } - if(tgt.empty()) { + if (tgt.empty()) { return; } unsigned sz = tgt.get_signature().size(); - for(unsigned i=0; iget_decl()==m_union_decl.get()) { - if(curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { + if (curr_tgt->get_decl()==m_union_decl.get()) { + if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { tgt.m_data.set(i, curr_src); continue; } @@ -570,18 +569,18 @@ namespace datalog { relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn( const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols) { - if(&tgt.get_plugin()!=this || &src.get_plugin()!=this) { + if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) { return 0; } //this checks the join is one to one on all columns - if(tgt.get_signature()!=src.get_signature() + if (tgt.get_signature()!=src.get_signature() || joined_col_cnt!=tgt.get_signature().size() || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) { return 0; } counter ctr; ctr.count(joined_col_cnt, tgt_cols); - if(ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { + if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { return 0; } return alloc(intersection_filter_fn, *this); @@ -595,23 +594,23 @@ namespace datalog { // ----------------------------------- - mk_explanations::mk_explanations(context & ctx, bool relation_level) - : plugin(50000), - m_manager(ctx.get_manager()), - m_context(ctx), - m_decl_util(ctx.get_decl_util()), - m_relation_level(relation_level), - m_pinned(m_manager) { + mk_explanations::mk_explanations(context & ctx) + : plugin(50000), + m_manager(ctx.get_manager()), + m_context(ctx), + m_decl_util(ctx.get_decl_util()), + m_relation_level(ctx.explanations_on_relation_level()), + m_pinned(m_manager) { m_e_sort = m_decl_util.mk_rule_sort(); m_pinned.push_back(m_e_sort); relation_manager & rmgr = ctx.get_rel_context().get_rmanager(); - symbol er_symbol = explanation_relation_plugin::get_name(relation_level); + symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level); m_er_plugin = static_cast(rmgr.get_relation_plugin(er_symbol)); - if(!m_er_plugin) { - m_er_plugin = alloc(explanation_relation_plugin, relation_level, rmgr); + if (!m_er_plugin) { + m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr); rmgr.register_plugin(m_er_plugin); - if(!m_relation_level) { + if (!m_relation_level) { DEBUG_CODE( finite_product_relation_plugin * dummy; SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); @@ -620,7 +619,7 @@ namespace datalog { } } DEBUG_CODE( - if(!m_relation_level) { + if (!m_relation_level) { finite_product_relation_plugin * dummy; SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); } @@ -668,7 +667,7 @@ namespace datalog { func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) { decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0); - if(e->get_data().m_value==0) { + if (e->get_data().m_value==0) { relation_signature e_domain; e_domain.append(orig_decl->get_arity(), orig_decl->get_domain()); e_domain.push_back(m_e_sort); @@ -677,7 +676,7 @@ namespace datalog { m_pinned.push_back(new_decl); e->get_data().m_value = new_decl; - if(m_relation_level) { + if (m_relation_level) { assign_rel_level_kind(new_decl, orig_decl); } } @@ -716,13 +715,13 @@ namespace datalog { app_ref_vector e_tail(m_manager); svector neg_flags; unsigned pos_tail_sz = r->get_positive_tail_size(); - for(unsigned i=0; iget_tail(i), e_var)); neg_flags.push_back(false); } unsigned tail_sz = r->get_tail_size(); - for(unsigned i=pos_tail_sz; iget_tail(i)); neg_flags.push_back(r->is_neg_tail(i)); } @@ -730,9 +729,9 @@ namespace datalog { symbol rule_repr = get_rule_symbol(r); expr_ref_vector rule_expr_args(m_manager); - for(unsigned tail_idx=0; tail_idxget_arg(tail->get_num_args()-1)); } @@ -754,37 +753,32 @@ namespace datalog { return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr()); } - void mk_explanations::transform_rules(const rule_set & orig, rule_set & tgt) { - rule_set::iterator rit = orig.begin(); - rule_set::iterator rend = orig.end(); - for(; rit!=rend; ++rit) { + void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) { + rule_set::iterator rit = src.begin(); + rule_set::iterator rend = src.end(); + for (; rit!=rend; ++rit) { rule * e_rule = get_e_rule(*rit); - tgt.add_rule(e_rule); + dst.add_rule(e_rule); } //add rules that will (for output predicates) copy facts from explained relations back to //the original ones expr_ref_vector lit_args(m_manager); - decl_set::iterator pit = m_original_preds.begin(); - decl_set::iterator pend = m_original_preds.end(); - for(; pit!=pend; ++pit) { + decl_set::iterator pit = src.get_output_predicates().begin(); + decl_set::iterator pend = src.get_output_predicates().end(); + for (; pit != pend; ++pit) { func_decl * orig_decl = *pit; - if(!m_context.is_output_predicate(orig_decl)) { - continue; - } - lit_args.reset(); unsigned arity = orig_decl->get_arity(); - for(unsigned i=0; iget_domain(i))); } app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager); app_ref e_lit(get_e_lit(orig_lit, arity), m_manager); app * tail[] = { e_lit.get() }; - tgt.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); + dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); } - } void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, @@ -799,7 +793,7 @@ namespace datalog { sieve_relation * srels[] = { static_cast(&prod_rel[0]), static_cast(&prod_rel[1]) }; - if(&srels[0]->get_inner().get_plugin()==m_er_plugin) { + if (&srels[0]->get_inner().get_plugin()==m_er_plugin) { std::swap(srels[0], srels[1]); } SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin()); @@ -821,10 +815,9 @@ namespace datalog { } } - void mk_explanations::transform_facts(relation_manager & rmgr) { + void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) { - - if(!m_e_fact_relation) { + if (!m_e_fact_relation) { relation_signature expl_singleton_sig; expl_singleton_sig.push_back(m_e_sort); @@ -836,29 +829,26 @@ namespace datalog { SASSERT(&expl_singleton->get_plugin()==m_er_plugin); m_e_fact_relation = static_cast(expl_singleton); } - - - - decl_set::iterator it = m_original_preds.begin(); - decl_set::iterator end = m_original_preds.end(); - for(; it!=end; ++it) { + func_decl_set const& predicates = m_context.get_predicates(); + decl_set::iterator it = predicates.begin(); + decl_set::iterator end = predicates.end(); + for (; it!=end; ++it) { func_decl * orig_decl = *it; func_decl * e_decl = get_e_decl(orig_decl); - if(m_context.is_output_predicate(orig_decl)) { - m_context.set_output_predicate(e_decl); - } - - if(!rmgr.try_get_relation(orig_decl)) { - //there are no facts for this predicate + if (!rmgr.try_get_relation(orig_decl) && + !src.contains(orig_decl)) { + // there are no facts or rules for this predicate continue; } + dst.inherit_predicate(src, orig_decl, e_decl); + relation_base & orig_rel = rmgr.get_relation(orig_decl); relation_base & e_rel = rmgr.get_relation(e_decl); SASSERT(e_rel.empty()); //the e_rel should be a new relation - - if(m_relation_level) { + + if (m_relation_level) { translate_rel_level_relation(rmgr, orig_rel, e_rel); } else { @@ -869,18 +859,19 @@ namespace datalog { SASSERT(union_fun); (*union_fun)(e_rel, *aux_extended_rel); } - } } rule_set * mk_explanations::operator()(rule_set const & source) { - if(source.get_num_rules()==0) { + if (source.empty()) { + return 0; + } + if (!m_context.generate_explanations()) { return 0; } - m_context.collect_predicates(m_original_preds); rule_set * res = alloc(rule_set, m_context); - transform_facts(m_context.get_rel_context().get_rmanager()); + transform_facts(m_context.get_rel_context().get_rmanager(), source, *res); transform_rules(source, *res); return res; } diff --git a/src/muz_qe/dl_mk_explanations.h b/src/muz_qe/dl_mk_explanations.h index 4e7e23e98..9e4d705c3 100644 --- a/src/muz_qe/dl_mk_explanations.h +++ b/src/muz_qe/dl_mk_explanations.h @@ -32,19 +32,13 @@ namespace datalog { typedef obj_map decl_map; - ast_manager & m_manager; - context & m_context; + ast_manager & m_manager; + context & m_context; dl_decl_util & m_decl_util; - - bool m_relation_level; - - decl_set m_original_preds; - + bool m_relation_level; ast_ref_vector m_pinned; - explanation_relation_plugin * m_er_plugin; - - sort * m_e_sort; + sort * m_e_sort; scoped_rel m_e_fact_relation; decl_map m_e_decl_map; @@ -62,15 +56,15 @@ namespace datalog { void assign_rel_level_kind(func_decl * e_decl, func_decl * orig); void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel); - void transform_rules(const rule_set & orig, rule_set & tgt); + void transform_rules(const rule_set & src, rule_set & dst); - void transform_facts(relation_manager & rmgr); + void transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst); public: /** If relation_level is true, the explanation will not be stored for each fact, but we will rather store history of the whole relation. */ - mk_explanations(context & ctx, bool relation_level); + mk_explanations(context & ctx); /** \brief Return explanation predicate that corresponds to \c orig_decl. diff --git a/src/muz_qe/dl_mk_extract_quantifiers.cpp b/src/muz_qe/dl_mk_extract_quantifiers.cpp deleted file mode 100644 index 76e329d79..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_extract_quantifiers.cpp - -Abstract: - - Remove universal quantifiers over interpreted predicates in the body. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-21 - -Revision History: - ---*/ - -#include"dl_mk_extract_quantifiers.h" -#include"ast_pp.h" -#include"dl_bmc_engine.h" -#include"smt_quantifier.h" -#include"smt_context.h" -#include"for_each_expr.h" -#include "expr_abstract.h" - - -namespace datalog { - - - mk_extract_quantifiers::mk_extract_quantifiers(context & ctx) : - rule_transformer::plugin(101, false), - m_ctx(ctx), - m(ctx.get_manager()), - rm(ctx.get_rule_manager()), - m_query_pred(m) - {} - - mk_extract_quantifiers::~mk_extract_quantifiers() { - reset(); - } - - void mk_extract_quantifiers::set_query(func_decl* q) { - m_query_pred = q; - } - - void mk_extract_quantifiers::ensure_predicate(expr* e, unsigned& max_var, app_ref_vector& tail) { - SASSERT(is_app(e)); - SASSERT(to_app(e)->get_decl()->get_family_id() == null_family_id); - app* a = to_app(e); - expr_ref_vector args(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - expr* arg = a->get_arg(i); - if (is_var(arg) || m.is_value(arg)) { - args.push_back(arg); - } - else { - expr_ref new_var(m); - new_var = m.mk_var(++max_var, m.get_sort(arg)); - args.push_back(new_var); - tail.push_back(m.mk_eq(new_var, arg)); - } - } - tail.push_back(m.mk_app(a->get_decl(), args.size(), args.c_ptr())); - } - - class mk_extract_quantifiers::collect_insts { - ast_manager& m; - ptr_vector m_binding; - vector m_bindings; - ptr_vector m_quantifiers; - public: - collect_insts(ast_manager& m): m(m) { } - - void operator()(expr* n) { - expr* not_q_or_i, *e1, *e2, *e3; - if (m.is_quant_inst(n, not_q_or_i, m_binding)) { - VERIFY(m.is_or(not_q_or_i, e1, e2)); - VERIFY(m.is_not(e1, e3)); - SASSERT(is_quantifier(e3)); - m_quantifiers.push_back(to_quantifier(e3)); - m_bindings.push_back(expr_ref_vector(m,m_binding.size(), m_binding.c_ptr())); - m_binding.reset(); - } - else if ((m.is_rewrite(n, e1, e2) || - (m.is_rewrite_star(n) && - (e3 = to_app(n)->get_arg(to_app(n)->get_num_args()-1), - e1 = to_app(e3)->get_arg(0), - e2 = to_app(e3)->get_arg(1), - true))) && - is_quantifier(e1) && m.is_false(e2)) { - quantifier* q = to_quantifier(e1); - m_quantifiers.push_back(q); - m_bindings.push_back(expr_ref_vector(m)); - expr_ref_vector& b = m_bindings.back(); - for (unsigned i = 0; i < q->get_num_decls(); ++i) { - b.push_back(m.mk_fresh_const("V", q->get_decl_sort(i))); - } - } - } - - void reset() { - m_quantifiers.reset(); - m_bindings.reset(); - } - - unsigned size() const { return m_quantifiers.size(); } - ptr_vector const& quantifiers() const { return m_quantifiers; } - vector const& bindings() const { return m_bindings; } - }; - - - /* - * forall y . P1(x,y) & - * forall y . P2(x,y) & - * Body[x] & - * ~H[x] - * forall y . y != binding1 => ~ P1(x,y) - * forall y . y != binding2 => ~ P2(x,y) - */ - void mk_extract_quantifiers::extract(rule& r, rule_set& new_rules) { - unsigned utsz = r.get_uninterpreted_tail_size(); - expr_ref_vector conjs(m); - quantifier_ref_vector qs(m); - for (unsigned i = utsz; i < r.get_tail_size(); ++i) { - conjs.push_back(r.get_tail(i)); - } - datalog::flatten_and(conjs); - for (unsigned j = 0; j < conjs.size(); ++j) { - expr* e = conjs[j].get(); - quantifier* q; - if (rule_manager::is_forall(m, e, q)) { - qs.push_back(q); - conjs[j] = conjs.back(); - conjs.pop_back(); - --j; - } - } - if (qs.empty()) { - new_rules.add_rule(&r); - } - else { - expr_ref fml(m); - expr_ref_vector bindings(m); - obj_map insts; - for (unsigned i = 0; i < qs.size(); ++i) { - insts.insert(qs[i].get(), alloc(expr_ref_vector, m)); - } - - unsigned max_inst = 10; // TODO configuration. - - for (unsigned i = 0; i < max_inst; ++i) { - app_ref_vector sub(m); - rule2formula(r, insts, fml, sub); - bool new_binding = find_instantiations_proof_based(fml, sub, insts, bindings); - if (!new_binding) { - break; - } - } - - expr_ref_vector fmls(m); - for (unsigned i = 0; i < utsz; ++i) { - fmls.push_back(r.get_tail(i)); - } - fmls.append(bindings); - fmls.append(conjs); - fml = m.mk_implies(m.mk_and(fmls.size(), fmls.c_ptr()), r.get_head()); - TRACE("dl", tout << "new rule\n" << mk_pp(fml, m) << "\n";); - rule_ref_vector rules(rm); - proof_ref pr(m); - if (m_ctx.generate_proof_trace()) { - scoped_proof _scp(m); - expr_ref fml1(m); - r.to_formula(fml1); - pr = m.mk_rewrite(fml1, fml); - pr = m.mk_modus_ponens(r.get_proof(), pr); - } - rm.mk_rule(fml, pr, rules, r.name()); - for (unsigned i = 0; i < rules.size(); ++i) { - new_rules.add_rule(rules[i].get()); - m_quantifiers.insert(rules[i].get(), alloc(quantifier_ref_vector, qs)); - } - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - } - } - - void mk_extract_quantifiers::rule2formula( - rule& r, - obj_map const& insts, - expr_ref& fml, - app_ref_vector& sub) - { - expr_ref body(m); - expr_ref_vector fmls(m); - ptr_vector sorts; - var_subst vs(m, false); - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - quantifier* q = it->m_key; - expr_ref_vector& eqs = *it->m_value; - expr_ref_vector disj(m); - disj.append(eqs); - disj.push_back(m.mk_not(q->get_expr())); - body = m.mk_or(disj.size(), disj.c_ptr()); - fml = m.update_quantifier(q, body); - fmls.push_back(fml); - } - fml = m.mk_or(fmls.size(), fmls.c_ptr()); - fmls.reset(); - fmls.push_back(fml); - for (unsigned i = 0; i < r.get_tail_size(); ++i) { - SASSERT(!r.is_neg_tail(i)); - fmls.push_back(r.get_tail(i)); - } - fmls.push_back(m.mk_not(r.get_head())); - fml = m.mk_and(fmls.size(), fmls.c_ptr()); - - get_free_vars(fml, sorts); - for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } - sub.push_back(m.mk_const(symbol(i), sorts[i])); - } - vs(fml, sub.size(), (expr*const*)sub.c_ptr(), fml); - } - - bool mk_extract_quantifiers::find_instantiations_proof_based( - expr* fml, - app_ref_vector const& var_inst, - obj_map& insts, - expr_ref_vector& bindings) - { - datalog::scoped_proof _scp(m); - smt_params fparams; - fparams.m_mbqi = true; // false - fparams.m_soft_timeout = 1000; - smt::kernel solver(m, fparams); - solver.assert_expr(fml); - IF_VERBOSE(1, verbose_stream() << "check\n";); - lbool result = solver.check(); - IF_VERBOSE(1, verbose_stream() << "checked\n";); - TRACE("dl", tout << result << "\n";); - if (result != l_false) { - return false; - } - - map qid_map; - quantifier* q; - - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - q = it->m_key; - qid_map.insert(q->get_qid(), q); - } - - proof* p = solver.get_proof(); - TRACE("dl", tout << mk_pp(p, m) << "\n";); - collect_insts collector(m); - for_each_expr(collector, p); - ptr_vector const& quants = collector.quantifiers(); - - for (unsigned i = 0; i < collector.size(); ++i) { - symbol qid = quants[i]->get_qid(); - if (!qid_map.find(qid, q)) { - TRACE("dl", tout << "Could not find quantifier " << mk_pp(quants[i], m) << "\n";); - continue; - } - expr_ref_vector const& binding = collector.bindings()[i]; - - TRACE("dl", tout << "Instantiating:\n" << mk_pp(quants[i], m) << "\n"; - for (unsigned j = 0; j < binding.size(); ++j) { - tout << mk_pp(binding[j], m) << " "; - } - tout << "\n";); - - expr_ref_vector instantiation(m); - for (unsigned j = 0; j < binding.size(); ++j) { - instantiation.push_back(binding[j]); - } - add_binding(var_inst, bindings, q, instantiation, insts); - } - - return collector.size() > 0; - } - - void mk_extract_quantifiers::add_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts) - { - if (instantiation.size() == q->get_num_decls()) { - // Full binding. - apply_binding(var_inst, bindings, q, instantiation, insts); - } - } - - void mk_extract_quantifiers::apply_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts) - { - datalog::scoped_no_proof _scp(m); - expr_ref e(m); - expr_ref_vector eqs(m); - var_subst vs(m, false); - inv_var_shifter invsh(m); - vs(q->get_expr(), instantiation.size(), instantiation.c_ptr(), e); - invsh(e, q->get_num_decls(), e); - expr_ref_vector inst(m); - inst.append(var_inst.size(), (expr*const*)var_inst.c_ptr()); - inst.reverse(); - expr_abstract(m, 0, inst.size(), inst.c_ptr(), e, e); - bindings.push_back(e); - for (unsigned i = 0; i < instantiation.size(); ++i) { - e = instantiation[i]; - e = m.mk_eq(m.mk_var(i, q->get_decl_sort(i)), e); - eqs.push_back(e); - } - e = m.mk_and(eqs.size(), eqs.c_ptr()); - insts.find(q)->push_back(e); - - TRACE("dl", tout << mk_pp(q, m) << "\n"; - tout << "instantiation: "; - for (unsigned i = 0; i < instantiation.size(); ++i) { - tout << mk_pp(instantiation[i], m) << " "; - } - tout << "\n"; - tout << "inst: "; - for (unsigned i = 0; i < var_inst.size(); ++i) { - tout << mk_pp(var_inst[i], m) << " "; - } - tout << "\n"; - tout << mk_pp(bindings.back(), m) << "\n"; - tout << "eqs: " << mk_pp(e, m) << "\n"; - ); - } - - - void mk_extract_quantifiers::reset() { - obj_map::iterator it = m_quantifiers.begin(), - end = m_quantifiers.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - m_has_quantifiers = false; - m_quantifiers.reset(); - } - - rule_set * mk_extract_quantifiers::operator()(rule_set const & source) { - reset(); - rule_set::iterator it = source.begin(), end = source.end(); - for (; !m_has_quantifiers && it != end; ++it) { - m_has_quantifiers = (*it)->has_quantifiers(); - } - if (!m_has_quantifiers) { - return 0; - } - - rule_set* rules = alloc(rule_set, m_ctx); - it = source.begin(); - for (; it != end; ++it) { - extract(**it, *rules); - } - - return rules; - } - -}; - - diff --git a/src/muz_qe/dl_mk_extract_quantifiers.h b/src/muz_qe/dl_mk_extract_quantifiers.h deleted file mode 100644 index 27b13cd71..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers.h +++ /dev/null @@ -1,99 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_extract_quantifiers.h - -Abstract: - - Replace universal quantifiers over interpreted predicates in the body - by instantiations mined using bounded model checking search. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-21 - -Revision History: - ---*/ -#ifndef _DL_MK_EXTRACT_QUANTIFIERS_H_ -#define _DL_MK_EXTRACT_QUANTIFIERS_H_ - -#include"dl_context.h" -#include"dl_rule_set.h" -#include"dl_rule_transformer.h" -#include"obj_pair_hashtable.h" - -namespace datalog { - - /** - \brief Extract universal quantifiers from rules. - */ - class mk_extract_quantifiers : public rule_transformer::plugin { - - class collect_insts; - - context& m_ctx; - ast_manager& m; - rule_manager& rm; - func_decl_ref m_query_pred; - bool m_has_quantifiers; - obj_map m_quantifiers; - - void reset(); - - void extract(rule& r, rule_set& new_rules); - - void rule2formula( - rule& r, - obj_map const& insts, - expr_ref& fml, - app_ref_vector& sub); - - - void add_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts); - - void apply_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts); - - - public: - /** - \brief Create rule transformer that extracts universal quantifiers (over recursive predicates). - */ - mk_extract_quantifiers(context & ctx); - - virtual ~mk_extract_quantifiers(); - - void set_query(func_decl* q); - - rule_set * operator()(rule_set const & source); - - bool has_quantifiers() { return m_has_quantifiers; } - - obj_map& quantifiers() { return m_quantifiers; } - - void ensure_predicate(expr* e, unsigned& max_var, app_ref_vector& tail); - - bool find_instantiations_proof_based( - expr* fml, - app_ref_vector const& var_inst, - obj_map& insts, - expr_ref_vector& bindings); - - }; - -}; - -#endif /* _DL_MK_EXTRACT_QUANTIFIERS_H_ */ - diff --git a/src/muz_qe/dl_mk_filter_rules.cpp b/src/muz_qe/dl_mk_filter_rules.cpp index 62abd78c4..ef89c9ffa 100644 --- a/src/muz_qe/dl_mk_filter_rules.cpp +++ b/src/muz_qe/dl_mk_filter_rules.cpp @@ -27,10 +27,12 @@ namespace datalog { mk_filter_rules::mk_filter_rules(context & ctx): plugin(2000), m_context(ctx), - m_manager(ctx.get_manager()), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), m_result(0), - m_pinned(m_manager) { + m_pinned(m) { } + mk_filter_rules::~mk_filter_rules() { ptr_vector to_dealloc; filter_cache::iterator it = m_tail2filter.begin(); @@ -50,15 +52,15 @@ namespace datalog { \brief Return true if \c pred is a cadidate for a "filter" rule. */ bool mk_filter_rules::is_candidate(app * pred) { - if (!m_context.get_rule_manager().is_predicate(pred)) { - TRACE("mk_filter_rules", tout << mk_pp(pred, m_manager) << "\nis not a candidate because it is interpreted.\n";); + if (!m_context.is_predicate(pred)) { + TRACE("mk_filter_rules", tout << mk_pp(pred, m) << "\nis not a candidate because it is interpreted.\n";); return false; } var_idx_set used_vars; unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); - if (m_manager.is_value(arg)) + if (m.is_value(arg)) return true; SASSERT(is_var(arg)); unsigned vidx = to_var(arg)->get_idx(); @@ -73,10 +75,10 @@ namespace datalog { \brief Create a "filter" (if it doesn't exist already) for the given predicate. */ func_decl * mk_filter_rules::mk_filter_decl(app * pred, var_idx_set const & non_local_vars) { - sort_ref_buffer filter_domain(m_manager); + sort_ref_buffer filter_domain(m); - filter_key * key = alloc(filter_key, m_manager); - mk_new_rule_tail(m_manager, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); + filter_key * key = alloc(filter_key, m); + mk_new_rule_tail(m, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); func_decl * filter_decl = 0; if (!m_tail2filter.find(key, filter_decl)) { filter_decl = m_context.mk_fresh_head_predicate(pred->get_decl()->get_name(), symbol("filter"), @@ -84,8 +86,8 @@ namespace datalog { m_pinned.push_back(filter_decl); m_tail2filter.insert(key, filter_decl); - app_ref filter_head(m_manager); - filter_head = m_manager.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); + app_ref filter_head(m); + filter_head = m.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); app * filter_tail = key->new_pred; rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)0); filter_rule->set_accounting_parent_object(m_context, m_current); @@ -103,16 +105,15 @@ namespace datalog { void mk_filter_rules::process(rule * r) { m_current = r; app * new_head = r->get_head(); - app_ref_vector new_tail(m_manager); + app_ref_vector new_tail(m); svector new_is_negated; unsigned sz = r->get_tail_size(); bool rule_modified = false; for (unsigned i = 0; i < sz; i++) { app * tail = r->get_tail(i); if (is_candidate(tail)) { - TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m_manager) << "\n";); - var_idx_set non_local_vars; - collect_non_local_vars(m_manager, r, tail, non_local_vars); + TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m) << "\n";); + var_idx_set non_local_vars = rm.collect_rule_vars_ex(r, tail); func_decl * filter_decl = mk_filter_decl(tail, non_local_vars); ptr_buffer new_args; var_idx_set used_vars; @@ -128,7 +129,7 @@ namespace datalog { } } SASSERT(new_args.size() == filter_decl->get_arity()); - new_tail.push_back(m_manager.mk_app(filter_decl, new_args.size(), new_args.c_ptr())); + new_tail.push_back(m.mk_app(filter_decl, new_args.size(), new_args.c_ptr())); rule_modified = true; } else { @@ -151,19 +152,18 @@ namespace datalog { } rule_set * mk_filter_rules::operator()(rule_set const & source) { - // TODO mc, pc m_tail2filter.reset(); m_result = alloc(rule_set, m_context); m_modified = false; unsigned num_rules = source.get_num_rules(); for (unsigned i = 0; i < num_rules; i++) { - rule * r = source.get_rule(i); - process(r); + process(source.get_rule(i)); } if(!m_modified) { dealloc(m_result); return static_cast(0); } + m_result->inherit_predicates(source); return m_result; } diff --git a/src/muz_qe/dl_mk_filter_rules.h b/src/muz_qe/dl_mk_filter_rules.h index 4a247fdb5..b51cb8e24 100644 --- a/src/muz_qe/dl_mk_filter_rules.h +++ b/src/muz_qe/dl_mk_filter_rules.h @@ -45,17 +45,22 @@ namespace datalog { filter_key(ast_manager & m) : new_pred(m), filter_args(m) {} unsigned hash() const { - return new_pred->hash() ^ int_vector_hash(filter_args); + unsigned r = new_pred->hash(); + for (unsigned i = 0; i < filter_args.size(); ++i) { + r ^= filter_args[i]->hash(); + } + return r; } bool operator==(const filter_key & o) const { return o.new_pred==new_pred && vectors_equal(o.filter_args, filter_args); } }; - typedef map, deref_eq > filter_cache; + typedef obj_map filter_cache; context & m_context; - ast_manager & m_manager; + ast_manager & m; + rule_manager & rm; filter_cache m_tail2filter; rule_set * m_result; rule * m_current; diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp index 0d468f9ef..afd586627 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp @@ -67,24 +67,23 @@ namespace datalog { void mk_interp_tail_simplifier::rule_substitution::get_result(rule_ref & res) { SASSERT(m_rule); - app_ref new_head(m); - apply(m_rule->get_head(), new_head); + apply(m_rule->get_head(), m_head); - app_ref_vector tail(m); - svector tail_neg; + m_tail.reset(); + m_neg.reset(); unsigned tail_len = m_rule->get_tail_size(); for (unsigned i=0; iget_tail(i), new_tail_el); - tail.push_back(new_tail_el); - tail_neg.push_back(m_rule->is_neg_tail(i)); + m_tail.push_back(new_tail_el); + m_neg.push_back(m_rule->is_neg_tail(i)); } - mk_rule_inliner::remove_duplicate_tails(tail, tail_neg); + mk_rule_inliner::remove_duplicate_tails(m_tail, m_neg); - SASSERT(tail.size() == tail_neg.size()); - res = m_context.get_rule_manager().mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); + SASSERT(m_tail.size() == m_neg.size()); + res = m_context.get_rule_manager().mk(m_head, m_tail.size(), m_tail.c_ptr(), m_neg.c_ptr()); res->set_accounting_parent_object(m_context, m_rule); res->norm_vars(res.get_manager()); } @@ -362,14 +361,37 @@ namespace datalog { } }; + class mk_interp_tail_simplifier::normalizer_rw : public rewriter_tpl { + public: + normalizer_rw(ast_manager& m, normalizer_cfg& cfg): rewriter_tpl(m, false, cfg) {} + }; + + + mk_interp_tail_simplifier::mk_interp_tail_simplifier(context & ctx, unsigned priority) + : plugin(priority), + m(ctx.get_manager()), + m_context(ctx), + m_simp(ctx.get_rewriter()), + a(m), + m_rule_subst(ctx), + m_tail(m), + m_itail_members(m), + m_conj(m) { + m_cfg = alloc(normalizer_cfg, m); + m_rw = alloc(normalizer_rw, m, *m_cfg); + } + + mk_interp_tail_simplifier::~mk_interp_tail_simplifier() { + dealloc(m_rw); + dealloc(m_cfg); + } + + void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res) { expr_ref simp1_res(m); m_simp(a, simp1_res); - normalizer_cfg r_cfg(m); - rewriter_tpl rwr(m, false, r_cfg); - expr_ref dl_form_e(m); - rwr(simp1_res.get(), res); + (*m_rw)(simp1_res.get(), res); /*if (simp1_res.get()!=res.get()) { std::cout<<"pre norm:\n"< todo; + m_todo.reset(); + m_leqs.reset(); for (unsigned i = u_len; i < len; i++) { - todo.push_back(r->get_tail(i)); + m_todo.push_back(r->get_tail(i)); SASSERT(!r->is_neg_tail(i)); } m_rule_subst.reset(r); - obj_hashtable leqs; expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); bool found_something = false; @@ -401,10 +423,10 @@ namespace datalog { #define TRY_UNIFY(_x,_y) if (m_rule_subst.unify(_x,_y)) { found_something = true; } #define IS_FLEX(_x) (is_var(_x) || m.is_value(_x)) - while (!todo.empty()) { + while (!m_todo.empty()) { expr * arg1, *arg2; - expr * t0 = todo.back(); - todo.pop_back(); + expr * t0 = m_todo.back(); + m_todo.pop_back(); expr* t = t0; bool neg = m.is_not(t, t); if (is_var(t)) { @@ -412,7 +434,7 @@ namespace datalog { } else if (!neg && m.is_and(t)) { app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); + m_todo.append(a->get_num_args(), a->get_args()); } else if (!neg && m.is_eq(t, arg1, arg2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); @@ -440,12 +462,12 @@ namespace datalog { else if (!neg && (a.is_le(t, arg1, arg2) || a.is_ge(t, arg2, arg1))) { tmp1 = a.mk_sub(arg1, arg2); tmp2 = a.mk_sub(arg2, arg1); - if (false && leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { + if (false && m_leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); } else { trail.push_back(tmp1); - leqs.insert(tmp1); + m_leqs.insert(tmp1); } } } @@ -485,12 +507,12 @@ namespace datalog { } app_ref head(r->get_head(), m); - app_ref_vector tail(m); - svector tail_neg; + m_tail.reset(); + m_tail_neg.reset(); for (unsigned i=0; iget_tail(i)); - tail_neg.push_back(r->is_neg_tail(i)); + m_tail.push_back(r->get_tail(i)); + m_tail_neg.push_back(r->is_neg_tail(i)); } bool modified = false; @@ -502,12 +524,12 @@ namespace datalog { SASSERT(!r->is_neg_tail(u_len)); } else { - expr_ref_vector itail_members(m); + m_itail_members.reset(); for (unsigned i=u_len; iget_tail(i)); + m_itail_members.push_back(r->get_tail(i)); SASSERT(!r->is_neg_tail(i)); } - itail = m.mk_and(itail_members.size(), itail_members.c_ptr()); + itail = m.mk_and(m_itail_members.size(), m_itail_members.c_ptr()); modified = true; } @@ -523,21 +545,21 @@ namespace datalog { SASSERT(m.is_bool(simp_res)); if (modified) { - expr_ref_vector conjs(m); - flatten_and(simp_res, conjs); - for (unsigned i = 0; i < conjs.size(); ++i) { - expr* e = conjs[i].get(); + m_conj.reset(); + flatten_and(simp_res, m_conj); + for (unsigned i = 0; i < m_conj.size(); ++i) { + expr* e = m_conj[i].get(); if (is_app(e)) { - tail.push_back(to_app(e)); + m_tail.push_back(to_app(e)); } else { - tail.push_back(m.mk_eq(e, m.mk_true())); + m_tail.push_back(m.mk_eq(e, m.mk_true())); } - tail_neg.push_back(false); + m_tail_neg.push_back(false); } - SASSERT(tail.size() == tail_neg.size()); - res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); + SASSERT(m_tail.size() == m_tail_neg.size()); + res = m_context.get_rule_manager().mk(head, m_tail.size(), m_tail.c_ptr(), m_tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); } else { @@ -583,7 +605,9 @@ namespace datalog { } rule_set * res = alloc(rule_set, m_context); - if (!transform_rules(source, *res)) { + if (transform_rules(source, *res)) { + res->inherit_predicates(source); + } else { dealloc(res); res = 0; } diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.h b/src/muz_qe/dl_mk_interp_tail_simplifier.h index 247b20755..5047e1c6e 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.h +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.h @@ -34,15 +34,17 @@ namespace datalog { { ast_manager& m; context& m_context; - substitution m_subst; - unifier m_unif; - - rule * m_rule; + substitution m_subst; + unifier m_unif; + app_ref m_head; + app_ref_vector m_tail; + svector m_neg; + rule * m_rule; void apply(app * a, app_ref& res); public: rule_substitution(context & ctx) - : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_rule(0) {} + : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_head(m), m_tail(m), m_rule(0) {} /** Reset substitution and get it ready for working with rule r. @@ -61,13 +63,23 @@ namespace datalog { } }; + class normalizer_cfg; + class normalizer_rw; + ast_manager & m; context & m_context; th_rewriter & m_simp; arith_util a; rule_substitution m_rule_subst; + ptr_vector m_todo; + obj_hashtable m_leqs; + app_ref_vector m_tail; + expr_ref_vector m_itail_members; + expr_ref_vector m_conj; + svector m_tail_neg; + normalizer_cfg* m_cfg; + normalizer_rw* m_rw; - class normalizer_cfg; void simplify_expr(app * a, expr_ref& res); @@ -77,13 +89,8 @@ namespace datalog { /** Return true if something was modified */ bool transform_rules(const rule_set & orig, rule_set & tgt); public: - mk_interp_tail_simplifier(context & ctx, unsigned priority=40000) - : plugin(priority), - m(ctx.get_manager()), - m_context(ctx), - m_simp(ctx.get_rewriter()), - a(m), - m_rule_subst(ctx) {} + mk_interp_tail_simplifier(context & ctx, unsigned priority=40000); + virtual ~mk_interp_tail_simplifier(); /**If rule should be retained, assign transformed version to res and return true; if rule can be deleted, return false. diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz_qe/dl_mk_karr_invariants.cpp index c4a6a3cdb..143a38636 100644 --- a/src/muz_qe/dl_mk_karr_invariants.cpp +++ b/src/muz_qe/dl_mk_karr_invariants.cpp @@ -35,6 +35,9 @@ Revision History: #include"dl_mk_karr_invariants.h" #include"expr_safe_replace.h" #include"bool_rewriter.h" +#include"dl_mk_backwards.h" +#include"dl_mk_loop_counter.h" +#include "for_each_expr.h" namespace datalog { @@ -45,7 +48,8 @@ namespace datalog { m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_inner_ctx(m, ctx.get_fparams()), - a(m) { + a(m), + m_pinned(m) { params_ref params; params.set_sym("default_relation", symbol("karr_relation")); params.set_sym("engine", symbol("datalog")); @@ -199,48 +203,90 @@ namespace datalog { return 0; } } + mk_loop_counter lc(m_ctx); + mk_backwards bwd(m_ctx); + + scoped_ptr src_loop = lc(source); + TRACE("dl", src_loop->display(tout << "source loop\n");); + + get_invariants(*src_loop); + + // figure out whether to update same rules as used for saturation. + scoped_ptr rev_source = bwd(*src_loop); + get_invariants(*rev_source); + scoped_ptr src_annot = update_rules(*src_loop); + rule_set* rules = lc.revert(*src_annot); + rules->inherit_predicates(source); + TRACE("dl", rules->display(tout);); + m_pinned.reset(); + m_fun2inv.reset(); + return rules; + } + + void mk_karr_invariants::get_invariants(rule_set const& src) { + m_inner_ctx.reset(); rel_context& rctx = m_inner_ctx.get_rel_context(); ptr_vector heads; - m_inner_ctx.ensure_opened(); - it = source.begin(); - for (; it != end; ++it) { - rule_ref r(*it, m_inner_ctx.get_rule_manager()); - m_inner_ctx.add_rule(r); - m_inner_ctx.register_predicate(r->get_decl(), false); + func_decl_set const& predicates = m_ctx.get_predicates(); + for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { + m_inner_ctx.register_predicate(*fit, false); } + m_inner_ctx.ensure_opened(); + m_inner_ctx.replace_rules(src); m_inner_ctx.close(); - rule_set::decl2rules::iterator dit = source.begin_grouped_rules(); - rule_set::decl2rules::iterator dend = source.end_grouped_rules(); + rule_set::decl2rules::iterator dit = src.begin_grouped_rules(); + rule_set::decl2rules::iterator dend = src.end_grouped_rules(); for (; dit != dend; ++dit) { heads.push_back(dit->m_key); } m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); - - rule_set* rules = alloc(rule_set, m_ctx); - it = source.begin(); + + // retrieve invariants. + dit = src.begin_grouped_rules(); + for (; dit != dend; ++dit) { + func_decl* p = dit->m_key; + relation_base* rb = rctx.try_get_relation(p); + if (rb) { + expr_ref fml(m); + rb->to_formula(fml); + if (m.is_true(fml)) { + continue; + } + expr* inv = 0; + if (m_fun2inv.find(p, inv)) { + fml = m.mk_and(inv, fml); + } + m_pinned.push_back(fml); + m_fun2inv.insert(p, fml); + } + } + } + + rule_set* mk_karr_invariants::update_rules(rule_set const& src) { + scoped_ptr dst = alloc(rule_set, m_ctx); + rule_set::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { - update_body(rctx, *rules, **it); + update_body(*dst, **it); } if (m_ctx.get_model_converter()) { add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m); - rule_set::decl2rules::iterator git = source.begin_grouped_rules(); - rule_set::decl2rules::iterator gend = source.end_grouped_rules(); + rule_set::decl2rules::iterator git = src.begin_grouped_rules(); + rule_set::decl2rules::iterator gend = src.end_grouped_rules(); for (; git != gend; ++git) { func_decl* p = git->m_key; - expr_ref fml(m); - relation_base* rb = rctx.try_get_relation(p); - if (rb) { - rb->to_formula(fml); + expr* fml = 0; + if (m_fun2inv.find(p, fml)) { kmc->add(p, fml); } } m_ctx.add_model_converter(kmc); } - TRACE("dl", rules->display(tout);); - return rules; + + dst->inherit_predicates(src); + return dst.detach(); } - void mk_karr_invariants::update_body(rel_context& rctx, rule_set& rules, rule& r) { + void mk_karr_invariants::update_body(rule_set& rules, rule& r) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); app_ref_vector tail(m); @@ -249,17 +295,17 @@ namespace datalog { tail.push_back(r.get_tail(i)); } for (unsigned i = 0; i < utsz; ++i) { - func_decl* q = r.get_decl(i); - relation_base* rb = rctx.try_get_relation(r.get_decl(i)); - if (rb) { - rb->to_formula(fml); + func_decl* q = r.get_decl(i); + expr* fml = 0; + if (m_fun2inv.find(q, fml)) { expr_safe_replace rep(m); for (unsigned j = 0; j < q->get_arity(); ++j) { rep.insert(m.mk_var(j, q->get_domain(j)), r.get_tail(i)->get_arg(j)); } - rep(fml); - tail.push_back(to_app(fml)); + expr_ref tmp(fml, m); + rep(tmp); + tail.push_back(to_app(tmp)); } } rule* new_rule = &r; @@ -937,11 +983,8 @@ namespace datalog { class karr_relation_plugin::union_fn : public relation_union_fn { - karr_relation_plugin& m_plugin; public: - union_fn(karr_relation_plugin& p) : - m_plugin(p) { - } + union_fn() {} virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { @@ -965,7 +1008,7 @@ namespace datalog { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } - return alloc(union_fn, *this); + return alloc(union_fn); } class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn { @@ -1006,16 +1049,17 @@ namespace datalog { class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn { unsigned m_col; rational m_value; + bool m_valid; public: filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) : m_col(col) { arith_util arith(m.get_context().get_manager()); - VERIFY(arith.is_numeral(value, m_value)); + m_valid = arith.is_numeral(value, m_value) && m_value.is_int(); } virtual void operator()(relation_base & _r) { karr_relation & r = get(_r); - if (m_value.is_int()) { + if (m_valid) { r.get_ineqs(); vector row; row.resize(r.get_signature().size()); @@ -1031,7 +1075,7 @@ namespace datalog { relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { - if(check_kind(r)) { + if (check_kind(r)) { return alloc(filter_equal_fn, get_manager(), value, col); } return 0; diff --git a/src/muz_qe/dl_mk_karr_invariants.h b/src/muz_qe/dl_mk_karr_invariants.h index 414953e4f..ec554e284 100644 --- a/src/muz_qe/dl_mk_karr_invariants.h +++ b/src/muz_qe/dl_mk_karr_invariants.h @@ -55,8 +55,13 @@ namespace datalog { rule_manager& rm; context m_inner_ctx; arith_util a; - void update_body(rel_context& rctx, rule_set& result, rule& r); + obj_map m_fun2inv; + ast_ref_vector m_pinned; + void get_invariants(rule_set const& src); + + void update_body(rule_set& result, rule& r); + rule_set* update_rules(rule_set const& src); public: mk_karr_invariants(context & ctx, unsigned priority); @@ -89,12 +94,7 @@ namespace datalog { {} virtual bool can_handle_signature(const relation_signature & sig) { - for (unsigned i = 0; i < sig.size(); ++i) { - if (a.is_int(sig[i])) { - return true; - } - } - return false; + return true; } static symbol get_name() { return symbol("karr_relation"); } diff --git a/src/muz_qe/dl_mk_loop_counter.cpp b/src/muz_qe/dl_mk_loop_counter.cpp index 6b95671f2..678bfc5a3 100644 --- a/src/muz_qe/dl_mk_loop_counter.cpp +++ b/src/muz_qe/dl_mk_loop_counter.cpp @@ -25,13 +25,14 @@ namespace datalog { mk_loop_counter::mk_loop_counter(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), + m_ctx(ctx), a(m), m_refs(m) { } mk_loop_counter::~mk_loop_counter() { } - app_ref mk_loop_counter::add_arg(app* fn, unsigned idx) { + app_ref mk_loop_counter::add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx) { expr_ref_vector args(m); func_decl* new_fn, *old_fn = fn->get_decl(); args.append(fn->get_num_args(), fn->get_args()); @@ -45,17 +46,29 @@ namespace datalog { m_old2new.insert(old_fn, new_fn); m_new2old.insert(new_fn, old_fn); m_refs.push_back(new_fn); + m_ctx.register_predicate(new_fn, false); + if (src.is_output_predicate(old_fn)) { + dst.set_output_predicate(new_fn); + } } return app_ref(m.mk_app(new_fn, args.size(), args.c_ptr()), m); } + + app_ref mk_loop_counter::del_arg(app* fn) { + expr_ref_vector args(m); + func_decl* old_fn, *new_fn = fn->get_decl(); + SASSERT(fn->get_num_args() > 0); + args.append(fn->get_num_args()-1, fn->get_args()); + VERIFY (m_new2old.find(new_fn, old_fn)); + return app_ref(m.mk_app(old_fn, args.size(), args.c_ptr()), m); + } rule_set * mk_loop_counter::operator()(rule_set const & source) { m_refs.reset(); m_old2new.reset(); m_new2old.reset(); - context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); - rule_set * result = alloc(rule_set, ctx); + rule_set * result = alloc(rule_set, m_ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); @@ -70,16 +83,14 @@ namespace datalog { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j, ++cnt) { - tail.push_back(add_arg(r.get_tail(j), cnt)); + tail.push_back(add_arg(source, *result, r.get_tail(j), cnt)); neg.push_back(r.is_neg_tail(j)); - ctx.register_predicate(tail.back()->get_decl(), false); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } - head = add_arg(r.get_head(), cnt); - ctx.register_predicate(head->get_decl(), false); + head = add_arg(source, *result, r.get_head(), cnt); // set the loop counter to be an increment of the previous bool found = false; unsigned last = head->get_num_args()-1; @@ -110,4 +121,38 @@ namespace datalog { return result; } + rule_set * mk_loop_counter::revert(rule_set const & source) { + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + neg.reset(); + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < utsz; ++j) { + tail.push_back(del_arg(r.get_tail(j))); + neg.push_back(r.is_neg_tail(j)); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + head = del_arg(r.get_head()); + new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + + // model converter: ... + // proof converter: ... + + return result; + + } }; diff --git a/src/muz_qe/dl_mk_loop_counter.h b/src/muz_qe/dl_mk_loop_counter.h index 6601f837f..d67c88e0e 100644 --- a/src/muz_qe/dl_mk_loop_counter.h +++ b/src/muz_qe/dl_mk_loop_counter.h @@ -26,12 +26,14 @@ namespace datalog { class mk_loop_counter : public rule_transformer::plugin { ast_manager& m; + context& m_ctx; arith_util a; func_decl_ref_vector m_refs; obj_map m_new2old; obj_map m_old2new; - app_ref add_arg(app* fn, unsigned idx); + app_ref add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx); + app_ref del_arg(app* fn); public: mk_loop_counter(context & ctx, unsigned priority = 33000); ~mk_loop_counter(); @@ -39,6 +41,8 @@ namespace datalog { rule_set * operator()(rule_set const & source); func_decl* get_old(func_decl* f) const { return m_new2old.find(f); } + + rule_set * revert(rule_set const& source); }; }; diff --git a/src/muz_qe/dl_mk_magic_sets.cpp b/src/muz_qe/dl_mk_magic_sets.cpp index 6885edc4e..f6f79f348 100644 --- a/src/muz_qe/dl_mk_magic_sets.cpp +++ b/src/muz_qe/dl_mk_magic_sets.cpp @@ -24,13 +24,13 @@ Revision History: namespace datalog { - mk_magic_sets::mk_magic_sets(context & ctx, rule * goal_rule) : + mk_magic_sets::mk_magic_sets(context & ctx, func_decl* goal) : plugin(10000, true), m_context(ctx), - m_manager(ctx.get_manager()), - m_rules(ctx.get_rule_manager()), - m_pinned(m_manager), - m_goal_rule(goal_rule, ctx.get_rule_manager()) { + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_pinned(m), + m_goal(goal, m) { } void mk_magic_sets::reset() { @@ -39,14 +39,13 @@ namespace datalog { m_adorned_preds.reset(); m_adornments.reset(); m_magic_preds.reset(); - m_rules.reset(); m_pinned.reset(); } void mk_magic_sets::adornment::populate(app * lit, const var_idx_set & bound_vars) { SASSERT(empty()); unsigned arity = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); bool bound = !is_var(arg) || bound_vars.contains(to_var(arg)->get_idx()); push_back(bound ? AD_BOUND : AD_FREE); @@ -57,17 +56,8 @@ namespace datalog { std::string res; const_iterator eit = begin(); const_iterator eend = end(); - for(; eit!=eend; ++eit) { - switch(*eit) { - case AD_BOUND: - res+='b'; - break; - case AD_FREE: - res+='f'; - break; - default: - UNREACHABLE(); - } + for (; eit != eend; ++eit) { + res += (*eit == AD_BOUND)?'b':'f'; } return res; } @@ -75,14 +65,13 @@ namespace datalog { unsigned get_bound_arg_count(app * lit, const var_idx_set & bound_vars) { unsigned res = 0; unsigned n = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { - continue; + if (!is_var(arg) || bound_vars.contains(to_var(arg)->get_idx())) { + SASSERT(is_var(arg) || is_app(arg)); + SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0); + res++; } - SASSERT(is_var(arg) || is_app(arg)); - SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0); - res++; } return res; } @@ -91,10 +80,10 @@ namespace datalog { func_decl * pred = lit->get_decl(); float res = 1; unsigned n = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { - res*=m_context.get_sort_size_estimate(pred->get_domain(i)); + if (is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { + res *= m_context.get_sort_size_estimate(pred->get_domain(i)); } //res-=1; } @@ -111,22 +100,22 @@ namespace datalog { float best_cost; int candidate_index = -1; unsigned n = cont.size(); - for(unsigned i=0; iget_tail(cont[i]); unsigned bound_cnt = get_bound_arg_count(lit, bound_vars); - if(bound_cnt==0) { + if (bound_cnt==0) { continue; } float cost = get_unbound_cost(lit, bound_vars); - if(candidate_index==-1 || cost(n-1)) { + if (candidate_index != static_cast(n-1)) { std::swap(cont[candidate_index], cont[n-1]); } unsigned res = cont.back(); @@ -137,12 +126,12 @@ namespace datalog { app * mk_magic_sets::adorn_literal(app * lit, const var_idx_set & bound_vars) { SASSERT(!m_extentional.contains(lit->get_decl())); func_decl * old_pred = lit->get_decl(); - SASSERT(m_manager.is_bool(old_pred->get_range())); + SASSERT(m.is_bool(old_pred->get_range())); adornment_desc adn(old_pred); adn.m_adornment.populate(lit, bound_vars); adornment_map::entry * e = m_adorned_preds.insert_if_not_there2(adn, 0); func_decl * new_pred = e->get_data().m_value; - if(new_pred==0) { + if (new_pred==0) { std::string suffix = "ad_"+adn.m_adornment.to_string(); new_pred = m_context.mk_fresh_head_predicate( old_pred->get_name(), symbol(suffix.c_str()), @@ -152,34 +141,34 @@ namespace datalog { m_todo.push_back(adn); m_adornments.insert(new_pred, adn.m_adornment); } - app * res = m_manager.mk_app(new_pred, lit->get_args()); + app * res = m.mk_app(new_pred, lit->get_args()); m_pinned.push_back(res); return res; } app * mk_magic_sets::create_magic_literal(app * l) { func_decl * l_pred = l->get_decl(); - SASSERT(m_manager.is_bool(l_pred->get_range())); + SASSERT(m.is_bool(l_pred->get_range())); pred_adornment_map::obj_map_entry * ae = m_adornments.find_core(l_pred); SASSERT(ae); const adornment & adn = ae->get_data().m_value; unsigned l_arity = l->get_num_args(); ptr_vector bound_args; - for(unsigned i=0; iget_arg(i)); } } pred2pred::obj_map_entry * e = m_magic_preds.insert_if_not_there2(l_pred, 0); func_decl * mag_pred = e->get_data().m_value; - if(mag_pred==0) { + if (mag_pred==0) { unsigned mag_arity = bound_args.size(); ptr_vector mag_domain; - for(unsigned i=0; iget_domain(i)); } } @@ -190,12 +179,12 @@ namespace datalog { e->get_data().m_value = mag_pred; } - app * res = m_manager.mk_app(mag_pred, bound_args.c_ptr()); + app * res = m.mk_app(mag_pred, bound_args.c_ptr()); m_pinned.push_back(res); return res; } - void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated) { + void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result) { //TODO: maybe include relevant interpreted predicates from the original rule ptr_vector new_tail; svector negations; @@ -204,26 +193,26 @@ namespace datalog { negations.push_back(false); negations.append(tail_cnt, negated); - for(unsigned i=0; iget_decl())) { + for (unsigned i=0; iget_decl())) { continue; } app * mag_head = create_magic_literal(tail[i]); rule * r = m_context.get_rule_manager().mk(mag_head, i+1, new_tail.c_ptr(), negations.c_ptr()); TRACE("dl", r->display(m_context,tout); ); - m_rules.push_back(r); + result.add_rule(r); } } - void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r) { + void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r, rule_set& result) { app * head = r->get_head(); unsigned head_len = head->get_num_args(); SASSERT(head_len==head_adornment.size()); var_idx_set bound_vars; - for(unsigned i=0; iget_arg(i); - if(head_adornment[i]==AD_BOUND && is_var(arg)) { + if (head_adornment[i]==AD_BOUND && is_var(arg)) { bound_vars.insert(to_var(arg)->get_idx()); } } @@ -231,9 +220,9 @@ namespace datalog { unsigned processed_tail_len = r->get_uninterpreted_tail_size(); unsigned_vector exten_tails; unsigned_vector inten_tails; - for(unsigned i=0; iget_tail(i); - if(m_extentional.contains(t->get_decl())) { + if (m_extentional.contains(t->get_decl())) { exten_tails.push_back(i); } else { @@ -243,17 +232,17 @@ namespace datalog { ptr_vector new_tail; svector negations; - while(new_tail.size()!=processed_tail_len) { + while (new_tail.size()!=processed_tail_len) { bool intentional = false; int curr_index = pop_bound(exten_tails, r, bound_vars); - if(curr_index==-1) { + if (curr_index==-1) { curr_index = pop_bound(inten_tails, r,bound_vars); - if(curr_index!=-1) { + if (curr_index!=-1) { intentional = true; } } - if(curr_index==-1) { - if(!exten_tails.empty()) { + if (curr_index==-1) { + if (!exten_tails.empty()) { curr_index = exten_tails.back(); exten_tails.pop_back(); } @@ -266,24 +255,24 @@ namespace datalog { } SASSERT(curr_index!=-1); app * curr = r->get_tail(curr_index); - if(intentional) { + if (intentional) { curr = adorn_literal(curr, bound_vars); } new_tail.push_back(curr); negations.push_back(r->is_neg_tail(curr_index)); - collect_vars(m_manager, curr, bound_vars); + bound_vars |= rm.collect_vars(curr); } func_decl * new_head_pred; VERIFY( m_adorned_preds.find(adornment_desc(head->get_decl(), head_adornment), new_head_pred) ); - app * new_head = m_manager.mk_app(new_head_pred, head->get_args()); + app * new_head = m.mk_app(new_head_pred, head->get_args()); SASSERT(new_tail.size()==r->get_uninterpreted_tail_size()); - create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr()); + create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr(), result); unsigned tail_len = r->get_tail_size(); - for(unsigned i=processed_tail_len; iget_tail(i)); negations.push_back(r->is_neg_tail(i)); } @@ -292,43 +281,51 @@ namespace datalog { negations.push_back(false); rule * nr = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr()); - m_rules.push_back(nr); + result.add_rule(nr); nr->set_accounting_parent_object(m_context, r); } - void mk_magic_sets::create_transfer_rule(const adornment_desc & d) { - func_decl * adn_pred; - TRUSTME( m_adorned_preds.find(d, adn_pred) ); + void mk_magic_sets::create_transfer_rule(const adornment_desc & d, rule_set& result) { + func_decl * adn_pred = m_adorned_preds.find(d); unsigned arity = adn_pred->get_arity(); - SASSERT(arity==d.m_pred->get_arity()); + SASSERT(arity == d.m_pred->get_arity()); ptr_vector args; - for(unsigned i=0; iget_domain(i))); + for (unsigned i=0; iget_domain(i))); } - app * lit = m_manager.mk_app(d.m_pred, args.c_ptr()); - app * adn_lit = m_manager.mk_app(adn_pred, args.c_ptr()); + app * lit = m.mk_app(d.m_pred, args.c_ptr()); + app * adn_lit = m.mk_app(adn_pred, args.c_ptr()); app * mag_lit = create_magic_literal(adn_lit); app * tail[] = {lit, mag_lit}; rule * r = m_context.get_rule_manager().mk(adn_lit, 2, tail, 0); - m_rules.push_back(r); + result.add_rule(r); } rule_set * mk_magic_sets::operator()(rule_set const & source) { - SASSERT(!m_context.get_model_converter()); + + if (!m_context.magic_sets_for_queries()) { + return 0; + } + SASSERT(source.contains(m_goal)); + SASSERT(source.get_predicate_rules(m_goal).size() == 1); + + app * goal_head = source.get_predicate_rules(m_goal)[0]->get_head(); + unsigned init_rule_cnt = source.get_num_rules(); { func_decl_set intentional; - for(unsigned i=0; iget_head()->get_decl()); + for (unsigned i=0; iget_decl(); + intentional.insert(pred); } //now we iterate through all predicates and collect the set of extentional ones const rule_dependencies * deps; rule_dependencies computed_deps(m_context); - if(source.is_closed()) { + if (source.is_closed()) { deps = &source.get_dependencies(); } else { @@ -337,9 +334,9 @@ namespace datalog { } rule_dependencies::iterator it = deps->begin(); rule_dependencies::iterator end = deps->end(); - for(; it!=end; ++it) { + for (; it!=end; ++it) { func_decl * pred = it->m_key; - if(intentional.contains(pred)) { + if (intentional.contains(pred)) { continue; } SASSERT(it->m_value->empty());//extentional predicates have no dependency @@ -347,48 +344,39 @@ namespace datalog { } } - SASSERT(m_rules.empty()); - - app * goal_head = m_goal_rule->get_head(); //adornment goal_adn; //goal_adn.populate(goal_head, ); var_idx_set empty_var_idx_set; adorn_literal(goal_head, empty_var_idx_set); - while(!m_todo.empty()) { + rule_set * result = alloc(rule_set, m_context); + result->inherit_predicates(source); + + while (!m_todo.empty()) { adornment_desc task = m_todo.back(); m_todo.pop_back(); const rule_vector & pred_rules = source.get_predicate_rules(task.m_pred); rule_vector::const_iterator it = pred_rules.begin(); rule_vector::const_iterator end = pred_rules.end(); - for(; it!=end; ++it) { + for (; it != end; ++it) { rule * r = *it; - transform_rule(task.m_adornment, r); + transform_rule(task.m_adornment, r, *result); } - if(!m_context.get_rel_context().get_relation(task.m_pred).empty()) { + if (!m_context.get_rel_context().get_relation(task.m_pred).empty()) { //we need a rule to copy facts that are already in a relation into the adorned //relation (since out intentional predicates can have facts, not only rules) - create_transfer_rule(task); + create_transfer_rule(task, *result); } } app * adn_goal_head = adorn_literal(goal_head, empty_var_idx_set); app * mag_goal_head = create_magic_literal(adn_goal_head); SASSERT(mag_goal_head->is_ground()); - //SASSERT(is_fact(m_manager, mag_goal_head)); - //m_context.add_fact(mag_goal_head); rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, 0, 0); - m_rules.push_back(mag_goal_rule); + result->add_rule(mag_goal_rule); rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, 0); - m_rules.push_back(back_to_goal_rule); - - rule_set * result = static_cast(0); - result = alloc(rule_set, m_context); - unsigned fin_rule_cnt = m_rules.size(); - for(unsigned i=0; iadd_rule(m_rules.get(i)); - } + result->add_rule(back_to_goal_rule); return result; } }; diff --git a/src/muz_qe/dl_mk_magic_sets.h b/src/muz_qe/dl_mk_magic_sets.h index 2dc91c7e8..3496a5967 100644 --- a/src/muz_qe/dl_mk_magic_sets.h +++ b/src/muz_qe/dl_mk_magic_sets.h @@ -47,6 +47,11 @@ namespace datalog { AD_BOUND }; + struct a_flag_hash { + typedef a_flag data; + unsigned operator()(a_flag x) const { return x; } + }; + struct adornment : public svector { void populate(app * lit, const var_idx_set & bound_vars); @@ -71,7 +76,7 @@ namespace datalog { return m_pred==o.m_pred && m_adornment==o.m_adornment; } unsigned hash() const { - return m_pred->hash()^int_vector_hash(m_adornment); + return m_pred->hash()^svector_hash()(m_adornment); } }; @@ -88,21 +93,21 @@ namespace datalog { typedef obj_map pred_adornment_map; typedef obj_map pred2pred; - context & m_context; - ast_manager & m_manager; - rule_ref_vector m_rules; - ast_ref_vector m_pinned; - rule_ref m_goal_rule; + context & m_context; + ast_manager & m; + rule_manager& rm; + ast_ref_vector m_pinned; /** \brief Predicates from the original set that appear in a head of a rule */ - func_decl_set m_extentional; + func_decl_set m_extentional; //adornment_set m_processed; vector m_todo; - adornment_map m_adorned_preds; - pred_adornment_map m_adornments; - pred2pred m_magic_preds; + adornment_map m_adorned_preds; + pred_adornment_map m_adornments; + pred2pred m_magic_preds; + func_decl_ref m_goal; void reset(); @@ -110,16 +115,16 @@ namespace datalog { int pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars); app * create_magic_literal(app * l); - void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated); + void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result); app * adorn_literal(app * lit, const var_idx_set & bound_vars); - void transform_rule(const adornment & head_adornment, rule * r); - void create_transfer_rule(const adornment_desc & d); + void transform_rule(const adornment & head_adornment, rule * r, rule_set& result); + void create_transfer_rule(const adornment_desc & d, rule_set& result); public: /** \brief Create magic sets rule transformer for \c goal_rule. When applying the transformer, the \c goal_rule must be present in the \c rule_set that is being transformed. */ - mk_magic_sets(context & ctx, rule * goal_rule); + mk_magic_sets(context & ctx, func_decl* goal); rule_set * operator()(rule_set const & source); }; diff --git a/src/muz_qe/dl_mk_partial_equiv.cpp b/src/muz_qe/dl_mk_partial_equiv.cpp index 35f8ff8df..5ba1e71dd 100644 --- a/src/muz_qe/dl_mk_partial_equiv.cpp +++ b/src/muz_qe/dl_mk_partial_equiv.cpp @@ -23,7 +23,7 @@ Revision History: namespace datalog { bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) { - func_decl* p = r->get_head()->get_decl(); + func_decl* p = r->get_decl(); return p->get_arity() == 2 && p->get_domain(0) == p->get_domain(1) && @@ -38,7 +38,7 @@ namespace datalog { bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) { - func_decl* p = r->get_head()->get_decl(); + func_decl* p = r->get_decl(); if (p->get_arity() != 2 || p->get_domain(0) != p->get_domain(1) || r->get_tail_size() != 2 || @@ -144,6 +144,7 @@ namespace datalog { dealloc(res); return 0; } + res->inherit_predicates(source); return res; } diff --git a/src/muz_qe/dl_mk_quantifier_abstraction.cpp b/src/muz_qe/dl_mk_quantifier_abstraction.cpp index d4abb1ffc..68f5bc3a3 100644 --- a/src/muz_qe/dl_mk_quantifier_abstraction.cpp +++ b/src/muz_qe/dl_mk_quantifier_abstraction.cpp @@ -147,9 +147,10 @@ namespace datalog { mk_quantifier_abstraction::~mk_quantifier_abstraction() { } - func_decl* mk_quantifier_abstraction::declare_pred(func_decl* old_p) { + func_decl* mk_quantifier_abstraction::declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p) { - if (m_ctx.is_output_predicate(old_p)) { + if (rules.is_output_predicate(old_p)) { + dst.inherit_predicate(rules, old_p, old_p); return 0; } @@ -210,8 +211,8 @@ namespace datalog { return new_p; } - app_ref mk_quantifier_abstraction::mk_head(app* p, unsigned idx) { - func_decl* new_p = declare_pred(p->get_decl()); + app_ref mk_quantifier_abstraction::mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx) { + func_decl* new_p = declare_pred(rules, dst, p->get_decl()); if (!new_p) { return app_ref(p, m); } @@ -239,9 +240,9 @@ namespace datalog { return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); } - app_ref mk_quantifier_abstraction::mk_tail(app* p) { + app_ref mk_quantifier_abstraction::mk_tail(rule_set const& rules, rule_set& dst, app* p) { func_decl* old_p = p->get_decl(); - func_decl* new_p = declare_pred(old_p); + func_decl* new_p = declare_pred(rules, dst, old_p); if (!new_p) { return app_ref(p, m); } @@ -332,18 +333,17 @@ namespace datalog { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { - tail.push_back(mk_tail(r.get_tail(j))); + tail.push_back(mk_tail(source, *result, r.get_tail(j))); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); } - head = mk_head(r.get_head(), cnt); + head = mk_head(source, *result, r.get_head(), cnt); fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), head); rule_ref_vector added_rules(rm); proof_ref pr(m); - rm.mk_rule(fml, pr, added_rules); - result->add_rules(added_rules.size(), added_rules.c_ptr()); - TRACE("dl", added_rules.back()->display(m_ctx, tout);); + rm.mk_rule(fml, pr, *result); + TRACE("dl", result->last()->display(m_ctx, tout);); } // proof converter: proofs are not necessarily preserved using this transformation. diff --git a/src/muz_qe/dl_mk_quantifier_abstraction.h b/src/muz_qe/dl_mk_quantifier_abstraction.h index c7e6c6bb4..0e65c54f7 100644 --- a/src/muz_qe/dl_mk_quantifier_abstraction.h +++ b/src/muz_qe/dl_mk_quantifier_abstraction.h @@ -43,9 +43,9 @@ namespace datalog { obj_map m_old2new; qa_model_converter* m_mc; - func_decl* declare_pred(func_decl* old_p); - app_ref mk_head(app* p, unsigned idx); - app_ref mk_tail(app* p); + func_decl* declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p); + app_ref mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx); + app_ref mk_tail(rule_set const& rules, rule_set& dst, app* p); expr* mk_select(expr* a, unsigned num_args, expr* const* args); public: diff --git a/src/muz_qe/dl_mk_quantifier_instantiation.cpp b/src/muz_qe/dl_mk_quantifier_instantiation.cpp index 1d80d9b77..ddbddca01 100644 --- a/src/muz_qe/dl_mk_quantifier_instantiation.cpp +++ b/src/muz_qe/dl_mk_quantifier_instantiation.cpp @@ -232,20 +232,20 @@ namespace datalog { fml = m.mk_implies(fml, r.get_head()); TRACE("dl", r.display(m_ctx, tout); tout << mk_pp(fml, m) << "\n";); - rule_ref_vector added_rules(rm); + rule_set added_rules(m_ctx); proof_ref pr(m); rm.mk_rule(fml, pr, added_rules); if (r.get_proof()) { // use def-axiom to encode that new rule is a weakening of the original. proof* p1 = r.get_proof(); - for (unsigned i = 0; i < added_rules.size(); ++i) { - rule* r2 = added_rules[i].get(); + for (unsigned i = 0; i < added_rules.get_num_rules(); ++i) { + rule* r2 = added_rules.get_rule(i); r2->to_formula(fml); pr = m.mk_modus_ponens(m.mk_def_axiom(m.mk_implies(m.get_fact(p1), fml)), p1); r2->set_proof(m, pr); } } - rules.add_rules(added_rules.size(), added_rules.c_ptr()); + rules.add_rules(added_rules); } rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) { @@ -286,7 +286,10 @@ namespace datalog { // model convertion: identity function. - if (!instantiated) { + if (instantiated) { + result->inherit_predicates(source); + } + else { dealloc(result); result = 0; } diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz_qe/dl_mk_rule_inliner.cpp index 91cfbe3fc..4afc1d323 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz_qe/dl_mk_rule_inliner.cpp @@ -230,10 +230,10 @@ namespace datalog { } } - bool mk_rule_inliner::inlining_allowed(func_decl * pred) + bool mk_rule_inliner::inlining_allowed(rule_set const& source, func_decl * pred) { if (//these three conditions are important for soundness - m_context.is_output_predicate(pred) || + source.is_output_predicate(pred) || m_preds_with_facts.contains(pred) || m_preds_with_neg_occurrence.contains(pred) || //this condition is used for breaking of cycles among inlined rules @@ -260,7 +260,7 @@ namespace datalog { unsigned rcnt = orig.get_num_rules(); for (unsigned i=0; iget_decl())) { + if (inlining_allowed(orig, r->get_decl())) { res->add_rule(r); } } @@ -324,7 +324,7 @@ namespace datalog { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); - if (!inlining_allowed(tail_pred)) { + if (!inlining_allowed(orig, tail_pred)) { continue; } unsigned tail_pred_head_cnt = m_head_pred_ctr.get(tail_pred); @@ -359,7 +359,7 @@ namespace datalog { func_decl * head_pred = r->get_decl(); - if (inlining_allowed(head_pred)) { + if (inlining_allowed(orig, head_pred)) { //we have already processed inlined rules continue; } @@ -368,7 +368,7 @@ namespace datalog { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); - if (!inlining_allowed(pred)) { + if (!inlining_allowed(orig, pred)) { continue; } if (m_head_pred_ctr.get(pred)<=1) { @@ -417,14 +417,14 @@ namespace datalog { const rule_vector& pred_rules = candidate_inlined_set->get_predicate_rules(pred); rule_vector::const_iterator iend = pred_rules.end(); for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { - transform_rule(*iit, m_inlined_rules); + transform_rule(orig, *iit, m_inlined_rules); } } TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); } - bool mk_rule_inliner::transform_rule(rule * r0, rule_set& tgt) { + bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) { bool modified = false; rule_ref_vector todo(m_rm); todo.push_back(r0); @@ -436,7 +436,7 @@ namespace datalog { unsigned i = 0; - for (; i < pt_len && !inlining_allowed(r->get_decl(i)); ++i) {}; + for (; i < pt_len && !inlining_allowed(orig, r->get_decl(i)); ++i) {}; SASSERT(!has_quantifier(*r.get())); @@ -478,12 +478,12 @@ namespace datalog { // this relation through inlining, // so we don't add its rules to the result - something_done |= !inlining_allowed(pred) && transform_rule(r, tgt); + something_done |= !inlining_allowed(orig, pred) && transform_rule(orig, r, tgt); } if (something_done && m_mc) { for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { - if (inlining_allowed((*rit)->get_decl())) { + if (inlining_allowed(orig, (*rit)->get_decl())) { datalog::del_rule(m_mc, **rit); } } @@ -505,9 +505,6 @@ namespace datalog { unsigned head_arity = head_pred->get_arity(); - //var_idx_set head_vars; - //var_idx_set same_strat_vars; - //collect_vars(m, r->get_head(), head_vars); unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti=0; tiget_head(), same_strat_vars); if (pred->get_arity()>head_arity || (pred->get_arity()==head_arity && pred->get_id()>=head_pred->get_id()) ) { return false; @@ -667,7 +663,7 @@ namespace datalog { return et->get_data().m_value; } - void mk_rule_inliner::add_rule(rule* r, unsigned i) { + void mk_rule_inliner::add_rule(rule_set const& source, rule* r, unsigned i) { svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); app* head = r->get_head(); @@ -676,7 +672,7 @@ namespace datalog { m_head_index.insert(head); m_pinned.push_back(r); - if (m_context.is_output_predicate(headd) || + if (source.is_output_predicate(headd) || m_preds_with_facts.contains(headd)) { can_remove.set(i, false); TRACE("dl", output_predicate(m_context, head, tout << "cannot remove: " << i << " "); tout << "\n";); @@ -692,7 +688,7 @@ namespace datalog { tl_sz == 1 && r->get_positive_tail_size() == 1 && !m_preds_with_facts.contains(r->get_decl(0)) - && !m_context.is_output_predicate(r->get_decl(0)); + && !source.is_output_predicate(r->get_decl(0)); can_expand.set(i, can_exp); } @@ -710,7 +706,6 @@ namespace datalog { #define PRT(_x_) ((_x_)?"T":"F") bool mk_rule_inliner::inline_linear(scoped_ptr& rules) { - scoped_ptr res = alloc(rule_set, m_context); bool done_something = false; unsigned sz = rules->get_num_rules(); @@ -731,7 +726,7 @@ namespace datalog { svector& can_expand = m_head_visitor.can_expand(); for (unsigned i = 0; i < sz; ++i) { - add_rule(acc[i].get(), i); + add_rule(*rules, acc[i].get(), i); } // initialize substitution. @@ -808,7 +803,7 @@ namespace datalog { TRACE("dl", r->display(m_context, tout); r2->display(m_context, tout); rl_res->display(m_context, tout); ); del_rule(r, i); - add_rule(rl_res.get(), i); + add_rule(*rules, rl_res.get(), i); r = rl_res; @@ -828,13 +823,15 @@ namespace datalog { } } if (done_something) { - rules = alloc(rule_set, m_context); + scoped_ptr res = alloc(rule_set, m_context); for (unsigned i = 0; i < sz; ++i) { if (valid.get(i)) { - rules->add_rule(acc[i].get()); + res->add_rule(acc[i].get()); } } - TRACE("dl", rules->display(tout);); + res->inherit_predicates(*rules); + TRACE("dl", res->display(tout);); + rules = res.detach(); } return done_something; } @@ -871,8 +868,14 @@ namespace datalog { // 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); + } + else { + res = alloc(rule_set, source); } if (m_context.get_params().inline_linear() && inline_linear(res)) { diff --git a/src/muz_qe/dl_mk_rule_inliner.h b/src/muz_qe/dl_mk_rule_inliner.h index 5ef8db7eb..3a933f990 100644 --- a/src/muz_qe/dl_mk_rule_inliner.h +++ b/src/muz_qe/dl_mk_rule_inliner.h @@ -129,7 +129,7 @@ namespace datalog { bool try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res); - bool inlining_allowed(func_decl * pred); + bool inlining_allowed(rule_set const& orig, func_decl * pred); void count_pred_occurrences(rule_set const & orig); @@ -143,7 +143,7 @@ namespace datalog { bool forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules); /** Return true if the rule was modified */ - bool transform_rule(rule * r, rule_set& tgt); + bool transform_rule(rule_set const& orig, rule * r, rule_set& tgt); /** Return true if some transformation was performed */ bool transform_rules(const rule_set & orig, rule_set & tgt); @@ -174,7 +174,7 @@ namespace datalog { */ bool inline_linear(scoped_ptr& rules); - void add_rule(rule* r, unsigned i); + void add_rule(rule_set const& rule_set, rule* r, unsigned i); void del_rule(rule* r, unsigned i); public: diff --git a/src/muz_qe/dl_mk_similarity_compressor.cpp b/src/muz_qe/dl_mk_similarity_compressor.cpp index 9855196c9..b600811f0 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.cpp +++ b/src/muz_qe/dl_mk_similarity_compressor.cpp @@ -23,16 +23,17 @@ Revision History: namespace datalog { - mk_similarity_compressor::mk_similarity_compressor(context & ctx, unsigned threshold_count) : - plugin(5000), - m_context(ctx), - m_manager(ctx.get_manager()), - m_threshold_count(threshold_count), - m_result_rules(ctx.get_rule_manager()), - m_pinned(m_manager) { - SASSERT(threshold_count>1); + mk_similarity_compressor::mk_similarity_compressor(context & ctx) : + plugin(5000), + m_context(ctx), + m_manager(ctx.get_manager()), + m_threshold_count(ctx.similarity_compressor_threshold()), + m_result_rules(ctx.get_rule_manager()), + m_modified(false), + m_pinned(m_manager) { + SASSERT(m_threshold_count>1); } - + void mk_similarity_compressor::reset() { m_rules.reset(); m_result_rules.reset(); @@ -43,10 +44,10 @@ namespace datalog { Allows to traverse head and positive tails in a single for loop starting from -1 */ static app * get_by_tail_index(rule * r, int idx) { - if(idx==-1) { + if (idx < 0) { return r->get_head(); } - SASSERT(idx(r->get_positive_tail_size())); + SASSERT(idx < static_cast(r->get_positive_tail_size())); return r->get_tail(idx); } @@ -55,19 +56,25 @@ namespace datalog { return (a>b) ? 1 : ( (a==b) ? 0 : -1); } + template + static int aux_compare(T* a, T* b); + static int compare_var_args(app* t1, app* t2) { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); - for(unsigned i=0; iget_arg(i); expr * a2 = t2->get_arg(i); - res = aux_compare(is_var(a1), is_var(a2)); - if(res!=0) { return res; } - if(is_var(a1)) { + if (res != 0) { + return res; + } + if (is_var(a1)) { res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx()); - if(res!=0) { return res; } + if (res != 0) { + return res; + } } } return 0; @@ -77,16 +84,16 @@ namespace datalog { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); - for(unsigned i=0; iget_arg(i))) { - SASSERT(t1->get_arg(i)==t2->get_arg(i)); + for (unsigned i=0; iget_arg(i))) { + SASSERT(t1->get_arg(i) == t2->get_arg(i)); continue; } - if((skip_countdown--)==0) { + if ((skip_countdown--) == 0) { continue; } - res = aux_compare(t1->get_arg(i), t2->get_arg(i)); - if(res!=0) { return res; } + res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id()); + if (res!=0) { return res; } } return 0; } @@ -100,26 +107,26 @@ namespace datalog { */ static int rough_compare(rule * r1, rule * r2) { int res = aux_compare(r1->get_tail_size(), r2->get_tail_size()); - if(res!=0) { return res; } + if (res!=0) { return res; } res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); - if(res!=0) { return res; } + if (res!=0) { return res; } res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size()); - if(res!=0) { return res; } + if (res!=0) { return res; } int pos_tail_sz = r1->get_positive_tail_size(); - for(int i=-1; iget_decl(), t2->get_decl()); - if(res!=0) { return res; } + res = aux_compare(t1->get_decl()->get_id(), t2->get_decl()->get_id()); + if (res!=0) { return res; } res = compare_var_args(t1, t2); - if(res!=0) { return res; } + if (res!=0) { return res; } } unsigned tail_sz = r1->get_tail_size(); - for(unsigned i=pos_tail_sz; iget_tail(i), r2->get_tail(i)); - if(res!=0) { return res; } + for (unsigned i=pos_tail_sz; iget_tail(i)->get_id(), r2->get_tail(i)->get_id()); + if (res!=0) { return res; } } return 0; @@ -132,9 +139,9 @@ namespace datalog { static int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { SASSERT(rough_compare(r1, r2)==0); int pos_tail_sz = r1->get_positive_tail_size(); - for(int i=-1; iget_num_args(); - for(unsigned i=0; iget_arg(i))) { + for (unsigned i=0; iget_arg(i))) { continue; } res.push_back(const_info(tail_index, i)); @@ -178,7 +185,7 @@ namespace datalog { static void collect_const_indexes(rule * r, info_vector & res) { collect_const_indexes(r->get_head(), -1, res); unsigned pos_tail_sz = r->get_positive_tail_size(); - for(unsigned i=0; iget_tail(i), i, res); } } @@ -187,9 +194,9 @@ namespace datalog { static void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { unsigned const_cnt = const_infos.size(); tgt.reset(); - for(unsigned i=0; iget_arg(const_infos[i].arg_index())); - if(vals[i]!=val) { + if (vals[i]!=val) { vals[i] = 0; } } } unsigned removed_cnt = 0; - for(unsigned i=0; i possible_parents(const_cnt); - for(unsigned i=1; iget_head()->get_num_args() - count_variable_arguments(r->get_head()); unsigned pos_tail_sz = r->get_positive_tail_size(); - for(unsigned i=0; iget_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i)); } return res; @@ -313,7 +320,7 @@ namespace datalog { static bool initial_comparator(rule * r1, rule * r2) { int res = rough_compare(r1, r2); - if(res!=0) { return res>0; } + if (res!=0) { return res>0; } return total_compare(r1, r2)>0; } @@ -348,7 +355,7 @@ namespace datalog { ptr_vector aux_domain; collect_orphan_sorts(r, const_infos, aux_domain); - func_decl* head_pred = r->get_head()->get_decl(); + func_decl* head_pred = r->get_decl(); symbol const& name_prefix = head_pred->get_name(); std::string name_suffix = "sc_" + to_string(const_cnt); func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()), @@ -357,7 +364,7 @@ namespace datalog { relation_fact val_fact(m_manager, const_cnt); rule_vector::iterator it = first; - for(; it!=after_last; ++it) { + for (; it!=after_last; ++it) { collect_orphan_consts(*it, const_infos, val_fact); m_context.add_fact(aux_pred, val_fact); } @@ -367,7 +374,7 @@ namespace datalog { ptr_vector new_tail; svector new_negs; unsigned tail_sz = r->get_tail_size(); - for(unsigned i=0; iget_tail(i)); new_negs.push_back(r->is_neg_tail(i)); } @@ -375,7 +382,7 @@ namespace datalog { rule_counter ctr; ctr.count_rule_vars(m_manager, r); unsigned max_var_idx, new_var_idx_base; - if(ctr.get_max_positive(max_var_idx)) { + if (ctr.get_max_positive(max_var_idx)) { new_var_idx_base = max_var_idx+1; } else { @@ -387,15 +394,15 @@ namespace datalog { unsigned aux_column_index = 0; - for(unsigned i=0; i mod_args(mod_tail->get_num_args(), mod_tail->get_args()); - for(; im_threshold_count) { + if (it==after_last || !comparator.eq(*prev, *it)) { + if (grp_size>m_threshold_count) { merge_class(grp_begin, it); //group was processed, so we remove it from the class - if(it==after_last) { + if (it==after_last) { after_last=grp_begin; it=after_last; } @@ -484,9 +491,9 @@ namespace datalog { //TODO: compress also rules with pairs (or tuples) of equal constants #if 1 - if(const_cnt>0) { + if (const_cnt>0 && !source.is_output_predicate((*first)->get_decl())) { unsigned rule_cnt = static_cast(after_last-first); - if(rule_cnt>m_threshold_count) { + if (rule_cnt>m_threshold_count) { merge_class(first, after_last); return; } @@ -495,7 +502,7 @@ namespace datalog { //put rules which weren't merged into result rule_vector::iterator it = first; - for(; it!=after_last; ++it) { + for (; it!=after_last; ++it) { m_result_rules.push_back(*it); } } @@ -505,7 +512,7 @@ namespace datalog { m_modified = false; unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); - for(unsigned i=0; i(0); - if(m_modified) { + if (m_modified) { result = alloc(rule_set, m_context); unsigned fin_rule_cnt = m_result_rules.size(); - for(unsigned i=0; iadd_rule(m_result_rules.get(i)); } + result->inherit_predicates(source); } reset(); return result; diff --git a/src/muz_qe/dl_mk_similarity_compressor.h b/src/muz_qe/dl_mk_similarity_compressor.h index 6e0ca9db5..34b76e7e1 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.h +++ b/src/muz_qe/dl_mk_similarity_compressor.h @@ -63,11 +63,11 @@ namespace datalog { ast_ref_vector m_pinned; void merge_class(rule_vector::iterator first, rule_vector::iterator after_last); - void process_class(rule_vector::iterator first, rule_vector::iterator after_last); + void process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last); void reset(); public: - mk_similarity_compressor(context & ctx, unsigned threshold_count); + mk_similarity_compressor(context & ctx); rule_set * operator()(rule_set const & source); }; diff --git a/src/muz_qe/dl_mk_simple_joins.cpp b/src/muz_qe/dl_mk_simple_joins.cpp index 3a3f96acf..990125475 100644 --- a/src/muz_qe/dl_mk_simple_joins.cpp +++ b/src/muz_qe/dl_mk_simple_joins.cpp @@ -29,7 +29,8 @@ namespace datalog { mk_simple_joins::mk_simple_joins(context & ctx): plugin(1000), - m_context(ctx) { + m_context(ctx), + rm(ctx.get_rule_manager()) { } class join_planner { @@ -89,7 +90,7 @@ namespace datalog { m_consumers++; } if(m_stratified) { - unsigned head_stratum = pl.get_stratum(r->get_head()->get_decl()); + unsigned head_stratum = pl.get_stratum(r->get_decl()); SASSERT(head_stratum>=m_src_stratum); if(head_stratum==m_src_stratum) { m_stratified = false; @@ -120,6 +121,7 @@ namespace datalog { context & m_context; ast_manager & m; + rule_manager & rm; var_subst & m_var_subst; rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels @@ -130,10 +132,13 @@ namespace datalog { ptr_hashtable, ptr_eq > m_modified_rules; ast_ref_vector m_pinned; + mutable ptr_vector m_vars; public: join_planner(context & ctx, rule_set & rs_aux_copy) - : m_context(ctx), m(ctx.get_manager()), m_var_subst(ctx.get_var_subst()), + : m_context(ctx), m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_var_subst(ctx.get_var_subst()), m_rs_aux_copy(rs_aux_copy), m_introduced_rules(ctx.get_rule_manager()), m_pinned(ctx.get_manager()) @@ -175,9 +180,7 @@ namespace datalog { unsigned max_var_idx = 0; { - var_idx_set orig_var_set; - collect_vars(m, t1, orig_var_set); - collect_vars(m, t2, orig_var_set); + var_idx_set& orig_var_set = rm.collect_vars(t1, t2); var_idx_set::iterator ovit = orig_var_set.begin(); var_idx_set::iterator ovend = orig_var_set.end(); for(; ovit!=ovend; ++ovit) { @@ -323,14 +326,13 @@ namespace datalog { } for(unsigned i=0; iget_tail(i); - var_idx_set t1_vars; - collect_vars(m, t1, t1_vars); + var_idx_set t1_vars = rm.collect_vars(t1); counter.count_vars(m, t1, -1); //temporarily remove t1 variables from counter for(unsigned j=i+1; jget_tail(j); counter.count_vars(m, t2, -1); //temporarily remove t2 variables from counter - var_idx_set scope_vars(t1_vars); - collect_vars(m, t2, scope_vars); + var_idx_set scope_vars = rm.collect_vars(t2); + scope_vars |= t1_vars; var_idx_set non_local_vars; counter.collect_positive(non_local_vars); counter.count_vars(m, t2, 1); //restore t2 variables in counter @@ -383,7 +385,7 @@ namespace datalog { rule * one_parent = inf.m_rules.back(); - func_decl* parent_head = one_parent->get_head()->get_decl(); + func_decl* parent_head = one_parent->get_decl(); const char * one_parent_name = parent_head->get_name().bare_str(); std::string parent_name; if(inf.m_rules.size()>1) { @@ -472,8 +474,7 @@ namespace datalog { while(!added_tails.empty()) { app * a_tail = added_tails.back(); //added tail - var_idx_set a_tail_vars; - collect_vars(m, a_tail, a_tail_vars); + var_idx_set a_tail_vars = rm.collect_vars(a_tail); counter.count_vars(m, a_tail, -1); //temporarily remove a_tail variables from counter for(unsigned i=0; iinherit_predicates(source); return result; } }; rule_set * mk_simple_joins::operator()(rule_set const & source) { rule_set rs_aux_copy(m_context); - rs_aux_copy.add_rules(source); + rs_aux_copy.replace_rules(source); if(!rs_aux_copy.is_closed()) { rs_aux_copy.close(); } diff --git a/src/muz_qe/dl_mk_simple_joins.h b/src/muz_qe/dl_mk_simple_joins.h index 89832626f..36eb08dd5 100644 --- a/src/muz_qe/dl_mk_simple_joins.h +++ b/src/muz_qe/dl_mk_simple_joins.h @@ -49,7 +49,8 @@ namespace datalog { We say that a rule containing C_i's is a rule with a "big tail". */ class mk_simple_joins : public rule_transformer::plugin { - context & m_context; + context & m_context; + rule_manager & rm; public: mk_simple_joins(context & ctx); diff --git a/src/muz_qe/dl_mk_slice.cpp b/src/muz_qe/dl_mk_slice.cpp index c98d6503e..5b9d43acc 100644 --- a/src/muz_qe/dl_mk_slice.cpp +++ b/src/muz_qe/dl_mk_slice.cpp @@ -120,12 +120,11 @@ namespace datalog { obj_map::iterator end = m_rule2slice.end(); expr_ref fml(m); for (; it != end; ++it) { - TRACE("dl", - it->m_key->display(m_ctx, tout << "orig:\n"); - it->m_value->display(m_ctx, tout << "new:\n");); - it->m_value->to_formula(fml); m_pinned_exprs.push_back(fml); + TRACE("dl", + tout << "orig: " << mk_pp(fml, m) << "\n"; + it->m_value->display(m_ctx, tout << "new:\n");); m_sliceform2rule.insert(fml, it->m_key); } } @@ -202,9 +201,10 @@ namespace datalog { proof* p0_new = m_new_proof.find(p0); expr* fact0 = m.get_fact(p0); TRACE("dl", tout << "fact0: " << mk_pp(fact0, m) << "\n";); - rule* orig0 = m_sliceform2rule.find(fact0); - /* rule* slice0 = */ m_rule2slice.find(orig0); - /* unsigned_vector const& renaming0 = m_renaming.find(orig0); */ + rule* orig0; + if (!m_sliceform2rule.find(fact0, orig0)) { + return false; + } premises.push_back(p0_new); rule_ref r1(rm), r2(rm), r3(rm); r1 = orig0; @@ -214,9 +214,10 @@ namespace datalog { proof* p1_new = m_new_proof.find(p1); expr* fact1 = m.get_fact(p1); TRACE("dl", tout << "fact1: " << mk_pp(fact1, m) << "\n";); - rule* orig1 = m_sliceform2rule.find(fact1); - /* rule* slice1 = */ m_rule2slice.find(orig1); - /* unsigned_vector const& renaming1 = m_renaming.find(orig1); TBD */ + rule* orig1 = 0; + if (!m_sliceform2rule.find(fact1, orig1)) { + return false; + } premises.push_back(p1_new); // TODO: work with substitutions. @@ -241,6 +242,9 @@ namespace datalog { proof* new_p = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), concl, positions, substs); m_pinned_exprs.push_back(new_p); m_pinned_rules.push_back(r1.get()); + TRACE("dl", + tout << "orig: " << mk_pp(slice_concl, m) << "\n"; + r1->display(m_ctx, tout << "new:");); m_sliceform2rule.insert(slice_concl, r1.get()); m_rule2slice.insert(r1.get(), 0); m_renaming.insert(r1.get(), unsigned_vector()); @@ -703,9 +707,10 @@ namespace datalog { m_pinned.reset(); } - void mk_slice::declare_predicates() { + void mk_slice::declare_predicates(rule_set const& src, rule_set& dst) { obj_map::iterator it = m_sliceable.begin(), end = m_sliceable.end(); ptr_vector domain; + bool has_output = false; func_decl* f; for (; it != end; ++it) { domain.reset(); @@ -720,10 +725,19 @@ namespace datalog { f = m_ctx.mk_fresh_head_predicate(p->get_name(), symbol("slice"), domain.size(), domain.c_ptr(), p); m_pinned.push_back(f); m_predicates.insert(p, f); + dst.inherit_predicate(src, p, f); if (m_mc) { m_mc->add_predicate(p, f); } } + else if (src.is_output_predicate(p)) { + dst.set_output_predicate(p); + has_output = true; + } + } + // disable slicing if the output predicates don't occur in rules. + if (!has_output) { + m_predicates.reset(); } } @@ -820,13 +834,14 @@ namespace datalog { m_mc = smc.get(); reset(); saturate(src); - declare_predicates(); + rule_set* result = alloc(rule_set, m_ctx); + declare_predicates(src, *result); if (m_predicates.empty()) { // nothing could be sliced. + dealloc(result); return 0; } TRACE("dl", display(tout);); - rule_set* result = alloc(rule_set, m_ctx); update_rules(src, *result); TRACE("dl", result->display(tout);); if (m_mc) { diff --git a/src/muz_qe/dl_mk_slice.h b/src/muz_qe/dl_mk_slice.h index 1b4312e77..aedc1feb0 100644 --- a/src/muz_qe/dl_mk_slice.h +++ b/src/muz_qe/dl_mk_slice.h @@ -83,7 +83,7 @@ namespace datalog { expr_ref_vector get_tail_conjs(rule const& r); - void declare_predicates(); + void declare_predicates(rule_set const& src, rule_set& dst); bool rule_updated(rule const& r); diff --git a/src/muz_qe/dl_mk_subsumption_checker.cpp b/src/muz_qe/dl_mk_subsumption_checker.cpp index fb55a377c..8c9e7e69f 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.cpp +++ b/src/muz_qe/dl_mk_subsumption_checker.cpp @@ -21,11 +21,8 @@ Revision History: #include #include"ast_pp.h" - #include "rewriter.h" #include "rewriter_def.h" - - #include"dl_mk_subsumption_checker.h" #include"dl_table_relation.h" @@ -82,7 +79,7 @@ namespace datalog { void mk_subsumption_checker::on_discovered_total_relation(func_decl * pred, rule * r) { //this should be rule marking a new relation as total SASSERT(!m_total_relations.contains(pred)); - SASSERT(!r || pred==r->get_head()->get_decl()); + SASSERT(!r || pred==r->get_decl()); SASSERT(!r || is_total_rule(r)); m_total_relations.insert(pred); @@ -102,7 +99,7 @@ namespace datalog { rule_set::iterator rend = rules.end(); for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) { rule * r = *rit; - func_decl * head_pred = r->get_head()->get_decl(); + func_decl * head_pred = r->get_decl(); if(is_total_rule(r) && !m_total_relations.contains(head_pred)) { on_discovered_total_relation(head_pred, r); new_discovered = true; @@ -196,10 +193,10 @@ namespace datalog { for(rule_set::iterator rit = rbegin; rit!=rend; ++rit) { rule * r = *rit; - func_decl * head_pred = r->get_head()->get_decl(); + func_decl * head_pred = r->get_decl(); if(m_total_relations.contains(head_pred)) { - if(!m_context.is_output_predicate(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'll eliminate them from the problem. @@ -217,7 +214,7 @@ namespace datalog { modified = true; } tgt.add_rule(totality_rule); - SASSERT(totality_rule->get_head()->get_decl()==head_pred); + SASSERT(totality_rule->get_decl()==head_pred); } else { modified = true; @@ -244,30 +241,30 @@ namespace datalog { tgt.add_rule(new_rule); subs_index.add(new_rule); } + tgt.inherit_predicates(orig); TRACE("dl", tout << "original set size: "<knows_exact_size()) { continue; } + if (!rel || !rel->knows_exact_size()) { continue; } unsigned arity = pred->get_arity(); - if(arity>30) { continue; } + if (arity > 30) { continue; } //for now we only check booleans domains for(unsigned i=0; iget_head()->get_decl(); + func_decl * pred = r->get_decl(); if(r->get_tail_size()!=0) { continue; } @@ -324,9 +321,7 @@ namespace datalog { if(!m_ground_unconditional_rule_heads.contains(pred)) { m_ground_unconditional_rule_heads.insert(pred, alloc(obj_hashtable)); } - obj_hashtable * head_store; - m_ground_unconditional_rule_heads.find(pred, head_store); - head_store->insert(head); + m_ground_unconditional_rule_heads.find(pred)->insert(head); next_rule:; } @@ -337,14 +332,14 @@ namespace datalog { m_have_new_total_rule = false; collect_ground_unconditional_rule_heads(source); - scan_for_relations_total_due_to_facts(); + scan_for_relations_total_due_to_facts(source); scan_for_total_rules(source); m_have_new_total_rule = false; rule_set * res = alloc(rule_set, m_context); bool modified = transform_rules(source, *res); - if(!m_have_new_total_rule && !modified) { + if (!m_have_new_total_rule && !modified) { dealloc(res); return 0; } @@ -353,7 +348,7 @@ namespace datalog { //During the construction of the new set we may discover new total relations //(by quantifier elimination on the uninterpreted tails). SASSERT(m_new_total_relation_discovery_during_transformation || !m_have_new_total_rule); - while(m_have_new_total_rule) { + while (m_have_new_total_rule) { m_have_new_total_rule = false; rule_set * old = res; diff --git a/src/muz_qe/dl_mk_subsumption_checker.h b/src/muz_qe/dl_mk_subsumption_checker.h index 59904b3ef..da42e4202 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.h +++ b/src/muz_qe/dl_mk_subsumption_checker.h @@ -64,8 +64,8 @@ namespace datalog { /** Function to be called when a new total relation is discovered */ void on_discovered_total_relation(func_decl * pred, rule * r); - void scan_for_total_rules(const rule_set & rules); - void scan_for_relations_total_due_to_facts(); + void scan_for_total_rules(rule_set const& rules); + void scan_for_relations_total_due_to_facts(rule_set const& rules); void collect_ground_unconditional_rule_heads(const rule_set & rules); diff --git a/src/muz_qe/dl_mk_unbound_compressor.cpp b/src/muz_qe/dl_mk_unbound_compressor.cpp index 40926c2a8..7eb6f829d 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.cpp +++ b/src/muz_qe/dl_mk_unbound_compressor.cpp @@ -26,9 +26,10 @@ namespace datalog { mk_unbound_compressor::mk_unbound_compressor(context & ctx) : plugin(500), m_context(ctx), - m_manager(ctx.get_manager()), - m_rules(ctx.get_rule_manager()), - m_pinned(m_manager) { + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_rules(rm), + m_pinned(m) { } void mk_unbound_compressor::reset() { @@ -47,10 +48,7 @@ namespace datalog { } unsigned var_idx = to_var(head_arg)->get_idx(); - var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); - - return tail_vars.contains(var_idx); + return rm.collect_tail_vars(r).contains(var_idx); } void mk_unbound_compressor::add_task(func_decl * pred, unsigned arg_index) { @@ -81,22 +79,21 @@ namespace datalog { m_map.insert(ci, cpred); } - void mk_unbound_compressor::detect_tasks(unsigned rule_index) { + void mk_unbound_compressor::detect_tasks(rule_set const& source, unsigned rule_index) { rule * r = m_rules.get(rule_index); - var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); + var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); - if (m_context.is_output_predicate(head_pred)) { + if (source.is_output_predicate(head_pred)) { //we don't compress output predicates return; } unsigned n = head_pred->get_arity(); - - var_counter head_var_counter; - head_var_counter.count_vars(m_manager, head, 1); + + rm.get_counter().reset(); + rm.get_counter().count_vars(m, head, 1); for (unsigned i=0; iget_arg(i); @@ -107,7 +104,7 @@ namespace datalog { if (!tail_vars.contains(var_idx)) { //unbound - unsigned occurence_cnt = head_var_counter.get(var_idx); + unsigned occurence_cnt = rm.get_counter().get(var_idx); SASSERT(occurence_cnt>0); if (occurence_cnt == 1) { TRACE("dl", r->display(m_context, tout << "Compress: ");); @@ -118,18 +115,17 @@ namespace datalog { } } - void mk_unbound_compressor::try_compress(unsigned rule_index) { + void mk_unbound_compressor::try_compress(rule_set const& source, unsigned rule_index) { start: rule * r = m_rules.get(rule_index); - var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); + var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); unsigned head_arity = head_pred->get_arity(); - var_counter head_var_counter; - head_var_counter.count_vars(m_manager, head); + rm.get_counter().reset(); + rm.get_counter().count_vars(m, head); unsigned arg_index; for (arg_index = 0; arg_index < head_arity; arg_index++) { @@ -140,7 +136,7 @@ namespace datalog { unsigned var_idx = to_var(arg)->get_idx(); if (!tail_vars.contains(var_idx)) { //unbound - unsigned occurence_cnt = head_var_counter.get(var_idx); + unsigned occurence_cnt = rm.get_counter().get(var_idx); SASSERT(occurence_cnt>0); if ( occurence_cnt==1 && m_in_progress.contains(c_info(head_pred, arg_index)) ) { //we have found what to compress @@ -163,13 +159,13 @@ namespace datalog { } } - app_ref chead(m_manager.mk_app(cpred, head_arity-1, cargs.c_ptr()), m_manager); + app_ref chead(m.mk_app(cpred, head_arity-1, cargs.c_ptr()), m); if (r->get_tail_size()==0 && m_context.get_rule_manager().is_fact(chead)) { m_non_empty_rels.insert(cpred); m_context.add_fact(chead); //remove the rule that became fact by placing the last rule on its place - m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_head()->get_decl()); + m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); m_rules.set(rule_index, m_rules.get(m_rules.size()-1)); m_rules.shrink(m_rules.size()-1); //since we moved the last rule to rule_index, we have to try to compress it as well @@ -181,10 +177,10 @@ namespace datalog { rule_ref new_rule(m_context.get_rule_manager().mk(r, chead), m_context.get_rule_manager()); new_rule->set_accounting_parent_object(m_context, r); - m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_head()->get_decl()); + m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); m_rules.set(rule_index, new_rule); - m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_head()->get_decl()); - detect_tasks(rule_index); + m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_decl()); + detect_tasks(source, rule_index); } m_modified = true; @@ -205,10 +201,10 @@ namespace datalog { } } SASSERT(dtail_args.size()==dtail_pred->get_arity()); - app_ref dtail(m_manager.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m_manager); + app_ref dtail(m.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m); svector tails_negated; - app_ref_vector tails(m_manager); + app_ref_vector tails(m); unsigned tail_len = r->get_tail_size(); for (unsigned i=0; iis_neg_tail(i)); @@ -232,17 +228,17 @@ namespace datalog { m_context.get_rule_manager().fix_unbound_vars(res, true); } - void mk_unbound_compressor::add_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index) { + void mk_unbound_compressor::add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index) { rule_ref new_rule(m_context.get_rule_manager()); mk_decompression_rule(r, tail_index, arg_index, new_rule); unsigned new_rule_index = m_rules.size(); m_rules.push_back(new_rule); m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule.get()); - m_head_occurrence_ctr.inc(new_rule->get_head()->get_decl()); + m_head_occurrence_ctr.inc(new_rule->get_decl()); - detect_tasks(new_rule_index); + detect_tasks(source, new_rule_index); m_modified = true; @@ -258,7 +254,7 @@ namespace datalog { //P:- R1(x), S1(x) } - void mk_unbound_compressor::replace_by_decompression_rule(unsigned rule_index, unsigned tail_index, unsigned arg_index) + void mk_unbound_compressor::replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index) { rule * r = m_rules.get(rule_index); @@ -269,12 +265,12 @@ namespace datalog { //we don't update the m_head_occurrence_ctr because the head predicate doesn't change - detect_tasks(rule_index); + detect_tasks(source, rule_index); m_modified = true; } - void mk_unbound_compressor::add_decompression_rules(unsigned rule_index) { + void mk_unbound_compressor::add_decompression_rules(rule_set const& source, unsigned rule_index) { unsigned_vector compressed_tail_pred_arg_indexes; @@ -306,11 +302,11 @@ namespace datalog { m_head_occurrence_ctr.get(t_pred)==0; if (can_remove_orig_rule || is_negated_predicate) { - replace_by_decompression_rule(rule_index, tail_index, arg_index); + replace_by_decompression_rule(source, rule_index, tail_index, arg_index); orig_rule_replaced = true; } else { - add_decompression_rule(r, tail_index, arg_index); + add_decompression_rule(source, r, tail_index, arg_index); } } if (orig_rule_replaced) { @@ -345,11 +341,11 @@ namespace datalog { for (unsigned i=0; iget_head()->get_decl()); + m_head_occurrence_ctr.inc(r->get_decl()); } for (unsigned i=0; iadd_rule(m_rules.get(i)); } + result->inherit_predicates(source); } reset(); return result; diff --git a/src/muz_qe/dl_mk_unbound_compressor.h b/src/muz_qe/dl_mk_unbound_compressor.h index cad953783..4e2ff0b3c 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.h +++ b/src/muz_qe/dl_mk_unbound_compressor.h @@ -50,8 +50,9 @@ namespace datalog { typedef hashtable > in_progress_table; typedef svector todo_stack; - context & m_context; - ast_manager & m_manager; + context & m_context; + ast_manager & m; + rule_manager & rm; rule_ref_vector m_rules; bool m_modified; todo_stack m_todo; @@ -71,13 +72,13 @@ namespace datalog { bool is_unbound_argument(rule * r, unsigned head_index); bool has_unbound_head_var(rule * r); - void detect_tasks(unsigned rule_index); + void detect_tasks(rule_set const& source, unsigned rule_index); void add_task(func_decl * pred, unsigned arg_index); - void try_compress(unsigned rule_index); - void add_decompression_rules(unsigned rule_index); + void try_compress(rule_set const& source, unsigned rule_index); + void add_decompression_rules(rule_set const& source, unsigned rule_index); void mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index, rule_ref& res); - void add_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index); - void replace_by_decompression_rule(unsigned rule_index, unsigned tail_index, unsigned arg_index); + void add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index); + void replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index); void reset(); public: mk_unbound_compressor(context & ctx); diff --git a/src/muz_qe/dl_mk_unfold.cpp b/src/muz_qe/dl_mk_unfold.cpp index dfbd87122..a9357f88a 100644 --- a/src/muz_qe/dl_mk_unfold.cpp +++ b/src/muz_qe/dl_mk_unfold.cpp @@ -56,6 +56,7 @@ namespace datalog { for (; it != end; ++it) { expand_tail(**it, 0, source, *rules); } + rules->inherit_predicates(source); return rules; } diff --git a/src/muz_qe/dl_product_relation.cpp b/src/muz_qe/dl_product_relation.cpp index 66074d463..c10b5799a 100644 --- a/src/muz_qe/dl_product_relation.cpp +++ b/src/muz_qe/dl_product_relation.cpp @@ -269,7 +269,7 @@ namespace datalog { unsigned_vector r1_tables_indexes; unsigned_vector r2_tables_indexes; for (unsigned i = 0; i < num_rels1; ++i) { - if(is_tableish_relation(*r1[i])) { + if (is_tableish_relation(*r1[i])) { r1_tables_indexes.push_back(i); continue; } @@ -291,7 +291,7 @@ namespace datalog { if (!found) { relation_plugin & r1_plugin = get_nonsieve_plugin(*r1[i]); relation_base* rel2; - if(r1_plugin.can_handle_signature(r2_sig)) { + if (r1_plugin.can_handle_signature(r2_sig)) { rel2 = r1_plugin.mk_full(p, r2_sig, r1_kind); } else { @@ -307,7 +307,7 @@ namespace datalog { } } for (unsigned i = 0; i < num_rels2; ++i) { - if(is_tableish_relation(*r2[i])) { + if (is_tableish_relation(*r2[i])) { r2_tables_indexes.push_back(i); continue; } @@ -315,7 +315,7 @@ namespace datalog { relation_plugin & r2_plugin = get_nonsieve_plugin(*r2[i]); family_id r2_kind = get_nonsieve_kind(*r2[i]); relation_base* rel1; - if(r2_plugin.can_handle_signature(r1_sig)) { + if (r2_plugin.can_handle_signature(r1_sig)) { rel1 = r2_plugin.mk_full(p, r1_sig, r2_kind); } else { @@ -331,7 +331,7 @@ namespace datalog { } } - if(!r1_tables_indexes.empty() && !r2_tables_indexes.empty()) { + if (!r1_tables_indexes.empty() && !r2_tables_indexes.empty()) { //We may perhaps want to group the table relations by kinds so that tables of the same kind //get joined... diff --git a/src/muz_qe/dl_product_relation.h b/src/muz_qe/dl_product_relation.h index c5e755939..91c2286a7 100644 --- a/src/muz_qe/dl_product_relation.h +++ b/src/muz_qe/dl_product_relation.h @@ -40,8 +40,12 @@ namespace datalog { class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; + struct fid_hash { + typedef family_id data; + unsigned operator()(data x) const { return static_cast(x); } + }; - rel_spec_store m_spec_store; + rel_spec_store > m_spec_store; family_id get_relation_kind(const product_relation & r); diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz_qe/dl_relation_manager.cpp index e001dd1a4..ce20961f1 100644 --- a/src/muz_qe/dl_relation_manager.cpp +++ b/src/muz_qe/dl_relation_manager.cpp @@ -124,14 +124,6 @@ namespace datalog { e->get_data().m_value = rel; } - void relation_manager::collect_predicates(decl_set & res) const { - relation_map::iterator it = m_relations.begin(); - relation_map::iterator end = m_relations.end(); - for(; it!=end; ++it) { - res.insert(it->m_key); - } - } - void relation_manager::collect_non_empty_predicates(decl_set & res) const { relation_map::iterator it = m_relations.begin(); relation_map::iterator end = m_relations.end(); @@ -539,8 +531,8 @@ namespace datalog { } } - void relation_manager::display_output_tables(std::ostream & out) const { - const decl_set & output_preds = get_context().get_output_predicates(); + 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) { @@ -728,6 +720,13 @@ namespace datalog { return t.get_plugin().mk_filter_interpreted_fn(t, condition); } + relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, + unsigned removed_col_cnt, + const unsigned * removed_cols) { + return t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); + } + class relation_manager::default_relation_select_equal_and_project_fn : public relation_transformer_fn { scoped_ptr m_filter; @@ -748,7 +747,6 @@ namespace datalog { relation_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col) { relation_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); - TRACE("dl", tout << t.get_plugin().get_name() << " " << value << " " << col << "\n";); if(!res) { relation_mutator_fn * selector = mk_filter_equal_fn(t, value, col); if(selector) { @@ -1098,12 +1096,10 @@ namespace datalog { class relation_manager::default_table_rename_fn : public convenient_table_rename_fn, auxiliary_table_transformer_fn { - const unsigned m_cycle_len; public: default_table_rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) - : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle), - m_cycle_len(permutation_cycle_len) { + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); } @@ -1398,6 +1394,45 @@ namespace datalog { } + class relation_manager::default_table_filter_interpreted_and_project_fn + : public table_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + app_ref m_condition; + unsigned_vector m_removed_cols; + public: + default_table_filter_interpreted_and_project_fn(context & ctx, table_mutator_fn * filter, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) + : m_filter(filter), m_condition(condition, ctx.get_manager()), + m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual table_base* operator()(const table_base & tb) { + table_base *t2 = tb.clone(); + (*m_filter)(*t2); + if (!m_project) { + relation_manager & rmgr = t2->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*t2, m_removed_cols.size(), m_removed_cols.c_ptr()); + if (!m_project) { + throw default_exception("projection does not exist"); + } + } + return (*m_project)(*t2); + } + }; + + table_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + table_transformer_fn * res = t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); + if (res) + return res; + + table_mutator_fn * filter = mk_filter_interpreted_fn(t, condition); + SASSERT(filter); + res = alloc(default_table_filter_interpreted_and_project_fn, get_context(), filter, condition, removed_col_cnt, removed_cols); + return res; + } + + table_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const table_base & t, const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, diff --git a/src/muz_qe/dl_relation_manager.h b/src/muz_qe/dl_relation_manager.h index 6b350642e..ccdba2783 100644 --- a/src/muz_qe/dl_relation_manager.h +++ b/src/muz_qe/dl_relation_manager.h @@ -22,7 +22,6 @@ Revision History: #include"map.h" #include"vector.h" - #include"dl_base.h" namespace datalog { @@ -35,8 +34,8 @@ namespace datalog { class finite_product_relation_plugin; class sieve_relation; class sieve_relation_plugin; + class rule_set; - typedef hashtable, ptr_eq > decl_set; class relation_manager { class empty_signature_relation_join_fn; @@ -56,6 +55,7 @@ namespace datalog { class default_table_filter_equal_fn; class default_table_filter_identical_fn; class default_table_filter_interpreted_fn; + class default_table_filter_interpreted_and_project_fn; class default_table_negation_filter_fn; class default_table_filter_not_equal_fn; class default_table_select_equal_and_project_fn; @@ -153,7 +153,6 @@ namespace datalog { } } - void collect_predicates(decl_set & res) const; void collect_non_empty_predicates(decl_set & res) const; void restrict_predicates(const decl_set & preds); @@ -352,6 +351,9 @@ namespace datalog { relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. @@ -524,6 +526,9 @@ namespace datalog { table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); + table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. @@ -595,7 +600,7 @@ namespace datalog { void display(std::ostream & out) const; void display_relation_sizes(std::ostream & out) const; - void display_output_tables(std::ostream & out) const; + void display_output_tables(rule_set const& rules, std::ostream & out) const; private: relation_intersection_filter_fn * try_mk_default_filter_by_intersection_fn(const relation_base & t, @@ -607,7 +612,7 @@ namespace datalog { /** This is a helper class for relation_plugins whose relations can be of various kinds. */ - template, class Eq=vector_eq_proc > + template > class rel_spec_store { typedef relation_signature::hash r_hash; typedef relation_signature::eq r_eq; @@ -663,17 +668,15 @@ namespace datalog { SASSERT(res_idxinsert(m_allocated_kinds[res_idx], spec); } return m_allocated_kinds[res_idx]; } void get_relation_spec(const relation_signature & sig, family_id kind, Spec & spec) { - family_id2spec * idspecs; - VERIFY( m_kind_specs.find(sig, idspecs) ); - VERIFY( idspecs->find(kind, spec) ); + family_id2spec * idspecs = m_kind_specs.find(sig); + spec = idspecs->find(kind); } }; diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index b7d6d9fae..ac683eca9 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -40,19 +40,20 @@ Revision History: #include"quant_hoist.h" #include"expr_replacer.h" #include"bool_rewriter.h" -#include"qe_lite.h" #include"expr_safe_replace.h" -#include"hnf.h" namespace datalog { rule_manager::rule_manager(context& ctx) : m(ctx.get_manager()), - m_ctx(ctx) {} - - bool rule_manager::is_predicate(func_decl * f) const { - return m_ctx.is_predicate(f); - } + m_ctx(ctx), + m_body(m), + m_head(m), + m_args(m), + m_hnf(m), + m_qe(m), + m_cfg(m), + m_rwr(m, false, m_cfg) {} void rule_manager::inc_ref(rule * r) { if (r) { @@ -71,29 +72,23 @@ namespace datalog { } } - class remove_label_cfg : public default_rewriter_cfg { - family_id m_label_fid; - public: - remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} - virtual ~remove_label_cfg() {} + rule_manager::remove_label_cfg::~remove_label_cfg() {} - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, - proof_ref & result_pr) - { - if (is_decl_of(f, m_label_fid, OP_LABEL)) { - SASSERT(num == 1); - result = args[0]; - return BR_DONE; - } - return BR_FAILED; + br_status rule_manager::remove_label_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + proof_ref & result_pr) + { + if (is_decl_of(f, m_label_fid, OP_LABEL)) { + SASSERT(num == 1); + result = args[0]; + return BR_DONE; } - }; + return BR_FAILED; + } + void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); - remove_label_cfg r_cfg(m); - rewriter_tpl rwr(m, false, r_cfg); - rwr(fml, tmp); + m_rwr(fml, tmp); if (pr && fml != tmp) { pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp)); @@ -101,8 +96,69 @@ namespace datalog { fml = tmp; } + var_idx_set& rule_manager::collect_vars(expr* e) { + return collect_vars(e, 0); + } - void rule_manager::mk_rule(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name) { + var_idx_set& rule_manager::collect_vars(expr* e1, expr* e2) { + reset_collect_vars(); + if (e1) accumulate_vars(e1); + if (e2) accumulate_vars(e2); + return finalize_collect_vars(); + } + + void rule_manager::reset_collect_vars() { + m_vars.reset(); + m_var_idx.reset(); + m_todo.reset(); + m_mark.reset(); + } + + var_idx_set& rule_manager::finalize_collect_vars() { + unsigned sz = m_vars.size(); + for (unsigned i=0; iget_tail_size(); + for (unsigned i=0;iget_tail(i)); + } + return finalize_collect_vars(); + } + + var_idx_set& rule_manager::collect_rule_vars_ex(rule * r, app* t) { + reset_collect_vars(); + unsigned n = r->get_tail_size(); + accumulate_vars(r->get_head()); + for (unsigned i=0;iget_tail(i) != t) { + accumulate_vars(r->get_tail(i)); + } + } + return finalize_collect_vars(); + } + + var_idx_set& rule_manager::collect_rule_vars(rule * r) { + reset_collect_vars(); + unsigned n = r->get_tail_size(); + accumulate_vars(r->get_head()); + for (unsigned i=0;iget_tail(i)); + } + return finalize_collect_vars(); + } + + void rule_manager::accumulate_vars(expr* e) { + ::get_free_vars(m_mark, m_todo, e, m_vars); + } + + + void rule_manager::mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); proof_ref pr(p, m); expr_ref fml1(m); @@ -111,13 +167,13 @@ namespace datalog { pr = m.mk_asserted(fml1); } remove_labels(fml1, pr); - mk_rule_core_new(fml1, pr, rules, name); + mk_rule_core(fml1, pr, rules, name); } void rule_manager::mk_negations(app_ref_vector& body, svector& is_negated) { for (unsigned i = 0; i < body.size(); ++i) { expr* e = body[i].get(), *e1; - if (m.is_not(e, e1) && is_predicate(e1)) { + if (m.is_not(e, e1) && m_ctx.is_predicate(e1)) { check_app(e1); body[i] = to_app(e1); is_negated.push_back(true); @@ -128,40 +184,39 @@ namespace datalog { } } - void rule_manager::mk_rule_core_new(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name) { - hnf h(m); + void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) { expr_ref_vector fmls(m); proof_ref_vector prs(m); - h.set_name(name); - h(fml, p, fmls, prs); - for (unsigned i = 0; i < h.get_fresh_predicates().size(); ++i) { - m_ctx.register_predicate(h.get_fresh_predicates()[i], false); + m_hnf.reset(); + m_hnf.set_name(name); + m_hnf(fml, p, fmls, prs); + for (unsigned i = 0; i < m_hnf.get_fresh_predicates().size(); ++i) { + m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false); } for (unsigned i = 0; i < fmls.size(); ++i) { - mk_rule_core2(fmls[i].get(), prs[i].get(), rules, name); + mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name); } } - void rule_manager::mk_rule_core2(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name) { + void rule_manager::mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { - app_ref_vector body(m); - app_ref head(m); - svector is_negated; - unsigned index = extract_horn(fml, body, head); - hoist_compound_predicates(index, head, body); + m_body.reset(); + m_neg.reset(); + unsigned index = extract_horn(fml, m_body, m_head); + hoist_compound_predicates(index, m_head, m_body); TRACE("dl_rule", - tout << mk_pp(head, m) << " :- "; - for (unsigned i = 0; i < body.size(); ++i) { - tout << mk_pp(body[i].get(), m) << " "; + tout << mk_pp(m_head, m) << " :- "; + for (unsigned i = 0; i < m_body.size(); ++i) { + tout << mk_pp(m_body[i].get(), m) << " "; } tout << "\n";); - mk_negations(body, is_negated); - check_valid_rule(head, body.size(), body.c_ptr()); + mk_negations(m_body, m_neg); + check_valid_rule(m_head, m_body.size(), m_body.c_ptr()); rule_ref r(*this); - r = mk(head.get(), body.size(), body.c_ptr(), is_negated.c_ptr(), name); + r = mk(m_head.get(), m_body.size(), m_body.c_ptr(), m_neg.c_ptr(), name); expr_ref fml1(m); if (p) { @@ -189,7 +244,7 @@ namespace datalog { } r->set_proof(m, p); } - rules.push_back(r); + rules.add_rule(r); } unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) { @@ -224,7 +279,7 @@ namespace datalog { } - void rule_manager::mk_query(expr* query, func_decl_ref& qpred, rule_ref_vector& query_rules, rule_ref& query_rule) { + func_decl* rule_manager::mk_query(expr* query, rule_set& rules) { ptr_vector vars; svector names; app_ref_vector body(m); @@ -279,7 +334,9 @@ namespace datalog { } vars.reverse(); names.reverse(); - qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); + func_decl* qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); + m_ctx.register_predicate(qpred, false); + rules.set_output_predicate(qpred); expr_ref_vector qhead_args(m); for (unsigned i = 0; i < vars.size(); i++) { @@ -297,9 +354,8 @@ namespace datalog { if (m_ctx.generate_proof_trace()) { pr = m.mk_asserted(rule_expr); } - mk_rule(rule_expr, pr, query_rules); - SASSERT(query_rules.size() >= 1); - query_rule = query_rules.back(); + mk_rule(rule_expr, pr, rules); + return qpred; } void rule_manager::bind_variables(expr* fml, bool is_forall, expr_ref& result) { @@ -329,45 +385,45 @@ namespace datalog { fml = m.mk_not(fml); return; } - expr_ref_vector args(m); - if (!is_predicate(fml)) { + if (!m_ctx.is_predicate(fml)) { return; } + m_args.reset(); for (unsigned i = 0; i < fml->get_num_args(); ++i) { e = fml->get_arg(i); if (!is_app(e)) { - args.push_back(e); + m_args.push_back(e); continue; } app* b = to_app(e); if (m.is_value(b)) { - args.push_back(e); + m_args.push_back(e); } else { var* v = m.mk_var(num_bound++, m.get_sort(b)); - args.push_back(v); + m_args.push_back(v); body.push_back(m.mk_eq(v, b)); } } - fml = m.mk_app(fml->get_decl(), args.size(), args.c_ptr()); + fml = m.mk_app(fml->get_decl(), m_args.size(), m_args.c_ptr()); TRACE("dl_rule", tout << mk_pp(fml.get(), m) << "\n";); } class contains_predicate_proc { - rule_manager const& m; + context const& ctx; public: struct found {}; - contains_predicate_proc(rule_manager const& m): m(m) {} + contains_predicate_proc(context const& ctx): ctx(ctx) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app* n) { - if (m.is_predicate(n)) throw found(); + if (ctx.is_predicate(n)) throw found(); } }; bool rule_manager::contains_predicate(expr* fml) const { - contains_predicate_proc proc(*this); + contains_predicate_proc proc(m_ctx); try { quick_for_each_expr(proc, fml); } @@ -434,7 +490,7 @@ namespace datalog { bool is_neg = (is_negated != 0 && is_negated[i]); app * curr = tail[i]; - if (is_neg && !is_predicate(curr)) { + if (is_neg && !m_ctx.is_predicate(curr)) { curr = m.mk_not(curr); is_neg = false; } @@ -442,7 +498,7 @@ namespace datalog { has_neg = true; } app * tail_entry = TAG(app *, curr, is_neg); - if (is_predicate(curr)) { + if (m_ctx.is_predicate(curr)) { *uninterp_tail=tail_entry; uninterp_tail++; } @@ -514,29 +570,22 @@ namespace datalog { void rule_manager::reduce_unbound_vars(rule_ref& r) { unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned t_len = r->get_tail_size(); - ptr_vector vars; - uint_set index_set; - qe_lite qe(m); expr_ref_vector conjs(m); if (ut_len == t_len) { return; } - get_free_vars(r->get_head(), vars); + reset_collect_vars(); + accumulate_vars(r->get_head()); for (unsigned i = 0; i < ut_len; ++i) { - get_free_vars(r->get_tail(i), vars); + accumulate_vars(r->get_tail(i)); } + var_idx_set& index_set = finalize_collect_vars(); for (unsigned i = ut_len; i < t_len; ++i) { conjs.push_back(r->get_tail(i)); } - - for (unsigned i = 0; i < vars.size(); ++i) { - if (vars[i]) { - index_set.insert(i); - } - } - qe(index_set, false, conjs); + m_qe(index_set, false, conjs); bool change = conjs.size() != t_len - ut_len; for (unsigned i = 0; !change && i < conjs.size(); ++i) { change = r->get_tail(ut_len+i) != conjs[i].get(); @@ -573,15 +622,14 @@ namespace datalog { return; } - ptr_vector free_rule_vars; var_counter vctr; app_ref_vector tail(m); svector tail_neg; app_ref head(r->get_head(), m); - get_free_vars(r, free_rule_vars); + collect_rule_vars(r); vctr.count_vars(m, head); - + ptr_vector& free_rule_vars = m_vars; for (unsigned i = 0; i < ut_len; i++) { app * t = r->get_tail(i); @@ -755,7 +803,7 @@ namespace datalog { void rule_manager::check_valid_head(expr * head) const { SASSERT(head); - if (!is_predicate(head)) { + if (!m_ctx.is_predicate(head)) { std::ostringstream out; out << "Illegal head. The head predicate needs to be uninterpreted and registered (as recursive) " << mk_pp(head, m); throw default_exception(out.str()); @@ -909,7 +957,7 @@ namespace datalog { } void rule::norm_vars(rule_manager & rm) { - used_vars used; + used_vars& used = rm.reset_used(); get_used_vars(used); unsigned first_unsused = used.get_max_found_var_idx_plus_1(); @@ -966,7 +1014,7 @@ namespace datalog { if (is_neg_tail(i)) out << "not "; app * t = get_tail(i); - if (ctx.get_rule_manager().is_predicate(t)) { + if (ctx.is_predicate(t)) { output_predicate(ctx, t, out); } else { @@ -1007,16 +1055,14 @@ namespace datalog { } svector names; used_symbols<> us; - - us(fml); - sorts.reverse(); - for (unsigned i = 0; i < sorts.size(); ++i) { if (!sorts[i]) { sorts[i] = m.mk_bool_sort(); } } - + + us(fml); + sorts.reverse(); for (unsigned j = 0, i = 0; i < sorts.size(); ++j) { for (char c = 'A'; i < sorts.size() && c <= 'Z'; ++c) { func_decl_ref f(m); @@ -1070,6 +1116,8 @@ namespace datalog { } - + }; +template class rewriter_tpl; + diff --git a/src/muz_qe/dl_rule.h b/src/muz_qe/dl_rule.h index 3f535125f..77bf9ac74 100644 --- a/src/muz_qe/dl_rule.h +++ b/src/muz_qe/dl_rule.h @@ -27,11 +27,15 @@ Revision History: #include"proof_converter.h" #include"model_converter.h" #include"ast_counter.h" +#include"rewriter.h" +#include"hnf.h" +#include"qe_lite.h" namespace datalog { class rule; class rule_manager; + class rule_set; class table; class context; @@ -46,9 +50,33 @@ namespace datalog { */ class rule_manager { + class remove_label_cfg : public default_rewriter_cfg { + family_id m_label_fid; + public: + remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} + virtual ~remove_label_cfg(); + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + proof_ref & result_pr); + }; + ast_manager& m; context& m_ctx; rule_counter m_counter; + used_vars m_used; + ptr_vector m_vars; + var_idx_set m_var_idx; + ptr_vector m_todo; + ast_mark m_mark; + app_ref_vector m_body; + app_ref m_head; + expr_ref_vector m_args; + svector m_neg; + hnf m_hnf; + qe_lite m_qe; + remove_label_cfg m_cfg; + rewriter_tpl m_rwr; + // only the context can create a rule_manager friend class context; @@ -74,13 +102,11 @@ namespace datalog { void bind_variables(expr* fml, bool is_forall, expr_ref& result); - void mk_rule_core(expr* fml, rule_ref_vector& rules, symbol const& name); - void mk_negations(app_ref_vector& body, svector& is_negated); - void mk_rule_core_new(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name); + void mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name); - void mk_rule_core2(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name); + void mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name); static expr_ref mk_implies(app_ref_vector const& body, expr* head); @@ -91,6 +117,10 @@ namespace datalog { */ void reduce_unbound_vars(rule_ref& r); + void reset_collect_vars(); + + var_idx_set& finalize_collect_vars(); + public: ast_manager& get_manager() const { return m; } @@ -99,18 +129,36 @@ namespace datalog { void dec_ref(rule * r); + used_vars& reset_used() { m_used.reset(); return m_used; } + + var_idx_set& collect_vars(expr * pred); + + var_idx_set& collect_vars(expr * e1, expr* e2); + + var_idx_set& collect_rule_vars(rule * r); + + var_idx_set& collect_rule_vars_ex(rule * r, app* t); + + var_idx_set& collect_tail_vars(rule * r); + + void accumulate_vars(expr* pred); + + ptr_vector& get_var_sorts() { return m_vars; } + + var_idx_set& get_var_idx() { return m_var_idx; } + /** \brief Create a Datalog rule from a Horn formula. The formula is of the form (forall (...) (forall (...) (=> (and ...) head))) */ - void mk_rule(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name = symbol::null); + void mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name = symbol::null); /** \brief Create a Datalog query from an expression. The formula is of the form (exists (...) (exists (...) (and ...)) */ - void mk_query(expr* query, func_decl_ref& query_pred, rule_ref_vector& query_rules, rule_ref& query_rule); + func_decl* mk_query(expr* query, rule_set& rules); /** \brief Create a Datalog rule head :- tail[0], ..., tail[n-1]. @@ -166,11 +214,6 @@ namespace datalog { bool is_fact(app * head) const; - bool is_predicate(func_decl * f) const; - bool is_predicate(expr * e) const { - return is_app(e) && is_predicate(to_app(e)->get_decl()); - } - static bool is_forall(ast_manager& m, expr* e, quantifier*& q); rule_counter& get_counter() { return m_counter; } diff --git a/src/muz_qe/dl_rule_set.cpp b/src/muz_qe/dl_rule_set.cpp index f9ae3620d..ad3b512a3 100644 --- a/src/muz_qe/dl_rule_set.cpp +++ b/src/muz_qe/dl_rule_set.cpp @@ -64,8 +64,7 @@ namespace datalog { reset_dealloc_values(m_data); } - void rule_dependencies::remove_m_data_entry(func_decl * key) - { + void rule_dependencies::remove_m_data_entry(func_decl * key) { item_set * itm_set = m_data.find(key); dealloc(itm_set); m_data.remove(key); @@ -109,7 +108,7 @@ namespace datalog { void rule_dependencies::populate(rule const* r) { TRACE("dl_verbose", tout << r->get_decl()->get_name() << "\n";); m_visited.reset(); - func_decl * d = r->get_head()->get_decl(); + func_decl * d = r->get_decl(); func_decl_set & s = ensure_key(d); for (unsigned i = 0; i < r->get_tail_size(); ++i) { @@ -164,7 +163,7 @@ namespace datalog { } ptr_vector::iterator rit = to_remove.begin(); ptr_vector::iterator rend = to_remove.end(); - for (; rit!=rend; ++rit) { + for (; rit != rend; ++rit) { remove_m_data_entry(*rit); } } @@ -173,7 +172,7 @@ namespace datalog { remove_m_data_entry(itm); iterator pit = begin(); iterator pend = end(); - for (; pit!=pend; ++pit) { + for (; pit != pend; ++pit) { item_set & itms = *pit->get_value(); itms.remove(itm); } @@ -244,7 +243,7 @@ namespace datalog { } curr_index++; } - if (res.size()::iterator it = other.m_orig2pred.begin(); + obj_map::iterator end = other.m_orig2pred.end(); + for (; it != end; ++it) { + m_orig2pred.insert(it->m_key, it->m_value); + } + } + { + obj_map::iterator it = other.m_pred2orig.begin(); + obj_map::iterator end = other.m_pred2orig.end(); + for (; it != end; ++it) { + m_pred2orig.insert(it->m_key, it->m_value); + } + } + } + + void rule_set::inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred) { + if (other.is_output_predicate(orig)) { + set_output_predicate(pred); + } + orig = other.get_orig(orig); + m_refs.push_back(pred); + m_refs.push_back(orig); + m_orig2pred.insert(orig, pred); + m_pred2orig.insert(pred, orig); + } + void rule_set::add_rule(rule * r) { TRACE("dl_verbose", r->display(m_context, tout << "add:");); SASSERT(!is_closed()); @@ -329,7 +374,7 @@ namespace datalog { void rule_set::del_rule(rule * r) { TRACE("dl", r->display(m_context, tout << "del:");); - func_decl* d = r->get_head()->get_decl(); + func_decl* d = r->get_decl(); rule_vector* rules = m_head2rules.find(d); #define DEL_VECTOR(_v) \ for (unsigned i = (_v).size(); i > 0; ) { \ @@ -345,34 +390,29 @@ namespace datalog { DEL_VECTOR(m_rules); } - void rule_set::ensure_closed() - { + void rule_set::ensure_closed() { if (!is_closed()) { VERIFY(close()); } } bool rule_set::close() { - SASSERT(!is_closed()); //the rule_set is not already closed - - + SASSERT(!is_closed()); //the rule_set is not already closed m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); - if (!stratified_negation()) { m_stratifier = 0; m_deps.reset(); return false; } - return true; } void rule_set::reopen() { - SASSERT(is_closed()); - - m_stratifier = 0; - m_deps.reset(); + if (is_closed()) { + m_stratifier = 0; + m_deps.reset(); + } } /** @@ -401,18 +441,20 @@ namespace datalog { return true; } - void rule_set::add_rules(const rule_set & src) { - SASSERT(!is_closed()); - unsigned n = src.get_num_rules(); - for (unsigned i=0; iget_predicate_strat(pred); } + void rule_set::split_founded_rules(func_decl_set& founded, func_decl_set& non_founded) { + founded.reset(); + non_founded.reset(); + { + decl2rules::iterator it = begin_grouped_rules(), end = end_grouped_rules(); + for (; it != end; ++it) { + non_founded.insert(it->m_key); + } + } + bool change = true; + while (change) { + change = false; + func_decl_set::iterator it = non_founded.begin(), end = non_founded.end(); + for (; it != end; ++it) { + rule_vector const& rv = get_predicate_rules(*it); + bool found = false; + for (unsigned i = 0; !found && i < rv.size(); ++i) { + rule const& r = *rv[i]; + bool is_founded = true; + for (unsigned j = 0; is_founded && j < r.get_uninterpreted_tail_size(); ++j) { + is_founded = founded.contains(r.get_decl(j)); + } + if (is_founded) { + founded.insert(*it); + non_founded.remove(*it); + change = true; + found = true; + } + } + } + } + } void rule_set::display(std::ostream & out) const { out << "; rule count: " << get_num_rules() << "\n"; out << "; predicate count: " << m_head2rules.size() << "\n"; + func_decl_set::iterator pit = m_output_preds.begin(); + func_decl_set::iterator pend = m_output_preds.end(); + for (; pit != pend; ++pit) { + out << "; output: " << (*pit)->get_name() << '\n'; + } decl2rules::iterator it = m_head2rules.begin(); decl2rules::iterator end = m_head2rules.end(); for (; it != end; ++it) { @@ -451,17 +530,6 @@ namespace datalog { r->display(m_context, out); } } - -#if 0 //print dependencies - out<<"##\n"; - out< m_stratifier; //!< contains stratifier object iff the rule_set is closed + rule_ref_vector m_rules; //!< all rules + decl2rules m_head2rules; //!< mapping from head symbol to rules. + rule_dependencies m_deps; //!< dependencies + scoped_ptr m_stratifier; //!< contains stratifier object iff the rule_set is closed + func_decl_set m_output_preds; //!< output predicates + obj_map m_orig2pred; + obj_map m_pred2orig; + func_decl_ref_vector m_refs; //sometimes we need to return reference to an empty rule_vector, @@ -184,6 +188,12 @@ namespace datalog { rule_manager & get_rule_manager() const { return const_cast(m_rule_manager); } context& get_context() const { return m_context; } + + void inherit_predicates(rule_set const& other); + void inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred); + func_decl* get_orig(func_decl* pred) const; + func_decl* get_pred(func_decl* orig) const; + /** \brief Add rule \c r to the rule set. */ @@ -198,7 +208,7 @@ namespace datalog { \brief Add all rules from a different rule_set. */ void add_rules(const rule_set& src); - void add_rules(unsigned sz, rule * const * rules); + void replace_rules(const rule_set& other); /** \brief This method should be invoked after all rules are added to the rule set. @@ -216,11 +226,14 @@ namespace datalog { bool is_closed() const { return m_stratifier != 0; } unsigned get_num_rules() const { return m_rules.size(); } + bool empty() const { return m_rules.size() == 0; } rule * get_rule(unsigned i) const { return m_rules[i]; } + rule * last() const { return m_rules[m_rules.size()-1]; } rule_ref_vector const& get_rules() const { return m_rules; } const rule_vector & get_predicate_rules(func_decl * pred) const; + bool contains(func_decl* pred) const { return m_head2rules.contains(pred); } const rule_stratifier & get_stratifier() const { SASSERT(m_stratifier); @@ -230,9 +243,17 @@ namespace datalog { unsigned get_predicate_strat(func_decl * pred) const; const rule_dependencies & get_dependencies() const { SASSERT(is_closed()); return m_deps; } + // split predicats into founded and non-founded. + void split_founded_rules(func_decl_set& founded, func_decl_set& non_founded); void reset(); + void set_output_predicate(func_decl * pred) { m_refs.push_back(pred); m_output_preds.insert(pred); } + bool is_output_predicate(func_decl * pred) const { return m_output_preds.contains(pred); } + const func_decl_set & get_output_predicates() const { return m_output_preds; } + func_decl* get_output_predicate() const { SASSERT(m_output_preds.size() == 1); return *m_output_preds.begin(); } + + void display(std::ostream & out) const; /** diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz_qe/dl_rule_transformer.cpp index 5cc686052..0cad08cb4 100644 --- a/src/muz_qe/dl_rule_transformer.cpp +++ b/src/muz_qe/dl_rule_transformer.cpp @@ -80,36 +80,38 @@ namespace datalog { tout<<"init:\n"; rules.display(tout); ); + rule_set* new_rules = alloc(rule_set, rules); plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); for(; it!=end && !m_context.canceled(); ++it) { plugin & p = **it; - rule_set * new_rules = p(rules); - if (!new_rules) { + rule_set * new_rules1 = p(*new_rules); + if (!new_rules1) { continue; } - if (p.can_destratify_negation()) { - if (!new_rules->is_closed()) { - if (!new_rules->close()) { - warning_msg("a rule transformation skipped because it destratified negation"); - dealloc(new_rules); - continue; - } - } + if (p.can_destratify_negation() && + !new_rules1->is_closed() && + !new_rules1->close()) { + warning_msg("a rule transformation skipped " + "because it destratified negation"); + dealloc(new_rules1); + continue; } modified = true; - rules.reset(); - rules.add_rules(*new_rules); dealloc(new_rules); - rules.ensure_closed(); + new_rules = new_rules1; + new_rules->ensure_closed(); TRACE("dl_rule_transf", tout << typeid(p).name()<<":\n"; - rules.display(tout); + new_rules->display(tout); ); - } + if (modified) { + rules.replace_rules(*new_rules); + } + dealloc(new_rules); return modified; } diff --git a/src/muz_qe/dl_sieve_relation.cpp b/src/muz_qe/dl_sieve_relation.cpp index c3ea5a3d0..9f9419089 100644 --- a/src/muz_qe/dl_sieve_relation.cpp +++ b/src/muz_qe/dl_sieve_relation.cpp @@ -158,7 +158,7 @@ namespace datalog { inner_sig_singleton.push_back(s[i]); inner_columns[i] = inner.can_handle_signature(inner_sig_singleton); } -#if Z3DEBUG +#if Z3DEBUG //we assume that if a relation plugin can handle two sets of columns separetely, //it can also handle them in one relation relation_signature inner_sig; @@ -246,7 +246,8 @@ namespace datalog { relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { relation_signature empty_sig; - relation_base * inner = get_manager().mk_full_relation(empty_sig, p, null_family_id); + relation_plugin& plugin = get_manager().get_appropriate_plugin(s); + relation_base * inner = plugin.mk_full(p, empty_sig, null_family_id); svector inner_cols; inner_cols.resize(s.size(), false); return mk_from_inner(s, inner_cols, inner); @@ -567,8 +568,7 @@ namespace datalog { const relation_signature sig = r.get_signature(); unsigned sz = sig.size(); - var_idx_set cond_vars; - collect_vars(m, condition, cond_vars); + var_idx_set& cond_vars = get_context().get_rule_manager().collect_vars(condition); expr_ref_vector subst_vect(m); subst_vect.resize(sz); unsigned subst_ofs = sz-1; diff --git a/src/muz_qe/dl_sieve_relation.h b/src/muz_qe/dl_sieve_relation.h index d6df3af55..551f5d705 100644 --- a/src/muz_qe/dl_sieve_relation.h +++ b/src/muz_qe/dl_sieve_relation.h @@ -52,7 +52,7 @@ namespace datalog { struct hash { unsigned operator()(const rel_spec & s) const { - return int_vector_hash(s.m_inner_cols)^s.m_inner_kind; + return svector_hash()(s.m_inner_cols)^s.m_inner_kind; } }; }; diff --git a/src/muz_qe/dl_skip_table.cpp b/src/muz_qe/dl_skip_table.cpp deleted file mode 100644 index 28efaabee..000000000 --- a/src/muz_qe/dl_skip_table.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_skip_table.h - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) - Leonardo de Moura (leonardo) 2010-10-14 - - -Revision History: - ---*/ - -#ifndef _EXTERNAL_RELEASE - -#include "dl_skip_table.h" -#include "dl_table.h" -#include "dl_context.h" - - -namespace datalog { - - skip_table & skip_table_plugin::get(table_base& r) { - return static_cast(r); - } - - skip_table const & skip_table_plugin::get(table_base const& r) { - return static_cast(r); - } - - table_base * skip_table_plugin::mk_empty(const table_signature & s) { - return alloc(skip_table, *this, s); - } - - skip_table* skip_table_plugin::mk_join( - table_base const& t1, table_base const& t2, table_signature const& result_sig, - unsigned_vector const& cols1, unsigned_vector const& cols2) { - skip_table const& s1 = get(t1); - skip_table const& s2 = get(t2); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref pr(m); - m.mk_join(s1.get_imdd(), s2.get_imdd(), pr, cols1, cols2); - return alloc(skip_table, s1.get_plugin(), result_sig, pr); - } - - skip_table* skip_table_plugin::mk_join_project( - table_base const& t1, table_base const& t2, table_signature const& result_sig, - unsigned_vector const& cols1, unsigned_vector const& cols2, - unsigned_vector const& proj_cols) { - - skip_table const& s1 = get(t1); - skip_table const& s2 = get(t2); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref pr(m); - m.mk_join_project(s1.get_imdd(), s2.get_imdd(), pr, cols1, cols2, proj_cols); - return alloc(skip_table, s1.get_plugin(), result_sig, pr); - } - - class skip_table_plugin::join_fn : public convenient_table_join_fn { - public: - join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2): - convenient_table_join_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2) { - } - - virtual table_base* operator()(const table_base & t1, const table_base & t2) { - return skip_table_plugin::mk_join(t1, t2, get_result_signature(), m_cols1, m_cols2); - } - }; - - table_join_fn * skip_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (check_kind(t1) && check_kind(t2)) { - return alloc(join_fn, t1, t2, col_cnt, cols1, cols2); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::join_project_fn : public convenient_table_join_project_fn { - public: - join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols): - convenient_table_join_project_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, - removed_col_cnt, removed_cols) { - } - - virtual table_base* operator()(const table_base & t1, const table_base & t2) { - return skip_table_plugin::mk_join_project(t1, t2, get_result_signature(), m_cols1, m_cols2, m_removed_cols); - } - }; - - - - table_join_fn * skip_table_plugin::mk_join_project_fn( - const table_base & t1, const table_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { - if (check_kind(t1) && check_kind(t2)) { - return alloc(join_project_fn, t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::union_fn : public table_union_fn { - public: - virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) { - skip_table& s1 = get(tgt); - skip_table const& s2 = get(src); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref r(m); - m.mk_union(s1.get_imdd(), s2.get_imdd(), r); - if (delta) { - skip_table& d = get(*delta); - if (m.is_subset(r, s1.get_imdd())) { - d.update(m.mk_empty(s1.get_signature().size())); - } - else { - d.update(r); - } - } - s1.update(r); - } - }; - - table_union_fn * skip_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { - if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { - return alloc(union_fn); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - skip_table* skip_table_plugin::mk_project(table_base const& src, table_signature const& result_sig, unsigned_vector const& cols) { - skip_table const& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - imdd_ref pr(m); - m.mk_project(s.get_imdd(), pr, cols.size(), cols.c_ptr()); - return alloc(skip_table, s.get_plugin(), result_sig, pr); - } - - - class skip_table_plugin::project_fn : public convenient_table_project_fn { - public: - project_fn(table_signature const& orig_sig, unsigned col_cnt, unsigned const* removed_cols): - convenient_table_project_fn(orig_sig, col_cnt, removed_cols) {} - - table_base* operator()(table_base const& src) { - return mk_project(src, get_result_signature(), m_removed_cols); - } - }; - - table_transformer_fn * skip_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { - if (check_kind(t)) { - return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::rename_fn : public convenient_table_rename_fn { - - void swap2(imdd_ref& n, unsigned col1, unsigned col2) { - imdd_manager& m = n.get_manager(); - imdd_ref tmp(m); - if (col1 == col2) { - return; - } - if (col1 > col2) { - std::swap(col1, col2); - } - for (unsigned i = col1; i < col2; ++i) { - m.mk_swap(n, tmp, i); - n = tmp; - } - for (unsigned i = col2 - 1; i > col1; ) { - --i; - m.mk_swap(n, tmp, i); - n = tmp; - } - } - public: - rename_fn(table_signature const& sig, unsigned cycle_len, unsigned const* cycle): - convenient_rename_fn(sig, cycle_len, cycle) {} - - table_base* operator()(table_base const& src) { - TRACE("skip", - for (unsigned i = 0; i < m_cycle.size(); ++i) { - tout << m_cycle[i] << " "; - } - tout << "\n"; - src.display(tout);); - skip_table const& s = get(src); - imdd_ref n(s.m_imdd, s.get_imdd_manager()); - unsigned cycle_len = m_cycle.size(); - unsigned col1, col2; - // TBD: review this for proper direction - for (unsigned i = 0; i + 1 < cycle_len; ++i) { - col1 = m_cycle[i]; - col2 = m_cycle[i+1]; - swap2(n, col1, col2); - } - if (cycle_len > 2) { - col1 = m_cycle[cycle_len-1]; - col2 = m_cycle[0]; - swap2(n, col1, col2); - } - skip_table* res = alloc(skip_table, s.get_plugin(), get_result_signature(), n); - TRACE("skip",res->display(tout);); - return res; - } - }; - - table_transformer_fn * skip_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { - if (check_kind(t)) { - return alloc(rename_fn, t.get_signature(), len, cycle); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_identical_fn : public table_mutator_fn { - unsigned_vector m_cols; - - public: - filter_identical_fn(unsigned cnt, unsigned const* cols): - m_cols(cnt, cols) - {} - - void operator()(table_base & t) { - skip_table& s = get(t); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_identical(s.get_imdd(), s.m_imdd, m_cols.size(), m_cols.c_ptr(), true); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt, - const unsigned * identical_cols) { - if (check_kind(t)) { - return alloc(filter_identical_fn, col_cnt, identical_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_equal_fn : public table_mutator_fn { - unsigned m_col; - unsigned m_value; - public: - filter_equal_fn(const table_base & t, const table_element & v, unsigned col): - m_col(col), - m_value(static_cast(v)) - { - SASSERT(v <= UINT_MAX); - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_equal(s.get_imdd(), s.m_imdd, m_col, m_value); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, - unsigned col) { - if (check_kind(t)) { - return alloc(filter_equal_fn, t, value, col); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_not_equal_fn : public table_mutator_fn { - unsigned m_col; - unsigned m_value; - public: - filter_not_equal_fn(const table_base & t, const table_element & v, unsigned col): - m_col(col), - m_value(static_cast(v)) - { - SASSERT(v <= UINT_MAX); - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_disequal(s.get_imdd(), s.m_imdd, m_col, m_value); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_not_equal_fn(const table_base & t, const table_element & value, - unsigned col) { - if (check_kind(t)) { - return alloc(filter_not_equal_fn, t, value, col); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_distinct_fn : public table_mutator_fn { - unsigned m_col1; - unsigned m_col2; - public: - filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2): - m_col1(col1), - m_col2(col2) { - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_distinct(s.get_imdd(), s.m_imdd, m_col1, m_col2); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2) { - if (check_kind(t)) { - return alloc(filter_distinct_fn, t, col1, col2); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - // - // The default implementation uses an iterator - // if the condition is a comparison <, <=, then interval table native will be an advantage. - // - table_mutator_fn * skip_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) { - ast_manager& m = get_ast_manager(); - dl_decl_util& util = get_context().get_decl_util(); - uint64 value; - - if (m.is_eq(condition)) { - expr* x = condition->get_arg(0); - expr* y = condition->get_arg(1); - if (is_var(y)) { - std::swap(x,y); - } - if (is_var(x) && is_var(y)) { - unsigned cols[2] = { to_var(x)->get_idx(), to_var(y)->get_idx() }; - return mk_filter_identical_fn(t, 2, cols); - } - if (is_var(x) && util.is_numeral_ext(y, value)) { - return mk_filter_equal_fn(t, value, to_var(x)->get_idx()); - } - } - - if (m.is_not(condition) && is_app(condition->get_arg(0))) { - condition = to_app(condition->get_arg(0)); - if (m.is_eq(condition)) { - expr* x = condition->get_arg(0); - expr* y = condition->get_arg(1); - if (is_var(y)) { - std::swap(x,y); - } - if (is_var(x) && is_var(y)) { - return mk_filter_distinct_fn(t, to_var(x)->get_idx(), to_var(y)->get_idx()); - } - if (is_var(x) && util.is_numeral_ext(y, value)) { - return mk_filter_not_equal_fn(t, value, to_var(x)->get_idx()); - } - } - } - - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_by_negation_fn : public convenient_table_negation_filter_fn { - public: - filter_by_negation_fn( - const table_base & tgt, const table_base & neg, - unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) - : convenient_table_negation_filter_fn(tgt, neg, joined_col_cnt, t_cols, negated_cols) { - - } - - // - // Compute - // { (x,y) | t(x,y) & ! exists z . negated_obj(x,z) } - // - // 1. Project z - // 2. Join with result. - // - - virtual void operator()(table_base & tgt0, const table_base & neg0) { - skip_table & tgt = get(tgt0); - const skip_table & neg = get(neg0); - unsigned_vector cols2(m_cols2); - unsigned_vector proj_cols; - table_base* t1 = 0; - if (!m_all_neg_bound) { - unsigned_vector proj_cols, remap; - table_signature sig2; - table_signature const& neg_sig = neg.get_signature(); - for (unsigned i = 0, j = 0; i < m_bound.size(); ++i) { - if (m_bound[i]) { - remap.push_back(j++); - sig2.push_back(neg_sig[i]); - } - else { - proj_cols.push_back(i); - remap.push_back(0); - } - } - for (unsigned i = 0; i < cols2.size(); ++i) { - cols2[i] = remap[cols2[i]]; - } - skip_table* t0 = skip_table_plugin::mk_project(neg, sig2, proj_cols); - t1 = t0->complement(); - t0->deallocate(); - proj_cols.reset(); - } - else { - t1 = neg.complement(); - } - for (unsigned i = 0; i < t1->get_signature().size(); ++i) { - proj_cols.push_back(tgt0.get_signature().size()+i); - } - skip_table* t2 = skip_table_plugin::mk_join_project(tgt0, *t1, tgt0.get_signature(), m_cols1, cols2, proj_cols); - t1->deallocate(); - tgt.update(*t2); - t2->deallocate(); - } - }; - - table_intersection_filter_fn * skip_table_plugin::mk_filter_by_negation_fn( - const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) { - - if (check_kind(t) && check_kind(negated_obj)) { - return alloc(filter_by_negation_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - bool skip_table_plugin::can_handle_signature(table_signature const& sig) { - for (unsigned i = 0; i < sig.size(); ++i) { - if (sig[i] >= UINT_MAX) { - return false; - } - } - return true; - } - - // ------------------ - // skip_table - - - skip_table::skip_table(skip_table_plugin & p, const table_signature & sig): - table_base(p, sig), - m_imdd(p.get_imdd_manager().mk_empty(sig.size()), p.get_imdd_manager()) { - SASSERT(well_formed()); - } - - skip_table::skip_table(skip_table_plugin & p, const table_signature & sig, imdd* m): - table_base(p, sig), - m_imdd(m, p.get_imdd_manager()) { - SASSERT(well_formed()); - } - - skip_table::~skip_table() { - } - - - bool skip_table::well_formed() const { - table_signature const& sig = get_signature(); - return - get_plugin().can_handle_signature(sig) && - (get_imdd()->get_arity() == sig.size()); - } - - bool skip_table::empty() const { - return get_imdd()->empty(); - } - - void skip_table::update(imdd* n) { - m_imdd = n; - SASSERT(well_formed()); - } - - void skip_table::add_fact(const table_fact & f) { - imdd_manager& m = get_plugin().get_imdd_manager(); - unsigned const* fact = get_fact(f.c_ptr()); - m.add_fact(get_imdd(), m_imdd, f.size(), fact); - SASSERT(well_formed()); - } - - void skip_table::remove_fact(const table_element* f) { - imdd_manager& m = get_imdd_manager(); - unsigned const* fact = get_fact(f); - m.remove_facts(get_imdd(), m_imdd, get_signature().size(), fact, fact); - } - - bool skip_table::contains_fact(const table_fact & f) const { - imdd_manager& m = get_imdd_manager(); - unsigned const* fact = get_fact(f.c_ptr()); - return m.contains(get_imdd(), f.size(), fact); - } - - table_base * skip_table::clone() const { - return alloc(skip_table, get_plugin(), get_signature(), get_imdd()); - } - - table_base * skip_table::complement() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - table_signature const& sig = get_signature(); - unsigned_vector mins, maxs; - for (unsigned i = 0; i < sig.size(); ++i) { - SASSERT(sig[i] < UINT_MAX); - mins.push_back(0); - maxs.push_back(static_cast(sig[i])); - } - imdd_ref cmpl(m); - m.mk_complement(get_imdd(), cmpl, sig.size(), mins.c_ptr(), maxs.c_ptr()); - return alloc(skip_table, get_plugin(), get_signature(), cmpl); - } - - unsigned const* skip_table::get_fact(table_element const* f) const { - table_signature const& sig = get_signature(); - const_cast(m_fact).reset(); - for (unsigned i = 0; i < sig.size(); ++i) { - const_cast(m_fact).push_back(static_cast(f[i])); - SASSERT(f[i] < UINT_MAX); - } - return m_fact.c_ptr(); - } - - - - class skip_table::our_iterator_core : public table_base::iterator_core { - skip_table const& m_table; - imdd_manager::iterator m_iterator; - - class our_row : public row_interface { - const our_iterator_core & m_parent; - public: - our_row(const our_iterator_core & parent) : row_interface(parent.m_table), m_parent(parent) {} - - virtual void get_fact(table_fact & result) const { - result.reset(); - unsigned arity = m_parent.m_iterator.get_arity(); - unsigned const* values = *(m_parent.m_iterator); - for (unsigned i = 0; i < arity; ++i) { - result.push_back(values[i]); - } - } - virtual table_element operator[](unsigned col) const { - SASSERT(col < m_parent.m_iterator.get_arity()); - unsigned const* values = *(m_parent.m_iterator); - return values[col]; - } - }; - - our_row m_row_obj; - - public: - struct b {}; - struct e {}; - - our_iterator_core(skip_table const& t, b): - m_table(t), - m_iterator(t.m_imdd.get_manager(), t.get_imdd()), - m_row_obj(*this) {} - - our_iterator_core(skip_table const& t, e): - m_table(t), - m_iterator(), - m_row_obj(*this) {} - - virtual bool is_finished() const { - return m_iterator == imdd_manager::iterator(); - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - - virtual void operator++() { - SASSERT(!is_finished()); - ++m_iterator; - } - }; - - - table_base::iterator skip_table::begin() const { - return mk_iterator(alloc(our_iterator_core, *this, our_iterator_core::b())); - } - - table_base::iterator skip_table::end() const { - return mk_iterator(alloc(our_iterator_core, *this, our_iterator_core::e())); - } - - unsigned skip_table::get_size_estimate_rows() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - size_t sz = m.get_num_rows(get_imdd()); - unsigned sz0 = static_cast(sz); - SASSERT (sz == sz0 && "we need to use size_t or big-ints for row count"); - return sz0; - } - - unsigned skip_table::get_size_estimate_bytes() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - return m.memory(get_imdd()); - } - -}; - -#endif diff --git a/src/muz_qe/dl_skip_table.h b/src/muz_qe/dl_skip_table.h deleted file mode 100644 index 6a9af9636..000000000 --- a/src/muz_qe/dl_skip_table.h +++ /dev/null @@ -1,161 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_skip_table.h - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) - Leonardo de Moura (leonardo) 2010-10-14 - - -Revision History: - ---*/ - -#ifndef _DL_SKIP_TABLE_H_ -#define _DL_SKIP_TABLE_H_ - -#include "dl_base.h" -#include "imdd.h" - -namespace datalog { - class skip_table; - - class skip_table_plugin : public table_plugin { - friend class skip_table; - imdd_manager m_manager; - protected: - class join_fn; - class join_project_fn; - class union_fn; - class transformer_fn; - class rename_fn; - class project_fn; - class filter_equal_fn; - class filter_not_equal_fn; - class filter_identical_fn; - class filter_distinct_fn; - class filter_by_negation_fn; - - imdd_manager& get_imdd_manager() const { return const_cast(m_manager); } - - public: - typedef skip_table table; - - skip_table_plugin(relation_manager & manager) - : table_plugin(symbol("skip"), manager) {} - - virtual table_base * mk_empty(const table_signature & s); - - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_join_fn * mk_join_project_fn( - const table_base & t1, const table_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols); - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, - unsigned col); - virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); - virtual table_intersection_filter_fn * mk_filter_by_negation_fn( - const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - - virtual bool can_handle_signature(table_signature const& s); - - private: - static skip_table& get(table_base& r); - - static skip_table const & get(table_base const& r); - - static skip_table* mk_join(table_base const& t1, table_base const& t2, table_signature const& s, - unsigned_vector const& cols_t1, unsigned_vector const& cols_t2); - - static skip_table* mk_join_project(table_base const& t1, table_base const& t2, table_signature const& s, - unsigned_vector const& cols_t1, unsigned_vector const& cols_t2, unsigned_vector const& proj_cols); - - static skip_table* mk_project(table_base const& src, table_signature const& result_sig, - unsigned_vector const& cols); - - virtual table_mutator_fn * mk_filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2); - - virtual table_mutator_fn * mk_filter_not_equal_fn(const table_base & t, const table_element & value, - unsigned col); - - }; - - class skip_table : public table_base { - friend class skip_table_plugin; - friend class skip_table_plugin::join_fn; - friend class skip_table_plugin::union_fn; - friend class skip_table_plugin::transformer_fn; - friend class skip_table_plugin::rename_fn; - friend class skip_table_plugin::project_fn; - friend class skip_table_plugin::filter_equal_fn; - friend class skip_table_plugin::filter_not_equal_fn; - friend class skip_table_plugin::filter_identical_fn; - friend class skip_table_plugin::filter_distinct_fn; - - class our_iterator_core; - - imdd_ref m_imdd; - unsigned_vector m_fact; - - imdd* get_imdd() const { return m_imdd.get(); } - - imdd_manager& get_imdd_manager() const { return get_plugin().get_imdd_manager(); } - - unsigned const* get_fact(table_element const* f) const; - - bool well_formed() const; - - void update(imdd* n); - - void update(skip_table& t) { update(t.m_imdd); } - - skip_table(skip_table_plugin & p, const table_signature & sig); - - skip_table(skip_table_plugin & p, const table_signature & sig, imdd*); - - skip_table(const skip_table & t); - - virtual ~skip_table(); - - public: - - skip_table_plugin & get_plugin() const { - return static_cast(table_base::get_plugin()); - } - - virtual bool empty() const; - virtual void add_fact(const table_fact & f); - virtual void remove_fact(const table_element * fact); - virtual bool contains_fact(const table_fact & f) const; - virtual table_base * complement() const; - virtual table_base * clone() const; - - virtual iterator begin() const; - virtual iterator end() const; - - virtual unsigned get_size_estimate_rows() const; - virtual unsigned get_size_estimate_bytes() const; - }; - - }; - - #endif /* _DL_SKIP_TABLE_H_ */ diff --git a/src/muz_qe/dl_sparse_table.cpp b/src/muz_qe/dl_sparse_table.cpp index 1449b7d3d..52d9618b8 100644 --- a/src/muz_qe/dl_sparse_table.cpp +++ b/src/muz_qe/dl_sparse_table.cpp @@ -33,7 +33,7 @@ namespace datalog { entry_storage::store_offset entry_storage::insert_or_get_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); - if(m_reserve==entry_ofs) { + if (m_reserve == entry_ofs) { //entry inserted, so reserve is no longer a reserve m_reserve = NO_RESERVE; } @@ -42,7 +42,7 @@ namespace datalog { bool entry_storage::insert_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); - if(m_reserve==entry_ofs) { + if (m_reserve == entry_ofs) { //entry inserted, so reserve is no longer a reserve m_reserve = NO_RESERVE; return true; @@ -53,7 +53,7 @@ namespace datalog { bool entry_storage::remove_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs; - if(!find_reserve_content(entry_ofs)) { + if (!find_reserve_content(entry_ofs)) { //the fact was not in the table return false; } @@ -64,8 +64,8 @@ namespace datalog { void entry_storage::remove_offset(store_offset ofs) { m_data_indexer.remove(ofs); store_offset last_ofs = after_last_offset() - m_entry_size; - if(ofs!=last_ofs) { - SASSERT(ofs+m_entry_size<=last_ofs); + if (ofs!=last_ofs) { + SASSERT(ofs + m_entry_size <= last_ofs); //we don't want any holes, so we put the last element at the place //of the removed one m_data_indexer.remove(last_ofs); @@ -73,7 +73,7 @@ namespace datalog { memcpy(base+ofs, base+last_ofs, m_entry_size); m_data_indexer.insert(ofs); } - if(has_reserve()) { + if (has_reserve()) { //we already have a reserve, so we need to shrink a little to keep having just one resize_data(m_data_size-m_entry_size); } @@ -98,20 +98,20 @@ namespace datalog { unsigned length = 0; unsigned dom_size_sm; - if(dom_size>UINT_MAX) { + if (dom_size>UINT_MAX) { dom_size_sm = static_cast(dom_size>>32); length += 32; - if( (dom_size&UINT_MAX)!=0 && dom_size_sm!=UINT_MAX ) { + if ( (dom_size&UINT_MAX)!=0 && dom_size_sm!=UINT_MAX ) { dom_size_sm++; } } else { dom_size_sm=static_cast(dom_size); } - if(dom_size_sm==1) { + if (dom_size_sm == 1) { length += 1; //unary domains } - else if(dom_size_sm>0x80000000u) { + else if (dom_size_sm > 0x80000000u) { length += 32; } else { @@ -122,30 +122,30 @@ namespace datalog { sparse_table::column_layout::column_layout(const table_signature & sig) : m_functional_col_cnt(sig.functional_columns()) { - SASSERT(sig.size()>0); + SASSERT(sig.size() > 0); unsigned ofs = 0; unsigned sig_sz = sig.size(); unsigned first_functional = sig_sz-m_functional_col_cnt; - for(unsigned i=0; i0); SASSERT(length<=64); - if(size()>0 && (length>54 || i==first_functional)) { + if (size() > 0 && (length > 54 || i == first_functional)) { //large domains must start byte-aligned, as well as functional columns make_byte_aligned_end(size()-1); ofs = back().next_ofs(); } push_back(column_info(ofs, length)); - ofs+=length; + ofs += length; } make_byte_aligned_end(size()-1); - SASSERT(back().next_ofs()%8==0);//the entries must be aligned to whole bytes + SASSERT(back().next_ofs()%8 == 0);//the entries must be aligned to whole bytes m_entry_size = back().next_ofs()/8; - if(m_functional_col_cnt) { - SASSERT((*this)[first_functional].m_offset%8==0); + if (m_functional_col_cnt) { + SASSERT((*this)[first_functional].m_offset%8 == 0); m_functional_part_size = m_entry_size - (*this)[first_functional].m_offset/8; } else { @@ -156,9 +156,9 @@ namespace datalog { void sparse_table::column_layout::make_byte_aligned_end(unsigned col_index0) { unsigned ofs = (*this)[col_index0].next_ofs(); unsigned ofs_bit_part = ofs%8; - unsigned rounded_ofs = (ofs_bit_part==0) ? ofs : (ofs+8-ofs_bit_part); + unsigned rounded_ofs = (ofs_bit_part == 0) ? ofs : (ofs+8-ofs_bit_part); - if(rounded_ofs!=ofs) { + if (rounded_ofs!=ofs) { SASSERT(rounded_ofs>ofs); int diff = rounded_ofs-ofs; unsigned col_idx = col_index0+1; @@ -168,18 +168,18 @@ namespace datalog { col_idx--; column_info & ci = (*this)[col_idx]; unsigned new_length = ci.m_length; - if(ci.m_length<64) { + if (ci.m_length < 64) { unsigned swallowed = std::min(64-static_cast(ci.m_length), diff); - diff-=swallowed; - new_length+=swallowed; + diff -= swallowed; + new_length += swallowed; } unsigned new_ofs = ci.m_offset+diff; ci = column_info(new_ofs, new_length); } } - SASSERT(rounded_ofs%8==0); - SASSERT((*this)[col_index0].next_ofs()%8==0); + SASSERT(rounded_ofs%8 == 0); + SASSERT((*this)[col_index0].next_ofs()%8 == 0); } // ----------------------------------- @@ -218,7 +218,7 @@ namespace datalog { m_layout(t.m_column_layout) {} virtual bool is_finished() const { - return m_ptr==m_end; + return m_ptr == m_end; } virtual row_interface & operator*() { @@ -267,7 +267,7 @@ namespace datalog { offset_iterator begin() const { return m_singleton ? &m_single_result : m_many.begin; } offset_iterator end() const { return m_singleton ? (&m_single_result+1) : m_many.end; } - bool empty() const { return begin()==end(); } + bool empty() const { return begin() == end(); } }; key_indexer(unsigned key_len, const unsigned * key_cols) @@ -299,7 +299,7 @@ namespace datalog { key_to_reserve(key); store_offset ofs = m_keys.insert_or_get_reserve_content(); index_map::entry * e = m_map.find_core(ofs); - if(!e) { + if (!e) { TRACE("dl_table_relation", tout << "inserting\n";); e = m_map.insert_if_not_there2(ofs, offset_vector()); } @@ -312,7 +312,7 @@ namespace datalog { m_first_nonindexed(0) {} virtual void update(const sparse_table & t) { - if(m_first_nonindexed==t.m_data.after_last_offset()) { + if (m_first_nonindexed == t.m_data.after_last_offset()) { return; } SASSERT(m_first_nonindexedget_data().m_value; @@ -381,15 +381,15 @@ namespace datalog { static bool can_handle(unsigned key_len, const unsigned * key_cols, const sparse_table & t) { unsigned non_func_cols = t.get_signature().first_functional(); - if(key_len!=non_func_cols) { + if (key_len!=non_func_cols) { return false; } counter ctr; ctr.count(key_len, key_cols); - if(ctr.get_max_counter_value()!=1 || ctr.get_max_positive()!=non_func_cols-1) { + if (ctr.get_max_counter_value()!=1 || ctr.get_max_positive()!=non_func_cols-1) { return false; } - SASSERT(ctr.get_positive_count()==non_func_cols); + SASSERT(ctr.get_positive_count() == non_func_cols); return true; } @@ -399,7 +399,7 @@ namespace datalog { SASSERT(can_handle(key_len, key_cols, t)); m_permutation.resize(key_len); - for(unsigned i=0; iget_data().m_value) { - if(full_signature_key_indexer::can_handle(key_len, key_cols, *this)) { + if (!key_map_entry->get_data().m_value) { + if (full_signature_key_indexer::can_handle(key_len, key_cols, *this)) { key_map_entry->get_data().m_value = alloc(full_signature_key_indexer, key_len, key_cols, *this); } else { @@ -488,7 +488,7 @@ namespace datalog { void sparse_table::reset_indexes() { key_index_map::iterator kmit = m_key_indexes.begin(); key_index_map::iterator kmend = m_key_indexes.end(); - for(; kmit!=kmend; ++kmit) { + for (; kmit!=kmend; ++kmit) { dealloc((*kmit).m_value); } m_key_indexes.reset(); @@ -499,8 +499,8 @@ namespace datalog { m_data.ensure_reserve(); char * reserve = m_data.get_reserve_ptr(); unsigned col_cnt = m_column_layout.size(); - for(unsigned i=0; i(*this); t.write_into_reserve(f.c_ptr()); unsigned func_col_cnt = get_signature().functional_columns(); - if(func_col_cnt==0) { + if (func_col_cnt == 0) { return t.m_data.reserve_content_already_present(); } else { store_offset ofs; - if(!t.m_data.find_reserve_content(ofs)) { + if (!t.m_data.find_reserve_content(ofs)) { return false; } unsigned sz = get_signature().size(); - for(unsigned i=func_col_cnt; i(*this); t.write_into_reserve(f.c_ptr()); store_offset ofs; - if(!t.m_data.find_reserve_content(ofs)) { + if (!t.m_data.find_reserve_content(ofs)) { return false; } unsigned sz = sig.size(); - for(unsigned i=sig.first_functional(); im_value; sp_table_vector::iterator it = vect->begin(); sp_table_vector::iterator end = vect->end(); - for(; it!=end; ++it) { + for (; it!=end; ++it) { (*it)->destroy(); //calling deallocate() would only put the table back into the pool } dealloc(vect); @@ -764,7 +764,7 @@ namespace datalog { table_pool::entry * e = m_pool.insert_if_not_there2(sig, 0); sp_table_vector * & vect = e->get_data().m_value; - if(vect==0) { + if (vect == 0) { vect = alloc(sp_table_vector); } IF_VERBOSE(12, verbose_stream() << "Recycle: " << t->get_size_estimate_bytes() << "\n";); @@ -776,7 +776,7 @@ namespace datalog { SASSERT(can_handle_signature(s)); sp_table_vector * vect; - if(!m_pool.find(s, vect) || vect->empty()) { + if (!m_pool.find(s, vect) || vect->empty()) { return alloc(sparse_table, *this, s); } sparse_table * res = vect->back(); @@ -793,7 +793,7 @@ namespace datalog { bool sparse_table_plugin::join_involves_functional(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if(col_cnt==0) { + if (col_cnt == 0) { return false; } return counter().count(col_cnt, cols1).get_max_positive()>=s1.first_functional() @@ -824,7 +824,7 @@ namespace datalog { //do indexing into the bigger one. If we simply do a product, we want the bigger //one to be at the outer iteration (then the small one will hopefully fit into //the cache) - if( (t1.row_count() > t2.row_count()) == (!m_cols1.empty()) ) { + if ( (t1.row_count() > t2.row_count()) == (!m_cols1.empty()) ) { sparse_table::self_agnostic_join_project(t2, t1, m_cols1.size(), m_cols2.c_ptr(), m_cols1.c_ptr(), m_removed_cols.c_ptr(), true, *res); } @@ -841,7 +841,7 @@ namespace datalog { unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { const table_signature & sig1 = t1.get_signature(); const table_signature & sig2 = t2.get_signature(); - if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() + if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { //We also don't allow indexes on functional columns (and they are needed for joins) return 0; @@ -854,8 +854,8 @@ namespace datalog { const unsigned * removed_cols) { const table_signature & sig1 = t1.get_signature(); const table_signature & sig2 = t2.get_signature(); - if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() - || removed_col_cnt==t1.get_signature().size()+t2.get_signature().size() + if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() + || removed_col_cnt == t1.get_signature().size()+t2.get_signature().size() || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { //We don't allow sparse tables with zero signatures (and project on all columns leads to such) //We also don't allow indexes on functional columns. @@ -876,8 +876,8 @@ namespace datalog { unsigned fact_size = tgt.m_fact_size; const char* ptr = src.m_data.begin(); const char* after_last=src.m_data.after_last(); - for(; ptradd_fact(ptr); } } @@ -886,7 +886,7 @@ namespace datalog { table_union_fn * sparse_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { - if(tgt.get_kind()!=get_kind() || src.get_kind()!=get_kind() + if (tgt.get_kind()!=get_kind() || src.get_kind()!=get_kind() || (delta && delta->get_kind()!=get_kind()) || tgt.get_signature()!=src.get_signature() || (delta && delta->get_signature()!=tgt.get_signature())) { @@ -913,8 +913,8 @@ namespace datalog { const sparse_table::column_layout & tgt_layout) { unsigned r_idx=0; unsigned tgt_i=0; - for(unsigned i=0; im_data.ensure_reserve(); char * res_ptr = res->m_data.get_reserve_ptr(); @@ -952,7 +952,7 @@ namespace datalog { table_transformer_fn * sparse_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { - if(col_cnt==t.get_signature().size()) { + if (col_cnt == t.get_signature().size()) { return 0; } return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); @@ -981,14 +981,14 @@ namespace datalog { sparse_table::key_indexer & indexer = t.get_key_indexer(1, &m_col); sparse_table::key_indexer::query_result t_offsets = indexer.get_matching_offsets(m_key); - if(t_offsets.empty()) { + if (t_offsets.empty()) { //no matches return res; } sparse_table::key_indexer::offset_iterator ofs_it=t_offsets.begin(); sparse_table::key_indexer::offset_iterator ofs_end=t_offsets.end(); - for(; ofs_it!=ofs_end; ++ofs_it) { + for (; ofs_it!=ofs_end; ++ofs_it) { sparse_table::store_offset t_ofs = *ofs_it; const char * t_ptr = t.get_at_offset(t_ofs); @@ -996,8 +996,8 @@ namespace datalog { char * res_reserve = res->m_data.get_reserve_ptr(); unsigned res_i = 0; - for(unsigned i=0; i=t.get_signature().first_functional()) { + if (t.get_kind()!=get_kind() || t.get_signature().size() == 1 || col>=t.get_signature().first_functional()) { //We don't allow sparse tables with zero signatures (and project on a single //column table produces one). //We also don't allow indexes on functional columns. And our implementation of @@ -1022,20 +1022,17 @@ namespace datalog { class sparse_table_plugin::rename_fn : public convenient_table_rename_fn { - const unsigned m_cycle_len; - const unsigned m_col_cnt; unsigned_vector m_out_of_cycle; public: rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) - : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle), - m_cycle_len(permutation_cycle_len), m_col_cnt(orig_sig.size()) { + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); idx_set cycle_cols; - for(unsigned i=0; im_data.begin(); char* res_end = res_ptr+res_data_size; - for(; res_ptr!=res_end; t_ptr+=t_fact_size, res_ptr+=res_fact_size) { + for (; res_ptr!=res_end; t_ptr+=t_fact_size, res_ptr+=res_fact_size) { transform_row(t_ptr, res_ptr, t.m_column_layout, res->m_column_layout); } //and insert them into the hash-map - for(unsigned i=0; i!=res_data_size; i+=res_fact_size) { + for (unsigned i=0; i!=res_data_size; i+=res_fact_size) { TRUSTME(res->m_data.insert_offset(i)); } @@ -1094,7 +1091,7 @@ namespace datalog { table_transformer_fn * sparse_table_plugin::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { - if(t.get_kind()!=get_kind()) { + if (t.get_kind()!=get_kind()) { return 0; } return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); @@ -1120,9 +1117,9 @@ namespace datalog { unsigned neg_fisrt_func = neg.get_signature().first_functional(); counter ctr; ctr.count(m_cols2); - m_joining_neg_non_functional = ctr.get_max_counter_value()==1 - && ctr.get_positive_count()==neg_fisrt_func - && (neg_fisrt_func==0 || ctr.get_max_positive()==neg_fisrt_func-1); + m_joining_neg_non_functional = ctr.get_max_counter_value() == 1 + && ctr.get_positive_count() == neg_fisrt_func + && (neg_fisrt_func == 0 || ctr.get_max_positive() == neg_fisrt_func-1); } /** @@ -1133,7 +1130,7 @@ namespace datalog { bool tgt_is_first, svector & res) { SASSERT(res.empty()); - if(!tgt_is_first) { + if (!tgt_is_first) { m_intersection_content.reset(); } @@ -1150,32 +1147,32 @@ namespace datalog { bool key_modified=true; key_indexer::query_result t2_offsets; store_offset t1_after_last = t1.m_data.after_last_offset(); - for(store_offset t1_ofs=0; t1_ofs(tgt0); const sparse_table & neg = static_cast(neg0); - if(m_cols1.size()==0) { - if(!neg.empty()) { + if (m_cols1.size() == 0) { + if (!neg.empty()) { tgt.reset(); } return; @@ -1204,14 +1201,14 @@ namespace datalog { //We don't do just the simple tgt.row_count()>neg.row_count() because the swapped case is //more expensive. The constant 4 is, however, just my guess what the ratio might be. - if(tgt.row_count()/4>neg.row_count()) { + if (tgt.row_count()/4>neg.row_count()) { collect_intersection_offsets(neg, tgt, false, to_remove); } else { collect_intersection_offsets(tgt, neg, true, to_remove); } - if(to_remove.empty()) { + if (to_remove.empty()) { return; } @@ -1229,7 +1226,7 @@ namespace datalog { table_intersection_filter_fn * sparse_table_plugin::mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { - if(!check_kind(t) || !check_kind(negated_obj) + if (!check_kind(t) || !check_kind(negated_obj) || join_involves_functional(t.get_signature(), negated_obj.get_signature(), joined_col_cnt, t_cols, negated_cols) ) { return 0; diff --git a/src/muz_qe/dl_sparse_table.h b/src/muz_qe/dl_sparse_table.h index 3920836e6..010277b6b 100644 --- a/src/muz_qe/dl_sparse_table.h +++ b/src/muz_qe/dl_sparse_table.h @@ -359,7 +359,7 @@ namespace datalog { typedef svector key_spec; //sequence of columns in a key typedef svector key_value; //values of key columns - typedef map, + typedef map, vector_eq_proc > key_index_map; static const store_offset NO_RESERVE = UINT_MAX; diff --git a/src/muz_qe/dl_table.h b/src/muz_qe/dl_table.h index 8dc0a355b..3a240c337 100644 --- a/src/muz_qe/dl_table.h +++ b/src/muz_qe/dl_table.h @@ -73,7 +73,7 @@ namespace datalog { class our_iterator_core; - typedef hashtable, + typedef hashtable, vector_eq_proc > storage; storage m_data; diff --git a/src/muz_qe/dl_table_relation.cpp b/src/muz_qe/dl_table_relation.cpp index fc661366d..3c30c58bb 100644 --- a/src/muz_qe/dl_table_relation.cpp +++ b/src/muz_qe/dl_table_relation.cpp @@ -354,6 +354,21 @@ namespace datalog { return alloc(tr_mutator_fn, tfun); } + relation_transformer_fn * table_relation_plugin::mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + if (!t.from_table()) + return 0; + + const table_relation & tr = static_cast(t); + table_transformer_fn * tfun = get_manager().mk_filter_interpreted_and_project_fn(tr.get_table(), + condition, removed_col_cnt, removed_cols); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_project(t.get_signature(), removed_col_cnt, removed_cols, sig); + return alloc(tr_transformer_fn, sig, tfun); + } + class table_relation_plugin::tr_intersection_filter_fn : public relation_intersection_filter_fn { scoped_ptr m_tfun; public: diff --git a/src/muz_qe/dl_table_relation.h b/src/muz_qe/dl_table_relation.h index 12f0d4eaa..f86143c0f 100644 --- a/src/muz_qe/dl_table_relation.h +++ b/src/muz_qe/dl_table_relation.h @@ -71,6 +71,8 @@ namespace datalog { virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, diff --git a/src/muz_qe/dl_util.cpp b/src/muz_qe/dl_util.cpp index 95d268510..1b1042345 100644 --- a/src/muz_qe/dl_util.cpp +++ b/src/muz_qe/dl_util.cpp @@ -158,36 +158,7 @@ namespace datalog { ::get_free_vars(trm, vars); return var_idx < vars.size() && vars[var_idx] != 0; } - - - void collect_vars(ast_manager & m, expr * e, var_idx_set & result) { - ptr_vector vars; - ::get_free_vars(e, vars); - unsigned sz = vars.size(); - for(unsigned i=0; iget_tail_size(); - for(unsigned i=0;iget_tail(i), result); - } - } - - void get_free_tail_vars(rule * r, ptr_vector& sorts) { - unsigned n = r->get_tail_size(); - for(unsigned i=0;iget_tail(i), sorts); - } - } - - void get_free_vars(rule * r, ptr_vector& sorts) { - get_free_vars(r->get_head(), sorts); - get_free_tail_vars(r, sorts); - } - unsigned count_variable_arguments(app * pred) { SASSERT(is_uninterp(pred)); @@ -202,26 +173,6 @@ namespace datalog { return res; } - void collect_non_local_vars(ast_manager & m, rule const * r, app * t, var_idx_set & result) { - collect_vars(m, r->get_head(), result); - unsigned sz = r->get_tail_size(); - for (unsigned i = 0; i < sz; i++) { - app * curr = r->get_tail(i); - if (curr != t) - collect_vars(m, curr, result); - } - } - - void collect_non_local_vars(ast_manager & m, rule const * r, app * t_1, app * t_2, var_idx_set & result) { - collect_vars(m, r->get_head(), result); - unsigned sz = r->get_tail_size(); - for (unsigned i = 0; i < sz; i++) { - app * curr = r->get_tail(i); - if (curr != t_1 && curr != t_2) - collect_vars(m, curr, result); - } - } - void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, unsigned & next_idx, varidx2var_map & varidx2var, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred) { expr_ref_buffer new_args(m); @@ -404,6 +355,7 @@ namespace datalog { void rule_counter::count_rule_vars(ast_manager & m, const rule * r, int coef) { + reset(); count_vars(m, r->get_head(), 1); unsigned n = r->get_tail_size(); for (unsigned i = 0; i < n; i++) { diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index b313f0ae3..ea2def025 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -54,6 +54,7 @@ namespace datalog { BMC_ENGINE, QBMC_ENGINE, TAB_ENGINE, + CLP_ENGINE, LAST_ENGINE }; @@ -81,33 +82,13 @@ namespace datalog { void flatten_or(expr* fml, expr_ref_vector& result); - - bool contains_var(expr * trm, unsigned var_idx); - /** - \brief Collect the variables in \c pred. - \pre \c pred must be a valid head or tail. - */ - void collect_vars(ast_manager & m, expr * pred, var_idx_set & result); - void collect_tail_vars(ast_manager & m, rule * r, var_idx_set & result); - - void get_free_vars(rule * r, ptr_vector& sorts); - /** \brief Return number of arguments of \c pred that are variables */ unsigned count_variable_arguments(app * pred); - /** - \brief Store in \c result the set of variables used by \c r when ignoring the tail \c t. - */ - void collect_non_local_vars(ast_manager & m, rule const * r, app * t, var_idx_set & result); - - /** - \brief Store in \c result the set of variables used by \c r when ignoring the tail elements \c t_1 and \c t_2. - */ - void collect_non_local_vars(ast_manager & m, rule const * r, app * t_1, app * t_2, var_idx_set & result); template void copy_nonvariables(app * src, T& tgt) @@ -198,8 +179,6 @@ namespace datalog { { bool values_match(const expr * v1, const expr * v2); - ast_manager & m_manager; - unsigned_vector m_args1; unsigned_vector m_args2; @@ -209,9 +188,11 @@ namespace datalog { static unsigned expr_cont_get_size(app * a) { return a->get_num_args(); } static expr * expr_cont_get(app * a, unsigned i) { return a->get_arg(i); } static unsigned expr_cont_get_size(const ptr_vector & v) { return v.size(); } + static unsigned expr_cont_get_size(const expr_ref_vector & v) { return v.size(); } static expr * expr_cont_get(const ptr_vector & v, unsigned i) { return v[i]; } + static expr * expr_cont_get(const expr_ref_vector & v, unsigned i) { return v[i]; } public: - variable_intersection(ast_manager & m) : m_manager(m), m_consts(m) {} + variable_intersection(ast_manager & m) : m_consts(m) {} unsigned size() const { return m_args1.size(); @@ -316,6 +297,12 @@ namespace datalog { } container[i-ofs] = container[i]; } + if (r_i != removed_col_cnt) { + for (unsigned i = 0; i < removed_col_cnt; ++i) { + std::cout << removed_cols[i] << " "; + } + std::cout << " container size: " << n << "\n"; + } SASSERT(r_i==removed_col_cnt); container.resize(n-removed_col_cnt); } @@ -581,17 +568,31 @@ namespace datalog { } template - unsigned int_vector_hash(const T & cont) { - return string_hash(reinterpret_cast(cont.c_ptr()), - cont.size()*sizeof(typename T::data), 0); + struct default_obj_chash { + unsigned operator()(T const& cont, unsigned i) const { + return cont[i]->hash(); + } + }; + template + unsigned obj_vector_hash(const T & cont) { + return get_composite_hash(cont, cont.size(),default_kind_hash_proc(), default_obj_chash()); } template - struct int_vector_hash_proc { + struct obj_vector_hash_proc { unsigned operator()(const T & cont) const { - return int_vector_hash(cont); + return obj_vector_hash(cont); } }; + + template + struct svector_hash_proc { + unsigned operator()(const svector & cont) const { + return svector_hash()(cont); + } + }; + + template struct vector_eq_proc { bool operator()(const T & c1, const T & c2) const { return vectors_equal(c1, c2); } @@ -759,11 +760,6 @@ namespace datalog { // // ----------------------------------- - struct uint64_hash { - typedef uint64 data; - unsigned operator()(uint64 x) const { return hash_ull(x); } - }; - template void universal_delete(T* ptr) { dealloc(ptr); diff --git a/src/muz_qe/fdd.cpp b/src/muz_qe/fdd.cpp deleted file mode 100644 index 9ca5f758a..000000000 --- a/src/muz_qe/fdd.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - fdd.cpp - -Abstract: - - Finite decision diagram trie. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-07-03. - -Revision History: - - ---*/ - -#include "fdd.h" -#include "hash.h" -#include "bit_vector.h" -#include "trace.h" - -#define OFFSET_OF(th, ty, field) (unsigned char*)(&((ty*)(th))->field) - (unsigned char*)(ty*)(th) - -using namespace fdd; - -unsigned node::get_hash() const { - return string_hash((char*)this, static_cast(OFFSET_OF(this, node, m_ref_count)), 11); -} - -bool node::operator==(node const& other) const { - return - m_var == other.m_var && - m_lo == other.m_lo && - m_hi == other.m_hi; -} - - -// ------------------------------------------ -// manager - -manager::manager() : - m_alloc_node(2), - m_false(0), - m_true(1), - m_root(m_false) -{ - m_nodes.push_back(node()); // false - m_nodes.push_back(node()); // true - inc_ref(m_false); - inc_ref(m_true); - alloc_node(); // pre-allocate a node. -} - -manager::~manager() { -} - -void manager::alloc_node() { - unsigned index; - while (!m_free.empty()) { - index = m_free.back(); - node& n = m_nodes[index]; - m_free.pop_back(); - if (n.get_ref_count() == 0) { - if (!is_leaf(n.lo())) { - m_free.push_back(n.lo()); - } - if (!is_leaf(n.hi())) { - m_free.push_back(n.hi()); - } - m_alloc_node = index; - m_table.erase(n); - return; - } - } - index = m_nodes.size(); - m_nodes.push_back(node()); - m_alloc_node = index; -} - -node_id manager::mk_node(unsigned var, node_id lo, node_id hi) { - if (lo == hi) { - return lo; - } - node n(var, lo, hi); - unsigned index = m_alloc_node; - - node_id result = m_table.insert_if_not_there(n, index).m_value; - - if (result == index) { - alloc_node(); - m_nodes[result] = n; - inc_ref(lo); - inc_ref(hi); - } - - TRACE("fdd", tout << "mk_node: " << var << " " << lo << " " << hi << " -> " << result << "\n";); - - return result; -} - - -void manager::inc_ref(node_id n) { - TRACE("fdd", tout << "incref: " << n << "\n";); - if (!is_leaf(n)) { - m_nodes[n].inc_ref(); - } -} - -void manager::dec_ref(node_id n) { - if (!is_leaf(n) && 0 == m_nodes[n].dec_ref()) { - m_free.push_back(n); - } -} - -void manager::setup_keys(Key const* keys) { - for (unsigned i = 0; i < m_num_keys; ++i) { - m_keys[i] = (uint64)keys[i]; - m_sign[i] = keys[i] < 0; - } - -} - -void manager::insert(Key const* keys) { - setup_keys(keys); - m_insert_cache.reset(); - node_id result = insert_sign(m_num_idx + m_num_keys, m_root); - inc_ref(result); - dec_ref(m_root); - m_root = result; -} - -node_id manager::insert_sign(unsigned idx, node_id n) { - if (idx > m_num_idx) { - --idx; - bool s = idx2sign(idx); - node nd = m_nodes[n]; - if (!is_leaf(n) && nd.var() == idx) { - if (s) { - return mk_node(idx, insert_sign(idx, nd.lo()), nd.hi()); - } - else { - return mk_node(idx, nd.lo(), insert_sign(idx, nd.hi())); - } - } - else { - if (s) { - return mk_node(idx, insert_sign(idx, n), n); - } - else { - return mk_node(idx, n, insert_sign(idx, n)); - } - } - } - SASSERT(m_num_idx == idx); - return insert(idx, n); -} - -node_id manager::insert(unsigned idx, node_id n) { - node_id result; - SASSERT(0 <= idx && idx <= m_num_idx); - TRACE("fdd", tout << "insert: " << idx << " " << n << "\n";); - if (is_leaf(n)) { - while (idx > 0) { - --idx; - if (idx2bit(idx) && !is_dont_care(idx2key(idx))) { - return mk_node(idx, n, insert(idx, n)); - } - } - return m_true; - } - - SASSERT(0 < idx); - --idx; - - config c(m_dont_cares, idx, n); - if (m_insert_cache.find(c, result)) { - return result; - } - - node nd = m_nodes[n]; - SASSERT(idx >= nd.var()); - while (idx > nd.var()) { - if (idx2bit(idx) && !is_dont_care(idx2key(idx))) { - return mk_node(idx, n, insert(idx, n)); - } - --idx; - } - SASSERT(nd.var() == idx); - unsigned key = idx2key(idx); - if (is_dont_care(key)) { - result = mk_node(idx, insert(idx, nd.lo()), insert(idx, nd.hi())); - } - else { - bool bit = idx2bit(idx); - node_id lo, hi; - if (bit) { - hi = insert(idx, nd.hi()); - lo = nd.lo(); - } - else { - lo = insert(idx, nd.lo()); - scoped_dont_cares _set(*this, key); - hi = insert(idx, nd.hi()); - } - result = mk_node(idx, lo, hi); - } - m_insert_cache.insert(c, result); - return result; -} - -void manager::set_dont_care(unsigned key) { - SASSERT(!is_dont_care(key)); - m_dont_cares |= (1ull << key); -} - -void manager::unset_dont_care(unsigned key) { - m_dont_cares &= ~(1ull << key); -} - -bool manager::is_dont_care(unsigned key) const { - return 0 != (m_dont_cares & (1ull << key)); -} - -void manager::collect_statistics(statistics& st) const { - st.update("fdd.num_nodes", m_nodes.size()); -} - - -void manager::reset(unsigned num_keys) { - m_num_keys = num_keys; - m_num_idx = m_num_keys * m_num_bits; - m_dont_cares = 0; - m_sign.resize(num_keys); - m_keys.resize(num_keys); - SASSERT(num_keys <= 8*sizeof(m_dont_cares)); -} - - - -bool manager::find_le(Key const* keys) { - setup_keys(keys); - unsigned idx = m_num_idx + m_num_keys; - node_id n = m_root; - node nc = m_nodes[n]; - while (n > 1 && idx > m_num_idx) { - --idx; - if (nc.var() == idx) { - if (idx2sign(idx)) { - n = nc.lo(); - } - else { - n = nc.hi(); - } - nc = m_nodes[n]; - } - } - while (n > 1) { - SASSERT(idx > 0); - --idx; - while (nc.var() < idx) { - if (idx2bit(idx) && is_dont_care(idx2key(idx))) { - set_dont_care(idx2key(idx)); - } - --idx; - } - SASSERT(nc.var() == idx); - if (is_dont_care(idx2key(idx)) || idx2bit(idx)) { - n = nc.hi(); - } - else { - n = nc.lo(); - } - nc = m_nodes[n]; - } - m_dont_cares = 0; - return n == 1; -} - - -std::ostream& manager::display(std::ostream& out, node_id n) const{ - svector mark; - svector nodes; - nodes.push_back(n); - while (!nodes.empty()) { - n = nodes.back(); - nodes.pop_back(); - if (mark.size() <= n) { - mark.resize(n+1, false); - } - node const& nc = m_nodes[n]; - if (is_leaf(n) || mark[n]) { - continue; - } - nodes.push_back(nc.lo()); - nodes.push_back(nc.hi()); - mark[n] = true; - - if (nc.var() >= m_num_idx) { - out << n << " if " << idx2key(nc.var()) << " then " << nc.hi() << " else " << nc.lo() << "\n"; - } - else { - out << n << " if " << idx2key(nc.var()) << ":" << idx2bitnum(nc.var()) << " then " << nc.hi() << " else " << nc.lo() << "\n"; - } - } - return out; -} - diff --git a/src/muz_qe/fdd.h b/src/muz_qe/fdd.h deleted file mode 100644 index 59d5a78f5..000000000 --- a/src/muz_qe/fdd.h +++ /dev/null @@ -1,173 +0,0 @@ -/*++ -Copyright (c) 2007 Microsoft Corporation - -Module Name: - - fdd.h - -Abstract: - - Finite decision diagram. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-07-03. - -Revision History: - - ---*/ - -#ifndef __FDD_H__ -#define __FDD_H__ - -#include "hashtable.h" -#include "hash.h" -#include "map.h" -#include "vector.h" -#include "statistics.h" - -namespace fdd { - - - typedef unsigned node_id; - - class node { - unsigned m_var; - node_id m_lo; - node_id m_hi; - unsigned m_ref_count; - void reset(); - public: - node() : m_var(0), m_lo(0), m_hi(0), m_ref_count(0) {} - node(unsigned var, node_id l, node_id h): m_var(var), m_lo(l), m_hi(h), m_ref_count(0) {} - - unsigned get_hash() const; - bool operator==(node const& other) const; - - void inc_ref() { ++m_ref_count; } - unsigned dec_ref() { return --m_ref_count; } - unsigned get_ref_count() const { return m_ref_count; } - node_id lo() const { return m_lo; } - node_id hi() const { return m_hi; } - unsigned var() const { return m_var; } - - struct hash { unsigned operator()(node const& n) const { return n.get_hash(); } }; - struct eq { bool operator()(node const& l, node const& r) const { return l == r; } }; - std::ostream& display(std::ostream& out) const { return out << m_var << " " << m_lo << " " << m_hi << ""; } - }; - - inline std::ostream& operator<<(std::ostream& out, node const& n) { return n.display(out); } - - class config { - uint64 m_dont_cares; - unsigned m_idx; - node_id m_node; - public: - - config(): m_dont_cares(0), m_idx(0), m_node(0) {} - - config(uint64 dont_cares, unsigned idx, node_id n): - m_dont_cares(dont_cares), - m_idx(idx), - m_node(n) - {} - - struct hash { - unsigned operator()(config const& c) const { - return string_hash((char*)&c, sizeof(c), 12); - }; - }; - - struct eq { - bool operator()(config const& a, config const& b) const { - return - a.m_dont_cares == b.m_dont_cares && - a.m_idx == b.m_idx && - a.m_node == b.m_node; - } - }; - }; - - - class manager { - public: - typedef int64 Key; - typedef node::hash node_hash; - typedef node::eq node_eq; - typedef config::hash config_hash; - typedef config::eq config_eq; - private: - typedef map node_table; - typedef map insert_cache; - node_table m_table; - insert_cache m_insert_cache; - svector m_nodes; - unsigned_vector m_free; - unsigned m_alloc_node; - node_id m_false; - node_id m_true; - node_id m_root; - - static const unsigned m_num_bits = 64; - unsigned m_num_keys; - unsigned m_num_idx; // = m_num_keys * m_num_bits - - // state associated with insert. - svector m_keys; - svector m_sign; - - uint64 m_dont_cares; - - public: - manager(); - ~manager(); - - void reset(unsigned num_keys); - - void insert(Key const* keys); - - bool find_le(Key const* keys); - - void collect_statistics(statistics& st) const; - void reset_statistics() {} - unsigned size() const { return m_nodes.size(); } - - void display(std::ostream& out) const { display(out, m_root); } - - private: - void dec_ref(node_id n); - void inc_ref(node_id n); - node_id mk_node(unsigned var, node_id lo, node_id hi); - inline unsigned get_ref_count(node_id n) { return m_nodes[n].get_ref_count(); } - - std::ostream& display(std::ostream& out, node_id n) const; - - void setup_keys(Key const* keys); - node_id insert(unsigned idx, node_id n); - node_id insert_sign(unsigned idx, node_id n); - bool is_dont_care(unsigned idx) const; - - void set_dont_care(unsigned key); - void unset_dont_care(unsigned key); - - struct scoped_dont_cares { - manager& m; - unsigned m_key; - scoped_dont_cares(manager& m, unsigned key):m(m), m_key(key) { m.set_dont_care(key); } - ~scoped_dont_cares() { m.unset_dont_care(m_key); } - }; - - void alloc_node(); - - unsigned idx2key(unsigned i) const { return i % m_num_keys; } - unsigned idx2bitnum(unsigned i) const { SASSERT(i < m_num_idx); return (i / m_num_keys); } - bool idx2bit(unsigned i) const { return 0 != (m_keys[idx2key(i)] & (1LL << idx2bitnum(i))); } - bool idx2sign(unsigned i) const { return m_sign[idx2key(i)]; } - - bool is_leaf(node_id n) const { return n <= 1; } - - }; -}; - -#endif diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index 774559cdb..d38c3c9b0 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -60,7 +60,9 @@ def_module_params('fixedpoint', ('print_answer', BOOL, False, 'print answer instance(s) to query'), ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), ('print_statistics', BOOL, False, 'print statistics'), + ('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'), ('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), + ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), )) diff --git a/src/muz_qe/hilbert_basis.cpp b/src/muz_qe/hilbert_basis.cpp index 62f091dbc..a2a3f4654 100644 --- a/src/muz_qe/hilbert_basis.cpp +++ b/src/muz_qe/hilbert_basis.cpp @@ -21,7 +21,6 @@ Revision History: #include "heap.h" #include "map.h" #include "heap_trie.h" -#include "fdd.h" #include "stopwatch.h" @@ -237,64 +236,8 @@ public: void display(std::ostream& out) const { // m_trie.display(out); } - - }; -class hilbert_basis::value_index3 { - hilbert_basis& hb; - fdd::manager m_fdd; - unsigned m_offset; - svector m_keys; - - int64 const* get_keys(values const& vs) { - numeral const* nums = vs()-m_offset; - for (unsigned i = 0; i < m_keys.size(); ++i) { - m_keys[i] = nums[i].get_int64(); - } - return m_keys.c_ptr(); - } - -public: - - value_index3(hilbert_basis & hb): hb(hb), m_offset(1) {} - - void insert(offset_t, values const& vs) { - m_fdd.insert(get_keys(vs)); - } - - bool find(offset_t, values const& vs) { - return m_fdd.find_le(get_keys(vs)); - } - - void reset(unsigned offset) { - m_offset = offset; - m_fdd.reset(hb.get_num_vars()+m_offset); - m_keys.resize(hb.get_num_vars()+m_offset); - } - - void collect_statistics(statistics& st) const { - m_fdd.collect_statistics(st); - } - - void reset_statistics() { - m_fdd.reset_statistics(); - } - - unsigned size() const { - return m_fdd.size(); - } - - void remove(offset_t idx, values const& vs) { - UNREACHABLE(); - } - - void display(std::ostream& out) const { - m_fdd.display(out); - } - - -}; class hilbert_basis::index { diff --git a/src/muz_qe/hnf.cpp b/src/muz_qe/hnf.cpp index 5a7d1c4ba..75a85061c 100644 --- a/src/muz_qe/hnf.cpp +++ b/src/muz_qe/hnf.cpp @@ -71,6 +71,9 @@ class hnf::imp { obj_map m_memoize_disj; obj_map m_memoize_proof; func_decl_ref_vector m_fresh_predicates; + expr_ref_vector m_body; + proof_ref_vector m_defs; + public: imp(ast_manager & m): @@ -82,7 +85,9 @@ public: m_refs(m), m_name("P"), m_qh(m), - m_fresh_predicates(m) { + m_fresh_predicates(m), + m_body(m), + m_defs(m) { } void operator()(expr * n, @@ -181,30 +186,32 @@ private: void mk_horn(expr_ref& fml, proof_ref& premise) { + SASSERT(!premise || fml == m.get_fact(premise)); expr* e1, *e2; - expr_ref_vector body(m); - proof_ref_vector defs(m); expr_ref fml0(m), fml1(m), fml2(m), head(m); proof_ref p(m); fml0 = fml; m_names.reset(); m_sorts.reset(); + m_body.reset(); + m_defs.reset(); m_qh.pull_quantifier(true, fml0, &m_sorts, &m_names); if (premise){ fml1 = bind_variables(fml0); if (!m_sorts.empty()) { proof* p1 = m.mk_pull_quant(fml, to_quantifier(fml1)); premise = mk_modus_ponens(premise, p1); + fml = fml1; } } head = fml0; while (m.is_implies(head, e1, e2)) { - body.push_back(e1); + m_body.push_back(e1); head = e2; } - datalog::flatten_and(body); + datalog::flatten_and(m_body); if (premise) { - p = m.mk_rewrite(fml0, mk_implies(body, head)); + p = m.mk_rewrite(fml0, mk_implies(m_body, head)); } // @@ -214,8 +221,8 @@ private: // A -> C // B -> C // - if (body.size() == 1 && m.is_or(body[0].get()) && contains_predicate(body[0].get())) { - app* _or = to_app(body[0].get()); + if (m_body.size() == 1 && m.is_or(m_body[0].get()) && contains_predicate(m_body[0].get())) { + app* _or = to_app(m_body[0].get()); unsigned sz = _or->get_num_args(); expr* const* args = _or->get_args(); for (unsigned i = 0; i < sz; ++i) { @@ -224,7 +231,7 @@ private: } if (premise) { - expr_ref f1 = bind_variables(mk_implies(body, head)); + expr_ref f1 = bind_variables(mk_implies(m_body, head)); expr* f2 = m.mk_and(sz, m_todo.c_ptr()+m_todo.size()-sz); proof_ref p2(m), p3(m); p2 = m.mk_def_axiom(m.mk_iff(f1, f2)); @@ -240,13 +247,13 @@ private: } - eliminate_disjunctions(body, defs); - p = mk_congruence(p, body, head, defs); + eliminate_disjunctions(m_body, m_defs); + p = mk_congruence(p, m_body, head, m_defs); - eliminate_quantifier_body(body, defs); - p = mk_congruence(p, body, head, defs); + eliminate_quantifier_body(m_body, m_defs); + p = mk_congruence(p, m_body, head, m_defs); - fml2 = mk_implies(body, head); + fml2 = mk_implies(m_body, head); fml = bind_variables(fml2); diff --git a/src/muz_qe/horn_subsume_model_converter.cpp b/src/muz_qe/horn_subsume_model_converter.cpp index 374333a9c..ced4e657b 100644 --- a/src/muz_qe/horn_subsume_model_converter.cpp +++ b/src/muz_qe/horn_subsume_model_converter.cpp @@ -28,10 +28,8 @@ Revision History: #include "well_sorted.h" void horn_subsume_model_converter::insert(app* head, expr* body) { - func_decl_ref pred(m); - expr_ref body_res(m); - VERIFY(mk_horn(head, body, pred, body_res)); - insert(pred.get(), body_res.get()); + m_delay_head.push_back(head); + m_delay_body.push_back(body); } void horn_subsume_model_converter::insert(app* head, unsigned sz, expr* const* body) { @@ -148,6 +146,7 @@ bool horn_subsume_model_converter::mk_horn( } void horn_subsume_model_converter::add_default_proc::operator()(app* n) { + // // predicates that have not been assigned values // in the Horn model are assumed false. @@ -174,6 +173,16 @@ void horn_subsume_model_converter::add_default_false_interpretation(expr* e, mod void horn_subsume_model_converter::operator()(model_ref& mr) { + + func_decl_ref pred(m); + expr_ref body_res(m); + for (unsigned i = 0; i < m_delay_head.size(); ++i) { + VERIFY(mk_horn(m_delay_head[i].get(), m_delay_body[i].get(), pred, body_res)); + insert(pred.get(), body_res.get()); + } + m_delay_head.reset(); + m_delay_body.reset(); + TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0);); for (unsigned i = m_funcs.size(); i > 0; ) { --i; diff --git a/src/muz_qe/horn_subsume_model_converter.h b/src/muz_qe/horn_subsume_model_converter.h index edde02b19..993f29cc9 100644 --- a/src/muz_qe/horn_subsume_model_converter.h +++ b/src/muz_qe/horn_subsume_model_converter.h @@ -43,6 +43,8 @@ class horn_subsume_model_converter : public model_converter { func_decl_ref_vector m_funcs; expr_ref_vector m_bodies; th_rewriter m_rewrite; + app_ref_vector m_delay_head; + expr_ref_vector m_delay_body; void add_default_false_interpretation(expr* e, model_ref& md); @@ -56,7 +58,9 @@ class horn_subsume_model_converter : public model_converter { public: - horn_subsume_model_converter(ast_manager& m): m(m), m_funcs(m), m_bodies(m), m_rewrite(m) {} + horn_subsume_model_converter(ast_manager& m): + m(m), m_funcs(m), m_bodies(m), m_rewrite(m), + m_delay_head(m), m_delay_body(m) {} bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body); diff --git a/src/muz_qe/horn_tactic.cpp b/src/muz_qe/horn_tactic.cpp index 1a8f562d9..9d331cbfa 100644 --- a/src/muz_qe/horn_tactic.cpp +++ b/src/muz_qe/horn_tactic.cpp @@ -124,13 +124,23 @@ class horn_tactic : public tactic { enum formula_kind { IS_RULE, IS_QUERY, IS_NONE }; + bool is_implication(expr* f) { + expr* e1; + while (is_forall(f)) { + f = to_quantifier(f)->get_expr(); + } + while (m.is_implies(f, e1, f)) ; + return is_predicate(f); + } + formula_kind get_formula_kind(expr_ref& f) { - normalize(f); + expr_ref tmp(f); + normalize(tmp); ast_mark mark; expr_ref_vector args(m), body(m); expr_ref head(m); expr* a = 0, *a1 = 0; - datalog::flatten_or(f, args); + datalog::flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { a = args[i].get(); check_predicate(mark, a); @@ -147,12 +157,15 @@ class horn_tactic : public tactic { body.push_back(m.mk_not(a)); } } - f = m.mk_and(body.size(), body.c_ptr()); if (head) { - f = m.mk_implies(f, head); + if (!is_implication(f)) { + f = m.mk_and(body.size(), body.c_ptr()); + f = m.mk_implies(f, head); + } return IS_RULE; } else { + f = m.mk_and(body.size(), body.c_ptr()); return IS_QUERY; } } @@ -171,7 +184,7 @@ class horn_tactic : public tactic { tactic_report report("horn", *g); bool produce_proofs = g->proofs_enabled(); - if (produce_proofs) { + if (produce_proofs) { if (!m_ctx.get_params().generate_proof_trace()) { params_ref params = m_ctx.get_params().p; params.set_bool("generate_proof_trace", true); @@ -199,6 +212,7 @@ class horn_tactic : public tactic { break; default: msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n"; + TRACE("horn", tout << msg.str();); throw tactic_exception(msg.str().c_str()); } } @@ -229,7 +243,15 @@ class horn_tactic : public tactic { model_converter_ref & mc, proof_converter_ref & pc) { - lbool is_reachable = m_ctx.query(q); + lbool is_reachable = l_undef; + + try { + is_reachable = m_ctx.query(q); + } + catch (default_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); + throw ex; + } g->inc_depth(); bool produce_models = g->models_enabled(); @@ -239,10 +261,13 @@ class horn_tactic : public tactic { switch (is_reachable) { case l_true: { // goal is unsat - g->assert_expr(m.mk_false()); if (produce_proofs) { proof_ref proof = m_ctx.get_proof(); pc = proof2proof_converter(m, proof); + g->assert_expr(m.mk_false(), proof, 0); + } + else { + g->assert_expr(m.mk_false()); } break; } diff --git a/src/muz_qe/imdd.cpp b/src/muz_qe/imdd.cpp deleted file mode 100644 index 1d7c6d2b2..000000000 --- a/src/muz_qe/imdd.cpp +++ /dev/null @@ -1,3688 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - imdd.cpp - -Abstract: - - Interval based Multiple-valued Decision Diagrams. - -Author: - - Leonardo de Moura (leonardo) 2010-10-13. - -Revision History: - ---*/ - -#include"imdd.h" -#include"map.h" - -#define DEFAULT_SIMPLE_MAX_ENTRIES 32 - -inline bool is_cached(imdd2imdd_cache & cache, imdd * d, imdd * & r) { - if (d->is_shared()) { - if (cache.find(d, r) && (r == 0 || !r->is_dead())) - return true; - } - return false; -} - -inline void cache_result(imdd2imdd_cache & cache, imdd * d, imdd * r) { - if (d->is_shared()) - cache.insert(d, r); -} - -inline bool is_cached(imdd_pair2imdd_cache & cache, imdd * d1, imdd * d2, imdd * & r) { - if (d1->is_shared() && d2->is_shared()) { - if (cache.find(d1, d2, r) && (r == 0 || !r->is_dead())) - return true; - } - return false; -} - -inline void cache_result(imdd_pair2imdd_cache & cache, imdd * d1, imdd * d2, imdd * r) { - if (d1->is_shared() && d2->is_shared()) - cache.insert(d1, d2, r); -} - -inline bool destructive_update_at(bool destructive, imdd * d) { - return destructive && !d->is_memoized() && !d->is_shared(); -} - -void sl_imdd_manager::inc_ref_eh(imdd * v) { - m_manager->inc_ref(v); -} - -void sl_imdd_manager::dec_ref_eh(imdd * v) { - m_manager->dec_ref(v); -} - -unsigned imdd::hc_hash() const { - unsigned r = 0; - r = get_arity(); - imdd_children::iterator it = begin_children(); - imdd_children::iterator end = end_children(); - for (; it != end; ++it) { - unsigned b = it->begin_key(); - unsigned e = it->end_key(); - imdd const * child = it->val(); - mix(b, e, r); - if (child) { - SASSERT(child->is_memoized()); - unsigned v = child->get_id(); - mix(v, v, r); - } - } - return r; -} - -bool imdd::hc_equal(imdd const * other) const { - if (m_arity != other->m_arity) - return false; - return m_children.is_equal(other->m_children); -} - -imdd_manager::delay_dealloc::~delay_dealloc() { - SASSERT(m_manager.m_to_delete.size() >= m_to_delete_size); - ptr_vector::iterator it = m_manager.m_to_delete.begin() + m_to_delete_size; - ptr_vector::iterator end = m_manager.m_to_delete.end(); - for (; it != end; ++it) { - SASSERT((*it)->is_dead()); - m_manager.deallocate_imdd(*it); - } - m_manager.m_to_delete.shrink(m_to_delete_size); - m_manager.m_delay_dealloc = m_delay_dealloc_value; -} - -imdd_manager::imdd_manager(): - m_sl_manager(m_alloc), - m_simple_max_entries(DEFAULT_SIMPLE_MAX_ENTRIES), - m_delay_dealloc(false) { - m_sl_manager.m_manager = this; - m_swap_new_child = 0; -} - -imdd * imdd_manager::_mk_empty(unsigned arity) { - SASSERT(arity > 0); - void * mem = m_alloc.allocate(sizeof(imdd)); - return new (mem) imdd(m_sl_manager, m_id_gen.mk(), arity); -} - -imdd * imdd_manager::defrag_core(imdd * d) { - if (d->is_memoized()) - return d; - unsigned h = d->get_arity(); - if (h == 1 && !is_simple_node(d)) - return d; - imdd * new_d = 0; - if (is_cached(m_defrag_cache, d, new_d)) - return new_d; - - if (h == 1) { - SASSERT(is_simple_node(d)); - imdd * new_can_d = memoize(d); - cache_result(m_defrag_cache, d, new_can_d); - return new_can_d; - } - - SASSERT(h > 1); - new_d = _mk_empty(h); - imdd_children::push_back_proc push_back(m_sl_manager, new_d->m_children); - bool has_new = false; - bool children_memoized = true; - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - imdd * new_child = defrag_core(child); - if (child != new_child) - has_new = true; - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - - if (has_new) { - if (children_memoized && is_simple_node(new_d)) { - imdd * new_can_d = memoize(new_d); - if (new_can_d != new_d) { - SASSERT(new_d->get_ref_count() == 0); - delete_imdd(new_d); - } - new_d = new_can_d; - } - } - else { - SASSERT(!has_new); - delete_imdd(new_d); - new_d = d; - if (children_memoized && is_simple_node(new_d)) { - new_d = memoize(new_d); - } - } - - cache_result(m_defrag_cache, d, new_d); - return new_d; -} - -/** - \brief Compress the given IMDD by using hash-consing. -*/ -void imdd_manager::defrag(imdd_ref & d) { - delay_dealloc delay(*this); - m_defrag_cache.reset(); - d = defrag_core(d); -} - -/** - \brief Memoize the given IMDD. - Return an IMDD structurally equivalent to d. - This method assumes the children of d are memoized. - If that is not the case, the user should invoke defrag instead. -*/ -imdd * imdd_manager::memoize(imdd * d) { - if (d->is_memoized()) - return d; - unsigned h = d->get_arity(); - m_tables.reserve(h); - DEBUG_CODE({ - if (h > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - SASSERT(it->val()->is_memoized()); - } - } - }); - imdd * r = m_tables[h-1].insert_if_not_there(d); - if (r == d) { - r->mark_as_memoized(); - } - SASSERT(r->is_memoized()); - return r; -} - -/** - \brief Remove the given IMDD from the hash-consing table -*/ -void imdd_manager::unmemoize(imdd * d) { - SASSERT(d->is_memoized()); - m_tables[d->get_arity()-1].erase(d); - d->mark_as_memoized(false); -} - - -/** - \brief Remove the given IMDD (and its children) from the hash-consing tables. -*/ -void imdd_manager::unmemoize_rec(imdd * d) { - SASSERT(m_worklist.empty()); - m_worklist.push_back(d); - while (!m_worklist.empty()) { - d = m_worklist.back(); - if (d->is_memoized()) { - unmemoize(d); - SASSERT(!d->is_memoized()); - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) - m_worklist.push_back(it->val()); - } - } - } -} - -bool imdd_manager::is_simple_node(imdd * d) const { - return !d->m_children.has_more_than_k_entries(m_simple_max_entries); -} - -void imdd_manager::mark_as_dead(imdd * d) { - // The references to the children were decremented by delete_imdd. - SASSERT(!d->is_dead()); - d->m_children.deallocate_no_decref(m_sl_manager); - d->mark_as_dead(); - if (m_delay_dealloc) - m_to_delete.push_back(d); - else - deallocate_imdd(d); -} - -void imdd_manager::deallocate_imdd(imdd * d) { - SASSERT(d->is_dead()); - memset(d, 0, sizeof(*d)); - m_alloc.deallocate(sizeof(imdd), d); -} - -void imdd_manager::delete_imdd(imdd * d) { - SASSERT(m_worklist.empty()); - m_worklist.push_back(d); - - while (!m_worklist.empty()) { - d = m_worklist.back(); - m_worklist.pop_back(); - - m_id_gen.recycle(d->get_id()); - - SASSERT(d->get_ref_count() == 0); - if (d->is_memoized()) { - unsigned arity = d->get_arity(); - SASSERT(m_tables[arity-1].contains(d)); - m_tables[arity-1].erase(d); - } - - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - SASSERT(child); - child->dec_ref(); - if (child->get_ref_count() == 0) - m_worklist.push_back(child); - } - } - - mark_as_dead(d); - } -} - -/** - \brief Return a (non-memoized) shallow copy of d. -*/ -imdd * imdd_manager::copy_main(imdd * d) { - imdd * d_copy = _mk_empty(d->get_arity()); - d_copy->m_children.copy(m_sl_manager, d->m_children); - SASSERT(!d_copy->is_memoized()); - SASSERT(d_copy->get_ref_count() == 0); - return d_copy; -} - -/** - \brief Insert the values [b, e] into a IMDD of arity 1. - - If destructive == true, d is not memoized and is not shared, then a - destructive update is performed, and d is returned. - - Otherwise, a fresh IMDD is returned. -*/ -imdd * imdd_manager::insert_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == 1); - if (destructive_update_at(destructive, d)) { - add_child(d, b, e, 0); - return d; - } - else { - imdd * new_d = copy_main(d); - add_child(new_d, b, e, 0); - return memoize_new_imdd_if(memoize_res, new_d); - } -} - - -/** - \brief Remove the values [b, e] from an IMDD of arity 1. - - If destructive == true, d is not memoized and is not shared, then a - destructive update is performed, and d is returned. - - Otherwise, a fresh IMDD is returned. -*/ -imdd * imdd_manager::remove_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == 1); - if (destructive_update_at(destructive, d)) { - remove_child(d, b, e); - return d; - } - else { - imdd * new_d = copy_main(d); - remove_child(new_d, b, e); - return memoize_new_imdd_if(memoize_res, new_d); - } -} - -/** - \brief Auxiliary functor used to implement destructive version of mk_product. -*/ -struct imdd_manager::null2imdd_proc { - imdd * m_d2; - null2imdd_proc(imdd * d2):m_d2(d2) {} - imdd * operator()(imdd * d) { SASSERT(d == 0); return m_d2; } -}; - -/** - \brief Auxiliary functor used to implement destructive version of mk_product. -*/ -struct imdd_manager::mk_product_proc { - imdd_manager & m_manager; - imdd * m_d2; - bool m_memoize; - mk_product_proc(imdd_manager & m, imdd * d2, bool memoize): - m_manager(m), - m_d2(d2), - m_memoize(memoize) { - } - imdd * operator()(imdd * d1) { return m_manager.mk_product_core(d1, m_d2, true, m_memoize); } -}; - -imdd * imdd_manager::mk_product_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - if (destructive && !d1->is_shared()) { - if (d1->is_memoized()) - unmemoize(d1); - - if (d1->get_arity() == 1) { - null2imdd_proc f(d2); - d1->m_children.update_values(m_sl_manager, f); - d1->m_arity += d2->m_arity; - } - else { - mk_product_proc f(*this, d2, memoize_res); - d1->m_children.update_values(m_sl_manager, f); - d1->m_arity += d2->m_arity; - } - return d1; - } - else { - imdd * new_d1 = 0; - if (is_cached(m_mk_product_cache, d1, new_d1)) - return new_d1; - unsigned arity1 = d1->get_arity(); - unsigned arity2 = d2->get_arity(); - new_d1 = _mk_empty(arity1 + arity2); - imdd_children::push_back_proc push_back(m_sl_manager, new_d1->m_children); - imdd_children::iterator it = d1->begin_children(); - imdd_children::iterator end = d1->end_children(); - bool children_memoized = true; - for (; it != end; ++it) { - imdd * new_child; - if (arity1 == 1) - new_child = d2; - else - new_child = mk_product_core(it->val(), d2, false, memoize_res); - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - new_d1 = memoize_new_imdd_if(memoize_res && children_memoized, new_d1); - cache_result(m_mk_product_cache, d1, new_d1); - return new_d1; - } -} - -imdd * imdd_manager::mk_product_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - if (d1->empty() || d2->empty()) { - unsigned a1 = d1->get_arity(); - unsigned a2 = d2->get_arity(); - return _mk_empty(a1 + a2); - } - delay_dealloc delay(*this); - if (d1 == d2) - destructive = false; - m_mk_product_cache.reset(); - return mk_product_core(d1, d2, destructive, memoize_res); -} - -void imdd_manager::init_add_facts_new_children(unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res) { - if (m_add_facts_new_children[num] != 0) { - DEBUG_CODE({ - for (unsigned i = 1; i <= num; i++) { - SASSERT(m_add_facts_new_children[i] != 0); - }}); - return; - } - for (unsigned i = 1; i <= num; i++) { - if (m_add_facts_new_children[i] == 0) { - imdd * new_child = _mk_empty(i); - unsigned b = lowers[num - i]; - unsigned e = uppers[num - i]; - bool prev_memoized = true; - if (i == 1) { - add_child(new_child, b, e, 0); - } - else { - SASSERT(m_add_facts_new_children[i-1] != 0); - prev_memoized = m_add_facts_new_children[i-1]->is_memoized(); - add_child(new_child, b, e, m_add_facts_new_children[i-1]); - } - new_child = memoize_new_imdd_if(memoize_res && prev_memoized, new_child); - m_add_facts_new_children[i] = new_child; - } - } - DEBUG_CODE({ - for (unsigned i = 1; i <= num; i++) { - SASSERT(m_add_facts_new_children[i] != 0); - }}); -} - -imdd * imdd_manager::add_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - imdd_ref new_child(*this); - bool new_children_memoized = true; -#define INIT_NEW_CHILD() { \ - if (new_child == 0) { \ - init_add_facts_new_children(num - 1, lowers + 1, uppers + 1, memoize_res); \ - new_child = m_add_facts_new_children[num-1]; \ - if (!new_child->is_memoized()) new_children_memoized = false; \ - }} - - if (destructive_update_at(destructive, d)) { - if (num == 1) - return insert_main(d, *lowers, *uppers, destructive, memoize_res); - SASSERT(num > 1); - sbuffer to_insert; - new_child = m_add_facts_new_children[num-1]; - - unsigned b = *lowers; - unsigned e = *uppers; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - for (; it != end && b <= e; ++it) { - imdd_children::entry const & curr_entry = *it; - if (e < curr_entry.begin_key()) - break; - if (b < curr_entry.begin_key()) { - INIT_NEW_CHILD(); - SASSERT(b <= curr_entry.begin_key() - 1); - to_insert.push_back(entry(b, curr_entry.begin_key() - 1, new_child)); - b = curr_entry.begin_key(); - } - imdd * curr_child = curr_entry.val(); - SASSERT(b >= curr_entry.begin_key()); - bool cover = b == curr_entry.begin_key() && e >= curr_entry.end_key(); - // If cover == true, then the curr_child is completely covered by the new facts, and it is not needed anymore. - // So, we can perform a destructive update. - imdd * new_curr_child = add_facts_core(curr_child, num - 1, lowers + 1, uppers + 1, cover, memoize_res); - - if (e >= curr_entry.end_key()) { - SASSERT(b <= curr_entry.end_key()); - to_insert.push_back(entry(b, curr_entry.end_key(), new_curr_child)); - } - else { - SASSERT(e < curr_entry.end_key()); - SASSERT(b <= e); - to_insert.push_back(entry(b, e, new_curr_child)); - } - b = curr_entry.end_key() + 1; - } - // Re-insert entries in m_add_facts_to_insert into d->m_children, - // and if b <= e also insert [b, e] -> new_child - if (b <= e) { - INIT_NEW_CHILD(); - add_child(d, b, e, new_child); - } - - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(d, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - } - - return d; - } - else { - imdd * new_d = 0; - if (is_cached(m_add_facts_cache, d, new_d)) - return new_d; - - if (num == 1) { - new_d = insert_main(d, *lowers, *uppers, destructive, memoize_res); - } - else { - new_d = copy_main(d); - new_child = m_add_facts_new_children[num-1]; - TRACE("add_facts_bug", tout << "after copying: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";); - - unsigned b = *lowers; - unsigned e = *uppers; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - for (; it != end && b <= e; ++it) { - imdd_children::entry const & curr_entry = *it; - if (e < curr_entry.begin_key()) - break; - if (b < curr_entry.begin_key()) { - INIT_NEW_CHILD(); - SASSERT(b <= curr_entry.begin_key() - 1); - add_child(new_d, b, curr_entry.begin_key() - 1, new_child); - TRACE("add_facts_bug", tout << "after inserting new child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";); - b = curr_entry.begin_key(); - } - imdd * curr_child = curr_entry.val(); - imdd * new_curr_child = add_facts_core(curr_child, num - 1, lowers + 1, uppers + 1, false, memoize_res); - if (!new_curr_child->is_memoized()) - new_children_memoized = false; - if (e >= curr_entry.end_key()) { - SASSERT(b <= curr_entry.end_key()); - add_child(new_d, b, curr_entry.end_key(), new_curr_child); - TRACE("add_facts_bug", tout << "1) after inserting new curr child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n"; - tout << "new_curr_child: " << mk_ll_pp(new_curr_child, *this) << "\n";); - } - else { - SASSERT(e < curr_entry.end_key()); - SASSERT(b <= e); - add_child(new_d, b, e, new_curr_child); - TRACE("add_facts_bug", tout << "2) after inserting new curr child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n"; - tout << "new_curr_child: " << mk_ll_pp(new_curr_child, *this) << "\n";); - } - b = curr_entry.end_key() + 1; - } - if (b <= e) { - INIT_NEW_CHILD(); - add_child(new_d, b, e, new_child); - } - - new_d = memoize_new_imdd_if(memoize_res && d->is_memoized() && new_children_memoized, new_d); - } - - cache_result(m_add_facts_cache, d, new_d); - return new_d; - } -} - -imdd * imdd_manager::add_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - delay_dealloc delay(*this); - m_add_facts_cache.reset(); - m_add_facts_new_children.reset(); - m_add_facts_new_children.resize(num, 0); - return add_facts_core(d, num, lowers, uppers, destructive, memoize_res); -} - -inline void update_memoized_flag(imdd * d, bool & memoized_flag) { - if (d && !d->is_memoized()) - memoized_flag = false; -} - -void imdd_manager::push_back_entries(unsigned head, imdd_children::iterator & it, imdd_children::iterator & end, - imdd_children::push_back_proc & push_back, bool & children_memoized) { - if (it != end) { - push_back(head, it->end_key(), it->val()); - update_memoized_flag(it->val(), children_memoized); - ++it; - for (; it != end; ++it) { - update_memoized_flag(it->val(), children_memoized); - push_back(it->begin_key(), it->end_key(), it->val()); - } - } -} - -/** - \brief Push the entries starting at head upto the given limit (no included). - That is, we are copying the entries in the interval [head, limit). -*/ -void imdd_manager::push_back_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, - unsigned limit, imdd_children::push_back_proc & push_back, bool & children_memoized) { - SASSERT(it != end); - SASSERT(head <= it->end_key()); - SASSERT(head >= it->begin_key()); - SASSERT(head < limit); - while (head < limit && it != end) { - if (it->end_key() < limit) { - update_memoized_flag(it->val(), children_memoized); - push_back(head, it->end_key(), it->val()); - ++it; - if (it != end) - head = it->begin_key(); - } - else { - SASSERT(it->end_key() >= limit); - update_memoized_flag(it->val(), children_memoized); - push_back(head, limit-1, it->val()); - head = limit; - } - } - SASSERT(head == limit || it == end || head == it->begin_key()); -} - -void imdd_manager::move_head(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned new_head) { - SASSERT(new_head >= head); - SASSERT(head >= it->begin_key()); - SASSERT(head <= it->end_key()); - SASSERT(new_head <= it->end_key()); - if (new_head < it->end_key()) { - head = new_head+1; - SASSERT(head <= it->end_key()); - } - else { - SASSERT(new_head == it->end_key()); - ++it; - if (it != end) - head = it->begin_key(); - } -} - -/** - \brief Copy the entries starting at head upto the given limit (no included). - That is, we are copying the entries in the interval [head, limit). -*/ -void imdd_manager::copy_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, - unsigned limit, sbuffer & result) { - SASSERT(it != end); - SASSERT(head <= it->end_key()); - SASSERT(head >= it->begin_key()); - SASSERT(head < limit); - while (head < limit && it != end) { - if (it->end_key() < limit) { - result.push_back(entry(head, it->end_key(), it->val())); - ++it; - if (it != end) - head = it->begin_key(); - } - else { - SASSERT(it->end_key() >= limit); - result.push_back(entry(head, limit-1, it->val())); - head = limit; - } - } - SASSERT(head == limit || it == end || head == it->begin_key()); -} - -imdd * imdd_manager::mk_union_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - if (d1 == d2) - return d1; - if (destructive_update_at(destructive, d1)) { - if (d1->get_arity() == 1) { - imdd_children::iterator it = d2->begin_children(); - imdd_children::iterator end = d2->end_children(); - for (; it != end; ++it) - add_child(d1, it->begin_key(), it->end_key(), 0); - return d1; - } - else { - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - imdd_children::iterator it1 = d1->m_children.find_geq(it2->begin_key()); - imdd_children::iterator end1 = d1->end_children(); - sbuffer to_insert; - unsigned head1 = it1 != end1 ? it1->begin_key() : UINT_MAX; - SASSERT(it2 != end2); - unsigned head2 = it2->begin_key(); - while (true) { - if (it1 == end1) { - // copy it2 to d1 - // Remark: we don't need to copy to to_insert, since we will not be using it1 anymore. - // That is, we can directly insert into d1->m_children. - if (it2 != end2) { - add_child(d1, head2, it2->end_key(), it2->val()); - ++it2; - for (; it2 != end2; ++it2) - add_child(d1, it2->begin_key(), it2->end_key(), it2->val()); - } - break; - } - - if (it2 == end2) { - break; - } - - if (head1 < head2) { - it1.move_to(head2); - head1 = it1 != end1 ? (it1->begin_key() < head2?head2:it1->begin_key()): UINT_MAX; - } - else if (head1 > head2) { - copy_upto(head2, it2, end2, head1, to_insert); - } - else { - SASSERT(head1 == head2); - unsigned tail = std::min(it1->end_key(), it2->end_key()); - imdd * new_child = 0; - SASSERT(d1->get_arity() > 1); - bool cover = head1 == it1->begin_key() && tail == it1->end_key(); - // If cover == true, then the it1->val() (curr_child) is completely covered by - // the new_child, and it is not needed anymore. - // So, we can perform a destructive update. - new_child = mk_union_core(it1->val(), it2->val(), cover, memoize_res); - if (new_child != it1->val()) - to_insert.push_back(entry(head1, tail, new_child)); - move_head(head1, it1, end1, tail); - move_head(head2, it2, end2, tail); - } - } - sbuffer::const_iterator it3 = to_insert.begin(); - sbuffer::const_iterator end3 = to_insert.end(); - for (; it3 != end3; ++it3) - add_child(d1, it3->begin_key(), it3->end_key(), it3->val()); - return d1; - } - } - else { - imdd * r = 0; - if (is_cached(m_union_cache, d1, d2, r)) - return r; - - unsigned arity = d1->get_arity(); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - unsigned head1 = it1->begin_key(); - unsigned head2 = it2->begin_key(); - bool children_memoized = true; - while (true) { - if (it1 == end1) { - // copy it2 to result - push_back_entries(head2, it2, end2, push_back, children_memoized); - break; - } - if (it2 == end2) { - // copy it1 to result - push_back_entries(head1, it1, end1, push_back, children_memoized); - break; - } - - if (head1 < head2) { - push_back_upto(head1, it1, end1, head2, push_back, children_memoized); - } - else if (head1 > head2) { - push_back_upto(head2, it2, end2, head1, push_back, children_memoized); - } - else { - SASSERT(head1 == head2); - unsigned tail = std::min(it1->end_key(), it2->end_key()); - imdd * new_child = 0; - if (arity > 1) { - new_child = mk_union_core(it1->val(), it2->val(), false, memoize_res); - update_memoized_flag(new_child, children_memoized); - } - push_back(head1, tail, new_child); - move_head(head1, it1, end1, tail); - move_head(head2, it2, end2, tail); - } - } - r = memoize_new_imdd_if(memoize_res && children_memoized, r); - cache_result(m_union_cache, d1, d2, r); - return r; - } -} - -void imdd_manager::reset_union_cache() { - m_union_cache.reset(); -} - -imdd * imdd_manager::mk_union_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2) - return d1; - if (d1->empty()) - return d2; - if (d2->empty()) - return d1; - delay_dealloc delay(*this); - reset_union_cache(); - return mk_union_core(d1, d2, destructive, memoize_res); -} - -void imdd_manager::mk_union_core_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2 || d2->empty()) - return; - if (d1->empty()) { - d1 = d2; - return; - } - d1 = mk_union_core(d1, d2, true, memoize_res); -} - -void imdd_manager::mk_union_core(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2 || d2->empty()) { - r = d1; - return; - } - if (d1->empty()) { - r = d2; - return; - } - TRACE("mk_union_core", - tout << "d1:\n"; - display_ll(tout, d1); - tout << "d2:\n"; - display_ll(tout, d2);); - r = mk_union_core(d1, d2, false, memoize_res); -} - -imdd * imdd_manager::mk_complement_core(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - SASSERT(!d->empty()); - SASSERT(*mins <= *maxs); - unsigned arity = d->get_arity(); - imdd_ref new_child(*this); - bool new_children_memoized = true; -#undef INIT_NEW_CHILD -#define INIT_NEW_CHILD() { \ - if (arity > 1 && new_child == 0) { \ - init_add_facts_new_children(num - 1, mins + 1, maxs + 1, memoize_res); \ - new_child = m_add_facts_new_children[num-1]; \ - SASSERT(new_child != 0); \ - if (!new_child->is_memoized()) new_children_memoized = false; \ - }} - - if (false && destructive_update_at(destructive, d)) { - // TODO - NOT_IMPLEMENTED_YET(); - return 0; - } - else { - destructive = false; - imdd * r = 0; - if (is_cached(m_complement_cache, d, r)) - return r; - - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - unsigned prev_key = *mins; - for (; it != end; ++it) { - SASSERT(it->begin_key() >= *mins); - SASSERT(it->end_key() <= *maxs); - if (prev_key < it->begin_key()) { - INIT_NEW_CHILD(); - push_back(prev_key, it->begin_key() - 1, new_child); - } - if (arity > 1) { - imdd * new_curr = mk_complement_core(it->val(), num - 1, mins + 1, maxs + 1, false, memoize_res); - if (new_curr != 0) { - push_back(it->begin_key(), it->end_key(), new_curr); - if (!new_curr->is_memoized()) - new_children_memoized = false; - } - } - prev_key = it->end_key() + 1; - } - - if (prev_key <= *maxs) { - INIT_NEW_CHILD(); - push_back(prev_key, *maxs, new_child); - } - - if (r->empty()) { - delete_imdd(r); - r = 0; - } - - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_complement_cache, d, r); - return r; - } -} - -imdd * imdd_manager::mk_complement_main(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res) { - unsigned arity = d->get_arity(); - SASSERT(arity == num); - // reuse m_add_facts_new_children for creating the universe-set IMDDs - m_add_facts_new_children.reset(); - m_add_facts_new_children.resize(num+1, 0); - - if (d->empty()) { - // return the universe-set - init_add_facts_new_children(num, mins, maxs, memoize_res); - return m_add_facts_new_children[num]; - } - - delay_dealloc delay(*this); - m_complement_cache.reset(); - imdd * r = mk_complement_core(d, num, mins, maxs, destructive, memoize_res); - if (r == 0) - return _mk_empty(arity); - else - return r; -} - -/** - \brief Replace the IMDD children with new_children. - The function will also decrement the ref-counter of every IMDD in new_children, since - it assumes the counter was incremented when the IMDD was inserted into the buffer. -*/ -void imdd::replace_children(sl_imdd_manager & m, sbuffer & new_children) { - m_children.reset(m); - imdd_children::push_back_proc push_back(m, m_children); - svector::iterator it = new_children.begin(); - svector::iterator end = new_children.end(); - for (; it != end; ++it) { - SASSERT(it->val() == 0 || it->val()->get_ref_count() > 0); - push_back(it->begin_key(), it->end_key(), it->val()); - SASSERT(it->val() == 0 || it->val()->get_ref_count() > 1); - if (it->val() != 0) - it->val()->dec_ref(); - } -} - -imdd * imdd_manager::mk_filter_equal_core(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res) { - SASSERT(!d->empty()); - SASSERT(vidx >= 0 && vidx < d->get_arity()); - unsigned arity = d->get_arity(); - if (destructive_update_at(destructive, d)) { - if (vidx == 0) { - imdd * child = 0; - if (d->m_children.find(value, child)) { - imdd_ref ref(*this); - ref = child; // protect child, we don't want the following reset to delete it. - d->m_children.reset(m_sl_manager); - add_child(d, value, child); - return d; - } - else { - return 0; - } - } - SASSERT(arity > 1); - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = mk_filter_equal_core(curr_child, vidx-1, value, true, memoize_res); - if (new_child != 0) { - new_child->inc_ref(); // protect new child, we will be resetting d->m_children later. - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - } - if (to_insert.empty()) { - return 0; - } - d->replace_children(m_sl_manager, to_insert); - return d; - } - - imdd * r = 0; - if (is_cached(m_filter_equal_cache, d, r)) - return r; - bool new_children_memoized = true; - - if (vidx == 0) { - // found filter variable - imdd * child = 0; - if (d->m_children.find(value, child)) { - r = _mk_empty(arity); - add_child(r, value, child); - if (child && !child->is_memoized()) - new_children_memoized = false; - } - else { - r = 0; - } - } - else { - SASSERT(arity > 1); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = mk_filter_equal_core(curr_child, vidx-1, value, false, memoize_res); - if (new_child != 0) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - } - - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_filter_equal_cache, d, r); - return r; -} - -imdd * imdd_manager::mk_filter_equal_main(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res) { - unsigned arity = d->get_arity(); - SASSERT(vidx >= 0 && vidx < arity); - if (d->empty()) - return d; - delay_dealloc delay(*this); - m_filter_equal_cache.reset(); - imdd * r = mk_filter_equal_core(d, vidx, value, destructive, memoize_res); - if (r == 0) - return _mk_empty(arity); - else - return r; -} - - -/** - \brief create map from imdd nodes to the interval of variables that are covered by variable. - -*/ - -static void mk_interval_set_intersect(sl_manager_base &m, sl_interval_set const& src, unsigned b, unsigned e, sl_interval_set& dst) { - sl_interval_set::iterator it = src.find_geq(b); - sl_interval_set::iterator end = src.end(); - for (; it != end; ++it) { - unsigned b1 = it->begin_key(); - unsigned e1 = it->end_key(); - if (e < b1) { - break; - } - if (b1 < b) { - b1 = b; - } - if (e < e1) { - e1 = e; - } - SASSERT(b <= b1 && b1 <= e1 && e1 <= e); - dst.insert(m, b1, e1); - } -} - -static void mk_interval_set_union(sl_manager_base &m, sl_interval_set& dst, sl_interval_set const& src) { - sl_interval_set::iterator it = src.begin(), end = src.end(); - for (; it != end; ++it) { - dst.insert(m, it->begin_key(), it->end_key()); - } -} - -void imdd_manager::reset_fi_intervals(sl_imanager& m) { - for (unsigned i = 0; i < m_alloc_is.size(); ++i) { - m_alloc_is[i]->deallocate(m); - dealloc(m_alloc_is[i]); - } - m_alloc_is.reset(); - m_imdd2interval_set.reset(); -} - -sl_interval_set const* imdd_manager::init_fi_intervals(sl_imanager& m, imdd* d, unsigned var, unsigned num_found) { - sl_interval_set* result = 0; -#define ALLOC_RESULT() if (!result) { result = alloc(sl_interval_set, m); m_alloc_is.push_back(result); } - if (m_imdd2interval_set.find(d, result)) { - return result; - } - bool _is_fi_var = is_fi_var(var); - unsigned new_num_found = _is_fi_var?num_found+1:num_found; - bool last_fi_var = (new_num_found == m_fi_num_vars); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_children::entry const & curr_entry = *it; - unsigned b = curr_entry.begin_key(); - unsigned e = curr_entry.end_key(); - - if (last_fi_var) { - ALLOC_RESULT(); - result->insert(m, b, e, 1); - } - else { - SASSERT(d->get_arity() > 1); - imdd* curr_child = curr_entry.val(); - sl_interval_set const* is2 = init_fi_intervals(m, curr_child, var+1, new_num_found); - if (!is2) { - continue; - } - if (_is_fi_var) { - sl_interval_set is3(m); - mk_interval_set_intersect(m, *is2, b, e, is3); - if (!is3.empty()) { - ALLOC_RESULT(); - mk_interval_set_union(m, *result, is3); - } - is3.deallocate(m); - } - else { - if (is2 && result != is2) { - ALLOC_RESULT(); - mk_interval_set_union(m, *result, *is2); - } - } - } - } - m_imdd2interval_set.insert(d, result); - return result; -} - -inline mk_fi_result mk(imdd * d) { - mk_fi_result r; - r.m_d = d; - return r; -} - -inline mk_fi_result mk(fi_cache_entry * e) { - mk_fi_result r; - r.m_entry = e; - return r; -} - -fi_cache_entry * imdd_manager::mk_fi_cache_entry(imdd * d, unsigned lower, unsigned upper, unsigned num_pairs, imdd_value_pair pairs[]) { - void * mem = m_fi_entries.allocate(sizeof(fi_cache_entry) + sizeof(imdd_value_pair) * num_pairs); - DEBUG_CODE({ - for (unsigned i = 0; i < num_pairs; i++) { - SASSERT(pairs[i].first != 0); - } - }); - return new (mem) fi_cache_entry(d, lower, upper, num_pairs, pairs); -} - -struct imdd_value_pair_lt_value { - bool operator()(imdd_value_pair const & p1, imdd_value_pair const & p2) const { - return p1.second < p2.second; - } -}; - -// Review note: identical nodes should be unit intervals? -// 1 -> 1 -> x - -mk_fi_result imdd_manager::mk_filter_identical_core(imdd * d, unsigned var, unsigned num_found, unsigned lower, unsigned upper, - bool destructive, bool memoize_res) { - TRACE("imdd", tout << "#" << d->get_id() << " var: " << var << " num_found: " << num_found << - " lower: " << lower << " upper: " << upper << "\n";); - - unsigned arity = d->get_arity(); -#define MK_EMPTY_ENTRY (num_found == 0 && destructive_update_at(destructive, d))?mk(static_cast(0)):mk(static_cast(0)) - sl_interval_set* node_ranges = m_imdd2interval_set.find(d); - if (!node_ranges) { - return MK_EMPTY_ENTRY; - } - sl_interval_set::iterator sl_it = node_ranges->find_geq(lower); - if (sl_it == node_ranges->end() || upper < sl_it->begin_key()) { - return MK_EMPTY_ENTRY; - } - - bool new_children_memoized = true; - bool _is_fi_var = is_fi_var(var); - if (num_found == 0) { - SASSERT(arity > 1); - if (destructive_update_at(destructive, d)) { - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - if (_is_fi_var) { - fi_cache_entry * child_entry = (mk_filter_identical_core(curr_child, - var+1, - 1, - it->begin_key(), - it->end_key(), - false, - memoize_res)).m_entry; - for (unsigned i = 0; child_entry && i < child_entry->m_num_result; i++) { - imdd * new_child = child_entry->m_result[i].first; - unsigned value = child_entry->m_result[i].second; - to_insert.push_back(entry(value, value, new_child)); - } - } - else { - imdd * new_child = (mk_filter_identical_core(curr_child, var+1, 0, 0, UINT_MAX, false, memoize_res)).m_d; - if (new_child != 0) { - new_child->inc_ref(); - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - } - } - if (to_insert.empty()) { - return mk(static_cast(0)); - } - d->replace_children(m_sl_manager, to_insert); - return mk(d); - } - else { - // num_found == 0 && variable is not part of the filter && no destructive update. - imdd * r = 0; - if (is_cached(m_fi_top_cache, d, r)) - return mk(r); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - if (_is_fi_var) { - fi_cache_entry * entry = (mk_filter_identical_core(curr_child, - var+1, - 1, - it->begin_key(), - it->end_key(), - false, - memoize_res)).m_entry; - for (unsigned i = 0; entry && i < entry->m_num_result; i++) { - imdd * new_child = entry->m_result[i].first; - unsigned value = entry->m_result[i].second; - push_back(value, value, new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - else { - imdd * new_child = (mk_filter_identical_core(curr_child, var+1, 0, 0, UINT_MAX, false, memoize_res)).m_d; - if (new_child != 0) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_fi_top_cache, d, r); - return mk(r); - } - } - else { - SASSERT(num_found > 0); - fi_cache_entry d_entry(d, lower, upper); - fi_cache_entry * r_entry; - if (d->is_shared() && m_fi_bottom_cache.find(&d_entry, r_entry)) - return mk(r_entry); - sbuffer result; - if (_is_fi_var) { - unsigned new_num_found = num_found + 1; - bool last_fi_var = (new_num_found == m_fi_num_vars); - imdd_children::iterator it = d->m_children.find_geq(lower); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - unsigned curr_lower = it->begin_key(); - unsigned curr_upper = it->end_key(); - if (curr_lower < lower) - curr_lower = lower; - if (curr_upper > upper) - curr_upper = upper; - if (curr_lower <= curr_upper) { - if (last_fi_var) { - for (unsigned i = curr_lower; i <= curr_upper; i++) { - imdd * new_d = _mk_empty(arity); - add_child(new_d, i, curr_child); - new_d = memoize_new_imdd_if(memoize_res && (curr_child == 0 || curr_child->is_memoized()), new_d); - result.push_back(imdd_value_pair(new_d, i)); - } - } - else { - fi_cache_entry * new_entry = (mk_filter_identical_core(curr_child, - var+1, - new_num_found, - curr_lower, - curr_upper, - false, - memoize_res)).m_entry; - for (unsigned i = 0; new_entry && i < new_entry->m_num_result; i++) { - SASSERT(new_entry->m_result[i].first != 0); - imdd * curr_child = new_entry->m_result[i].first; - unsigned value = new_entry->m_result[i].second; - imdd * new_d = _mk_empty(arity); - add_child(new_d, value, curr_child); - SASSERT(curr_child != 0); - new_d = memoize_new_imdd_if(memoize_res && curr_child->is_memoized(), new_d); - result.push_back(imdd_value_pair(new_d, value)); - } - } - } - if (curr_upper == upper) - break; - } - } - else { - SASSERT(arity > 1); - u_map value2imdd; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - SASSERT(curr_child != 0); - fi_cache_entry * new_entry = (mk_filter_identical_core(curr_child, - var+1, - num_found, - lower, - upper, - false, - memoize_res)).m_entry; - for (unsigned i = 0; new_entry && i < new_entry->m_num_result; i++) { - unsigned value = new_entry->m_result[i].second; - imdd * new_child = new_entry->m_result[i].first; - imdd * new_d = 0; - if (!value2imdd.find(value, new_d)) { - new_d = _mk_empty(arity); - value2imdd.insert(value, new_d); - result.push_back(imdd_value_pair(new_d, value)); - } - SASSERT(new_d != 0); - add_child(new_d, it->begin_key(), it->end_key(), new_child); - } - } - std::sort(result.begin(), result.end(), imdd_value_pair_lt_value()); - } - r_entry = mk_fi_cache_entry(d, lower, upper, result.size(), result.c_ptr()); - if (d->is_shared()) - m_fi_bottom_cache.insert(r_entry); - return mk(r_entry); - } -} - -imdd * imdd_manager::mk_filter_identical_main(imdd * d, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - if (d->empty() || num_vars < 2) - return d; - TRACE("imdd", - tout << "vars: "; - for (unsigned i = 0; i < num_vars; ++i) { - tout << vars[i] << " "; - } - tout << "\n"; - tout << "rows: " << get_num_rows(d) << "\n"; - display_ll(tout, d); ); - unsigned arity = d->get_arity(); - DEBUG_CODE(for (unsigned i = 0; i < num_vars; ++i) SASSERT(vars[i] < arity);); - m_fi_num_vars = num_vars; - m_fi_begin_vars = vars; - m_fi_end_vars = vars + num_vars; - delay_dealloc delay(*this); - m_fi_bottom_cache.reset(); - m_fi_top_cache.reset(); - m_fi_entries.reset(); - - sl_imanager imgr; - init_fi_intervals(imgr, d, 0, 0); - - TRACE("imdd_verbose", - ptr_addr_hashtable ht; - imdd2intervals::iterator it = m_imdd2interval_set.begin(); - imdd2intervals::iterator end = m_imdd2interval_set.end(); - for (; it != end; ++it) { - tout << it->m_key->get_id() << " "; - if (it->m_value) { - if (ht.contains(it->m_value)) { - tout << "dup\n"; - continue; - } - ht.insert(it->m_value); - sl_interval_set::iterator sit = it->m_value->begin(); - sl_interval_set::iterator send = it->m_value->end(); - for (; sit != send; ++sit) { - tout << "[" << sit->begin_key() << ":" << sit->end_key() << "] "; - } - } - else { - tout << "{}"; - } - tout << "\n"; - }); - mk_fi_result r = mk_filter_identical_core(d, 0, 0, 0, UINT_MAX, destructive, memoize_res); - reset_fi_intervals(imgr); - - TRACE("imdd", if (r.m_d) display_ll(tout << "result\n", r.m_d);); - if (r.m_d == 0) - return _mk_empty(arity); - else - return r.m_d; -} - -void imdd_manager::swap_in(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars) { - // variables are sorted. - DEBUG_CODE(for (unsigned i = 0; i + 1 < num_vars; ++i) SASSERT(vars[i] < vars[i+1]);); - imdd_ref tmp1(*this), tmp2(*this); - tmp1 = d; - SASSERT(num_vars > 1); - unsigned v1 = vars[0]+1; // next position to swap to. - for (unsigned i = 1; i < num_vars; ++i) { - unsigned v2 = vars[i]; - SASSERT(v1 <= v2); - for (unsigned j = v2-1; j >= v1; --j) { - mk_swap(tmp1, tmp2, j); - tmp1 = tmp2; - } - ++v1; - } - TRACE("imdd", - for (unsigned i = 0; i < num_vars; ++i) tout << vars[i] << " "; - tout << "\n"; - display_ll(tout << "in\n", d); - display_ll(tout << "out\n", tmp1); - tout << "\n";); - r = tmp1; -} - -void imdd_manager::swap_out(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars) { - // variables are sorted. - DEBUG_CODE(for (unsigned i = 0; i + 1 < num_vars; ++i) SASSERT(vars[i] < vars[i+1]);); - imdd_ref tmp1(*this), tmp2(*this); - tmp1 = d; - SASSERT(num_vars > 1); - unsigned v1 = vars[0]+num_vars-1; // position of next variable to be swapped. - for (unsigned i = num_vars; i > 1; ) { - --i; - unsigned v2 = vars[i]; - for (unsigned j = v1; j < v2; ++j) { - mk_swap(tmp1, tmp2, j); - tmp1 = tmp2; - } - --v1; - } - TRACE("imdd", - for (unsigned i = 0; i < num_vars; ++i) tout << vars[i] << " "; - tout << "\n"; - display_ll(tout << "out\n", d); - display_ll(tout << "in\n", tmp1); - tout << "\n";); - r = tmp1; -} - -void imdd_manager::filter_identical_core2(imdd* d, unsigned num_vars, unsigned b, unsigned e, ptr_vector& ch) { - SASSERT(num_vars > 0); - imdd* r = 0; - - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->m_children.end(); - - for (; it != end; ++it) { - unsigned b1 = it->begin_key(); - unsigned e1 = it->end_key(); - if (e < b1) { - break; - } - if (b1 < b) { - b1 = b; - } - if (e <= e1) { - e1 = e; - } - SASSERT(b <= b1 && b1 <= e1 && e1 <= e); - imdd* curr_child = it->val(); - if (num_vars == 1) { - for (unsigned i = b1; i <= e1; ++i) { - r = _mk_empty(d->get_arity()); - add_child(r, i, i, it->val()); - r = memoize_new_imdd_if(!it->val() || it->val()->is_memoized(), r); - ch.push_back(r); - } - continue; - } - ptr_vector ch2; - filter_identical_core2(curr_child, num_vars-1, b1, e1, ch2); - for (unsigned i = 0; i < ch2.size(); ++i) { - r = _mk_empty(d->get_arity()); - unsigned key = ch2[i]->begin_children()->begin_key(); - SASSERT(ch2[i]->begin_children()->end_key() == key); - add_child(r, key, key, ch2[i]); - r = memoize_new_imdd_if(ch2[i]->is_memoized(), r); - ch.push_back(r); - } - } -} - -imdd* imdd_manager::filter_identical_core2(imdd* d, unsigned var, unsigned num_vars, bool memoize_res) { - imdd* r = 0; - if (m_filter_identical_cache.find(d, r)) { - return r; - } - - bool children_memoized = true; - - r = _mk_empty(d->get_arity()); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - if (var > 0) { - for (; it != end; ++it) { - imdd* curr_child = it->val(); - imdd* new_child = filter_identical_core2(curr_child, var-1, num_vars, memoize_res); - if (new_child) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) { - children_memoized = false; - } - } - } - } - else { - ptr_vector ch; - - for (; it != end; ++it) { - imdd* curr_child = it->val(); - filter_identical_core2(curr_child, num_vars-1, it->begin_key(), it->end_key(), ch); - } - for (unsigned i = 0; i < ch.size(); ++i) { - unsigned key = ch[i]->begin_children()->begin_key(); - SASSERT(ch[i]->begin_children()->end_key() == key); - push_back(key, key, ch[i]); - if (!ch[i]->is_memoized()) { - children_memoized = false; - } - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - m_filter_identical_cache.insert(d, r); - r = memoize_new_imdd_if(r && memoize_res && children_memoized, r); - return r; -} - -void imdd_manager::filter_identical_main2(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - m_filter_identical_cache.reset(); - imdd_ref tmp1(*this), tmp2(*this); - swap_in(d, tmp1, num_vars, vars); - tmp2 = filter_identical_core2(tmp1, vars[0], num_vars, memoize_res); - if (!tmp2) { - r = _mk_empty(d->get_arity()); - } - else { - swap_out(tmp2, r, num_vars, vars); - } - m_filter_identical_cache.reset(); -} - -void imdd_manager::filter_identical_main3(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - for (unsigned i = 0; i+1 < num_vars; ++i) { - imdd_ref tmp(*this); - tmp = r; - filter_identical_main3(tmp, r, vars[i], false, vars[i+1], false, memoize_res); - } -} - -void imdd_manager::filter_identical_main3(imdd * d, imdd_ref& r, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res) { - r = filter_identical_loop3(d, v1, del1, v2, del2, memoize_res); - if (r == 0) { - r = _mk_empty(d->get_arity()-del1-del2); - } - m_filter_identical_cache.reset(); -} - -imdd* imdd_manager::filter_identical_loop3(imdd * d, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res) { - imdd* r; - if (m_filter_identical_cache.find(d, r)) { - return r; - } - if (v1 == 0) { - return filter_identical_mk_nodes(d, v2, del1, del2, memoize_res); - } - - r = _mk_empty(d->get_arity()-del1-del2); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - bool children_memoized = true; - - for (; it != end; ++it) { - imdd* curr_child = it->val(); - imdd* new_child = filter_identical_loop3(curr_child, v1-1, del1, v2-1, del2, memoize_res); - if (!new_child) { - continue; - } - if (new_child->empty()) { - delete_imdd(new_child); - continue; - } - if (!new_child->is_memoized()) { - children_memoized = false; - } - push_back(it->begin_key(), it->end_key(), new_child); - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - m_filter_identical_cache.insert(d, r); - r = memoize_new_imdd_if(r && memoize_res && children_memoized, r); - return r; -} - -void imdd_manager::merge_intervals(svector& dst, svector const& src) { - svector& tmp = m_i_nodes_tmp; - tmp.reset(); - // invariant: intervals are sorted. - for (unsigned i = 0, j = 0; i < src.size() || j < dst.size();) { - SASSERT(!(i + 1 < src.size()) || src[i].m_hi < src[i+1].m_lo); - SASSERT(!(i + 1 < dst.size()) || dst[i].m_hi < dst[i+1].m_lo); - SASSERT(!(i < src.size()) || src[i].m_lo <= src[i].m_hi); - SASSERT(!(i < dst.size()) || dst[i].m_lo <= dst[i].m_hi); - if (i < src.size() && j < dst.size()) { - if (src[i].m_lo == dst[j].m_lo) { - tmp.push_back(src[i]); - ++i; - ++j; - } - else if (src[i].m_lo < dst[j].m_lo) { - tmp.push_back(src[i]); - ++i; - } - else { - tmp.push_back(dst[j]); - ++j; - } - } - else if (i < src.size()) { - tmp.push_back(src[i]); - ++i; - } - else { - tmp.push_back(dst[j]); - ++j; - } - } - dst.reset(); - dst.append(tmp); -} - -/** - * Propagate intervals down: what intervals can reach which nodes. - */ -imdd* imdd_manager::filter_identical_mk_nodes(imdd* d, unsigned v, bool del1, bool del2, bool memoize_res) { - SASSERT(v > 0); - - TRACE("imdd", display_ll(tout << "v: " << v << "\n", d);); - - // - // (0) - // Create map d |-> [I] from nodes to ordered set of disjoint intervals that visit the node. - // - // For each level up to 'v' create a list of nodes visited - // insert to a map the set of intervals that visit the node. - // - m_nodes.reset(); - filter_id_map& nodes = m_nodes; - imdd* d1, *d2, *d3; - vector > levels; - levels.push_back(ptr_vector()); - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd* curr_child; - for (; it != end; ++it) { - curr_child = it->val(); - svector& iv = nodes.init(curr_child); - if (iv.empty()) { - levels.back().push_back(curr_child); - } - iv.push_back(interval(it->begin_key(), it->end_key())); - } - - for (unsigned j = 0; j+1 < v; ++j) { - levels.push_back(ptr_vector()); - for (unsigned i = 0; i < levels[j].size(); ++i) { - d1 = levels[j][i]; - svector& i_nodes = nodes.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - for(; it != end; ++it) { - imdd* curr_child = it->val(); - svector& i_nodes2 = nodes.init(curr_child); - if (i_nodes2.empty()) { - levels[j+1].push_back(curr_child); - i_nodes2.append(i_nodes); - } - else { - merge_intervals(i_nodes2, i_nodes); - } - } - } - } - - TRACE("imdd", - for (unsigned i = 0; i < levels.size(); ++i) { - tout << "Level: " << i << "\n"; - for (unsigned j = 0; j < levels[i].size(); ++j) { - tout << levels[i][j]->get_id() << " "; - svector const& i_nodes = nodes.init(levels[i][j]); - for (unsigned k = 0; k < i_nodes.size(); ++k) { - tout << i_nodes[k].m_lo << ":" << i_nodes[k].m_hi << " "; - } - tout << "\n"; - } - } - ); - - - // - // (1) - // Intersect with children: - // - d [l1:h1:ch1][l2:h2:ch2][...] - // => produce_units: d |-> [uI1 |-> d'[uI1:ch1], uI2 |-> d''[uI2:ch1], ...] // unit intervals - // => del2: d |-> [I |-> union of ch1, ch2, .. under intersection] - // => del1 & !del2: d |-> [I |-> d'[I:ch]] // intersections of intervals. - // - - m_nodes_dd.reset(); - filter_idd_map& nodes_dd = m_nodes_dd; - SASSERT(levels.size() == v); - for (unsigned i = 0; i < levels[v-1].size(); ++i) { - d1 = levels[v-1][i]; - svector const & i_nodes = nodes.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - unsigned j = 0; - svector& i_nodes_dd = nodes_dd.init(d1); - while (it != end && j < i_nodes.size()) { - unsigned lo1 = it->begin_key(); - unsigned hi1 = it->end_key(); - unsigned lo2 = i_nodes[j].m_lo; - unsigned hi2 = i_nodes[j].m_hi; - if (hi2 < lo1) { - ++j; - } - else if (hi1 < lo2) { - ++it; - } - // lo1 <= hi2 && lo2 <= hi1 - else { - curr_child = it->val(); - unsigned lo = std::max(lo1, lo2); - unsigned hi = std::min(hi1, hi2); - SASSERT(lo <= hi); - - if (!del1 && !del2) { - for (unsigned k = lo; k <= hi; ++k) { - imdd* d2 = _mk_empty(d1->get_arity()); - add_child(d2, k, k, curr_child); - i_nodes_dd.push_back(interval_dd(k, k, d2)); - } - } - else if (del2) { - i_nodes_dd.push_back(interval_dd(lo, hi, curr_child)); // retrofill after loop. - } - else { - imdd* d2 = _mk_empty(d1->get_arity()); - add_child(d2, lo, hi, curr_child); - i_nodes_dd.push_back(interval_dd(lo, hi, d2)); - } - if (hi2 <= hi) { - ++j; - } - if (hi1 <= hi) { - ++it; - } - } - } - // take union of accumulated children. - // retrofill union inside list. - if (del2) { - d2 = 0; - for (unsigned k = 0; k < i_nodes_dd.size(); ++k) { - d3 = i_nodes_dd[k].m_dd; - if (!d2) { - d2 = d3; - } - else { - d2 = mk_union_core(d2, d3, true, memoize_res); - } - } - for (unsigned k = 0; k < i_nodes_dd.size(); ++k) { - i_nodes_dd[k].m_dd = d2; - } - } - } - - TRACE("imdd", print_filter_idd(tout, nodes_dd);); - - // - // (2) - // Move up: - // d1 |-> [I1] // intervals visiting d1 - // d1 |-> [lo:hi:child] // children of d1 - // child |-> [I2 |-> child'] // current decomposition - // result: - // d3 = d1' |-> [lo:hi:child'] - // d1 |-> [I3 |-> d3] for I3 in merge of [I1] and [I2] - // - // The merge is defined as the intersection of intervals that reside in I1 and - // the fractions in I2. They are decomposed so that all intervals are covered. - // By construction I2 are contained in I1, - // but they may be overlapping among different I2. - // - - for (unsigned i = v-1; i > 0; ) { - --i; - for (unsigned j = 0; j < levels[i].size(); ++j) { - d1 = levels[i][j]; - m_i_nodes_dd.reset(); - svector i_nodes = nodes.init(d1); - svector& i_nodes_dd = nodes_dd.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - unsigned num_children = 0; - for( ; it != end; ++it, ++num_children); - - m_offsets.reset(); - unsigned_vector& offsets = m_offsets; - offsets.resize(num_children); - it = d1->begin_children(); - for( ; it != end; ++it) { - curr_child = it->val(); - refine_intervals(i_nodes, nodes_dd.init(curr_child)); - } - - for (unsigned k = 0; k < i_nodes.size(); ++k) { - interval const& intv = i_nodes[k]; - d3 = _mk_empty(d1->get_arity()-del2); - it = d1->begin_children(); - for(unsigned child_id = 0; it != end; ++it, ++child_id) { - curr_child = it->val(); - svector const& ch_nodes_dd = nodes_dd.init(curr_child); - unsigned offset = offsets[child_id]; - TRACE("imdd_verbose", tout << intv.m_lo << ":" << intv.m_hi << "\n"; - for (unsigned l = offset; l < ch_nodes_dd.size(); ++l) { - tout << ch_nodes_dd[l].m_lo << ":" << ch_nodes_dd[l].m_hi << " " << ch_nodes_dd[l].m_dd->get_id() << " "; - } - tout << "\n"; - ); - - unsigned hi, lo; - d2 = 0; - while (offset < ch_nodes_dd.size() && !d2) { - lo = ch_nodes_dd[offset].m_lo; - hi = ch_nodes_dd[offset].m_hi; - if (intv.m_hi < lo) { - break; - } - if (hi < intv.m_lo) { - ++offset; - continue; - } - SASSERT(lo <= intv.m_lo); - SASSERT(intv.m_hi <= hi); - d2 = ch_nodes_dd[offset].m_dd; - if (intv.m_hi == hi) { - ++offset; - } - } - offsets[child_id] = offset; - if (d2) { - add_child(d3, it->begin_key(), it->end_key(), d2); - } - } - if (d3->empty()) { - delete_imdd(d3); - } - else { - i_nodes_dd.push_back(interval_dd(intv.m_lo, intv.m_hi, d3)); - } - } - TRACE("imdd", tout << d1->get_id() << ": "; print_interval_dd(tout, i_nodes_dd);); - - } - } - - TRACE("imdd", print_filter_idd(tout, nodes_dd);); - - // - // (3) - // Finalize: - // d |-> [I1:child] // children of d - // child |-> [I2 |-> child'] // current decomposition - // result: - // d' = union of child' // if del1 - // d' |-> [I2:child'] // if !del1 - // - - - it = d->begin_children(); - end = d->end_children(); - d1 = _mk_empty(d->get_arity()-del1-del2); - m_i_nodes_dd.reset(); - m_i_nodes_tmp.reset(); - svector& i_nodes_dd = m_i_nodes_dd; - svector& i_nodes_tmp = m_i_nodes_dd_tmp; - for (; it != end; ++it) { - curr_child = it->val(); - i_nodes_tmp.reset(); - svector const& i_nodes_dd1 = nodes_dd.init(curr_child); - for (unsigned i = 0, j = 0; i < i_nodes_dd.size() || j < i_nodes_dd1.size(); ) { - if (i < i_nodes_dd.size() && j < i_nodes_dd1.size()) { - interval_dd const& iv1 = i_nodes_dd[i]; - interval_dd const& iv2 = i_nodes_dd1[j]; - if (iv1.m_lo == iv2.m_lo) { - SASSERT(iv1.m_hi == iv2.m_hi); - SASSERT(iv1.m_dd == iv2.m_dd); - i_nodes_tmp.push_back(iv1); - ++i; - ++j; - } - else if (iv1.m_lo < iv2.m_lo) { - SASSERT(iv1.m_hi < iv2.m_lo); - i_nodes_tmp.push_back(iv1); - ++i; - } - else { - SASSERT(iv2.m_hi < iv1.m_lo); - i_nodes_tmp.push_back(iv2); - ++j; - } - } - else if (i < i_nodes_dd.size()) { - i_nodes_tmp.push_back(i_nodes_dd[i]); - ++i; - } - else if (j < i_nodes_dd1.size()) { - i_nodes_tmp.push_back(i_nodes_dd1[j]); - ++j; - } - } - i_nodes_dd.reset(); - i_nodes_dd.append(i_nodes_tmp); - } - - for (unsigned i = 0; i < i_nodes_dd.size(); ++i) { - imdd* ch = i_nodes_dd[i].m_dd; - unsigned lo = i_nodes_dd[i].m_lo; - unsigned hi = i_nodes_dd[i].m_hi; - if (del1) { - d1 = mk_union_core(d1, ch, true, memoize_res); - } - else { - add_child(d1, lo, hi, ch); - } - } - - TRACE("imdd", display_ll(tout, d1);); - - return d1; -} - - -void imdd_manager::print_interval_dd(std::ostream& out, svector const& m) { - for (unsigned i = 0; i < m.size(); ++i) { - out << m[i].m_lo << ":" << m[i].m_hi << " " << m[i].m_dd->get_id() << " "; - } - out << "\n"; -} - -void imdd_manager::print_filter_idd(std::ostream& out, filter_idd_map const& m) { - filter_idd_map::iterator it = m.begin(), end = m.end(); - for (unsigned i = 0; it != end; ++it, ++i) { - out << i << ": "; - print_interval_dd(out, *it); - } -} - -/** - \brief partition intervals of i_nodes by sub-intervals that are used in i_nodes_dd. - Assumption: - - All values covered in i_nodes_dd are present in i_nodes. -*/ - -void imdd_manager::refine_intervals(svector& i_nodes, svector const& i_nodes_dd) { - m_i_nodes.reset(); - svector& result = m_i_nodes; - unsigned sz1 = i_nodes.size(); - unsigned sz2 = i_nodes_dd.size(); - for (unsigned i = 0, j = 0; i < sz1; ++i) { - interval& iv = i_nodes[i]; - result.push_back(iv); - unsigned lo = iv.m_lo; - unsigned hi = iv.m_hi; - for (; j < sz2; ++j) { - unsigned lo1 = i_nodes_dd[j].m_lo; - unsigned hi1 = i_nodes_dd[j].m_hi; - SASSERT(lo <= hi); - SASSERT(lo1 <= hi1); - if (hi < lo1) { - break; - } - if (lo < lo1) { - result.back().m_hi = lo1-1; - result.push_back(interval(lo1, hi)); - lo = lo1; - } - SASSERT(lo1 <= lo); - if (lo > hi1) { - continue; - } - if (hi1 < hi) { - result.back().m_hi = hi1; - result.push_back(interval(hi1+1, hi)); - lo = hi1+1; - } - } - } - i_nodes.reset(); - i_nodes.append(result); -} - - - -void imdd_manager::mk_project_core(imdd * d, imdd_ref & r, unsigned var, unsigned num_found, bool memoize_res) { - unsigned arity = d->get_arity(); - bool _is_proj_var = is_proj_var(var); - - if (_is_proj_var && arity == (m_proj_num_vars - num_found)) { - r = 0; // 0 here means the unit element (aka the 0-tuple). - return; - } - - imdd * _r = 0; - if (is_cached(m_proj_cache, d, _r)) { - r = _r; - return; - } - - bool new_children_memoized = true; - if (_is_proj_var) { - SASSERT(d != 0); - SASSERT(d->get_arity() > 1); - unsigned new_var = var + 1; - unsigned new_num_found = num_found + 1; - bool found_all = new_num_found == m_proj_num_vars; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_ref tmp_r(*this); - CTRACE("imdd", it == end, display_ll(tout, d);); - SASSERT(it != end); - for (; it != end; ++it) { - imdd_ref new_child(*this); - if (found_all) - new_child = it->val(); - else - mk_project_core(it->val(), new_child, new_var, new_num_found, memoize_res); - - if (tmp_r == 0) - tmp_r = new_child; - else - mk_union_core(tmp_r, new_child, tmp_r, memoize_res); - SASSERT(tmp_r != 0); - } - SASSERT(tmp_r != 0); - SASSERT(tmp_r->get_arity() == d->get_arity() - (m_proj_num_vars - num_found)); - r = tmp_r; - } - else { - SASSERT(num_found < m_proj_num_vars); - unsigned new_var = var+1; - _r = _mk_empty(arity - (m_proj_num_vars - num_found)); - imdd_children::push_back_proc push_back(m_sl_manager, _r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - mk_project_core(curr_child, new_child, new_var, num_found, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (new_child != 0 && !new_child->is_memoized()) - new_children_memoized = false; - } - SASSERT(!_r->empty()); - _r = memoize_new_imdd_if(memoize_res && new_children_memoized, _r); - r = _r; - } - cache_result(m_proj_cache, d, r); -} - -void imdd_manager::mk_project_dupdt_core(imdd_ref & d, unsigned var, unsigned num_found, bool memoize_res) { - unsigned arity = d->get_arity(); - bool _is_proj_var = is_proj_var(var); - - if (_is_proj_var && arity == (m_proj_num_vars - num_found)) { - d = 0; // 0 here means the unit element (aka the 0-tuple). - return; - } - - if (!destructive_update_at(true, d)) { - mk_project_core(d, d, var, num_found, memoize_res); - return; - } - - if (_is_proj_var) { - SASSERT(d != 0); - SASSERT(d->get_arity() > 1); - unsigned new_var = var + 1; - unsigned new_num_found = num_found + 1; - bool found_all = new_num_found == m_proj_num_vars; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_ref r(*this); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - if (!found_all) { - mk_project_dupdt_core(new_child, new_var, new_num_found, memoize_res); - } - if (r == 0) - r = new_child; - else - mk_union_core_dupdt(r, new_child, memoize_res); - } - // we traverse the children of d, and decrement the reference counter of each one of them. - d->m_children.reset_no_decref(m_sl_manager); - d = r; - SASSERT(r != 0); - SASSERT(r->get_arity() == d->get_arity() - (m_proj_num_vars - num_found)); - } - else { - SASSERT(!_is_proj_var); - sbuffer to_insert; - unsigned new_var = var+1; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - mk_project_dupdt_core(new_child, new_var, num_found, memoize_res); - if (new_child) - new_child->inc_ref(); // protect new child, since we will insert it into to_insert - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); - } -} - -void imdd_manager::mk_project_init(unsigned num_vars, unsigned * vars) { - m_proj_num_vars = num_vars; - m_proj_begin_vars = vars; - m_proj_end_vars = vars + num_vars; - m_proj_cache.reset(); - reset_union_cache(); -} - -void imdd_manager::mk_project_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res) { - if (num_vars == 0) { - return; - } - unsigned arity = d->get_arity(); - SASSERT(num_vars < arity); - if (d->empty()) { - d = _mk_empty(arity - num_vars); - return; - } - delay_dealloc delay(*this); - mk_project_init(num_vars, vars); - mk_project_dupdt_core(d, 0, 0, memoize_res); -} - -void imdd_manager::mk_project(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res) { - if (num_vars == 0) { - r = d; - return; - } - unsigned arity = d->get_arity(); - SASSERT(num_vars < arity); - if (d->empty()) { - r = _mk_empty(arity - num_vars); - return; - } - delay_dealloc delay(*this); - mk_project_init(num_vars, vars); - mk_project_core(d, r, 0, 0, memoize_res); - - STRACE("imdd_trace", tout << "mk_project(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num_vars; i++) tout << vars[i] << ", "; - tout << memoize_res << ");\n";); -} - -void imdd_manager::mk_join( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - unsigned d1_arity = d1->get_arity(); - mk_product(d1, d2, tmp); - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned vars[2] = { vars1[i], vars2[i] + d1_arity }; - mk_filter_identical_dupdt(tmp, 2, vars); - } - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} - -#if 0 -void imdd_manager::mk_join_project( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - unsigned d1_arity = d1->get_arity(); - mk_product(d1, d2, tmp); - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned vars[2] = { vars1[i], vars2[i] + d1_arity }; - mk_filter_identical(tmp, tmp, 2, vars); - } - mk_project(tmp, tmp, proj_vars.size(), proj_vars.c_ptr()); - TRACE("dl", - tout << "vars: "; - for (unsigned i = 0; i < vars1.size(); ++i) tout << vars1[i] << ":" << vars2[i] << " "; - tout << "\n"; - tout << "proj: "; - for (unsigned i = 0; i < proj_vars.size(); ++i) tout << proj_vars[i] << " "; - tout << "\n"; - tout << "arity: " << d1->get_arity() << " + " << d2->get_arity() << "\n"; - display_ll(tout << "d1\n", d1); - display_ll(tout << "d2\n", d2); - display_ll(tout << "result\n", tmp); - tout << "\n"; - ); - - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} -#endif - - -void imdd_manager::mk_join_project( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - mk_product(d1, d2, tmp); - unsigned d1_arity = d1->get_arity(); - unsigned sz = tmp->get_arity(); - - // set up schedule for join-project. - unsigned_vector remap; - svector projected; - unsigned_vector ref_count; - for (unsigned i = 0; i < sz; ++i) { - remap.push_back(i); - } - projected.resize(sz, false); - ref_count.resize(sz, 0); - for (unsigned i = 0; i < proj_vars.size(); ++i) { - projected[proj_vars[i]] = true; - } - for (unsigned i = 0; i < vars1.size(); ++i) { - ref_count[vars1[i]]++; - ref_count[vars2[i]+d1_arity]++; - } - -#define UPDATE_REMAP() \ - for (unsigned i = 0, j = 0; i < sz; ++i) { \ - remap[i] = j; \ - if (!projected[i] || ref_count[i] > 0) { \ - ++j; \ - } \ - } \ - - - UPDATE_REMAP(); - // project unused variables: - unsigned_vector proj2; - for (unsigned i = 0; i < sz; ++i) { - if (projected[i] && ref_count[i] == 0) { - proj2.push_back(i); - } - } - mk_project(tmp, tmp, proj2.size(), proj2.c_ptr()); - - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned idx1 = vars1[i]; - unsigned idx2 = vars2[i]+d1_arity; - ref_count[idx1]--; - ref_count[idx2]--; - bool del1 = ref_count[idx1] == 0 && projected[idx1]; - bool del2 = ref_count[idx2] == 0 && projected[idx2]; - - filter_identical_main3(tmp, tmp, remap[idx1], del1, remap[idx2], del2, memoize_res); - if (del1 || del2) { - UPDATE_REMAP(); - } - } - TRACE("imdd", - tout << "vars: "; - for (unsigned i = 0; i < vars1.size(); ++i) tout << vars1[i] << ":" << vars2[i] << " "; - tout << "\n"; - tout << "proj: "; - for (unsigned i = 0; i < proj_vars.size(); ++i) tout << proj_vars[i] << " "; - tout << "\n"; - tout << "arity: " << d1->get_arity() << " + " << d2->get_arity() << "\n"; - display_ll(tout << "d1\n", d1); - display_ll(tout << "d2\n", d2); - display_ll(tout << "result\n", tmp); - tout << "\n"; - ); - - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} - - - -imdd * imdd_manager::mk_swap_new_child(unsigned lower, unsigned upper, imdd * grandchild) { - if (m_swap_new_child == 0) { - m_swap_new_child = _mk_empty(grandchild == 0 ? 1 : grandchild->get_arity() + 1); - add_child(m_swap_new_child, lower, upper, grandchild); - if (grandchild && !grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - inc_ref(m_swap_new_child); - } - SASSERT(m_swap_new_child != 0); - return m_swap_new_child; -} - -void imdd_manager::mk_swap_acc1_dupdt(imdd_ref & d, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res) { - SASSERT(d->get_ref_count() == 1); - sbuffer to_insert; - imdd_children::ext_iterator it; - imdd_children::ext_iterator end; - d->m_children.move_geq(it, lower); - while (it != end && lower <= upper) { - imdd_children::entry const & curr_entry = *it; - unsigned curr_entry_begin_key = curr_entry.begin_key(); - unsigned curr_entry_end_key = curr_entry.end_key(); - imdd * curr_entry_val = curr_entry.val(); - bool move_head = true; - if (upper < curr_entry_begin_key) - break; - if (lower < curr_entry_begin_key) { - to_insert.push_back(entry(lower, curr_entry_begin_key - 1, grandchild)); - lower = curr_entry_begin_key; - } - SASSERT(lower >= curr_entry_begin_key); - imdd * curr_grandchild = curr_entry_val; - imdd_ref new_grandchild(*this); - SASSERT((curr_grandchild == 0) == (grandchild == 0)); - if (curr_grandchild != 0) { - bool cover = lower == curr_entry_begin_key && upper >= curr_entry_end_key; - // If cover == true, then the curr_child is completely covered, and it is not needed anymore. - // So, we can perform a destructive update. - if (cover) { - new_grandchild = curr_grandchild; // take over the ownership of curr_grandchild - it.erase(m_sl_manager); - move_head = false; // it.erase is effectively moving the head. - mk_union_core_dupdt(new_grandchild, grandchild, memoize_res); - } - else { - mk_union_core(curr_grandchild, grandchild, new_grandchild, memoize_res); - } - if (!new_grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - // protect new_grandchild, since it will be stored in to_insert. - new_grandchild->inc_ref(); - } - if (upper >= curr_entry_end_key) { - to_insert.push_back(entry(lower, curr_entry_end_key, new_grandchild)); - } - else { - to_insert.push_back(entry(lower, upper, new_grandchild)); - } - lower = curr_entry_end_key + 1; - if (move_head) - ++it; - } - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(d, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - if (curr_entry.val()) - curr_entry.val()->dec_ref(); // to_insert will be destroyed, so decrement ref. - } - if (lower <= upper) { - add_child(d, lower, upper, grandchild); - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc1_dupt\n" << mk_ll_pp(d, *this) << "\n";); -} - -void imdd_manager::mk_swap_acc1(imdd * d, imdd_ref & r, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res) { - copy(d, r); - imdd_children::iterator it = d->m_children.find_geq(lower); - imdd_children::iterator end = d->end_children(); - for (; it != end && lower <= upper; ++it) { - imdd_children::entry const & curr_entry = *it; - if (upper < curr_entry.begin_key()) - break; - if (lower < curr_entry.begin_key()) { - SASSERT(lower <= curr_entry.begin_key() - 1); - add_child(r, lower, curr_entry.begin_key() - 1, grandchild); - lower = curr_entry.begin_key(); - } - SASSERT(lower >= curr_entry.begin_key()); - imdd * curr_grandchild = curr_entry.val(); - SASSERT((curr_grandchild == 0) == (grandchild == 0)); - imdd_ref new_curr_grandchild(*this); - mk_union_core(curr_grandchild, grandchild, new_curr_grandchild, memoize_res); - if (new_curr_grandchild && !new_curr_grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - if (upper >= curr_entry.end_key()) { - add_child(r, lower, curr_entry.end_key(), new_curr_grandchild); - } - else { - SASSERT(upper < curr_entry.end_key()); - SASSERT(lower <= upper); - add_child(r, lower, upper, new_curr_grandchild); - } - lower = curr_entry.end_key() + 1; - } - if (lower <= upper) { - add_child(r, lower, upper, grandchild); - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc1\n" << mk_ll_pp(r, *this) << "\n";); -} - -/** - \brief Auxiliary function for mk_swap_core -*/ -void imdd_manager::mk_swap_acc2(imdd_ref & r, unsigned lower1, unsigned upper1, unsigned lower2, unsigned upper2, imdd * grandchild, bool memoize_res) { - SASSERT((r->get_arity() == 2 && grandchild == 0) || - (r->get_arity() == grandchild->get_arity() + 2)); - SASSERT(r->get_ref_count() <= 1); // we perform destructive updates on r. - SASSERT(!r->is_memoized()); - TRACE("mk_swap_bug", - tout << mk_ll_pp(r, *this) << "\n"; - tout << "adding\n[" << lower1 << ", " << upper1 << "] -> [" << lower2 << ", " << upper2 << "] ->\n"; - tout << mk_ll_pp(grandchild, *this) << "\n";); - - sbuffer to_insert; - imdd_children::ext_iterator it; - imdd_children::ext_iterator end; - r->m_children.move_geq(it, lower1); - SASSERT(m_swap_new_child == 0); - - while(it != end && lower1 <= upper1) { - imdd_children::entry const & curr_entry = *it; - unsigned curr_entry_begin_key = curr_entry.begin_key(); - unsigned curr_entry_end_key = curr_entry.end_key(); - imdd * curr_entry_val = curr_entry.val(); - bool move_head = true; - TRACE("mk_swap_bug", tout << lower1 << " " << upper1 << " " << curr_entry_begin_key << "\n";); - if (upper1 < curr_entry_begin_key) - break; - if (lower1 < curr_entry_begin_key) { - imdd * new_child = mk_swap_new_child(lower2, upper2, grandchild); - SASSERT(lower1 <= curr_entry_begin_key - 1); - add_child(r, lower1, curr_entry_begin_key - 1, new_child); - lower1 = curr_entry_begin_key; - } - SASSERT(lower1 >= curr_entry_begin_key); - imdd * curr_child = curr_entry_val; - SASSERT(curr_child != 0); - SASSERT(!curr_child->is_memoized()); - imdd_ref new_curr_child(*this); - bool destructive = curr_child->get_ref_count() == 1 && lower1 == curr_entry_begin_key && upper1 >= curr_entry_end_key; - if (destructive) { - new_curr_child = curr_child; // take over curr_child. - it.erase(m_sl_manager); - move_head = false; // it.erase is effectively moving the head. - mk_swap_acc1_dupdt(new_curr_child, lower2, upper2, grandchild, memoize_res); - } - else { - mk_swap_acc1(curr_child, new_curr_child, lower2, upper2, grandchild, memoize_res); - } - new_curr_child->inc_ref(); // it will be stored in to_insert - if (upper1 >= curr_entry_end_key) { - to_insert.push_back(entry(lower1, curr_entry_end_key, new_curr_child)); - } - else { - to_insert.push_back(entry(lower1, upper1, new_curr_child)); - } - lower1 = curr_entry_end_key + 1; - if (move_head) - ++it; - } - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(r, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - SASSERT(curr_entry.val()); - curr_entry.val()->dec_ref(); // to_insert will be destroyed, so decrement ref. - } - if (lower1 <= upper1) { - imdd * new_child = mk_swap_new_child(lower2, upper2, grandchild); - add_child(r, lower1, upper1, new_child); - } - if (m_swap_new_child != 0) { - dec_ref(m_swap_new_child); - m_swap_new_child = 0; - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc2\n" << mk_ll_pp(r, *this) << "\n";); -} - -/** - \brief Memoize the given IMDD assuming all grandchildren of d are memoized. -*/ -imdd * imdd_manager::mk_swap_memoize(imdd * d) { - if (d->is_memoized()) - return d; - bool children_memoized = true; - imdd * new_d = _mk_empty(d->get_arity()); - imdd_children::push_back_proc push_back(m_sl_manager, new_d->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - imdd * new_child = memoize(child); - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - - if (children_memoized && is_simple_node(new_d)) { - imdd * new_can_d = memoize(new_d); - if (new_can_d != new_d) { - SASSERT(new_d->get_ref_count() == 0); - delete_imdd(new_d); - } - new_d = new_can_d; - } - return new_d; -} - -/** - \brief Swap the two top vars. -*/ -void imdd_manager::mk_swap_top_vars(imdd * d, imdd_ref & r, bool memoize_res) { - SASSERT(d->get_arity() >= 2); - r = _mk_empty(d->get_arity()); - m_swap_granchildren_memoized = true; - m_swap_new_child = 0; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - SASSERT(curr_child != 0); - SASSERT(m_swap_new_child == 0); - imdd_children::iterator it2 = curr_child->begin_children(); - imdd_children::iterator end2 = curr_child->end_children(); - for (; it2 != end2; ++it2) { - imdd * grandchild = it2->val(); - mk_swap_acc2(r, it2->begin_key(), it2->end_key(), it->begin_key(), it->end_key(), grandchild, memoize_res); - } - SASSERT(m_swap_new_child == 0); - } - - if (memoize_res && m_swap_granchildren_memoized) { - r = mk_swap_memoize(r); - } - TRACE("mk_swap_bug", tout << "result:\n" << mk_ll_pp(r, *this) << "\n";); -} - -void imdd_manager::mk_swap_core(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - - imdd * _r = 0; - if (is_cached(m_swap_cache, d, _r)) { - r = _r; - return; - } - - if (vidx == 0) { - mk_swap_top_vars(d, r, memoize_res); - } - else { - SASSERT(vidx > 0); - bool new_children_memoized = true; - unsigned new_vidx = vidx - 1; - _r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, _r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - mk_swap_core(curr_child, new_child, new_vidx, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (new_child != 0 && !new_child->is_memoized()) - new_children_memoized = false; - } - SASSERT(!_r->empty()); - _r = memoize_new_imdd_if(memoize_res && new_children_memoized, _r); - r = _r; - } - cache_result(m_swap_cache, d, r); -} - -void imdd_manager::mk_swap_dupdt_core(imdd_ref & d, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - - if (!destructive_update_at(true, d)) { - mk_swap_core(d, d, vidx, memoize_res); - return; - } - - if (vidx == 0) { - mk_swap_top_vars(d, d, memoize_res); - return; - } - - SASSERT(vidx > 0); - sbuffer to_insert; - unsigned new_vidx = vidx-1; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - mk_swap_dupdt_core(new_child, new_vidx, memoize_res); - new_child->inc_ref(); // protect new child, since we insert into to_insert - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); -} - -void imdd_manager::mk_swap_dupdt(imdd_ref & d, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - SASSERT(vidx < arity - 1); - if (d->empty()) { - return; - } - m_swap_cache.reset(); - reset_union_cache(); - delay_dealloc delay(*this); - mk_swap_dupdt_core(d, vidx, memoize_res); -} - -void imdd_manager::mk_swap(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - SASSERT(vidx < arity - 1); - if (d->empty()) { - r = d; - return; - } - TRACE("mk_swap_bug", tout << "mk_swap: " << vidx << "\n"; display_ll(tout, d); ); - TRACE("mk_swap_bug2", tout << "mk_swap: " << vidx << "\n"; display_ll(tout, d); ); - m_swap_cache.reset(); - reset_union_cache(); - delay_dealloc delay(*this); - mk_swap_core(d, r, vidx, memoize_res); - TRACE("mk_swap_bug2", tout << "mk_swap result\n"; display_ll(tout, r); ); - STRACE("imdd_trace", tout << "mk_swap(0x" << d << ", 0x" << r.get() << ", " << vidx << ", " << memoize_res << ");\n";); -} - -imdd_manager::iterator::iterator(imdd_manager const & m, imdd const * d) { - if (d->empty()) { - m_done = true; - return; - } - m_done = false; - unsigned arity = d->get_arity(); - m_iterators.resize(arity); - m_element.resize(arity); - begin_iterators(d, 0); -} - -void imdd_manager::iterator::begin_iterators(imdd const * curr, unsigned start_idx) { - for (unsigned i = start_idx; i < get_arity(); i++) { - m_iterators[i] = curr->begin_children(); - imdd_children::iterator & it = m_iterators[i]; - SASSERT(!it.at_end()); - m_element[i] = it->begin_key(); - curr = it->val(); - } -} - -imdd_manager::iterator & imdd_manager::iterator::operator++() { - unsigned i = get_arity(); - while (i > 0) { - --i; - imdd_children::iterator & it = m_iterators[i]; - if (m_element[i] < it->end_key()) { - m_element[i]++; - begin_iterators(it->val(), i+1); - return *this; - } - else { - ++it; - if (!it.at_end()) { - m_element[i] = it->begin_key(); - begin_iterators(it->val(), i+1); - return *this; - } - } - } - m_done = true; // at end... - return *this; -} - -bool imdd_manager::iterator::operator==(iterator const & it) const { - if (m_done && it.m_done) - return true; - if (m_done || it.m_done) - return false; - if (m_element.size() != it.m_element.size()) - return false; - unsigned sz = m_element.size(); - for (unsigned i = 0; i < sz; i++) - if (m_element[i] != it.m_element[i]) - return false; - return true; -} - -bool imdd_manager::is_equal_core(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - if (d1->is_memoized() && d2->is_memoized()) - return false; - SASSERT(d1->get_arity() == d2->get_arity()); - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - bool shared = d1->is_shared() && d2->is_shared(); - bool r; - if (shared && m_is_equal_cache.find(d1, d2, r)) - return r; - - if (d1->get_arity() == 1) { - r = d1->m_children.is_equal(d2->m_children); - } - else { - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - if (it1->begin_key() != it2->begin_key()) { - r = false; - } - else { - while (true) { - if (it1 == end1) { - r = (it2 == end2); - break; - } - if (it2 == end2) { - r = (it1 == end1); - break; - } - SASSERT(it1->val() != 0 && it2->val() != 0); - if (!is_equal_core(it1->val(), it2->val())) { - r = false; - break; - } - if (it1->end_key() < it2->end_key()) { - unsigned prev_end_key = it1->end_key(); - ++it1; - if (it1 == end1 || it1->begin_key() != prev_end_key + 1) { - r = false; - break; - } - } - else if (it1->end_key() > it2->end_key()) { - unsigned prev_end_key = it2->end_key(); - ++it2; - if (it2 == end2 || it2->begin_key() != prev_end_key + 1) { - r = false; - break; - } - } - else { - SASSERT(it1->end_key() == it2->end_key()); - ++it1; - ++it2; - } - } - } - } - if (shared) - m_is_equal_cache.insert(d1, d2, r); - return r; -} - -bool imdd_manager::is_equal(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - if (d1->is_memoized() && d2->is_memoized()) - return false; - if (d1->get_arity() != d2->get_arity()) - return false; - if (d1->empty() && d2->empty()) - return true; - if (d1->empty() || d2->empty()) - return false; - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - m_is_equal_cache.reset(); - return is_equal_core(d1, d2); -} - -/** - \brief Return true if the given imdd contains the tuple (values[0], ..., values[num-1]) -*/ -bool imdd_manager::contains(imdd * d, unsigned num, unsigned const * values) const { - SASSERT(d->get_arity() == num); - imdd * curr = d; - for (unsigned i = 0; i < num; i++) { - imdd * child; - if (!curr->m_children.find(values[i], child)) - return false; - curr = child; - } - return true; -} - -inline bool overlap(unsigned b1, unsigned e1, unsigned b2, unsigned e2) { - // [b1, e1] [b2, e2] - if (e1 < b2) - return false; - // [b2, e2] [b1, e1] - if (e2 < b1) - return false; - return true; -} - -bool imdd_manager::subsumes_core(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - SASSERT(d1->get_arity() == d2->get_arity()); - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - bool shared = d1->is_shared() && d2->is_shared(); - bool r; - if (shared && m_subsumes_cache.find(d1, d2, r)) - return r; - - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - if (it1->begin_key() > it2->begin_key()) { - r = false; - goto subsumes_end; - } - - if (d1->get_arity() == 1) { - // leaf case - while(true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - it1.move_to(it2->begin_key()); - if (it1 == end1 || - it1->begin_key() > it2->begin_key() || // missed beginning of it2 curr entry - it1->end_key() < it2->end_key() // missed end of it2 curr entry - ) { - r = false; - goto subsumes_end; - } - SASSERT(it1->end_key() >= it2->end_key()); - ++it2; - if (it2 == end2) { - r = true; - goto subsumes_end; - } - } - } - else { - // non-leaf case - while (true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - SASSERT(it1->val() != 0); - SASSERT(it2->val() != 0); - it1.move_to(it2->begin_key()); - if (it1 == end1 || - it1->begin_key() > it2->begin_key() // missed beginning of it2 curr entry - ) { - r = false; - goto subsumes_end; - } - // the beginning of it2 is inside it1 - SASSERT(it2->begin_key() >= it1->begin_key() && it2->begin_key() <= it1->end_key()); - // it1: [ ][ ][ ] - // it2 [ ] - while (true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - // there is a overlap between the current entry in it1 and the current entry in it2 - SASSERT(overlap(it1->begin_key(), it1->end_key(), - it2->begin_key(), it2->end_key())); - if (!subsumes_core(it1->val(), it2->val())) { - r = false; - goto subsumes_end; - } - if (it1->end_key() >= it2->end_key()) { - ++it2; // processed the whole entry in it2 - break; - } - SASSERT(it1->end_key() < it2->end_key()); - unsigned prev_end_key = it1->end_key(); - ++it1; - if (it1 == end1 || it1->begin_key() != prev_end_key + 1) { - r = false; - goto subsumes_end; - } - } - if (it2 == end2) { - r = true; - goto subsumes_end; - } - } - } - subsumes_end: - if (shared) - m_subsumes_cache.insert(d1, d2, r); - return r; -} - -/** - \brief Return true is d1 is a superset of d2, or equal to d2. -*/ -bool imdd_manager::subsumes(imdd * d1, imdd * d2) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2) - return true; - if (d2->empty()) - return true; - if (d1->empty()) { - SASSERT(!d2->empty()); - return false; - } - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - m_subsumes_cache.reset(); - return subsumes_core(d1, d2); -} - -void imdd_manager::remove_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers) { - SASSERT(d->get_arity() == num); - d = remove_facts_main(d, num, lowers, uppers, true, true); -} - -void imdd_manager::remove_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers) { - SASSERT(d->get_arity() == num); - r = remove_facts_main(d, num, lowers, uppers, false, true); -} - -imdd * imdd_manager::remove_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - delay_dealloc delay(*this); - m_remove_facts_cache.reset(); - return remove_facts_core(d, num, lowers, uppers, destructive, memoize_res); -} - -imdd * imdd_manager::remove_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num && num > 0); - - imdd * new_d = 0; - unsigned b = *lowers; - unsigned e = *uppers; - sbuffer to_insert, to_remove; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - bool is_destructive = destructive_update_at(destructive, d); - - if (!is_destructive && is_cached(m_remove_facts_cache, d, new_d)) { - return new_d; - } - - if (num == 1) { - new_d = remove_main(d, *lowers, *uppers, destructive, memoize_res); - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, new_d); - } - return new_d; - } - - if (it == end || e < it->begin_key()) { - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, d); - } - return d; - } - - if (!is_destructive) { - new_d = copy_main(d); - } - else { - new_d = d; - } - for (; it != end && b <= e; ++it) { - // - // remove ([b:e]::rest), [b_key:e_key] * ch) = - // { [b_key:e_key] * ch } if e < b_key - // { [b_key:e_key] * ch' } if b <= b_key <= e_key <= e - // { [b_key:e]*ch' [e+1:e_key]*ch } if b <= b_key <= e < e_key - // { [b_key:b-1]*ch [b:e]*ch', [e+1:e_key]*ch } if b_key < b <= e < e_key - // { [b_key:b-1]*ch [b:e_key]*ch' } if b_key < b <= e_key <= e - // where - // ch' = remove(rest, ch) - // assumption: remove_child retains the cases where ch is in the tree. - // - - imdd_children::entry const & curr_entry = *it; - unsigned b_key = curr_entry.begin_key(); - unsigned e_key = curr_entry.end_key(); - imdd* curr_child = curr_entry.val(); - imdd* new_curr_child = 0; - - if (e < b_key) { - break; - } - - new_curr_child = remove_facts_core(curr_child, num-1, lowers+1, uppers+1, destructive, memoize_res); - - if (new_curr_child == curr_child) { - continue; - } - - if (new_curr_child != 0) { - if (b <= b_key && e_key <= e) { - to_insert.push_back(entry(b_key, e_key, new_curr_child)); - } - if (b <= b_key && e < e_key) { - to_insert.push_back(entry(b_key, e, new_curr_child)); - } - if (b_key < b && e < e_key) { - to_insert.push_back(entry(b, e, new_curr_child)); - } - if (b_key < b && e_key <= e) { - to_insert.push_back(entry(b, e_key, new_curr_child)); - } - } - if (is_destructive) { - to_remove.push_back(entry(b, e, 0)); - } - else { - remove_child(new_d, b, e); - } - b = e_key + 1; - } - for (unsigned i = 0; i < to_remove.size(); ++i) { - remove_child(new_d, to_remove[i].begin_key(), to_remove[i].end_key()); - } - for (unsigned i = 0; i < to_insert.size(); ++i) { - add_child(new_d, to_insert[i].begin_key(), to_insert[i].end_key(), to_insert[i].val()); - } - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, new_d); - } - return new_d; -} - -void imdd_manager::display(std::ostream & out, imdd const * d, unsigned_vector & intervals, bool & first) const { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - if (d->get_arity() == 1) { - for (; it != end; ++it) { - SASSERT(it->val() == 0); - if (first) { - first = false; - } - else { - out << ",\n "; - } - out << "("; - unsigned sz = intervals.size(); - for (unsigned i = 0; i < sz; i+=2) { - out << "[" << intervals[i] << ", " << intervals[i+1] << "]"; - out << ", "; - } - out << "[" << it->begin_key() << ", " << it->end_key() << "])"; - } - } - else { - for (; it != end; ++it) { - intervals.push_back(it->begin_key()); - intervals.push_back(it->end_key()); - display(out, it->val(), intervals, first); - intervals.pop_back(); - intervals.pop_back(); - } - } -} - -void imdd_manager::display(std::ostream & out, imdd const * d) const { - unsigned_vector intervals; - bool first = true; - out << "{"; - display(out, d, intervals, first); - out << "}"; -} - -struct display_ll_context { - typedef map, default_eq > imdd2uint; - std::ostream & m_out; - unsigned m_next_id; - imdd2uint m_map; - display_ll_context(std::ostream & out):m_out(out), m_next_id(1) {} - - void display_tabs(unsigned num_tabs) { - for (unsigned i = 0; i < num_tabs; i++) - m_out << " "; - } - - void display_node(unsigned num_tabs, imdd const * d) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - if (it == end) { - m_out << "{}"; - return; - } - if (d->get_arity() == 1) { - // leaf - m_out << "{"; - for (bool first = true; it != end; ++it) { - if (first) - first = false; - else - m_out << ", "; - m_out << "[" << it->begin_key() << ", " << it->end_key() << "]"; - } - m_out << "}"; - } - else { - m_out << "{\n"; - display_tabs(num_tabs); - while (it != end) { - m_out << " [" << it->begin_key() << ", " << it->end_key() << "] -> "; - display(num_tabs+1, it->val()); - ++it; - if (it != end) { - m_out << "\n"; - display_tabs(num_tabs); - } - else { - m_out << "}"; - } - } - } - m_out << (d->is_memoized() ? "*" : "") << "$" << d->memory(); - } - - void display(unsigned num_tabs, imdd const * d) { - if (d == 0) { - m_out << ""; - return; - } - unsigned id; - if (m_map.find(const_cast(d), id)) { - m_out << "#" << id; - } - else if (d->is_shared()) { - id = m_next_id; - m_next_id++; - m_map.insert(const_cast(d), id); - m_out << "#" << id << ":"; - display_node(num_tabs, d); - } - else { - display_node(num_tabs, d); - } - } -}; - -void imdd_manager::display_ll(std::ostream & out, imdd const * d) const { - display_ll_context ctx(out); - ctx.display(0, d); - out << "\n"; -} - -struct addone_proc { - unsigned m_r; - addone_proc():m_r(0) {} - void operator()(imdd * d) { m_r++; } -}; - -/** - \brief Return the number of nodes in an IMDD. - This is *not* a constant time operation. - It is linear on the size of the IMDD -*/ -unsigned imdd_manager::get_num_nodes(imdd const * d) const { - addone_proc p; - for_each(const_cast(d), p); - return p.m_r; -} - -struct addmem_proc { - unsigned m_r; - addmem_proc():m_r(0) {} - void operator()(imdd * d) { m_r += d->memory(); } -}; - -/** - \brief Return the amount of memory consumed by the given IMDD. -*/ -unsigned imdd_manager::memory(imdd const * d) const { - addmem_proc p; - for_each(const_cast(d), p); - return p.m_r; -} - -/** - \brief Return number of rows the given IMDD represents. -*/ -size_t imdd_manager::get_num_rows(imdd const* root) const { - obj_map counts; - ptr_vector todo; - todo.push_back(root); - while (!todo.empty()) { - imdd const* d = todo.back(); - if (counts.contains(d)) { - todo.pop_back(); - continue; - } - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - bool all_valid = true; - size_t count = 0, c; - for (; it != end; ++it) { - imdd const* ch = it->val(); - if (!ch) { - count += (it->end_key()-it->begin_key()+1); - } - else if (counts.find(ch, c)) { - count += c*(it->end_key()-it->begin_key()+1); - } - else { - all_valid = false; - todo.push_back(ch); - } - } - if (all_valid) { - todo.pop_back(); - counts.insert(d, count); - } - } - return counts.find(root); -} - -imdd * imdd_manager::add_bounded_var_core(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res) { - if (d == 0) { - SASSERT(before_vidx == 0); - imdd * r = _mk_empty(1); - add_child(r, lower, upper, 0); - r = memoize_new_imdd_if(memoize_res, r); - return r; - } - imdd * r = 0; - if (is_cached(m_add_bounded_var_cache, d, r)) - return r; - if (before_vidx == 0) { - imdd * r = _mk_empty(d->get_arity() + 1); - add_child(r, lower, upper, d); - r = memoize_new_imdd_if(d->is_memoized() && memoize_res, r); - return r; - } - - if (destructive_update_at(destructive, d)) { - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = add_bounded_var_core(curr_child, before_vidx-1, lower, upper, true, memoize_res); - new_child->inc_ref(); // we will be resetting d->m_children later. - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); - return d; - } - - bool new_children_memoized = true; - r = _mk_empty(d->get_arity() + 1); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = add_bounded_var_core(curr_child, before_vidx-1, lower, upper, false, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_add_bounded_var_cache, d, r); - return r; -} - -/** - \brief Add a variable (bounded by lower and upper) before the variable before_var. - - That is, for each tuple (v_1, ..., v_n) in the IMDD \c d, the resultant IMDD contains the - tuples - - (v_1, ..., v_{after_vidx}, lower, ..., v_n) - (v_1, ..., v_{after_vidx}, lower+1, ..., v_n) - ... - (v_1, ..., v_{after_vidx}, upper, ..., v_n) - - This function is mainly used to implement mk_filter. - - \pre after_vidx < d->get_arity() -*/ -imdd * imdd_manager::add_bounded_var_main(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res) { - SASSERT(before_vidx <= d->get_arity()); - if (d->empty()) - return d; - m_add_bounded_var_cache.reset(); - delay_dealloc delay(*this); - imdd * r = add_bounded_var_core(d, before_vidx, lower, upper, destructive, memoize_res); - return r; -} - -filter_cache_entry * imdd_manager::mk_filter_cache_entry(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r) { - void * mem = m_filter_entries.allocate(filter_cache_entry::get_obj_size(ctx_sz)); - return new (mem) filter_cache_entry(d, r, ctx_sz, ctx); -} - -imdd * imdd_manager::is_mk_filter_cached(imdd * d, unsigned ctx_sz, unsigned * ctx) { - if (!d->is_shared()) - return 0; - m_filter_entries.push_scope(); - filter_cache_entry * tmp_entry = mk_filter_cache_entry(d, ctx_sz, ctx, 0); - filter_cache_entry * e = 0; - bool r = m_filter_cache.find(tmp_entry, e); - m_filter_entries.pop_scope(); - if (!r || e->m_r->is_dead()) - return 0; - return e->m_r; -} - -void imdd_manager::cache_mk_filter(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r) { - if (d->is_shared()) { - filter_cache_entry * new_entry = mk_filter_cache_entry(d, ctx_sz, ctx, r); - m_filter_cache.insert(new_entry); - } -} - -void imdd_manager::init_mk_filter(unsigned arity, unsigned num_vars, unsigned * vars) { - m_filter_cache.reset(); - m_filter_entries.reset(); - m_filter_context.reset(); - m_filter_num_vars = num_vars; - m_filter_begin_vars = vars; - m_filter_end_vars = vars + num_vars; - DEBUG_CODE({ - for (unsigned i = 0; i < num_vars; i++) { - SASSERT(vars[i] < arity); - } - }); -} - -template -void imdd_manager::mk_filter_dupdt_core(imdd_ref & d, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res) { - SASSERT(!d->empty()); - - if (!destructive_update_at(true, d)) { - mk_filter_core(d, d, vidx, num_found, proc, memoize_res); - return; - } - - bool _is_filter_var = is_filter_var(vidx); - if (_is_filter_var) - num_found++; - unsigned new_vidx = vidx+1; - imdd_ref new_r(*this); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); // take ownership of the current child. - if (!_is_filter_var) { - mk_filter_dupdt_core(new_child, new_vidx, num_found, proc, memoize_res); - add_bounded_var_dupdt(new_child, num_found, it->begin_key(), it->end_key(), memoize_res); - if (new_r == 0) - new_r = new_child; - else - mk_union_core_dupdt(new_r, new_child, memoize_res); - } - else { - m_filter_context.push_back(it->begin_key()); - m_filter_context.push_back(it->end_key()); - if (num_found < m_filter_num_vars) { - mk_filter_dupdt_core(new_child, new_vidx, num_found, proc, memoize_res); - } - else { - proc(m_filter_context.c_ptr(), new_child, new_child, memoize_res); - } - m_filter_context.pop_back(); - m_filter_context.pop_back(); - if (new_r == 0) - new_r = new_child; - else - mk_union_core_dupdt(new_r, new_child, memoize_res); - } - } - d->m_children.reset(m_sl_manager); - d = new_r; -} - -template -void imdd_manager::mk_filter_core(imdd * d, imdd_ref & r, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res) { - SASSERT(!d->empty()); - - imdd * _r = is_mk_filter_cached(d, m_filter_context.size(), m_filter_context.c_ptr()); - if (_r) { - r = _r; - return; - } - - bool _is_filter_var = is_filter_var(vidx); - if (_is_filter_var) - num_found++; - unsigned new_vidx = vidx+1; - imdd_ref new_r(*this); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - if (!_is_filter_var) { - mk_filter_core(curr_child, new_child, new_vidx, num_found, proc, memoize_res); - imdd_ref tmp(*this); - add_bounded_var(new_child, tmp, num_found, it->begin_key(), it->end_key(), memoize_res); - if (new_r == 0) - new_r = tmp; - else - mk_union_core(new_r, tmp, new_r, memoize_res); - } - else { - m_filter_context.push_back(it->begin_key()); - m_filter_context.push_back(it->end_key()); - if (num_found < m_filter_num_vars) { - mk_filter_core(curr_child, new_child, new_vidx, num_found, proc, memoize_res); - } - else { - proc(m_filter_context.c_ptr(), curr_child, new_child, memoize_res); - } - m_filter_context.pop_back(); - m_filter_context.pop_back(); - if (new_r == 0) - new_r = new_child; - else - mk_union_core(new_r, new_child, new_r, memoize_res); - } - } - r = new_r; - cache_mk_filter(d, m_filter_context.size(), m_filter_context.c_ptr(), r); -} - -template -void imdd_manager::mk_filter_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res) { - if (d->empty()) - return; - unsigned arity = d->get_arity(); - init_mk_filter(arity, num_vars, vars); - mk_filter_dupdt_core(d, 0, 0, proc, memoize_res); - if (d == 0) - d = _mk_empty(arity); -} - -template -void imdd_manager::mk_filter(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res) { - if (d->empty()) { - r = d; - return; - } - unsigned arity = d->get_arity(); - init_mk_filter(arity, num_vars, vars); - mk_filter_core(d, r, 0, 0, proc, memoize_res); - if (r == 0) - r = _mk_empty(arity); -} - -imdd * imdd_manager::mk_distinct_imdd(unsigned l1, unsigned u1, unsigned l2, unsigned u2, imdd * d, bool memoize_res) { - unsigned d_arity; - if (d == 0) { - d_arity = 0; - } - else { - d_arity = d->get_arity(); - memoize_res = memoize_res && d->is_memoized(); - } - imdd * r = _mk_empty(d_arity + 2); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - TRACE("mk_distinct_imdd", tout << "STARTING: " << l1 << " " << u1 << " " << l2 << " " << u2 << "\n";); - -#define ADD_ENTRY(L1, U1, L2, U2) { \ - TRACE("mk_distinct_imdd", tout << "[" << L1 << " " << U1 << " " << L2 << " " << U2 << "]\n";); \ - imdd * new_child = _mk_empty(d_arity + 1); \ - add_child(new_child, L2, U2, d); \ - new_child = memoize_new_imdd_if(memoize_res, new_child); \ - push_back(L1, U1, new_child);} - - if (u1 < l2 || u2 < l1) { - ADD_ENTRY(l1, u1, l2, u2); // the intervals are disjoint - } - else { - if (l1 < l2) { - SASSERT(u1 >= l2); - // [l1 ... - // [l2 ... - // --> - // [l1, l2-1] X [l2, u2] - ADD_ENTRY(l1, l2-1, l2, u2); - } - - unsigned l = std::max(l1, l2); - unsigned u = std::min(u1, u2); - // [l, l] X [l2, l-1] // if l-1 >= l2 (i.e., l > l2) - // [l, l] X [l+1, u2] - // [l+1, l+1] X [l2, l] - // [l+1, l+1] X [l+2, u2] - // [l+2, l+2] X [l2, l+1] - // [l+2, l+2] X [l+3, u2] - // ... - // [u, u] X [l2, u-1] - // [u, u] X [u+1, u2] // if u+1 <= u2 (i.e., u < u2) - for (unsigned i = l; i <= u; i++) { - if (i > l2 && i < u2) { - // ADD_ENTRY(i, i, l2, i-1); - // ADD_ENTRY(i, i, i+1, u2); - imdd * new_child = _mk_empty(d_arity + 1); - add_child(new_child, l2, i-1, d); - add_child(new_child, i+1, u2, d); - new_child = memoize_new_imdd_if(memoize_res, new_child); - push_back(i, i, new_child); - } - else if (i > l2) { - SASSERT(!(i < u2)); - ADD_ENTRY(i, i, l2, i-1); - } - else if (i < u2) { - SASSERT(!(i > l2)); - ADD_ENTRY(i, i, i+1, u2); - } - } - - if (u1 > u2) { - // ... u1] - // ... u2] - // --> - // [u2+1, u1] X [l2, u2] - SASSERT(u2 >= l1); - ADD_ENTRY(u2+1, u1, l2, u2); - } - } -#undef ADD_ENTRY - - r = memoize_new_imdd_if(memoize_res, r); - return r; -} - -/** - \brief Auxiliary functor used to implement mk_filter_distinct -*/ -struct distinct_proc { - imdd_manager & m_manager; - - distinct_proc(imdd_manager & m): - m_manager(m) { - } - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res) { - r = m_manager.mk_distinct_imdd(lowers_uppers[0], lowers_uppers[1], lowers_uppers[2], lowers_uppers[3], d, memoize_res); - } -}; - -void imdd_manager::mk_filter_distinct_dupdt(imdd_ref & d, unsigned v1, unsigned v2, bool memoize_res) { - unsigned vars[2] = { v1, v2 }; - distinct_proc proc(*this); - mk_filter_dupdt(d, 2, vars, proc, memoize_res); -} - -void imdd_manager::mk_filter_distinct(imdd * d, imdd_ref & r, unsigned v1, unsigned v2, bool memoize_res) { - unsigned vars[2] = { v1, v2 }; - distinct_proc proc(*this); - mk_filter(d, r, 2, vars, proc, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_distinct(0x" << d << ", 0x" << r.get() << ", " << v1 << ", " << v2 << ", " << memoize_res << ");\n";); -} - -imdd * imdd_manager::mk_disequal_imdd(unsigned l1, unsigned u1, unsigned value, imdd * d, bool memoize_res) { - unsigned d_arity; - if (d == 0) { - d_arity = 0; - } - else { - d_arity = d->get_arity(); - memoize_res = memoize_res && d->is_memoized(); - } - imdd * r = _mk_empty(d_arity + 1); - if (value < l1 || value > u1) { - add_child(r, l1, u1, d); - } - else { - SASSERT(l1 <= value && value <= u1); - if (l1 < value) { - add_child(r, l1, value-1, d); - } - if (value < u1) { - add_child(r, value+1, u1, d); - } - } - r = memoize_new_imdd_if(memoize_res, r); - return r; -} - -struct disequal_proc { - imdd_manager & m_manager; - unsigned m_value; - - disequal_proc(imdd_manager & m, unsigned v): - m_manager(m), - m_value(v) { - } - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res) { - r = m_manager.mk_disequal_imdd(lowers_uppers[0], lowers_uppers[1], m_value, d, memoize_res); - } -}; - -void imdd_manager::mk_filter_disequal_dupdt(imdd_ref & d, unsigned var, unsigned value, bool memoize_res) { - unsigned vars[1] = { var }; - disequal_proc proc(*this, value); - mk_filter_dupdt(d, 1, vars, proc, memoize_res); -} - -void imdd_manager::mk_filter_disequal(imdd * d, imdd_ref & r, unsigned var, unsigned value, bool memoize_res) { - unsigned vars[1] = { var }; - disequal_proc proc(*this, value); - mk_filter(d, r, 1, vars, proc, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_disequal(0x" << d << ", 0x" << r.get() << ", " << var << ", " << value << ", " << memoize_res << ");\n";); -} - - diff --git a/src/muz_qe/imdd.h b/src/muz_qe/imdd.h deleted file mode 100644 index b2da275bf..000000000 --- a/src/muz_qe/imdd.h +++ /dev/null @@ -1,849 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - imdd.h - -Abstract: - - Interval based Multiple-valued Decision Diagrams. - -Author: - - Leonardo de Moura (leonardo) 2010-10-13. - -Revision History: - ---*/ -#ifndef _IMDD_H_ -#define _IMDD_H_ - -#include"id_gen.h" -#include"hashtable.h" -#include"map.h" -#include"obj_hashtable.h" -#include"obj_pair_hashtable.h" -#include"buffer.h" -#include"interval_skip_list.h" -#include"region.h" -#include"obj_ref.h" - -class imdd; -class imdd_manager; - -/** - \brief Manager for skip-lists used to implement IMDD nodes. -*/ -class sl_imdd_manager : public random_level_manager { - imdd_manager * m_manager; // real manager - small_object_allocator & m_alloc; - friend class imdd_manager; -public: - sl_imdd_manager(small_object_allocator & alloc):m_alloc(alloc) {} - void * allocate(size_t size) { return m_alloc.allocate(size); } - void deallocate(size_t size, void* p) { m_alloc.deallocate(size, p); } - void inc_ref_eh(imdd * v); - void dec_ref_eh(imdd * v); -}; - -#define IMDD_BUCKET_CAPACITY 128 -#define IMDD_MAX_LEVEL 32 - -typedef interval_skip_list, - IMDD_BUCKET_CAPACITY, - IMDD_MAX_LEVEL, - true, /* support ref-counting */ - sl_imdd_manager> > imdd_children; - -typedef interval_skip_list, - IMDD_BUCKET_CAPACITY, - IMDD_MAX_LEVEL, - false, - sl_manager_base > > sl_interval_set; - -/* - Notes: - - - We use reference counting for garbage collecting IMDDs nodes. - - - Each IMDD node has a "memoized" flag. If the flag is true, the we use hash-consing for this node. - - - The children of a memoized node must be memoized. - - - The children of a non-memoized node may be memoized. - - - The "memoized" flag cannot be reset after it was set. - - - The result of some operations may be cached. We only use caching for - operations processing memoized nodes. - - - For non-memoized nodes, if m_ref_count <= 1, destructive updates may be performed by some operations. - - - IMPORTANT: "memoized" flag == false doesn't imply m_ref_count <= 1. -*/ - - -/** - \brief IMDDs -*/ -class imdd { - -protected: - friend class imdd_manager; - - unsigned m_id; //!< Unique ID - unsigned m_ref_count; - unsigned m_arity:30; - unsigned m_memoized:1; - unsigned m_dead:1; - imdd_children m_children; - - void inc_ref() { - m_ref_count ++; - } - - void dec_ref() { - SASSERT(m_ref_count > 0); - m_ref_count --; - } - - void mark_as_memoized(bool flag = true) { - SASSERT(is_memoized() != flag); - m_memoized = flag; - } - - void mark_as_dead() { SASSERT(!m_dead); m_dead = true; } - - void replace_children(sl_imdd_manager & m, sbuffer & new_children); - -public: - imdd(sl_imdd_manager & m, unsigned id, unsigned arity):m_id(id), m_ref_count(0), m_arity(arity), m_memoized(false), m_dead(false), m_children(m) {} - unsigned get_id() const { return m_id; } - unsigned get_ref_count() const { return m_ref_count; } - bool is_memoized() const { return m_memoized; } - bool is_shared() const { return m_ref_count > 1; } - bool is_dead() const { return m_dead; } - unsigned get_arity() const { return m_arity; } - imdd_children::iterator begin_children() const { return m_children.begin(); } - imdd_children::iterator end_children() const { return m_children.end(); } - unsigned hc_hash() const; // hash code for hash-consing. - bool hc_equal(imdd const * other) const; // eq function for hash-consing - bool empty() const { return m_children.empty(); } - unsigned hash() const { return m_id; } - unsigned memory() const { return sizeof(imdd) + m_children.memory() - sizeof(imdd_children); } -}; - -// ----------------------------------- -// -// IMDD hash-consing -// -// ----------------------------------- - -// this is the internal hashing functor for hash-consing IMDDs. -struct imdd_hash_proc { - unsigned operator()(imdd const * d) const { return d->hc_hash(); } -}; - -// This is the internal comparison functor for hash-consing IMDDs. -struct imdd_eq_proc { - bool operator()(imdd const * d1, imdd const * d2) const { return d1->hc_equal(d2); } -}; - -typedef ptr_hashtable imdd_table; -typedef obj_hashtable imdd_cache; -typedef obj_map imdd2imdd_cache; -typedef obj_pair_map imdd_pair2imdd_cache; -typedef obj_pair_map imdd_pair2bool_cache; -typedef obj_map imdd2intervals; - -typedef std::pair imdd_value_pair; - -struct fi_cache_entry { - imdd * m_d; - unsigned m_lower; - unsigned m_upper; - unsigned m_hash; - unsigned m_num_result; - imdd_value_pair m_result[0]; - - void mk_hash() { - m_hash = hash_u_u(m_d->get_id(), hash_u_u(m_lower, m_upper)); - } - - fi_cache_entry(imdd * d, unsigned l, unsigned u): - m_d(d), - m_lower(l), - m_upper(u) { - mk_hash(); - } - - fi_cache_entry(imdd * d, unsigned l, unsigned u, unsigned num, imdd_value_pair result[]): - m_d(d), - m_lower(l), - m_upper(u), - m_num_result(num) { - mk_hash(); - memcpy(m_result, result, sizeof(imdd_value_pair)*num); - } - - unsigned hash() const { - return m_hash; - } - - bool operator==(fi_cache_entry const & other) const { - return - m_d == other.m_d && - m_lower == other.m_lower && - m_upper == other.m_upper; - } -}; - -typedef obj_hashtable imdd_fi_cache; -typedef union { - imdd * m_d; - fi_cache_entry * m_entry; -} mk_fi_result; - -struct filter_cache_entry { - imdd * m_d; - imdd * m_r; - unsigned m_hash; - unsigned m_ctx_size; - unsigned m_ctx[0]; // lower and upper bounds that are part of the context. - - static unsigned get_obj_size(unsigned ctx_size) { - return sizeof(filter_cache_entry) + ctx_size * sizeof(unsigned); - } - - void mk_hash() { - if (m_ctx_size > 0) - m_hash = string_hash(reinterpret_cast(m_ctx), m_ctx_size * sizeof(unsigned), m_d->get_id()); - else - m_hash = m_d->get_id(); - } - - filter_cache_entry(imdd * d, imdd * r, unsigned ctx_size, unsigned * ctx): - m_d(d), - m_r(r), - m_ctx_size(ctx_size) { - memcpy(m_ctx, ctx, sizeof(unsigned)*m_ctx_size); - mk_hash(); - } - - unsigned hash() const { - return m_hash; - } - - bool operator==(filter_cache_entry const & other) const { - if (m_d != other.m_d) - return false; - if (m_ctx_size != other.m_ctx_size) - return false; - for (unsigned i = 0; i < m_ctx_size; i++) - if (m_ctx[i] != other.m_ctx[i]) - return false; - return true; - } -}; - -typedef obj_hashtable imdd_mk_filter_cache; - -typedef obj_ref imdd_ref; - -class imdd_manager { - typedef imdd_children::entry entry; - small_object_allocator m_alloc; - id_gen m_id_gen; - vector m_tables; // we keep a table for each height. - sl_imdd_manager m_sl_manager; - unsigned m_simple_max_entries; //!< maximum number of entries in a "simple" node. - bool m_delay_dealloc; - ptr_vector m_to_delete; //!< list of IMDDs marked as dead. These IMDDs may still be in cache tables. - - // generic cache and todo-lists - ptr_vector m_worklist; - imdd_cache m_visited; - - void mark_as_dead(imdd * d); - void deallocate_imdd(imdd * d); - void delete_imdd(imdd * d); - - class delay_dealloc; - friend class delay_dealloc; - - class delay_dealloc { - imdd_manager & m_manager; - bool m_delay_dealloc_value; - unsigned m_to_delete_size; - public: - delay_dealloc(imdd_manager & m): - m_manager(m), - m_delay_dealloc_value(m_manager.m_delay_dealloc), - m_to_delete_size(m_manager.m_to_delete.size()) { - m_manager.m_delay_dealloc = true; - } - ~delay_dealloc(); - }; - - bool is_simple_node(imdd * d) const; - - void add_child(imdd * d, unsigned lower, unsigned upper, imdd * child) { - d->m_children.insert(m_sl_manager, lower, upper, child); - } - - void add_child(imdd * d, unsigned value, imdd * child) { - add_child(d, value, value, child); - } - - void remove_child(imdd * d, unsigned lower, unsigned upper) { - d->m_children.remove(m_sl_manager, lower, upper); - } - - imdd * copy_main(imdd * d); - - imdd * insert_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res); - - imdd * remove_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res); - - imdd2imdd_cache m_mk_product_cache; - struct null2imdd_proc; - struct mk_product_proc; - friend struct mk_product_proc; - imdd * mk_product_core(imdd * d1, imdd * d2, bool destructive, bool memoize); - imdd * mk_product_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - - imdd2imdd_cache m_add_facts_cache; - ptr_vector m_add_facts_new_children; - void init_add_facts_new_children(unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res); - imdd * add_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - imdd * add_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - - imdd2imdd_cache m_remove_facts_cache; - imdd * remove_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - imdd * remove_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - - - imdd2imdd_cache m_defrag_cache; - imdd * defrag_core(imdd * d); - - imdd_pair2imdd_cache m_union_cache; - void push_back_entries(unsigned head, imdd_children::iterator & it, imdd_children::iterator & end, - imdd_children::push_back_proc & push_back, bool & children_memoized); - void push_back_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned limit, - imdd_children::push_back_proc & push_back, bool & children_memoized); - void move_head(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned new_head); - void copy_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned limit, sbuffer & result); - void reset_union_cache(); - imdd * mk_union_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - imdd * mk_union_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - void mk_union_core_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res); - void mk_union_core(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res); - - imdd_pair2bool_cache m_is_equal_cache; - bool is_equal_core(imdd * d1, imdd * d2); - - imdd_pair2bool_cache m_subsumes_cache; - bool subsumes_core(imdd * d1, imdd * d2); - - imdd2imdd_cache m_complement_cache; - imdd * mk_complement_core(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res); - imdd * mk_complement_main(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res); - - imdd2imdd_cache m_filter_equal_cache; - imdd * mk_filter_equal_core(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res); - imdd * mk_filter_equal_main(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res); - - - // original - imdd2intervals m_imdd2interval_set; - ptr_vector m_alloc_is; - typedef sl_manager_base sl_imanager; - void reset_fi_intervals(sl_imanager& m); - sl_interval_set const* init_fi_intervals(sl_imanager& m, imdd* d, unsigned var, unsigned num_found); - - imdd2imdd_cache m_fi_top_cache; - imdd_fi_cache m_fi_bottom_cache; - unsigned m_fi_num_vars; - unsigned * m_fi_begin_vars; - unsigned * m_fi_end_vars; - region m_fi_entries; - bool is_fi_var(unsigned v) const { return std::find(m_fi_begin_vars, m_fi_end_vars, v) != m_fi_end_vars; } - fi_cache_entry * mk_fi_cache_entry(imdd * d, unsigned lower, unsigned upper, unsigned num_pairs, imdd_value_pair pairs[]); - mk_fi_result mk_filter_identical_core(imdd * d, unsigned offset, unsigned num_found, unsigned lower, unsigned upper, - bool destructive, bool memoize_res); - imdd * mk_filter_identical_main(imdd * d, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - - // v2 - obj_map m_filter_identical_cache; - void filter_identical_core2(imdd* d, unsigned num_vars, unsigned b, unsigned e, ptr_vector& ch); - imdd* filter_identical_core2(imdd* d, unsigned var, unsigned num_vars, bool memoize_res); - void filter_identical_main2(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - void swap_in(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars); - void swap_out(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars); - - // v3 - struct interval { - unsigned m_lo; - unsigned m_hi; - interval(unsigned lo, unsigned hi): m_lo(lo), m_hi(hi) {} - }; - struct interval_dd : public interval { - imdd* m_dd; - interval_dd(unsigned lo, unsigned hi, imdd* d): interval(lo, hi), m_dd(d) {} - }; - template - class id_map { - unsigned m_T; - unsigned_vector m_Ts; - svector*> m_vecs; - unsigned_vector m_alloc; - unsigned m_first_free; - void hard_reset() { - std::for_each(m_vecs.begin(), m_vecs.end(), delete_proc >()); - m_alloc.reset(); - m_first_free = 0; - m_vecs.reset(); - m_Ts.reset(); - m_T = 0; - } - - void allocate_entry(unsigned id) { - if (m_vecs[id]) { - return; - } - while (m_first_free < m_alloc.size()) { - if (m_vecs[m_first_free] && m_Ts[m_first_free] < m_T) { - svector* v = m_vecs[m_first_free]; - m_vecs[m_first_free] = 0; - m_vecs[id] = v; - ++m_first_free; - return; - } - ++m_first_free; - } - m_vecs[id] = alloc(svector); - m_alloc.push_back(id); - } - public: - id_map():m_T(0) {} - ~id_map() { hard_reset(); } - void reset() { ++m_T; m_first_free = 0; if (m_T == UINT_MAX) hard_reset(); } - svector& init(imdd* d) { - unsigned id = d->get_id(); - if (id >= m_vecs.size()) { - m_vecs.resize(id+1); - m_Ts.resize(id+1); - } - if (m_Ts[id] < m_T) { - allocate_entry(id); - m_vecs[id]->reset(); - m_Ts[id] = m_T; - } - return *m_vecs[id]; - } - - typedef svector data; - struct iterator { - unsigned m_offset; - id_map const& m; - iterator(unsigned o, id_map const& m):m_offset(o),m(m) {} - data const & operator*() const { return *m.m_vecs[m_offset]; } - data const * operator->() const { return &(operator*()); } - data * operator->() { return &(operator*()); } - iterator & operator++() { ++m_offset; return move_to_used(); } - iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const & it) const { return m_offset == it.m_offset; } - bool operator!=(iterator const & it) const { return m_offset != it.m_offset; } - iterator & move_to_used() { - while (m_offset < m.m_vecs.size() && - m.m_Ts[m_offset] < m.m_T) { - ++m_offset; - } - return *this; - } - }; - iterator begin() const { return iterator(0, *this).move_to_used(); } - iterator end() const { return iterator(m_vecs.size(), *this); } - }; - typedef id_map filter_id_map; - typedef id_map filter_idd_map; - filter_id_map m_nodes; - filter_idd_map m_nodes_dd; - svector m_i_nodes_dd, m_i_nodes_dd_tmp; - svector m_i_nodes, m_i_nodes_tmp; - unsigned_vector m_offsets; - void filter_identical_main3(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - void filter_identical_main3(imdd * d, imdd_ref& r, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res); - imdd* filter_identical_loop3(imdd * d, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res); - void refine_intervals(svector& i_nodes, svector const& i_nodes_dd); - void merge_intervals(svector& dst, svector const& src); - imdd* filter_identical_mk_nodes(imdd* d, unsigned v, bool del1, bool del2, bool memoize_res); - void print_filter_idd(std::ostream& out, filter_idd_map const& m); - void print_interval_dd(std::ostream& out, svector const& m); - - - - unsigned m_proj_num_vars; - unsigned * m_proj_begin_vars; - unsigned * m_proj_end_vars; - imdd2imdd_cache m_proj_cache; - bool is_proj_var(unsigned v) const { return std::find(m_proj_begin_vars, m_proj_end_vars, v) != m_proj_end_vars; } - void mk_project_init(unsigned num_vars, unsigned * vars); - void mk_project_core(imdd * d, imdd_ref & r, unsigned var, unsigned num_found, bool memoize_res); - void mk_project_dupdt_core(imdd_ref & d, unsigned var, unsigned num_found, bool memoize_res); - - imdd2imdd_cache m_swap_cache; - imdd * m_swap_new_child; - bool m_swap_granchildren_memoized; - imdd * mk_swap_new_child(unsigned lower, unsigned upper, imdd * child); - void mk_swap_acc1_dupdt(imdd_ref & d, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res); - void mk_swap_acc1(imdd * d, imdd_ref & r, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res); - void mk_swap_acc2(imdd_ref & r, unsigned lower1, unsigned upper1, unsigned lower2, unsigned upper2, imdd * grandchild, bool memoize_res); - void mk_swap_top_vars(imdd * d, imdd_ref & r, bool memoize_res); - imdd * mk_swap_memoize(imdd * d); - void mk_swap_core(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res); - void mk_swap_dupdt_core(imdd_ref & d, unsigned vidx, bool memoize_res); - - imdd2imdd_cache m_add_bounded_var_cache; - imdd * add_bounded_var_core(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res); - imdd * add_bounded_var_main(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res); - - friend struct distinct_proc; - imdd * mk_distinct_imdd(unsigned l1, unsigned u1, unsigned l2, unsigned u2, imdd * d, bool memoize_res = true); - - imdd_mk_filter_cache m_filter_cache; - region m_filter_entries; - unsigned m_filter_num_vars; - unsigned * m_filter_begin_vars; - unsigned * m_filter_end_vars; - unsigned_vector m_filter_context; - bool is_filter_var(unsigned v) const { return std::find(m_filter_begin_vars, m_filter_end_vars, v) != m_filter_end_vars; } - filter_cache_entry * mk_filter_cache_entry(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r); - imdd * is_mk_filter_cached(imdd * d, unsigned ctx_sz, unsigned * ctx); - void cache_mk_filter(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r); - void init_mk_filter(unsigned arity, unsigned num_vars, unsigned * vars); - template - void mk_filter_dupdt_core(imdd_ref & d, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res); - template - void mk_filter_core(imdd * d, imdd_ref & r, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res); - /** - \brief Filter the elements of the given IMDD using the given filter. - - The FilterProc template parameter is a filter for computing subsets of sets of the form: - - [L_1, U_1] X [L_2, U_2] X ... X [L_n, U_n] X d (where d is an IMDD) - - where n == num_vars - - The subset of elements is returned as an IMDD. - - FilterProc must have a method of the form: - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res); - - The size of the array lowers_uppers is 2*num_vars - - The arity of the resultant IMDD must be num_vars + d->get_arity(). - */ - template - void mk_filter_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res = true); - template - void mk_filter(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res = true); - - imdd * mk_disequal_imdd(unsigned l1, unsigned u1, unsigned value, imdd * d, bool memoize_res); - friend struct disequal_proc; - -public: - imdd_manager(); - - void inc_ref(imdd * d) { - if (d) - d->inc_ref(); - } - - void dec_ref(imdd * d) { - if (d) { - d->dec_ref(); - if (d->get_ref_count() == 0) - delete_imdd(d); - } - } - - unsigned get_num_nodes(imdd const * d) const; - - // count number of keys (rows) in table as if table is uncompressed. - size_t get_num_rows(imdd const* d) const; - - unsigned memory(imdd const * d) const; - -private: - imdd * _mk_empty(unsigned arity); - -public: - imdd * mk_empty(unsigned arity) { - imdd * r = _mk_empty(arity); - STRACE("imdd_trace", tout << "mk_empty(" << arity << ", 0x" << r << ");\n";); - return r; - } - -private: - imdd * memoize(imdd * d); - -public: - void memoize(imdd_ref const & d, imdd_ref & r) { r = memoize(d.get()); } - - void memoize(imdd_ref & d) { d = memoize(d.get()); } - - imdd * memoize_new_imdd_if(bool cond, imdd * r) { - if (cond && is_simple_node(r)) { - SASSERT(!r->is_shared()); - imdd * can_r = memoize(r); - if (can_r != r) { - SASSERT(r->get_ref_count() == 0); - delete_imdd(r); - } - return can_r; - } - return r; - } - -public: - void defrag(imdd_ref & d); - - void unmemoize(imdd * d); - - void unmemoize_rec(imdd * d); - - void copy(imdd * d, imdd_ref & r) { r = copy_main(d); } - - void insert_dupdt(imdd_ref & d, unsigned b, unsigned e, bool memoize_res = true) { - d = insert_main(d, b, e, true, memoize_res); - } - - void insert(imdd * d, imdd_ref & r, unsigned b, unsigned e, bool memoize_res = true) { - r = insert_main(d, b, e, false, memoize_res); - } - - void mk_product_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res = true) { - d1 = mk_product_main(d1.get(), d2, true, memoize_res); - } - - void mk_product(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res = true) { - r = mk_product_main(d1, d2, false, memoize_res); - STRACE("imdd_trace", tout << "mk_product(0x" << d1 << ", 0x" << d2 << ", 0x" << r.get() << ", " << memoize_res << ");\n";); - } - - void mk_union_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res = true) { - d1 = mk_union_main(d1.get(), d2, true, memoize_res); - } - - void mk_union(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res = true) { - r = mk_union_main(d1, d2, false, memoize_res); - STRACE("imdd_trace", tout << "mk_union(0x" << d1 << ", 0x" << d2 << ", 0x" << r.get() << ", " << memoize_res << ");\n";); - } - - void mk_complement_dupdt(imdd_ref & d, unsigned num, unsigned const * mins, unsigned const * maxs, bool memoize_res = true) { - d = mk_complement_main(d, num, mins, maxs, true, memoize_res); - } - - void mk_complement(imdd * d, imdd_ref & r, unsigned num, unsigned const * mins, unsigned const * maxs, bool memoize_res = true) { - r = mk_complement_main(d, num, mins, maxs, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_complement(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num; i++) tout << mins[i] << ", " << maxs[i] << ", "; - tout << memoize_res << ");\n";); - } - - void mk_filter_equal_dupdt(imdd_ref & d, unsigned vidx, unsigned value, bool memoize_res = true) { - d = mk_filter_equal_main(d, vidx, value, true, memoize_res); - } - - void mk_filter_equal(imdd * d, imdd_ref & r, unsigned vidx, unsigned value, bool memoize_res = true) { - r = mk_filter_equal_main(d, vidx, value, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_equal(0x" << d << ", 0x" << r.get() << ", " << vidx << ", " << value << ", " << memoize_res << ");\n";); - } - - void mk_filter_identical_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res = true) { - // d = mk_filter_identical_main(d, num_vars, vars, true, memoize_res); - filter_identical_main3(d, d, num_vars, vars, true, memoize_res); - } - - void mk_filter_identical(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res = true) { - filter_identical_main3(d, r, num_vars, vars, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_identical(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num_vars; i++) tout << vars[i] << ", "; - tout << memoize_res << ");\n";); - } - - void mk_project_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res = true); - - void mk_project(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res = true); - - // swap vidx and vidx+1 - void mk_swap_dupdt(imdd_ref & d, unsigned vidx, bool memoize_res = true); - - // swap vidx and vidx+1 - void mk_swap(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res = true); - - void add_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res = true) { - d = add_facts_main(d, num, lowers, uppers, true, memoize_res); - } - - void add_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res = true) { - r = add_facts_main(d, num, lowers, uppers, false, memoize_res); - - STRACE("imdd_trace", tout << "add_facts(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num; i++) tout << lowers[i] << ", " << uppers[i] << ", "; - tout << memoize_res << ");\n";); - } - - void add_fact_dupdt(imdd_ref & d, unsigned num, unsigned const * values, bool memoize_res = true) { - add_facts_dupdt(d, num, values, values, memoize_res); - } - - void add_fact(imdd * d, imdd_ref & r, unsigned num, unsigned const * values, bool memoize_res = true) { - add_facts(d, r, num, values, values, memoize_res); - } - - void add_bounded_var_dupdt(imdd_ref & d, unsigned before_vidx, unsigned lower, unsigned upper, bool memoize_res = true) { - d = add_bounded_var_main(d, before_vidx, lower, upper, true, memoize_res); - } - - void add_bounded_var(imdd * d, imdd_ref & r, unsigned before_vidx, unsigned lower, unsigned upper, bool memoize_res = true) { - r = add_bounded_var_main(d, before_vidx, lower, upper, false, memoize_res); - } - - void mk_filter_distinct_dupdt(imdd_ref & d, unsigned v1, unsigned v2, bool memoize_res = true); - - void mk_filter_distinct(imdd * d, imdd_ref & r, unsigned v1, unsigned v2, bool memoize_res = true); - - void mk_filter_disequal_dupdt(imdd_ref & d, unsigned var, unsigned value, bool memoize_res = true); - - void mk_filter_disequal(imdd * d, imdd_ref & r, unsigned var, unsigned value, bool memoize_res = true); - - void mk_join(imdd * d1, imdd * d2, imdd_ref & r, unsigned_vector const& vars1, unsigned_vector const& vars2, bool memoize_res = true); - - void mk_join_project(imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res = true); - - void mk_join_dupdt(imdd_ref & d1, imdd * d2, unsigned num_vars, unsigned const * vars1, unsigned const * vars2, bool memoize_res = true); - - void remove_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers); - - void remove_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers); - - bool is_equal(imdd * d1, imdd * d2); - - bool contains(imdd * d, unsigned num, unsigned const * values) const; - - bool contains(imdd * d, unsigned v) const { return contains(d, 1, &v); } - - bool contains(imdd * d, unsigned v1, unsigned v2) const { unsigned vs[2] = {v1, v2}; return contains(d, 2, vs); } - - bool contains(imdd * d, unsigned v1, unsigned v2, unsigned v3) const { unsigned vs[3] = {v1,v2,v3}; return contains(d, 3, vs); } - - bool subsumes(imdd * d1, imdd * d2); - - bool is_subset(imdd * d1, imdd * d2) { return subsumes(d2, d1); } - -private: - void display(std::ostream & out, imdd const * d, unsigned_vector & intervals, bool & first) const; - -public: - void display(std::ostream & out, imdd const * d) const; - - void display_ll(std::ostream & out, imdd const * d) const; - - /** - \brief Execute proc (once) in each node in the IMDD rooted by d. - */ - template - void for_each(imdd * d, Proc & proc) const { - // for_each is a generic procedure, we don't know what proc will actually do. - // So, it is not safe to reuse m_worklist and m_visited. - ptr_buffer worklist; - imdd_cache visited; - worklist.push_back(d); - while (!worklist.empty()) { - d = worklist.back(); - worklist.pop_back(); - if (d->is_shared() && visited.contains(d)) - continue; - if (d->is_shared()) - visited.insert(d); - proc(d); - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) - worklist.push_back(it->val()); - } - } - } - - class iterator { - bool m_done; - svector m_iterators; - svector m_element; - - void begin_iterators(imdd const * curr, unsigned start_idx); - - public: - iterator():m_done(true) {} - iterator(imdd_manager const & m, imdd const * d); - - unsigned get_arity() const { return m_element.size(); } - unsigned * operator*() const { return m_element.c_ptr(); } - iterator & operator++(); - - bool operator==(iterator const & it) const; - bool operator!=(iterator const & it) const { return !operator==(it); } - }; - - friend class iterator; - - iterator begin(imdd const * d) const { return iterator(*this, d); } - iterator end(imdd const * d) const { return iterator(); } -}; - -inline std::ostream & operator<<(std::ostream & out, imdd_ref const & r) { - r.get_manager().display(out, r.get()); - return out; -} - -struct mk_imdd_pp { - imdd * m_d; - imdd_manager & m_manager; - mk_imdd_pp(imdd * d, imdd_manager & m):m_d(d), m_manager(m) {} -}; - -inline mk_imdd_pp mk_pp(imdd * d, imdd_manager & m) { - return mk_imdd_pp(d, m); -} - -inline std::ostream & operator<<(std::ostream & out, mk_imdd_pp const & pp) { - pp.m_manager.display(out, pp.m_d); - return out; -} - -struct mk_imdd_ll_pp : public mk_imdd_pp { - mk_imdd_ll_pp(imdd * d, imdd_manager & m):mk_imdd_pp(d, m) {} -}; - -inline mk_imdd_ll_pp mk_ll_pp(imdd * d, imdd_manager & m) { - return mk_imdd_ll_pp(d, m); -} - -inline std::ostream & operator<<(std::ostream & out, mk_imdd_ll_pp const & pp) { - pp.m_manager.display_ll(out, pp.m_d); - return out; -} - -#endif /* _IMDD_H_ */ - diff --git a/src/muz_qe/interval_skip_list.h b/src/muz_qe/interval_skip_list.h deleted file mode 100644 index 7865e6ca5..000000000 --- a/src/muz_qe/interval_skip_list.h +++ /dev/null @@ -1,1876 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - interval_skip_list.h - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-01. - -Revision History: - ---*/ -#ifndef _INTERVAL_SKIP_LIST_H_ -#define _INTERVAL_SKIP_LIST_H_ - -#include"skip_list_base.h" - -/* - Interval skip lists implement a mapping from keys to values. - The key must be a total order. - - It compress consecutive entries k->v and (k+1)->v by - using intervals. Internally, we have [k1, k2]->v to represent - the set of entries k1->v, (k1+1)->v, ..., k2->v. - - For improving cache behavior, the entries are packed in - buckets. As regular skip-lists, a node/bucket may have - several next/forward pointers. - - Interval skip lists can also encode sets. In this case, - there is no value type. We achieve that by allowing the template - to be instantiated with an arbitrary "entry" class for encoding - [k1, k2]->v. This class must provide the methods: - - key const & begin_key() const - key const & end_key() const - value const & val() const - void set_begin_key(key const & k) - void set_end_key(key const & k) - void set_val(value const & v) - void display(ostream & out) const - bool operator==(entry const & other) const; - - And a definition for the key and value types. -*/ - -/** - \brief Default interval_skip_list entry. - It is useful for implementing mappings. -*/ -template -class default_islist_entry { -public: - typedef Key key; - typedef Value value; -private: - key m_begin_key; - key m_end_key; - value m_value; -public: - default_islist_entry() {} - default_islist_entry(key const & b, key const & e, value const & v): - m_begin_key(b), - m_end_key(e), - m_value(v) { - } - key const & begin_key() const { return m_begin_key; } - key const & end_key() const { return m_end_key; } - value const & val() const { return m_value; } - void set_begin_key(key const & k) { m_begin_key = k; } - void set_end_key(key const & k) { m_end_key = k; } - void set_val(value const & v) { m_value = v; } - void display(std::ostream & out) const { - out << "[" << begin_key() << ", " << end_key() << "] -> " << val(); - } -}; - -/** - \brief Default interval_skip_list entry for encoding sets. -*/ -template -struct default_set_islist_entry { -public: - typedef Key key; - typedef unsigned value; // don't care type -private: - key m_begin_key; - key m_end_key; -public: - default_set_islist_entry() {} - default_set_islist_entry(key const & b, key const & e): - m_begin_key(b), - m_end_key(e) { - } - key const & begin_key() const { return m_begin_key; } - key const & end_key() const { return m_end_key; } - unsigned const & val() const { static unsigned v = 0; return v; } - void set_begin_key(key const & k) { m_begin_key = k; } - void set_end_key(key const & k) { m_end_key = k; } - void set_val(value const & v) { /* do nothing */ } - void display(std::ostream & out) const { - out << "[" << begin_key() << ", " << end_key() << "]"; - } -}; - - -/** - \brief An interval skip list. - - See comments at skip_list_base.h -*/ -template -class interval_skip_list : public skip_list_base { -protected: - typedef typename skip_list_base::bucket bucket; -public: - typedef typename Traits::manager manager; - typedef typename Traits::entry entry; - typedef typename entry::key key; - typedef typename entry::value value; - -protected: - bool lt(key const & k1, key const & k2) const { return skip_list_base::lt(k1, k2); } - - static key const & first_key(bucket const * bt) { return bt->first_entry().begin_key(); } - - static key const & last_key(bucket const * bt) { return bt->last_entry().end_key(); } - - static void set_entry(bucket * bt, unsigned idx, key const & b, key const & e, value const & v) { - entry & en = bt->get(idx); - en.set_begin_key(b); - en.set_end_key(e); - en.set_val(v); - } - - /** - \brief Add first entry to the list. - - \remark This method will invoke inc_ref_eh for v. - */ - void insert_first_entry(manager & m, key const & b, key const & e, value const & v) { - entry en; - en.set_begin_key(b); - en.set_end_key(e); - en.set_val(v); - skip_list_base::insert_first_entry(m, en); - } - - /** - \brief Return true if the given key \c k is in the entry \c e. That is, - k \in [e.begin_key(), e.end_key()]. - */ - bool contains(entry const & e, key const & k) const { return this->leq(e.begin_key(), k) && this->leq(k, e.end_key()); } - - /** - \brief Return true if the given key \c k is in the entry \c e. That is, - k \in [e.begin_key(), e.end_key()]. If that is the case, then store e.value() in v. - Otherwise, return false. - */ - bool contains(entry const & e, key const & k, value & v) const { - if (contains(e, k)) { - v = e.val(); - return true; - } - else { - return false; - } - } - - /** - \brief Search for a key in a bucket starting at position s_idx using binary search. - - Return true if k was found in the bucket and store the index of - the entry containing \c k in \c idx. - - Otherwise, return false and \c idx will contain the index - s.t. - (idx == s_idx || entry[idx-1].end_key() < k) && (idx == bt->size() || k < entry[idx].begin_key()) - */ - bool find_core(bucket const * bt, unsigned s_idx, key const & k, unsigned & idx) const { - if (s_idx >= bt->size()) { - idx = s_idx; - return false; - } - int low = s_idx; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry const & mid_entry = bt->get(mid); - if (this->gt(k, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - idx = static_cast(mid) + 1; - SASSERT(idx >= s_idx); - SASSERT(idx == s_idx || lt(bt->get(idx - 1).end_key(), k)); - SASSERT(idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return false; - } - - } - else if (lt(k, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - idx = static_cast(mid); - SASSERT(idx >= s_idx); - SASSERT(idx == s_idx || lt(bt->get(idx - 1).end_key(), k)); - SASSERT(idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return false; - } - } - else { - SASSERT(contains(mid_entry, k)); - SASSERT(mid >= 0); - idx = static_cast(mid); - SASSERT(idx >= s_idx); - return true; - } - } - } - - /** - \brief Search for a key in a bucket using binary search. - - Return true if k was found in the bucket and store the index of - the entry containing \c k in \c idx. - - Otherwise, return false and \c idx will contain the index - s.t. - (idx == 0 || entry[idx-1].end_key() < k) && (idx == bt->size() || k < entry[idx].begin_key() - */ - bool find_core(bucket const * bt, key const & k, unsigned & idx) const { - bool r = find_core(bt, 0, k, idx); - SASSERT(!r || contains(bt->get(idx), k)); - SASSERT( r || idx == 0 || lt(bt->get(idx - 1).end_key(), k)); - SASSERT( r || idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return r; - } - - /** - \brief Search for the given key in the interval skip list. - Return true if the key is stored in the list, and store the location of the entry that contains the k in the - pair (bt, idx). The location should be read as the key is in the entry idx of the bucket bt. - Otherwise, returns false and (bt, idx) contains: - Case 1: bt != 0 && 0 < idx < bt->size() && bt->get(idx-1).end_key() < k && k < bt->get(idx).begin_key() - Case 2: bt != 0 && idx == 0 && (pred_bucket(bt) == m_header || last_key(pred_bucket(bt)) < k) && k < first_key(bt) - Case 3: bt == 0 && idx == 0 && k is greater than all keys in the list. - bt != m_header - - Even when find_core returns false, the pair (bt, idx) can be used to create an iterator object - to traverse keys >= k. - */ - template - bool find_core(key const & k, bucket * & bt, unsigned & idx, bucket * pred_vect[]) const { - bucket * curr = this->m_header; - unsigned i = this->m_header->level(); - bucket * next; - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0 && lt(first_key(next), k)) - curr = next; - else - break; - } - if (UpdatePredVect) - pred_vect[i] = curr; - } - - SASSERT(next == curr->get_next(0)); - - // the search_key must be in the current bucket, or in the first entry of the next bucket (if the next bucket is not 0). - SASSERT(curr->empty() || lt(first_key(curr), k)); - SASSERT(next == 0 || this->geq(first_key(next), k)); - DEBUG_CODE({ - if (UpdatePredVect && next != 0) - for (unsigned i = 0; i < next->level(); i++) - SASSERT(pred_vect[i]->get_next(i) == next); - }); - - if (next != 0 && contains(next->first_entry(), k)) { - bt = next; - idx = 0; - return true; - } - - bool r = find_core(curr, k, idx); - if (idx == curr->size()) { - SASSERT(!r); - bt = next; - idx = 0; - } - else { - SASSERT(idx < curr->size()); - bt = curr; - } - SASSERT(bt != this->m_header); - SASSERT((bt == 0 && idx == 0) || (bt != 0 && idx < bt->size())); - SASSERT(!r || contains(bt->get(idx), k)); - SASSERT(r || - // Case 1 - (bt != 0 && 0 < idx && idx < bt->size() && lt(bt->get(idx-1).end_key(), k) && lt(k, bt->get(idx).begin_key())) || - // Case 2 - (bt != 0 && idx == 0 && (this->pred_bucket(bt) == this->m_header || lt(last_key(this->pred_bucket(bt)), k)) && lt(k, first_key(bt))) || - // Case 3 - (bt == 0 && idx == 0) // k is greater than all keys in the list. - ); - return r; - } - - /** - \brief Return true if the two entries (that satisfy lt(e1, e2)) can be merged. - */ - bool can_be_merged(entry const & e1, entry const & e2) const { - return this->val_eq(e1.val(), e2.val()) && this->eq(this->succ(e1.end_key()), e2.begin_key()); - } - - /** - \brief Try to merge the last entry with bt with the first entry of its successor. - - \remark pred_vect contains the predecessors of the successor of bt. - */ - void merge_first_of_succ_if_possible(manager & m, bucket * bt, bucket * pred_vect[]) { - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - bucket * next_bucket = bt->get_next(0); - if (next_bucket != 0) { - entry & curr_entry = bt->last_entry(); - entry & next_entry = next_bucket->get(0); - if (can_be_merged(curr_entry, next_entry)) { - curr_entry.set_end_key(next_entry.end_key()); - this->del_entry(m, next_bucket, 0); // del_entry invokes dec_ref_eh - if (next_bucket->empty()) - this->del_bucket(m, next_bucket, pred_vect); - } - } - } - - /** - \brief Try to merge the entry at position idx with the next entry if possible. - */ - void merge_next_if_possible(manager & m, bucket * bt, unsigned idx, bucket * pred_vect[]) { - SASSERT(!bt->empty()); - if (idx + 1 == bt->size()) { - // it is the last element - merge_first_of_succ_if_possible(m, bt, pred_vect); - } - else { - entry & curr_entry = bt->get(idx); - entry & next_entry = bt->get(idx+1); - if (can_be_merged(curr_entry, next_entry)) { - curr_entry.set_end_key(next_entry.end_key()); - this->del_entry(m, bt, idx+1); // del_entry invokes dec_ref_eh - } - } - } - - /** - \brief Try to merge the entry at position idx with the previous entry if possible. - - \remark This method assumes that idx > 0. - */ - void merge_prev_if_possible(manager & m, bucket * bt, unsigned idx) { - SASSERT(idx > 0); - entry & curr_entry = bt->get(idx); - entry & prev_entry = bt->get(idx-1); - if (can_be_merged(prev_entry, curr_entry)) { - prev_entry.set_end_key(curr_entry.end_key()); - this->del_entry(m, bt, idx); // del_entry invokes dec_ref_eh - } - } - - /** - \brief Delete entries starting at indices [s_idx, e_idx), assuming e_idx < bt->size() - - \remark The pre-condition guarantees that the bucket will not be empty after the entries - are deleted. - - \remark If RECYCLE_ENTRY is true, then method will try to recycle position s_idx if it is deleted, and will return true. - Position s_idx will be an empty slot in this case. - */ - template - bool del_entries(manager & m, bucket * bt, unsigned s_idx, unsigned e_idx) { - bool result = false; - if (RECYCLE_ENTRY && s_idx + 1 < e_idx) { - // The position s_idx will be recycled, but the reference to the value stored there - // will be lost. - this->dec_ref(m, bt->get(s_idx).val()); - s_idx++; - result = true; - } - TRACE("del_entries_upto_bug", this->display(tout, bt); tout << "[" << s_idx << ", " << e_idx << ")\n";); - SASSERT(e_idx >= s_idx); - SASSERT(e_idx < bt->size()); - // move entries - unsigned num_removed = e_idx - s_idx; - entry * dest_it = bt->get_entries() + s_idx; - entry * source_it = bt->get_entries() + e_idx; - entry * source_end = bt->get_entries() + bt->size(); - if (Traits::ref_count) { - // invoke dec_ref_eh for entries between dest_it and source_it, since they are being removed - entry * it = dest_it; - for (; it < source_it; ++it) { - this->dec_ref(m, it->val()); - } - } - for (; source_it < source_end; ++dest_it, ++source_it) { - *dest_it = *source_it; - } - // update size - bt->shrink(num_removed); - SASSERT(!bt->empty()); - TRACE("del_entries_upto_bug", this->display(tout, bt);); - return result; - } - - /** - \brief Delete all keys (starting at position s_idx) in the given bucket that are <= k. - The method assumes that k < bt->last_key(). - This condition guarantees that the bucket will not be empty after removing the keys. - - - \remark If RECYCLE_ENTRY is true, then method will try to recycle position s_idx if it is deleted, and will return true. - Position s_idx will be an empty slot in this case. - */ - template - bool del_last_entries_upto(manager & m, bucket * bt, unsigned s_idx, key const & k) { - SASSERT(s_idx < bt->size()); - SASSERT(lt(k, last_key(bt))); - int low = s_idx; - int high = bt->size() - 1; - SASSERT(low <= high); - for (;;) { - int mid = low + ((high - low) / 2); - SASSERT(mid < static_cast(bt->size())); - entry & mid_entry = bt->get(mid); - if (this->gt(k, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // mid entry must be deleted since k > mid_entry.end_key(). - TRACE("del_entries_upto_bug", tout << "exit 1) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid + 1 < static_cast(bt->size())); // Reason: method pre-condition: lt(k, last_key(bt)) - return del_entries(m, bt, s_idx, mid + 1); - } - } - else if (lt(k, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // mid entry must not be deleted since k < mid_entry.begin_key(). - TRACE("del_entries_upto_bug", tout << "exit 2) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid < static_cast(bt->size())); // Reason: loop invariant - return del_entries(m, bt, s_idx, mid); - } - } - else { - SASSERT(contains(mid_entry, k)); - if (lt(k, mid_entry.end_key())) { - TRACE("del_entries_upto_bug", tout << "exit 3) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - mid_entry.set_begin_key(this->succ(k)); - SASSERT(mid < static_cast(bt->size())); // Reason: loop invariant - return del_entries(m, bt, s_idx, mid); - } - else { - // mid_entry should also be removed - TRACE("del_entries_upto_bug", tout << "exit 4) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid + 1 < static_cast(bt->size())); // Reason: method pre-condition: lt(k, last_key(bt)) - return del_entries(m, bt, s_idx, mid + 1); - } - } - } - } - - /** - \brief Keep deleting keys <= k in bt and its successors. - */ - void del_entries_upto_loop(manager & m, bucket * bt, key const & k, bucket * pred_vect []) { - SASSERT(this->check_pred_vect(bt, pred_vect)); - while (bt != 0) { - key const & bt_last_key = last_key(bt); - if (lt(k, bt_last_key)) { - del_last_entries_upto(m, bt, 0, k); - return; - } - else if (this->eq(k, bt_last_key)) { - this->del_bucket(m, bt, pred_vect); - return; - } - else { - SASSERT(this->gt(k, bt_last_key)); - bucket * next = bt->get_next(0); - this->del_bucket(m, bt, pred_vect); - bt = next; - // continue deleting... - } - } - } - - /** - \brief Delete entries starting at position 0 such that keys are <= k. - - If INSERT == true, then try to save/recycle an entry. Return true, if - managed to recycle the entry. - - The bucket bt may be deleted when INSERT==false and k >= last_key(bt). - - - pred_vect must contain the predecessors of bt. - - - next_pred_vect is an uninitialized predecessor vector. It may be initialized - when INSERT == true. If needed it is initialized using - update_predecessor_vector(pred_vect, bt, next_pred_vect); - */ - template - bool del_entries_upto(manager & m, bucket * bt, key const & k, bucket * pred_vect[], bucket * next_pred_vect[]) { - SASSERT(this->check_pred_vect(bt, pred_vect)); // pred_vect contains the predecessors of bt. - if (lt(k, first_key(bt))) { - // nothing to be done... - return false; // didn't manage to recycle entry. - } - - key const & bt_last_key = last_key(bt); - TRACE("del_entries_upto_bug", tout << "bt_last_key: " << bt_last_key << "\n";); - if (this->lt(k, bt_last_key)) { - return del_last_entries_upto(m, bt, 0, k); - } - else { - if (INSERT) { - // Invoke DEC-REF for all entries in bt - this->dec_ref(m, bt); - // REMARK: the slot 0 will be reused, but the element there is gone. - bt->set_size(1); - if (this->gt(k, bt_last_key)) { - bucket * next = bt->get_next(0); - if (next != 0) { - this->update_predecessor_vector(pred_vect, bt, next_pred_vect); - del_entries_upto_loop(m, next, k, next_pred_vect); - } - } - return true; // recycled entry. - } - else { - bucket * next = bt->get_next(0); - this->del_bucket(m, bt, pred_vect); // it will invoke dec_ref_eh for all values in bt. - // pred_vect does not need to be updated since it contains the predecessors of - // bt, since bt was deleted they are now the predecessors of its successor. - if (next != 0) { - del_entries_upto_loop(m, next, k, pred_vect); - } - return false; // don't care in this case, since it is not an insertion. - } - } - } - - /** - \brief Delete entries starting at position s_idx (> 0) such that keys are <= k. - The bucket bt cannot be deleted since s_idx > 0. - - If INSERT == true, then try to save/recycle an entry. Return true, if - managed to recycle the entry. - - - pred_vect must contain the predecessors of bt->get_next(0). - */ - template - bool del_entries_upto(manager & m, bucket * bt, unsigned s_idx, key const & k, bucket * pred_vect[]) { - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); // pred_vect contains the predecessors of the successor of bt. - SASSERT(s_idx > 0); - TRACE("del_entries_upto_bug", - tout << "INSERT: " << INSERT << "\n"; - tout << "del_entries_upto, s_idx: " << s_idx << ", k: " << k << "\n"; - this->display(tout, bt); - tout << "\n"; - this->display_predecessor_vector(tout, pred_vect);); - - if (s_idx >= bt->size()) { - // nothing to do in bt, moving to successors... - del_entries_upto_loop(m, bt->get_next(0), k, pred_vect); - return false; // didn't manage to recycle an entry - } - - if (lt(k, bt->get(s_idx).begin_key())) { - // nothing to be done... - return false; // didn't manage to recycle an entry - } - - key const & bt_last_key = last_key(bt); - TRACE("del_entries_upto_bug", tout << "bt_last_key: " << bt_last_key << "\n";); - if (lt(k, bt_last_key)) { - return del_last_entries_upto(m, bt, s_idx, k); - } - else { - if (this->gt(k, bt_last_key)) { - del_entries_upto_loop(m, bt->get_next(0), k, pred_vect); - } - if (Traits::ref_count) { - // Invoke dec_ref_eh for all values in [s_idx, bt->size()) - unsigned sz = bt->size(); - for (unsigned i = s_idx; i < sz; i++) - this->dec_ref(m, bt->get(i).val()); - } - if (INSERT) { - SASSERT(s_idx < bt->size()); - bt->set_size(s_idx + 1); - return true; // recycled an entry - - } - else { - bt->set_size(s_idx); - return false; // don't care. it is not an insertion. - } - } - } - - /** - \brief Insert entry [b,e]->v in the beginning of the bucket bt. - */ - void insert_begin(manager & m, bucket * bt, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "insert_begin: [" << b << ", " << e << "] -> " << v << "\n"; this->display(tout, bt);); - SASSERT(this->check_pred_vect(bt, pred_vect)); - SASSERT(!bt->empty()); - SASSERT(bt->size() <= bt->capacity()); - SASSERT(this->leq(b, first_key(bt))); - bucket * next_pred_vect[Traits::max_level]; - next_pred_vect[0] = 0; - - this->inc_ref(m, v); - - // Delete entries that will be overlapped by new entry. - // Try to reuse a slot that was deleted... - bool recycled = del_entries_upto(m, bt, e, pred_vect, next_pred_vect); - if (recycled) { - set_entry(bt, 0, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - TRACE("interval_skip_list_bug", this->display_physical(tout);); - if (next_pred_vect[0] != 0) { - // the vector next_pred_vect was initialized by del_entries_upto. - merge_next_if_possible(m, bt, 0, next_pred_vect); - } - else { - this->update_predecessor_vector(pred_vect, bt); - merge_next_if_possible(m, bt, 0, pred_vect); - } - return; - } - // check if can merge with first entry in the bucket. - entry & fe = bt->first_entry(); - if (this->val_eq(fe.val(), v) && this->eq(fe.begin_key(), this->succ(e))) { - // can merge - fe.set_begin_key(b); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - // Is there space for the new entry? - if (bt->size() == bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - } - else { - // there is no space - this->splice(m, bt, pred_vect); - } - } - this->open_space(bt, 0); - set_entry(bt, 0, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - SASSERT(!can_be_merged(bt->get(0), bt->get(1))); - } - - /** - \brief Insert the entry [b, e]->v at position idx. - */ - void insert_at(manager & m, bucket * bt, unsigned idx, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - SASSERT(idx > 0); - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - - this->inc_ref(m, v); - TRACE("insert_at_bug", tout << "before del_entries_upto:\n"; this->display_physical(tout);); - - bool recycled = del_entries_upto(m, bt, idx, e, pred_vect); - - TRACE("insert_at_bug", tout << "after del_entries_upto:\n"; this->display_physical(tout);); - - if (recycled) { - set_entry(bt, idx, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - merge_next_if_possible(m, bt, idx, pred_vect); - merge_prev_if_possible(m, bt, idx); - TRACE("insert_at_bug", tout << "using recycled:\n"; this->display_physical(tout);); - return; - } - - // Is there space for the new entry? - if (bt->size() == bt->capacity()) { - // there is no space - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - // there is no need to update pred_vect, since the list contains only one bucket. - } - else { - this->splice(m, bt, pred_vect); - bucket * new_next = bt->get_next(0); - SASSERT(bt->size() == bt->capacity()/2); - if (idx == bt->capacity()/2) { - entry & bt_last_entry = bt->last_entry(); - if (this->val_eq(bt_last_entry.val(), v) && this->eq(bt_last_entry.end_key(), this->pred(b))) { - // merged with the last key of bt - bt_last_entry.set_end_key(e); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - entry & new_next_first_entry = new_next->first_entry(); - if (this->val_eq(new_next_first_entry.val(), v) && this->eq(new_next_first_entry.begin_key(), this->succ(e))) { - // merged with the first key of new_next - new_next_first_entry.set_begin_key(b); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - // insert in the end of bt. - bt->set_size(bt->capacity()/2 + 1); - set_entry(bt, bt->capacity()/2, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - return; - } - else if (idx > bt->capacity()/2) { - idx -= bt->capacity()/2; - SASSERT(idx > 0); - bt = new_next; - this->update_predecessor_vector(pred_vect, bt); - } - } - } - SASSERT(idx > 0); - this->open_space(bt, idx); - set_entry(bt, idx, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - merge_next_if_possible(m, bt, idx, pred_vect); - merge_prev_if_possible(m, bt, idx); - TRACE("insert_at_bug", tout << "using open-space:\n"; this->display_physical(tout);); - } - - /** - \brief Insert the entry [b,e]->v into the bucket bt. - - pred_vect contains the predecessors of the successor of bt (i.e., bt->get_next(0)) - */ - void insert_inside(manager & m, bucket * bt, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "insert_inside: [" << b << ", " << e << "] -> " << v << "\n";); - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - SASSERT(!bt->empty()); - SASSERT(bt->size() <= bt->capacity()); - // perform binary search to find position to insert [b, e]->v - int low = 0; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry & mid_entry = bt->get(mid); - if (this->gt(b, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // insert after mid_entry since b > mid_entry.end_key(). - insert_at(m, bt, mid+1, b, e, v, pred_vect); - return; - } - } - else if (lt(b, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // insert before mid_entry since b < mid_entry.begin_key(). - SASSERT(mid > 0); // Reason: insert_begin would have been called instead. - insert_at(m, bt, mid, b, e, v, pred_vect); - return; - } - } - else { - SASSERT(contains(mid_entry, b)); - TRACE("insert_inside_bug", tout << "insert_inside:\n"; this->display(tout, bt);); - if (this->val_eq(mid_entry.val(), v)) { - if (this->gt(e, mid_entry.end_key())) { - // No need to create space. - // We did not create a new reference to v. - mid_entry.set_end_key(e); - del_entries_upto(m, bt, mid+1, e, pred_vect); - merge_next_if_possible(m, bt, mid, pred_vect); - return; - } - } - else { - if (this->gt(b, mid_entry.begin_key())) { - if (this->lt(e, mid_entry.end_key())) { - // New interval is the middle of existing interval - - // We must INVOKE add_ref_eh for mid_entry.val() and v. - this->inc_ref(m, v); - this->inc_ref(m, mid_entry.val()); // mid_entry was split in two. - - // we need two new entries. - if (bt->size() >= bt->capacity() - 1) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - } - else { - this->splice(m, bt, pred_vect); - int new_sz = bt->size(); - bucket * new_next = bt->get_next(0); - if (mid >= new_sz) { - mid -= new_sz; - SASSERT(mid >= 0); - bt = new_next; - } - } - } - this->open_2spaces(bt, mid); - entry & mid1_entry = bt->get(mid); - entry & new_entry = bt->get(mid+1); - entry & mid2_entry = bt->get(mid+2); - mid2_entry = mid1_entry; - mid1_entry.set_end_key(this->pred(b)); - new_entry.set_begin_key(b); - new_entry.set_end_key(e); - new_entry.set_val(v); - mid2_entry.set_begin_key(this->succ(e)); - } - else { - mid_entry.set_end_key(this->pred(b)); - insert_at(m, bt, mid+1, b, e, v, pred_vect); - } - } - else { - SASSERT(this->eq(b, mid_entry.begin_key())); - SASSERT(mid > 0); // Reason: insert_begin would have been called instead. - insert_at(m, bt, mid, b, e, v, pred_vect); - } - } - return; - } - } - } - - /** - \brief Remove [b,e]->v from the beginning of the bucket bt. - */ - void remove_begin(manager & m, bucket * bt, key const & b, key const & e, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "remove_begin: [" << b << ", " << e << "]\n";); - SASSERT(!bt->empty()); - SASSERT(pred_vect[0]->get_next(0) == bt); - del_entries_upto(m, bt, e, pred_vect, 0); - } - - /** - \brief Remove [b,e]->v from the bucket bt. - */ - void remove_inside(manager & m, bucket * bt, key const & b, key const & e, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "remove_inside: [" << b << ", " << e << "]\n";); - // perform binary search to find position to insert [b, e]->v - int low = 0; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry & mid_entry = bt->get(mid); - if (this->gt(b, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // insert after mid_entry since b > mid_entry.end_key(). - del_entries_upto(m, bt, mid+1, e, pred_vect); - return; - } - } - else if (this->lt(b, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // insert before mid_entry since b < mid_entry.begin_key(). - SASSERT(mid > 0); // Reason: remove_begin would have been called instead. - del_entries_upto(m, bt, mid, e, pred_vect); - return; - } - } - else { - SASSERT(contains(mid_entry, b)); - if (this->gt(b, mid_entry.begin_key())) { - if (this->lt(e, mid_entry.end_key())) { - // The removed interval is inside of an existing interval. - - // mid_entry will be split in two. So, we must invoke add_ref_eh for mid_entry.val() - this->inc_ref(m, mid_entry.val()); - - // We need to break mid_entry in two parts. - if (bt->size() == bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - SASSERT(bt->size() < bt->capacity()); - } - else { - this->splice(m, bt, pred_vect); - if (mid >= static_cast(bt->size())) { - // mid_entry moved to new (successor) bucket - mid -= bt->size(); - bt = bt->get_next(0); - } - } - } - this->open_space(bt, mid); - entry & mid1_entry = bt->get(mid); - entry & mid2_entry = bt->get(mid+1); - mid1_entry.set_end_key(this->pred(b)); - mid2_entry.set_begin_key(this->succ(e)); - } - else { - mid_entry.set_end_key(this->pred(b)); - del_entries_upto(m, bt, mid+1, e, pred_vect); - } - } - else { - SASSERT(this->eq(b, mid_entry.begin_key())); - SASSERT(mid > 0); // Reason: remove_begin would have been called instead. - del_entries_upto(m, bt, mid, e, pred_vect); - } - return; - } - } - } - -public: - interval_skip_list() { - } - - interval_skip_list(manager & m):skip_list_base(m) { - } - - ~interval_skip_list() { - } - - /** - \brief Copy the elements of other. - This method assumes that the *this* skip-list is empty. - */ - void copy(manager & m, interval_skip_list const & other) { - SASSERT(this->empty()); - other.clone_core(m, this); - } - - /** - \brief Return the smallest key stored in the interval skip list. - */ - key const & smallest() const { - SASSERT(!this->empty()); - return this->first_bucket()->get(0).begin_key(); - } - - /** - \brief Search for the given key in the interval skip list. - Return true if the key is stored in the list, and store the associated value in \c v. - */ - bool contains(key const & k, value & v) const { - bucket * bt; - unsigned idx; - if (find_core(k, bt, idx, 0)) { - v = bt->get(idx).val(); - return true; - } - return false; - } - - /** - \brief Alias for #contains. - */ - bool find(key const & k, value & v) const { - return contains(k, v); - } - -private: - /** - \brief Search for a bucket based on the key \c k. - - curr, next and pred_vect are output arguments. - - pred_vect must be an array of size level(). - - Post-conditions: - - pred_vect contains the predecessors of next. - That is, pred_vect[i] is the predecessor of level i. - - next is the successor of curr. - - pred_vect[0] == curr. - - curr == m_header || first_key(curr) < k - - next == 0 || k <= first_key(next) - */ - void find_bucket(key const & k, bucket * & curr, bucket * & next, bucket * pred_vect[]) { - SASSERT(this->level() > 0); - curr = this->m_header; - unsigned i = curr->level(); - SASSERT(i > 0); - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0 && lt(first_key(next), k)) - curr = next; - else - break; - } - pred_vect[i] = curr; - } - - SASSERT(next == curr->get_next(0)); - SASSERT(pred_vect[0] == curr); - DEBUG_CODE({ - if (next != 0) - for (unsigned i = 0; i < next->level(); i++) - SASSERT(pred_vect[i]->get_next(i) == next); - }); - SASSERT(curr == this->m_header || lt(first_key(curr), k)); - SASSERT(next == 0 || this->leq(k, first_key(next))); - } - -public: - - /** - \brief Insert the entries [i -> v] for every i \in [b, e]. - */ - void insert(manager & m, key const & b, key const & e, value const & v) { - SASSERT(this->leq(b, e)); - if (this->empty()) { - insert_first_entry(m, b, e, v); - return; - } - - // find the bucket where the new entries should be stored. - - // pred_vect[i] contains a pointer to the rightmost bucket of - // level i or higher that is to the left of the location of - // the insertion. - bucket * pred_vect[Traits::max_level]; - bucket * curr, * next; - find_bucket(b, curr, next, pred_vect); - - if (curr == this->m_header) { - SASSERT(next != 0); - // entry must be inserted in the first bucket. - SASSERT(this->first_bucket() == next); - insert_begin(m, next, b, e, v, pred_vect); - } - else if (next == 0 || this->gt(first_key(next), b)) { - insert_inside(m, curr, b, e, v, pred_vect); - } - else { - SASSERT(!curr->empty()); - SASSERT(!next->empty()); - SASSERT(next != 0); - SASSERT(this->eq(first_key(next), b)); - // Bucket curr is the predecessor of next. - SASSERT(curr->get_next(0) == next); - - // check if we can merge with last entry of curr - entry & curr_last_entry = curr->last_entry(); - if (this->val_eq(curr_last_entry.val(), v) && this->eq(curr_last_entry.end_key(), this->pred(b))) { - // No new reference to v was create, we don't need to invok inc_ref_eh - curr_last_entry.set_end_key(e); - del_entries_upto(m, next, e, pred_vect, 0); - merge_first_of_succ_if_possible(m, curr, pred_vect); - return; - } - insert_begin(m, next, b, e, v, pred_vect); - } - } - - /** - \brief Insert key [k->v]. - */ - void insert(manager & m, key const & k, value const & v) { - insert(m, k, k, v); - } - - class push_back_proc; - friend class push_back_proc; - - /** - \brief Functor for efficiently inserting elements in the end of the skip list. - - \remark The context becomes invalid if the skip-list is updated by other methods. - */ - class push_back_proc { - friend class interval_skip_list; - manager & m_manager; - interval_skip_list & m_list; - bucket * m_pred_vect[Traits::max_level]; - - bucket * last_bucket() const { return m_pred_vect[0]; } - - public: - push_back_proc(manager & m, interval_skip_list & l): - m_manager(m), - m_list(l) { - // initialize m_pred_vect - unsigned lvl = m_list.level(); - bucket * curr = m_list.m_header; - bucket * next; - unsigned i = lvl; - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0) - curr = next; - else - break; - } - m_pred_vect[i] = curr; - } - SASSERT(next == 0); - } - - interval_skip_list & list() { - return m_list; - } - - bool empty() const { - return m_list.empty(); - } - - key const & last_key() const { - return last_bucket()->last_entry().end_key(); - } - - void operator()(key const & b, key const & e, value const & v) { - SASSERT(m_list.leq(b, e)); - if (m_list.empty()) { - m_list.insert_first_entry(m_manager, b, e, v); - bucket * new_bucket = m_list.first_bucket(); - skip_list_base::update_predecessor_vector(m_pred_vect, new_bucket); - } - else { - bucket * bt = last_bucket(); - entry & et = bt->last_entry(); - SASSERT(m_list.lt(et.end_key(), b)); - // first check if new entry can be merged with the last entry in the list - if (m_list.val_eq(et.val(), v) && m_list.eq(et.end_key(), m_list.pred(b))) { - // can merge - et.set_end_key(e); - return; - } - // insert in the last bucket - unsigned sz = bt->size(); - if (sz >= bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(m_list.first_bucket() == bt && m_list.first_bucket()->get_next(0) == 0); - m_list.expand_first_bucket(m_manager); - bt = m_list.first_bucket(); - SASSERT(bt->size() < bt->capacity()); - skip_list_base::update_predecessor_vector(m_pred_vect, bt); - sz = bt->size(); - } - else { - // last bucket is full... creating new bucket... - unsigned new_bucket_lvl = m_manager.random_level(Traits::max_level); - bucket * new_bucket = interval_skip_list::mk_bucket(m_manager, new_bucket_lvl); - m_list.update_list_level(m_manager, new_bucket_lvl, m_pred_vect); - for (unsigned i = 0; i < new_bucket_lvl; i++) { - SASSERT(m_pred_vect[i]->get_next(i) == 0); - m_pred_vect[i]->set_next(i, new_bucket); - m_pred_vect[i] = new_bucket; - SASSERT(m_pred_vect[i]->get_next(i) == 0); - } - SASSERT(last_bucket() == new_bucket); - bt = new_bucket; - sz = 0; - } - } - SASSERT(sz < bt->capacity()); - m_list.inc_ref(m_manager, v); - bt->expand(1); - interval_skip_list::set_entry(bt, sz, b, e, v); - } - } - }; - - /** - \brief For each i \in [b, e] remove any entry [i->v] if it is in the list. - */ - void remove(manager & m, key const & b, key const & e) { - SASSERT(this->leq(b, e)); - if (this->empty()) - return; - bucket * pred_vect[Traits::max_level]; - bucket * curr, * next; - - find_bucket(b, curr, next, pred_vect); - - if (curr == this->m_header) { - SASSERT(next != 0); - remove_begin(m, next, b, e, pred_vect); - } - else if (next == 0 || this->gt(first_key(next), b)) { - remove_inside(m, curr, b, e, pred_vect); - } - else { - SASSERT(next != 0); - SASSERT(this->eq(first_key(next), b)); - remove_begin(m, next, b, e, pred_vect); - } - } - - /** - \brief Remove entry [k->v] for some v, if it is in the list. - */ - void remove(manager & m, key const & k) { - remove(m, k, k); - } - - /** - \brief Alias for #remove. - */ - void erase(manager & m, key const & b, key const & e) { - remove(m, b, e); - } - - /** - \brief Alias for #remove. - */ - void erase(manager & m, key const & k) { - remove(m, k, k); - } - - /** - \begin Traverse the list applying the functor f. - The functor must have a method - - bool operator()(key const & b, key const & e, value const & v) - - The method will invoke f(b, e, v) whenever the entries [i -> v] for i \in [b, e] are - in the list. - - If the functor returns false, then the traversal is interrupted. - */ - template - void for_each(Functor & f) const { - SASSERT(this->m_header->empty()); - bucket * curr = this->first_bucket(); - while (curr != 0) { - unsigned sz = curr->size(); - for (unsigned i = 0; i < sz; i++) { - entry const & e = curr->get(i); - if (!f(e.begin_key(), e.end_key(), e.val())) - return; - } - curr = curr->get_next(0); - } - } - - /** - \brief Return the next/successor buffer, but skipping buffers that do not contains keys greater than or equal to k. - */ - bucket * next_bucket(bucket const * bt, key const & k) const { - bucket * curr = bt->get_next(0); // move to successor - if (curr == 0) - return 0; - unsigned i = curr->level(); - unsigned max = i; - bucket * next = 0; - while (i > 0) { - --i; - for (;;) { - next = curr->get_next(i); - if (next != 0 && this->leq(first_key(next), k)) { - TRACE("interval_skip_list", tout << "next_bucket(" << k << "), i: " << i << " skipping #" << this->get_bucket_idx(curr); - tout << ", moving to: #" << this->get_bucket_idx(next) << "\n"; this->display(tout, next);); - curr = next; - if (curr->level() > max) { - max = curr->level(); - i = curr->level(); - TRACE("interval_skip_list", tout << "max: " << max << ", curr->level(): " << curr->level() << ", i: " << i << "\n";); - break; - } - } - else { - break; - } - } - } - SASSERT(i == 0); - SASSERT(curr->get_next(0) == next); - SASSERT(next == 0 || lt(k, first_key(next))); - return curr; - } - - class iterator; - friend class iterator; - - class iterator { - interval_skip_list const * m_list; - bucket const * m_curr; - unsigned m_idx; - public: - iterator():m_list(0), m_curr(0), m_idx(0) {} - iterator(interval_skip_list const * l, bucket const * b = 0, unsigned idx = 0):m_list(l), m_curr(b), m_idx(idx) {} - entry const & operator*() const { return m_curr->get(m_idx); } - entry const * operator->() const { return &(operator*()); } - iterator & operator++() { - SASSERT(m_curr); - m_idx++; - if (m_idx >= m_curr->size()) { - m_idx = 0; - m_curr = m_curr->get_next(0); - } - return *this; - } - iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - - bool at_end() const { - return m_curr == 0; - } - - /** - \brief Move the iterator to the next entry of the form ([b, e] -> v) s.t. - - 1) k in [b, e], or - 2) b > k and for every entry ([b',e']->v') between the old current entry and ([b,e]->v), we - have k > e' - - If such entry does not exist, then the iterator is moved to the end. - That is, at_end() returns true. - */ - void move_to(key const & k) { - SASSERT(m_curr); - SASSERT(m_idx < m_curr->size()); - entry const & curr_entry = m_curr->get(m_idx); - if (m_list->gt(k, curr_entry.end_key())) { - m_list->find_core(m_curr, m_idx+1, k, m_idx); - if (m_idx < m_curr->size()) - return; // found new position - SASSERT(m_idx == m_curr->size()); - m_curr = m_list->next_bucket(m_curr, k); - if (m_curr != 0) { - // search for k in the current buffer. - m_list->find_core(m_curr, k, m_idx); - if (m_idx == m_curr->size()) { - // k is greater than all keys in the list. - m_curr = 0; - m_idx = 0; - } - } - else { - SASSERT(m_curr == 0); - m_idx = 0; - } - } - } - - bool operator==(iterator const & it) const { - SASSERT(m_list == it.m_list); - return m_curr == it.m_curr && m_idx == it.m_idx; - } - - bool operator!=(iterator const & it) const { - SASSERT(m_list == it.m_list); - return m_curr != it.m_curr; - } - - /** - \brief Take the ownership of the current value and reset it to 0. - - \warning This method should be used with extreme care, since it puts the interval skip list - in an inconsistent state that must be restored. This method should be only used by developers - familiar with the interval skip-lists internal invariants. - For users not familiar with internal invariants, they should only use the reset method in the skip list class - after this method is invoked. - - Store in r the current value. - */ - template - void take_curr_ownership(manager & m, ObjRef & r) { - SASSERT(!at_end()); - entry & curr = const_cast(operator*()); // <<< HACK - r = curr.val(); - if (Traits::ref_count) - m.dec_ref_eh(curr.val()); - curr.set_val(0); - } - }; - - iterator begin() const { return iterator(this, this->first_bucket()); } - - iterator end() const { return iterator(this); } - - /** - \brief Return an iterator starting at the first entry that contains k. - If the skip-list does not contain k, then return the "end()" iterator. - */ - iterator find(key const & k) const { - bucket * bt; - unsigned idx; - if (find_core(k, bt, idx, 0)) { - return iterator(this, bt, idx); - } - else { - return end(); - } - } - - iterator find_geq(key const & k) const { - bucket * bt; - unsigned idx; - find_core(k, bt, idx, 0); - return iterator(this, bt, idx); - } - - /** - \brief Return true if the skip lists are equal. - */ - bool is_equal(interval_skip_list const & other) const { - iterator it1 = begin(); - iterator end1 = end(); - iterator it2 = other.begin(); - iterator end2 = other.end(); - for (; it1 != end1 && it2 != end2; it1++, it2++) { - entry const & e1 = *it1; - entry const & e2 = *it2; - if (!this->eq(e1.begin_key(), e2.begin_key())) - return false; - if (!this->eq(e1.end_key(), e2.end_key())) - return false; - if (!this->val_eq(e1.val(), e2.val())) - return false; - } - return true; - } - - /** - \brief Update the values stored in the skip-list by appling the given - functor to them. The functor must provide the operation: - - value operator()(value const & v); - - The functor must be injective. That is - - x != y implies f(x) != f(y) - - If a non-injective functor is used, then the resultant skip-list may - not be in a consistent state. - */ - template - void update_values(manager & m, InjectiveFunction & f) { - if (!this->empty()) { - iterator it = begin(); - iterator it_end = end(); - for (; it != it_end; ++it) { - entry & curr = const_cast(*it); - value const & old_val = curr.val(); - value new_val = f(old_val); - this->inc_ref(m, new_val); - this->dec_ref(m, old_val); - curr.set_val(new_val); - } - SASSERT(check_invariant()); - } - } - - class ext_iterator; - friend class ext_iterator; - - class ext_iterator { - friend class interval_skip_list; - - interval_skip_list * m_list; - bucket * m_curr; - unsigned m_idx; - bucket * m_pred_vect[Traits::max_level]; - - void move_next_bucket() { - m_list->update_predecessor_vector(m_pred_vect, m_curr); - m_idx = 0; - m_curr = m_curr->get_next(0); - } - - public: - ext_iterator():m_list(0), m_curr(0), m_idx(0) {} - - entry const & operator*() const { return m_curr->get(m_idx); } - - entry const * operator->() const { return &(operator*()); } - - ext_iterator & operator++() { - SASSERT(m_curr); - m_idx++; - if (m_idx >= m_curr->size()) - move_next_bucket(); - return *this; - } - - ext_iterator operator++(int) { ext_iterator tmp = *this; ++*this; return tmp; } - - bool at_end() const { - return m_curr == 0; - } - - bool operator==(ext_iterator const & it) const { - return m_curr == it.m_curr && m_idx == it.m_idx; - } - - bool operator!=(ext_iterator const & it) const { - return m_curr != it.m_curr; - } - - void erase(manager & m) { - SASSERT(!at_end()); - SASSERT(m_curr->size() > 0); - if (m_curr->size() > 1) { - m_list->del_entry(m, m_curr, m_idx); - if (m_idx >= m_curr->size()) - move_next_bucket(); - } - else { - SASSERT(m_curr->size() == 1); - bucket * old_curr = m_curr; - m_curr = m_curr->get_next(0); - m_list->del_bucket(m, old_curr, m_pred_vect); - } - } - }; - - void move_begin(ext_iterator & it) { - if (!this->empty()) { - it.m_list = this; - it.m_curr = this->first_bucket(); - it.m_idx = 0; - unsigned lvl = this->level(); - for (unsigned i = 0; i < lvl; i++) - it.m_pred_vect[i] = this->m_header; - } - else { - it.m_curr = 0; - it.m_idx = 0; - } - } - - void move_geq(ext_iterator & it, key const & k) { - it.m_list = this; - find_core(k, it.m_curr, it.m_idx, it.m_pred_vect); - } - -private: - /** - \brief Auxiliary data-structure used to implement the join of two interval_skip_lists. - To implement an efficient join, we want to be able to skip as many entries as possible. - */ - struct join_state { - bucket * m_bucket; - unsigned m_entry_idx; - key m_head; // it it a key in [m_bucket->m_entries[m_entry_idx].begin_key(), m_bucket->m_entries[m_entry_idx].end_key()] - public: - join_state(bucket * bt): - m_bucket(bt), - m_entry_idx(0), - m_head(bt->first_entry().begin_key()) { - } - - bool done() const { - return m_bucket == 0; - } - - key const & head() const { - SASSERT(!done()); - return m_head; - } - - key const & tail() const { - SASSERT(!done()); - SASSERT(m_entry_idx < m_bucket->size()); - return m_bucket->get(m_entry_idx).end_key(); - } - - value const & val() const { - SASSERT(!done()); - return m_bucket->get(m_entry_idx).val(); - } - }; - - /** - \brief Create a join_state auxiliary data-structure for performing a join starting at key k. - */ - join_state mk_join_state(key const & k) const { - return join_state(next_bucket(this->m_header, k)); - } - - /** - \brief Move the join_state towards k. - */ - void move_js(join_state & js, key const & k) const { - SASSERT(!js.done()); - if (this->leq(k, js.tail())) { - // We can't skip the current entry, because k in inside it. - // So, we just update the head. - js.m_head = k; - } - else { - // Moving to the next entry. - js.m_entry_idx++; - if (js.m_entry_idx < js.m_bucket->size()) { - // Update js.m_head with the beginning of the next entry. - js.m_head = js.m_bucket->get(js.m_entry_idx).begin_key(); - } - else { - // We processed all entries in the current bucket. So, set state to js.m_move_next. - js.m_bucket = next_bucket(js.m_bucket, k); - js.m_entry_idx = 0; - if (js.m_bucket != 0) - js.m_head = first_key(js.m_bucket); - } - } - } - -public: - - /** - \brief Join two interval_skip_lists and apply the given functor in the process. - - The functor must have a method - - bool operator()(key const & b, key const & e, value const & v1, value const & v2) - - The method will invoke f(b, e, v1, v2) whenever forall i \in [b, e] entries [i -> v1] are in the *this* list, and [i->v2] in the *other* list. - */ - template - void join(interval_skip_list const & other, Functor & f) { - if (this->empty() || other.empty()) - return; - key const & f1 = smallest(); - key const & f2 = other.smallest(); - key const & smallest_key = leq(f1, f2) ? f1 : f2; - join_state s1 = mk_join_state(smallest_key); - join_state s2 = other.mk_join_state(smallest_key); - while (!s1.done() && !s2.done()) { - key const & h1 = s1.head(); - key const & h2 = s2.head(); - if (eq(h1, h2)) { - key const & t1 = s1.tail(); - key const & t2 = s2.tail(); - key const & t = leq(t1, t2) ? t1 : t2; - f(h1, t, s1.val(), s2.val()); - key next_key = succ(t); - move_js(s1, next_key); - move_js(s2, next_key); - } - else if (lt(h1, h2)) { - move_js(s1, h2); - } - else { - SASSERT(lt(h2, h1)); - move_js(s2, h1); - } - } - } - -#ifdef Z3DEBUG -private: - bool check_invariant(entry const & e) const { - SASSERT(this->leq(e.begin_key(), e.end_key())); - return true; - } - - /** - \brief Return true if the last key of \c e1 is less than the first key of \c e2. - */ - bool lt(entry const & e1, entry const & e2) const { - return lt(e1.end_key(), e2.begin_key()); - } - - bool check_invariant(bucket const * bt) const { - SASSERT(bt->size() <= bt->capacity()); - SASSERT(bt == this->m_header || !bt->empty()); - for (unsigned i = 0; i < bt->size(); i++) { - entry const & curr = bt->get(i); - check_invariant(curr); - if (i > 0) { - entry const & prev = bt->get(i-1); - CTRACE("interval_skip_list", !lt(prev, curr), this->display_physical(tout);); - SASSERT(lt(prev, curr)); - CTRACE("interval_skip_list", can_be_merged(prev, curr), this->display_physical(tout);); - SASSERT(!can_be_merged(prev, curr)); - } - } - return true; - } - -public: - bool check_invariant() const { - SASSERT(this->m_header->empty()); - for (unsigned i = 0; i < this->m_header->level(); i++) { - // traverse buckets using get_next(i) pointers - bucket const * curr = this->m_header->get_next(i); - while (curr != 0) { - SASSERT(!curr->empty()); // only the header is empty. - bucket const * next = curr->get_next(i); - if (next != 0) { - SASSERT(next->level() >= i); - SASSERT(i == 0 || this->is_reachable_at_i(curr, next, i-1)); - SASSERT(!next->empty()); - entry const & last_of_curr = curr->last_entry(); - entry const & first_of_next = next->first_entry(); - CTRACE("interval_skip_list", !lt(last_of_curr, first_of_next), - this->display_physical(tout); - tout << "\ncurr:\n"; - this->display(tout, curr); - tout << "\nnext:\n"; - this->display(tout, next);); - SASSERT(lt(last_of_curr, first_of_next)); - CTRACE("interval_skip_list", can_be_merged(last_of_curr, first_of_next), - this->display_physical(tout); - tout << "\ncurr:\n"; - this->display(tout, curr); - tout << "\nnext:\n"; - this->display(tout, next);); - SASSERT(!can_be_merged(last_of_curr, first_of_next)); - } - curr = next; - } - } - bucket const * curr = this->m_header; - while (curr != 0) { - check_invariant(curr); - curr = curr->get_next(0); - } - return true; - } -#endif - - static void display_size_info(std::ostream & out) { - skip_list_base::display_size_info_core(out, sizeof(interval_skip_list)); - } - - /** - \brief Return the amount of memory consumed by the list. - */ - unsigned memory() const { - return this->memory_core(sizeof(interval_skip_list)); - } - -}; - -/** - \brief Traits for instantiating a mapping from unsigned to Value using the interval_skip_list template. -*/ -template, - unsigned MaxCapacity=32, - unsigned MaxLevel=32, - bool RefCount=false, - typename Manager=sl_manager_base > -struct unsigned_interval_skip_list_traits : private EqProc { - typedef default_islist_entry entry; - typedef Manager manager; - typedef typename entry::key key; - typedef typename entry::value value; - static const unsigned max_capacity = MaxCapacity; - static const unsigned initial_capacity = 2; - static const unsigned max_level = MaxLevel; - static const bool ref_count = RefCount; - - bool lt(key const & k1, key const & k2) const { return k1 < k2; } - bool eq(key const & k1, key const & k2) const { return k1 == k2; } - key succ(key const & k) const { return k + 1; } - key pred(key const & k) const { SASSERT(k > 0); return k - 1; } - bool val_eq(value const & v1, value const & v2) const { return EqProc::operator()(v1, v2); } -}; - -/** - \brief Traits for instantiating a set of unsigned values using the interval_skip_list template. -*/ -template > -struct unsigned_set_interval_skip_list_traits { - typedef default_set_islist_entry entry; - typedef Manager manager; - typedef typename entry::key key; - typedef typename entry::value value; - static const unsigned max_capacity = MaxCapacity; - static const unsigned initial_capacity = 2; - static const unsigned max_level = MaxLevel; - static const bool ref_count = false; - - bool lt(key const & k1, key const & k2) const { return k1 < k2; } - bool eq(key const & k1, key const & k2) const { return k1 == k2; } - key succ(key const & k) const { return k + 1; } - key pred(key const & k) const { SASSERT(k > 0); return k - 1; } - bool val_eq(value const & v1, value const & v2) const { return true; } -}; - -/** - \brief Generic adapater for generating a set-like API on top of the map API -*/ -template -class map2set_adapter : public Map { - typedef typename Map::manager manager; - typedef typename Map::key key; - typedef typename Map::value value; - - template - struct for_each_functor_adapter : public Functor { - for_each_functor_adapter(Functor const & f):Functor(f) { - } - bool operator()(key const & b, key const & e, value const & v) { - return Functor::operator()(b, e); - } - }; - -public: - map2set_adapter(manager & m): - Map(m) { - SASSERT(this->m_header != 0); - } - - void insert(manager & m, key const & b, key const & e) { - value _dummy; - Map::insert(m, b, e, _dummy); - } - - void insert(manager & m, key const & k) { - value _dummy; - Map::insert(m, k, k, _dummy); - } - - bool contains(key const & k) const { - value _dummy; - return Map::contains(k); - } - - bool find(key const & k) const { - return contains(k); - } - - /** - \begin Traverse the set applying the functor f. - The functor must have a method - - bool operator()(key const & b, key const & e) - - The method will invoke f(b, e) whenever the interval [b, e] are - in the set. - - If the functor returns false, then the traversal is interrupted. - */ - template - void for_each(Functor & f) const { - for_each_functor_adapter F(f); - Map::for_each(F); - } -}; - - -/** - \brief A set of unsigned values using interval_skip_list. -*/ -template > -class unsigned_isp_set : public map2set_adapter > > { -public: - unsigned_isp_set(Manager & m): - map2set_adapter > >(m) { - } -}; - -#endif /* _INTERVAL_SKIP_LIST_H_ */ - - diff --git a/src/muz_qe/nlarith_util.cpp b/src/muz_qe/nlarith_util.cpp index 5f8a24e99..c555b71f1 100644 --- a/src/muz_qe/nlarith_util.cpp +++ b/src/muz_qe/nlarith_util.cpp @@ -1028,7 +1028,6 @@ namespace nlarith { }; class sqrt_subst : public isubst { - bool m_even; sqrt_form const& m_s; public: sqrt_subst(imp& i, sqrt_form const& s): isubst(i), m_s(s) {} diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index d4027d73d..76f744325 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -43,6 +43,7 @@ Notes: #include "ast_ll_pp.h" #include "proof_checker.h" #include "smt_value_sort.h" +#include "proof_utils.h" namespace pdr { @@ -275,7 +276,7 @@ namespace pdr { 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(); @@ -596,7 +597,7 @@ namespace pdr { expr_ref fml = pm.mk_and(conj); th_rewriter rw(m); rw(fml); - if (ctx.is_dl()) { + if (ctx.is_dl() || ctx.is_utvpi()) { hoist_non_bool_if(fml); } TRACE("pdr", tout << mk_pp(fml, m) << "\n";); @@ -1225,6 +1226,7 @@ namespace pdr { m_search(m_params.bfs_model_search()), m_last_result(l_undef), m_inductive_lvl(0), + m_expanded_lvl(0), m_cancel(false) { } @@ -1285,7 +1287,7 @@ namespace pdr { 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";); - VERIFY (rels.find(*itf, pt_user)); + pt_user = rels.find(*itf); pt_user->add_use(pt); } } @@ -1357,9 +1359,10 @@ namespace pdr { 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(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) { @@ -1405,6 +1408,7 @@ namespace pdr { bool is_dl() const { return m_is_dl; } + bool is_utvpi() const { return m_is_utvpi; } private: @@ -1425,6 +1429,7 @@ namespace pdr { mark.reset(); m_is_dl = false; + m_is_utvpi = false; if (m_has_arith) { ptr_vector forms; for (it = rules.begin(); it != end; ++it) { @@ -1436,6 +1441,7 @@ namespace pdr { } } 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()); } } @@ -1559,6 +1565,12 @@ namespace pdr { m_fparams.m_arith_mode = AS_DIFF_LOGIC; m_fparams.m_arith_expand_eqs = true; } + else if (classify.is_utvpi() && m_params.use_utvpi()) { + IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); + m_fparams.m_arith_mode = AS_UTVPI; + m_fparams.m_arith_expand_eqs = true; + } + } if (!use_mc && m_params.use_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); @@ -1680,6 +1692,9 @@ namespace pdr { proof = m_search.get_proof_trace(*this); TRACE("pdr", tout << "PDR trace: " << mk_pp(proof, m) << "\n";); apply(m, m_pc.get(), proof); + TRACE("pdr", tout << "PDR trace: " << mk_pp(proof, m) << "\n";); + // proof_utils::push_instantiations_up(proof); + // TRACE("pdr", tout << "PDR up: " << mk_pp(proof, m) << "\n";); return proof; } @@ -1711,6 +1726,7 @@ namespace pdr { bool reachable; while (true) { checkpoint(); + m_expanded_lvl = lvl; reachable = check_reachability(lvl); if (reachable) { throw model_exception(); @@ -1769,6 +1785,10 @@ namespace pdr { 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(); + } if (n.pt().is_reachable(n.state())) { TRACE("pdr", tout << "reachable\n";); @@ -1835,7 +1855,7 @@ namespace pdr { if (m_params.simplify_formulas_pre()) { simplify_formulas(); } - for (unsigned lvl = 0; lvl <= max_prop_lvl; lvl++) { + 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(); diff --git a/src/muz_qe/pdr_context.h b/src/muz_qe/pdr_context.h index 6aa02ef10..1785991c6 100644 --- a/src/muz_qe/pdr_context.h +++ b/src/muz_qe/pdr_context.h @@ -303,6 +303,7 @@ namespace pdr { 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; volatile bool m_cancel; @@ -366,7 +367,7 @@ namespace pdr { 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(); diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index e2232dafe..437c08f6a 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -25,7 +25,6 @@ Revision History: #include "dl_mk_rule_inliner.h" #include "dl_rule.h" #include "dl_rule_transformer.h" -#include "dl_mk_extract_quantifiers.h" #include "smt2parser.h" #include "pdr_context.h" #include "pdr_dl_interface.h" @@ -33,7 +32,7 @@ Revision History: #include "dl_mk_slice.h" #include "dl_mk_unfold.h" #include "dl_mk_coalesce.h" -#include "pdr_quantifiers.h" +#include "model_smt2_pp.h" using namespace pdr; @@ -57,41 +56,36 @@ dl_interface::~dl_interface() { // re-use existing context. // void dl_interface::check_reset() { - datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules(); + 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.size(); ++i) { + 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[i])) { + if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) { is_subsumed = true; } } if (!is_subsumed) { - TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule ");); + TRACE("pdr", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule ");); m_context->reset(); } } - m_old_rules.reset(); - m_old_rules.add_rules(new_rules.size(), new_rules.c_ptr()); + 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_pdr_rules.reset(); m_refs.reset(); m_pred2slice.reset(); ast_manager& m = m_ctx.get_manager(); - datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); + datalog::rule_manager& rm = m_ctx.get_rule_manager(); datalog::rule_set old_rules(m_ctx.get_rules()); func_decl_ref query_pred(m); - datalog::rule_ref_vector query_rules(rule_manager); - datalog::rule_ref query_rule(rule_manager); - rule_manager.mk_query(query, query_pred, query_rules, query_rule); - m_ctx.add_rules(query_rules); + rm.mk_query(query, m_ctx.get_rules()); expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); @@ -107,7 +101,6 @@ lbool dl_interface::query(expr * query) { ); - m_ctx.set_output_predicate(query_pred); m_ctx.apply_default_transformation(); if (m_ctx.get_params().slice()) { @@ -115,8 +108,6 @@ lbool dl_interface::query(expr * query) { datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); - query_pred = slice->get_predicate(query_pred.get()); - m_ctx.set_output_predicate(query_pred); // track sliced predicates. obj_map const& preds = slice->get_predicates(); @@ -142,23 +133,20 @@ lbool dl_interface::query(expr * query) { --num_unfolds; } } - // remove universal quantifiers from body. + if (m_ctx.get_rules().get_output_predicates().empty()) { + m_context->set_unsat(); + return l_false; + } - - datalog::mk_extract_quantifiers* extract_quantifiers = alloc(datalog::mk_extract_quantifiers, m_ctx); - datalog::rule_transformer extract_q_tr(m_ctx); - extract_q_tr.register_plugin(extract_quantifiers); - m_ctx.transform_rules(extract_q_tr); - + query_pred = m_ctx.get_rules().get_output_predicate(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); - m_pdr_rules.add_rules(m_ctx.get_rules()); + m_pdr_rules.replace_rules(m_ctx.get_rules()); m_pdr_rules.close(); + m_ctx.record_transformed_rules(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); - - quantifier_model_checker quantifier_mc(*m_context, m, extract_quantifiers->quantifiers(), m_pdr_rules); datalog::scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. @@ -170,23 +158,11 @@ lbool dl_interface::query(expr * query) { 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; } - lbool result; - while (true) { - result = m_context->solve(); - if (result == l_true && extract_quantifiers->has_quantifiers()) { - result = quantifier_mc.check(); - if (result != l_false) { - return result; - } - // else continue - } - else { - return result; - } - } + return m_context->solve(); } diff --git a/src/muz_qe/pdr_farkas_learner.cpp b/src/muz_qe/pdr_farkas_learner.cpp index 489b2437e..71404ab12 100644 --- a/src/muz_qe/pdr_farkas_learner.cpp +++ b/src/muz_qe/pdr_farkas_learner.cpp @@ -216,6 +216,9 @@ namespace pdr { } 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 tmp(m); rw(res, tmp, pr); @@ -418,11 +421,10 @@ namespace pdr { class farkas_learner::constant_replacer_cfg : public default_rewriter_cfg { - ast_manager& m; const obj_map& m_translation; public: - constant_replacer_cfg(ast_manager& m, const obj_map& translation) - : m(m), m_translation(translation) + constant_replacer_cfg(const obj_map& translation) + : m_translation(translation) { } bool get_subst(expr * s, expr * & t, proof * & t_pr) { diff --git a/src/muz_qe/pdr_manager.cpp b/src/muz_qe/pdr_manager.cpp index 04facc776..3e79e4f00 100644 --- a/src/muz_qe/pdr_manager.cpp +++ b/src/muz_qe/pdr_manager.cpp @@ -132,7 +132,7 @@ namespace pdr { 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_head()->get_decl()); + bound_decls.insert(rules[i]->get_decl()); } for (unsigned i = 0; i < rules.size(); ++i) { unsigned u_sz = rules[i]->get_uninterpreted_tail_size(); diff --git a/src/muz_qe/pdr_prop_solver.cpp b/src/muz_qe/pdr_prop_solver.cpp index daa52992f..e3cd0d9c5 100644 --- a/src/muz_qe/pdr_prop_solver.cpp +++ b/src/muz_qe/pdr_prop_solver.cpp @@ -145,7 +145,8 @@ namespace pdr { 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_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); } @@ -382,26 +383,32 @@ namespace pdr { fl.get_lemmas(pr, bs, lemmas); safe.elim_proxies(lemmas); fl.simplify_lemmas(lemmas); // redundant? - if (m_fparams.m_arith_mode == AS_DIFF_LOGIC && - !is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) { - IF_VERBOSE(1, - 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); - return; + + 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_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); } lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) { diff --git a/src/muz_qe/pdr_quantifiers.cpp b/src/muz_qe/pdr_quantifiers.cpp deleted file mode 100644 index 4a7b4b995..000000000 --- a/src/muz_qe/pdr_quantifiers.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - pdr_quantifiers.cpp - -Abstract: - - Module for handling quantifiers in rule bodies. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-19. - -Revision History: - ---*/ - -#include "pdr_quantifiers.h" -#include "pdr_context.h" -#include "qe.h" -#include "var_subst.h" -#include "dl_rule_set.h" -#include "ast_smt2_pp.h" -#include "model_smt2_pp.h" -#include "ast_smt_pp.h" -#include "expr_abstract.h" -#include "dl_mk_extract_quantifiers.h" -#include "qe_lite.h" -#include "well_sorted.h" -#include "expr_safe_replace.h" - - -namespace pdr { - - /** - \brief model check a potential model against quantifiers in bodies of rules. - - \return true if the model rooted in 'root' is checks with the quantifiers, otherwise - 'false' and a set of instantiations that contradict the current model. - */ - - static void get_nodes(model_node& root, ptr_vector& nodes) { - ptr_vector todo; - todo.push_back(&root); - while (!todo.empty()) { - model_node* n = todo.back(); - todo.pop_back(); - nodes.push_back(n); - todo.append(n->children().size(), n->children().c_ptr()); - } - } - - quantifier_model_checker::~quantifier_model_checker() { - obj_map::iterator it = m_reachable.begin(), end = m_reachable.end(); - for (; it != end; ++it) { - m.dec_ref(it->m_value); - } - } - - void quantifier_model_checker::generalize_binding(expr_ref_vector const& binding, vector& bindings) { - expr_ref_vector new_binding(m); - generalize_binding(binding, 0, new_binding, bindings); - } - - void quantifier_model_checker::generalize_binding( - expr_ref_vector const& binding, unsigned idx, - expr_ref_vector& new_binding, vector& bindings) { - if (idx == binding.size()) { - bool non_zero = false; - for (unsigned i = 0; i < binding.size(); ++i) { - if (new_binding[i].get()) { - non_zero = true; - } - else { - new_binding[i] = binding[i]; - } - } - if (non_zero) { - TRACE("pdr", - for (unsigned i = 0; i < new_binding.size(); ++i) { - tout << mk_pp(new_binding[i].get(), m) << " "; - } - tout << "\n";); - bindings.push_back(new_binding); - } - return; - } - model_node& node = *m_current_node; - expr_ref_vector ands(m); - expr* e1, *e2; - datalog::flatten_and(node.state(), ands); - new_binding.push_back(0); - generalize_binding(binding, idx + 1, new_binding, bindings); - for (unsigned i = 0; i < ands.size(); ++i) { - if (m.is_eq(ands[i].get(), e1, e2)) { - if (e2 == binding[idx]) { - new_binding[new_binding.size()-1] = e1; - generalize_binding(binding, idx + 1, new_binding, bindings); - } - } - } - new_binding.pop_back(); - } - - - void quantifier_model_checker::add_binding(quantifier* q, expr_ref_vector& binding) { - if (binding.size() != q->get_num_decls()) { - // not a full binding. It may happen that the quantifier got simplified. - return; - } - apply_binding(q, binding); - vector bindings; - generalize_binding(binding, bindings); - for (unsigned i = 0; i < bindings.size(); ++i) { - apply_binding(q, bindings[i]); - } - } - - void quantifier_model_checker::apply_binding(quantifier* q, expr_ref_vector& binding) { - datalog::scoped_no_proof _scp(m); - app_ref_vector& var_inst = m_current_pt->get_inst(m_current_rule); - expr_ref e(m); - var_subst vs(m, false); - inv_var_shifter invsh(m); - vs(q->get_expr(), binding.size(), binding.c_ptr(), e); - invsh(e, q->get_num_decls(), e); - expr_ref_vector inst(m); - inst.append(var_inst.size(), (expr*const*)var_inst.c_ptr()); - inst.reverse(); - expr_abstract(m, 0, inst.size(), inst.c_ptr(), e, e); - if (m_instantiations.contains(to_app(e))) { - return; - } - m_instantiated_rules.push_back(m_current_rule); - m_instantiations.push_back(to_app(e)); - TRACE("pdr", tout << mk_pp(q, m) << "\n"; - tout << "binding: "; - for (unsigned i = 0; i < binding.size(); ++i) { - tout << mk_pp(binding[i].get(), m) << " "; - } - tout << "\n"; - tout << "inst: "; - for (unsigned i = 0; i < var_inst.size(); ++i) { - tout << mk_pp(var_inst[i].get(), m) << " "; - } - tout << "\n"; - tout << mk_pp(e, m) << "\n"; - ); - } - - - // As & not Body_i is satisfiable - // then instantiate with model for parameters to Body_i - - void quantifier_model_checker::find_instantiations(quantifier_ref_vector const& qs, unsigned level) { - find_instantiations_proof_based(qs, level); - } - - class collect_insts { - ast_manager& m; - ptr_vector m_binding; - vector m_bindings; - ptr_vector m_quantifiers; - public: - collect_insts(ast_manager& m): m(m) { } - - void operator()(expr* n) { - expr* not_q_or_i, *e1, *e2, *e3; - if (m.is_quant_inst(n, not_q_or_i, m_binding)) { - VERIFY(m.is_or(not_q_or_i, e1, e2)); - VERIFY(m.is_not(e1, e3)); - SASSERT(is_quantifier(e3)); - m_quantifiers.push_back(to_quantifier(e3)); - m_bindings.push_back(expr_ref_vector(m,m_binding.size(), m_binding.c_ptr())); - m_binding.reset(); - } - else if ((m.is_rewrite(n, e1, e2) || - (m.is_rewrite_star(n) && - (e3 = to_app(n)->get_arg(to_app(n)->get_num_args()-1), - e1 = to_app(e3)->get_arg(0), - e2 = to_app(e3)->get_arg(1), - true))) && - is_quantifier(e1) && m.is_false(e2)) { - quantifier* q = to_quantifier(e1); - m_quantifiers.push_back(q); - m_bindings.push_back(expr_ref_vector(m)); - expr_ref_vector& b = m_bindings.back(); - for (unsigned i = 0; i < q->get_num_decls(); ++i) { - b.push_back(m.mk_fresh_const("V", q->get_decl_sort(i))); - } - } - } - - void reset() { - m_quantifiers.reset(); - m_bindings.reset(); - } - - unsigned size() const { return m_quantifiers.size(); } - ptr_vector const& quantifiers() const { return m_quantifiers; } - vector const& bindings() const { return m_bindings; } - - }; - - void quantifier_model_checker::find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level) { - bool found_instance = false; - - datalog::scoped_proof _scp(m); - - expr_ref_vector fmls(m); - smt_params fparams; - SASSERT(m.proofs_enabled()); - fparams.m_mbqi = true; - - fmls.push_back(m_A.get()); - fmls.append(m_Bs); - TRACE("pdr", - tout << "assert\n"; - for (unsigned i = 0; i < fmls.size(); ++i) { - tout << mk_pp(fmls[i].get(), m) << "\n"; - }); - - smt::kernel solver(m, fparams); - for (unsigned i = 0; i < fmls.size(); ++i) { - solver.assert_expr(fmls[i].get()); - } - lbool result = solver.check(); - - TRACE("pdr", tout << result << "\n";); - - if (m_rules_model_check != l_false) { - m_rules_model_check = result; - } - - if (result != l_false) { - return; - } - - map qid_map; - quantifier* q; - for (unsigned i = 0; i < qs.size(); ++i) { - q = qs[i]; - qid_map.insert(q->get_qid(), q); - } - - proof* p = solver.get_proof(); - TRACE("pdr", tout << mk_ismt2_pp(p, m) << "\n";); - collect_insts collector(m); - for_each_expr(collector, p); - ptr_vector const& quants = collector.quantifiers(); - - for (unsigned i = 0; i < collector.size(); ++i) { - symbol qid = quants[i]->get_qid(); - if (!qid_map.find(qid, q)) { - TRACE("pdr", tout << "Could not find quantifier " << mk_pp(quants[i], m) << "\n";); - continue; - } - expr_ref_vector const& binding = collector.bindings()[i]; - - TRACE("pdr", tout << "Instantiating:\n" << mk_pp(quants[i], m) << "\n"; - for (unsigned j = 0; j < binding.size(); ++j) { - tout << mk_pp(binding[j], m) << " "; - } - tout << "\n";); - - expr_ref_vector new_binding(m); - for (unsigned j = 0; j < binding.size(); ++j) { - new_binding.push_back(binding[j]); - } - add_binding(q, new_binding); - found_instance = true; - } - if (found_instance) { - m_rules_model_check = l_false; - } - else if (m_rules_model_check != l_false) { - m_rules_model_check = l_undef; - } - } - - - /** - For under-approximations: - - m_reachable: set of reachable states, per predicate - - rules: P(x) :- B[x,y] & Fa z . Q(y,z) - Q(y,z) :- C[y,z,u] & Fa w . R(u,w) - - qis: Fa z . Q(y,z) - - M: model satisfying P(x) & B[x,y] - - B'[x,y]: body with reachable states substituted for predicates. - - Q'[y,z]: reachable states substituted for Q. - - S'[x]: Ex y . B'[x,y] & Fa z . Q'[y, z] - - Method: - - 1. M |= Fa z . Q'[y, z] => done - - Weaker variant: - Check B[x,y] & Fa z . Q'[y, z] for consistency. - - 2. Otherwise, extract instantiations. - - 3. Update reachable (for next round): - - Q'[y,z] := Q'[y,z] \/ C'[y,z,u] & Fa w . R'(u,w) - - */ - - - /** - For over-approximations: - - - pt - predicate transformer for rule: - P(x) :- Body1(x,y) || Body2(x,z) & (Fa u . Q(u,x,z)). - - rule - P(x) :- Body2(x,z) - - - qis - Fa u . Q(u,x,z) - - - A := node.state(x) && Body2(x,y) - - - - Bs := array of Bs of the form: - . Fa u . Q(u, P_x, P_y) - instantiate quantifier to P variables. - . B := inv(Q_0,Q_1,Q_2) - . B := inv(u, P_x, P_y) := B[u/Q_0, P_x/Q_1, P_y/Q_2] - . B := Fa u . inv(u, P_x, P_y) - - */ - - void quantifier_model_checker::update_reachable(func_decl* f, expr* e) { - expr* e_old; - m.inc_ref(e); - if (m_reachable.find(f, e_old)) { - m.dec_ref(e_old); - } - m_reachable.insert(f, e); - } - - - expr_ref quantifier_model_checker::get_reachable(func_decl* p) { - expr* e = 0; - if (!m_reachable.find(p, e)) { - e = m_ctx.get_pred_transformer(p).initial_state(); - update_reachable(p, e); - } - return expr_ref(e, m); - } - - void quantifier_model_checker::add_over_approximations(quantifier_ref_vector& qis, model_node& n) { - add_approximations(qis, n, true); - } - - void quantifier_model_checker::add_under_approximations(quantifier_ref_vector& qis, model_node& n) { - add_approximations(qis, n, false); - } - - void quantifier_model_checker::add_approximations(quantifier_ref_vector& qis, model_node& n, bool is_over) { - pred_transformer& pt = n.pt(); - manager& pm = pt.get_pdr_manager(); - unsigned level = n.level(); - expr_ref_vector Bs(m); - expr_ref B(m), v(m); - quantifier_ref q(m); - datalog::scoped_no_proof _no_proof(m); - scoped_ptr rep = mk_default_expr_replacer(m); - for (unsigned j = 0; j < qis.size(); ++j) { - q = qis[j].get(); - SASSERT(is_forall(q)); - app_ref_vector& inst = pt.get_inst(m_current_rule); - TRACE("pdr", - tout << "q:\n" << mk_pp(q, m) << "\n"; - tout << "level: " << level << "\n"; - model_smt2_pp(tout, m, n.get_model(), 0); - m_current_rule->display(m_ctx.get_context(), tout << "rule:\n"); - ); - - var_subst vs(m, false); - vs(q, inst.size(), (expr*const*)inst.c_ptr(), B); - q = to_quantifier(B); - TRACE("pdr", tout << "q instantiated:\n" << mk_pp(q, m) << "\n";); - - app* a = to_app(q->get_expr()); - func_decl* f = a->get_decl(); - pred_transformer& pt2 = m_ctx.get_pred_transformer(f); - if (is_over) { - B = pt2.get_formulas(level - 1, false); - } - else { - B = get_reachable(f); - SASSERT(is_well_sorted(m, B)); - } - TRACE("pdr", tout << "B:\n" << mk_pp(B, m) << "\n";); - - expr_safe_replace sub(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_const(pm.o2n(pt2.sig(i),0)); - sub.insert(v, a->get_arg(i)); - } - sub(B); - TRACE("pdr", tout << "B substituted:\n" << mk_pp(B, m) << "\n";); - datalog::flatten_and(B, Bs); - for (unsigned i = 0; i < Bs.size(); ++i) { - m_Bs.push_back(m.update_quantifier(q, Bs[i].get())); - } - } - } - - /** - \brief compute strongest post-conditions for each predicate transformer. - (or at least something sufficient to change the set of current counter-examples) - */ - void quantifier_model_checker::weaken_under_approximation() { - - datalog::rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules(), end = m_rules.end_grouped_rules(); - - for (; it != end; ++it) { - func_decl* p = it->m_key; - datalog::rule_vector& rules = *it->m_value; - expr_ref_vector bodies(m); - for (unsigned i = 0; i < rules.size(); ++i) { - bodies.push_back(strongest_post_condition(*rules[i])); - } - update_reachable(p, m.mk_or(bodies.size(), bodies.c_ptr())); - } - } - - expr_ref quantifier_model_checker::strongest_post_condition(datalog::rule& r) { - pred_transformer& pt = m_ctx.get_pred_transformer(r.get_decl()); - manager& pm = pt.get_pdr_manager(); - quantifier_ref_vector* qis = 0; - m_quantifiers.find(&r, qis); - expr_ref_vector body(m), inst(m); - expr_ref fml(m), v(m); - app* a; - func_decl* p; - svector names; - unsigned ut_size = r.get_uninterpreted_tail_size(); - unsigned t_size = r.get_tail_size(); - var_subst vs(m, false); - ptr_vector vars; - uint_set empty_index_set; - qe_lite qe(m); - - r.get_vars(vars); - - if (qis) { - quantifier_ref_vector const& qi = *qis; - for (unsigned i = 0; i < qi.size(); ++i) { - quantifier* q = qi[i]; - fml = q->get_expr(); - a = to_app(fml); - p = a->get_decl(); - expr* p_reach = get_reachable(p); - pred_transformer& pt2 = m_ctx.get_pred_transformer(p); - expr_safe_replace sub(m); - for (unsigned j = 0; j < a->get_num_args(); ++j) { - v = m.mk_const(pm.o2n(pt2.sig(j),0)); - sub.insert(v, a->get_arg(j)); - } - sub(p_reach, fml); - uint_set is; - for (unsigned j = 0; j < q->get_num_decls(); ++j) { - is.insert(j); - } - fml = m.mk_not(fml); - qe(is, true, fml); - fml = m.mk_not(fml); - body.push_back(m.update_quantifier(q, fml)); - } - } - - a = r.get_head(); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_var(vars.size()+i, m.get_sort(a->get_arg(i))); - body.push_back(m.mk_eq(v, a->get_arg(i))); - } - for (unsigned i = 0; i < ut_size; ++i) { - a = r.get_tail(i); - p = a->get_decl(); - pred_transformer& pt2 = m_ctx.get_pred_transformer(p); - expr* p_reach = get_reachable(p); - expr_safe_replace sub(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_const(pm.o2n(pt2.sig(i),0)); - sub.insert(v, a->get_arg(i)); - } - sub(p_reach, fml); - body.push_back(fml); - } - for (unsigned i = ut_size; i < t_size; ++i) { - body.push_back(r.get_tail(i)); - } - fml = m.mk_and(body.size(), body.c_ptr()); - vars.reverse(); - for (unsigned i = 0; i < vars.size(); ++i) { - names.push_back(symbol(i)); - } - if (!vars.empty()) { - fml = m.mk_exists(vars.size(), vars.c_ptr(), names.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - } - - for (unsigned i = 0; i < r.get_head()->get_num_args(); ++i) { - inst.push_back(m.mk_const(pm.o2n(pt.sig(i),0))); - } - vs(fml, inst.size(), inst.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - if (!vars.empty()) { - fml = to_quantifier(fml)->get_expr(); - qe(empty_index_set, false, fml); - fml = m.mk_exists(vars.size(), vars.c_ptr(), names.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - m_ctx.get_context().get_rewriter()(fml); - } - SASSERT(is_well_sorted(m, fml)); - - IF_VERBOSE(0, verbose_stream() << "instantiate to:\n" << mk_pp(fml, m) << "\n";); - return fml; - } - - - void quantifier_model_checker::model_check_node(model_node& node) { - TRACE("pdr", node.display(tout, 0);); - pred_transformer& pt = node.pt(); - manager& pm = pt.get_pdr_manager(); - expr_ref A(m), C(m); - expr_ref_vector As(m); - m_Bs.reset(); - // - // nodes from leaves that are repeated - // inside the search tree don't have models. - // - if (!node.get_model_ptr()) { - return; - } - m_current_rule = node.get_rule(); - m_current_pt = &pt; - m_current_node = &node; - if (!m_current_rule) { - return; - } - - quantifier_ref_vector* qis = 0; - m_quantifiers.find(m_current_rule, qis); - if (!qis) { - return; - } - unsigned level = node.level(); - if (level == 0) { - return; - } - - As.push_back(pt.get_propagation_formula(m_ctx.get_pred_transformers(), level)); - As.push_back(node.state()); - As.push_back(pt.rule2tag(m_current_rule)); - m_A = pm.mk_and(As); - - // Add quantifiers: - // add_over_approximations(*qis, node); - add_under_approximations(*qis, node); - - TRACE("pdr", - tout << "A:\n" << mk_pp(m_A, m) << "\n"; - tout << "quantifier:\n"; - for (unsigned i = 0; i < qis->size(); ++i) { - tout << mk_pp((*qis)[i].get(), m) << " "; - } - tout << "\n"; - tout << "B:\n"; - for (unsigned i = 0; i < m_Bs.size(); ++i) { - tout << mk_pp(m_Bs[i].get(), m) << "\n"; - } - ast_smt_pp pp(m); - pp.add_assumption(m_A); - for (unsigned i = 0; i < m_Bs.size(); ++i) { - pp.add_assumption(m_Bs[i].get()); - } - pp.display_smt2(tout, m.mk_true()); - ); - - find_instantiations(*qis, level); - } - - lbool quantifier_model_checker::model_check(model_node& root) { - m_instantiations.reset(); - m_instantiated_rules.reset(); - m_rules_model_check = l_true; - ptr_vector nodes; - get_nodes(root, nodes); - for (unsigned i = nodes.size(); i > 0; ) { - --i; - model_check_node(*nodes[i]); - } - if (m_rules_model_check == l_false) { - weaken_under_approximation(); - } - return m_rules_model_check; - } - - void quantifier_model_checker::refine() { - datalog::mk_extract_quantifiers eq(m_ctx.get_context()); - datalog::rule_manager& rm = m_rules.get_rule_manager(); - datalog::rule_set new_rules(m_rules.get_context()); - datalog::rule_set::iterator it = m_rules.begin(), end = m_rules.end(); - for (; it != end; ++it) { - datalog::rule* r = *it; - datalog::rule_counter vc(true); - unsigned max_var = vc.get_max_rule_var(*r); - app_ref_vector body(m); - for (unsigned i = 0; i < m_instantiations.size(); ++i) { - if (r == m_instantiated_rules[i]) { - eq.ensure_predicate(m_instantiations[i].get(), max_var, body); - } - } - if (body.empty()) { - new_rules.add_rule(r); - } - else { - for (unsigned i = 0; i < r->get_tail_size(); ++i) { - body.push_back(r->get_tail(i)); - } - quantifier_ref_vector* qs = 0; - m_quantifiers.find(r, qs); - m_quantifiers.remove(r); - datalog::rule_ref new_rule(rm); - new_rule = rm.mk(r->get_head(), body.size(), body.c_ptr(), 0, r->name(), false); - new_rules.add_rule(new_rule); - m_quantifiers.insert(new_rule, qs); - IF_VERBOSE(1, - verbose_stream() << "instantiating quantifiers\n"; - r->display(m_ctx.get_context(), verbose_stream()); - verbose_stream() << "replaced by\n"; - new_rule->display(m_ctx.get_context(), verbose_stream());); - } - } - new_rules.close(); - m_rules.reset(); - m_rules.add_rules(new_rules); - m_rules.close(); - m_ctx.update_rules(m_rules); - TRACE("pdr", m_rules.display(tout);); - } - - lbool quantifier_model_checker::check() { - lbool result = model_check(m_ctx.get_root()); - if (result == l_false) { - refine(); - } - return result; - } -}; - diff --git a/src/muz_qe/pdr_quantifiers.h b/src/muz_qe/pdr_quantifiers.h deleted file mode 100644 index 941fab3d9..000000000 --- a/src/muz_qe/pdr_quantifiers.h +++ /dev/null @@ -1,117 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - pdr_quantifiers.h - -Abstract: - - Module for handling quantifiers in rule bodies. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-19. - -Revision History: - ---*/ - -#ifndef _PDR_QUANTIFIERS_H_ -#define _PDR_QUANTIFIERS_H_ - -#include "ast.h" -#include "lbool.h" -#include "dl_rule.h" -#include "obj_pair_hashtable.h" - -namespace datalog { - class rule_set; -}; - -namespace pdr { - - class model_node; - class pred_transformer; - class context; - - class quantifier_model_checker { - context& m_ctx; - ast_manager& m; - obj_map& m_quantifiers; - datalog::rule_set& m_rules; - - obj_map m_reachable; // set of reachable states - expr_ref m_A; - expr_ref_vector m_Bs; - pred_transformer* m_current_pt; - datalog::rule const* m_current_rule; - model_node* m_current_node; - lbool m_rules_model_check; - app_ref_vector m_instantiations; - ptr_vector m_instantiated_rules; - - void model_check_node(model_node& node); - - void weaken_under_approximation(); - - void find_instantiations(quantifier_ref_vector const& qs, unsigned level); - - void find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level); - - void add_binding(quantifier* q, expr_ref_vector& binding); - - void apply_binding(quantifier* q, expr_ref_vector& binding); - - void generalize_binding(expr_ref_vector const& binding, vector& bindings); - - void generalize_binding(expr_ref_vector const& binding, unsigned idx, expr_ref_vector& new_binding, vector& bindings); - - void refine(); - - - /** - \brief model check a potential model against quantifiers in bodies of rules. - - \return true if the model rooted in 'root' is checks with the quantifiers, otherwise - 'false' and a set of instantiations that contradict the current model. - */ - - lbool model_check(model_node& root); - - void add_over_approximations(quantifier_ref_vector& qis, model_node& n); - - void add_under_approximations(quantifier_ref_vector& qis, model_node& n); - - void add_approximations(quantifier_ref_vector& qis, model_node& n, bool is_over); - - expr_ref get_reachable(func_decl* f); - - void update_reachable(func_decl* f, expr* e); - - expr_ref strongest_post_condition(datalog::rule& r); - - public: - quantifier_model_checker( - context& ctx, - ast_manager& m, - obj_map& quantifiers, - datalog::rule_set& rules) : - m_ctx(ctx), - m(m), - m_quantifiers(quantifiers), - m_rules(rules), - m_A(m), - m_Bs(m), - m_current_pt(0), - m_current_rule(0), - m_current_node(0), - m_instantiations(m) {} - - ~quantifier_model_checker(); - - lbool check(); - }; - -}; -#endif diff --git a/src/muz_qe/pdr_util.cpp b/src/muz_qe/pdr_util.cpp index 237cf9415..9711cffc2 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz_qe/pdr_util.cpp @@ -1081,6 +1081,7 @@ namespace pdr { 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)) { @@ -1115,6 +1116,16 @@ namespace pdr { } 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); } @@ -1140,6 +1151,9 @@ namespace pdr { 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)) @@ -1209,8 +1223,10 @@ namespace pdr { } public: - test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true) {} - + 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; @@ -1232,7 +1248,11 @@ namespace pdr { } if (!m_is_dl) { - IF_VERBOSE(1, verbose_stream() << "non-diff: " << mk_pp(e, m) << "\n";); + char const* msg = "non-diff: "; + if (m_test_for_utvpi) { + msg = "non-utvpi: "; + } + IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } @@ -1248,6 +1268,16 @@ namespace pdr { 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(); + } + } template class rewriter_tpl; diff --git a/src/muz_qe/pdr_util.h b/src/muz_qe/pdr_util.h index ddbf0d122..5f2d22b76 100644 --- a/src/muz_qe/pdr_util.h +++ b/src/muz_qe/pdr_util.h @@ -151,6 +151,8 @@ namespace pdr { bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); + } #endif diff --git a/src/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index 369c3aae6..36e721b5c 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz_qe/proof_utils.cpp @@ -1,6 +1,7 @@ #include "dl_util.h" #include "proof_utils.h" #include "ast_smt2_pp.h" +#include "var_subst.h" class reduce_hypotheses { typedef obj_hashtable expr_set; @@ -517,3 +518,93 @@ void proof_utils::permute_unit_resolution(proof_ref& pr) { obj_map cache; ::permute_unit_resolution(refs, cache, pr); } + +class push_instantiations_up_cl { + ast_manager& m; +public: + push_instantiations_up_cl(ast_manager& m): m(m) {} + + void operator()(proof_ref& p) { + expr_ref_vector s0(m); + p = push(p, s0); + } + +private: + + proof* push(proof* p, expr_ref_vector const& sub) { + proof_ref_vector premises(m); + expr_ref conclusion(m); + svector > positions; + vector substs; + + if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { + for (unsigned i = 0; i < premises.size(); ++i) { + compose(substs[i], sub); + premises[i] = push(premises[i].get(), substs[i]); + substs[i].reset(); + } + instantiate(sub, conclusion); + return + m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, + positions, + substs); + } + if (sub.empty()) { + return p; + } + if (m.is_modus_ponens(p)) { + SASSERT(m.get_num_parents(p) == 2); + proof* p0 = m.get_parent(p, 0); + proof* p1 = m.get_parent(p, 1); + if (m.get_fact(p0) == m.get_fact(p)) { + return push(p0, sub); + } + expr* e1, *e2; + if (m.is_rewrite(p1, e1, e2) && + is_quantifier(e1) && is_quantifier(e2) && + to_quantifier(e1)->get_num_decls() == to_quantifier(e2)->get_num_decls()) { + expr_ref r1(e1,m), r2(e2,m); + instantiate(sub, r1); + instantiate(sub, r2); + p1 = m.mk_rewrite(r1, r2); + return m.mk_modus_ponens(push(p0, sub), p1); + } + } + premises.push_back(p); + substs.push_back(sub); + conclusion = m.get_fact(p); + instantiate(sub, conclusion); + return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); + } + + void compose(expr_ref_vector& sub, expr_ref_vector const& s0) { + for (unsigned i = 0; i < sub.size(); ++i) { + expr_ref e(m); + var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr(), e); + sub[i] = e; + } + } + + void instantiate(expr_ref_vector const& sub, expr_ref& fml) { + if (sub.empty()) { + return; + } + if (!is_forall(fml)) { + return; + } + quantifier* q = to_quantifier(fml); + if (q->get_num_decls() != sub.size()) { + TRACE("proof_utils", tout << "quantifier has different number of variables than substitution"; + tout << mk_pp(q, m) << "\n"; + tout << sub.size() << "\n";); + return; + } + var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr(), fml); + } + +}; + +void proof_utils::push_instantiations_up(proof_ref& pr) { + push_instantiations_up_cl push(pr.get_manager()); + push(pr); +} diff --git a/src/muz_qe/proof_utils.h b/src/muz_qe/proof_utils.h index 53a38592e..dc3cdc3ef 100644 --- a/src/muz_qe/proof_utils.h +++ b/src/muz_qe/proof_utils.h @@ -36,6 +36,12 @@ public: */ static void permute_unit_resolution(proof_ref& pr); + /** + \brief Push instantiations created in hyper-resolutions up to leaves. + This produces a "ground" proof where leaves are annotated by instantiations. + */ + static void push_instantiations_up(proof_ref& pr); + }; #endif diff --git a/src/muz_qe/qe.cpp b/src/muz_qe/qe.cpp index bb65f8bf8..894a935b5 100644 --- a/src/muz_qe/qe.cpp +++ b/src/muz_qe/qe.cpp @@ -1093,8 +1093,7 @@ namespace qe { bool has_branch(rational const& branch_id) const { return m_branch_index.contains(branch_id); } search_tree* child(rational const& branch_id) const { - unsigned idx; - VERIFY(m_branch_index.find(branch_id, idx)); + unsigned idx = m_branch_index.find(branch_id); return m_children[idx]; } @@ -1963,7 +1962,6 @@ namespace qe { expr_ref m_assumption; bool m_produce_models; ptr_vector m_plugins; - unsigned m_name_counter; // fresh-id volatile bool m_cancel; bool m_eliminate_variables_as_block; @@ -1973,7 +1971,6 @@ namespace qe { m_fparams(p), m_assumption(m), m_produce_models(m_fparams.m_model), - m_name_counter(0), m_cancel(false), m_eliminate_variables_as_block(true) { diff --git a/src/muz_qe/qe_dl_plugin.cpp b/src/muz_qe/qe_dl_plugin.cpp index 15e972e88..61466795b 100644 --- a/src/muz_qe/qe_dl_plugin.cpp +++ b/src/muz_qe/qe_dl_plugin.cpp @@ -12,14 +12,12 @@ namespace qe { // dl_plugin class eq_atoms { - ast_manager& m; expr_ref_vector m_eqs; expr_ref_vector m_neqs; app_ref_vector m_eq_atoms; app_ref_vector m_neq_atoms; public: eq_atoms(ast_manager& m): - m(m), m_eqs(m), m_neqs(m), m_eq_atoms(m), diff --git a/src/muz_qe/qe_lite.cpp b/src/muz_qe/qe_lite.cpp index ff49584ff..f840f19d6 100644 --- a/src/muz_qe/qe_lite.cpp +++ b/src/muz_qe/qe_lite.cpp @@ -1417,6 +1417,7 @@ namespace fm { fm(ast_manager & _m): m(_m), + m_is_variable(0), m_allocator("fm-elim"), m_util(m), m_bvar2expr(m), @@ -1424,6 +1425,9 @@ namespace fm { m_new_fmls(m), m_inconsistent_core(m) { m_cancel = false; + updt_params(); + m_counter = 0; + m_inconsistent = false; } ~fm() { @@ -2525,15 +2529,15 @@ public: m_params(p) { m_imp = alloc(imp, m, p); } - - virtual tactic * translate(ast_manager & m) { - return alloc(qe_lite_tactic, m, m_params); - } virtual ~qe_lite_tactic() { dealloc(m_imp); } + virtual tactic * translate(ast_manager & m) { + return alloc(qe_lite_tactic, m, m_params); + } + virtual void updt_params(params_ref const & p) { m_params = p; // m_imp->updt_params(p); diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 58263b9d0..7aade28e2 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -18,6 +18,8 @@ Revision History: Extracted from dl_context --*/ + + #include"rel_context.h" #include"dl_context.h" #include"dl_compiler.h" @@ -32,9 +34,46 @@ Revision History: #include"dl_sparse_table.h" #include"dl_table.h" #include"dl_table_relation.h" +#include"aig_exporter.h" namespace datalog { - + + class rel_context::scoped_query { + context& m_ctx; + rule_set m_rules; + decl_set m_preds; + bool m_was_closed; + + public: + + scoped_query(context& ctx): + m_ctx(ctx), + m_rules(ctx.get_rules()), + m_preds(ctx.get_predicates()), + m_was_closed(ctx.is_closed()) + { + if (m_was_closed) { + ctx.reopen(); + } + } + + ~scoped_query() { + m_ctx.reopen(); + m_ctx.restrict_predicates(m_preds); + m_ctx.replace_rules(m_rules); + if (m_was_closed) { + m_ctx.close(); + } + } + + void reset() { + m_ctx.reopen(); + m_ctx.restrict_predicates(m_preds); + m_ctx.replace_rules(m_rules); + m_ctx.close(); + } + }; + rel_context::rel_context(context& ctx) : m_context(ctx), m(ctx.get_manager()), @@ -56,9 +95,7 @@ namespace datalog { get_rmanager().register_plugin(alloc(bound_relation_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(interval_relation_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(karr_relation_plugin, get_rmanager())); - - -} + } rel_context::~rel_context() { if (m_last_result_relation) { @@ -67,50 +104,40 @@ namespace datalog { } } - void rel_context::collect_predicates(decl_set & res) { - unsigned rule_cnt = m_context.get_rules().get_num_rules(); - for (unsigned rindex=0; rindexget_head()->get_decl()); - unsigned tail_len = r->get_uninterpreted_tail_size(); - for (unsigned tindex=0; tindexget_tail(tindex)->get_decl()); - } - } - decl_set::iterator oit = m_output_preds.begin(); - decl_set::iterator oend = m_output_preds.end(); - for (; oit!=oend; ++oit) { - res.insert(*oit); - } - get_rmanager().collect_predicates(res); - } - - lbool rel_context::saturate() { - m_context.ensure_closed(); - + m_context.ensure_closed(); bool time_limit = m_context.soft_timeout()!=0; unsigned remaining_time_limit = m_context.soft_timeout(); unsigned restart_time = m_context.initial_restart_timeout(); - rule_set original_rules(m_context.get_rules()); - decl_set original_predicates; - m_context.collect_predicates(original_predicates); + scoped_query scoped_query(m_context); - m_code.reset(); instruction_block termination_code; - m_ectx.reset(); lbool result; TRACE("dl", m_context.display(tout);); while (true) { + m_ectx.reset(); + m_code.reset(); + termination_code.reset(); + m_context.ensure_closed(); m_context.transform_rules(); if (m_context.canceled()) { result = l_undef; break; } + TRACE("dl", m_context.display(tout);); + + if (m_context.get_params().dump_aig().size()) { + const char *filename = static_cast(m_context.get_params().dump_aig().c_ptr()); + aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts); + std::ofstream strm(filename, std::ios_base::binary); + aig(strm); + exit(0); + } + compiler::compile(m_context, m_context.get_rules(), m_code, termination_code); TRACE("dl", m_code.display(*this, tout); ); @@ -163,47 +190,18 @@ namespace datalog { else { restart_time = static_cast(new_restart_time); } - - termination_code.reset(); - m_context.reopen(); - restrict_predicates(original_predicates); - m_context.replace_rules(original_rules); - m_context.close(); + scoped_query.reset(); } - m_context.reopen(); - restrict_predicates(original_predicates); m_context.record_transformed_rules(); - m_context.replace_rules(original_rules); - m_context.close(); - TRACE("dl", m_ectx.report_big_relations(100, tout);); - m_code.process_all_costs(); - m_code.make_annotations(m_ectx); + TRACE("dl", display_profile(tout);); return result; } - -#define BEGIN_QUERY() \ - rule_set original_rules(m_context.get_rules()); \ - decl_set original_preds; \ - m_context.collect_predicates(original_preds); \ - bool was_closed = m_context.is_closed(); \ - if (was_closed) { \ - m_context.reopen(); \ - } \ - -#define END_QUERY() \ - m_context.reopen(); \ - m_context.replace_rules(original_rules); \ - restrict_predicates(original_preds); \ - \ - if (was_closed) { \ - m_context.close(); \ - } \ lbool rel_context::query(unsigned num_rels, func_decl * const* rels) { get_rmanager().reset_saturated_marks(); - BEGIN_QUERY(); + scoped_query _scoped_query(m_context); for (unsigned i = 0; i < num_rels; ++i) { - set_output_predicate(rels[i]); + m_context.set_output_predicate(rels[i]); } m_context.close(); reset_negated_tables(); @@ -238,59 +236,37 @@ namespace datalog { case l_undef: break; } - END_QUERY(); return res; } lbool rel_context::query(expr* query) { get_rmanager().reset_saturated_marks(); - BEGIN_QUERY(); + scoped_query _scoped_query(m_context); rule_manager& rm = m_context.get_rule_manager(); - rule_ref qrule(rm); - rule_ref_vector qrules(rm); func_decl_ref query_pred(m); try { - rm.mk_query(query, query_pred, qrules, qrule); - } - catch(default_exception& exn) { - m_context.close(); - m_context.set_status(INPUT_ERROR); - throw exn; - } - try { - m_context.add_rules(qrules); + query_pred = rm.mk_query(query, m_context.get_rules()); } catch (default_exception& exn) { - m_context.close(); m_context.set_status(INPUT_ERROR); throw exn; } - set_output_predicate(qrule->get_head()->get_decl()); m_context.close(); reset_negated_tables(); if (m_context.generate_explanations()) { - rule_transformer transformer(m_context); - //expl_plugin is deallocated when transformer goes out of scope - mk_explanations * expl_plugin = - alloc(mk_explanations, m_context, m_context.explanations_on_relation_level()); - transformer.register_plugin(expl_plugin); - m_context.transform_rules(transformer); - - //we will retrieve the predicate with explanations instead of the original query predicate - query_pred = expl_plugin->get_e_decl(query_pred); - const rule_vector & query_rules = m_context.get_rules().get_predicate_rules(query_pred); - SASSERT(query_rules.size()==1); - qrule = query_rules.back(); + m_context.transform_rules(alloc(mk_explanations, m_context)); } + query_pred = m_context.get_rules().get_pred(query_pred); + if (m_context.magic_sets_for_queries()) { - rule_transformer transformer(m_context); - transformer.register_plugin(alloc(mk_magic_sets, m_context, qrule.get())); - m_context.transform_rules(transformer); + m_context.transform_rules(alloc(mk_magic_sets, m_context, query_pred)); } + query_pred = m_context.get_rules().get_pred(query_pred); + lbool res = saturate(); if (res != l_undef) { @@ -304,7 +280,6 @@ namespace datalog { } } - END_QUERY(); return res; } @@ -372,15 +347,8 @@ namespace datalog { } } - void rel_context::set_output_predicate(func_decl * pred) { - if (!m_output_preds.contains(pred)) { - m_output_preds.insert(pred); - } - } - - void rel_context::restrict_predicates( const decl_set & res ) { - set_intersection(m_output_preds, res); - get_rmanager().restrict_predicates(res); + void rel_context::restrict_predicates(func_decl_set const& predicates) { + get_rmanager().restrict_predicates(predicates); } relation_base & rel_context::get_relation(func_decl * pred) { return get_rmanager().get_relation(pred); } @@ -515,15 +483,18 @@ namespace datalog { } } - void rel_context::display_output_facts(std::ostream & out) const { - get_rmanager().display_output_tables(out); + void rel_context::display_output_facts(rule_set const& rules, std::ostream & out) const { + get_rmanager().display_output_tables(rules, out); } void rel_context::display_facts(std::ostream& out) const { get_rmanager().display(out); } - void rel_context::display_profile(std::ostream& out) const { + void rel_context::display_profile(std::ostream& out) { + m_code.make_annotations(m_ectx); + m_code.process_all_costs(); + out << "\n--------------\n"; out << "Instructions\n"; m_code.display(*this, out); diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h index d2f6973da..7b4ee551c 100644 --- a/src/muz_qe/rel_context.h +++ b/src/muz_qe/rel_context.h @@ -28,20 +28,20 @@ Revision History: namespace datalog { class context; + typedef vector > fact_vector; class rel_context { - typedef vector > fact_vector; - context& m_context; ast_manager& m; relation_manager m_rmanager; expr_ref m_answer; relation_base * m_last_result_relation; - decl_set m_output_preds; fact_vector m_table_facts; execution_context m_ectx; instruction_block m_code; + class scoped_query; + void reset_negated_tables(); relation_plugin & get_ordinary_relation_plugin(symbol relation_name); @@ -78,13 +78,7 @@ namespace datalog { The function deallocates unsused relations, it does not deal with rules. */ - void restrict_predicates(const decl_set & res); - - void collect_predicates(decl_set & res); - - void set_output_predicate(func_decl * pred); - bool is_output_predicate(func_decl * pred) { return m_output_preds.contains(pred); } - const decl_set & get_output_predicates() const { return m_output_preds; } + void restrict_predicates(func_decl_set const& predicates); /** @@ -107,10 +101,10 @@ namespace datalog { */ void store_relation(func_decl * pred, relation_base * rel); - void display_output_facts(std::ostream & out) const; + void display_output_facts(rule_set const& rules, std::ostream & out) const; void display_facts(std::ostream & out) const; - void display_profile(std::ostream& out) const; + void display_profile(std::ostream& out); lbool saturate(); diff --git a/src/muz_qe/tab_context.cpp b/src/muz_qe/tab_context.cpp index 9ee059f44..653f5f188 100644 --- a/src/muz_qe/tab_context.cpp +++ b/src/muz_qe/tab_context.cpp @@ -191,6 +191,7 @@ namespace tb { unsigned get_index() const { return m_index; } void set_index(unsigned index) { m_index = index; } app* get_head() const { return m_head; } + func_decl* get_decl() const { return m_head->get_decl(); } void set_head(app* h) { m_head = h; } unsigned get_parent_index() const { return m_parent_index; } unsigned get_parent_rule() const { return m_parent_rule; } @@ -447,7 +448,7 @@ namespace tb { void insert(ref& g) { unsigned idx = m_rules.size(); m_rules.push_back(g); - func_decl* f = g->get_head()->get_decl(); + func_decl* f = g->get_decl(); map::obj_map_entry* e = m_index.insert_if_not_there2(f, unsigned_vector()); SASSERT(e); e->get_data().m_value.push_back(idx); @@ -613,7 +614,7 @@ namespace tb { bool match_head(clause const& g) { return - m_head->get_decl() == g.get_head()->get_decl() && + m_head->get_decl() == g.get_decl() && m_matcher(m_head, g.get_head(), m_subst, m_sideconds) && match_predicates(0, g); } @@ -739,7 +740,6 @@ namespace tb { typedef svector double_vector; typedef obj_map score_map; typedef obj_map pred_map; - datalog::context& m_ctx; ast_manager& m; datatype_util dt; score_map m_score_map; @@ -749,19 +749,16 @@ namespace tb { pred_map m_pred_map; expr_ref_vector m_refs; double m_weight_multiply; - unsigned m_num_invocations; unsigned m_update_frequency; unsigned m_next_update; public: selection(datalog::context& ctx): - m_ctx(ctx), m(ctx.get_manager()), dt(m), m_refs(m), m_weight_multiply(1.0), - m_num_invocations(0), m_update_frequency(20), m_next_update(20) { set_strategy(ctx.get_params().tab_selection()); @@ -1080,7 +1077,7 @@ namespace tb { bool unify(clause const& tgt, unsigned idx, clause const& src, bool compute_subst, ref& result) { qe_lite qe(m); reset(); - SASSERT(tgt.get_predicate(idx)->get_decl() == src.get_head()->get_decl()); + SASSERT(tgt.get_predicate(idx)->get_decl() == src.get_decl()); unsigned var_cnt = std::max(tgt.get_num_vars(), src.get_num_vars()); m_S1.reserve(2, var_cnt); if (!m_unifier(tgt.get_predicate(idx), src.get_head(), m_S1)) { @@ -1380,11 +1377,10 @@ namespace datalog { m_displayed_rules.reset(); m_rules.init(m_ctx.get_rules()); m_selection.init(m_rules); - rule_ref_vector query_rules(rm); + rule_set query_rules(m_ctx); rule_ref clause(rm); - func_decl_ref query_pred(m); - rm.mk_query(query, query_pred, query_rules, clause); - + rm.mk_query(query, query_rules); + clause = query_rules.last(); ref g = alloc(tb::clause, m); g->init(clause); g->set_head(m.mk_false()); diff --git a/src/parsers/smt/smtparser.cpp b/src/parsers/smt/smtparser.cpp index b6b40c01a..d26190541 100644 --- a/src/parsers/smt/smtparser.cpp +++ b/src/parsers/smt/smtparser.cpp @@ -1472,7 +1472,7 @@ private: SASSERT(sorts.size() > 0); - idbuilder* pop_q = new (region) pop_quantifier(this, (head_symbol == m_forall), weight, qid, skid, patterns, no_patterns, sorts, vars, local_scope, current); + idbuilder* pop_q = new (region) pop_quantifier(this, (head_symbol == m_forall), weight, qid, skid, patterns, no_patterns, sorts, vars, local_scope); expr_ref_vector * empty_v = alloc(expr_ref_vector, m_manager); up.push_back(new (region) parse_frame(current, pop_q, empty_v, 0, m_binding_level)); @@ -2522,7 +2522,7 @@ private: class pop_quantifier : public idbuilder { public: pop_quantifier(smtparser * smt, bool is_forall, int weight, symbol const& qid, symbol const& skid, expr_ref_buffer & patterns, expr_ref_buffer & no_patterns, sort_ref_buffer & sorts, - svector& vars, symbol_table & local_scope, proto_expr* p_expr): + svector& vars, symbol_table & local_scope): m_smt(smt), m_is_forall(is_forall), m_weight(weight), @@ -2531,8 +2531,7 @@ private: m_patterns(m_smt->m_manager), m_no_patterns(m_smt->m_manager), m_sorts(m_smt->m_manager), - m_local_scope(local_scope), - m_p_expr(p_expr) { + m_local_scope(local_scope) { SASSERT(sorts.size() == vars.size()); m_vars.append(vars); @@ -2619,7 +2618,6 @@ private: sort_ref_buffer m_sorts; svector m_vars; symbol_table& m_local_scope; - proto_expr* m_p_expr; }; class builtin_builder : public idbuilder { diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 0698f4eaf..b5af6353e 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -1159,7 +1159,7 @@ namespace smt2 { m_num_expr_frames++; unsigned num_vars = parse_sorted_vars(); if (num_vars == 0) - throw parser_exception("invalied quantifier, list of sorted variables is empty"); + throw parser_exception("invalid quantifier, list of sorted variables is empty"); } symbol parse_indexed_identifier_core() { diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index 0f6101a93..f653cbb25 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -243,7 +243,6 @@ namespace smt2 { } scanner::scanner(cmd_context & ctx, std::istream& stream, bool interactive): - m_ctx(ctx), m_interactive(interactive), m_spos(0), m_curr(0), // avoid Valgrind warning diff --git a/src/parsers/smt2/smt2scanner.h b/src/parsers/smt2/smt2scanner.h index 7b74c752f..631c71f17 100644 --- a/src/parsers/smt2/smt2scanner.h +++ b/src/parsers/smt2/smt2scanner.h @@ -31,7 +31,6 @@ namespace smt2 { class scanner { private: - cmd_context & m_ctx; bool m_interactive; int m_spos; // position in the current line of the stream char m_curr; // current char; diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index fe2699504..efcab14ea 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -43,17 +43,7 @@ static datalog::context * g_ctx = 0; static datalog::rule_set * g_orig_rules; static datalog::instruction_block * g_code; static datalog::execution_context * g_ectx; -static smt_params * g_params; -datalog_params::datalog_params(): - m_default_table("sparse"), - m_default_table_checked(false) -{} - -// void datalog_params::register_params(ini_params& p) { -// p.register_symbol_param("DEFAULT_TABLE", m_default_table, "Datalog engine: default table (sparse)"); -// p.register_bool_param("DEFAULT_TABLE_CHECKED", m_default_table_checked, "Wrap default table with a sanity checker"); -// } static void display_statistics( std::ostream& out, @@ -61,7 +51,6 @@ static void display_statistics( datalog::rule_set& orig_rules, datalog::instruction_block& code, datalog::execution_context& ex_ctx, - smt_params& params, bool verbose ) { @@ -109,7 +98,7 @@ static void display_statistics( static void display_statistics() { if (g_ctx) { - display_statistics(std::cout, *g_ctx, *g_orig_rules, *g_code, *g_ectx, *g_params, true); + display_statistics(std::cout, *g_ctx, *g_orig_rules, *g_code, *g_ectx, true); } } @@ -127,7 +116,6 @@ static void on_ctrl_c(int) { unsigned read_datalog(char const * file) { IF_VERBOSE(1, verbose_stream() << "Z3 Datalog Engine\n";); - datalog_params dl_params; smt_params s_params; ast_manager m; g_overall_time.start(); @@ -135,8 +123,6 @@ unsigned read_datalog(char const * file) { signal(SIGINT, on_ctrl_c); params_ref params; params.set_sym("engine", symbol("datalog")); - params.set_sym("default_table", dl_params.m_default_table); - params.set_bool("default_table_checked", dl_params.m_default_table_checked); datalog::context ctx(m, s_params, params); datalog::relation_manager & rmgr = ctx.get_rel_context().get_rmanager(); @@ -175,8 +161,6 @@ unsigned read_datalog(char const * file) { TRACE("dl_compiler", ctx.display(tout);); datalog::rule_set original_rules(ctx.get_rules()); - datalog::decl_set original_predicates; - ctx.collect_predicates(original_predicates); datalog::instruction_block rules_code; datalog::instruction_block termination_code; @@ -188,7 +172,6 @@ unsigned read_datalog(char const * file) { g_orig_rules = &original_rules; g_code = &rules_code; g_ectx = &ex_ctx; - g_params = &s_params; try { g_piece_timer.reset(); @@ -237,7 +220,6 @@ unsigned read_datalog(char const * file) { termination_code.reset(); ex_ctx.reset(); ctx.reopen(); - ctx.restrict_predicates(original_predicates); ctx.replace_rules(original_rules); ctx.close(); } @@ -248,7 +230,7 @@ unsigned read_datalog(char const * file) { rules_code.display(ctx.get_rel_context(), tout);); if (ctx.get_params().output_tuples()) { - ctx.get_rel_context().display_output_facts(std::cout); + ctx.get_rel_context().display_output_facts(ctx.get_rules(), std::cout); } display_statistics( @@ -257,7 +239,6 @@ unsigned read_datalog(char const * file) { original_rules, rules_code, ex_ctx, - s_params, false); } @@ -269,7 +250,6 @@ unsigned read_datalog(char const * file) { original_rules, rules_code, ex_ctx, - s_params, true); return ERR_MEMOUT; } diff --git a/src/shell/datalog_frontend.h b/src/shell/datalog_frontend.h index e53e35c89..8022d5046 100644 --- a/src/shell/datalog_frontend.h +++ b/src/shell/datalog_frontend.h @@ -19,11 +19,6 @@ Revision History: #ifndef _DATALOG_FRONTEND_H_ #define _DATALOG_FRONTEND_H_ -struct datalog_params { - symbol m_default_table; - bool m_default_table_checked; - datalog_params(); -}; unsigned read_datalog(char const * file); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 63e604719..e0fa4a1f2 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -27,8 +27,8 @@ Revision History: #include"z3_log_frontend.h" #include"warning.h" #include"version.h" -#include"datalog_frontend.h" #include"dimacs_frontend.h" +#include"datalog_frontend.h" #include"timeout.h" #include"z3_exception.h" #include"error_codes.h" diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index ef0b4ad6b..b5d8635da 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -54,13 +54,19 @@ static void display_statistics() { } static void on_timeout() { - display_statistics(); - exit(0); + #pragma omp critical (g_display_stats) + { + display_statistics(); + exit(0); + } } static void on_ctrl_c(int) { signal (SIGINT, SIG_DFL); - display_statistics(); + #pragma omp critical (g_display_stats) + { + display_statistics(); + } raise(SIGINT); } @@ -83,9 +89,12 @@ unsigned read_smtlib_file(char const * benchmark_file) { } } - display_statistics(); - register_on_timeout_proc(0); - g_solver = 0; + #pragma omp critical (g_display_stats) + { + display_statistics(); + register_on_timeout_proc(0); + g_solver = 0; + } return solver.get_error_code(); } @@ -103,7 +112,6 @@ unsigned read_smtlib2_commands(char const * file_name) { install_subpaving_cmds(ctx); g_cmd_context = &ctx; - register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); bool result = true; @@ -119,8 +127,12 @@ unsigned read_smtlib2_commands(char const * file_name) { result = parse_smt2_commands(ctx, std::cin, true); } - display_statistics(); - g_cmd_context = 0; + + #pragma omp critical (g_display_stats) + { + display_statistics(); + g_cmd_context = 0; + } return result ? 0 : 1; } diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index c5d3c08cf..4775e44a4 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -408,7 +408,7 @@ void asserted_formulas::apply_quasi_macros() { TRACE("before_quasi_macros", display(tout);); expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); - quasi_macros proc(m_manager, m_macro_manager, *m_bsimp, m_simplifier); + quasi_macros proc(m_manager, m_macro_manager, m_simplifier); while (proc(m_asserted_formulas.size() - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead, m_asserted_formula_prs.c_ptr() + m_asserted_qhead, diff --git a/src/smt/database.h b/src/smt/database.h index 2e71cbae4..1975fe3c3 100644 --- a/src/smt/database.h +++ b/src/smt/database.h @@ -1,4 +1,4 @@ -char const * g_pattern_database = +static char const * g_pattern_database = "(benchmark patterns \n" " :status unknown \n" " :logic ALL \n" diff --git a/src/smt/database.smt b/src/smt/database.smt index 186dd9b95..2f5e5e1c9 100644 --- a/src/smt/database.smt +++ b/src/smt/database.smt @@ -311,4 +311,4 @@ (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pats { (?select (?select (?asElems e) a) i) }) - ) \ No newline at end of file + ) diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index 49d7313d1..6fd156e41 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -21,8 +21,10 @@ Revision History: #include"vector.h" #include"heap.h" +#include"statistics.h" #include"trace.h" #include"warning.h" +#include"uint_set.h" typedef int dl_var; @@ -118,7 +120,7 @@ const edge_id null_edge_id = -1; template class dl_graph { - struct statistics { + struct stats { unsigned m_propagation_cost; unsigned m_implied_literal_cost; unsigned m_num_implied_literals; @@ -131,16 +133,16 @@ class dl_graph { m_num_helpful_implied_literals = 0; m_num_relax = 0; } - statistics() { reset(); } - void display(std::ostream& out) const { - out << "num. prop. steps. " << m_propagation_cost << "\n"; - out << "num. impl. steps. " << m_implied_literal_cost << "\n"; - out << "num. impl. lits. " << m_num_implied_literals << "\n"; - out << "num. impl. conf lits. " << m_num_helpful_implied_literals << "\n"; - out << "num. bound relax. " << m_num_relax << "\n"; + stats() { reset(); } + void collect_statistics(::statistics& st) const { + st.update("dl prop steps", m_propagation_cost); + st.update("dl impl steps", m_implied_literal_cost); + st.update("dl impl lits", m_num_implied_literals); + st.update("dl impl conf lits", m_num_helpful_implied_literals); + st.update("dl bound relax", m_num_relax); } }; - statistics m_stats; + stats m_stats; typedef typename Ext::numeral numeral; typedef typename Ext::explanation explanation; typedef vector assignment; @@ -264,7 +266,6 @@ class dl_graph { m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight(); } - public: // An assignment is feasible if all edges are feasible. bool is_feasible() const { @@ -306,14 +307,6 @@ private: return true; } - // Update the assignment of variable v, that is, - // m_assignment[v] += inc - // This method also stores the old value of v in the assignment stack. - void acc_assignment(dl_var v, const numeral & inc) { - TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";); - m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); - m_assignment[v] += inc; - } // Restore the assignment using the information in m_assignment_stack. // This method is called when make_feasible fails. @@ -472,8 +465,9 @@ public: m_bw(m_mark) { } - void display_statistics(std::ostream& out) const { - m_stats.display(out); + + void collect_statistics(::statistics& st) const { + m_stats.collect_statistics(st); } // Create/Initialize a variable with the given id. @@ -655,10 +649,8 @@ public: throw default_exception("edges are not inconsistent"); } -#if 1 - // experimental feature: + // allow theory to introduce shortcut lemmas. prune_edges(edges, f); -#endif for (unsigned i = 0; i < edges.size(); ++i) { edge const& e = m_edges[edges[i]]; @@ -752,7 +744,6 @@ public: f.new_edge(src, dst, idx2-idx1+1, edges.begin()+idx1); } - // Create a new scope. // That is, save the number of edges in the graph. void push() { @@ -829,6 +820,16 @@ public: } } + // Update the assignment of variable v, that is, + // m_assignment[v] += inc + // This method also stores the old value of v in the assignment stack. + void acc_assignment(dl_var v, const numeral & inc) { + TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";); + m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); + m_assignment[v] += inc; + } + + struct every_var_proc { bool operator()(dl_var v) const { return true; @@ -839,6 +840,36 @@ public: display_core(out, every_var_proc()); } + void display_agl(std::ostream & out) const { + uint_set vars; + typename edges::const_iterator it = m_edges.begin(); + typename edges::const_iterator end = m_edges.end(); + for (; it != end; ++it) { + edge const& e = *it; + if (e.is_enabled()) { + vars.insert(e.get_source()); + vars.insert(e.get_target()); + } + } + out << "digraph "" {\n"; + + unsigned n = m_assignment.size(); + for (unsigned v = 0; v < n; v++) { + if (vars.contains(v)) { + out << "\"" << v << "\" [label=\"" << v << ":" << m_assignment[v] << "\"]\n"; + } + } + it = m_edges.begin(); + for (; it != end; ++it) { + edge const& e = *it; + if (e.is_enabled()) { + out << "\"" << e.get_source() << "\"->\"" << e.get_target() << "\"[label=\"" << e.get_weight() << "\"]\n"; + } + } + + out << "}\n"; + } + template void display_core(std::ostream & out, FilterAssignmentProc p) const { display_edges(out); @@ -1002,6 +1033,38 @@ public: } } + void compute_zero_succ(dl_var v, int_vector& succ) { + unsigned n = m_assignment.size(); + m_dfs_time.reset(); + m_dfs_time.resize(n, -1); + m_dfs_time[v] = 0; + succ.push_back(v); + numeral gamma; + for (unsigned i = 0; i < succ.size(); ++i) { + v = succ[i]; + edge_id_vector & edges = m_out_edges[v]; + typename edge_id_vector::iterator it = edges.begin(); + typename edge_id_vector::iterator end = edges.end(); + for (; it != end; ++it) { + edge_id e_id = *it; + edge & e = m_edges[e_id]; + if (!e.is_enabled()) { + continue; + } + SASSERT(e.get_source() == v); + set_gamma(e, gamma); + if (gamma.is_zero()) { + dl_var target = e.get_target(); + if (m_dfs_time[target] == -1) { + succ.push_back(target); + m_dfs_time[target] = 0; + } + } + } + + } + } + numeral get_assignment(dl_var v) const { return m_assignment[v]; } @@ -1643,7 +1706,3 @@ public: #endif /* _DIFF_LOGIC_H_ */ -#if 0 - - -#endif diff --git a/src/smt/expr_context_simplifier.cpp b/src/smt/expr_context_simplifier.cpp index b23bb3bdc..66252c3cf 100644 --- a/src/smt/expr_context_simplifier.cpp +++ b/src/smt/expr_context_simplifier.cpp @@ -311,7 +311,7 @@ bool expr_context_simplifier::is_false(expr* e) const { // expr_strong_context_simplifier::expr_strong_context_simplifier(smt_params& p, ast_manager& m): - m_manager(m), m_params(p), m_arith(m), m_id(0), m_fn(0,m), m_solver(m, p) { + m_manager(m), m_arith(m), m_fn(0,m), m_solver(m, p) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } diff --git a/src/smt/expr_context_simplifier.h b/src/smt/expr_context_simplifier.h index 982b65878..a1d01b78c 100644 --- a/src/smt/expr_context_simplifier.h +++ b/src/smt/expr_context_simplifier.h @@ -57,9 +57,7 @@ private: class expr_strong_context_simplifier { ast_manager& m_manager; - smt_params & m_params; arith_util m_arith; - unsigned m_id; func_decl_ref m_fn; smt::kernel m_solver; diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index c9d6ead88..d92eef5b8 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -1849,11 +1849,9 @@ namespace smt { unsigned m_curr_max_generation; // temporary var used to store a copy of m_max_generation unsigned m_num_args; unsigned m_oreg; - unsigned m_ireg; enode * m_n1; enode * m_n2; enode * m_app; - instruction * m_alt; const bind * m_b; ptr_vector m_used_enodes; unsigned m_curr_used_enodes_size; diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 52fef8ca4..30bc65b6d 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -26,7 +26,9 @@ enum arith_solver_id { AS_NO_ARITH, AS_DIFF_LOGIC, AS_ARITH, - AS_DENSE_DIFF_LOGIC + AS_DENSE_DIFF_LOGIC, + AS_UTVPI, + AS_HORN }; enum bound_prop_mode { diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 1f020cdd3..f91a58f87 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -22,6 +22,8 @@ Revision History: #include"theory_arith.h" #include"theory_dense_diff_logic.h" #include"theory_diff_logic.h" +#include"theory_horn_ineq.h" +#include"theory_utvpi.h" #include"theory_array.h" #include"theory_array_full.h" #include"theory_bv.h" @@ -723,6 +725,18 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } break; + case AS_HORN: + if (m_params.m_arith_int_only) + m_context.register_plugin(alloc(smt::theory_ihi, m_manager)); + else + m_context.register_plugin(alloc(smt::theory_rhi, m_manager)); + break; + case AS_UTVPI: + if (m_params.m_arith_int_only) + m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager)); + else + m_context.register_plugin(alloc(smt::theory_rutvpi, m_manager)); + break; default: if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index fafe73a79..195d78e25 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -408,7 +408,7 @@ namespace smt { mk_axiom(eqz, upper); rational k; if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && - k.is_pos() && k < rational(512)) { + k.is_pos() && k < rational(8)) { rational j(0); #if 1 literal_buffer lits; diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 4140f683c..7302ccfd4 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -46,7 +46,6 @@ namespace smt { unsigned m_num_conflicts; unsigned m_num_assertions; unsigned m_num_th2core_eqs; - unsigned m_num_th2core_prop; unsigned m_num_core2th_eqs; unsigned m_num_core2th_diseqs; @@ -59,109 +58,30 @@ namespace smt { } }; - class dl_conflict : public simple_justification { - public: - dl_conflict(region & r, unsigned nls, literal const * lits): simple_justification(r, nls, lits) { } - - virtual proof * mk_proof(conflict_resolution & cr) { - NOT_IMPLEMENTED_YET(); - return 0; - } - }; - - template class theory_diff_logic : public theory, private Ext { typedef typename Ext::numeral numeral; - class implied_eq_justification : public justification { - theory_diff_logic & m_theory; - theory_var m_v1; - theory_var m_v2; - unsigned m_timestamp; - public: - implied_eq_justification(theory_diff_logic & theory, theory_var v1, theory_var v2, unsigned ts): - m_theory(theory), - m_v1(v1), - m_v2(v2), - m_timestamp(ts) { - } - - virtual void get_antecedents(conflict_resolution & cr) { - m_theory.get_eq_antecedents(m_v1, m_v2, m_timestamp, cr); - } - - virtual proof * mk_proof(conflict_resolution & cr) { NOT_IMPLEMENTED_YET(); return 0; } - }; - - class implied_bound_justification : public justification { - theory_diff_logic& m_theory; - edge_id m_subsumed_edge; - edge_id m_bridge_edge; - public: - implied_bound_justification(theory_diff_logic & theory, edge_id se, edge_id be): - m_theory(theory), - m_subsumed_edge(se), - m_bridge_edge(be) { - } - - virtual void get_antecedents(conflict_resolution & cr) { - m_theory.get_implied_bound_antecedents(m_bridge_edge, m_subsumed_edge, cr); - } - - virtual proof * mk_proof(conflict_resolution & cr) { NOT_IMPLEMENTED_YET(); return 0; } - }; - - enum atom_kind { - LE_ATOM, - EQ_ATOM - }; - class atom { - protected: - atom_kind m_kind; bool_var m_bvar; bool m_true; + int m_pos; + int m_neg; public: - atom(atom_kind k, bool_var bv) : m_kind(k), m_bvar(bv), m_true(false) {} - virtual ~atom() {} - atom_kind kind() const { return m_kind; } - bool_var get_bool_var() const { return m_bvar; } - bool is_true() const { return m_true; } - void assign_eh(bool is_true) { m_true = is_true; } - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - }; - - class le_atom : public atom { - int m_pos; - int m_neg; - public: - le_atom(bool_var bv, int pos, int neg): - atom(LE_ATOM, bv), + atom(bool_var bv, int pos, int neg): + m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) { } - virtual ~le_atom() {} + ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + bool is_true() const { return m_true; } + void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } int get_pos() const { return m_pos; } int get_neg() const { return m_neg; } - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - }; - - class eq_atom : public atom { - app_ref m_le; - app_ref m_ge; - public: - eq_atom(bool_var bv, app_ref& le, app_ref& ge): - atom(EQ_ATOM, bv), - m_le(le), - m_ge(ge) - {} - virtual ~eq_atom() {} - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - app* get_le() const { return m_le.get(); } - app* get_ge() const { return m_ge.get(); } + std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; }; typedef ptr_vector atoms; @@ -239,19 +159,7 @@ namespace smt { unsigned m_asserted_qhead_old; }; - class theory_diff_logic_del_eh : public clause_del_eh { - theory_diff_logic& m_super; - public: - theory_diff_logic_del_eh(theory_diff_logic& s) : m_super(s) {} - virtual ~theory_diff_logic_del_eh() {} - virtual void operator()(ast_manager&, clause* cls) { - TRACE("dl_activity", tout << "deleting " << cls << "\n";); - m_super.del_clause_eh(cls); - dealloc(this); - } - }; - - smt_params & m_params; + smt_params & m_params; arith_util m_util; arith_eq_adapter m_arith_eq_adapter; theory_diff_logic_statistics m_stats; @@ -259,8 +167,6 @@ namespace smt { theory_var m_zero_int; // cache the variable representing the zero variable. theory_var m_zero_real; // cache the variable representing the zero variable. int_vector m_scc_id; // Cheap equality propagation - bool m_modified_since_eq_prop; // true if new constraints were asserted - // since last eq propagation. eq_prop_info_set m_eq_prop_info_set; // set of existing equality prop infos ptr_vector m_eq_prop_infos; @@ -289,20 +195,14 @@ namespace smt { virtual theory_var mk_var(enode* n); virtual theory_var mk_var(app* n); - - void mark_as_modified_since_eq_prop(); - - void unmark_as_modified_since_eq_prop(); - - bool propagate_cheap_equalities(); - + void compute_delta(); void found_non_diff_logic_expr(expr * n); - bool is_interpreted(app* n) const; - - void del_clause_eh(clause* cls); + bool is_interpreted(app* n) const { + return get_family_id() == n->get_family_id(); + } public: theory_diff_logic(ast_manager& m, smt_params & params): @@ -312,7 +212,6 @@ namespace smt { m_arith_eq_adapter(*this, params, m_util), m_zero_int(null_theory_var), m_zero_real(null_theory_var), - m_modified_since_eq_prop(false), m_asserted_qhead(0), m_num_core_conflicts(0), m_num_propagation_calls(0), @@ -323,7 +222,7 @@ namespace smt { m_nc_functor(*this) { } - ~theory_diff_logic() { + virtual ~theory_diff_logic() { reset_eh(); } @@ -360,7 +259,7 @@ namespace smt { m_arith_eq_adapter.restart_eh(); } - virtual void relevant_eh(app* e); + virtual void relevant_eh(app* e) {} virtual void init_search_eh() { m_arith_eq_adapter.init_search_eh(); diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 9f2c97a84..390803957 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -31,34 +31,15 @@ Revision History: using namespace smt; + template -std::ostream& theory_diff_logic::atom::display(theory_diff_logic const& th, std::ostream& out) const { +std::ostream& theory_diff_logic::atom::display(theory_diff_logic const& th, std::ostream& out) const { context& ctx = th.get_context(); lbool asgn = ctx.get_assignment(m_bvar); //SASSERT(asgn == l_undef || ((asgn == l_true) == m_true)); bool sign = (l_undef == asgn) || m_true; return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; -} - -template -std::ostream& theory_diff_logic::eq_atom::display(theory_diff_logic const& th, std::ostream& out) const { - atom::display(th, out); - lbool asgn = th.get_context().get_assignment(this->m_bvar); - if (l_undef == asgn) { - out << "unassigned\n"; - } - else { - out << mk_pp(m_le.get(), m_le.get_manager()) << " " - << mk_pp(m_ge.get(), m_ge.get_manager()) << "\n"; - } - return out; -} - -template -std::ostream& theory_diff_logic::le_atom::display(theory_diff_logic const& th, std::ostream& out) const { - atom::display(th, out); - lbool asgn = th.get_context().get_assignment(this->m_bvar); if (l_undef == asgn) { out << "unassigned\n"; } @@ -94,7 +75,6 @@ void theory_diff_logic::init(context * ctx) { e = ctx->mk_enode(zero, false, false, true); SASSERT(!is_attached_to_var(e)); m_zero_real = mk_var(e); - } @@ -277,7 +257,7 @@ bool theory_diff_logic::internalize_atom(app * n, bool gate_ctx) { k -= this->m_epsilon; } edge_id neg = m_graph.add_edge(target, source, k, ~l); - le_atom * a = alloc(le_atom, bv, pos, neg); + atom * a = alloc(atom, bv, pos, neg); m_atoms.push_back(a); m_bool_var2atom.insert(bv, a); @@ -318,7 +298,7 @@ template void theory_diff_logic::assign_eh(bool_var v, bool is_true) { m_stats.m_num_assertions++; atom * a = 0; - m_bool_var2atom.find(v, a); + VERIFY (m_bool_var2atom.find(v, a)); SASSERT(a); SASSERT(get_context().get_assignment(v) != l_undef); SASSERT((get_context().get_assignment(v) == l_true) == is_true); @@ -330,10 +310,11 @@ void theory_diff_logic::assign_eh(bool_var v, bool is_true) { template void theory_diff_logic::collect_statistics(::statistics & st) const { st.update("dl conflicts", m_stats.m_num_conflicts); - st.update("dl propagations", m_stats.m_num_th2core_prop); st.update("dl asserts", m_stats.m_num_assertions); st.update("core->dl eqs", m_stats.m_num_core2th_eqs); + st.update("core->dl diseqs", m_stats.m_num_core2th_diseqs); m_arith_eq_adapter.collect_statistics(st); + m_graph.collect_statistics(st); } template @@ -374,15 +355,6 @@ final_check_status theory_diff_logic::final_check_eh() { // either will already be zero (as we don't do mixed constraints). m_graph.set_to_zero(m_zero_int, m_zero_real); SASSERT(is_consistent()); - - -#if 0 - TBD: - if (propagate_cheap_equalities()) { - return FC_CONTINUE; - } -#endif - if (m_non_diff_logic_exprs) { return FC_GIVEUP; } @@ -506,61 +478,14 @@ bool theory_diff_logic::propagate_atom(atom* a) { if (ctx.inconsistent()) { return false; } - switch(a->kind()) { - case LE_ATOM: { - int edge_id = dynamic_cast(a)->get_asserted_edge(); - if (!m_graph.enable_edge(edge_id)) { - set_neg_cycle_conflict(); - return false; - } -#if 0 - if (m_params.m_arith_bound_prop != BP_NONE) { - svector subsumed; - m_graph.find_subsumed1(edge_id, subsumed); - for (unsigned i = 0; i < subsumed.size(); ++i) { - int subsumed_edge_id = subsumed[i]; - literal l = m_graph.get_explanation(subsumed_edge_id); - context & ctx = get_context(); - region& r = ctx.get_region(); - ++m_stats.m_num_th2core_prop; - ctx.assign(l, new (r) implied_bound_justification(*this, subsumed_edge_id, edge_id)); - } - - } -#endif - break; - } - case EQ_ATOM: - if (!a->is_true()) { - SASSERT(ctx.get_assignment(a->get_bool_var()) == l_false); - // eq_atom * ea = dynamic_cast(a); - } - break; + int edge_id = a->get_asserted_edge(); + if (!m_graph.enable_edge(edge_id)) { + set_neg_cycle_conflict(); + return false; } return true; } - - -template -void theory_diff_logic::mark_as_modified_since_eq_prop() { - if (!m_modified_since_eq_prop) { - get_context().push_trail(value_trail(m_modified_since_eq_prop)); - m_modified_since_eq_prop = true; - } -} - -template -void theory_diff_logic::unmark_as_modified_since_eq_prop() { - get_context().push_trail(value_trail(m_modified_since_eq_prop)); - m_modified_since_eq_prop = false; -} - -template -void theory_diff_logic::del_clause_eh(clause* cls) { - -} - template void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { @@ -609,7 +534,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges atom* a = 0; m_bool_var2atom.find(bv, a); SASSERT(a); - edge_id e_id = static_cast(a)->get_pos(); + edge_id e_id = a->get_pos(); literal_vector lits; for (unsigned i = 0; i < num_edges; ++i) { @@ -633,11 +558,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges lits.size(), lits.c_ptr(), params.size(), params.c_ptr()); } - clause_del_eh* del_eh = alloc(theory_diff_logic_del_eh, *this); - clause* cls = ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh); - if (!cls) { - dealloc(del_eh); - } + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); if (dump_lemmas()) { char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA"; ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); @@ -677,7 +598,6 @@ void theory_diff_logic::set_neg_cycle_conflict() { literal_vector const& lits = m_nc_functor.get_lits(); context & ctx = get_context(); TRACE("arith_conflict", - //display(tout); tout << "conflict: "; for (unsigned i = 0; i < lits.size(); ++i) { ctx.display_literal_info(tout, lits[i]); @@ -802,7 +722,6 @@ theory_var theory_diff_logic::mk_num(app* n, rational const& r) { template theory_var theory_diff_logic::mk_var(enode* n) { - mark_as_modified_since_eq_prop(); theory_var v = theory::mk_var(n); TRACE("diff_logic_vars", tout << "mk_var: " << v << "\n";); m_graph.init_var(v); @@ -810,10 +729,6 @@ theory_var theory_diff_logic::mk_var(enode* n) { return v; } -template -bool theory_diff_logic::is_interpreted(app* n) const { - return n->get_family_id() == get_family_id(); -} template theory_var theory_diff_logic::mk_var(app* n) { @@ -854,7 +769,6 @@ void theory_diff_logic::reset_eh() { m_asserted_atoms .reset(); m_stats .reset(); m_scopes .reset(); - m_modified_since_eq_prop = false; m_asserted_qhead = 0; m_num_core_conflicts = 0; m_num_propagation_calls = 0; @@ -865,70 +779,6 @@ void theory_diff_logic::reset_eh() { } -template -bool theory_diff_logic::propagate_cheap_equalities() { - bool result = false; - TRACE("dl_new_eq", get_context().display(tout);); - context& ctx = get_context(); - region& reg = ctx.get_region(); - SASSERT(m_eq_prop_info_set.empty()); - SASSERT(m_eq_prop_infos.empty()); - if (m_modified_since_eq_prop) { - m_graph.compute_zero_edge_scc(m_scc_id); - int n = get_num_vars(); - for (theory_var v = 0; v < n; v++) { - rational delta_r; - theory_var x_v = expand(true, v, delta_r); - numeral delta(delta_r); - int scc_id = m_scc_id[x_v]; - if (scc_id != -1) { - delta += m_graph.get_assignment(x_v); - TRACE("eq_scc", tout << v << " " << x_v << " " << scc_id << " " << delta << "\n";); - eq_prop_info info(scc_id, delta); - typename eq_prop_info_set::entry * entry = m_eq_prop_info_set.find_core(&info); - if (entry == 0) { - eq_prop_info * new_info = alloc(eq_prop_info, scc_id, delta, v); - m_eq_prop_info_set.insert(new_info); - m_eq_prop_infos.push_back(new_info); - } - else { - // new equality found - theory_var r = entry->get_data()->get_root(); - - enode * n1 = get_enode(v); - enode * n2 = get_enode(r); - if (n1->get_root() != n2->get_root()) { - // r may be an alias (i.e., it is not realy in the graph). So, I should expand it. - // nsb: ?? - rational r_delta; - theory_var x_r = expand(true, r, r_delta); - - justification* j = new (reg) implied_eq_justification(*this, x_v, x_r, m_graph.get_timestamp()); - (void)j; - - m_stats.m_num_th2core_eqs++; - // TBD: get equality into core. - - NOT_IMPLEMENTED_YET(); - // new_eq_eh(x_v, x_r, *j); - result = true; - } - } - } - } - m_eq_prop_info_set.reset(); - std::for_each(m_eq_prop_infos.begin(), m_eq_prop_infos.end(), delete_proc()); - m_eq_prop_infos.reset(); - unmark_as_modified_since_eq_prop(); - } - - TRACE("dl_new_eq", get_context().display(tout);); - SASSERT(!m_modified_since_eq_prop); - - return result; -} - - template void theory_diff_logic::compute_delta() { m_delta = rational(1); @@ -1001,30 +851,9 @@ bool theory_diff_logic::is_consistent() const { lbool asgn = ctx.get_assignment(bv); if (ctx.is_relevant(ctx.bool_var2expr(bv)) && asgn != l_undef) { SASSERT((asgn == l_true) == a->is_true()); - switch(a->kind()) { - case LE_ATOM: { - le_atom* le = dynamic_cast(a); - int edge_id = le->get_asserted_edge(); - SASSERT(m_graph.is_enabled(edge_id)); - SASSERT(m_graph.is_feasible(edge_id)); - break; - } - case EQ_ATOM: { - eq_atom* ea = dynamic_cast(a); - bool_var bv1 = ctx.get_bool_var(ea->get_le()); - bool_var bv2 = ctx.get_bool_var(ea->get_ge()); - lbool val1 = ctx.get_assignment(bv1); - lbool val2 = ctx.get_assignment(bv2); - if (asgn == l_true) { - SASSERT(val1 == l_true); - SASSERT(val2 == l_true); - } - else { - SASSERT(val1 == l_false || val2 == l_false); - } - break; - } - } + int edge_id = a->get_asserted_edge(); + SASSERT(m_graph.is_enabled(edge_id)); + SASSERT(m_graph.is_feasible(edge_id)); } } return m_graph.is_feasible(); @@ -1087,7 +916,6 @@ void theory_diff_logic::new_eq_or_diseq(bool is_eq, theory_var v1, theory_v // assign the corresponding equality literal. // - mark_as_modified_since_eq_prop(); app_ref eq(m), s2(m), t2(m); app* s1 = get_enode(s)->get_owner(); @@ -1142,10 +970,6 @@ void theory_diff_logic::new_diseq_eh(theory_var v1, theory_var v2) { } -template -void theory_diff_logic::relevant_eh(app* e) { -} - struct imp_functor { conflict_resolution & m_cr; diff --git a/src/smt/theory_dl.cpp b/src/smt/theory_dl.cpp index 758f78c2c..9c3489aec 100644 --- a/src/smt/theory_dl.cpp +++ b/src/smt/theory_dl.cpp @@ -68,14 +68,11 @@ namespace smt { bv_util& b() { return m_bv; } class dl_value_proc : public smt::model_value_proc { - smt::model_generator & m_mg; theory_dl& m_th; smt::enode* m_node; public: - dl_value_proc(smt::model_generator & m, theory_dl& th, smt::enode* n): - m_mg(m), m_th(th), m_node(n) - { } + dl_value_proc(theory_dl& th, smt::enode* n) : m_th(th), m_node(n) {} virtual void get_dependencies(buffer & result) {} @@ -165,8 +162,8 @@ namespace smt { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } - virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator & m) { - return alloc(dl_value_proc, m, *this, n); + virtual smt::model_value_proc * mk_value(smt::enode * n) { + return alloc(dl_value_proc, *this, n); } virtual void apply_sort_cnstr(enode * n, sort * s) { diff --git a/src/smt/theory_horn_ineq.cpp b/src/smt/theory_horn_ineq.cpp new file mode 100644 index 000000000..978b5b003 --- /dev/null +++ b/src/smt/theory_horn_ineq.cpp @@ -0,0 +1,236 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq.h + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ +#include "theory_horn_ineq.h" +#include "theory_horn_ineq_def.h" + +namespace smt { + + template class theory_horn_ineq; + template class theory_horn_ineq; + + // similar to test_diff_logic: + + horn_ineq_tester::horn_ineq_tester(ast_manager& m): m(m), a(m) {} + + bool horn_ineq_tester::operator()(expr* e) { + m_todo.reset(); + m_pols.reset(); + pos_mark.reset(); + neg_mark.reset(); + m_todo.push_back(e); + m_pols.push_back(l_true); + while (!m_todo.empty()) { + expr* e = m_todo.back(); + lbool p = m_pols.back(); + m_todo.pop_back(); + m_pols.pop_back(); + switch (p) { + case l_true: + if (pos_mark.is_marked(e)) { + continue; + } + pos_mark.mark(e, true); + break; + case l_false: + if (neg_mark.is_marked(e)) { + continue; + } + neg_mark.mark(e, true); + break; + case l_undef: + if (pos_mark.is_marked(e) && neg_mark.is_marked(e)) { + continue; + } + pos_mark.mark(e, true); + neg_mark.mark(e, true); + break; + } + if (!test_expr(p, e)) { + return false; + } + } + return true; + } + + vector > const& horn_ineq_tester::get_linearization() const { + return m_terms; + } + + bool horn_ineq_tester::test_expr(lbool p, expr* e) { + expr* e1, *e2, *e3; + if (is_var(e)) { + return true; + } + if (!is_app(e)) { + return false; + } + app* ap = to_app(e); + if (m.is_and(ap) || m.is_or(ap)) { + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + m_todo.push_back(ap->get_arg(i)); + m_pols.push_back(p); + } + } + else if (m.is_not(e, e1)) { + m_todo.push_back(e); + m_pols.push_back(~p); + } + else if (m.is_ite(e, e1, e2, e3)) { + m_todo.push_back(e1); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + m_pols.push_back(p); + m_todo.push_back(e3); + m_pols.push_back(p); + } + else if (m.is_iff(e, e1, e2)) { + m_todo.push_back(e1); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + } + else if (m.is_implies(e, e1, e2)) { + m_todo.push_back(e1); + m_pols.push_back(~p); + m_todo.push_back(e2); + m_pols.push_back(p); + } + else if (m.is_eq(e, e1, e2)) { + return linearize(e1, e2) == diff; + } + else if (m.is_true(e) || m.is_false(e)) { + // no-op + } + else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || + a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + if (p == l_false) { + std::swap(e2, e1); + } + classify_t cl = linearize(e1, e2); + switch(p) { + case l_undef: + return cl == diff; + case l_true: + case l_false: + return cl == horn || cl == diff; + } + } + else if (!is_uninterp_const(e)) { + return false; + } + return true; + } + + bool horn_ineq_tester::operator()(unsigned num_fmls, expr* const* fmls) { + for (unsigned i = 0; i < num_fmls; ++i) { + if (!(*this)(fmls[i])) { + return false; + } + } + return true; + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e, rational(1))); + return linearize(); + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e1, expr* e2) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e1, rational(1))); + m_terms.push_back(std::make_pair(e2, rational(-1))); + return linearize(); + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize() { + + m_weight.reset(); + m_coeff_map.reset(); + + while (!m_terms.empty()) { + expr* e1, *e2; + rational num; + rational mul = m_terms.back().second; + expr* e = m_terms.back().first; + m_terms.pop_back(); + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); + } + } + else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_sub(e, e1, e2)) { + m_terms.push_back(std::make_pair(e1, mul)); + m_terms.push_back(std::make_pair(e2, -mul)); + } + else if (a.is_uminus(e, e1)) { + m_terms.push_back(std::make_pair(e1, -mul)); + } + else if (a.is_numeral(e, num)) { + m_weight += num*mul; + } + else if (a.is_to_real(e, e1)) { + m_terms.push_back(std::make_pair(e1, mul)); + } + else if (!is_uninterp_const(e)) { + return non_horn; + } + else { + m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; + } + } + unsigned num_negative = 0; + unsigned num_positive = 0; + bool is_unit_pos = true, is_unit_neg = true; + obj_map::iterator it = m_coeff_map.begin(); + obj_map::iterator end = m_coeff_map.end(); + for (; it != end; ++it) { + rational r = it->m_value; + if (r.is_zero()) { + continue; + } + m_terms.push_back(std::make_pair(it->m_key, r)); + if (r.is_pos()) { + is_unit_pos = is_unit_pos && r.is_one(); + num_positive++; + } + else { + is_unit_neg = is_unit_neg && r.is_minus_one(); + num_negative++; + } + } + if (num_negative <= 1 && is_unit_pos && is_unit_neg && num_positive <= 1) { + return diff; + } + if (num_positive <= 1 && is_unit_pos) { + return horn; + } + if (num_negative <= 1 && is_unit_neg) { + return co_horn; + } + return non_horn; + } + + +} diff --git a/src/smt/theory_horn_ineq.h b/src/smt/theory_horn_ineq.h new file mode 100644 index 000000000..ed96f25f6 --- /dev/null +++ b/src/smt/theory_horn_ineq.h @@ -0,0 +1,328 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq.h + +Abstract: + + A*x <= weight + D*x, coefficients to A and D are non-negative, + D is a diagonal matrix. + Coefficients to weight may have both signs. + + Label variables by weight. + Select inequality that is not satisfied. + Set delta(LHS) := 0 + Set delta(RHS(x)) := weight(x) - b + Propagate weight increment through inequalities. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ + +#ifndef _THEORY_HORN_INEQ_H_ +#define _THEORY_HORN_INEQ_H_ + +#include"rational.h" +#include"inf_rational.h" +#include"inf_int_rational.h" +#include"inf_eps_rational.h" +#include"smt_theory.h" +#include"arith_decl_plugin.h" +#include"smt_justification.h" +#include"map.h" +#include"smt_params.h" +#include"arith_eq_adapter.h" +#include"smt_model_generator.h" +#include"numeral_factory.h" +#include"smt_clause.h" + +namespace smt { + + class horn_ineq_tester { + ast_manager& m; + arith_util a; + ptr_vector m_todo; + svector m_pols; + ast_mark pos_mark, neg_mark; + obj_map m_coeff_map; + rational m_weight; + vector > m_terms; + + public: + enum classify_t { + co_horn, + horn, + diff, + non_horn + }; + horn_ineq_tester(ast_manager& m); + + // test if formula is in the Horn inequality fragment: + bool operator()(expr* fml); + bool operator()(unsigned num_fmls, expr* const* fmls); + + // linearize inequality/equality + classify_t linearize(expr* e); + classify_t linearize(expr* e1, expr* e2); + + // retrieve linearization + vector > const& get_linearization() const; + rational const& get_weight() const { return m_weight; } + private: + bool test_expr(lbool p, expr* e); + classify_t linearize(); + }; + + template + class theory_horn_ineq : public theory, private Ext { + + typedef typename Ext::numeral numeral; + typedef typename Ext::inf_numeral inf_numeral; + typedef literal explanation; + typedef theory_var th_var; + typedef svector th_var_vector; + typedef unsigned clause_id; + typedef vector > coeffs; + + class clause; + class graph; + class assignment_trail; + class parent_trail; + + class atom { + protected: + bool_var m_bvar; + bool m_true; + int m_pos; + int m_neg; + public: + atom(bool_var bv, int pos, int neg) : + m_bvar(bv), m_true(false), + m_pos(pos), m_neg(neg) {} + ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + bool is_true() const { return m_true; } + void assign_eh(bool is_true) { m_true = is_true; } + int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } + int get_pos() const { return m_pos; } + int get_neg() const { return m_neg; } + std::ostream& display(theory_horn_ineq const& th, std::ostream& out) const; + }; + typedef svector atoms; + + struct scope { + unsigned m_atoms_lim; + unsigned m_asserted_atoms_lim; + unsigned m_asserted_qhead_old; + }; + + struct stats { + unsigned m_num_conflicts; + unsigned m_num_assertions; + unsigned m_num_core2th_eqs; + unsigned m_num_core2th_diseqs; + + void reset() { + memset(this, 0, sizeof(*this)); + } + + stats() { + reset(); + } + }; + + stats m_stats; + smt_params m_params; + arith_util a; + arith_eq_adapter m_arith_eq_adapter; + th_var m_zero_int; // cache the variable representing the zero variable. + th_var m_zero_real; // cache the variable representing the zero variable. + + graph* m_graph; + atoms m_atoms; + unsigned_vector m_asserted_atoms; // set of asserted atoms + unsigned m_asserted_qhead; + u_map m_bool_var2atom; + svector m_scopes; + + double m_agility; + bool m_lia; + bool m_lra; + bool m_non_horn_ineq_exprs; + + horn_ineq_tester m_test; + + + arith_factory * m_factory; + rational m_delta; + rational m_lambda; + + + // Set a conflict due to a negative cycle. + void set_neg_cycle_conflict(); + + // Create a new theory variable. + virtual th_var mk_var(enode* n); + + virtual th_var mk_var(expr* n); + + void compute_delta(); + + void found_non_horn_ineq_expr(expr * n); + + bool is_interpreted(app* n) const { + return n->get_family_id() == get_family_id(); + } + + public: + theory_horn_ineq(ast_manager& m); + + virtual ~theory_horn_ineq(); + + virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_horn_ineq, get_manager()); } + + virtual char const * get_name() const { return "horn-inequality-logic"; } + + /** + \brief See comment in theory::mk_eq_atom + */ + virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } + + virtual void init(context * ctx); + + virtual bool internalize_atom(app * atom, bool gate_ctx); + + virtual bool internalize_term(app * term); + + virtual void internalize_eq_eh(app * atom, bool_var v); + + virtual void assign_eh(bool_var v, bool is_true); + + virtual void new_eq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_eq_eh(v1, v2); + } + + virtual bool use_diseqs() const { return true; } + + virtual void new_diseq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_diseq_eh(v1, v2); + } + + virtual void push_scope_eh(); + + virtual void pop_scope_eh(unsigned num_scopes); + + virtual void restart_eh() { + m_arith_eq_adapter.restart_eh(); + } + + virtual void relevant_eh(app* e) {} + + virtual void init_search_eh() { + m_arith_eq_adapter.init_search_eh(); + } + + virtual final_check_status final_check_eh(); + + virtual bool is_shared(th_var v) const { + return false; + } + + virtual bool can_propagate() { + SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); + return m_asserted_qhead != m_asserted_atoms.size(); + } + + virtual void propagate(); + + virtual justification * why_is_diseq(th_var v1, th_var v2) { + UNREACHABLE(); + return 0; + } + + virtual void reset_eh(); + + virtual void init_model(model_generator & m); + + virtual model_value_proc * mk_value(enode * n, model_generator & mg); + + virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { + return true; + } + + virtual void display(std::ostream & out) const; + + virtual void collect_statistics(::statistics & st) const; + + private: + + virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_eqs++; + new_eq_or_diseq(true, v1, v2, j); + } + + virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_diseqs++; + new_eq_or_diseq(false, v1, v2, j); + } + + void negate(coeffs& coeffs, rational& weight); + numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; + void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); + + void del_atoms(unsigned old_size); + + void propagate_core(); + + bool propagate_atom(atom const& a); + + th_var mk_term(app* n); + + th_var mk_num(app* n, rational const& r); + + bool is_consistent() const; + + th_var expand(bool pos, th_var v, rational & k); + + void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); + + th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } + + th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } + + void inc_conflicts(); + + }; + + struct rhi_ext { + typedef inf_rational inf_numeral; + typedef inf_eps_rational numeral; + numeral m_epsilon; + numeral m_minus_infty; + rhi_ext() : m_epsilon(inf_rational(rational(), true)), m_minus_infty(rational(-1),inf_rational()) {} + }; + + struct ihi_ext { + typedef rational inf_numeral; + typedef inf_eps_rational numeral; + numeral m_epsilon; + numeral m_minus_infty; + ihi_ext() : m_epsilon(rational(1)), m_minus_infty(rational(-1),rational(0)) {} + }; + + typedef theory_horn_ineq theory_rhi; + typedef theory_horn_ineq theory_ihi; +}; + + + + +#endif /* _THEORY_HORN_INEQ_H_ */ diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h new file mode 100644 index 000000000..f4fa7e8d7 --- /dev/null +++ b/src/smt/theory_horn_ineq_def.h @@ -0,0 +1,1166 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq_def.h + +Abstract: + + A*x <= b + D*x, coefficients to A and D are non-negative, + D is a diagonal matrix. + Coefficients to b may have both signs. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + +--*/ + +#ifndef _THEORY_HORN_INEQ_DEF_H_ +#define _THEORY_HORN_INEQ_DEF_H_ +#include "theory_horn_ineq.h" +#include "ast_pp.h" +#include "smt_context.h" + +namespace smt { + + static const unsigned null_clause_id = UINT_MAX; + + /** + A clause represents an inequality of the form + + v1*c1 + v2*c2 + .. + v_n*c_n + w <= v*c + + where + - m_vars: [v1,v2,...,v_n] + - m_coeffs: [c1,c2,...,c_n] + - m_var: v + - m_coeff: c + - m_weight: w + + */ + template + class theory_horn_ineq::clause { + vector m_coeffs; // coefficients of body. + svector m_vars; // variables of body. + rational m_coeff; // coefficient of head. + th_var m_var; // head variable. + numeral m_weight; // constant to add + literal m_explanation; + bool m_enabled; + public: + clause(unsigned sz, rational const* coeffs, th_var const* vars, + rational const& coeff, th_var var, numeral const& w, + const literal& ex): + m_coeffs(sz, coeffs), + m_vars(sz, vars), + m_coeff(coeff), + m_var(var), + m_weight(w), + m_explanation(ex), + m_enabled(false) { + DEBUG_CODE( + { + for (unsigned i = 0; i < size(); ++i) { + SASSERT(coeffs[i].is_pos()); + } + SASSERT(coeff.is_pos()); + }); + } + + th_var vars(unsigned i) const { return m_vars[i]; } + rational const& coeffs(unsigned i) const { return m_coeffs[i]; } + th_var var() const { return m_var; } + rational const& coeff() const { return m_coeff; } + const numeral & get_weight() const { return m_weight; } + const literal & get_explanation() const { return m_explanation; } + bool is_enabled() const { return m_enabled; } + unsigned size() const { return m_vars.size(); } + + void enable() { m_enabled = true; } + void disable() { m_enabled = false; } + + void display(std::ostream& out) const { + out << (is_enabled()?"+ ":"- "); + for (unsigned i = 0; i < size(); ++i) { + if (i > 0 && coeffs(i).is_pos()) { + out << " + "; + } + display(out, coeffs(i), vars(i)); + } + if (!get_weight().is_zero()) { + out << " + " << get_weight(); + } + display(out << " <= ", coeff(), var()); + out << "\n"; + } + + private: + + void display(std::ostream& out, rational const& c, th_var v) const { + if (!c.is_one()) { + out << c << "*"; + } + out << "v" << v; + } + }; + + template + class theory_horn_ineq::assignment_trail { + th_var m_var; + numeral m_old_value; + public: + assignment_trail(th_var v, const numeral & val): + m_var(v), + m_old_value(val) {} + th_var get_var() const { return m_var; } + const numeral & get_old_value() const { return m_old_value; } + }; + + template + class theory_horn_ineq::parent_trail { + th_var m_var; + clause_id m_old_value; + public: + parent_trail(th_var v, clause_id val): + m_var(v), + m_old_value(val) {} + th_var get_var() const { return m_var; } + clause_id get_old_value() const { return m_old_value; } + }; + + + template + class theory_horn_ineq::graph : private Ext { + + typedef vector assignment_stack; + typedef vector parent_stack; + typedef unsigned_vector clause_id_vector; + + struct stats { + unsigned m_propagation_cost; + + void reset() { + memset(this, 0, sizeof(*this)); + } + }; + + struct scope { + unsigned m_clauses_lim; + unsigned m_enabled_clauses_lim; + unsigned m_assignment_lim; + unsigned m_parent_lim; + scope(unsigned e, unsigned enabled, unsigned alim, unsigned plim): + m_clauses_lim(e), + m_enabled_clauses_lim(enabled), + m_assignment_lim(alim), + m_parent_lim(plim) { + } + }; + + stats m_stats; + vector m_clauses; + vector m_assignment; // per var + clause_id_vector m_parent; // per var + assignment_stack m_assignment_stack; // stack for restoring the assignment + parent_stack m_parent_stack; // stack for restoring parents + clause_id_vector m_enabled_clauses; + vector m_out_clauses; // use-list for clauses. + vector m_in_clauses; // clauses that have variable in head. + // forward reachability + unsigned_vector m_onstack; + unsigned m_ts; + unsigned_vector m_todo; + literal_vector m_lits; + vector m_coeffs; + th_var m_zero; + clause_id m_unsat_clause; + svector m_trail_stack; + + + public: + + graph(): m_ts(0), m_zero(null_theory_var), m_unsat_clause(null_clause_id) {} + + void reset() { + m_clauses .reset(); + m_assignment .reset(); + m_parent .reset(); + m_assignment_stack .reset(); + m_parent_stack .reset(); + m_out_clauses .reset(); + m_in_clauses .reset(); + m_enabled_clauses .reset(); + m_onstack .reset(); + m_ts = 0; + m_lits .reset(); + m_trail_stack .reset(); + m_unsat_clause = null_clause_id; + } + + + void traverse_neg_cycle1(bool /*stronger_lemmas*/) { + TRACE("horn_ineq", display(tout);); + SASSERT(!m_enabled_clauses.empty()); + clause_id id = m_unsat_clause; + SASSERT(id != null_clause_id); + SASSERT(!is_feasible(m_clauses[id])); + clause_id_vector todo; + vector muls; + todo.push_back(id); + muls.push_back(rational(1)); + u_map lits; + while (!todo.empty()) { + id = todo.back(); + rational mul = muls.back(); + todo.pop_back(); + muls.pop_back(); + clause const& cl = m_clauses[id]; + literal lit = cl.get_explanation(); + if (lit != null_literal) { + lits.insert_if_not_there2(id, rational(0))->get_data().m_value += mul; + } + for (unsigned i = 0; i < cl.size(); ++i) { + id = m_parent[cl.vars(i)]; + if (id != null_clause_id) { + todo.push_back(id); + muls.push_back(mul*cl.coeffs(i)); + } + } + } + u_map::iterator it = lits.begin(), end = lits.end(); + m_lits.reset(); + m_coeffs.reset(); + for (; it != end; ++it) { + m_lits.push_back(m_clauses[it->m_key].get_explanation()); + m_coeffs.push_back(it->m_value); + } + + // TODO: use topological sort to tune traversal of parents to linear. + // (current traversal can be exponential). + // TODO: negative cycle traversal with inline resolution to find + // stronger conflict clauses. + // follow heuristic used in theory_diff_logic_def.h: + } + + unsigned get_num_clauses() const { + return m_clauses.size(); + } + + literal_vector const& get_lits() const { + return m_lits; + } + + vector const& get_coeffs() const { + return m_coeffs; + } + + numeral get_assignment(th_var v) const { + return m_assignment[v]; + } + + numeral eval_body(clause const& cl) const { + numeral v(0); + for (unsigned i = 0; i < cl.size(); ++i) { + v += cl.coeffs(i)*m_assignment[cl.vars(i)]; + } + v += cl.get_weight(); + return v; + } + + numeral eval_body(clause_id id) const { + return eval_body(m_clauses[id]); + } + + numeral eval_head(clause_id id) const { + return eval_head(m_clauses[id]); + } + + numeral eval_head(clause const& cl) const { + return cl.coeff()*m_assignment[cl.var()]; + } + + clause const& get_clause(clause_id id) const { + return m_clauses[id]; + } + + void display_clause(std::ostream& out, clause_id id) const { + if (id == null_clause_id) { + out << "null\n"; + } + else { + m_clauses[id].display(out); + } + } + + void display(std::ostream& out) const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + display_clause(out, i); + } + for (unsigned i = 0; i < m_assignment.size(); ++i) { + out << m_assignment[i] << "\n"; + } + } + + void collect_statistics(::statistics& st) const { + st.update("hi_propagation_cst", m_stats.m_propagation_cost); + } + + void push() { + m_trail_stack.push_back(scope(m_clauses.size(), m_enabled_clauses.size(), + m_assignment_stack.size(), m_parent_stack.size())); + } + + void pop(unsigned num_scopes) { + unsigned lvl = m_trail_stack.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_trail_stack[new_lvl]; + // restore enabled clauses + for (unsigned i = m_enabled_clauses.size(); i > s.m_enabled_clauses_lim; ) { + --i; + m_clauses[m_enabled_clauses[i]].disable(); + } + m_enabled_clauses.shrink(s.m_enabled_clauses_lim); + + // restore assignment stack + for (unsigned i = m_assignment_stack.size(); i > s.m_assignment_lim; ) { + --i; + m_assignment[m_assignment_stack[i].get_var()] = m_assignment_stack[i].get_old_value(); + } + m_assignment_stack.shrink(s.m_assignment_lim); + + // restore parent stack + for (unsigned i = m_parent_stack.size(); i > s.m_parent_lim; ) { + --i; + m_parent[m_parent_stack[i].get_var()] = m_parent_stack[i].get_old_value(); + } + m_assignment_stack.shrink(s.m_assignment_lim); + + // restore clauses + unsigned old_num_clauses = s.m_clauses_lim; + unsigned num_clauses = m_clauses.size(); + SASSERT(old_num_clauses <= num_clauses); + unsigned to_delete = num_clauses - old_num_clauses; + for (unsigned i = 0; i < to_delete; i++) { + const clause & cl = m_clauses.back(); + TRACE("horn_ineq", tout << "deleting clause:\n"; cl.display(tout);); + for (unsigned j = 0; j < cl.size(); ++j) { + m_out_clauses[cl.vars(j)].pop_back(); + } + m_in_clauses[cl.var()].pop_back(); + m_clauses.pop_back(); + } + m_trail_stack.shrink(new_lvl); + SASSERT(check_invariant()); + } + + /** + \brief Add clause z <= z and the assignment z := 0 + Then z cannot be incremented without causing a loop (and therefore a contradiction). + */ + void set_to_zero(th_var z) { + m_zero = z; + } + + bool enable_clause(clause_id id) { + if (id == null_clause_id) { + return true; + } + clause& cl = m_clauses[id]; + bool r = true; + if (!cl.is_enabled()) { + cl.enable(); + if (!is_feasible(cl)) { + r = make_feasible(id); + } + m_enabled_clauses.push_back(id); + } + return r; + } + + void init_var(th_var v) { + unsigned sz = static_cast(v); + while (m_assignment.size() <= sz) { + m_assignment.push_back(Ext::m_minus_infty); + m_out_clauses.push_back(clause_id_vector()); + m_in_clauses.push_back(clause_id_vector()); + m_parent.push_back(null_clause_id); + m_onstack.push_back(0); + } + m_assignment[v] = Ext::m_minus_infty; + SASSERT(m_out_clauses[v].empty()); + SASSERT(m_in_clauses[v].empty()); + SASSERT(check_invariant()); + } + + clause_id add_ineq(vector > const& terms, numeral const& weight, literal l) { + vector coeffs; + svector vars; + rational coeff(1); + th_var var = null_theory_var; + bool found_negative = false; + for (unsigned i = 0; i < terms.size(); ++i) { + rational const& r = terms[i].second; + if (r.is_pos()) { + coeffs.push_back(terms[i].second); + vars.push_back(terms[i].first); + } + else if (found_negative) { + return null_clause_id; + } + else { + SASSERT(r.is_neg()); + found_negative = true; + coeff = -r; + var = terms[i].first; + } + } + if (!found_negative) { + coeff = rational(1); + var = m_zero; + } + if (!coeff.is_one()) { + // so far just support unit coefficients on right. + return null_clause_id; + } + clause_id id = m_clauses.size(); + m_clauses.push_back(clause(coeffs.size(), coeffs.c_ptr(), vars.c_ptr(), coeff, var, weight, l)); + for (unsigned i = 0; i < vars.size(); ++i) { + m_out_clauses[vars[i]].push_back(id); + } + m_in_clauses[var].push_back(id); + + return id; + } + + bool is_feasible() const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (!is_feasible(m_clauses[i])) { + return false; + } + } + return true; + } + + private: + + bool check_invariant() { + return true; + } + + /** + assignments are fully retraced on backtracking. + This is not always necessary. + */ + + void acc_assignment(th_var v, const numeral & inc) { + m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); + m_assignment[v] += inc; + } + + void acc_parent(th_var v, clause_id parent) { + m_parent[v] = parent; + m_parent_stack.push_back(parent_trail(v, parent)); + } + + numeral get_delta(const clause & cl) const { + SASSERT(cl.coeff().is_one() && "Not yet support for non-units"); + return eval_body(cl) - eval_head(cl); + } + + void set_onstack(th_var v) { + SASSERT(m_ts != 0); + m_onstack[v] = m_ts; + } + + void reset_onstack(th_var v) { + m_onstack[v] = 0; + } + + bool is_onstack(th_var v) const { + return m_onstack[v] == m_ts; + } + + void inc_ts() { + m_ts++; + if (m_ts == 0) { + m_ts++; + m_onstack.reset(); + m_onstack.resize(m_assignment.size(), 0); + } + } + + // Make the assignment feasible. An assignment is feasible if + // Forall clause cl. eval_body(cl) <= eval_head(cl) + // + // This method assumes that if the assignment is not feasible, + // then the only infeasible clause is the last added clause. + // + // Traversal is by naive DFS search. + // + bool make_feasible(clause_id id) { + SASSERT(is_almost_feasible(id)); + SASSERT(!m_clauses.empty()); + SASSERT(!is_feasible(m_clauses[id])); + const clause & cl0 = m_clauses[id]; + inc_ts(); + for (unsigned i = 0; i < cl0.size(); ++i) { + set_onstack(cl0.vars(i)); + } + th_var source = cl0.var(); + numeral delta = get_delta(cl0); + acc_parent(source, id); + SASSERT(delta.is_pos()); + acc_assignment(source, delta); + m_todo.reset(); + m_todo.push_back(source); + + TRACE("horn_ineq", cl0.display(tout);); + + do { + ++m_stats.m_propagation_cost; + + typename clause_id_vector::iterator it = m_out_clauses[source].begin(); + typename clause_id_vector::iterator end = m_out_clauses[source].end(); + for (; it != end; ++it) { + clause & cl = m_clauses[*it]; + if (!cl.is_enabled()) { + continue; + } + delta = get_delta(cl); + + if (delta.is_pos()) { + TRACE("horn_ineq", cl.display(tout);); + th_var target = cl.var(); + if (is_onstack(target)) { + m_unsat_clause = *it; + return false; + } + else { + acc_assignment(target, delta); + acc_parent(target, *it); + m_todo.push_back(target); + } + } + } + set_onstack(source); + source = m_todo.back(); + // pop stack until there is a new variable to process. + while (is_onstack(source)) { + m_todo.pop_back(); + reset_onstack(source); + if (m_todo.empty()) { + break; + } + source = m_todo.back(); + } + } + while (!m_todo.empty()); + return true; + } + + bool is_almost_feasible(clause_id id) const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (id != static_cast(i) && !is_feasible(m_clauses[i])) { + return false; + } + } + return true; + } + + bool is_feasible(const clause & cl) const { + return !cl.is_enabled() || get_delta(cl).is_nonpos(); + } + + }; + + template + theory_horn_ineq::theory_horn_ineq(ast_manager& m): + theory(m.mk_family_id("arith")), + a(m), + m_arith_eq_adapter(*this, m_params, a), + m_zero_int(null_theory_var), + m_zero_real(null_theory_var), + m_graph(0), + m_asserted_qhead(0), + m_agility(0.5), + m_lia(false), + m_lra(false), + m_non_horn_ineq_exprs(false), + m_test(m), + m_factory(0) { + m_graph = alloc(graph); + } + + template + theory_horn_ineq::~theory_horn_ineq() { + reset_eh(); + dealloc(m_graph); + } + + template + std::ostream& theory_horn_ineq::atom::display(theory_horn_ineq const& th, std::ostream& out) const { + context& ctx = th.get_context(); + lbool asgn = ctx.get_assignment(m_bvar); + bool sign = (l_undef == asgn) || m_true; + return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; + if (l_undef == asgn) { + out << "unassigned\n"; + } + else { + th.m_graph->display_clause(out, get_asserted_edge()); + } + return out; + } + + template + theory_var theory_horn_ineq::mk_var(enode* n) { + th_var v = theory::mk_var(n); + m_graph->init_var(v); + get_context().attach_th_var(n, this, v); + return v; + } + + template + theory_var theory_horn_ineq::mk_var(expr* n) { + context & ctx = get_context(); + enode* e = 0; + th_var v = null_theory_var; + m_lia |= a.is_int(n); + m_lra |= a.is_real(n); + if (!is_app(n)) { + return v; + } + if (ctx.e_internalized(n)) { + e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + } + else { + ctx.internalize(n, false); + e = ctx.get_enode(n); + } + if (v == null_theory_var) { + v = mk_var(e); + } + if (is_interpreted(to_app(n))) { + found_non_horn_ineq_expr(n); + } + return v; + } + + template + void theory_horn_ineq::reset_eh() { + m_graph ->reset(); + m_zero_int = null_theory_var; + m_zero_real = null_theory_var; + m_atoms .reset(); + m_asserted_atoms .reset(); + m_stats .reset(); + m_scopes .reset(); + m_asserted_qhead = 0; + m_agility = 0.5; + m_lia = false; + m_lra = false; + m_non_horn_ineq_exprs = false; + theory::reset_eh(); + } + + + + template + void theory_horn_ineq::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { + rational k; + th_var s = expand(true, v1, k); + th_var t = expand(false, v2, k); + context& ctx = get_context(); + ast_manager& m = get_manager(); + + if (s == t) { + if (is_eq != k.is_zero()) { + // conflict 0 /= k; + inc_conflicts(); + ctx.set_conflict(&eq_just); + } + } + else { + // + // Create equality ast, internalize_atom + // assign the corresponding equality literal. + // + + app_ref eq(m), s2(m), t2(m); + app* s1 = get_enode(s)->get_owner(); + app* t1 = get_enode(t)->get_owner(); + s2 = a.mk_sub(t1, s1); + t2 = a.mk_numeral(k, m.get_sort(s2.get())); + // t1 - s1 = k + eq = m.mk_eq(s2.get(), t2.get()); + + TRACE("horn_ineq", + tout << v1 << " .. " << v2 << "\n"; + tout << mk_pp(eq.get(), m) <<"\n";); + + if (!internalize_atom(eq.get(), false)) { + UNREACHABLE(); + } + + literal l(ctx.get_literal(eq.get())); + if (!is_eq) { + l = ~l; + } + + ctx.assign(l, b_justification(&eq_just), false); + } + } + + template + void theory_horn_ineq::inc_conflicts() { + m_stats.m_num_conflicts++; + if (m_params.m_arith_adaptive) { + double g = m_params.m_arith_adaptive_propagation_threshold; + m_agility = m_agility*g + 1 - g; + } + } + + template + void theory_horn_ineq::set_neg_cycle_conflict() { + m_graph->traverse_neg_cycle1(m_params.m_arith_stronger_lemmas); + inc_conflicts(); + literal_vector const& lits = m_graph->get_lits(); + context & ctx = get_context(); + TRACE("horn_ineq", + tout << "conflict: "; + for (unsigned i = 0; i < lits.size(); ++i) { + ctx.display_literal_info(tout, lits[i]); + } + tout << "\n"; + ); + + if (m_params.m_arith_dump_lemmas) { + char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; + ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); + } + + vector params; + if (get_manager().proofs_enabled()) { + params.push_back(parameter(symbol("farkas"))); + vector const& coeffs = m_graph->get_coeffs(); + for (unsigned i = 0; i < coeffs.size(); ++i) { + params.push_back(parameter(coeffs[i])); + } + } + + ctx.set_conflict( + ctx.mk_justification( + ext_theory_conflict_justification( + get_id(), ctx.get_region(), + lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); + } + + template + void theory_horn_ineq::found_non_horn_ineq_expr(expr* n) { + if (!m_non_horn_ineq_exprs) { + TRACE("horn_ineq", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";); + get_context().push_trail(value_trail(m_non_horn_ineq_exprs)); + m_non_horn_ineq_exprs = true; + } + } + + template + void theory_horn_ineq::init(context* ctx) { + theory::init(ctx); + m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); + m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); + m_graph->set_to_zero(m_zero_int); + m_graph->set_to_zero(m_zero_real); + } + + /** + \brief Create negated literal. + + The negation of: E <= 0 + + -E + epsilon <= 0 + or + -E + 1 <= 0 + */ + template + void theory_horn_ineq::negate(coeffs& coeffs, rational& weight) { + for (unsigned i = 0; i < coeffs.size(); ++i) { + coeffs[i].second.neg(); + } + weight.neg(); + } + + template + typename theory_horn_ineq::numeral theory_horn_ineq::mk_weight(bool is_real, bool is_strict, rational const& w) const { + if (is_strict) { + return numeral(inf_numeral(w)) + (is_real?Ext::m_epsilon:numeral(1)); + } + else { + return numeral(inf_numeral(w)); + } + } + + template + void theory_horn_ineq::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { + coeffs.reset(); + w = m_test.get_weight(); + for (unsigned i = 0; i < terms.size(); ++i) { + coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); + } + } + + template + bool theory_horn_ineq::internalize_atom(app * n, bool) { + context & ctx = get_context(); + if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { + found_non_horn_ineq_expr(n); + return false; + } + SASSERT(!ctx.b_internalized(n)); + expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); + if (a.is_ge(n) || a.is_gt(n)) { + std::swap(e1, e2); + } + bool is_strict = a.is_gt(n) || a.is_lt(n); + + horn_ineq_tester::classify_t cl = m_test.linearize(e1, e2); + if (cl == horn_ineq_tester::non_horn) { + found_non_horn_ineq_expr(n); + return false; + } + + rational w; + coeffs coeffs; + mk_coeffs(m_test.get_linearization(), coeffs, w); + bool_var bv = ctx.mk_bool_var(n); + ctx.set_var_theory(bv, get_id()); + literal l(bv); + numeral w1 = mk_weight(a.is_real(e1), is_strict, w); + clause_id pos = m_graph->add_ineq(coeffs, w1, l); + negate(coeffs, w); + numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); + clause_id neg = m_graph->add_ineq(coeffs, w2, ~l); + m_bool_var2atom.insert(bv, m_atoms.size()); + m_atoms.push_back(atom(bv, pos, neg)); + + TRACE("horn_ineq", + tout << mk_pp(n, get_manager()) << "\n"; + m_graph->display_clause(tout << "pos: ", pos); + m_graph->display_clause(tout << "neg: ", neg); + ); + + return true; + } + + template + bool theory_horn_ineq::internalize_term(app * term) { + bool result = null_theory_var != mk_term(term); + CTRACE("horn_ineq", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); + TRACE("horn_ineq", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";); + found_non_horn_ineq_expr(term); + return result; + } + + template + void theory_horn_ineq::internalize_eq_eh(app * atom, bool_var) { + // noop + } + + template + void theory_horn_ineq::assign_eh(bool_var v, bool is_true) { + m_stats.m_num_assertions++; + unsigned idx = m_bool_var2atom.find(v); + SASSERT(get_context().get_assignment(v) != l_undef); + SASSERT((get_context().get_assignment(v) == l_true) == is_true); + m_atoms[idx].assign_eh(is_true); + m_asserted_atoms.push_back(idx); + } + + template + void theory_horn_ineq::push_scope_eh() { + theory::push_scope_eh(); + m_graph->push(); + m_scopes.push_back(scope()); + scope & s = m_scopes.back(); + s.m_atoms_lim = m_atoms.size(); + s.m_asserted_atoms_lim = m_asserted_atoms.size(); + s.m_asserted_qhead_old = m_asserted_qhead; + } + + template + void theory_horn_ineq::pop_scope_eh(unsigned num_scopes) { + unsigned lvl = m_scopes.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_scopes[new_lvl]; + del_atoms(s.m_atoms_lim); + m_asserted_atoms.shrink(s.m_asserted_atoms_lim); + m_asserted_qhead = s.m_asserted_qhead_old; + m_scopes.shrink(new_lvl); + m_graph->pop(num_scopes); + theory::pop_scope_eh(num_scopes); + } + + template + final_check_status theory_horn_ineq::final_check_eh() { + SASSERT(is_consistent()); + TRACE("horn_ineq", display(tout);); + if (can_propagate()) { + propagate_core(); + return FC_CONTINUE; + } + else if (m_non_horn_ineq_exprs) { + return FC_GIVEUP; + } + else { + return FC_DONE; + } + } + + template + void theory_horn_ineq::propagate() { + propagate_core(); + } + + template + void theory_horn_ineq::display(std::ostream& out) const { + for (unsigned i = 0; i < m_atoms.size(); ++i) { + m_atoms[i].display(*this, out); + } + out << "\n"; + m_graph->display(out); + } + + template + void theory_horn_ineq::collect_statistics(::statistics& st) const { + st.update("horn ineq conflicts", m_stats.m_num_conflicts); + st.update("horn ineq assertions", m_stats.m_num_assertions); + m_arith_eq_adapter.collect_statistics(st); + m_graph->collect_statistics(st); + } + + template + void theory_horn_ineq::del_atoms(unsigned old_size) { + typename atoms::iterator begin = m_atoms.begin() + old_size; + typename atoms::iterator it = m_atoms.end(); + while (it != begin) { + --it; + m_bool_var2atom.erase(it->get_bool_var()); + } + m_atoms.shrink(old_size); + } + + template + void theory_horn_ineq::propagate_core() { + bool consistent = true; + while (consistent && can_propagate()) { + unsigned idx = m_asserted_atoms[m_asserted_qhead]; + m_asserted_qhead++; + consistent = propagate_atom(m_atoms[idx]); + } + } + + template + bool theory_horn_ineq::propagate_atom(atom const& a) { + context& ctx = get_context(); + TRACE("horn_ineq", a.display(*this, tout); tout << "\n";); + if (ctx.inconsistent()) { + return false; + } + int clause_id = a.get_asserted_edge(); + if (!m_graph->enable_clause(clause_id)) { + set_neg_cycle_conflict(); + return false; + } + return true; + } + + template + theory_var theory_horn_ineq::mk_term(app* n) { + context& ctx = get_context(); + + horn_ineq_tester::classify_t cl = m_test.linearize(n); + if (cl == horn_ineq_tester::non_horn) { + found_non_horn_ineq_expr(n); + return null_theory_var; + } + + coeffs coeffs; + rational w; + mk_coeffs(m_test.get_linearization(), coeffs, w); + if (coeffs.empty()) { + return mk_num(n, w); + } + if (coeffs.size() == 1 && coeffs[0].second.is_one()) { + return coeffs[0].first; + } + th_var target = mk_var(ctx.mk_enode(n, false, false, true)); + coeffs.push_back(std::make_pair(target, rational(-1))); + + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); + negate(coeffs, w); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); + return target; + } + + template + theory_var theory_horn_ineq::mk_num(app* n, rational const& r) { + theory_var v = null_theory_var; + context& ctx = get_context(); + if (r.is_zero()) { + v = a.is_int(n)?m_zero_int:m_zero_real; + } + else if (ctx.e_internalized(n)) { + enode* e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + } + else { + v = mk_var(ctx.mk_enode(n, false, false, true)); + // v = k: v <= k k <= v + coeffs coeffs; + coeffs.push_back(std::make_pair(v, rational(1))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(r)), null_literal))); + coeffs.back().second.neg(); + VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(-r)), null_literal))); + } + return v; + } + + template + theory_var theory_horn_ineq::expand(bool pos, th_var v, rational & k) { + context& ctx = get_context(); + enode* e = get_enode(v); + expr* x, *y; + rational r; + for (;;) { + app* n = e->get_owner(); + if (a.is_add(n, x, y)) { + if (a.is_numeral(x, r)) { + e = ctx.get_enode(y); + } + else if (a.is_numeral(y, r)) { + e = ctx.get_enode(x); + } + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + if (v == null_theory_var) { + break; + } + if (pos) { + k += r; + } + else { + k -= r; + } + } + else { + break; + } + } + return v; + } + + template + bool theory_horn_ineq::is_consistent() const { + return m_graph->is_feasible(); + } + + // models: + template + void theory_horn_ineq::init_model(model_generator & m) { + m_factory = alloc(arith_factory, get_manager()); + m.register_factory(m_factory); + compute_delta(); + } + + template + model_value_proc * theory_horn_ineq::mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + SASSERT(v != null_theory_var); + numeral val = m_graph->get_assignment(v); + rational num = val.get_infinity()*m_lambda + val.get_rational() + val.get_infinitesimal()*m_delta; + TRACE("horn_ineq", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); + return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); + } + + /** + \brief Compute numeral values for the infinitesimals to satisfy the inequalities. + */ + + template + void theory_horn_ineq::compute_delta() { + m_delta = rational(1); + m_lambda = rational(0); + unsigned sz = m_graph->get_num_clauses(); + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph->get_clause(i).is_enabled()) { + continue; + } + numeral b = m_graph->eval_body(i); + numeral h = m_graph->eval_head(i); + + if (b.get_infinity() < h.get_infinity()) { + continue; + } + SASSERT(b.get_infinity() == h.get_infinity()); + + // b <= h + // suppose that h.eps < b.eps + // then we have h.num > b.num + // but also h.num + delta*h.eps >= b.num + delta*b.eps + // <=> + // (h.num - b.num)/(b.eps - h.eps) >= delta + rational num_r = h.get_rational() - b.get_rational(); + rational eps_r = b.get_infinitesimal() - h.get_infinitesimal(); + if (eps_r.is_pos()) { + SASSERT(num_r.is_pos()); + rational new_delta = num_r/eps_r; + if (new_delta < m_delta) { + m_delta = new_delta; + } + } + } + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph->get_clause(i).is_enabled()) { + continue; + } + numeral b = m_graph->eval_body(i); + numeral h = m_graph->eval_head(i); + + rational ir = h.get_infinity() - b.get_infinity(); + rational hr = b.get_rational() - h.get_rational(); + rational num_r = hr + m_delta*(b.get_infinitesimal() - h.get_infinitesimal()); + + SASSERT(b.get_infinity() <= h.get_infinity()); + + // b <= h + // suppose that h.finite < b.finite + // then we have h.infinite > b.infinite + // but also + // h.infinite*lambda + h.finite >= b.infinite*lambda + b.finite + // <=> + // lambda >= (b.finite - h.finite) / (h.infinite - b.infinite) + if (num_r.is_pos()) { + SASSERT(ir.is_pos()); + rational new_lambda = num_r/ir; + if (new_lambda > m_lambda) { + m_lambda = new_lambda; + } + } + } + } + + + +}; + +#endif diff --git a/src/smt/theory_utvpi.cpp b/src/smt/theory_utvpi.cpp new file mode 100644 index 000000000..c45cfe74a --- /dev/null +++ b/src/smt/theory_utvpi.cpp @@ -0,0 +1,160 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi.h + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ +#include "theory_utvpi.h" +#include "theory_utvpi_def.h" + +namespace smt { + + template class theory_utvpi; + template class theory_utvpi; + + // similar to test_diff_logic: + + utvpi_tester::utvpi_tester(ast_manager& m): m(m), a(m) {} + + bool utvpi_tester::operator()(expr* e) { + m_todo.reset(); + m_mark.reset(); + m_todo.push_back(e); + expr* e1, *e2; + + while (!m_todo.empty()) { + expr* e = m_todo.back(); + m_todo.pop_back(); + if (!m_mark.is_marked(e)) { + m_mark.mark(e, true); + if (is_var(e)) { + continue; + } + if (!is_app(e)) { + return false; + } + app* ap = to_app(e); + if (m.is_eq(ap, e1, e2)) { + if (!linearize(e1, e2)) { + return false; + } + } + else if (ap->get_family_id() == m.get_basic_family_id()) { + continue; + } + else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || + a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + if (!linearize(e1, e2)) { + return false; + } + } + else if (is_uninterp_const(e)) { + continue; + } + else { + return false; + } + } + } + return true; + } + + vector > const& utvpi_tester::get_linearization() const { + SASSERT(m_terms.size() <= 2); + return m_terms; + } + + bool utvpi_tester::operator()(unsigned num_fmls, expr* const* fmls) { + for (unsigned i = 0; i < num_fmls; ++i) { + if (!(*this)(fmls[i])) { + return false; + } + } + return true; + } + + bool utvpi_tester::linearize(expr* e) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e, rational(1))); + return linearize(); + } + + bool utvpi_tester::linearize(expr* e1, expr* e2) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e1, rational(1))); + m_terms.push_back(std::make_pair(e2, rational(-1))); + return linearize(); + } + + bool utvpi_tester::linearize() { + + m_weight.reset(); + m_coeff_map.reset(); + + while (!m_terms.empty()) { + expr* e1, *e2; + rational num; + rational mul = m_terms.back().second; + expr* e = m_terms.back().first; + m_terms.pop_back(); + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); + } + } + else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_sub(e, e1, e2)) { + m_terms.push_back(std::make_pair(e1, mul)); + m_terms.push_back(std::make_pair(e2, -mul)); + } + else if (a.is_uminus(e, e1)) { + m_terms.push_back(std::make_pair(e1, -mul)); + } + else if (a.is_numeral(e, num)) { + m_weight += num*mul; + } + else if (a.is_to_real(e, e1)) { + m_terms.push_back(std::make_pair(e1, mul)); + } + else if (!is_uninterp_const(e)) { + return false; + } + else { + m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; + } + } + obj_map::iterator it = m_coeff_map.begin(); + obj_map::iterator end = m_coeff_map.end(); + for (; it != end; ++it) { + rational r = it->m_value; + if (r.is_zero()) { + continue; + } + m_terms.push_back(std::make_pair(it->m_key, r)); + if (m_terms.size() > 2) { + return false; + } + if (!r.is_one() && !r.is_minus_one()) { + return false; + } + } + return true; + } + + +} diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h new file mode 100644 index 000000000..4a5d97d52 --- /dev/null +++ b/src/smt/theory_utvpi.h @@ -0,0 +1,343 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi.h + +Abstract: + + use Bellman Ford traversal algorithm for UTVPI. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ + +#ifndef _THEORY_UTVPI_H_ +#define _THEORY_UTVPI_H_ + +#include"theory_diff_logic.h" + +namespace smt { + + class utvpi_tester { + ast_manager& m; + arith_util a; + ptr_vector m_todo; + ast_mark m_mark; + obj_map m_coeff_map; + rational m_weight; + vector > m_terms; + + public: + utvpi_tester(ast_manager& m); + + // test if formula is in the Horn inequality fragment: + bool operator()(expr* fml); + bool operator()(unsigned num_fmls, expr* const* fmls); + + // linearize inequality/equality + bool linearize(expr* e); + bool linearize(expr* e1, expr* e2); + + // retrieve linearization + vector > const& get_linearization() const; + rational const& get_weight() const { return m_weight; } + private: + bool linearize(); + }; + + template + class theory_utvpi : public theory, private Ext { + + typedef typename Ext::numeral numeral; + typedef theory_var th_var; + typedef svector th_var_vector; + typedef vector > coeffs; + + class assignment_trail; + class parent_trail; + + struct GExt : public Ext { + typedef std::pair explanation; + }; + + class atom { + protected: + bool_var m_bvar; + bool m_true; + int m_pos; + int m_neg; + public: + atom(bool_var bv, int pos, int neg) : + m_bvar(bv), m_true(false), + m_pos(pos), m_neg(neg) {} + ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + void assign_eh(bool is_true) { m_true = is_true; } + int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } + int get_pos() const { return m_pos; } + int get_neg() const { return m_neg; } + std::ostream& display(theory_utvpi const& th, std::ostream& out) const; + }; + typedef svector atoms; + + struct scope { + unsigned m_atoms_lim; + unsigned m_asserted_atoms_lim; + unsigned m_asserted_qhead_old; + }; + + struct stats { + unsigned m_num_conflicts; + unsigned m_num_assertions; + unsigned m_num_core2th_eqs; + unsigned m_num_core2th_diseqs; + + void reset() { + memset(this, 0, sizeof(*this)); + } + + stats() { + reset(); + } + }; + + // Functor used to collect the proofs for a conflict due to + // a negative cycle. + class nc_functor { + literal_vector m_antecedents; + unsigned_vector m_coeffs; + theory_utvpi& m_super; + public: + nc_functor(theory_utvpi& s) : m_super(s) {} + void reset() { m_antecedents.reset(); m_coeffs.reset(); } + literal_vector const& get_lits() const { return m_antecedents; } + unsigned_vector const& get_coeffs() const { return m_coeffs; } + + void operator()(std::pair const & ex) { + if (ex.first != null_literal) { + m_antecedents.push_back(ex.first); + m_coeffs.push_back(ex.second); + } + } + + void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { + m_super.new_edge(src, dst, num_edges, edges); + } + }; + + + stats m_stats; + smt_params m_params; + arith_util a; + arith_eq_adapter m_arith_eq_adapter; + th_var m_zero_int; // cache the variable representing the zero variable. + th_var m_zero_real; // cache the variable representing the zero variable. + + dl_graph m_graph; + nc_functor m_nc_functor; + atoms m_atoms; + unsigned_vector m_asserted_atoms; // set of asserted atoms + unsigned m_asserted_qhead; + u_map m_bool_var2atom; + svector m_scopes; + + double m_agility; + bool m_lia; + bool m_lra; + bool m_non_utvpi_exprs; + + utvpi_tester m_test; + + + arith_factory * m_factory; + rational m_delta; + + + // Set a conflict due to a negative cycle. + void set_conflict(); + + void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {} + + // Create a new theory variable. + virtual th_var mk_var(enode* n); + + virtual th_var mk_var(expr* n); + + void compute_delta(); + + void found_non_utvpi_expr(expr * n); + + bool is_interpreted(app* n) const { + return n->get_family_id() == get_family_id(); + } + + public: + theory_utvpi(ast_manager& m); + + virtual ~theory_utvpi(); + + virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_utvpi, get_manager()); } + + virtual char const * get_name() const { return "utvpi-logic"; } + + /** + \brief See comment in theory::mk_eq_atom + */ + virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } + + virtual void init(context * ctx); + + virtual bool internalize_atom(app * atom, bool gate_ctx); + + virtual bool internalize_term(app * term); + + virtual void internalize_eq_eh(app * atom, bool_var v); + + virtual void assign_eh(bool_var v, bool is_true); + + virtual void new_eq_eh(th_var v1, th_var v2) { + m_stats.m_num_core2th_eqs++; + m_arith_eq_adapter.new_eq_eh(v1, v2); + } + + virtual bool use_diseqs() const { return true; } + + virtual void new_diseq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_diseq_eh(v1, v2); + } + + virtual void push_scope_eh(); + + virtual void pop_scope_eh(unsigned num_scopes); + + virtual void restart_eh() { + m_arith_eq_adapter.restart_eh(); + } + + virtual void relevant_eh(app* e) {} + + virtual void init_search_eh() { + m_arith_eq_adapter.init_search_eh(); + } + + virtual final_check_status final_check_eh(); + + virtual bool is_shared(th_var v) const { + return false; + } + + virtual bool can_propagate() { + SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); + return m_asserted_qhead != m_asserted_atoms.size(); + } + + virtual void propagate(); + + virtual justification * why_is_diseq(th_var v1, th_var v2) { + UNREACHABLE(); + return 0; + } + + virtual void reset_eh(); + + virtual void init_model(model_generator & m); + + virtual model_value_proc * mk_value(enode * n, model_generator & mg); + + virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { + return true; + } + + virtual void display(std::ostream & out) const; + + virtual void collect_statistics(::statistics & st) const; + + private: + + rational mk_value(theory_var v, bool is_int); + + bool is_parity_ok(unsigned v) const; + + void enforce_parity(); + + void validate_model(); + + bool eval(expr* e); + + rational eval_num(expr* e); + + bool check_z_consistency(); + + virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_eqs++; + new_eq_or_diseq(true, v1, v2, j); + } + + virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_diseqs++; + new_eq_or_diseq(false, v1, v2, j); + } + + void negate(coeffs& coeffs, rational& weight); + numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; + void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); + + void del_atoms(unsigned old_size); + + bool propagate_atom(atom const& a); + + th_var mk_term(app* n); + + th_var mk_num(app* n, rational const& r); + + bool is_consistent() const; + + th_var expand(bool pos, th_var v, rational & k); + + void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); + + th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } + + th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } + + void inc_conflicts(); + + edge_id add_ineq(vector > const& terms, numeral const& weight, literal l); + + bool enable_edge(edge_id id); + + th_var to_var(th_var v) const { + return 2*v; + } + + th_var from_var(th_var v) const { + return v/2; + } + + th_var pos(th_var v) const { + return v & 0xFFFFFFFE; + } + + th_var neg(th_var v) const { + return v | 0x1; + } + + }; + + + typedef theory_utvpi theory_rutvpi; + typedef theory_utvpi theory_iutvpi; +}; + + + + +#endif /* _THEORY_UTVPI_H_ */ diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h new file mode 100644 index 000000000..8463eb17f --- /dev/null +++ b/src/smt/theory_utvpi_def.h @@ -0,0 +1,954 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi_def.h + +Abstract: + + Implementation of UTVPI solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + 1. introduce x^+ and x^-, such that 2*x := x^+ - x^- + 2. rewrite constraints as follows: + + x - y <= k => x^+ - y^+ <= k + y^- - x^- <= k + + x <= k => x^+ - x^- <= 2k + + + x + y <= k => x^+ - y^- <= k + y^+ - x^- <= k + + + - x - y <= k => x^- - y^+ <= k + y^- - x^+ <= k + + 3. Solve for x^+ and x^- + 4. Check parity condition for integers (see Lahiri and Musuvathi 05) + This checks if x^+ and x^- are in the same component but of different + parities. + 5. Enforce parity on variables. This checks if x^+ and x^- have different + parities. If they have different parities, the assignment to one + of the variables is decremented (choose the variable that is not tightly + constrained with 0). + The process that adjusts parities converges: Suppose we break a parity + of a different variable y while fixing x's parity. A cyclic breaking/fixing + of parities implies there is a strongly connected component between x, y + and the two polarities of the variables. This contradicts the test in 4. + 6. extract model for M(x) := (M(x^+)- M(x^-))/2 + +--*/ + +#ifndef _THEORY_UTVPI_DEF_H_ +#define _THEORY_UTVPI_DEF_H_ +#include "theory_utvpi.h" +#include "heap.h" +#include "ast_pp.h" +#include "smt_context.h" + +namespace smt { + + + template + theory_utvpi::theory_utvpi(ast_manager& m): + theory(m.mk_family_id("arith")), + a(m), + m_arith_eq_adapter(*this, m_params, a), + m_zero_int(null_theory_var), + m_zero_real(null_theory_var), + m_nc_functor(*this), + m_asserted_qhead(0), + m_agility(0.5), + m_lia(false), + m_lra(false), + m_non_utvpi_exprs(false), + m_test(m), + m_factory(0) { + } + + template + theory_utvpi::~theory_utvpi() { + reset_eh(); + } + + template + std::ostream& theory_utvpi::atom::display(theory_utvpi const& th, std::ostream& out) const { + context& ctx = th.get_context(); + lbool asgn = ctx.get_assignment(m_bvar); + bool sign = (l_undef == l_false); + return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; + if (l_undef == asgn) { + out << "unassigned\n"; + } + else { + th.m_graph.display_edge(out, get_asserted_edge()); + } + return out; + } + + template + theory_var theory_utvpi::mk_var(enode* n) { + th_var v = theory::mk_var(n); + TRACE("utvpi", tout << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";); + m_graph.init_var(to_var(v)); + m_graph.init_var(neg(to_var(v))); + get_context().attach_th_var(n, this, v); + return v; + } + + template + theory_var theory_utvpi::mk_var(expr* n) { + context & ctx = get_context(); + enode* e = 0; + th_var v = null_theory_var; + m_lia |= a.is_int(n); + m_lra |= a.is_real(n); + if (!is_app(n)) { + return v; + } + if (ctx.e_internalized(n)) { + e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + } + else { + ctx.internalize(n, false); + e = ctx.get_enode(n); + } + if (v == null_theory_var) { + v = mk_var(e); + } + if (is_interpreted(to_app(n))) { + found_non_utvpi_expr(n); + } + return v; + } + + template + void theory_utvpi::reset_eh() { + m_graph .reset(); + m_zero_int = null_theory_var; + m_zero_real = null_theory_var; + m_atoms .reset(); + m_asserted_atoms .reset(); + m_stats .reset(); + m_scopes .reset(); + m_asserted_qhead = 0; + m_agility = 0.5; + m_lia = false; + m_lra = false; + m_non_utvpi_exprs = false; + theory::reset_eh(); + } + + template + void theory_utvpi::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { + rational k; + th_var s = expand(true, v1, k); + th_var t = expand(false, v2, k); + context& ctx = get_context(); + ast_manager& m = get_manager(); + + if (s == t) { + if (is_eq != k.is_zero()) { + // conflict 0 /= k; + inc_conflicts(); + ctx.set_conflict(&eq_just); + } + } + else { + // + // Create equality ast, internalize_atom + // assign the corresponding equality literal. + // + + app_ref eq(m), s2(m), t2(m); + app* s1 = get_enode(s)->get_owner(); + app* t1 = get_enode(t)->get_owner(); + s2 = a.mk_sub(t1, s1); + t2 = a.mk_numeral(k, m.get_sort(s2.get())); + // t1 - s1 = k + eq = m.mk_eq(s2.get(), t2.get()); + + TRACE("utvpi", + tout << v1 << " .. " << v2 << "\n"; + tout << mk_pp(eq.get(), m) <<"\n";); + + if (!internalize_atom(eq.get(), false)) { + UNREACHABLE(); + } + + literal l(ctx.get_literal(eq.get())); + if (!is_eq) { + l = ~l; + } + ctx.assign(l, b_justification(&eq_just), false); + } + } + + template + void theory_utvpi::inc_conflicts() { + m_stats.m_num_conflicts++; + if (m_params.m_arith_adaptive) { + double g = m_params.m_arith_adaptive_propagation_threshold; + m_agility = m_agility*g + 1 - g; + } + } + + template + void theory_utvpi::set_conflict() { + inc_conflicts(); + literal_vector const& lits = m_nc_functor.get_lits(); + context & ctx = get_context(); + IF_VERBOSE(2, + verbose_stream() << "conflict:\n"; + for (unsigned i = 0; i < lits.size(); ++i) { + ast_manager& m = get_manager(); + expr_ref e(m); + ctx.literal2expr(lits[i], e); + verbose_stream() << mk_pp(e, m) << "\n"; + } + verbose_stream() << "\n";); + TRACE("utvpi", + tout << "conflict: "; + for (unsigned i = 0; i < lits.size(); ++i) { + ctx.display_literal_info(tout, lits[i]); + } + tout << "\n"; + ); + + if (m_params.m_arith_dump_lemmas) { + char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; + ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); + } + + vector params; + if (get_manager().proofs_enabled()) { + params.push_back(parameter(symbol("farkas"))); + for (unsigned i = 0; i < m_nc_functor.get_coeffs().size(); ++i) { + params.push_back(parameter(rational(m_nc_functor.get_coeffs()[i]))); + } + } + + ctx.set_conflict( + ctx.mk_justification( + ext_theory_conflict_justification( + get_id(), ctx.get_region(), + lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); + + m_nc_functor.reset(); + } + + template + void theory_utvpi::found_non_utvpi_expr(expr* n) { + if (!m_non_utvpi_exprs) { + std::stringstream msg; + msg << "found non utvpi logic expression:\n" << mk_pp(n, get_manager()) << "\n"; + TRACE("utvpi", tout << msg.str();); + warning_msg(msg.str().c_str()); + get_context().push_trail(value_trail(m_non_utvpi_exprs)); + m_non_utvpi_exprs = true; + } + } + + template + void theory_utvpi::init(context* ctx) { + theory::init(ctx); + m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); + m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); + } + + /** + \brief Create negated literal. + + The negation of: E <= 0 + + -E + epsilon <= 0 + or + -E + 1 <= 0 + */ + template + void theory_utvpi::negate(coeffs& coeffs, rational& weight) { + for (unsigned i = 0; i < coeffs.size(); ++i) { + coeffs[i].second.neg(); + } + weight.neg(); + } + + template + typename theory_utvpi::numeral theory_utvpi::mk_weight(bool is_real, bool is_strict, rational const& w) const { + if (is_strict) { + return numeral(w) + (is_real?Ext::m_epsilon:numeral(1)); + } + else { + return numeral(w); + } + } + + template + void theory_utvpi::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { + coeffs.reset(); + w = m_test.get_weight(); + for (unsigned i = 0; i < terms.size(); ++i) { + coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); + } + } + + template + void theory_utvpi::internalize_eq_eh(app * atom, bool_var v) { + context & ctx = get_context(); + app * lhs = to_app(atom->get_arg(0)); + app * rhs = to_app(atom->get_arg(1)); + if (a.is_numeral(rhs)) { + std::swap(rhs, lhs); + } + if (!a.is_numeral(rhs)) { + return; + } + if (a.is_add(lhs) || a.is_sub(lhs)) { + // force axioms for (= (+ x y) k) + // this is necessary because (+ x y) is not expressible as a utvpi term. + m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); + } + } + + template + bool theory_utvpi::internalize_atom(app * n, bool) { + context & ctx = get_context(); + if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { + found_non_utvpi_expr(n); + return false; + } + SASSERT(!ctx.b_internalized(n)); + expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); + if (a.is_ge(n) || a.is_gt(n)) { + std::swap(e1, e2); + } + bool is_strict = a.is_gt(n) || a.is_lt(n); + + bool cl = m_test.linearize(e1, e2); + if (!cl) { + found_non_utvpi_expr(n); + return false; + } + + rational w; + coeffs coeffs; + mk_coeffs(m_test.get_linearization(), coeffs, w); + bool_var bv = ctx.mk_bool_var(n); + ctx.set_var_theory(bv, get_id()); + literal l(bv); + numeral w1 = mk_weight(a.is_real(e1), is_strict, w); + edge_id pos = add_ineq(coeffs, w1, l); + negate(coeffs, w); + numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); + edge_id neg = add_ineq(coeffs, w2, ~l); + m_bool_var2atom.insert(bv, m_atoms.size()); + m_atoms.push_back(atom(bv, pos, neg)); + + TRACE("utvpi", + tout << mk_pp(n, get_manager()) << "\n"; + m_graph.display_edge(tout << "pos: ", pos); + m_graph.display_edge(tout << "neg: ", neg); + ); + + return true; + } + + template + bool theory_utvpi::internalize_term(app * term) { + bool result = null_theory_var != mk_term(term); + CTRACE("utvpi", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); + return result; + } + + template + void theory_utvpi::assign_eh(bool_var v, bool is_true) { + m_stats.m_num_assertions++; + unsigned idx = m_bool_var2atom.find(v); + SASSERT(get_context().get_assignment(v) != l_undef); + SASSERT((get_context().get_assignment(v) == l_true) == is_true); + m_atoms[idx].assign_eh(is_true); + m_asserted_atoms.push_back(idx); + } + + template + void theory_utvpi::push_scope_eh() { + theory::push_scope_eh(); + m_graph.push(); + m_scopes.push_back(scope()); + scope & s = m_scopes.back(); + s.m_atoms_lim = m_atoms.size(); + s.m_asserted_atoms_lim = m_asserted_atoms.size(); + s.m_asserted_qhead_old = m_asserted_qhead; + } + + template + void theory_utvpi::pop_scope_eh(unsigned num_scopes) { + unsigned lvl = m_scopes.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_scopes[new_lvl]; + del_atoms(s.m_atoms_lim); + m_asserted_atoms.shrink(s.m_asserted_atoms_lim); + m_asserted_qhead = s.m_asserted_qhead_old; + m_scopes.shrink(new_lvl); + m_graph.pop(num_scopes); + theory::pop_scope_eh(num_scopes); + } + + template + final_check_status theory_utvpi::final_check_eh() { + SASSERT(is_consistent()); + if (can_propagate()) { + propagate(); + return FC_CONTINUE; + } + else if (!check_z_consistency()) { + return FC_CONTINUE; + } + else if (m_non_utvpi_exprs) { + return FC_GIVEUP; + } + else { + return FC_DONE; + } + } + + template + bool theory_utvpi::check_z_consistency() { + int_vector scc_id; + m_graph.compute_zero_edge_scc(scc_id); + + unsigned sz = get_num_vars(); + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (!a.is_int(e->get_owner())) { + continue; + } + th_var v1 = to_var(i); + th_var v2 = neg(v1); + rational r1 = m_graph.get_assignment(v1).get_rational(); + rational r2 = m_graph.get_assignment(v2).get_rational(); + SASSERT(r1.is_int()); + SASSERT(r2.is_int()); + if (r1.is_even() == r2.is_even()) { + continue; + } + if (scc_id[v1] != scc_id[v2]) { + continue; + } + if (scc_id[v1] == -1) { + continue; + } + // they are in the same SCC and have different parities => contradiction. + m_nc_functor.reset(); + VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, UINT_MAX, m_nc_functor)); + VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, UINT_MAX, m_nc_functor)); + IF_VERBOSE(1, verbose_stream() << "parity conflict " << mk_pp(e->get_owner(), get_manager()) << "\n";); + set_conflict(); + + return false; + } + return true; + } + + template + void theory_utvpi::display(std::ostream& out) const { + for (unsigned i = 0; i < m_atoms.size(); ++i) { + m_atoms[i].display(*this, out); out << "\n"; + } + m_graph.display(out); + } + + template + void theory_utvpi::collect_statistics(::statistics& st) const { + st.update("utvpi conflicts", m_stats.m_num_conflicts); + st.update("utvpi asserts", m_stats.m_num_assertions); + st.update("core->utvpi eqs", m_stats.m_num_core2th_eqs); + st.update("core->utvpi diseqs", m_stats.m_num_core2th_diseqs); + m_arith_eq_adapter.collect_statistics(st); + m_graph.collect_statistics(st); + } + + template + void theory_utvpi::del_atoms(unsigned old_size) { + typename atoms::iterator begin = m_atoms.begin() + old_size; + typename atoms::iterator it = m_atoms.end(); + while (it != begin) { + --it; + m_bool_var2atom.erase(it->get_bool_var()); + } + m_atoms.shrink(old_size); + } + + template + void theory_utvpi::propagate() { + bool consistent = true; + while (consistent && can_propagate()) { + unsigned idx = m_asserted_atoms[m_asserted_qhead]; + m_asserted_qhead++; + consistent = propagate_atom(m_atoms[idx]); + } + } + + template + bool theory_utvpi::propagate_atom(atom const& a) { + context& ctx = get_context(); + TRACE("utvpi", a.display(*this, tout); tout << "\n";); + if (ctx.inconsistent()) { + return false; + } + int edge_id = a.get_asserted_edge(); + if (!enable_edge(edge_id)) { + m_graph.traverse_neg_cycle2(m_params.m_arith_stronger_lemmas, m_nc_functor); + set_conflict(); + return false; + } + return true; + } + + template + theory_var theory_utvpi::mk_term(app* n) { + TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n";); + context& ctx = get_context(); + + bool cl = m_test.linearize(n); + if (!cl) { + found_non_utvpi_expr(n); + return null_theory_var; + } + + coeffs coeffs; + rational w; + mk_coeffs(m_test.get_linearization(), coeffs, w); + if (coeffs.empty()) { + return mk_num(n, w); + } + if (coeffs.size() == 1 && coeffs[0].second.is_one()) { + return coeffs[0].first; + } + if (coeffs.size() == 2) { + // do not create an alias. + return null_theory_var; + } + for (unsigned i = 0; i < n->get_num_args(); ++i) { + mk_term(to_app(n->get_arg(i))); + } + th_var target = mk_var(ctx.mk_enode(n, false, false, true)); + coeffs.push_back(std::make_pair(target, rational(-1))); + + VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); + negate(coeffs, w); + VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); + return target; + } + + template + theory_var theory_utvpi::mk_num(app* n, rational const& r) { + theory_var v = null_theory_var; + context& ctx = get_context(); + if (r.is_zero()) { + v = a.is_int(n)?m_zero_int:m_zero_real; + } + else if (ctx.e_internalized(n)) { + enode* e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + } + else { + v = mk_var(ctx.mk_enode(n, false, false, true)); + // v = k: v <= k k <= v + coeffs coeffs; + coeffs.push_back(std::make_pair(v, rational(-1))); + VERIFY(enable_edge(add_ineq(coeffs, numeral(r), null_literal))); + coeffs.back().second.neg(); + VERIFY(enable_edge(add_ineq(coeffs, numeral(-r), null_literal))); + } + return v; + } + + template + theory_var theory_utvpi::expand(bool pos, th_var v, rational & k) { + context& ctx = get_context(); + enode* e = get_enode(v); + expr* x, *y; + rational r; + for (;;) { + app* n = e->get_owner(); + if (a.is_add(n, x, y)) { + if (a.is_numeral(x, r)) { + e = ctx.get_enode(y); + } + else if (a.is_numeral(y, r)) { + e = ctx.get_enode(x); + } + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + if (v == null_theory_var) { + break; + } + if (pos) { + k += r; + } + else { + k -= r; + } + } + else { + break; + } + } + return v; + } + + // m_graph(source, target, weight, ex); + // target - source <= weight + + template + edge_id theory_utvpi::add_ineq(vector > const& terms, numeral const& weight, literal l) { + + SASSERT(terms.size() <= 2); + SASSERT(terms.size() < 1 || terms[0].second.is_one() || terms[0].second.is_minus_one()); + SASSERT(terms.size() < 2 || terms[1].second.is_one() || terms[1].second.is_minus_one()); + + th_var v1 = null_theory_var, v2 = null_theory_var; + bool pos1 = true, pos2 = true; + if (terms.size() >= 1) { + v1 = terms[0].first; + pos1 = terms[0].second.is_one(); + SASSERT(v1 != null_theory_var); + SASSERT(pos1 || terms[0].second.is_minus_one()); + } + if (terms.size() >= 2) { + v2 = terms[1].first; + pos2 = terms[1].second.is_one(); + SASSERT(v1 != null_theory_var); + SASSERT(pos2 || terms[1].second.is_minus_one()); + } +// TRACE("utvpi", tout << (pos1?"$":"-$") << v1 << (pos2?" + $":" - $") << v2 << " + " << weight << " <= 0\n";); + edge_id id = m_graph.get_num_edges(); + th_var w1 = to_var(v1), w2 = to_var(v2); + if (terms.size() == 1 && pos1) { + m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); + m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); + } + else if (terms.size() == 1 && !pos1) { + m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); + m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); + } + else if (pos1 && pos2) { + m_graph.add_edge(neg(w2), pos(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(neg(w1), pos(w2), -weight, std::make_pair(l,1)); + } + else if (pos1 && !pos2) { + m_graph.add_edge(pos(w2), pos(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(neg(w1), neg(w2), -weight, std::make_pair(l,1)); + } + else if (!pos1 && pos2) { + m_graph.add_edge(neg(w2), neg(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(pos(w1), pos(w2), -weight, std::make_pair(l,1)); + } + else { + m_graph.add_edge(pos(w1), neg(w2), -weight, std::make_pair(l,1)); + m_graph.add_edge(pos(w2), neg(w1), -weight, std::make_pair(l,1)); + } + return id; + } + + template + bool theory_utvpi::enable_edge(edge_id id) { + return (id == null_edge_id) || (m_graph.enable_edge(id) && m_graph.enable_edge(id+1)); + } + + template + bool theory_utvpi::is_consistent() const { + return m_graph.is_feasible(); + } + + + template + bool theory_utvpi::is_parity_ok(unsigned i) const { + th_var v1 = to_var(i); + th_var v2 = neg(v1); + rational r1 = m_graph.get_assignment(v1).get_rational(); + rational r2 = m_graph.get_assignment(v2).get_rational(); + return r1.is_even() == r2.is_even(); + } + + + /** + \brief adjust values for variables in the difference graph + such that for variables of integer sort it is + the case that x^+ - x^- is even. + The informal justification for the procedure enforce_parity relies + on a set of properties: + 1. the graph does not contain a strongly connected component where + x^+ and x+- are connected. They can be independently changed. + This is checked prior to enforce_parity. + 2. When x^+ - x^- is odd, the values are adjusted by first + decrementing the value of x^+, provided x^- is not 0-dependent. + Otherwise decrement x^-. + x^- is "0-dependent" if there is a set of tight + inequalities from x^+ to x^-. + 3. The affinity to x^+ (the same component of x^+) ensures that + the parity is broken only a finite number of times when + traversing that component. Namely, suppose that the parity of y + gets broken when fixing 'x'. Then first note that 'y' cannot + be equal to 'x'. If it were, then we have a state where: + parity(x^+) != parity(x^-) and + parity(y^+) == parity(y^-) + but x^+ and y^+ are tightly connected and x^- and y^- are + also tightly connected using two copies of the same inequalities. + This is a contradiction. + Thus, 'y' cannot be equal to 'x' if 'y's parity gets broken when + repairing 'x'. + + */ + template + void theory_utvpi::enforce_parity() { + unsigned_vector todo; + unsigned sz = get_num_vars(); + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { + todo.push_back(i); + } + } + if (todo.empty()) { + return; + } + while (!todo.empty()) { + unsigned i = todo.back(); + todo.pop_back(); + if (is_parity_ok(i)) { + continue; + } + th_var v1 = to_var(i); + th_var v2 = neg(v1); + + int_vector zero_v; + m_graph.compute_zero_succ(v1, zero_v); + for (unsigned j = 0; j < zero_v.size(); ++j) { + if (zero_v[j] == v2) { + zero_v.reset(); + m_graph.compute_zero_succ(v2, zero_v); + break; + } + } + + TRACE("utvpi", + tout << "Disparity: " << v1 << "\n"; + for (unsigned j = 0; j < zero_v.size(); ++j) { + tout << "decrement: " << zero_v[j] << "\n"; + }); + + for (unsigned j = 0; j < zero_v.size(); ++j) { + int v = zero_v[j]; + m_graph.acc_assignment(v, numeral(-1)); + th_var k = from_var(v); + if (!is_parity_ok(k)) { + todo.push_back(k); + } + } + } + SASSERT(m_graph.is_feasible()); + DEBUG_CODE( + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { + IF_VERBOSE(0, verbose_stream() << "disparities not fixed\n";); + UNREACHABLE(); + } + }); + } + + + // models: + template + void theory_utvpi::init_model(model_generator & m) { + m_factory = alloc(arith_factory, get_manager()); + m.register_factory(m_factory); + enforce_parity(); + m_graph.set_to_zero(to_var(m_zero_int), to_var(m_zero_real)); + m_graph.set_to_zero(neg(to_var(m_zero_int)), neg(to_var(m_zero_real))); + m_graph.set_to_zero(to_var(m_zero_int), neg(to_var(m_zero_int))); + compute_delta(); + DEBUG_CODE(validate_model();); + } + + template + void theory_utvpi::validate_model() { + context& ctx = get_context(); + unsigned sz = m_atoms.size(); + for (unsigned i = 0; i < sz; ++i) { + bool_var b = m_atoms[i].get_bool_var(); + if (!ctx.is_relevant(b)) { + continue; + } + bool ok = true; + expr* e = ctx.bool_var2expr(b); + lbool assign = ctx.get_assignment(b); + switch(assign) { + case l_true: + ok = eval(e); + break; + case l_false: + ok = !eval(e); + break; + default: + break; + } + CTRACE("utvpi", !ok, + tout << "validation failed:\n"; + tout << "Assignment: " << assign << "\n"; + m_atoms[i].display(*this, tout); + tout << "\n"; + display(tout); + m_graph.display_agl(tout); + ); + if (!ok) { + std::cout << "validation failed:\n"; + std::cout << "Assignment: " << assign << "\n"; + m_atoms[i].display(*this, std::cout); + std::cout << "\n"; + display(std::cout); + m_graph.display_agl(std::cout); + + } + // CTRACE("utvpi", ok, tout << "validation success: " << mk_pp(e, get_manager()) << "\n";); + SASSERT(ok); + } + } + + template + bool theory_utvpi::eval(expr* e) { + expr* e1, *e2; + if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) { + return eval_num(e1) <= eval_num(e2); + } + if (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + return eval_num(e1) < eval_num(e2); + } + if (get_manager().is_eq(e, e1, e2)) { + return eval_num(e1) == eval_num(e2); + } + TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); + return false; + } + + template + rational theory_utvpi::eval_num(expr* e) { + rational r; + expr* e1, *e2; + if (a.is_numeral(e, r)) { + return r; + } + if (a.is_sub(e, e1, e2)) { + return eval_num(e1) - eval_num(e2); + } + if (a.is_add(e)) { + r.reset(); + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + r += eval_num(to_app(e)->get_arg(i)); + } + return r; + } + if (a.is_mul(e)) { + r = rational(1); + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + r *= eval_num(to_app(e)->get_arg(i)); + } + return r; + } + if (a.is_uminus(e, e1)) { + return -eval_num(e1); + } + if (a.is_to_real(e, e1)) { + return eval_num(e1); + } + if (is_uninterp_const(e)) { + return mk_value(mk_var(e), a.is_int(e)); + } + TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); + UNREACHABLE(); + return rational(0); + } + + + template + rational theory_utvpi::mk_value(th_var v, bool is_int) { + SASSERT(v != null_theory_var); + numeral val1 = m_graph.get_assignment(to_var(v)); + numeral val2 = m_graph.get_assignment(neg(to_var(v))); + numeral val = val1 - val2; + rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); + num = num/rational(2); + SASSERT(!is_int || num.is_int()); + TRACE("utvpi", + expr* n = get_enode(v)->get_owner(); + tout << mk_pp(n, get_manager()) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";); + + return num; + } + + template + model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + bool is_int = a.is_int(n->get_owner()); + rational num = mk_value(v, is_int); + TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); + return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int)); + } + + /** + \brief Compute numeral values for the infinitesimals to satisfy the inequalities. + */ + + template + void theory_utvpi::compute_delta() { + m_delta = rational(1); + unsigned sz = m_graph.get_num_edges(); + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph.is_enabled(i)) { + continue; + } + numeral w = m_graph.get_weight(i); + numeral tgt = m_graph.get_assignment(m_graph.get_target(i)); + numeral src = m_graph.get_assignment(m_graph.get_source(i)); + numeral b = tgt - src - w; + SASSERT(b.is_nonpos()); + rational eps_r = b.get_infinitesimal(); + + // Given: b <= 0 + // suppose that 0 < b.eps + // then we have 0 > b.num + // then delta must ensure: + // 0 >= b.num + delta*b.eps + // <=> + // -b.num/b.eps >= delta + if (eps_r.is_pos()) { + rational num_r = -b.get_rational(); + SASSERT(num_r.is_pos()); + rational new_delta = num_r/eps_r; + if (new_delta < m_delta) { + m_delta = new_delta; + } + } + } + } + + + +}; + +#endif + diff --git a/src/tactic/aig/aig.cpp b/src/tactic/aig/aig.cpp index 997ec642e..9fdce96e9 100644 --- a/src/tactic/aig/aig.cpp +++ b/src/tactic/aig/aig.cpp @@ -869,11 +869,7 @@ struct aig_manager::imp { void mk_ite(aig * n) { aig_lit c, t, e; -#ifdef Z3DEBUG - bool ok = -#endif - m.is_ite(n, c, t, e); - SASSERT(ok); + VERIFY(m.is_ite(n, c, t, e)); if (c.is_inverted()) { c.invert(); std::swap(t, e); diff --git a/src/tactic/aig/aig.h b/src/tactic/aig/aig.h index 96aa59ee6..c8befd9b2 100644 --- a/src/tactic/aig/aig.h +++ b/src/tactic/aig/aig.h @@ -70,7 +70,6 @@ public: void max_sharing(aig_ref & r); void to_formula(aig_ref const & r, expr_ref & result); void to_formula(aig_ref const & r, goal & result); - void to_cnf(aig_ref const & r, goal & result); void display(std::ostream & out, aig_ref const & r) const; void display_smt2(std::ostream & out, aig_ref const & r) const; unsigned get_num_aigs() const; diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 831efa087..2c2afab6f 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -371,6 +371,12 @@ struct ctx_simplify_tactic::imp { if (!modified) { r = t; } + else if (new_new_args.empty()) { + r = OR?m.mk_false():m.mk_true(); + } + else if (new_new_args.size() == 1) { + r = new_new_args[0]; + } else { std::reverse(new_new_args.c_ptr(), new_new_args.c_ptr() + new_new_args.size()); m_mk_app(t->get_decl(), new_new_args.size(), new_new_args.c_ptr(), r); diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 84a1efaff..363cf1df8 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -38,6 +38,17 @@ fpa2bv_converter::fpa2bv_converter(ast_manager & m) : fpa2bv_converter::~fpa2bv_converter() { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); + dec_ref_map_key_values(m, m_uf2bvuf); + + obj_map::iterator it = m_uf23bvuf.begin(); + obj_map::iterator end = m_uf23bvuf.end(); + for (; it != end; ++it) { + m.dec_ref(it->m_key); + m.dec_ref(it->m_value.f_sgn); + m.dec_ref(it->m_value.f_sig); + m.dec_ref(it->m_value.f_exp); + } + m_uf23bvuf.reset(); } void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { @@ -150,6 +161,104 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { } } +void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) { + SASSERT(is_float(srt)); + unsigned ebits = m_util.get_ebits(srt); + unsigned sbits = m_util.get_sbits(srt); + + expr_ref sgn(m), s(m), e(m); + + sgn = m.mk_var(base_inx, m_bv_util.mk_sort(1)); + s = m.mk_var(base_inx + 1, m_bv_util.mk_sort(sbits-1)); + e = m.mk_var(base_inx + 2, m_bv_util.mk_sort(ebits)); + + mk_triple(sgn, s, e, result); +} + +void fpa2bv_converter::mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +{ + TRACE("fpa2bv_dbg", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); + SASSERT(f->get_arity() == num); + + expr_ref_buffer new_args(m); + + for (unsigned i = 0; i < num ; i ++) + if (is_float(args[i])) + { + expr * sgn, * sig, * exp; + split(args[i], sgn, sig, exp); + new_args.push_back(sgn); + new_args.push_back(sig); + new_args.push_back(exp); + } + else + new_args.push_back(args[i]); + + func_decl * fd; + func_decl_triple fd3; + if (m_uf2bvuf.find(f, fd)) { + result = m.mk_app(fd, new_args.size(), new_args.c_ptr()); + } + else if (m_uf23bvuf.find(f, fd3)) + { + expr_ref a_sgn(m), a_sig(m), a_exp(m); + a_sgn = m.mk_app(fd3.f_sgn, new_args.size(), new_args.c_ptr()); + a_sig = m.mk_app(fd3.f_sig, new_args.size(), new_args.c_ptr()); + a_exp = m.mk_app(fd3.f_exp, new_args.size(), new_args.c_ptr()); + mk_triple(a_sgn, a_sig, a_exp, result); + } + else { + sort_ref_buffer new_domain(m); + + for (unsigned i = 0; i < f->get_arity() ; i ++) + if (is_float(f->get_domain()[i])) + { + new_domain.push_back(m_bv_util.mk_sort(1)); + new_domain.push_back(m_bv_util.mk_sort(m_util.get_sbits(f->get_domain()[i])-1)); + new_domain.push_back(m_bv_util.mk_sort(m_util.get_ebits(f->get_domain()[i]))); + } + else + new_domain.push_back(f->get_domain()[i]); + + if (!is_float(f->get_range())) + { + func_decl * fbv = m.mk_func_decl(f->get_name(), new_domain.size(), new_domain.c_ptr(), f->get_range(), *f->get_info()); + TRACE("fpa2bv_dbg", tout << "New UF func_decl : " << mk_ismt2_pp(fbv, m) << std::endl; ); + m_uf2bvuf.insert(f, fbv); + m.inc_ref(f); + m.inc_ref(fbv); + result = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); + } + else + { + string_buffer<> name_buffer; + name_buffer.reset(); name_buffer << f->get_name() << ".sgn"; + func_decl * f_sgn = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(1)); + name_buffer.reset(); name_buffer << f->get_name() << ".sig"; + func_decl * f_sig = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_sbits(f->get_range())-1)); + name_buffer.reset(); name_buffer << f->get_name() << ".exp"; + func_decl * f_exp = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_ebits(f->get_range()))); + expr_ref a_sgn(m), a_sig(m), a_exp(m); + a_sgn = m.mk_app(f_sgn, new_args.size(), new_args.c_ptr()); + a_sig = m.mk_app(f_sig, new_args.size(), new_args.c_ptr()); + a_exp = m.mk_app(f_exp, new_args.size(), new_args.c_ptr()); + TRACE("fpa2bv_dbg", tout << "New UF func_decls : " << std::endl; + tout << mk_ismt2_pp(f_sgn, m) << std::endl; + tout << mk_ismt2_pp(f_sig, m) << std::endl; + tout << mk_ismt2_pp(f_exp, m) << std::endl; ); + m_uf23bvuf.insert(f, func_decl_triple(f_sgn, f_sig, f_exp)); + m.inc_ref(f); + m.inc_ref(f_sgn); + m.inc_ref(f_sig); + m.inc_ref(f_exp); + mk_triple(a_sgn, a_sig, a_exp, result); + } + } + + TRACE("fpa2bv_dbg", tout << "UF result: " << mk_ismt2_pp(result, m) << std::endl; ); + + SASSERT(is_well_sorted(m, result)); +} void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); @@ -1939,7 +2048,8 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef _DEBUG - // return; + return; + // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); extra_assertions.push_back(m.mk_eq(new_e, e)); @@ -2293,6 +2403,25 @@ void fpa2bv_model_converter::display(std::ostream & out) { unsigned indent = n.size() + 4; out << mk_ismt2_pp(it->m_value, m, indent) << ")"; } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value.f_sgn, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_sig, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_exp, m, indent) << " ; " << + ")"; + } out << ")" << std::endl; } @@ -2413,6 +2542,20 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { } } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) + seen.insert(it->m_value); + + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) + { + seen.insert(it->m_value.f_sgn); + seen.insert(it->m_value.f_sig); + seen.insert(it->m_value.f_exp); + } + fu.fm().del(fp_val); // Keep all the non-float constants. @@ -2420,7 +2563,7 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { for (unsigned i = 0; i < sz; i++) { func_decl * c = bv_mdl->get_constant(i); - if (!seen.contains(c)) + if (!seen.contains(c)) float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); } @@ -2429,7 +2572,8 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { for (unsigned i = 0; i < sz; i++) { func_decl * c = bv_mdl->get_function(i); - float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); + if (!seen.contains(c)) + float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); } sz = bv_mdl->get_num_uninterpreted_sorts(); @@ -2443,6 +2587,8 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv) { - return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv); + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) { + return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf, uf23bvuf); } diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index e5d546763..2a68342f8 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -31,23 +31,39 @@ typedef enum { BV_RM_TIES_TO_AWAY=0, BV_RM_TIES_TO_EVEN=1, BV_RM_TO_NEGATIVE=2, class fpa2bv_model_converter; +struct func_decl_triple { + func_decl_triple () { f_sgn = 0; f_sig = 0; f_exp = 0; } + func_decl_triple (func_decl * sgn, func_decl * sig, func_decl * exp) + { + f_sgn = sgn; + f_sig = sig; + f_exp = exp; + } + func_decl * f_sgn; + func_decl * f_sig; + func_decl * f_exp; + }; + class fpa2bv_converter { ast_manager & m; basic_simplifier_plugin m_simp; float_util m_util; - mpf_manager & m_mpf_manager; + mpf_manager & m_mpf_manager; unsynch_mpz_manager & m_mpz_manager; bv_util m_bv_util; float_decl_plugin * m_plugin; obj_map m_const2bv; obj_map m_rm_const2bv; + obj_map m_uf2bvuf; + obj_map m_uf23bvuf; public: fpa2bv_converter(ast_manager & m); ~fpa2bv_converter(); float_util & fu() { return m_util; } + bv_util & bu() { return m_bv_util; } bool is_float(sort * s) { return m_util.is_float(s); } bool is_float(expr * e) { return is_app(e) && m_util.is_float(to_app(e)->get_decl()->get_range()); } @@ -66,8 +82,10 @@ public: void mk_rounding_mode(func_decl * f, expr_ref & result); void mk_value(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_const(func_decl * f, expr_ref & result); + void mk_const(func_decl * f, expr_ref & result); void mk_rm_const(func_decl * f, expr_ref & result); + void mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_var(unsigned base_inx, sort * srt, expr_ref & result); void mk_plus_inf(func_decl * f, expr_ref & result); void mk_minus_inf(func_decl * f, expr_ref & result); @@ -100,10 +118,12 @@ public: void mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); obj_map const & const2bv() const { return m_const2bv; } obj_map const & rm_const2bv() const { return m_rm_const2bv; } + obj_map const & uf2bvuf() const { return m_uf2bvuf; } + obj_map const & uf23bvuf() const { return m_uf23bvuf; } void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector extra_assertions; @@ -148,10 +168,14 @@ class fpa2bv_model_converter : public model_converter { ast_manager & m; obj_map m_const2bv; obj_map m_rm_const2bv; + obj_map m_uf2bvuf; + obj_map m_uf23bvuf; public: fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv) : + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) : m(m) { // Just create a copy? for (obj_map::iterator it = const2bv.begin(); @@ -170,6 +194,21 @@ public: m.inc_ref(it->m_key); m.inc_ref(it->m_value); } + for (obj_map::iterator it = uf2bvuf.begin(); + it != uf2bvuf.end(); + it++) + { + m_uf2bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = uf23bvuf.begin(); + it != uf23bvuf.end(); + it++) + { + m_uf23bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + } } virtual ~fpa2bv_model_converter() { @@ -202,6 +241,8 @@ protected: model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv); + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf); #endif diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index 4b3525a32..3398874f5 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -29,6 +29,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr_ref_vector m_out; fpa2bv_converter & m_conv; + sort_ref_vector m_bindings; + expr_ref_vector m_mappings; + unsigned long long m_max_memory; unsigned m_max_steps; @@ -38,7 +41,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { fpa2bv_rewriter_cfg(ast_manager & m, fpa2bv_converter & c, params_ref const & p): m_manager(m), m_out(m), - m_conv(c) { + m_conv(c), + m_bindings(m), + m_mappings(m) { updt_params(p); // We need to make sure that the mananger has the BV plugin loaded. symbol s_bv("bv"); @@ -53,6 +58,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { m_out.finalize(); } + void reset() { + } + void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); @@ -136,21 +144,112 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { throw tactic_exception("NYI"); } } + + if (f->get_family_id() == null_family_id) + { + bool is_float_uf = m_conv.is_float(f->get_range()); + unsigned i = 0; + while (!is_float_uf && i < num) + { + is_float_uf = m_conv.is_float(f->get_domain()[i]); + i++; + } + + if (is_float_uf) + { + m_conv.mk_uninterpreted_function(f, num, args, result); + return BR_DONE; + } + } return BR_FAILED; } + bool pre_visit(expr * t) + { + TRACE("fpa2bv", tout << "pre_visit: " << mk_ismt2_pp(t, m()) << std::endl;); + + if (is_quantifier(t)) { + quantifier * q = to_quantifier(t); + TRACE("fpa2bv", tout << "pre_visit quantifier [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m()) << std::endl;); + sort_ref_vector new_bindings(m_manager); + for (unsigned i = 0 ; i < q->get_num_decls(); i++) + new_bindings.push_back(q->get_decl_sort(i)); + SASSERT(new_bindings.size() == q->get_num_decls()); + m_bindings.append(new_bindings); + m_mappings.resize(m_bindings.size(), 0); + } + return true; + } + bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { - return false; + unsigned curr_sz = m_bindings.size(); + SASSERT(old_q->get_num_decls() <= curr_sz); + unsigned num_decls = old_q->get_num_decls(); + unsigned old_sz = curr_sz - num_decls; + string_buffer<> name_buffer; + ptr_buffer new_decl_sorts; + sbuffer new_decl_names; + for (unsigned i = 0; i < num_decls; i++) { + symbol const & n = old_q->get_decl_name(i); + sort * s = old_q->get_decl_sort(i); + if (m_conv.is_float(s)) { + unsigned ebits = m_conv.fu().get_ebits(s); + unsigned sbits = m_conv.fu().get_sbits(s); + name_buffer.reset(); + name_buffer << n << ".bv"; + new_decl_names.push_back(symbol(name_buffer.c_str())); + new_decl_sorts.push_back(m_conv.bu().mk_sort(sbits+ebits)); + } + else { + new_decl_sorts.push_back(s); + new_decl_names.push_back(n); + } + } + result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), + new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), + old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); + result_pr = 0; + m_bindings.shrink(old_sz); + m_mappings.shrink(old_sz); + TRACE("fpa2bv", tout << "reduce_quantifier[" << old_q->get_depth() << "]: " << + mk_ismt2_pp(old_q->get_expr(), m()) << std::endl << + " new body: " << mk_ismt2_pp(new_body, m()) << std::endl; + tout << "result = " << mk_ismt2_pp(result, m()) << std::endl;); + return true; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { - return false; + if (t->get_idx() >= m_bindings.size()) + return false; + unsigned inx = m_bindings.size() - t->get_idx() - 1; + + expr_ref new_exp(m()); + sort * s = t->get_sort(); + if (m_conv.is_float(s)) + { + expr_ref new_var(m()); + unsigned ebits = m_conv.fu().get_ebits(s); + unsigned sbits = m_conv.fu().get_sbits(s); + new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(sbits+ebits)); + m_conv.mk_triple(m_conv.bu().mk_extract(sbits+ebits-1, sbits+ebits-1, new_var), + m_conv.bu().mk_extract(sbits+ebits-2, ebits, new_var), + m_conv.bu().mk_extract(ebits-1, 0, new_var), + new_exp); + } + else + new_exp = m().mk_var(t->get_idx(), s); + m_mappings[inx] = new_exp; + + result = m_mappings[inx].get(); + result_pr = 0; + TRACE("fpa2bv", tout << "reduce_var: " << mk_ismt2_pp(t, m()) << " -> " << mk_ismt2_pp(result, m()) << std::endl;); + return true; } }; diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index a90ff9317..9bb35eea6 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -90,7 +90,7 @@ class fpa2bv_tactic : public tactic { } if (g->models_enabled()) - mc = mk_fpa2bv_model_converter(m, m_conv.const2bv(), m_conv.rm_const2bv()); + mc = mk_fpa2bv_model_converter(m, m_conv.const2bv(), m_conv.rm_const2bv(), m_conv.uf2bvuf(), m_conv.uf23bvuf()); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 55512f677..ae79446e3 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -94,7 +94,6 @@ public: smt_strategic_solver_factory(symbol const & logic):m_logic(logic) {} virtual ~smt_strategic_solver_factory() {} - virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { symbol l; if (m_logic != symbol::null) diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp index d7e55379a..393a40603 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -67,7 +67,7 @@ class quasi_macros_tactic : public tactic { simp.register_plugin(bvsimp); macro_manager mm(m_manager, simp); - quasi_macros qm(m_manager, mm, *bsimp, simp); + quasi_macros qm(m_manager, mm, simp); bool more = true; expr_ref_vector forms(m_manager), new_forms(m_manager); diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/tactic/ufbv/ufbv_rewriter.cpp index e4ed2ec01..40fdf5e3e 100644 --- a/src/tactic/ufbv/ufbv_rewriter.cpp +++ b/src/tactic/ufbv/ufbv_rewriter.cpp @@ -442,11 +442,10 @@ expr * ufbv_rewriter::rewrite(expr * n) { } class ufbv_rewriter::add_back_idx_proc { - ast_manager & m_manager; back_idx_map & m_back_idx; expr * m_expr; public: - add_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {} + add_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { @@ -469,11 +468,10 @@ public: }; class ufbv_rewriter::remove_back_idx_proc { - ast_manager & m_manager; back_idx_map & m_back_idx; expr * m_expr; public: - remove_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {} + remove_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { @@ -511,7 +509,7 @@ void ufbv_rewriter::reschedule_processed(func_decl * f) { expr * p = *sit; // remove p from m_processed and m_back_idx m_processed.remove(p); - remove_back_idx_proc proc(m_manager, m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. + remove_back_idx_proc proc(m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. for_each_expr(proc, p); // insert p into m_todo m_todo.push_back(p); @@ -619,7 +617,7 @@ void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { // remove d from m_back_idx // just remember it here, because otherwise it and/or esit might become invalid? // to_remove.insert(d); - remove_back_idx_proc proc(m_manager, m_back_idx, d); + remove_back_idx_proc proc(m_back_idx, d); for_each_expr(proc, d); // insert d into m_todo m_todo.push_back(d); @@ -674,7 +672,7 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * // insert n' into m_processed m_processed.insert(np); // update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx) - add_back_idx_proc proc(m_manager, m_back_idx, np); + add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } else { // np is a demodulator that allows us to replace 'large' with 'small'. @@ -702,7 +700,7 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * insert_fwd_idx(large, small, to_quantifier(np)); // update m_back_idx - add_back_idx_proc proc(m_manager, m_back_idx, np); + add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } } diff --git a/src/test/api.cpp b/src/test/api.cpp index b5aff7935..a09371f01 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -450,7 +450,7 @@ void test_bvneg() { void tst_api() { test_apps(); test_bvneg(); - bv_invariant(); + // bv_invariant(); } #else void tst_api() { diff --git a/src/test/bv_simplifier_plugin.cpp b/src/test/bv_simplifier_plugin.cpp index 635143021..77e57f1f5 100644 --- a/src/test/bv_simplifier_plugin.cpp +++ b/src/test/bv_simplifier_plugin.cpp @@ -4,7 +4,14 @@ #include "reg_decl_plugins.h" class tst_bv_simplifier_plugin_cls { + class mgr { + public: + mgr(ast_manager& m) { + reg_decl_plugins(m); + } + }; ast_manager m_manager; + mgr m_mgr; bv_simplifier_params m_bv_params; basic_simplifier_plugin m_bsimp; arith_util m_arith; @@ -75,12 +82,13 @@ class tst_bv_simplifier_plugin_cls { public: tst_bv_simplifier_plugin_cls() : + m_mgr(m_manager), m_bsimp(m_manager), m_arith(m_manager), m_simp(m_manager, m_bsimp, m_bv_params), m_bv_util(m_manager), - m_fid(m_manager.mk_family_id("bv")) { - reg_decl_plugins(m_manager); + m_fid(0) { + m_fid = m_manager.mk_family_id("bv"); } ~tst_bv_simplifier_plugin_cls() {} @@ -249,7 +257,9 @@ public: ar = m_manager.mk_app(m_fid, OP_BASHR, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); - SASSERT((sa >> b) == i32(e.get())); + + std::cout << "compare: " << sa << " >> " << b << " = " << (sa >> b) << " with " << i32(e.get()) << "\n"; + SASSERT(b >= 32 || ((sa >> b) == i32(e.get()))); if (b != 0) { ar = m_manager.mk_app(m_fid, OP_BSDIV, 2, e1e2); diff --git a/src/test/diff_logic.cpp b/src/test/diff_logic.cpp index f577ebfd6..70345c2d6 100644 --- a/src/test/diff_logic.cpp +++ b/src/test/diff_logic.cpp @@ -164,9 +164,9 @@ static void tst3() { } void tst_diff_logic() { - tst1(); - tst2(); - tst3(); + //tst1(); + //tst2(); + //tst3(); } #else void tst_diff_logic() { diff --git a/src/test/dl_query.cpp b/src/test/dl_query.cpp index 4dc770056..d0abfbac0 100644 --- a/src/test/dl_query.cpp +++ b/src/test/dl_query.cpp @@ -48,6 +48,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, bool use_magic_sets) { dl_decl_util decl_util(m); + random_gen ran(0); context ctx_q(m, fparams); params.set_bool("magic_sets_for_queries", use_magic_sets); @@ -59,7 +60,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, } relation_manager & rel_mgr_q = ctx_b.get_rel_context().get_rmanager(); - decl_set out_preds = ctx_b.get_output_predicates(); + decl_set out_preds = ctx_b.get_rules().get_output_predicates(); decl_set::iterator it = out_preds.begin(); decl_set::iterator end = out_preds.end(); for(; it!=end; ++it) { @@ -86,7 +87,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, warning_msg("cannot get sort size"); return; } - uint64 num = rand()%sort_sz; + uint64 num = ran()%sort_sz; app * el_b = decl_util.mk_numeral(num, sig_b[col]); f_b.push_back(el_b); app * el_q = decl_util.mk_numeral(num, sig_q[col]); @@ -112,7 +113,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, table_base::iterator fit = table_b.begin(); table_base::iterator fend = table_b.end(); for(; fit!=fend; ++fit) { - if(rand()%std::max(1u,table_sz/test_count)!=0) { + if(ran()%std::max(1u,table_sz/test_count)!=0) { continue; } fit->get_fact(tf); @@ -127,6 +128,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, void dl_query_test_wpa(smt_params & fparams, params_ref& params) { params.set_bool("magic_sets_for_queries", true); ast_manager m; + random_gen ran(0); reg_decl_plugins(m); arith_util arith(m); const char * problem_dir = "C:\\tvm\\src\\z3_2\\debug\\test\\w0.datalog"; @@ -151,8 +153,8 @@ void dl_query_test_wpa(smt_params & fparams, params_ref& params) { TRUSTME( ctx.try_get_sort_constant_count(var_sort, var_sz) ); for(unsigned attempt=0; attemptmk_empty(sig); } -static datalog::table_base* mk_skip_table(datalog::relation_manager& m, datalog::table_signature& sig) { - datalog::table_plugin * p = m.get_table_plugin(symbol("skip")); - SASSERT(p); - return p->mk_empty(sig); -} - - static void test_table(mk_table_fn mk_table) { datalog::table_signature sig; sig.push_back(2); @@ -96,13 +88,9 @@ void test_dl_bitvector_table() { test_table(mk_bv_table); } -void test_dl_skip_table() { - test_table(mk_skip_table); -} void tst_dl_table() { test_dl_bitvector_table(); - test_dl_skip_table(); } #else void tst_dl_table() { diff --git a/src/test/ext_numeral.cpp b/src/test/ext_numeral.cpp index c2f337d68..0e2b691c9 100644 --- a/src/test/ext_numeral.cpp +++ b/src/test/ext_numeral.cpp @@ -381,7 +381,7 @@ static void tst3() { { std::ostringstream buffer; display(buffer, m, a, EN_PLUS_INFINITY); - SASSERT(buffer.str() == "oo"); + SASSERT(buffer.str() == "+oo"); } { std::ostringstream buffer; diff --git a/src/test/fdd.cpp b/src/test/fdd.cpp deleted file mode 100644 index 2a0079456..000000000 --- a/src/test/fdd.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "fdd.h" - -static void test1() { - fdd::manager m; - - m.reset(2); - int64 keys1[2] = { 1, 2 }; - m.insert(keys1); - m.display(std::cout << "test1\n"); -} - -static void test2() { - fdd::manager m; - - m.reset(2); - int64 keys2[2] = { 2, 1 }; - m.insert(keys2); - m.display(std::cout << "test2\n"); - -} - -static void test3() { - fdd::manager m; - - m.reset(2); - int64 keys1[2] = { 1, 2 }; - int64 keys2[2] = { 2, 1 }; - m.insert(keys1); - m.insert(keys2); - m.display(std::cout << "test3\n"); -} - -static void test4() { - fdd::manager m; - - std::cout << "test4\n"; - - m.reset(2); - int64 keys1[2] = { 1, 2 }; - int64 keys2[2] = { 2, 1 }; - int64 keys3[2] = { 1, 1 }; - int64 keys4[2] = { 2, 2 }; - int64 keys5[2] = { 2, 3 }; - int64 keys6[2] = { 3, 1 }; - int64 keys7[2] = { 3, 4 }; - m.insert(keys1); - m.insert(keys2); - std::cout << m.find_le(keys1) << "\n"; - std::cout << m.find_le(keys2) << "\n"; - std::cout << m.find_le(keys3) << "\n"; - std::cout << m.find_le(keys4) << "\n"; - std::cout << m.find_le(keys5) << "\n"; - std::cout << m.find_le(keys6) << "\n"; - std::cout << m.find_le(keys7) << "\n"; - - SASSERT(m.find_le(keys1)); - SASSERT(m.find_le(keys2)); - SASSERT(!m.find_le(keys3)); - SASSERT(m.find_le(keys4)); - SASSERT(m.find_le(keys5)); - SASSERT(m.find_le(keys6)); - SASSERT(m.find_le(keys7)); -} - -static void test5() { - fdd::manager m; - - std::cout << "test5\n"; - - m.reset(2); - int64 keys1[2] = { 1, 2 }; - int64 keys2[2] = { 2, 1 }; - m.insert(keys1); - m.insert(keys2); - m.insert(keys2); - - m.display(std::cout); - -} - -void tst_fdd() { - test1(); - test2(); - test3(); - test4(); - test5(); -} diff --git a/src/test/imdd.cpp b/src/test/imdd.cpp deleted file mode 100644 index bfaca274c..000000000 --- a/src/test/imdd.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - imdd.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-14. - -Revision History: - ---*/ -#include"imdd.h" - -#if !defined(_AMD64_) && defined(Z3DEBUG) - -static void tst0() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m), d4(m); - d1 = m.mk_empty(1); - d2 = m.mk_empty(1); - m.insert_dupdt(d1, 10, 20); - m.insert_dupdt(d1, 31, 50); - m.insert_dupdt(d2, 1, 5); - m.insert_dupdt(d2, 11, 13); - m.mk_product(d1, d2, d4); - m.mk_product(d4, d2, d4); - m.mk_product_dupdt(d1, d2); - std::cout << "d1:\n" << mk_ll_pp(d1, m) << "\n-------\n"; - m.mk_product_dupdt(d1, d2); - std::cout << "d4:\n" << mk_ll_pp(d4, m) << "\nd1:\n" << mk_ll_pp(d1, m) << "\nd2:\n" << mk_ll_pp(d2, m) << "\n"; - std::cout << d1 << "\n" << d2 << "\n"; - m.mk_product_dupdt(d1, d1); - std::cout << "d1 X d1:\n" << mk_ll_pp(d1, m) << "\n"; -} - -static void add_triple(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, unsigned l3, unsigned u3, - bool destructive = false, bool memoize = true) { - unsigned lowers[3] = {l1, l2, l3}; - unsigned uppers[3] = {u1, u2, u3}; - if (destructive) - m.add_facts_dupdt(d, 3, lowers, uppers, memoize); - else - m.add_facts(d, d, 3, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void add_pair(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, bool destructive = false, bool memoize = true) { - unsigned lowers[2] = {l1, l2}; - unsigned uppers[2] = {u1, u2}; - if (destructive) - m.add_facts_dupdt(d, 2, lowers, uppers, memoize); - else - m.add_facts(d, d, 2, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void add_some_facts(imdd_manager & m, imdd_ref & d, bool destructive = false, bool memoize = true) { - std::cout << "destructive: " << destructive << ", memoize: " << memoize << std::endl; - add_triple(m, d, 1, 10, 3, 3, 0, 100, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - SASSERT(m.contains(d, 2, 3, 20)); - SASSERT(!m.contains(d, 2, 4, 20)); - SASSERT(!m.contains(d, 2, 3, 200)); - SASSERT(!m.contains(d, 0, 3, 200)); - SASSERT(m.contains(d,1,3,0)); - add_triple(m, d, 3, 6, 3, 4, 7, 101, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - add_triple(m, d, 3, 6, 2, 2, 7, 101, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - add_triple(m, d, 3, 6, 5, 6, 7, 101, destructive, memoize); - SASSERT(m.contains(d, 2, 3, 20)); - std::cout << mk_ll_pp(d, m) << std::endl; - SASSERT(!m.contains(d, 2, 4, 20)); - SASSERT(m.contains(d, 3, 4, 20)); - SASSERT(!m.contains(d, 2, 3, 200)); - SASSERT(!m.contains(d, 0, 3, 200)); - SASSERT(m.contains(d,1,3,0)); -} - -static void tst1() { - std::cout << "--------------------------------\n"; - imdd_manager m; - { - imdd_ref d(m); - d = m.mk_empty(3); - add_some_facts(m, d); - } - { - imdd_ref d(m); - d = m.mk_empty(3); - add_some_facts(m, d, true, false); - m.defrag(d); - std::cout << mk_ll_pp(d, m) << "\n"; - } -} - -static void tst2() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 10, 20, 11, 21, 12, 22); - add_triple(m, d1, 30, 40, 31, 41, 32, 42); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_union(d1, d2, d3); - SASSERT(m.subsumes(d3, d1)); - SASSERT(m.subsumes(d3, d2)); - SASSERT(!m.subsumes(d1, d3)); - SASSERT(!m.subsumes(d2, d3)); - std::cout << "d3: " << d3.get() << "\n" << mk_ll_pp(d3, m) << "\n"; - m.mk_union_dupdt(d1, d2, false); - std::cout << "d1: " << d1.get() << "\n" << mk_ll_pp(d1, m) << "\n"; - SASSERT(m.is_equal(d1, d3)); - SASSERT(!m.is_equal(d2, d3)); - SASSERT(!m.is_equal(d2, d1)); - std::cout << "memory(d1): " << m.memory(d1) << "\n"; -} - -static void tst3() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - d1 = m.mk_empty(3); - unsigned mins[3] = {0,0,0}; - unsigned maxs[3] = {127, 511, 255}; - - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << d1 << "\n"; - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << d1 << "\n"; - SASSERT(d1->empty()); - - d1 = m.mk_empty(3); - add_triple(m, d1, 10, 20, 11, 21, 12, 22); - add_triple(m, d1, 30, 40, 31, 41, 32, 42); - std::cout << d1 << "\n"; - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << mk_ll_pp(d1,m) << "\n"; - m.mk_filter_equal(d1, d1, 1, 15); - std::cout << "after selecting second column = 15\n" << mk_ll_pp(d1,m) << "\n"; -} - -static void tst4() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 1, 2, 2, 5, 4, 4); - add_triple(m, d1, 3, 4, 3, 4, 2, 5); - std::cout << "testing iterator:\n"; - imdd_manager::iterator it = m.begin(d1); - imdd_manager::iterator end = m.end(d1); - for (; it != end; ++it) { - unsigned * tuple = *it; - std::cout << "["; - for (unsigned i = 0; i < d1->get_arity(); i++) { - if (i > 0) - std::cout << ", "; - std::cout << tuple[i]; - } - std::cout << "]\n"; - } -} - -static void tst5() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - std::cout.flush(); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - std::cout << mk_ll_pp(d1,m) << std::endl; - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - std::cout << mk_ll_pp(d1,m) << std::endl; - unsigned vals[3] = {6, 8, 3}; - SASSERT(!m.contains(d1, 3, vals)); - add_triple(m, d1, 6, 25, 8, 30, 14, 50); - std::cout << mk_ll_pp(d1,m) << std::endl; - SASSERT(!m.contains(d1, 3, vals)); - unsigned vars[2] = {0, 2}; - d2 = d1; - d3 = d1; - m.mk_filter_identical(d1, d1, 2, vars); - vars[1] = 1; - std::cout << "d1:\n" << mk_ll_pp(d1,m) << "\n"; - m.mk_filter_identical(d2, d2, 2, vars); - std::cout << "d2:\n" << mk_ll_pp(d2,m) << "\n"; - vars[0] = 1; - vars[1] = 2; - m.mk_filter_identical(d3, d3, 2, vars); - std::cout << "d3:\n" << mk_ll_pp(d3,m) << "\n"; -} - -static void add_5tuple(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, unsigned l3, unsigned u3, - unsigned l4, unsigned u4, unsigned l5, unsigned u5, bool destructive = false, bool memoize = true) { - unsigned lowers[5] = {l1, l2, l3, l4, l5}; - unsigned uppers[5] = {u1, u2, u3, u4, u5}; - if (destructive) - m.add_facts_dupdt(d, 5, lowers, uppers, memoize); - else - m.add_facts(d, d, 5, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void tst6() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - std::cout.flush(); - d1 = m.mk_empty(5); - // TODO: make a more complicated mk_filter_identical example -} - -static void tst7() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << "mk_project\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - unsigned vars[1] = {1}; - m.mk_project(d2, d3, 1, vars); - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst8() { - std::cout << "--------------------------------\n"; - // enable_trace("mk_swap_bug"); - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_swap(d2, d3, 0); - std::cout << "after swap 0<->1\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - m.mk_swap(d2, d3, 1); - std::cout << "after swap 1<->2\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst9() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(5); - add_5tuple(m, d2, 2,2, 3,3, 1, 1, 5, 10, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 2,2, 3,3, 1, 1, 15, 20, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 4,4, 5,5, 1, 1, 5, 10, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 4,4, 5,5, 1, 1, 15, 20, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_swap(d2, d3, 2); - std::cout << "after swap 2<->3\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst10() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - m.add_bounded_var(d1, d2, 0, 66, 72); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - m.add_bounded_var(d1, d3, 1, 64, 73); - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst11() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - add_triple(m, d1, 20, 30, 5, 25, 11, 13); - m.mk_filter_distinct(d1, d2, 1, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(1,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst12() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 1, 10, 5, 25, 10, 13); - m.mk_filter_distinct(d1, d2, 1, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(1,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst13() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 25, 1, 10, 10, 13); - add_triple(m, d1, 5, 25, 20, 30, 10, 13); - m.mk_filter_distinct(d1, d2, 0, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst14() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 25, 1, 10, 10, 13); - add_triple(m, d1, 5, 25, 20, 30, 15, 18); - std::cout << "destructive version\n"; - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_distinct_dupdt(d1, 0, 2); - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d1, m) << "\n"; -} - -static void tst15() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 5, 1, 10, 5, 5); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_distinct(d1, d2, 0, 2); - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst16() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 15, 1, 10, 50, 500); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_disequal(d1, d2, 1, 4); - std::cout << "filter_disequal(var1,4):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst17() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 15, 10, 10, 50, 500); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_disequal(d1, d2, 1, 10); - std::cout << "filter_disequal(var1,10):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst18() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(2); - add_pair(m, d1, 1112, 1290, 1302, 1302); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_swap(d1, d2, 0); - std::cout << "mk_swap 0:\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst19() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << "mk_project_dupdt\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - unsigned vars[1] = {1}; - m.mk_project_dupdt(d2, 1, vars); - std::cout << "new table\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void init(unsigned* v, unsigned a, unsigned b, unsigned c) { - v[0] = a; - v[1] = b; - v[2] = c; -} - -static void tst20() { - std::cout << "--------------------------------\n"; - std::cout << "remove_facts\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - // - // [15, 22] -> #1:{ - // [15, 23] -> {[7, 18]}*$80}*$80 - // [28, 42] -> #2:{ - // [29, 39] -> {[34, 46], [100, 200]}*$160 - // [50, 60] -> {[100, 200]}*$80}*$80}$80 - // - unsigned lowers[3] = {23,1,1}; - unsigned uppers[3] = {24,1,1}; - - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - lowers[0] = 22; - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 0); - init(uppers, 24, 23, 0); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 7); - init(uppers, 24, 23, 18); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (narrow first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 18); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 17); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 19); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 30, 20, 120); - init(uppers, 40, 60, 140); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split second interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - - -static void tst21() { - std::cout << "--------------------------------\n"; - std::cout << "remove_facts\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - // - // [15, 22] -> #1:{ - // [15, 23] -> {[7, 18]}*$80}*$80 - // [28, 42] -> #2:{ - // [29, 39] -> {[34, 46], [100, 200]}*$160 - // [50, 60] -> {[100, 200]}*$80}*$80}$80 - // - unsigned lowers[3] = {23,1,1}; - unsigned uppers[3] = {24,1,1}; - - d3 = d2; - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - d2 = d3; - - lowers[0] = 22; - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 0); - init(uppers, 24, 23, 0); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 7); - init(uppers, 24, 23, 18); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (narrow first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 18); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 17); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 19); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 30, 20, 120); - init(uppers, 40, 60, 140); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split second interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst22() { - std::cout << "--------------------------------\n"; - std::cout << "swap\n"; - imdd_manager m; - imdd_ref d2(m), d3(m), d4(m); - - d2 = m.mk_empty(3); - random_gen rand; - for (unsigned i = 0; i < 130; ++i) { - unsigned a = rand(20); - unsigned b = rand(20); - unsigned c = rand(20); - add_triple(m, d2, a, a, b, b, c, c); - } - std::cout << mk_ll_pp(d2, m) << "\n"; - - m.mk_swap(d2, d3, 0, true); - std::cout << mk_ll_pp(d3, m) << "\n"; - - m.mk_swap(d3, d4, 0, true); - std::cout << mk_ll_pp(d4, m) << "\n"; - SASSERT(m.is_subset(d2, d4)); - SASSERT(m.is_subset(d4, d2)); -} - -void tst_imdd() { - // enable_trace("imdd_add_bug"); - // enable_trace("add_facts_bug"); - enable_trace("mk_distinct_imdd"); - enable_trace("mk_union_core"); - tst22(); - tst0(); - tst1(); - tst2(); - tst3(); - tst4(); - tst5(); - tst6(); - tst7(); - tst19(); - tst8(); - tst9(); - tst10(); - tst11(); - tst12(); - tst13(); - tst14(); - tst15(); - tst16(); - tst17(); - tst18(); - tst20(); - tst21(); - -} - -#else - -void tst_imdd() { -} - -#endif diff --git a/src/test/interval_skip_list.cpp b/src/test/interval_skip_list.cpp deleted file mode 100644 index bda85c86e..000000000 --- a/src/test/interval_skip_list.cpp +++ /dev/null @@ -1,716 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - interval_skip_list.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-05. - -Revision History: - ---*/ - -#include"interval_skip_list.h" -#include"map.h" -#include"vector.h" - -typedef sl_manager_base slist_manager; -template class interval_skip_list, 4, 4, false, slist_manager> >; -typedef interval_skip_list, 4, 4, false, slist_manager> > slist; -typedef u_map u2u_map; -typedef unsigned_isp_set<4, 4, slist_manager> uset; - -static void tst1() { - slist_manager m; - slist l(m); - SASSERT(l.check_invariant()); - SASSERT(l.empty()); - // l.display_physical(std::cout); - l.insert(m, 20, 30, 5); - l.insert(m, 31, 32, 5); - l.insert(m, 18, 19, 5); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.insert(m, 10, 15, 8); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.insert(m, 5, 25, 7); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.insert(m, 23, 27, 5); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst2() { - slist_manager(m); - slist l(m); - for(unsigned i = 0; i < 50; i++) { - l.insert(m,i,i,i*10); - } - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - for (unsigned i = 0; i < 25; i++) { - l.insert(m,i*2,i*2+1,i*20+1); - } - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - for (unsigned i = 0; i < 15; i++) { - l.insert(m,i*3,i*3+2,i*30+1); - } - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - // l.compress(4); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static bool check_interval(slist & l, unsigned k1, unsigned k2, unsigned val) { - for (unsigned i = k1; i <= k2; i++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(l.contains(i, _val) && _val == val); - }); - } - return true; -} - -static bool check_no_interval(slist & l, unsigned k1, unsigned k2) { - for (unsigned i = k1; i <= k2; i++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(!l.contains(i, _val)); - }); - } - return true; -} - -static void tst4() { - slist_manager m; - slist l(m); - l.insert(m, 1, 10, 0); - l.insert(m, 2, 5, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,1,0)); - SASSERT(check_interval(l,2,5,1)); - SASSERT(check_interval(l,6,10,0)); - SASSERT(check_no_interval(l,11,20)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst5() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 8, 20, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,5,0)); - SASSERT(check_no_interval(l,6,7)); - SASSERT(check_interval(l,8,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst6() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 3, 20, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,2,0)); - SASSERT(check_interval(l,3,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst7() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 2, 12, 0); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,12,0)); - SASSERT(check_interval(l,13,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst8() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 100; i++) { - l.insert(m, 10*i, 10*i+5, i); - } - SASSERT(!l.empty()); - l.insert(m, 0, 10000, 0); - SASSERT(!l.has_more_than_k_entries(1)); - // l.display_physical(std::cout); - l.deallocate(m); -} - -struct for_each_contains { - slist const & m_other; - - for_each_contains(slist const & other):m_other(other) {} - - bool operator()(unsigned b, unsigned e, unsigned v) { - for (unsigned i = b; i <= e; i++) { - DEBUG_CODE({ - unsigned _v; - SASSERT(m_other.contains(i, _v)); - SASSERT(v == _v); - }); - } - return true; - } -}; - -static void random_tsts(unsigned num_ops, unsigned max_key, unsigned max_val, unsigned max_interval_size) { - slist_manager m; - slist m1(m); - u2u_map m2; - for (unsigned i = 0; i < num_ops; i++) { - SASSERT(m1.check_invariant()); - TRACE("interval_skip_list", tout << "i: " << i << "\n"; m1.display_physical(tout);); - // std::cout << i << std::endl; - int op = rand()%8; - if (op < 3) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - m1.insert(m, bg, bg+sz, val); - for (unsigned j = bg; j <= bg+sz; j++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(m1.contains(j, _val)); - CTRACE("interval_skip_list", val != _val, tout << "i: " << i << ", j: " << j << ", val: " << val << ", _val: " << _val << "\n"; m1.display_physical(tout);); - SASSERT(val == _val); - TRACE("interval_skip_list", tout << "[insert]: " << j << " -> " << val << "\n";); - }); - m2.insert(j, val); - } - } - else if (op < 4) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - m1.erase(m, bg, bg+sz); - for (unsigned i = bg; i <= bg+sz; i++) { - m2.erase(i); - } - } - else if (op < 5) { - slist m1_copy(m); - m1_copy.copy(m, m1); - for_each_contains proc1(m1); - for_each_contains proc2(m1_copy); - m1.for_each(proc2); - m1_copy.for_each(proc1); - // m1.display_physical(std::cout); - // std::cout << "COPY===>\n"; - // m1_copy->display_physical(std::cout); - m1_copy.deallocate(m); - } - else if (op < 6) { - m1.compress(m, 3); - } - else { - SASSERT(m1.check_invariant()); - u2u_map::iterator it = m2.begin(); - u2u_map::iterator end = m2.end(); - for (; it != end; ++it) { - DEBUG_CODE({ - unsigned _val; - CTRACE("interval_skip_list", !m1.contains(it->m_key, _val), - tout << it->m_key << " -> " << it->m_value << "\n"; - m1.display_physical(tout);); - SASSERT(m1.contains(it->m_key, _val)); - SASSERT(it->m_value == _val); - }); - } - } - } - // m1.display_physical(std::cout); - // m1.compress(4); - // m1.display_physical(std::cout); - m1.deallocate(m); -} - -static void tst9() { - slist_manager m; - slist l(m); - l.insert(m,10,10,1); - l.insert(m,9,9,0); - l.insert(m,8,8,2); - l.insert(m,7,7,3); - l.insert(m,6,8,3); - SASSERT(!l.has_more_than_k_buckets(1)); - SASSERT(check_no_interval(l,0,5)); - SASSERT(check_interval(l,6,8,3)); - SASSERT(check_interval(l,9,9,0)); - SASSERT(check_interval(l,10,10,1)); - SASSERT(check_no_interval(l,11,20)); - l.deallocate(m); -} - -static void tst10() { - slist_manager m; - slist l(m); - l.insert(m,10,10,1); - l.insert(m,13,16,2); - l.insert(m,17,28,3); - l.remove(m,12,19); - SASSERT(l.check_invariant()); - SASSERT(check_no_interval(l,0,9)); - SASSERT(check_interval(l,10,10,1)); - SASSERT(check_no_interval(l,12,19)); - SASSERT(check_interval(l,20,28,3)); - SASSERT(check_no_interval(l,29,100)); - l.remove(m,10,11); - SASSERT(l.check_invariant()); - SASSERT(check_no_interval(l,0,19)); - SASSERT(check_interval(l,20,28,3)); - SASSERT(check_no_interval(l,29,100)); - l.remove(m,0,1000); - SASSERT(l.empty()); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst11() { - slist_manager m; - slist l(m); - l.insert(m,11,20,1); - l.insert(m,21,30,2); - l.insert(m,31,40,3); - l.insert(m,41,50,4); - l.insert(m,51,60,5); - l.compress(m,4); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 25, 26); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,24,2)); - SASSERT(check_no_interval(l,25,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 44,48); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,24,2)); - SASSERT(check_no_interval(l,25,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,43,4)); - SASSERT(check_no_interval(l,44,48)); - SASSERT(check_interval(l,49,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 22,24); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,21,2)); - SASSERT(check_no_interval(l,22,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,43,4)); - SASSERT(check_no_interval(l,44,48)); - SASSERT(check_interval(l,49,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 42,49); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,21,2)); - SASSERT(check_no_interval(l,22,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,41,4)); - SASSERT(check_no_interval(l,42,49)); - SASSERT(check_interval(l,50,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.deallocate(m); -} - -static void tst12() { - slist_manager m; - slist l(m); - l.insert(m,10,10,0); - l.insert(m,9,9,0); - SASSERT(l.check_invariant()); - l.insert(m,8,9,1); - SASSERT(l.check_invariant()); - l.insert(m,7,7,2); - SASSERT(l.check_invariant()); - l.insert(m,6,6,3); - SASSERT(l.check_invariant()); - l.insert(m,4,5,2); - SASSERT(l.check_invariant()); - l.insert(m,3,9,0); - // l.display_physical(std::cout); - l.deallocate(m); -} - -static void tst13() { - slist_manager m; - uset s(m); - s.insert(m, 10, 30); - s.insert(m, 32, 40); - s.display(std::cout); - std::cout << ", mem: " << s.memory() << "\n"; - s.deallocate(m); -} - -struct obj { - unsigned m_val; - unsigned m_ref_count; - void inc_ref() { - m_ref_count++; - } - void dec_ref() { - SASSERT(m_ref_count > 0); - m_ref_count--; - if (m_ref_count == 0) - dealloc(this); - } - obj(unsigned v):m_val(v), m_ref_count(0) { - } -}; - -std::ostream & operator<<(std::ostream & out, obj * o) { - out << o->m_val << "{" << o->m_ref_count << "}"; - return out; -} - -struct obj_slist_manager : public sl_manager_base { - void inc_ref_eh(obj * v) { - v->inc_ref(); - } - - void dec_ref_eh(obj * v) { - v->dec_ref(); - } -}; - -struct inc_ref_functor { - unsigned_vector & refs; - inc_ref_functor(unsigned_vector & r):refs(r) {} - bool operator()(unsigned b, unsigned e, obj * val) { - refs[val->m_val]++; - return true; - } -}; - -template class interval_skip_list, 16, 16, true, obj_slist_manager> >; -typedef interval_skip_list, 16, 16, true, obj_slist_manager> > obj_slist; - -void random_tsts_ref(unsigned num_ops, unsigned num_objs, unsigned max_key, unsigned max_interval_size) { - obj_slist_manager m; - obj_slist l(m); - ptr_vector objs; - unsigned_vector refs; - for (unsigned i = 0; i < num_objs; i++) { - objs.push_back(alloc(obj, i)); - objs.back()->inc_ref(); - refs.push_back(1); - } - - for (unsigned i = 0; i < num_ops; i++) { - SASSERT(l.check_invariant()); - TRACE("interval_skip_list", tout << "i: " << i << "\n"; l.display_physical(tout); tout << "\n";); - int op = rand()%5; - if (op < 3) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - unsigned val = rand() % num_objs; - TRACE("interval_skip_list", tout << "[inserting]: [" << bg << ", " << (bg+sz) << "] -> " << objs[val] << "\n";); - l.insert(m, bg, bg+sz, objs[val]); - SASSERT(objs[val]->m_ref_count > 1); - } - else if (op < 4) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - TRACE("interval_skip_list", tout << "[erasing]: [" << bg << ", " << (bg+sz) << "]\n";); - l.erase(m, bg, bg+sz); - } - else if (op < 5) { - obj_slist l_copy(m); - l_copy.copy(m, l); - TRACE("interval_skip_list", tout << "[copying]\n";); - l_copy.deallocate(m); - TRACE("interval_skip_list", tout << "[deleting copy]\n";); - } - else { - TRACE("interval_skip_list", tout << "[compressing]\n";); - l.compress(m, 3); - } - // check ref-counts - inc_ref_functor proc(refs); - l.for_each(proc); - for (unsigned i = 0; i < num_objs; i++) { - CTRACE("interval_skip_list", refs[i] != objs[i]->m_ref_count, - tout << "i: " << i << ", objs[i]: " << objs[i] << ", refs[i]: " << refs[i] << "\n\n"; - l.display_physical(tout);); - SASSERT(refs[i] == objs[i]->m_ref_count); - refs[i] = 1; - } - } - l.deallocate(m); - for (unsigned i = 0; i < num_objs; i++) { - SASSERT(objs[i]->m_ref_count == 1); - objs[i]->dec_ref(); - } -} - -void tst_ref() { - obj_slist_manager m; - obj_slist l(m); - for (unsigned i = 0; i < 30; i++) { - obj * n = alloc(obj, i); - l.insert(m, i*10, i*10+3, n); - // l.display_physical(std::cout); - // std::cout << "memory: " << l.memory() << "\n"; - } - l.deallocate(m); - -} - -void tst_push_back_aux(slist::push_back_proc & push_back, unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - unsigned prev_key; - - if (push_back.empty()) - prev_key = 0; - else - prev_key = push_back.last_key(); - - for (unsigned i = 0; i < num_ops; i++) { - unsigned next_key = prev_key + 1; - next_key += (rand() % max_sep); - unsigned sz = rand() % max_int; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - push_back(next_key, next_key+sz, val); - SASSERT(!push_back.empty()); - prev_key = push_back.last_key(); - } -} - -void tst_push_back1(unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - slist_manager m; - slist l(m); - slist::push_back_proc push_back(m, l); - - tst_push_back_aux(push_back, num_ops, max_int, max_sep, max_val); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -void tst_push_back2(unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - slist_manager m; - slist l(m); - - // insert some random values before creating push_back functor - for (unsigned i = 0; i < num_ops; i++) { - unsigned next_key = rand() % (num_ops * max_int/2); - unsigned sz = rand() % max_int; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - l.insert(m, next_key, next_key+sz, val); - } - - slist::push_back_proc push_back(m, l); - - tst_push_back_aux(push_back, num_ops, max_int, max_sep, max_val); - - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -void tst_find_geq1() { - slist_manager m; - slist l(m); - l.insert(m, 10, 20, 4); - l.insert(m, 23, 30, 3); - l.insert(m, 40, 45, 10); - l.insert(m, 50, 66, 1); - l.insert(m, 100, 120, 21); - l.insert(m, 140, 200, 2); - slist::iterator it = l.find_geq(22); - SASSERT(it->begin_key() == 23); - it = l.find_geq(42); - SASSERT(it->begin_key() == 40); - it.move_to(130); - SASSERT(it->begin_key() == 140); - it.move_to(400); - SASSERT(it == l.end()); - it = l.find_geq(300); - SASSERT(it == l.end()); - it = l.find_geq(9); - SASSERT(it->begin_key() == 10); - it.move_to(105); - SASSERT(it->begin_key() == 100); - it = l.find_geq(15); - SASSERT(it->begin_key() == 10); - it.move_to(31); - SASSERT(it->begin_key() == 40); - it = l.find_geq(22); - SASSERT(it->begin_key() == 23); - it = l.find_geq(124); - SASSERT(it->begin_key() == 140); - it = l.find_geq(102); - SASSERT(it->begin_key() == 100); - // l.display_physical(std::cout); - l.deallocate(m); -} - -struct add42 { - unsigned operator()(unsigned v) { return v + 42; } -}; - -void tst_move_to() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 500; i++) - l.insert(m, i*10, i*10 + 5, i); - l.compress(m, 4); - slist::iterator it = l.find_geq(137); - SASSERT(it->begin_key() == 140); - it.move_to(947); - SASSERT(it->begin_key() == 950); - it.move_to(4955); - SASSERT(it->begin_key() == 4950); - it.move_to(4955); - SASSERT(it->begin_key() == 4950); - it.move_to(4956); - SASSERT(it->begin_key() == 4960); - it.move_to(4982); - SASSERT(it->begin_key() == 4980); - it.move_to(4987); - SASSERT(it->begin_key() == 4990); - it.move_to(4990); - SASSERT(it->begin_key() == 4990); - it.move_to(4995); - SASSERT(it->begin_key() == 4990); - it.move_to(4996); - SASSERT(it.at_end()); - // l.display_physical(std::cout); - add42 f; - // l.display(std::cout); std::cout << "\n"; - l.update_values(m, f); - // l.display(std::cout); std::cout << "\n"; - l.deallocate(m); -} - -static void tst_ext_iterator() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 20; i++) - l.insert(m, i*10, i*10 + 5, i); - l.compress(m, 4); - l.display_physical(std::cout); std::cout << "\n"; - slist::ext_iterator it; - slist::ext_iterator end; - SASSERT(end.at_end()); - l.move_geq(it, 92); - SASSERT(!it.at_end()); - SASSERT(it->begin_key() == 90); - it++; - SASSERT(it->begin_key() == 100); - it.erase(m); - SASSERT(it->begin_key() == 110); - it.erase(m); - SASSERT(it->begin_key() == 120); - it.erase(m); - it.erase(m); - it.erase(m); - it.erase(m); - SASSERT(it->begin_key() == 160); - SASSERT(l.check_invariant()); - l.display_physical(std::cout); std::cout << "\n"; - l.move_geq(it, 0); - SASSERT(it->begin_key() == 0); - it.erase(m); - SASSERT(it->begin_key() == 10); - it.erase(m); - SASSERT(it->begin_key() == 20); - it.erase(m); - SASSERT(it->begin_key() == 30); - it.erase(m); - SASSERT(it->begin_key() == 40); - it.erase(m); - SASSERT(it->begin_key() == 50); - l.display_physical(std::cout); std::cout << "\n"; - l.deallocate(m); -} - -void tst_interval_skip_list() { - std::cout << "unsigned map stats:\n"; - slist::display_size_info(std::cout); - std::cout << "\nunsigned set stats:\n"; - uset::display_size_info(std::cout); - std::cout << "\n"; - tst1(); -// enable_trace("interval_skip_list_insert_bug"); -// enable_trace("interval_skip_list_bug"); -// enable_trace("del_entries_upto_bug"); -// enable_trace("insert_inside_bug"); -// enable_trace("insert_at_bug"); - tst2(); - tst4(); - tst5(); - tst6(); - tst7(); - tst8(); - tst9(); - tst10(); - tst11(); - tst12(); - tst13(); - tst_find_geq1(); - tst_move_to(); - tst_push_back1(300, 4, 2, 10); - tst_push_back2(300, 4, 2, 10); - random_tsts(1000, 20, 20, 5); - random_tsts_ref(1000, 20, 20, 5); - tst_ref(); - tst_ext_iterator(); -} - - diff --git a/src/test/main.cpp b/src/test/main.cpp index 6ecfd6d4f..c8c011674 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -22,7 +22,7 @@ if (do_display_usage) \ std::cout << #MODULE << "\n"; \ for (int i = 0; i < argc; i++) \ - if (strcmp(argv[i], #MODULE) == 0) { \ + if (test_all || strcmp(argv[i], #MODULE) == 0) { \ enable_trace(#MODULE); \ enable_debug(#MODULE); \ timeit timeit(true, s.c_str()); \ @@ -60,6 +60,7 @@ void display_usage() { std::cout << " /h prints this message.\n"; std::cout << " /v:level be verbose, where is the verbosity level.\n"; std::cout << " /w enable warning messages.\n"; + std::cout << " /a run all unit tests that don't require arguments.\n"; #if defined(Z3DEBUG) || defined(_TRACE) std::cout << "\nDebugging support:\n"; #endif @@ -71,7 +72,7 @@ void display_usage() { #endif } -void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { +void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& test_all) { int i = 1; while (i < argc) { char * arg = argv[i]; @@ -99,6 +100,9 @@ void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { else if (strcmp(opt_name, "w") == 0) { enable_warning_messages(true); } + else if (strcmp(opt_name, "a") == 0) { + test_all = true; + } #ifdef _TRACE else if (strcmp(opt_name, "tr") == 0) { if (!opt_arg) @@ -122,7 +126,8 @@ void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { int main(int argc, char ** argv) { memory::initialize(0); bool do_display_usage = false; - parse_cmd_line_args(argc, argv, do_display_usage); + bool test_all = false; + parse_cmd_line_args(argc, argv, do_display_usage, test_all); TST(random); TST(vector); TST(symbol_table); @@ -151,14 +156,9 @@ int main(int argc, char ** argv) { TST(simple_parser); TST(api); TST(old_interval); - TST(interval_skip_list); - TST(no_overflow); - TST(memory); TST(get_implied_equalities); TST(arith_simplifier_plugin); TST(matcher); - TST(datalog_parser); - TST_ARGV(datalog_parser_file); TST(object_allocator); TST(mpz); TST(mpq); @@ -166,11 +166,9 @@ int main(int argc, char ** argv) { TST(total_order); TST(dl_table); TST(dl_context); - TST(dl_query); TST(dl_util); TST(dl_product_relation); TST(dl_relation); - TST(imdd); TST(parray); TST(stack); TST(escaped); @@ -196,7 +194,6 @@ int main(int argc, char ** argv) { TST(nlsat); TST(ext_numeral); TST(interval); - TST(quant_solve); TST(f2n); TST(hwf); TST(trigo); @@ -206,11 +203,16 @@ int main(int argc, char ** argv) { TST(mpff); TST(horn_subsume_model_converter); TST(model2expr); - TST(rcf); TST(hilbert_basis); TST(heap_trie); TST(karr); - TST(fdd); + TST(no_overflow); + TST(memory); + TST(datalog_parser); + TST_ARGV(datalog_parser_file); + TST(dl_query); + TST(quant_solve); + TST(rcf); } void initialize_mam() {} diff --git a/src/test/matcher.cpp b/src/test/matcher.cpp index 05d971e24..598a82ae3 100644 --- a/src/test/matcher.cpp +++ b/src/test/matcher.cpp @@ -26,7 +26,7 @@ void tst_match(ast_manager & m, app * t, app * i) { substitution s(m); s.reserve(2, 10); // reserving a big number of variables to be safe. - matcher match(m); + matcher match; std::cout << "Is " << mk_pp(i, m) << " an instance of " << mk_pp(t, m) << "\n"; if (match(t, i, s)) { std::cout << "yes\n"; diff --git a/src/test/memory.cpp b/src/test/memory.cpp index 8fb09fb3d..0f5a92e2c 100644 --- a/src/test/memory.cpp +++ b/src/test/memory.cpp @@ -20,30 +20,36 @@ static void hit_me(char const* wm) { oom = false; cfg = Z3_mk_config(); - Z3_set_param_value(cfg, "MEMORY_MAX_SIZE", wm); - ctx = Z3_mk_context(cfg); - Z3_set_error_handler(ctx, &err_handler); - - unsigned i; - for (i = 1; !oom ; ++i) { - try { - Z3_mk_bv_sort(ctx,i); - - } - catch (std::bad_alloc) { - std::cout << "caught\n"; - } + if (!cfg) { + return; } - - std::cout << "oom " << i << "\n"; + Z3_global_param_set("MEMORY_MAX_SIZE", wm); + ctx = Z3_mk_context(cfg); + if (ctx) { + Z3_set_error_handler(ctx, &err_handler); + + unsigned i; + for (i = 1; !oom ; ++i) { + try { + Z3_mk_bv_sort(ctx,i); + + } + catch (std::bad_alloc) { + std::cout << "caught\n"; + } + } + std::cout << "oom " << i << "\n"; + Z3_del_context(ctx); + } + Z3_del_config(cfg); } void tst_memory() { - hit_me("1"); + hit_me("10"); Z3_reset_memory(); - hit_me("2"); + hit_me("20"); Z3_reset_memory(); - hit_me("3"); + hit_me("30"); Z3_reset_memory(); } diff --git a/src/test/no_overflow.cpp b/src/test/no_overflow.cpp index 4dd3a2287..122d41f2b 100644 --- a/src/test/no_overflow.cpp +++ b/src/test/no_overflow.cpp @@ -659,7 +659,7 @@ void test_equiv(Equivalence_params params, unsigned bvsize, bool is_signed) { typedef void (*TESTFUN)(unsigned bvsize, bool is_signed); void tst_no_overflow() { - + disable_debug("heap"); unsigned bvsizes[BVSIZES] = { 1, 16, 32, 42 }; TESTFUN tests[TESTNUM] = { test_add, test_sub, test_mul }; diff --git a/src/test/quant_elim.cpp b/src/test/quant_elim.cpp index 4e750c34e..15d23a574 100644 --- a/src/test/quant_elim.cpp +++ b/src/test/quant_elim.cpp @@ -76,6 +76,7 @@ static void test_formula(lbool expected_outcome, char const* fml) { } void tst_quant_elim() { + disable_debug("heap"); test_formula(l_undef, "(exists ((p1 Bool) (q1 Bool) (r1 Bool))\ (and (or (not p1) (not q1) r1)\ diff --git a/src/test/quant_solve.cpp b/src/test/quant_solve.cpp index 36b354b44..ae2dadee9 100644 --- a/src/test/quant_solve.cpp +++ b/src/test/quant_solve.cpp @@ -28,6 +28,7 @@ static void validate_quant_solution(ast_manager& m, expr* fml, expr* guard, qe:: (*rep)(fml1); expr_ref tmp(m); tmp = m.mk_not(m.mk_implies(guard, fml1)); + std::cout << "validating: " << mk_pp(tmp, m) << "\n"; smt_params fp; smt::kernel solver(m, fp); solver.assert_expr(tmp); @@ -174,11 +175,11 @@ static void test_quant_solve1() { app* xy[2] = { x, y }; + test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= x z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= x y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (>= x y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (>= (* 2 x) y) (= (mod x 2) 0))"); - test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= x z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= (* 3 x) z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(>= (* 2 x) a)"); test_quant_solver(m, x, "(<= (* 2 x) a)"); @@ -242,6 +243,7 @@ static void test_quant_solve1() { void tst_quant_solve() { + disable_debug("heap"); test_quant_solve1(); diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index d6cfd8b86..294a7df29 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -137,7 +137,6 @@ static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsi r.resize(A.n()); scoped_mpz_matrix B(mm); mm.linear_independent_rows(A, r.c_ptr(), B); - SASSERT(r.size() == ex_sz); for (unsigned i = 0; i < ex_sz; i++) { SASSERT(r[i] == ex_r[i]); } @@ -164,7 +163,6 @@ void tst_rcf() { enable_trace("rcf_clean"); enable_trace("rcf_clean_bug"); tst_denominators(); - return; tst1(); tst2(); { int A[] = {0, 1, 1, 1, 0, 1, 1, 1, -1}; int c[] = {10, 4, -4}; int b[] = {-2, 4, 6}; tst_solve(3, A, b, c, true); } diff --git a/src/test/simplifier.cpp b/src/test/simplifier.cpp index dbe5f5074..733c9e23a 100644 --- a/src/test/simplifier.cpp +++ b/src/test/simplifier.cpp @@ -5,7 +5,8 @@ #include "util.h" #include "trace.h" -void ev_const(Z3_context ctx, Z3_ast e) { + +static void ev_const(Z3_context ctx, Z3_ast e) { Z3_ast r = Z3_simplify(ctx, e); TRACE("simplifier", tout << Z3_ast_to_string(ctx, e) << " -> "; @@ -17,7 +18,7 @@ void ev_const(Z3_context ctx, Z3_ast e) { Z3_OP_FALSE == Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx, Z3_to_app(ctx, r)))))); } -void test_bv() { +static void test_bv() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv1 = Z3_mk_bv_sort(ctx,1); @@ -75,7 +76,7 @@ void test_bv() { Z3_del_context(ctx); } -void test_datatypes() { +static void test_datatypes() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort int_ty, int_list; @@ -108,17 +109,15 @@ void test_datatypes() { } -void test_skolemize_bug() { +static void test_skolemize_bug() { Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg, "MODEL", "true"); - Z3_set_param_value(cfg, "QUANT_FM","true"); - Z3_set_param_value(cfg, "FM","true"); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_sort Real = Z3_mk_real_sort(ctx); Z3_ast x = Z3_mk_bound(ctx, 0, Real); - Z3_symbol x_name = Z3_mk_string_symbol(ctx, "x"); + Z3_symbol x_name = Z3_mk_string_symbol(ctx, "x"); Z3_ast y = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "y"), Real); Z3_ast xp = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "xp"), Real); Z3_ast n0 = Z3_mk_numeral(ctx, "0", Real); @@ -136,7 +135,7 @@ void test_skolemize_bug() { } -void test_bool() { +static void test_bool() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); @@ -151,7 +150,7 @@ void test_bool() { Z3_del_context(ctx); } -void test_array() { +static void test_array() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); @@ -172,8 +171,10 @@ void test_array() { Z3_ast exy = Z3_mk_eq(ctx, x2, x1); Z3_ast rxy = Z3_simplify(ctx, exy); - SASSERT(rxy == Z3_mk_true(ctx)); - SASSERT(Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3)) == Z3_mk_false(ctx)); + TRACE("simplifier", tout << Z3_ast_to_string(ctx, rxy) << "\n";); + TRACE("simplifier", tout << Z3_ast_to_string(ctx, Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3))) << "\n";); + // SASSERT(rxy == Z3_mk_true(ctx)); + // SASSERT(Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3)) == Z3_mk_false(ctx)); for (unsigned i = 0; i < 4; ++i) { for (unsigned j = 0; j < 4; ++j) { diff --git a/src/util/array.h b/src/util/array.h index a3658c354..f983f7dec 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -122,7 +122,7 @@ public: if (m_data) { if (CallDestructors) destroy_elements(); - a.deallocate(size(), raw_ptr()); + a.deallocate(space(size()), raw_ptr()); m_data = 0; } } diff --git a/src/util/bit_util.cpp b/src/util/bit_util.cpp index 32861f754..0f1fd294d 100644 --- a/src/util/bit_util.cpp +++ b/src/util/bit_util.cpp @@ -19,6 +19,7 @@ Revision History: #include"bit_util.h" #include"util.h" #include"debug.h" +#include /** \brief (Debugging version) Return the position of the most significant (set) bit of a @@ -67,7 +68,11 @@ unsigned msb_pos(unsigned v) { */ unsigned nlz_core(unsigned x) { SASSERT(x != 0); +#ifdef __GNUC__ + return __builtin_clz(x); +#else return 31 - msb_pos(x); +#endif } /** @@ -92,8 +97,15 @@ unsigned nlz(unsigned sz, unsigned const * data) { */ unsigned ntz_core(unsigned x) { SASSERT(x != 0); +#ifdef __GNUC__ + return __builtin_ctz(x); +#else float f = static_cast(x & static_cast(-static_cast(x))); - return (*reinterpret_cast(&f) >> 23) - 0x7f; + unsigned u; + SASSERT(sizeof(u) == sizeof(f)); + memcpy(&u, &f, sizeof(u)); + return (u >> 23) - 0x7f; +#endif } /** diff --git a/src/util/bit_vector.cpp b/src/util/bit_vector.cpp index e3a2bc331..6885b93e8 100644 --- a/src/util/bit_vector.cpp +++ b/src/util/bit_vector.cpp @@ -22,6 +22,8 @@ Revision History: #define DEFAULT_CAPACITY 2 +#define MK_MASK(_num_bits_) ((1U << _num_bits_) - 1) + void bit_vector::expand_to(unsigned new_capacity) { unsigned * new_data = alloc_svect(unsigned, new_capacity); memset(new_data, 0, new_capacity * sizeof(unsigned)); @@ -51,7 +53,7 @@ void bit_vector::resize(unsigned new_size, bool val) { unsigned ewidx = num_words(new_size); unsigned * begin = m_data + bwidx; unsigned pos = m_num_bits % 32; - unsigned mask = (1 << pos) - 1; + unsigned mask = MK_MASK(pos); int cval; if (val) { @@ -128,7 +130,7 @@ bool bit_vector::operator==(bit_vector const & source) const { return false; } unsigned bit_rest = source.m_num_bits % 32; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); if (mask == 0) mask = UINT_MAX; return (m_data[i] & mask) == (source.m_data[i] & mask); } @@ -149,7 +151,7 @@ bit_vector & bit_vector::operator|=(bit_vector const & source) { unsigned i = 0; for (i = 0; i < n2 - 1; i++) m_data[i] |= source.m_data[i]; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); m_data[i] |= source.m_data[i] & mask; } return *this; @@ -175,7 +177,7 @@ bit_vector & bit_vector::operator&=(bit_vector const & source) { else { for (i = 0; i < n2 - 1; i++) m_data[i] &= source.m_data[i]; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); m_data[i] &= (source.m_data[i] & mask); } @@ -208,8 +210,8 @@ void bit_vector::display(std::ostream & out) const { void fr_bit_vector::reset() { unsigned sz = size(); - vector::const_iterator it = m_one_idxs.begin(); - vector::const_iterator end = m_one_idxs.end(); + unsigned_vector::const_iterator it = m_one_idxs.begin(); + unsigned_vector::const_iterator end = m_one_idxs.end(); for (; it != end; ++it) { unsigned idx = *it; if (idx < sz) @@ -217,5 +219,3 @@ void fr_bit_vector::reset() { } m_one_idxs.reset(); } - - diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index 1d6083717..0ccdeae9e 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -28,6 +28,7 @@ COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); #define BV_DEFAULT_CAPACITY 2 class bit_vector { +protected: unsigned m_num_bits; unsigned m_capacity; //!< in words unsigned * m_data; @@ -64,6 +65,13 @@ public: m_data(0) { } + bit_vector(unsigned reserve_num_bits) : + m_num_bits(0), + m_capacity(num_words(reserve_num_bits)), + m_data(alloc_svect(unsigned, m_capacity)) { + memset(m_data, 0, m_capacity * sizeof(unsigned)); + } + bit_vector(bit_vector const & source): m_num_bits(source.m_num_bits), m_capacity(source.m_capacity), @@ -204,7 +212,7 @@ inline std::ostream & operator<<(std::ostream & out, bit_vector const & b) { This class should be used if the reset is frequently called. */ class fr_bit_vector : private bit_vector { - svector m_one_idxs; + unsigned_vector m_one_idxs; public: void reset(); diff --git a/src/util/debug.cpp b/src/util/debug.cpp index 75ea8586a..c336ddb73 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -24,7 +24,7 @@ Revision History: #include"str_hashtable.h" #include"z3_exception.h" -volatile bool g_enable_assertions = true; +static volatile bool g_enable_assertions = true; void enable_assertions(bool f) { g_enable_assertions = f; @@ -41,7 +41,7 @@ void notify_assertion_violation(const char * fileName, int line, const char * co std::cerr << condition << "\n"; } -str_hashtable* g_enabled_debug_tags = 0; +static str_hashtable* g_enabled_debug_tags = 0; static void init_debug_table() { if (!g_enabled_debug_tags) { diff --git a/src/util/debug.h b/src/util/debug.h index c45ee5aa6..9e519982f 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -29,6 +29,10 @@ bool assertions_enabled(); #include #endif +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + #include"error_codes.h" #include"warning.h" @@ -53,7 +57,14 @@ bool is_debug_enabled(const char * tag); #define SASSERT(COND) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define CASSERT(TAG, COND) DEBUG_CODE(if (assertions_enabled() && is_debug_enabled(TAG) && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define XASSERT(COND, EXTRA_CODE) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); { EXTRA_CODE } INVOKE_DEBUGGER(); }) + +#if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 405)) || __has_builtin(__builtin_unreachable) +// only available in gcc >= 4.5 and in newer versions of clang +# define UNREACHABLE() __builtin_unreachable() +#else #define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNREACHABLE CODE WAS REACHED."); INVOKE_DEBUGGER();) +#endif + #define NOT_IMPLEMENTED_YET() { std::cerr << "NOT IMPLEMENTED YET!\n"; UNREACHABLE(); exit(ERR_NOT_IMPLEMENTED_YET); } ((void) 0) #ifdef Z3DEBUG diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 3b2e8edc1..1d9426390 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -22,7 +22,7 @@ Notes: extern void gparams_register_modules(); -char const * g_old_params_names[] = { +static char const * g_old_params_names[] = { "arith_adaptive","arith_adaptive_assertion_threshold","arith_adaptive_gcd","arith_adaptive_propagation_threshold","arith_add_binary_bounds","arith_blands_rule_threshold","arith_branch_cut_ratio","arith_dump_lemmas","arith_eager_eq_axioms","arith_eager_gcd","arith_eq_bounds","arith_euclidean_solver","arith_expand_eqs","arith_force_simplex","arith_gcd_test","arith_ignore_int","arith_lazy_adapter","arith_lazy_pivoting","arith_max_lemma_size","arith_process_all_eqs","arith_propagate_eqs","arith_propagation_mode","arith_propagation_threshold","arith_prop_strategy","arith_random_initial_value","arith_random_lower","arith_random_seed","arith_random_upper","arith_reflect","arith_skip_big_coeffs","arith_small_lemma_size","arith_solver","arith_stronger_lemmas","array_always_prop_upward","array_canonize","array_cg","array_delay_exp_axiom","array_extensional","array_laziness","array_lazy_ieq","array_lazy_ieq_delay","array_solver","array_weak","async_commands","at_labels_cex","auto_config","bb_eager","bb_ext_gates","bb_quantifiers","bin_clauses","bit2int","bv2int_distribute","bv_blast_max_size","bv_cc","bv_enable_int2bv_propagation","bv_lazy_le","bv_max_sharing","bv_reflect","bv_solver","case_split","check_at_labels","check_proof","cnf_factor","cnf_mode","context_simplifier","dack","dack_eq","dack_factor","dack_gc","dack_gc_inv_decay","dack_threshold","default_qid","default_table","default_table_checked","delay_units","delay_units_threshold","der","display_config","display_dot_proof","display_error_for_visual_studio","display_features","display_proof","display_unsat_core","distribute_forall","dt_lazy_splits","dump_goal_as_smt","elim_and","elim_bounds","elim_nlarith_quantifiers","elim_quantifiers","elim_term_ite","ematching","engine","eq_propagation","hi_div0","ignore_bad_patterns","ignore_setparameter","instruction_max","inst_gen","interactive","internalizer_nnf","lemma_gc_factor","lemma_gc_half","lemma_gc_initial","lemma_gc_new_clause_activity","lemma_gc_new_clause_relevancy","lemma_gc_new_old_ratio","lemma_gc_old_clause_activity","lemma_gc_old_clause_relevancy","lemma_gc_strategy","lift_ite","lookahead_diseq","macro_finder","max_conflicts","max_counterexamples","mbqi","mbqi_force_template","mbqi_max_cexs","mbqi_max_cexs_incr","mbqi_max_iterations","mbqi_trace","minimize_lemmas","model","model_compact","model_completion","model_display_arg_sort","model_hide_unused_partitions","model_on_final_check","model_on_timeout","model_partial","model_v1","model_v2","model_validate","new_core2th_eq","ng_lift_ite","nl_arith","nl_arith_branching","nl_arith_gb","nl_arith_gb_eqs","nl_arith_gb_perturbate","nl_arith_gb_threshold","nl_arith_max_degree","nl_arith_rounds","nnf_factor","nnf_ignore_labels","nnf_mode","nnf_sk_hack","order","order_var_weight","order_weights","phase_selection","pi_arith","pi_arith_weight","pi_avoid_skolems","pi_block_looop_patterns","pi_max_multi_patterns","pi_non_nested_arith_weight","pi_nopat_weight","pi_pull_quantifiers","pi_use_database","pi_warnings","pp_bounded","pp_bv_literals","pp_bv_neg","pp_decimal","pp_decimal_precision","pp_fixed_indent","pp_flat_assoc","pp_max_depth","pp_max_indent","pp_max_num_lines","pp_max_ribbon","pp_max_width","pp_min_alias_size","pp_simplify_implies","pp_single_line","precedence","precedence_gen","pre_demodulator","pre_simplifier","pre_simplify_expr","profile_res_sub","progress_sampling_freq","proof_mode","propagate_booleans","propagate_values","pull_cheap_ite_trees","pull_nested_quantifiers","qi_conservative_final_check","qi_cost","qi_eager_threshold","qi_lazy_instantiation","qi_lazy_quick_checker","qi_lazy_threshold","qi_max_eager_multi_patterns","qi_max_instances","qi_max_lazy_multi_pattern_matching","qi_new_gen","qi_profile","qi_profile_freq","qi_promote_unsat","qi_quick_checker","quasi_macros","random_case_split_freq","random_initial_activity","random_seed","recent_lemma_threshold","reduce_args","refine_inj_axiom","relevancy","relevancy_lemma","rel_case_split_order","restart_adaptive","restart_agility_threshold","restart_factor","restart_initial","restart_strategy","restricted_quasi_macros","simplify_clauses","smtlib2_compliant","smtlib_category","smtlib_dump_lemmas","smtlib_logic","smtlib_source_info","smtlib_trace_path","soft_timeout","solver","spc_bs","spc_es","spc_factor_subsumption_index_opt","spc_initial_subsumption_index_opt","spc_max_subsumption_index_features","spc_min_func_freq_subsumption_index","spc_num_iterations","spc_trace","statistics","strong_context_simplifier","tick","trace","trace_file_name","type_check","user_theory_persist_axioms","user_theory_preprocess_axioms","verbose","warning","well_sorted_check","z3_solver_ll_pp","z3_solver_smt_pp", 0 }; bool is_old_param_name(symbol const & name) { @@ -35,7 +35,7 @@ bool is_old_param_name(symbol const & name) { return false; } -char const * g_params_renames[] = { +static char const * g_params_renames[] = { "proof_mode", "proof", "soft_timeout", "timeout", "mbqi", "smt.mbqi", diff --git a/src/util/hash.h b/src/util/hash.h index 3c7e50a6c..4232dd2af 100644 --- a/src/util/hash.h +++ b/src/util/hash.h @@ -20,6 +20,7 @@ Revision History: #define _HASH_H_ #include +#include"util.h" #ifndef __fallthrough #define __fallthrough @@ -142,6 +143,11 @@ struct size_t_hash { unsigned operator()(size_t x) const { return static_cast(x); } }; +struct uint64_hash { + typedef uint64 data; + unsigned operator()(uint64 x) const { return static_cast(x); } +}; + struct bool_hash { typedef bool data; unsigned operator()(bool x) const { return static_cast(x); } diff --git a/src/util/heap.h b/src/util/heap.h index 75b41e329..73029f6a7 100644 --- a/src/util/heap.h +++ b/src/util/heap.h @@ -113,6 +113,7 @@ public: heap(int s, const LT & lt = LT()):LT(lt) { m_values.push_back(-1); set_bounds(s); + CASSERT("heap", check_invariant()); } bool empty() const { @@ -124,12 +125,14 @@ public: } void reset() { + CASSERT("heap", check_invariant()); if (empty()) { return; } memset(m_value2indices.begin(), 0, sizeof(int) * m_value2indices.size()); m_values.reset(); m_values.push_back(-1); + CASSERT("heap", check_invariant()); } void clear() { @@ -138,6 +141,7 @@ public: void set_bounds(int s) { m_value2indices.resize(s, 0); + CASSERT("heap", check_invariant()); } unsigned get_bounds() const { @@ -145,8 +149,10 @@ public: } void reserve(int s) { + CASSERT("heap", check_invariant()); if (s > static_cast(m_value2indices.size())) set_bounds(s); + CASSERT("heap", check_invariant()); } int min_value() const { @@ -155,6 +161,7 @@ public: } int erase_min() { + CASSERT("heap", check_invariant()); SASSERT(!empty()); SASSERT(m_values.size() >= 2); int result = m_values[1]; @@ -176,6 +183,7 @@ public: } void erase(int val) { + CASSERT("heap", check_invariant()); SASSERT(contains(val)); int idx = m_value2indices[val]; if (idx == static_cast(m_values.size()) - 1) { @@ -210,12 +218,14 @@ public: } void insert(int val) { + CASSERT("heap", check_invariant()); SASSERT(is_valid_value(val)); int idx = static_cast(m_values.size()); m_value2indices[val] = idx; m_values.push_back(val); SASSERT(idx == static_cast(m_values.size()) - 1); move_up(idx); + CASSERT("heap", check_invariant()); } iterator begin() { @@ -235,8 +245,14 @@ public: } void swap(heap & other) { - m_values.swap(other.m_values); - m_value2indices.swap(other.m_value2indices); + if (this != &other) { + CASSERT("heap", other.check_invariant()); + CASSERT("heap", check_invariant()); + m_values.swap(other.m_values); + m_value2indices.swap(other.m_value2indices); + CASSERT("heap", other.check_invariant()); + CASSERT("heap", check_invariant()); + } } /** diff --git a/src/util/hwf.h b/src/util/hwf.h index 3b7a0e94b..9059869a0 100644 --- a/src/util/hwf.h +++ b/src/util/hwf.h @@ -28,6 +28,12 @@ class hwf { friend class hwf_manager; double value; hwf & operator=(hwf const & other) { UNREACHABLE(); return *this; } + uint64 get_raw() const { + uint64 n; + SASSERT(sizeof(n) == sizeof(value)); + memcpy(&n, &value, sizeof(value)); + return n; + } public: hwf() {} @@ -122,16 +128,15 @@ public: bool sgn(hwf const & x) const { - uint64 raw = *reinterpret_cast(&x.value); - return (raw & 0x8000000000000000ull) != 0; + return (x.get_raw() & 0x8000000000000000ull) != 0; } const uint64 sig(hwf const & x) const { - return *reinterpret_cast(&x.value) & 0x000FFFFFFFFFFFFFull; + return x.get_raw() & 0x000FFFFFFFFFFFFFull; } const int exp(hwf const & x) const { - return ((*reinterpret_cast(&x.value) & 0x7FF0000000000000ull) >> 52) - 1023; + return ((x.get_raw() & 0x7FF0000000000000ull) >> 52) - 1023; } bool is_nan(hwf const & x); @@ -151,7 +156,7 @@ public: void mk_pinf(hwf & o); void mk_ninf(hwf & o); - unsigned hash(hwf const & a) { return hash_ull(*reinterpret_cast(&a.value)); } + unsigned hash(hwf const & a) { return hash_ull(a.get_raw()); } inline void set_rounding_mode(mpf_rounding_mode rm); diff --git a/src/util/inf_eps_rational.h b/src/util/inf_eps_rational.h new file mode 100644 index 000000000..659fdc400 --- /dev/null +++ b/src/util/inf_eps_rational.h @@ -0,0 +1,409 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + inf_eps_rational.h + +Abstract: + + Rational numbers with infinity and epsilon. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-4-23. + +Revision History: + +--*/ +#ifndef _INF_EPS_RATIONAL_H_ +#define _INF_EPS_RATIONAL_H_ +#include +#include +#include"debug.h" +#include"vector.h" +#include"rational.h" + +template +class inf_eps_rational { + rational m_infty; + Numeral m_r; + public: + + unsigned hash() const { + return m_infty.hash() ^ m_r.hash(); + } + + struct hash_proc { unsigned operator()(inf_eps_rational const& r) const { return r.hash(); } }; + + struct eq_proc { bool operator()(inf_eps_rational const& r1, inf_eps_rational const& r2) const { return r1 == r2; } }; + + void swap(inf_eps_rational & n) { + m_infty.swap(n.m_infty); + m_r.swap(n.m_r); + } + + std::string to_string() const { + if (m_infty.is_zero()) { + return m_r.to_string(); + } + std::string si; + if (m_infty.is_one()) { + si = "oo"; + } + else if (m_infty.is_minus_one()) { + si = "-oo"; + } + else { + si = m_infty.to_string() += "*oo"; + } + if (m_r.is_zero()) { + return si; + } + std::string s = "("; + s += si; + s += " + "; + s += m_r.to_string(); + s += ")"; + return s; + } + + inf_eps_rational(): + m_infty(), + m_r() + {} + + inf_eps_rational(const inf_eps_rational & r): + m_infty(r.m_infty), + m_r(r.m_r) + {} + + explicit inf_eps_rational(int n): + m_infty(), + m_r(n) + {} + + explicit inf_eps_rational(Numeral const& r): + m_infty(), + m_r(r) + {} + + explicit inf_eps_rational(rational const& i, Numeral const& r): + m_infty(i), + m_r(r) { + } + + ~inf_eps_rational() {} + + /** + \brief Set inf_eps_rational to 0. + */ + void reset() { + m_infty.reset(); + m_r.reset(); + } + + bool is_int() const { + return m_infty.is_zero() && m_r.is_int(); + } + + bool is_int64() const { + return m_infty.is_zero() && m_r.is_int64(); + } + + bool is_uint64() const { + return m_infty.is_zero() && m_r.is_uint64(); + } + + bool is_rational() const { return m_infty.is_zero() && m_r.is_rational(); } + + int64 get_int64() const { + SASSERT(is_int64()); + return m_r.get_int64(); + } + + uint64 get_uint64() const { + SASSERT(is_uint64()); + return m_r.get_uint64(); + } + + rational const& get_rational() const { + return m_r.get_rational(); + } + + rational const& get_infinitesimal() const { + return m_r.get_infinitesimal(); + } + + rational const& get_infinity() const { + return m_infty; + } + + inf_eps_rational & operator=(const inf_eps_rational & r) { + m_infty = r.m_infty; + m_r = r.m_r; + return *this; + } + + inf_eps_rational & operator=(const rational & r) { + m_infty.reset(); + m_r = r; + return *this; + } + + inf_eps_rational & operator+=(const inf_eps_rational & r) { + m_infty += r.m_infty; + m_r += r.m_r; + return *this; + } + + inf_eps_rational & operator-=(const inf_eps_rational & r) { + m_infty -= r.m_infty; + m_r -= r.m_r; + return *this; + } + + inf_eps_rational & operator+=(const rational & r) { + m_r += r; + return *this; + } + + inf_eps_rational & operator-=(const rational & r) { + m_r -= r; + return *this; + } + + inf_eps_rational & operator*=(const rational & r1) { + m_infty *= r1; + m_r *= r1; + return *this; + } + + inf_eps_rational & operator/=(const rational & r) { + m_infty /= r; + m_r /= r; + return *this; + } + + + inf_eps_rational & operator++() { + ++m_r; + return *this; + } + + const inf_eps_rational operator++(int) { inf_eps_rational tmp(*this); ++(*this); return tmp; } + + inf_eps_rational & operator--() { + --m_r; + return *this; + } + + const inf_eps_rational operator--(int) { inf_eps_rational tmp(*this); --(*this); return tmp; } + + friend inline bool operator==(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return r1.m_infty == r2.m_infty && r1.m_r == r2.m_r; + } + + friend inline bool operator==(const rational & r1, const inf_eps_rational & r2) { + return r1 == r2.m_infty && r2.m_r.is_zero(); + } + + friend inline bool operator==(const inf_eps_rational & r1, const rational & r2) { + return r1.m_infty == r2 && r1.m_r.is_zero(); + } + + friend inline bool operator<(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return + (r1.m_infty < r2.m_infty) || + (r1.m_infty == r2.m_infty && r1.m_r < r2.m_r); + } + + friend inline bool operator<(const rational & r1, const inf_eps_rational & r2) { + return + r2.m_infty.is_pos() || + (r2.m_infty.is_zero() && r1 < r2.m_r); + } + + friend inline bool operator<(const inf_eps_rational & r1, const rational & r2) { + return + r1.m_infty.is_neg() || + (r1.m_infty.is_zero() && r1.m_r < r2); + } + + void neg() { + m_infty.neg(); + m_r.neg(); + } + + bool is_zero() const { + return m_infty.is_zero() && m_r.is_zero(); + } + + bool is_one() const { + return m_infty.is_zero() && m_r.is_one(); + } + + bool is_minus_one() const { + return m_infty.is_zero() && m_r.is_minus_one(); + } + + bool is_neg() const { + return + m_infty.is_neg() || + (m_infty.is_zero() && m_r.is_neg()); + } + + bool is_pos() const { + return + m_infty.is_pos() || + (m_infty.is_zero() && m_r.is_pos()); + } + + bool is_nonneg() const { + return + m_infty.is_pos() || + (m_infty.is_zero() && m_r.is_nonneg()); + } + + bool is_nonpos() const { + return + m_infty.is_neg() || + (m_infty.is_zero() && m_r.is_nonpos()); + } + + friend inline rational floor(const inf_eps_rational & r) { + SASSERT(r.m_infty.is_zero()); + return floor(r.m_r); + } + + friend inline rational ceil(const inf_eps_rational & r) { + SASSERT(r.m_infty.is_zero()); + return ceil(r.m_r); + } + + + // Perform: this += c * k + void addmul(const rational & c, const inf_eps_rational & k) { + m_infty.addmul(c, k.m_infty); + m_r.addmul(c, k.m_r); + } + + // Perform: this += c * k + void submul(const rational & c, const inf_eps_rational & k) { + m_infty.submul(c, k.m_infty); + m_r.submul(c, k.m_r); + } +}; + +template +inline bool operator!=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator!=(const rational & r1, const inf_eps_rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator!=(const inf_eps_rational & r1, const rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator>(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator>(const inf_eps_rational & r1, const rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator>(const rational & r1, const inf_eps_rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator<=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator<=(const rational & r1, const inf_eps_rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator<=(const inf_eps_rational & r1, const rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator>=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator<(r1, r2); +} + +template +inline bool operator>=(const rational & r1, const inf_eps_rational & r2) { + return !operator<(r1, r2); +} + +template +inline bool operator>=(const inf_eps_rational & r1, const rational & r2) { + return !operator<(r1, r2); +} + +template +inline inf_eps_rational operator+(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return inf_eps_rational(r1) += r2; +} + +template +inline inf_eps_rational operator-(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return inf_eps_rational(r1) -= r2; +} + +template +inline inf_eps_rational operator-(const inf_eps_rational & r) { + inf_eps_rational result(r); + result.neg(); + return result; +} + +template +inline inf_eps_rational operator*(const rational & r1, const inf_eps_rational & r2) { + inf_eps_rational result(r2); + result *= r1; + return result; +} + +template +inline inf_eps_rational operator*(const inf_eps_rational & r1, const rational & r2) { + return r2 * r1; +} + +template +inline inf_eps_rational operator/(const inf_eps_rational & r1, const rational & r2) { + inf_eps_rational result(r1); + result /= r2; + return result; +} + +template +inline std::ostream & operator<<(std::ostream & target, const inf_eps_rational & r) { + target << r.to_string(); + return target; +} + +template +inline inf_eps_rational abs(const inf_eps_rational & r) { + inf_eps_rational result(r); + if (result.is_neg()) { + result.neg(); + } + return result; +} + +#endif /* _INF_EPS_RATIONAL_H_ */ diff --git a/src/util/inf_rational.h b/src/util/inf_rational.h index 9e1753484..5cdfe9e93 100644 --- a/src/util/inf_rational.h +++ b/src/util/inf_rational.h @@ -223,6 +223,7 @@ class inf_rational { } friend inline inf_rational operator*(const rational & r1, const inf_rational & r2); + friend inline inf_rational operator*(const inf_rational & r1, const rational & r2); friend inline inf_rational operator/(const inf_rational & r1, const rational & r2); inf_rational & operator++() { @@ -426,6 +427,10 @@ inline inf_rational operator*(const rational & r1, const inf_rational & r2) { return result; } +inline inf_rational operator*(const inf_rational & r1, const rational & r2) { + return r2 * r1; +} + inline inf_rational operator/(const inf_rational & r1, const rational & r2) { inf_rational result(r1); result.m_first /= r2; diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index de4e760d7..3f2e224d9 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -27,14 +27,14 @@ void mem_finalize(); out_of_memory_error::out_of_memory_error():z3_error(ERR_MEMOUT) { } -volatile bool g_memory_out_of_memory = false; -bool g_memory_initialized = false; -long long g_memory_alloc_size = 0; -long long g_memory_max_size = 0; -long long g_memory_max_used_size = 0; -long long g_memory_watermark = 0; -bool g_exit_when_out_of_memory = false; -char const * g_out_of_memory_msg = "ERROR: out of memory"; +static volatile bool g_memory_out_of_memory = false; +static bool g_memory_initialized = false; +static long long g_memory_alloc_size = 0; +static long long g_memory_max_size = 0; +static long long g_memory_max_used_size = 0; +static long long g_memory_watermark = 0; +static bool g_exit_when_out_of_memory = false; +static char const * g_out_of_memory_msg = "ERROR: out of memory"; void memory::exit_when_out_of_memory(bool flag, char const * msg) { g_exit_when_out_of_memory = flag; @@ -231,9 +231,8 @@ void * memory::allocate(size_t s) { return 0; s = s + sizeof(size_t); // we allocate an extra field! void * r = malloc(s); - if (r == 0) { + if (r == 0) throw_out_of_memory(); - } *(static_cast(r)) = s; g_memory_thread_alloc_size += s; if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index dfac97626..def9f4fd5 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -453,16 +453,14 @@ bool mpf_manager::lt(mpf const & x, mpf const & y) { else if (sgn(x)) { if (!sgn(y)) return true; - else - // CMW: Problem with denormal numbers? + else return exp(y) < exp(x) || (exp(y) == exp(x) && m_mpz_manager.lt(sig(y), sig(x))); } else { // !sgn(x) if (sgn(y)) return false; - else - // CMW: Problem with denormal numbers? + else return exp(x) < exp(y) || (exp(x)==exp(y) && m_mpz_manager.lt(sig(x), sig(y))); } @@ -545,7 +543,7 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp b.get().sign = sgn_y; // Unpack a/b, this inserts the hidden bit and adjusts the exponent. - unpack(a, true); + unpack(a, false); unpack(b, false); if (exp(b) > exp(a)) @@ -556,25 +554,21 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp SASSERT(exp(a) >= exp(b)); SASSERT(exp_delta >= 0); - mpf_exp_t u_delta = exp_delta; - if (u_delta > x.sbits+2) - u_delta = x.sbits+2; + if (exp_delta > x.sbits+2) + exp_delta = x.sbits+2; TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); - TRACE("mpf_dbg", tout << "d = " << u_delta << std::endl;); - - TRACE("mpf_dbg", tout << "UP A = " << to_string(a) << std::endl;); - TRACE("mpf_dbg", tout << "UP B = " << to_string(b) << std::endl;); + TRACE("mpf_dbg", tout << "d = " << exp_delta << std::endl;); // Introduce 3 extra bits into both numbers. m_mpz_manager.mul2k(a.significand(), 3, a.significand()); m_mpz_manager.mul2k(b.significand(), 3, b.significand()); // Alignment shift with sticky bit computation. - SASSERT(u_delta <= INT_MAX); + SASSERT(exp_delta <= INT_MAX); scoped_mpz sticky_rem(m_mpz_manager); - m_mpz_manager.machine_div_rem(b.significand(), m_powers2((int)u_delta), b.significand(), sticky_rem); + m_mpz_manager.machine_div_rem(b.significand(), m_powers2((int)exp_delta), b.significand(), sticky_rem); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(b.significand())) m_mpz_manager.inc(b.significand()); @@ -582,7 +576,7 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); // Significand addition - if (sgn(a) ^ sgn(b)) { + if (sgn(a) != sgn(b)) { TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); m_mpz_manager.sub(a.significand(), b.significand(), o.significand); } @@ -765,9 +759,167 @@ void mpf_manager::div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & } void mpf_manager::fused_mul_add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o) { - // CMW: Is this precise enough? - mul(rm, x, y, o); - add(rm, o, z, o); + SASSERT(x.sbits == y.sbits && x.ebits == y.ebits && + x.sbits == y.sbits && z.ebits == z.ebits); + + TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); + TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); + TRACE("mpf_dbg", tout << "Z = " << to_string(z) << std::endl;); + + if (is_nan(x) || is_nan(y) || is_nan(z)) + mk_nan(x.ebits, x.sbits, o); + else if (is_pinf(x)) { + if (is_zero(y)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, y.sign, o); + } + else if (is_pinf(y)) { + if (is_zero(x)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, x.sign, o); + } + else if (is_ninf(x)) { + if (is_zero(y)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, !y.sign, o); + } + else if (is_ninf(y)) { + if (is_zero(x)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, !x.sign, o); + } + else if (is_inf(z)) { + set(o, z); + } + else if (is_zero(x) || is_zero(y)) { + if (is_zero(z) && rm != MPF_ROUND_TOWARD_NEGATIVE) + mk_pzero(x.ebits, x.sbits, o); + else + set(o, z); + } + else { + o.ebits = x.ebits; + o.sbits = x.sbits; + + scoped_mpf mul_res(*this, x.ebits+2, 2*x.sbits); + scoped_mpf a(*this, x.ebits, x.sbits), b(*this, x.ebits, x.sbits), c(*this, x.ebits, x.sbits); + set(a, x); + set(b, y); + set(c, z); + unpack(a, true); + unpack(b, true); + unpack(c, true); + + TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); + TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); + TRACE("mpf_dbg", tout << "C = " << to_string(c) << std::endl;); + + SASSERT(m_mpz_manager.lt(a.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.lt(b.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.ge(a.significand(), m_powers2(x.sbits-1))); + SASSERT(m_mpz_manager.ge(b.significand(), m_powers2(x.sbits-1))); + + mul_res.get().sign = (a.sign() != b.sign()); + mul_res.get().exponent = a.exponent() + b.exponent(); + m_mpz_manager.mul(a.significand(), b.significand(), mul_res.get().significand); + + TRACE("mpf_dbg", tout << "PRODUCT = " << to_string(mul_res) << std::endl;); + + // mul_res is [-1][0].[2*sbits - 2], i.e., between 2*sbits-1 and 2*sbits. + SASSERT(m_mpz_manager.lt(mul_res.significand(), m_powers2(2*x.sbits))); + SASSERT(m_mpz_manager.ge(mul_res.significand(), m_powers2(2*x.sbits - 2))); + + // Introduce extra bits into c. + m_mpz_manager.mul2k(c.significand(), x.sbits-1, c.significand()); + + SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(2 * x.sbits - 1))); + SASSERT(m_mpz_manager.is_zero(c.significand()) || + m_mpz_manager.ge(c.significand(), m_powers2(2 * x.sbits - 2))); + + TRACE("mpf_dbg", tout << "C = " << to_string(c) << std::endl;); + + if (exp(c) > exp(mul_res)) + mul_res.swap(c); + + mpf_exp_t exp_delta = exp(mul_res) - exp(c); + + SASSERT(exp(mul_res) >= exp(c) && exp_delta >= 0); + + if (exp_delta > 2 * x.sbits) + exp_delta = 2 * x.sbits; + TRACE("mpf_dbg", tout << "exp_delta = " << exp_delta << std::endl;); + + // Alignment shift with sticky bit computation. + scoped_mpz sticky_rem(m_mpz_manager); + m_mpz_manager.machine_div_rem(c.significand(), m_powers2((int)exp_delta), c.significand(), sticky_rem); + TRACE("mpf_dbg", tout << "alignment shift -> sig = " << m_mpz_manager.to_string(c.significand()) << + " sticky_rem = " << m_mpz_manager.to_string(sticky_rem) << std::endl;); + if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(c.significand())) + m_mpz_manager.inc(c.significand()); + + TRACE("mpf_dbg", tout << "M' = " << m_mpz_manager.to_string(mul_res.significand()) << std::endl;); + TRACE("mpf_dbg", tout << "C' = " << m_mpz_manager.to_string(c.significand()) << std::endl;); + + // Significand addition + if (sgn(mul_res) != sgn(c)) { + TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); + m_mpz_manager.sub(mul_res.significand(), c.significand(), o.significand); + } + else { + TRACE("mpf_dbg", tout << "ADDING" << std::endl;); + m_mpz_manager.add(mul_res.significand(), c.significand(), o.significand); + } + + TRACE("mpf_dbg", tout << "sum[-1:] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + + bool neg = m_mpz_manager.is_neg(o.significand); + TRACE("mpf_dbg", tout << "NEG=" << neg << std::endl;); + if (neg) m_mpz_manager.abs(o.significand); + + o.exponent = mul_res.exponent(); + + unsigned extra = x.sbits-4; + // Result could overflow into 4.xxx ... + SASSERT(m_mpz_manager.lt(o.significand, m_powers2(2 * x.sbits + 2))); + if(m_mpz_manager.ge(o.significand, m_powers2(2 * x.sbits + 1))) + { + extra++; + o.exponent++; + TRACE("mpf_dbg", tout << "Addition overflew!" << std::endl;); + } + + // Get rid of the extra bits. + m_mpz_manager.machine_div_rem(o.significand, m_powers2(extra), o.significand, sticky_rem); + if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(o.significand)) + m_mpz_manager.inc(o.significand); + + TRACE("mpf_dbg", tout << "sum[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + + if (m_mpz_manager.is_zero(o.significand)) + mk_zero(x.ebits, x.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); + else { + o.sign = ((!mul_res.sign() && c.sign() && neg) || + ( mul_res.sign() && !c.sign() && !neg) || + ( mul_res.sign() && c.sign())); + TRACE("mpf_dbg", tout << "before round = " << to_string(o) << std::endl << + "fs[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + round(rm, o); + } + } + } void my_mpz_sqrt(unsynch_mpz_manager & m, unsigned sbits, bool odd_exp, mpz & in, mpz & o) { @@ -938,8 +1090,8 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { mk_nan(x.ebits, x.sbits, o); else if (is_inf(y)) set(o, x); - else if (is_zero(x)) - mk_pzero(x.ebits, x.sbits, o); + else if (is_zero(x)) + set(o, x); else if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else { @@ -955,7 +1107,7 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); - + if (a.exponent() < b.exponent()) set(o, x); else { @@ -986,22 +1138,22 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { } void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { - if (is_nan(x)) + if (is_nan(x) || (is_zero(x) && is_zero(y))) set(o, y); else if (is_nan(y)) set(o, x); - else if (gt(x, y) || (is_zero(x) && is_nzero(y))) + else if (gt(x, y)) set(o, x); else set(o, y); } void mpf_manager::minimum(mpf const & x, mpf const & y, mpf & o) { - if (is_nan(x)) + if (is_nan(x) || (is_zero(x) && is_zero(y))) set(o, y); else if (is_nan(y)) set(o, x); - else if (lt(x, y) || (is_nzero(x) && is_zero(y))) + else if (lt(x, y)) set(o, x); else set(o, y); @@ -1340,6 +1492,11 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "RND: " << to_string(o) << std::endl;); + DEBUG_CODE({ + const mpz & p_m3 = m_powers2(o.sbits+5); + SASSERT(m_mpz_manager.lt(o.significand, p_m3)); + }); + // Structure of the rounder: // (s, e_out, f_out) == (s, exprd(s, post(e, sigrd(s, f)))). @@ -1354,7 +1511,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { "e_max_norm = " << e_max_norm << std::endl;); const mpz & p_m1 = m_powers2(o.sbits+2); - const mpz & p_m2 = m_powers2(o.sbits+3); + const mpz & p_m2 = m_powers2(o.sbits+3); TRACE("mpf_dbg", tout << "p_m1 = " << m_mpz_manager.to_string(p_m1) << std::endl << "p_m2 = " << m_mpz_manager.to_string(p_m2) << std::endl;); @@ -1530,7 +1687,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "DENORMAL: " << m_mpz_manager.to_string(o.significand) << std::endl;); o.exponent = mk_bot_exp(o.ebits); } - } + } TRACE("mpf_dbg", tout << "ROUNDED = " << to_string(o) << std::endl;); } diff --git a/src/util/mpff.h b/src/util/mpff.h index de71ec75e..8d4c853af 100644 --- a/src/util/mpff.h +++ b/src/util/mpff.h @@ -107,7 +107,7 @@ class mpff_manager { unsigned m_precision; //!< Number of words in the significand. Must be an even number. unsigned m_precision_bits; //!< Number of bits in the significand. Must be 32*m_precision. - vector m_significands; //!< Array containing all significands. + unsigned_vector m_significands; //!< Array containing all significands. unsigned m_capacity; //!< Number of significands that can be stored in m_significands. bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity id_gen m_id_gen; diff --git a/src/util/small_object_allocator.cpp b/src/util/small_object_allocator.cpp index 8acdccd23..edc3885cd 100644 --- a/src/util/small_object_allocator.cpp +++ b/src/util/small_object_allocator.cpp @@ -96,7 +96,7 @@ void * small_object_allocator::allocate(size_t size) { return memory::allocate(size); #endif m_alloc_size += size; - if (size > SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) + if (size > SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) return memory::allocate(size); #ifdef Z3DEBUG size_t osize = size; diff --git a/src/util/small_object_allocator.h b/src/util/small_object_allocator.h index 538a9e035..957c4f475 100644 --- a/src/util/small_object_allocator.h +++ b/src/util/small_object_allocator.h @@ -21,6 +21,7 @@ Revision History: #define _SMALL_OBJECT_ALLOCATOR_H_ #include"machine.h" +#include"debug.h" class small_object_allocator { static const unsigned CHUNK_SIZE = (8192 - sizeof(void*)*2); @@ -52,8 +53,8 @@ public: inline void * operator new(size_t s, small_object_allocator & r) { return r.allocate(s); } inline void * operator new[](size_t s, small_object_allocator & r) { return r.allocate(s); } -inline void operator delete(void * p, size_t s, small_object_allocator & r) { r.deallocate(s,p); } -inline void operator delete[](void * p, size_t s, small_object_allocator & r) { r.deallocate(s,p); } +inline void operator delete(void * p, small_object_allocator & r) { UNREACHABLE(); } +inline void operator delete[](void * p, small_object_allocator & r) { UNREACHABLE(); } #endif /* _SMALL_OBJECT_ALLOCATOR_H_ */ diff --git a/src/util/vector.h b/src/util/vector.h index 704452d0f..c9ed900a9 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -432,24 +432,29 @@ typedef svector unsigned_vector; typedef svector char_vector; typedef svector double_vector; -template -struct vector_hash { +template +struct vector_hash_tpl { Hash m_hash; - typedef vector data; + typedef Vec data; unsigned operator()(data const& v, unsigned idx) const { return m_hash(v[idx]); } - vector_hash(Hash const& h = Hash()):m_hash(h) {} + vector_hash_tpl(Hash const& h = Hash()):m_hash(h) {} unsigned operator()(data const& v) const { if (v.empty()) { return 778; } - return get_composite_hash, vector_hash>(v, v.size()); + return get_composite_hash, vector_hash_tpl>(v, v.size()); } - }; +template +struct vector_hash : public vector_hash_tpl > {}; + +template +struct svector_hash : public vector_hash_tpl > {}; + #endif /* _VECTOR_H_ */ diff --git a/src/util/warning.cpp b/src/util/warning.cpp index 0a1ac9bbc..88f807e3c 100644 --- a/src/util/warning.cpp +++ b/src/util/warning.cpp @@ -60,11 +60,11 @@ void myInvalidParameterHandler( #define END_ERR_HANDLER() {} #endif -bool g_warning_msgs = true; -bool g_use_std_stdout = false; -std::ostream* g_error_stream = 0; -std::ostream* g_warning_stream = 0; -bool g_show_error_msg_prefix = true; +static bool g_warning_msgs = true; +static bool g_use_std_stdout = false; +static std::ostream* g_error_stream = 0; +static std::ostream* g_warning_stream = 0; +static bool g_show_error_msg_prefix = true; void send_warnings_to_stdout(bool flag) { g_use_std_stdout = flag;