diff --git a/CMakeLists.txt b/CMakeLists.txt index d33f352e1..c3dec71d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,12 +289,14 @@ endif() ################################################################################ # Tracing ################################################################################ -option(ENABLE_TRACING OFF "Enable tracing") -if (ENABLE_TRACING) +option(ENABLE_TRACING_FOR_NON_DEBUG "Enable tracing in non-debug builds." OFF) +if (ENABLE_TRACING_FOR_NON_DEBUG) list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_TRACE") +else() + # Tracing is always enabled in debug builds + list(APPEND Z3_COMPONENT_CXX_DEFINES $<$:_TRACE>) endif() # Should we always enable tracing when doing a debug build? -#list(APPEND Z3_COMPONENT_CXX_DEFINES $<$:_TRACE>) ################################################################################ # Postion indepdent code diff --git a/README-CMake.md b/README-CMake.md index cdedb8374..47ec11a7c 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -265,7 +265,8 @@ The following useful options can be passed to CMake whilst configuring. * ``CMAKE_INSTALL_INCLUDEDIR`` - STRING. The path to install z3 include files (relative to ``CMAKE_INSTALL_PREFIX``), e.g. ``include``. * ``CMAKE_INSTALL_LIBDIR`` - STRING. The path to install z3 libraries (relative to ``CMAKE_INSTALL_PREFIX``), e.g. ``lib``. * ``CMAKE_INSTALL_PREFIX`` - STRING. The install prefix to use (e.g. ``/usr/local/``). -* ``ENABLE_TRACING`` - BOOL. If set to ``TRUE`` enable tracing, if set to ``FALSE`` disable tracing. +* ``CMAKE_INSTALL_PYTHON_PKG_DIR`` - STRING. The path to install the z3 python bindings. This can be relative (to ``CMAKE_INSTALL_PREFIX``) or absolute. +* ``ENABLE_TRACING_FOR_NON_DEBUG`` - BOOL. If set to ``TRUE`` enable tracing in non-debug builds, if set to ``FALSE`` disable tracing in non-debug builds. Note in debug builds tracing is always enabled. * ``BUILD_LIBZ3_SHARED`` - BOOL. If set to ``TRUE`` build libz3 as a shared library otherwise build as a static library. * ``ENABLE_EXAMPLE_TARGETS`` - BOOL. If set to ``TRUE`` add the build targets for building the API examples. * ``USE_OPENMP`` - BOOL. If set to ``TRUE`` and OpenMP support is detected build with OpenMP support. diff --git a/contrib/cmake/src/api/python/CMakeLists.txt b/contrib/cmake/src/api/python/CMakeLists.txt index 6e4096057..555333c9f 100644 --- a/contrib/cmake/src/api/python/CMakeLists.txt +++ b/contrib/cmake/src/api/python/CMakeLists.txt @@ -74,35 +74,50 @@ add_custom_target(build_z3_python_bindings ############################################################################### option(INSTALL_PYTHON_BINDINGS "Install Python bindings when invoking install target" ON) if (INSTALL_PYTHON_BINDINGS) - # Determine the installation path for the bindings message(STATUS "Emitting rules to install Z3 python bindings") - execute_process( - COMMAND "${PYTHON_EXECUTABLE}" "-c" - "import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())" - RESULT_VARIABLE exit_code - OUTPUT_VARIABLE python_pkg_dir - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + # Try to guess the installation path for the bindings + if (NOT DEFINED CMAKE_INSTALL_PYTHON_PKG_DIR) + message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR not set. Trying to guess") + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" "-c" + "import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())" + RESULT_VARIABLE exit_code + OUTPUT_VARIABLE CMAKE_INSTALL_PYTHON_PKG_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) - if (NOT ("${exit_code}" EQUAL 0)) - message(FATAL_ERROR "Failed to determine your Python package directory") + if (NOT ("${exit_code}" EQUAL 0)) + message(FATAL_ERROR "Failed to determine your Python package directory") + endif() + message(STATUS "Detected Python package directory: \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\"") + # Set a cache variable that the user can modify if needed + set(CMAKE_INSTALL_PYTHON_PKG_DIR + "${CMAKE_INSTALL_PYTHON_PKG_DIR}" + CACHE PATH + "Path to install python bindings. This can be relative or absolute.") + mark_as_advanced(CMAKE_INSTALL_PYTHON_PKG_DIR) + else() + message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR already set (\"${CMAKE_INSTALL_PYTHON_PKG_DIR}\")" + ". Not trying to guess.") endif() - message(STATUS "Detected Python package directory: \"${python_pkg_dir}\"") - # Check if path exists under the install prefix - set(python_install_dir "") - string(FIND "${python_pkg_dir}" "${CMAKE_INSTALL_PREFIX}" position) - if (NOT ("${position}" EQUAL 0)) - message(WARNING "The detected Python package directory \"${python_pkg_dir}\" " - "does not exist under the install prefix \"${CMAKE_INSTALL_PREFIX}\"." - " Running the install target may lead to a broken installation." - ) + # Check if path exists under the install prefix if it is absolute. If the + # path is relative it will be installed under the install prefix so there + # if nothing to check + if (IS_ABSOLUTE "${CMAKE_INSTALL_PYTHON_PKG_DIR}") + string(FIND "${CMAKE_INSTALL_PYTHON_PKG_DIR}" "${CMAKE_INSTALL_PREFIX}" position) + if (NOT ("${position}" EQUAL 0)) + message(WARNING "The directory to install the python bindings \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\" " + "is not under the install prefix \"${CMAKE_INSTALL_PREFIX}\"." + " Running the install target may lead to a broken installation. " + "To change the install directory modify the CMAKE_INSTALL_PYTHON_PKG_DIR cache variable." + ) + endif() endif() # Using DESTDIR still seems to work even if we use an absolute path - set(python_install_dir "${python_pkg_dir}") - message(STATUS "Python bindings will be installed to \"${python_install_dir}\"") + message(STATUS "Python bindings will be installed to \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\"") install(FILES ${build_z3_python_bindings_target_depends} - DESTINATION "${python_install_dir}" + DESTINATION "${CMAKE_INSTALL_PYTHON_PKG_DIR}" ) else() message(STATUS "Not emitting rules to install Z3 python bindings") diff --git a/contrib/cmake/src/ast/rewriter/CMakeLists.txt b/contrib/cmake/src/ast/rewriter/CMakeLists.txt index 59834ea13..921cace75 100644 --- a/contrib/cmake/src/ast/rewriter/CMakeLists.txt +++ b/contrib/cmake/src/ast/rewriter/CMakeLists.txt @@ -12,6 +12,7 @@ z3_add_component(rewriter expr_safe_replace.cpp factor_rewriter.cpp fpa_rewriter.cpp + label_rewriter.cpp mk_simplified_app.cpp pb_rewriter.cpp quant_hoist.cpp diff --git a/contrib/cmake/src/qe/CMakeLists.txt b/contrib/cmake/src/qe/CMakeLists.txt index 9db253bd6..6e82e2c96 100644 --- a/contrib/cmake/src/qe/CMakeLists.txt +++ b/contrib/cmake/src/qe/CMakeLists.txt @@ -1,21 +1,28 @@ z3_add_component(qe SOURCES nlarith_util.cpp + nlqsat.cpp qe_arith.cpp qe_arith_plugin.cpp qe_array_plugin.cpp + qe_arrays.cpp qe_bool_plugin.cpp qe_bv_plugin.cpp qe_cmd.cpp qe.cpp qe_datatype_plugin.cpp + qe_datatypes.cpp qe_dl_plugin.cpp qe_lite.cpp + qe_mbp.cpp qe_sat_tactic.cpp qe_tactic.cpp - qe_util.cpp + qsat.cpp vsubst_tactic.cpp COMPONENT_DEPENDENCIES + nlsat_tactic + nlsat sat - smt + smt + tactic ) diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index 2e2978a61..38d173108 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -57,7 +57,7 @@ def sorted_headers_by_component(l): _logger.debug("get_key({})".format(path)) path_components = [] stripped_path = path - assert 'src' in stripped_path.split(os.path.sep) + assert 'src' in stripped_path.split(os.path.sep) or 'src' in stripped_path.split('/') # Keep stripping off directory components until we hit ``src`` while os.path.basename(stripped_path) != 'src': path_components.append(os.path.basename(stripped_path)) @@ -214,7 +214,7 @@ def mk_gparams_register_modules_internal(component_src_dirs, path): """ assert isinstance(component_src_dirs, list) assert check_dir_exists(path) - cmds = [] + cmds = [] mod_cmds = [] mod_descrs = [] fullname = os.path.join(path, 'gparams_register_modules.cpp') diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 3e476b99e..e7177000f 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -57,7 +57,7 @@ def init_project_def(): add_lib('fuzzing', ['ast'], 'test/fuzzing') add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') - add_lib('qe', ['smt','sat'], 'qe') + add_lib('qe', ['smt','sat','nlsat','tactic','nlsat_tactic'], 'qe') add_lib('duality', ['smt', 'interp', 'qe']) add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') add_lib('dataflow', ['muz'], 'muz/dataflow') diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 68122a941..fad4ca7a2 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -604,8 +604,16 @@ namespace z3 { /** \brief Return true if this expression is a numeral. + Specialized functions also return representations for the numerals as + small integers, 64 bit integers or rational or decimal strings. */ bool is_numeral() const { return kind() == Z3_NUMERAL_AST; } + bool is_numeral_i64(__int64& i) const { bool r = 0 != Z3_get_numeral_int64(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_u64(__uint64& i) const { bool r = 0 != Z3_get_numeral_uint64(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_i(int& i) const { bool r = 0 != Z3_get_numeral_int(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_u(unsigned& i) const { bool r = 0 != Z3_get_numeral_uint(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral(std::string& s) const { if (!is_numeral()) return false; s = Z3_get_numeral_string(ctx(), m_ast); check_error(); return true; } + bool is_numeral(std::string& s, unsigned precision) const { if (!is_numeral()) return false; s = Z3_get_numeral_decimal_string(ctx(), m_ast, precision); check_error(); return true; } /** \brief Return true if this expression is an application. */ @@ -625,7 +633,7 @@ namespace z3 { /** \brief Return true if expression is an algebraic number. */ - bool is_algebraic() const { return Z3_is_algebraic_number(ctx(), m_ast); } + bool is_algebraic() const { return 0 != Z3_is_algebraic_number(ctx(), m_ast); } /** \brief Return true if this expression is well sorted (aka type correct). @@ -649,12 +657,11 @@ namespace z3 { \pre is_numeral() */ - int get_numeral_int() const { - assert(is_numeral()); + int get_numeral_int() const { int result; - Z3_bool state = Z3_get_numeral_int(ctx(), m_ast, &result); - if (state != Z3_TRUE) + if (!is_numeral_i(result)) { throw exception("numeral does not fit in machine int"); + } return result; } @@ -667,9 +674,9 @@ namespace z3 { unsigned get_numeral_uint() const { assert(is_numeral()); unsigned result; - Z3_bool state = Z3_get_numeral_uint(ctx(), m_ast, &result); - if (state != Z3_TRUE) + if (!is_numeral_u(result)) { throw exception("numeral does not fit in machine uint"); + } return result; } @@ -682,9 +689,9 @@ namespace z3 { __int64 get_numeral_int64() const { assert(is_numeral()); __int64 result; - Z3_bool state = Z3_get_numeral_int64(ctx(), m_ast, &result); - if (state != Z3_TRUE) + if (!is_numeral_i64(result)) { throw exception("numeral does not fit in machine __int64"); + } return result; } @@ -697,9 +704,9 @@ namespace z3 { __uint64 get_numeral_uint64() const { assert(is_numeral()); __uint64 result; - Z3_bool state = Z3_get_numeral_uint64(ctx(), m_ast, &result); - if (state != Z3_TRUE) + if (!is_numeral_u64(result)) { throw exception("numeral does not fit in machine __uint64"); + } return result; } diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 374c71be4..211b6777e 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -3940,7 +3940,7 @@ class ArraySortRef(SortRef): >>> A.range() Bool """ - return _to_sort_ref(Z3_get_array_sort_range(self.ctx_ref(), self.ast), self.ctx) + return _to_sort_ref(Z3_get_array_sort_range(self.ctx_ref(), self.ast), self.ctx) class ArrayRef(ExprRef): """Array expressions. """ @@ -4162,6 +4162,7 @@ def Select(a, i): _z3_assert(is_array(a), "First argument must be a Z3 array expression") return a[i] + def Map(f, *args): """Return a Z3 map array expression. diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index 22751aa46..546f037de 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -664,3 +664,45 @@ algebraic_numbers::anum const & arith_util::to_irrational_algebraic_numeral(expr SASSERT(is_irrational_algebraic_numeral(n)); return plugin().aw().to_anum(to_app(n)->get_decl()); } + +expr_ref arith_util::mk_mul_simplify(expr_ref_vector const& args) { + return mk_mul_simplify(args.size(), args.c_ptr()); + +} +expr_ref arith_util::mk_mul_simplify(unsigned sz, expr* const* args) { + expr_ref result(m_manager); + + switch (sz) { + case 0: + result = mk_numeral(rational(1), true); + break; + case 1: + result = args[0]; + break; + default: + result = mk_mul(sz, args); + break; + } + return result; +} + +expr_ref arith_util::mk_add_simplify(expr_ref_vector const& args) { + return mk_add_simplify(args.size(), args.c_ptr()); + +} +expr_ref arith_util::mk_add_simplify(unsigned sz, expr* const* args) { + expr_ref result(m_manager); + + switch (sz) { + case 0: + result = mk_numeral(rational(0), true); + break; + case 1: + result = args[0]; + break; + default: + result = mk_add(sz, args); + break; + } + return result; +} diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 25356a667..b72a55e34 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -149,7 +149,6 @@ protected: func_decl * m_mod_0_decl; func_decl * m_u_asin_decl; func_decl * m_u_acos_decl; - ptr_vector m_small_ints; ptr_vector m_small_reals; @@ -416,6 +415,11 @@ public: return m_manager.mk_eq(lhs, rhs); } + expr_ref mk_mul_simplify(expr_ref_vector const& args); + expr_ref mk_mul_simplify(unsigned sz, expr* const* args); + + expr_ref mk_add_simplify(expr_ref_vector const& args); + expr_ref mk_add_simplify(unsigned sz, expr* const* args); }; #endif /* ARITH_DECL_PLUGIN_H_ */ diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 7d5248b30..1afa13117 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -1200,6 +1200,14 @@ std::ostream& operator<<(std::ostream& out, app_ref const& e) { return out << mk_ismt2_pp(e.get(), e.get_manager()); } +std::ostream& operator<<(std::ostream& out, func_decl_ref const& e) { + return out << mk_ismt2_pp(e.get(), e.get_manager()); +} + +std::ostream& operator<<(std::ostream& out, sort_ref const& e) { + return out << mk_ismt2_pp(e.get(), e.get_manager()); +} + std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) { for (unsigned i = 0; i < e.size(); ++i) { out << mk_ismt2_pp(e[i], e.get_manager()); @@ -1216,6 +1224,18 @@ std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) { return out; } +std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e) { + for (unsigned i = 0; i < e.size(); ++i) + out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + return out; +} + +std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e) { + for (unsigned i = 0; i < e.size(); ++i) + out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + return out; +} + #ifdef Z3DEBUG void pp(expr const * n, ast_manager & m) { std::cout << mk_ismt2_pp(const_cast(n), m) << std::endl; diff --git a/src/ast/ast_smt2_pp.h b/src/ast/ast_smt2_pp.h index 45d18ceff..f2d177041 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.h @@ -117,8 +117,13 @@ std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p); std::ostream& operator<<(std::ostream& out, expr_ref const& e); std::ostream& operator<<(std::ostream& out, app_ref const& e); +std::ostream& operator<<(std::ostream& out, func_decl_ref const& e); +std::ostream& operator<<(std::ostream& out, sort_ref const& e); + std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e); std::ostream& operator<<(std::ostream& out, app_ref_vector const& e); +std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e); +std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e); #endif diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index eba4d526b..0129a88d5 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -170,7 +170,6 @@ app* mk_and(ast_manager & m, unsigned num_args, app * const * args) { return to_app(mk_and(m, num_args, (expr* const*) args)); } - expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) { if (num_args == 0) return m.mk_false(); @@ -188,10 +187,43 @@ expr * mk_not(ast_manager & m, expr * arg) { expr * atom; if (m.is_not(arg, atom)) return atom; + else if (m.is_true(arg)) + return m.mk_false(); + else if (m.is_false(arg)) + return m.mk_true(); else return m.mk_not(arg); } +expr_ref push_not(const expr_ref& e) { + ast_manager& m = e.get_manager(); + if (!is_app(e)) { + return expr_ref(m.mk_not(e), m); + } + app* a = to_app(e); + if (m.is_and(a)) { + if (a->get_num_args() == 0) { + return expr_ref(m.mk_false(), m); + } + expr_ref_vector args(m); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + args.push_back(push_not(expr_ref(a->get_arg(i), m))); + } + return mk_or(args); + } + if (m.is_or(a)) { + if (a->get_num_args() == 0) { + return expr_ref(m.mk_true(), m); + } + expr_ref_vector args(m); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + args.push_back(push_not(expr_ref(a->get_arg(i), m))); + } + return mk_and(args); + } + return expr_ref(mk_not(m, e), m); +} + expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) { expr_ref_buffer new_diseqs(m); for (unsigned i = 0; i < num_args; i++) { @@ -201,6 +233,24 @@ expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) return mk_and(m, new_diseqs.size(), new_diseqs.c_ptr()); } +expr* mk_distinct(ast_manager& m, unsigned num_args, expr * const * args) { + switch (num_args) { + case 0: + case 1: + return m.mk_true(); + case 2: + return m.mk_not(m.mk_eq(args[0], args[1])); + default: + return m.mk_distinct(num_args, args); + } +} + +expr_ref mk_distinct(expr_ref_vector const& args) { + ast_manager& m = args.get_manager(); + return expr_ref(mk_distinct(m, args.size(), args.c_ptr()), m); +} + + void flatten_and(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index 311001a19..6244e16b9 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -121,18 +121,29 @@ app * mk_or(ast_manager & m, unsigned num_args, app * const * args); inline app_ref mk_or(app_ref_vector const& args) { return app_ref(mk_or(args.get_manager(), args.size(), args.c_ptr()), args.get_manager()); } inline expr_ref mk_or(expr_ref_vector const& args) { return expr_ref(mk_or(args.get_manager(), args.size(), args.c_ptr()), args.get_manager()); } - /** Return a if arg = (not a) Retur (not arg) otherwise */ expr * mk_not(ast_manager & m, expr * arg); +/** + Negate and push over conjunction or disjunction. + */ +expr_ref push_not(const expr_ref& arg); + /** Return the expression (and (not (= args[0] args[1])) (not (= args[0] args[2])) ... (not (= args[num_args-2] args[num_args-1]))) */ expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args); +/** + Create simplified distinct term. Binary distinct becomes a single disequality. + */ +expr * mk_distinct(ast_manager& m, unsigned num_args, expr * const * args); + +expr_ref mk_distinct(expr_ref_vector const& args); + /** \brief Collect top-level conjunctions and disjunctions. */ diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index f65462d1b..b1f3491fe 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -865,6 +865,12 @@ sort * bv_util::mk_sort(unsigned bv_size) { return m_manager.mk_sort(get_fid(), BV_SORT, 1, p); } +unsigned bv_util::get_int2bv_size(parameter const& p) { + int sz; + VERIFY(m_plugin->get_int2bv_size(1, &p, sz)); + return static_cast(sz); +} + app * bv_util::mk_bv2int(expr* e) { sort* s = m_manager.mk_sort(m_manager.mk_family_id("arith"), INT_SORT); parameter p(s); diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index 563622338..ac0ff7f79 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -234,7 +234,6 @@ protected: func_decl * mk_mkbv(unsigned arity, sort * const * domain); - bool get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result); func_decl * mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity); @@ -267,6 +266,8 @@ public: virtual expr * get_some_value(sort * s); + bool get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result); + virtual bool is_considered_uninterpreted(func_decl * f) { if (f->get_family_id() != get_family_id()) return false; @@ -390,6 +391,8 @@ public: return static_cast(s->get_parameter(0).get_int()); } unsigned get_bv_size(expr const * n) const { return get_bv_size(m_manager.get_sort(n)); } + unsigned get_int2bv_size(parameter const& p); + app * mk_ule(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_ULEQ, arg1, arg2); } app * mk_sle(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_SLEQ, arg1, arg2); } @@ -427,6 +430,7 @@ public: app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } app * mk_bv(unsigned n, expr* const* es) { return m_manager.mk_app(get_fid(), OP_MKBV, n, es); } + }; #endif /* BV_DECL_PLUGIN_H_ */ diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index c185540b7..d837ebb22 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -1000,3 +1000,25 @@ void datatype_util::display_datatype(sort *s0, std::ostream& strm) { } } + +bool datatype_util::is_func_decl(datatype_op_kind k, unsigned num_params, parameter const* params, func_decl* f) { + bool eq = + f->get_decl_kind() == k && + f->get_family_id() == m_family_id && + f->get_num_parameters() == num_params; + for (unsigned i = 0; eq && i < num_params; ++i) { + eq = params[i] == f->get_parameter(i); + } + return eq; +} + +bool datatype_util::is_constructor_of(unsigned num_params, parameter const* params, func_decl* f) { + return + num_params == 2 && + m_family_id == f->get_family_id() && + OP_DT_CONSTRUCTOR == f->get_decl_kind() && + 2 == f->get_num_parameters() && + params[0] == f->get_parameter(0) && + params[1] == f->get_parameter(1); +} + diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 99be75493..51d33c896 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -209,6 +209,8 @@ public: func_decl * get_recognizer_constructor(func_decl * recognizer); family_id get_family_id() const { return m_family_id; } bool are_siblings(sort * s1, sort * s2); + bool is_func_decl(datatype_op_kind k, unsigned num_params, parameter const* params, func_decl* f); + bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f); void reset(); void display_datatype(sort *s, std::ostream& strm); diff --git a/src/ast/expr_abstract.cpp b/src/ast/expr_abstract.cpp index 949168cad..513162043 100644 --- a/src/ast/expr_abstract.cpp +++ b/src/ast/expr_abstract.cpp @@ -22,6 +22,10 @@ Notes: void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { + if (num_bound == 0) { + result = n; + return; + } expr * curr = 0, *b = 0; SASSERT(n->get_ref_count() > 0); @@ -106,3 +110,27 @@ void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* cons expr_abstractor abs(m); abs(base, num_bound, bound, n, result); } + +expr_ref mk_quantifier(bool is_forall, ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { + expr_ref result(m); + expr_abstract(m, 0, num_bound, (expr* const*)bound, n, result); + if (num_bound > 0) { + ptr_vector sorts; + svector names; + for (unsigned i = 0; i < num_bound; ++i) { + sorts.push_back(m.get_sort(bound[i])); + names.push_back(bound[i]->get_decl()->get_name()); + } + result = m.mk_quantifier(is_forall, num_bound, sorts.c_ptr(), names.c_ptr(), result); + } + return result; + +} + +expr_ref mk_forall(ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { + return mk_quantifier(true, m, num_bound, bound, n); +} + +expr_ref mk_exists(ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { + return mk_quantifier(false, m, num_bound, bound, n); +} diff --git a/src/ast/expr_abstract.h b/src/ast/expr_abstract.h index 7f43ecf83..5c91a914e 100644 --- a/src/ast/expr_abstract.h +++ b/src/ast/expr_abstract.h @@ -33,6 +33,8 @@ public: }; void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); +expr_ref mk_forall(ast_manager& m, unsigned num_bound, app* const* bound, expr* n); +expr_ref mk_exists(ast_manager& m, unsigned num_bound, app* const* bound, expr* n); #endif diff --git a/src/ast/rewriter/expr_safe_replace.cpp b/src/ast/rewriter/expr_safe_replace.cpp index 25a8fd657..2655ac00d 100644 --- a/src/ast/rewriter/expr_safe_replace.cpp +++ b/src/ast/rewriter/expr_safe_replace.cpp @@ -30,6 +30,14 @@ void expr_safe_replace::insert(expr* src, expr* dst) { m_subst.insert(src, dst); } +void expr_safe_replace::operator()(expr_ref_vector& es) { + expr_ref val(m); + for (unsigned i = 0; i < es.size(); ++i) { + (*this)(es[i].get(), val); + es[i] = val; + } +} + void expr_safe_replace::operator()(expr* e, expr_ref& res) { m_todo.push_back(e); expr* a, *b; diff --git a/src/ast/rewriter/expr_safe_replace.h b/src/ast/rewriter/expr_safe_replace.h index fe7033439..84a74ccaa 100644 --- a/src/ast/rewriter/expr_safe_replace.h +++ b/src/ast/rewriter/expr_safe_replace.h @@ -42,6 +42,8 @@ public: void operator()(expr* src, expr_ref& e); + void operator()(expr_ref_vector& es); + void apply_substitution(expr* s, expr* def, expr_ref& t); void reset(); diff --git a/src/ast/rewriter/label_rewriter.cpp b/src/ast/rewriter/label_rewriter.cpp new file mode 100644 index 000000000..3017f413c --- /dev/null +++ b/src/ast/rewriter/label_rewriter.cpp @@ -0,0 +1,53 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + label_rewriter.cpp + +Abstract: + + Basic rewriting rules for removing labels. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-19-10 + +Notes: + +--*/ + +#include"rewriter.h" +#include"rewriter_def.h" +#include"label_rewriter.h" + + +label_rewriter::label_rewriter(ast_manager & m) : + m_label_fid(m.get_label_family_id()), + m_rwr(m, false, *this) {} + +label_rewriter::~label_rewriter() {} + +br_status label_rewriter::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 label_rewriter::remove_labels(expr_ref& fml, proof_ref& pr) { + ast_manager& m = fml.get_manager(); + expr_ref tmp(m); + m_rwr(fml, tmp); + if (pr && fml != tmp) { + pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp)); + } + fml = tmp; +} + + +//template class rewriter_tpl; diff --git a/src/ast/rewriter/label_rewriter.h b/src/ast/rewriter/label_rewriter.h new file mode 100644 index 000000000..6400aa442 --- /dev/null +++ b/src/ast/rewriter/label_rewriter.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + label_rewriter.h + +Abstract: + + Basic rewriting rules for labels. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-19-10 + +Notes: + +--*/ +#ifndef LABEL_REWRITER_H_ +#define LABEL_REWRITER_H_ + +#include"ast.h" +#include"rewriter_types.h" + + +class label_rewriter : public default_rewriter_cfg { + family_id m_label_fid; + rewriter_tpl m_rwr; +public: + label_rewriter(ast_manager & m); + ~label_rewriter(); + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + proof_ref & result_pr); + + + void remove_labels(expr_ref& fml, proof_ref& pr); + +}; + +#endif diff --git a/src/ast/rewriter/quant_hoist.cpp b/src/ast/rewriter/quant_hoist.cpp index dd9062828..e59a079e7 100644 --- a/src/ast/rewriter/quant_hoist.cpp +++ b/src/ast/rewriter/quant_hoist.cpp @@ -73,7 +73,7 @@ public: unsigned nd = q->get_num_decls(); for (unsigned i = 0; i < nd; ++i) { sort* s = q->get_decl_sort(i); - app* a = m.mk_fresh_const("x", s); + app* a = m.mk_fresh_const(q->get_decl_name(i).str().c_str(), s); vars.push_back(a); } expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd); @@ -154,9 +154,7 @@ private: } quantifier_type& negate(quantifier_type& qt) { - TRACE("qe", display(qt, tout); tout << "\n";); qt = static_cast(qt ^0x1); - TRACE("qe", display(qt, tout); tout << "\n";); return qt; } @@ -205,6 +203,7 @@ private: case AST_APP: { expr_ref_vector args(m); expr_ref tmp(m); + expr* t1, *t2, *t3; unsigned num_args = 0; app* a = to_app(fml); if (m.is_and(fml)) { @@ -228,16 +227,35 @@ private: negate(qt); result = m.mk_not(tmp); } - else if (m.is_implies(fml)) { - pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp); + else if (m.is_implies(fml, t1, t2)) { + pull_quantifier(t1, negate(qt), vars, tmp); negate(qt); - pull_quantifier(to_app(fml)->get_arg(1), qt, vars, result); + pull_quantifier(t2, qt, vars, result); result = m.mk_implies(tmp, result); } - else if (m.is_ite(fml)) { - pull_quantifier(to_app(fml)->get_arg(1), qt, vars, tmp); - pull_quantifier(to_app(fml)->get_arg(2), qt, vars, result); - result = m.mk_ite(to_app(fml)->get_arg(0), tmp, result); + else if (m.is_ite(fml, t1, t2, t3)) { + expr_ref tt1(m), tt2(m), tt3(m), ntt1(m), nt1(m); + pull_quantifier(t2, qt, vars, tt2); + pull_quantifier(t3, qt, vars, tt3); + if (has_quantifiers(t1)) { + pull_quantifier(t1, qt, vars, tt1); + nt1 = m.mk_not(t1); + pull_quantifier(nt1, qt, vars, ntt1); + result = m.mk_and(m.mk_or(ntt1, tt2), m.mk_or(tt1, tt3)); + } + else { + result = m.mk_ite(t1, tt2, tt3); + } + } + else if ((m.is_eq(fml, t1, t2) && m.is_bool(t1)) || m.is_iff(fml, t1, t2)) { + expr_ref tt1(m), tt2(m), ntt1(m), ntt2(m), nt1(m), nt2(m); + pull_quantifier(t1, qt, vars, tt1); + pull_quantifier(t2, qt, vars, tt2); + nt1 = m.mk_not(t1); + nt2 = m.mk_not(t2); + pull_quantifier(nt1, qt, vars, ntt1); + pull_quantifier(nt2, qt, vars, ntt2); + result = m.mk_and(m.mk_or(ntt1, tt2), m.mk_or(ntt2, tt1)); } else { // the formula contains a quantifier, but it is "inaccessible" diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index a91a681d9..ab711dde7 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -578,6 +578,16 @@ struct th_rewriter_cfg : public default_rewriter_cfg { return st; } + expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args) { + expr_ref result(m()); + proof_ref pr(m()); + if (BR_FAILED == reduce_app(f, num_args, args, result, pr)) { + result = m().mk_app(f, num_args, args); + } + return result; + } + + bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, @@ -695,6 +705,9 @@ struct th_rewriter::imp : public rewriter_tpl { rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } + expr_ref mk_app(func_decl* f, unsigned sz, expr* const* args) { + return m_cfg.mk_app(f, sz, args); + } }; th_rewriter::th_rewriter(ast_manager & m, params_ref const & p): @@ -776,3 +789,7 @@ void th_rewriter::reset_used_dependencies() { m_imp->cfg().m_used_dependencies = 0; } } + +expr_ref th_rewriter::mk_app(func_decl* f, unsigned num_args, expr* const* args) { + return m_imp->mk_app(f, num_args, args); +} diff --git a/src/ast/rewriter/th_rewriter.h b/src/ast/rewriter/th_rewriter.h index 222625077..bd5ce9c66 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -45,6 +45,8 @@ public: void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result); + expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args); + void cleanup(); void reset(); diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index 1c5da3829..f044c9736 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -303,8 +303,8 @@ public: goal_ref g = alloc(goal, m, ctx.produce_proofs(), ctx.produce_models(), ctx.produce_unsat_cores()); assert_exprs_from(ctx, *g); - unsigned timeout = p.get_uint("timeout", UINT_MAX); - unsigned rlimit = p.get_uint("rlimit", 0); + unsigned timeout = p.get_uint("timeout", ctx.params().m_timeout); + unsigned rlimit = p.get_uint("rlimit", ctx.params().m_rlimit); goal_ref_buffer result_goals; model_converter_ref mc; diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index e00c1f29f..f4eb8a003 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -539,7 +539,6 @@ namespace algebraic_numbers { } bool factor(scoped_upoly const & up, factors & r) { - // std::cout << "factor: "; upm().display(std::cout, up); std::cout << std::endl; if (m_factor) { return upm().factor(up, r, m_factor_params); } @@ -547,7 +546,7 @@ namespace algebraic_numbers { scoped_upoly & up_sqf = m_isolate_tmp3; up_sqf.reset(); upm().square_free(up.size(), up.c_ptr(), up_sqf); - TRACE("anum_bug", upm().display(tout, up_sqf.size(), up_sqf.c_ptr()); tout << "\n";); + TRACE("algebraic", upm().display(tout, up_sqf.size(), up_sqf.c_ptr()); tout << "\n";); r.push_back(up_sqf, 1); return false; } @@ -566,6 +565,7 @@ namespace algebraic_numbers { } void isolate_roots(scoped_upoly const & up, numeral_vector & roots) { + TRACE("algebraic", upm().display(tout, up); tout << "\n";); if (up.empty()) return; // ignore the zero polynomial factors & fs = m_isolate_factors; @@ -586,6 +586,7 @@ namespace algebraic_numbers { upolynomial::numeral_vector const & f = fs[i]; // polynomial f contains the non zero roots unsigned d = upm().degree(f); + TRACE("algebraic", tout << "factor " << i << " degree: " << d << "\n";); if (d == 0) continue; // found all roots of f scoped_mpq r(qm()); @@ -601,8 +602,9 @@ namespace algebraic_numbers { } SASSERT(m_isolate_roots.empty() && m_isolate_lowers.empty() && m_isolate_uppers.empty()); upm().sqf_isolate_roots(f.size(), f.c_ptr(), bqm(), m_isolate_roots, m_isolate_lowers, m_isolate_uppers); - // collect rational/basic roots + // collect rational/basic roots unsigned sz = m_isolate_roots.size(); + TRACE("algebraic", tout << "isolated roots: " << sz << "\n";); for (unsigned i = 0; i < sz; i++) { to_mpq(qm(), m_isolate_roots[i], r); roots.push_back(numeral(mk_basic_cell(r))); diff --git a/src/model/model_params.pyg b/src/model/model_params.pyg index dfa3748bc..14e83952c 100644 --- a/src/model/model_params.pyg +++ b/src/model/model_params.pyg @@ -4,6 +4,5 @@ def_module_params('model', ('v1', BOOL, False, 'use Z3 version 1.x pretty printer'), ('v2', BOOL, False, 'use Z3 version 2.x (x <= 16) pretty printer'), ('compact', BOOL, False, 'try to compact function graph (i.e., function interpretations that are lookup tables)'), - ('new_eval', BOOL, True, 'use new evaluator (temporary parameter for testing)'), )) diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 693391bef..56cc6e154 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -55,8 +55,7 @@ namespace datalog { m_args(m), m_hnf(m), m_qe(m), - m_cfg(m), - m_rwr(m, false, m_cfg), + m_rwr(m), m_ufproc(m) {} void rule_manager::inc_ref(rule * r) { @@ -76,28 +75,8 @@ namespace datalog { } } - rule_manager::remove_label_cfg::~remove_label_cfg() {} - - 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); - m_rwr(fml, tmp); - if (pr && fml != tmp) { - - pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp)); - } - fml = tmp; + m_rwr.remove_labels(fml, pr); } var_idx_set& rule_manager::collect_vars(expr* e) { @@ -1092,5 +1071,4 @@ namespace datalog { }; -template class rewriter_tpl; diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index 2261a7a00..f7dc18b5e 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -32,6 +32,7 @@ Revision History: #include"qe_lite.h" #include"var_subst.h" #include"datatype_decl_plugin.h" +#include"label_rewriter.h" namespace datalog { @@ -102,16 +103,6 @@ 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; @@ -124,8 +115,7 @@ namespace datalog { svector m_neg; hnf m_hnf; qe_lite m_qe; - remove_label_cfg m_cfg; - rewriter_tpl m_rwr; + label_rewriter m_rwr; mutable uninterpreted_function_finder_proc m_ufproc; mutable quantifier_finder_proc m_qproc; mutable expr_sparse_mark m_visited; diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 8b4344ce6..f20a77bda 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -29,7 +29,6 @@ Revision History: #include"ast_counter.h" #include"statistics.h" #include"lbool.h" -#include"qe_util.h" namespace datalog { diff --git a/src/muz/pdr/pdr_closure.cpp b/src/muz/pdr/pdr_closure.cpp index eacbb0b28..746b12416 100644 --- a/src/muz/pdr/pdr_closure.cpp +++ b/src/muz/pdr/pdr_closure.cpp @@ -20,6 +20,7 @@ Revision History: #include "pdr_closure.h" #include "pdr_context.h" #include "expr_safe_replace.h" +#include "ast_util.h" namespace pdr { @@ -147,7 +148,7 @@ namespace pdr { for (unsigned i = 0; i < fmls.size(); ++i) { fmls[i] = close_fml(fmls[i].get()); } - return qe::mk_and(fmls); + return expr_ref(mk_and(fmls), m); } expr_ref closure::relax(unsigned i, expr* fml) { @@ -169,7 +170,7 @@ namespace pdr { for (unsigned i = 0; i < As.size(); ++i) { fmls.push_back(relax(i, As[i])); } - B = qe::mk_and(fmls); + B = mk_and(fmls); return B; } diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index bd7ce0b3c..1c25b015d 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -45,7 +45,6 @@ Notes: #include "smt_value_sort.h" #include "proof_utils.h" #include "dl_boogie_proof.h" -#include "qe_util.h" #include "scoped_proof.h" #include "blast_term_ite_tactic.h" #include "model_implicant.h" diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index 94265e95d..0565cb23b 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -201,7 +201,7 @@ namespace pdr { lits.push_back(extract_consequence(lo, hi)); lo = hi; } - res = qe::mk_or(lits); + res = mk_or(lits); IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } }); #endif } @@ -415,6 +415,7 @@ namespace pdr { return false; } } + }; class collect_pure_proc { diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index fd700cfd7..5b7e11877 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -117,7 +117,7 @@ namespace pdr { void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { ast_manager& m = n.pt().get_manager(); if (core.empty()) return; - expr_ref A(m), B(qe::mk_and(core)), C(m); + expr_ref A(m), B(mk_and(core)), C(m); expr_ref_vector Bs(m); flatten_or(B, Bs); A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level()); @@ -129,13 +129,13 @@ namespace pdr { if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) { TRACE("pdr", tout << "Old core:\n" << mk_pp(B, m) << "\n"; - tout << "New core:\n" << mk_pp(qe::mk_and(lemmas), m) << "\n";); - Bs[i] = qe::mk_and(lemmas); + tout << "New core:\n" << mk_and(lemmas) << "\n";); + Bs[i] = mk_and(lemmas); change = true; } } if (change) { - C = qe::mk_or(Bs); + C = mk_or(Bs); TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";); core.reset(); flatten_and(C, core); @@ -186,7 +186,7 @@ namespace pdr { } closure cl(n.pt(), m_is_closure); - expr_ref fml1 = qe::mk_and(core); + expr_ref fml1 = mk_and(core); expr_ref fml2 = n.pt().get_formulas(n.level(), false); fml1_2.push_back(fml1); fml1_2.push_back(0); @@ -205,7 +205,7 @@ namespace pdr { if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) { new_cores.push_back(std::make_pair(conv2, uses_level1)); change = true; - expr_ref state1 = qe::mk_and(conv2); + expr_ref state1 = mk_and(conv2); TRACE("pdr", tout << mk_pp(state, m) << "\n"; tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";); @@ -593,7 +593,7 @@ namespace pdr { for (unsigned i = ut_size; i < t_size; i++) { conj.push_back(rule.get_tail(i)); } - result = qe::mk_and(conj); + result = mk_and(conj); if (!sub.empty()) { expr_ref tmp = result; var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result); @@ -685,7 +685,7 @@ namespace pdr { for (unsigned i = 0; i < rules.size(); ++i) { fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[i]))); } - fml = qe::mk_and(fmls); + fml = mk_and(fmls); TRACE("pdr", tout << mk_pp(fml, m) << "\n";); return fml; } @@ -741,7 +741,7 @@ namespace pdr { } } - expr_ref result = qe::mk_and(conjs); + expr_ref result = mk_and(conjs); TRACE("pdr", tout << mk_pp(result, m) << "\n";); return result; } diff --git a/src/muz/rel/check_relation.cpp b/src/muz/rel/check_relation.cpp index 7c6dac5e4..e630d7083 100644 --- a/src/muz/rel/check_relation.cpp +++ b/src/muz/rel/check_relation.cpp @@ -6,7 +6,6 @@ Copyright (c) 2015 Microsoft Corporation #include "check_relation.h" #include "dl_relation_manager.h" -#include "qe_util.h" #include "ast_util.h" #include "smt_kernel.h" #include diff --git a/src/muz/rel/udoc_relation.cpp b/src/muz/rel/udoc_relation.cpp index 140d1e94d..dda674131 100644 --- a/src/muz/rel/udoc_relation.cpp +++ b/src/muz/rel/udoc_relation.cpp @@ -23,7 +23,6 @@ Notes: --*/ #include "udoc_relation.h" #include "dl_relation_manager.h" -#include "qe_util.h" #include "ast_util.h" #include "smt_kernel.h" diff --git a/src/nlsat/nlsat_assignment.h b/src/nlsat/nlsat_assignment.h index 178a7f738..a52a84f3b 100644 --- a/src/nlsat/nlsat_assignment.h +++ b/src/nlsat/nlsat_assignment.h @@ -39,6 +39,17 @@ namespace nlsat { m_values.swap(other.m_values); m_assigned.swap(other.m_assigned); } + void copy(assignment const& other) { + m_assigned.reset(); + m_assigned.append(other.m_assigned); + m_values.reserve(m_assigned.size(), anum()); + for (unsigned i = 0; i < m_assigned.size(); ++i) { + if (is_assigned(i)) { + am().set(m_values[i], other.value(i)); + } + } + } + void set_core(var x, anum & v) { m_values.reserve(x+1, anum()); m_assigned.reserve(x+1, false); @@ -52,11 +63,26 @@ namespace nlsat { am().set(m_values[x], v); } void reset(var x) { if (x < m_assigned.size()) m_assigned[x] = false; } + void reset() { m_assigned.reset(); } bool is_assigned(var x) const { return m_assigned.get(x, false); } anum const & value(var x) const { return m_values[x]; } virtual anum_manager & m() const { return am(); } virtual bool contains(var x) const { return is_assigned(x); } virtual anum const & operator()(var x) const { SASSERT(is_assigned(x)); return value(x); } + void swap(var x, var y) { + SASSERT(x < m_values.size() && y < m_values.size()); + std::swap(m_assigned[x], m_assigned[y]); + std::swap(m_values[x], m_values[y]); + } + void display(std::ostream& out) const { + for (unsigned i = 0; i < m_assigned.size(); ++i) { + if (m_assigned[i]) { + out << "x" << i << " := "; + m_values.m().display(out, m_values[i]); + out << "\n"; + } + } + } }; /** diff --git a/src/nlsat/nlsat_evaluator.cpp b/src/nlsat/nlsat_evaluator.cpp index 92c5634a1..db18d8854 100644 --- a/src/nlsat/nlsat_evaluator.cpp +++ b/src/nlsat/nlsat_evaluator.cpp @@ -18,10 +18,12 @@ Revision History: --*/ #include"nlsat_evaluator.h" +#include"nlsat_solver.h" namespace nlsat { struct evaluator::imp { + solver& m_solver; assignment const & m_assignment; pmanager & m_pm; small_object_allocator & m_allocator; @@ -357,7 +359,8 @@ namespace nlsat { sign_table m_sign_table_tmp; - imp(assignment const & x2v, pmanager & pm, small_object_allocator & allocator): + imp(solver& s, assignment const & x2v, pmanager & pm, small_object_allocator & allocator): + m_solver(s), m_assignment(x2v), m_pm(pm), m_allocator(allocator), @@ -420,10 +423,25 @@ namespace nlsat { scoped_anum_vector & roots = m_tmp_values; roots.reset(); m_am.isolate_roots(polynomial_ref(a->p(), m_pm), undef_var_assignment(m_assignment, a->x()), roots); + TRACE("nlsat", + m_solver.display(tout << (neg?"!":""), *a); tout << "\n"; + if (roots.empty()) { + tout << "No roots\n"; + } + else { + tout << "Roots for "; + for (unsigned i = 0; i < roots.size(); ++i) { + m_am.display_interval(tout, roots[i]); tout << " "; + } + tout << "\n"; + } + m_assignment.display(tout); + ); SASSERT(a->i() > 0); - if (a->i() > roots.size()) - return false; // p does have sufficient roots - int sign = m_am.compare(m_assignment.value(a->x()), roots[a->i() - 1]); + if (a->i() > roots.size()) { + return neg; + } + int sign = m_am.compare(m_assignment.value(a->x()), roots[a->i() - 1]); return satisfied(sign, k, neg); } @@ -649,8 +667,8 @@ namespace nlsat { } }; - evaluator::evaluator(assignment const & x2v, pmanager & pm, small_object_allocator & allocator) { - m_imp = alloc(imp, x2v, pm, allocator); + evaluator::evaluator(solver& s, assignment const & x2v, pmanager & pm, small_object_allocator & allocator) { + m_imp = alloc(imp, s, x2v, pm, allocator); } evaluator::~evaluator() { diff --git a/src/nlsat/nlsat_evaluator.h b/src/nlsat/nlsat_evaluator.h index 0762c5360..a82ce79fb 100644 --- a/src/nlsat/nlsat_evaluator.h +++ b/src/nlsat/nlsat_evaluator.h @@ -26,11 +26,13 @@ Revision History: namespace nlsat { + class solver; + class evaluator { struct imp; imp * m_imp; public: - evaluator(assignment const & x2v, pmanager & pm, small_object_allocator & allocator); + evaluator(solver& s, assignment const & x2v, pmanager & pm, small_object_allocator & allocator); ~evaluator(); interval_set_manager & ism() const; diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 4b87f9e65..23480eed4 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -36,6 +36,7 @@ namespace nlsat { polynomial::cache & m_cache; pmanager & m_pm; polynomial_ref_vector m_ps; + polynomial_ref_vector m_ps2; polynomial_ref_vector m_psc_tmp; polynomial_ref_vector m_factors; scoped_anum_vector m_roots_tmp; @@ -43,6 +44,7 @@ namespace nlsat { bool m_full_dimensional; bool m_minimize_cores; bool m_factor; + bool m_signed_project; struct todo_set { polynomial::cache & m_cache; @@ -137,6 +139,7 @@ namespace nlsat { m_cache(u), m_pm(u.pm()), m_ps(m_pm), + m_ps2(m_pm), m_psc_tmp(m_pm), m_factors(m_pm), m_roots_tmp(m_am), @@ -148,6 +151,7 @@ namespace nlsat { m_simplify_cores = false; m_full_dimensional = false; m_minimize_cores = false; + m_signed_project = false; } ~imp() { @@ -202,7 +206,7 @@ namespace nlsat { void reset_already_added() { SASSERT(m_result != 0); unsigned sz = m_result->size(); - for (unsigned i = 0; i < sz; i++) + for (unsigned i = 0; i < sz; i++) m_already_added_literal[(*m_result)[i].index()] = false; } @@ -212,7 +216,7 @@ namespace nlsat { max_var(p) must be assigned in the current interpretation. */ int sign(polynomial_ref const & p) { - TRACE("nlsat_explain", tout << "p: " << p << "\n";); + TRACE("nlsat_explain", tout << "p: " << p << " var: " << max_var(p) << "\n";); SASSERT(max_var(p) == null_var || m_assignment.is_assigned(max_var(p))); return m_am.eval_sign_at(p, m_assignment); } @@ -697,39 +701,163 @@ namespace nlsat { } } + void test_root_literal(atom::kind k, var y, unsigned i, poly * p, scoped_literal_vector& result) { + m_result = &result; + add_root_literal(k, y, i, p); + reset_already_added(); + m_result = 0; + } + void add_root_literal(atom::kind k, var y, unsigned i, poly * p) { + polynomial_ref pr(p, m_pm); + TRACE("nlsat_explain", + display(tout << "x" << y << " " << k << "[" << i << "](", pr); tout << ")\n";); + + if (!mk_linear_root(k, y, i, p) && + //!mk_plinear_root(k, y, i, p) && + !mk_quadratic_root(k, y, i, p)&& + true) { + bool_var b = m_solver.mk_root_atom(k, y, i, p); + literal l(b, true); + TRACE("nlsat_explain", tout << "adding literal\n"; display(tout, l); tout << "\n";); + add_literal(l); + } + } + + /** + * literal can be expressed using a linear ineq_atom + */ + bool mk_linear_root(atom::kind k, var y, unsigned i, poly * p) { scoped_mpz c(m_pm.m()); - bool_var b; - bool lsign = false; if (m_pm.degree(p, y) == 1 && m_pm.const_coeff(p, y, 1, c)) { SASSERT(!m_pm.m().is_zero(c)); - // literal can be expressed using a linear ineq_atom - polynomial_ref p_prime(m_pm); - p_prime = p; - if (m_pm.m().is_neg(c)) - p_prime = neg(p_prime); - p = p_prime.get(); - switch (k) { - case atom::ROOT_EQ: k = atom::EQ; lsign = false; break; - case atom::ROOT_LT: k = atom::LT; lsign = false; break; - case atom::ROOT_GT: k = atom::GT; lsign = false; break; - case atom::ROOT_LE: k = atom::GT; lsign = true; break; - case atom::ROOT_GE: k = atom::LT; lsign = true; break; - default: - UNREACHABLE(); - break; - } - bool is_even = false; - b = m_solver.mk_ineq_atom(k, 1, &p, &is_even); + mk_linear_root(k, y, i, p, m_pm.m().is_neg(c)); + return true; } - else { - b = m_solver.mk_root_atom(k, y, i, p); - lsign = false; + return false; + } + + + /** + Create pseudo-linear root depending on the sign of the coefficient to y. + */ + bool mk_plinear_root(atom::kind k, var y, unsigned i, poly * p) { + if (m_pm.degree(p, y) != 1) { + return false; } - lsign = !lsign; // adding as an assumption - literal l(b, lsign); - TRACE("nlsat_explain", tout << "adding literal\n"; display(tout, l); tout << "\n";); - add_literal(l); + polynomial_ref c(m_pm); + c = m_pm.coeff(p, y, 1); + int s = sign(c); + if (s == 0) { + return false; + } + ensure_sign(c); + mk_linear_root(k, y, i, p, s < 0); + return true; + } + + /** + Encode root conditions for quadratic polynomials. + + Basically implements Thom's theorem. The roots are characterized by the sign of polynomials and their derivatives. + + b^2 - 4ac = 0: + - there is only one root, which is -b/2a. + - relation to root is a function of the sign of + - 2ax + b + b^2 - 4ac > 0: + - assert i == 1 or i == 2 + - relation to root is a function of the signs of: + - 2ax + b + - ax^2 + bx + c + */ + + bool mk_quadratic_root(atom::kind k, var y, unsigned i, poly * p) { + if (m_pm.degree(p, y) != 2) { + return false; + } + if (i != 1 && i != 2) { + return false; + } + + SASSERT(m_assignment.is_assigned(y)); + polynomial_ref A(m_pm), B(m_pm), C(m_pm), q(m_pm), p_diff(m_pm), yy(m_pm); + A = m_pm.coeff(p, y, 2); + B = m_pm.coeff(p, y, 1); + C = m_pm.coeff(p, y, 0); + // TBD throttle based on degree of q? + q = (B*B) - (4*A*C); + yy = m_pm.mk_polynomial(y); + p_diff = 2*A*yy + B; + p_diff = m_pm.normalize(p_diff); + int sq = ensure_sign(q); + if (sq < 0) { + return false; + } + int sa = ensure_sign(A); + if (sa == 0) { + q = B*yy + C; + return mk_plinear_root(k, y, i, q); + } + ensure_sign(p_diff); + if (sq > 0) { + polynomial_ref pr(p, m_pm); + ensure_sign(pr); + } + return true; + } + + int ensure_sign(polynomial_ref & p) { +#if 0 + polynomial_ref f(m_pm); + factor(p, m_factors); + m_is_even.reset(); + unsigned num_factors = m_factors.size(); + int s = 1; + for (unsigned i = 0; i < num_factors; i++) { + f = m_factors.get(i); + s *= sign(f); + m_is_even.push_back(false); + } + if (num_factors > 0) { + atom::kind k = atom::EQ; + if (s == 0) k = atom::EQ; + if (s < 0) k = atom::LT; + if (s > 0) k = atom::GT; + bool_var b = m_solver.mk_ineq_atom(k, num_factors, m_factors.c_ptr(), m_is_even.c_ptr()); + add_literal(literal(b, true)); + } + return s; +#else + int s = sign(p); + if (!is_const(p)) { + add_simple_assumption(s == 0 ? atom::EQ : (s < 0 ? atom::LT : atom::GT), p); + } + return s; +#endif + } + + /** + Auxiliary function to linear roots. + */ + void mk_linear_root(atom::kind k, var y, unsigned i, poly * p, bool mk_neg) { + polynomial_ref p_prime(m_pm); + p_prime = p; + bool lsign = false; + if (mk_neg) + p_prime = neg(p_prime); + p = p_prime.get(); + switch (k) { + case atom::ROOT_EQ: k = atom::EQ; lsign = false; break; + case atom::ROOT_LT: k = atom::LT; lsign = false; break; + case atom::ROOT_GT: k = atom::GT; lsign = false; break; + case atom::ROOT_LE: k = atom::GT; lsign = true; break; + case atom::ROOT_GE: k = atom::LT; lsign = true; break; + default: + UNREACHABLE(); + break; + } + add_simple_assumption(k, p, lsign); } /** @@ -1332,10 +1460,333 @@ namespace nlsat { TRACE("nlsat_explain", tout << "[explain] result\n"; display(tout, result);); CASSERT("nlsat", check_already_added()); } + + + void project(var x, unsigned num, literal const * ls, scoped_literal_vector & result) { + m_result = &result; + svector lits; + TRACE("nlsat", tout << "project x" << x << "\n"; m_solver.display(tout);); + + DEBUG_CODE( + for (unsigned i = 0; i < num; ++i) { + SASSERT(m_solver.value(ls[i]) == l_true); + atom* a = m_atoms[ls[i].var()]; + SASSERT(!a || m_evaluator.eval(a, ls[i].sign())); + }); + split_literals(x, num, ls, lits); + collect_polys(lits.size(), lits.c_ptr(), m_ps); + var mx_var = max_var(m_ps); + if (!m_ps.empty()) { + svector renaming; + if (x != mx_var) { + for (var i = 0; i < m_solver.num_vars(); ++i) { + renaming.push_back(i); + } + std::swap(renaming[x], renaming[mx_var]); + m_solver.reorder(renaming.size(), renaming.c_ptr()); + TRACE("qe", tout << "x: " << x << " max: " << mx_var << " num_vars: " << m_solver.num_vars() << "\n"; + m_solver.display(tout);); + } + elim_vanishing(m_ps); + if (m_signed_project) { + signed_project(m_ps, mx_var); + } + else { + project(m_ps, mx_var); + } + reset_already_added(); + m_result = 0; + if (x != mx_var) { + m_solver.restore_order(); + } + } + else { + reset_already_added(); + m_result = 0; + } + for (unsigned i = 0; i < result.size(); ++i) { + result.set(i, ~result[i]); + } + DEBUG_CODE( + for (unsigned i = 0; i < result.size(); ++i) { + SASSERT(l_true == m_solver.value(result[i])); + }); + + } + + void split_literals(var x, unsigned n, literal const* ls, svector& lits) { + var_vector vs; + for (unsigned i = 0; i < n; ++i) { + vs.reset(); + m_solver.vars(ls[i], vs); + if (vs.contains(x)) { + lits.push_back(ls[i]); + } + else { + add_literal(~ls[i]); + } + } + } + + /** + Signed projection. + + Assumptions: + - any variable in ps is at most x. + - root expressions are satisfied (positive literals) + + Effect: + - if x not in p, then + - if sign(p) < 0: p < 0 + - if sign(p) = 0: p = 0 + - if sign(p) > 0: p > 0 + else: + - let roots_j be the roots of p_j or roots_j[i] + - let L = { roots_j[i] | M(roots_j[i]) < M(x) } + - let U = { roots_j[i] | M(roots_j[i]) > M(x) } + - let E = { roots_j[i] | M(roots_j[i]) = M(x) } + - let glb in L, s.t. forall l in L . M(glb) >= M(l) + - let lub in U, s.t. forall u in U . M(lub) <= M(u) + - if root in E, then + - add E x . x = root & x > lb for lb in L + - add E x . x = root & x < ub for ub in U + - add E x . x = root & x = root2 for root2 in E \ { root } + - else + - assume |L| <= |U| (other case is symmetric) + - add E x . lb <= x & x <= glb for lb in L + - add E x . x = glb & x < ub for ub in U + */ + + + void signed_project(polynomial_ref_vector& ps, var x) { + + TRACE("nlsat_explain", tout << "Signed projection\n";); + polynomial_ref p(m_pm); + unsigned eq_index = 0; + bool eq_valid = false; + unsigned eq_degree = 0; + for (unsigned i = 0; i < ps.size(); ++i) { + p = ps.get(i); + int s = sign(p); + if (max_var(p) != x) { + atom::kind k = (s == 0)?(atom::EQ):((s < 0)?(atom::LT):(atom::GT)); + add_simple_assumption(k, p, false); + ps[i] = ps.back(); + ps.pop_back(); + --i; + } + else if (s == 0) { + if (!eq_valid || degree(p, x) < eq_degree) { + eq_index = i; + eq_valid = true; + eq_degree = degree(p, x); + } + } + } + + if (ps.empty()) { + return; + } + + if (ps.size() == 1) { + project_single(x, ps.get(0)); + return; + } + + // ax + b = 0, p(x) > 0 -> + + if (eq_valid) { + p = ps.get(eq_index); + if (degree(p, x) == 1) { + // ax + b = 0 + // let d be maximal degree of x in p. + // p(x) -> a^d*p(-b/a), a + // perform virtual substitution with equality. + solve_eq(x, eq_index, ps); + } + else { + project_pairs(x, eq_index, ps); + } + return; + } + + unsigned num_lub = 0, num_glb = 0; + unsigned glb_index = 0, lub_index = 0; + scoped_anum lub(m_am), glb(m_am), x_val(m_am); + x_val = m_assignment.value(x); + for (unsigned i = 0; i < ps.size(); ++i) { + p = ps.get(i); + scoped_anum_vector & roots = m_roots_tmp; + roots.reset(); + m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots); + bool glb_valid = false, lub_valid = false; + for (unsigned j = 0; j < roots.size(); ++j) { + int s = m_am.compare(x_val, roots[j]); + SASSERT(s != 0); + lub_valid |= s < 0; + glb_valid |= s > 0; + + if (s < 0 && m_am.lt(roots[j], lub)) { + lub_index = i; + m_am.set(lub, roots[j]); + } + + if (s > 0 && m_am.lt(glb, roots[j])) { + glb_index = i; + m_am.set(glb, roots[j]); + } + } + if (glb_valid) { + ++num_glb; + } + if (lub_valid) { + ++num_lub; + } + } + + if (num_lub == 0) { + project_plus_infinity(x, ps); + return; + } + + if (num_glb == 0) { + project_minus_infinity(x, ps); + return; + } + + if (num_lub <= num_glb) { + glb_index = lub_index; + } + + project_pairs(x, glb_index, ps); + } + + void project_plus_infinity(var x, polynomial_ref_vector const& ps) { + polynomial_ref p(m_pm), lc(m_pm); + for (unsigned i = 0; i < ps.size(); ++i) { + p = ps.get(i); + unsigned d = degree(p, x); + lc = m_pm.coeff(p, x, d); + if (!is_const(lc)) { + unsigned s = sign(p); + SASSERT(s != 0); + atom::kind k = (s > 0)?(atom::GT):(atom::LT); + add_simple_assumption(k, lc); + } + } + } + + void project_minus_infinity(var x, polynomial_ref_vector const& ps) { + polynomial_ref p(m_pm), lc(m_pm); + for (unsigned i = 0; i < ps.size(); ++i) { + p = ps.get(i); + unsigned d = degree(p, x); + lc = m_pm.coeff(p, x, d); + if (!is_const(lc)) { + unsigned s = sign(p); + SASSERT(s != 0); + atom::kind k; + if (s > 0) { + k = (d % 2 == 0)?(atom::GT):(atom::LT); + } + else { + k = (d % 2 == 0)?(atom::LT):(atom::GT); + } + add_simple_assumption(k, lc); + } + } + } + + void project_pairs(var x, unsigned idx, polynomial_ref_vector const& ps) { + polynomial_ref p(m_pm); + p = ps.get(idx); + for (unsigned i = 0; i < ps.size(); ++i) { + if (i != idx) { + project_pair(x, ps.get(i), p); + } + } + } + + void project_pair(var x, polynomial::polynomial* p1, polynomial::polynomial* p2) { + m_ps2.reset(); + m_ps2.push_back(p1); + m_ps2.push_back(p2); + project(m_ps2, x); + } + + void project_single(var x, polynomial::polynomial* p) { + m_ps2.reset(); + m_ps2.push_back(p); + project(m_ps2, x); + } + + void solve_eq(var x, unsigned idx, polynomial_ref_vector const& ps) { + polynomial_ref p(m_pm), A(m_pm), B(m_pm), C(m_pm), D(m_pm), E(m_pm), q(m_pm), r(m_pm); + polynomial_ref_vector qs(m_pm); + p = ps.get(idx); + SASSERT(degree(p, x) == 1); + A = m_pm.coeff(p, x, 1); + B = m_pm.coeff(p, x, 0); + B = neg(B); + TRACE("nlsat_explain", tout << "p: " << p << " A: " << A << " B: " << B << "\n";); + // x = B/A + for (unsigned i = 0; i < ps.size(); ++i) { + if (i != idx) { + q = ps.get(i); + unsigned d = degree(q, x); + D = m_pm.mk_const(rational(1)); + E = D; + r = m_pm.mk_zero(); + for (unsigned j = 0; j <= d; ++j) { + qs.push_back(D); + D = D*A; + } + for (unsigned j = 0; j <= d; ++j) { + // A^d*p0 + A^{d-1}*B*p1 + ... + B^j*A^{d-j}*pj + ... + B^d*p_d + C = m_pm.coeff(q, x, j); + if (!is_zero(C)) { + D = qs.get(d-j); + r = r + D*E*C; + } + E = E*B; + } + TRACE("nlsat_explain", tout << "q: " << q << " r: " << r << "\n";); + ensure_sign(r); + } + else { + ensure_sign(A); + } + } + + } + + void maximize(var x, unsigned num, literal const * ls, scoped_anum& val, bool& unbounded) { + svector lits; + polynomial_ref p(m_pm); + split_literals(x, num, ls, lits); + collect_polys(lits.size(), lits.c_ptr(), m_ps); + unbounded = true; + scoped_anum x_val(m_am); + x_val = m_assignment.value(x); + for (unsigned i = 0; i < m_ps.size(); ++i) { + p = m_ps.get(i); + scoped_anum_vector & roots = m_roots_tmp; + roots.reset(); + m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots); + for (unsigned j = 0; j < roots.size(); ++j) { + int s = m_am.compare(x_val, roots[j]); + if (s <= 0 && (unbounded || m_am.compare(roots[j], val) <= 0)) { + unbounded = false; + val = roots[j]; + } + } + } + } + }; - explain::explain(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq, - evaluator & ev) { + explain::explain(solver & s, assignment const & x2v, polynomial::cache & u, + atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev) { m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev); } @@ -1364,10 +1815,26 @@ namespace nlsat { m_imp->m_factor = f; } + void explain::set_signed_project(bool f) { + m_imp->m_signed_project = f; + } + void explain::operator()(unsigned n, literal const * ls, scoped_literal_vector & result) { (*m_imp)(n, ls, result); } + void explain::project(var x, unsigned n, literal const * ls, scoped_literal_vector & result) { + m_imp->project(x, n, ls, result); + } + + void explain::maximize(var x, unsigned n, literal const * ls, scoped_anum& val, bool& unbounded) { + m_imp->maximize(x, n, ls, val, unbounded); + } + + void explain::test_root_literal(atom::kind k, var y, unsigned i, poly* p, scoped_literal_vector & result) { + m_imp->test_root_literal(k, y, i, p, result); + } + }; #ifdef Z3DEBUG @@ -1398,3 +1865,4 @@ void pp_lit(nlsat::explain::imp & ex, nlsat::literal l) { std::cout << std::endl; } #endif + diff --git a/src/nlsat/nlsat_explain.h b/src/nlsat/nlsat_explain.h index ac99b434c..4309a0090 100644 --- a/src/nlsat/nlsat_explain.h +++ b/src/nlsat/nlsat_explain.h @@ -22,9 +22,11 @@ Revision History: #include"nlsat_solver.h" #include"nlsat_scoped_literal_vector.h" #include"polynomial_cache.h" +#include"algebraic_numbers.h" namespace nlsat { class evaluator; + class explain { public: @@ -32,8 +34,8 @@ namespace nlsat { private: imp * m_imp; public: - explain(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq, - evaluator & ev); + explain(solver & s, assignment const & x2v, polynomial::cache & u, + atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev); ~explain(); void reset(); @@ -41,6 +43,7 @@ namespace nlsat { void set_full_dimensional(bool f); void set_minimize_cores(bool f); void set_factor(bool f); + void set_signed_project(bool f); /** \brief Given a set of literals ls[0], ... ls[n-1] s.t. @@ -60,6 +63,48 @@ namespace nlsat { - s_1, ..., s_m are false in the current interpretation */ void operator()(unsigned n, literal const * ls, scoped_literal_vector & result); + + + /** + \brief projection for a given variable. + + Given: M |= l1[x] /\ ... /\ ln[x] + + Find: M |= s1, ..., sm + + Such that: |= ~s1 \/ ... \/ ~sm \/ E x. l1[x] /\ ... /\ ln[x] + + Contrast this with with the core-based projection above: + + Given: M |= A x . l1[x] \/ ... \/ ln[x] + + Find: M |= ~s1, ..., ~sm. + + Such that: |= s1 \/ ... \/ sm \/ A x . l1[x] \/ ... \/ ln[x] + + Claim: the two compute the same solutions if the projection operators are independent of the value of x. + Claim: A complete, convergent projection operator can be obtained from M in a way that is independent of x. + + + */ + void project(var x, unsigned n, literal const * ls, scoped_literal_vector & result); + + /** + Maximize the value of x (locally) under the current assignment to other variables and + while maintaining the assignment to the literals ls. + Set unbounded to 'true' if the value of x is unbounded. + + Precondition: the set of literals are true in the current model. + + By local optimization we understand that x is increased to the largest value within + the signs delineated by the roots of the polynomials in ls. + */ + void maximize(var x, unsigned n, literal const * ls, scoped_anum& val, bool& unbounded); + + /** + Unit test routine. + */ + void test_root_literal(atom::kind k, var y, unsigned i, poly* p, scoped_literal_vector & result); }; }; diff --git a/src/nlsat/nlsat_scoped_literal_vector.h b/src/nlsat/nlsat_scoped_literal_vector.h index 6ec7a3461..442853377 100644 --- a/src/nlsat/nlsat_scoped_literal_vector.h +++ b/src/nlsat/nlsat_scoped_literal_vector.h @@ -64,6 +64,13 @@ namespace nlsat { for (unsigned i = 0; i < sz; i++) push_back(ls[i]); } + void append(scoped_literal_vector const& ls) { + append(ls.size(), ls.c_ptr()); + } + void swap(scoped_literal_vector& other) { + SASSERT(&m_solver == &other.m_solver); + m_lits.swap(other.m_lits); + } }; diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index c723d9961..40177bb08 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -66,7 +66,6 @@ namespace nlsat { typedef polynomial::cache cache; typedef ptr_vector interval_set_vector; - solver & m_solver; reslimit& m_rlimit; small_object_allocator m_allocator; unsynch_mpq_manager m_qm; @@ -159,8 +158,7 @@ namespace nlsat { unsigned m_stages; unsigned m_irrational_assignments; // number of irrational witnesses - imp(solver & s, reslimit& rlim, params_ref const & p): - m_solver(s), + imp(solver& s, reslimit& rlim, params_ref const & p): m_rlimit(rlim), m_allocator("nlsat"), m_pm(rlim, m_qm, &m_allocator), @@ -168,7 +166,7 @@ namespace nlsat { m_am(rlim, m_qm, p, &m_allocator), m_asm(*this, m_allocator), m_assignment(m_am), - m_evaluator(m_assignment, m_pm, m_allocator), + m_evaluator(s, m_assignment, m_pm, m_allocator), m_ism(m_evaluator.ism()), m_num_bool_vars(0), m_display_var(m_perm), @@ -183,12 +181,7 @@ namespace nlsat { } ~imp() { - m_explain.reset(); - m_lemma.reset(); - m_lazy_clause.reset(); - undo_until_size(0); - del_clauses(); - del_unref_atoms(); + reset(); } void mk_true_bvar() { @@ -216,6 +209,18 @@ namespace nlsat { m_am.updt_params(p.p); } + void reset() { + m_explain.reset(); + m_lemma.reset(); + m_lazy_clause.reset(); + undo_until_size(0); + del_clauses(); + del_unref_atoms(); + m_cache.reset(); + m_assignment.reset(); + } + + void checkpoint() { if (!m_rlimit.inc()) throw solver_exception(m_rlimit.get_cancel_msg()); if (memory::get_allocation_size() > m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); @@ -252,6 +257,7 @@ namespace nlsat { } void inc_ref(bool_var b) { + TRACE("ref", tout << "inc: " << b << "\n";); if (b == null_bool_var) return; if (m_atoms[b] == 0) @@ -264,6 +270,7 @@ namespace nlsat { } void dec_ref(bool_var b) { + TRACE("ref", tout << "dec: " << b << "\n";); if (b == null_bool_var) return; atom * a = m_atoms[b]; @@ -412,6 +419,34 @@ namespace nlsat { return x; } + svector m_found_vars; + void vars(literal l, var_vector& vs) { + vs.reset(); + atom * a = m_atoms[l.var()]; + if (a == 0) { + + } + else if (a->is_ineq_atom()) { + unsigned sz = to_ineq_atom(a)->size(); + var_vector new_vs; + for (unsigned j = 0; j < sz; j++) { + m_found_vars.reset(); + m_pm.vars(to_ineq_atom(a)->p(j), new_vs); + for (unsigned i = 0; i < new_vs.size(); ++i) { + if (!m_found_vars.get(new_vs[i], false)) { + m_found_vars.setx(new_vs[i], true, false); + vs.push_back(new_vs[i]); + } + } + } + } + else { + m_pm.vars(to_root_atom(a)->p(), vs); + //vs.erase(max_var(to_root_atom(a)->p())); + vs.push_back(to_root_atom(a)->x()); + } + } + void deallocate(ineq_atom * a) { unsigned obj_sz = ineq_atom::get_obj_size(a->size()); a->~ineq_atom(); @@ -491,6 +526,7 @@ namespace nlsat { TRACE("nlsat_table_bug", ineq_atom::hash_proc h; tout << "mk_ineq_atom hash: " << h(new_atom) << "\n"; display(tout, *new_atom, m_display_var); tout << "\n";); ineq_atom * old_atom = m_ineq_atoms.insert_if_not_there(new_atom); + CTRACE("nlsat_table_bug", old_atom->max_var() != max, display(tout, *old_atom, m_display_var); tout << "\n";); SASSERT(old_atom->max_var() == max); if (old_atom != new_atom) { deallocate(new_atom); @@ -726,7 +762,7 @@ namespace nlsat { template void undo_until(Predicate const & pred) { - while (pred()) { + while (pred() && !m_trail.empty()) { trail & t = m_trail.back(); switch (t.m_kind) { case trail::BVAR_ASSIGNMENT: @@ -803,6 +839,14 @@ namespace nlsat { SASSERT(m_bvalues[b] == l_undef); } + struct true_pred { + bool operator()() const { return true; } + }; + + void undo_until_empty() { + undo_until(true_pred()); + } + /** \brief Create a new scope level */ @@ -868,10 +912,11 @@ namespace nlsat { var max = a->max_var(); if (!m_assignment.is_assigned(max)) return l_undef; - TRACE("value_bug", tout << "value of: "; display(tout, l); tout << "\n"; tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; - display_assignment(tout); - display_bool_assignment(tout);); - return to_lbool(m_evaluator.eval(a, l.sign())); + val = to_lbool(m_evaluator.eval(a, l.sign())); + TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n"; + tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; + display_assignment(tout);); + return val; } /** @@ -880,8 +925,10 @@ namespace nlsat { bool is_satisfied(clause const & cls) const { unsigned sz = cls.size(); for (unsigned i = 0; i < sz; i++) { - if (const_cast(this)->value(cls[i]) == l_true) + if (const_cast(this)->value(cls[i]) == l_true) { + TRACE("value_bug:", tout << cls[i] << " := true\n";); return true; + } } return false; } @@ -984,8 +1031,10 @@ namespace nlsat { If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 */ bool process_arith_clause(clause const & cls, bool satisfy_learned) { - if (!satisfy_learned && m_lazy >= 2 && cls.is_learned()) + if (!satisfy_learned && m_lazy >= 2 && cls.is_learned()) { + TRACE("nlsat", tout << "skip learned\n";); return true; // ignore lemmas in super lazy mode + } SASSERT(m_xk == max_var(cls)); unsigned num_undef = 0; // number of undefined literals unsigned first_undef = UINT_MAX; // position of the first undefined literal @@ -1064,7 +1113,7 @@ namespace nlsat { If satisfy_learned is true, then (arithmetic) learned clauses are satisfied even if m_lazy > 0 */ bool process_clause(clause const & cls, bool satisfy_learned) { - TRACE("nlsat", tout << "processing clause:\n"; display(tout, cls); tout << "\n";); + TRACE("nlsat", tout << "processing clause: "; display(tout, cls); tout << "\n";); if (is_satisfied(cls)) return true; if (m_xk == null_var) @@ -1151,7 +1200,7 @@ namespace nlsat { } TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); if (m_bk == null_bool_var && m_xk >= num_vars()) { - TRACE("nlsat", tout << "found model\n"; display_assignment(tout); display_bool_assignment(tout);); + TRACE("nlsat", tout << "found model\n"; display_assignment(tout);); return l_true; // all variables were assigned, and all clauses were satisfied. } TRACE("nlsat", tout << "processing variable "; @@ -1186,23 +1235,102 @@ namespace nlsat { lbool check() { TRACE("nlsat_smt2", display_smt2(tout);); TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); - m_xk = null_var; + init_search(); m_explain.set_full_dimensional(is_full_dimensional()); - if (m_random_order) { + bool reordered = false; + if (!can_reorder()) { + + } + else if (m_random_order) { shuffle_vars(); + reordered = true; } else if (m_reorder) { heuristic_reorder(); + reordered = true; } sort_watched_clauses(); lbool r = search(); - CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout); display_bool_assignment(tout);); - if (m_reorder) + CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout);); + if (reordered) restore_order(); - CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout); display_bool_assignment(tout);); + CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout);); + CTRACE("nlsat", r == l_false, display(tout);); return r; } + void init_search() { + undo_until_empty(); + while (m_scope_lvl > 0) { + undo_new_level(); + } + m_xk = null_var; + for (unsigned i = 0; i < m_bvalues.size(); ++i) { + m_bvalues[i] = l_undef; + } + m_assignment.reset(); + } + + lbool check(literal_vector& assumptions) { + literal_vector result; + unsigned sz = assumptions.size(); + literal const* ptr = assumptions.c_ptr(); + for (unsigned i = 0; i < sz; ++i) { + mk_clause(1, ptr+i, (assumption)(ptr+i)); + } + lbool r = check(); + + if (r == l_false) { + // collect used literals from m_lemma_assumptions + vector deps; + m_asm.linearize(m_lemma_assumptions.get(), deps); + for (unsigned i = 0; i < deps.size(); ++i) { + literal const* lp = (literal const*)(deps[i]); + if (ptr <= lp && lp < ptr + sz) { + result.push_back(*lp); + } + } + } + collect(assumptions, m_clauses); + collect(assumptions, m_learned); + + assumptions.reset(); + assumptions.append(result); + return r; + } + + void collect(literal_vector const& assumptions, clause_vector& clauses) { + unsigned n = clauses.size(); + unsigned j = 0; + for (unsigned i = 0; i < n; i++) { + clause * c = clauses[i]; + if (collect(assumptions, *c)) { + del_clause(c); + } + else { + clauses[j] = c; + j++; + } + } + clauses.shrink(j); + } + + bool collect(literal_vector const& assumptions, clause const& c) { + unsigned sz = assumptions.size(); + literal const* ptr = assumptions.c_ptr(); + _assumption_set asms = static_cast<_assumption_set>(c.assumptions()); + if (asms == 0) { + return false; + } + vector deps; + m_asm.linearize(asms, deps); + bool found = false; + for (unsigned i = 0; !found && i < deps.size(); ++i) { + found = ptr <= deps[i] && deps[i] < ptr + sz; + } + return found; + } + // ----------------------- // // Conflict Resolution @@ -1447,7 +1575,7 @@ namespace nlsat { TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause); tout << "\n"; tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; tout << "scope_lvl: " << scope_lvl() << "\n"; - tout << "current assignment\n"; display_assignment(tout); display_bool_assignment(tout);); + tout << "current assignment\n"; display_assignment(tout);); // static unsigned counter = 0; // counter++; @@ -1588,7 +1716,7 @@ namespace nlsat { conflict_clause = new_cls; goto start; } - TRACE("nlsat_resolve_done", display_assignment(tout); display_bool_assignment(tout);); + TRACE("nlsat_resolve_done", display_assignment(tout);); return true; } @@ -1801,6 +1929,15 @@ namespace nlsat { reorder(p.size(), p.c_ptr()); } + bool can_reorder() const { + for (unsigned i = 0; i < m_atoms.size(); ++i) { + if (m_atoms[i]) { + if (m_atoms[i]->is_root_atom()) return false; + } + } + return true; + } + /** \brief Reorder variables using the giving permutation. p maps internal variables to their new positions @@ -1921,6 +2058,9 @@ namespace nlsat { void reinit_cache() { reinit_cache(m_clauses); reinit_cache(m_learned); + for (unsigned i = 0; i < m_atoms.size(); ++i) { + reinit_cache(m_atoms[i]); + } } void reinit_cache(clause_vector const & cs) { unsigned sz = cs.size(); @@ -1934,10 +2074,13 @@ namespace nlsat { } void reinit_cache(literal l) { bool_var b = l.var(); - atom * a = m_atoms[b]; - if (a == 0) - return; - if (a->is_ineq_atom()) { + reinit_cache(m_atoms[b]); + } + void reinit_cache(atom* a) { + if (a == 0) { + + } + else if (a->is_ineq_atom()) { var max = 0; unsigned sz = to_ineq_atom(a)->size(); for (unsigned i = 0; i < sz; i++) { @@ -2073,7 +2216,7 @@ namespace nlsat { // // ----------------------- - void display_assignment(std::ostream & out, display_var_proc const & proc) const { + void display_num_assignment(std::ostream & out, display_var_proc const & proc) const { for (var x = 0; x < num_vars(); x++) { if (m_assignment.is_assigned(x)) { proc(out, x); @@ -2084,7 +2227,7 @@ namespace nlsat { } } - void display_bool_assignment(std::ostream & out, display_var_proc const & proc) const { + void display_bool_assignment(std::ostream & out) const { unsigned sz = m_atoms.size(); for (bool_var b = 0; b < sz; b++) { if (m_atoms[b] == 0 && m_bvalues[b] != l_undef) { @@ -2112,12 +2255,13 @@ namespace nlsat { return !first; } - void display_assignment(std::ostream & out) const { - display_assignment(out, m_display_var); + void display_num_assignment(std::ostream & out) const { + display_num_assignment(out, m_display_var); } - void display_bool_assignment(std::ostream & out) const { - display_bool_assignment(out, m_display_var); + void display_assignment(std::ostream& out) const { + display_bool_assignment(out); + display_num_assignment(out); } void display(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { @@ -2511,6 +2655,7 @@ namespace nlsat { void display(std::ostream & out) const { display(out, m_display_var); + display_assignment(out); } void display_vars(std::ostream & out) const { @@ -2562,6 +2707,20 @@ namespace nlsat { return m_imp->check(); } + lbool solver::check(literal_vector& assumptions) { + return m_imp->check(assumptions); + } + + void solver::reset() { + m_imp->reset(); + } + + + void solver::updt_params(params_ref const & p) { + m_imp->updt_params(p); + } + + void solver::collect_param_descrs(param_descrs & d) { algebraic_numbers::manager::collect_param_descrs(d); nlsat_params::collect_param_descrs(d); @@ -2583,6 +2742,10 @@ namespace nlsat { m_imp->m_display_var.m_proc = &proc; } + unsigned solver::num_vars() const { + return m_imp->num_vars(); + } + bool solver::is_int(var x) const { return m_imp->is_int(x); } @@ -2590,10 +2753,61 @@ namespace nlsat { bool_var solver::mk_bool_var() { return m_imp->mk_bool_var(); } + + literal solver::mk_true() { + return literal(0, false); + } atom * solver::bool_var2atom(bool_var b) { return m_imp->m_atoms[b]; } + + void solver::vars(literal l, var_vector& vs) { + m_imp->vars(l, vs); + } + + atom_vector const& solver::get_atoms() { + return m_imp->m_atoms; + } + + atom_vector const& solver::get_var2eq() { + return m_imp->m_var2eq; + } + + evaluator& solver::get_evaluator() { + return m_imp->m_evaluator; + } + + explain& solver::get_explain() { + return m_imp->m_explain; + } + + void solver::reorder(unsigned sz, var const* p) { + m_imp->reorder(sz, p); + } + + void solver::restore_order() { + m_imp->restore_order(); + } + + void solver::set_rvalues(assignment const& as) { + m_imp->m_assignment.copy(as); + } + + void solver::get_rvalues(assignment& as) { + as.copy(m_imp->m_assignment); + } + + void solver::get_bvalues(svector& vs) { + vs.reset(); + vs.append(m_imp->m_bvalues); + } + + void solver::set_bvalues(svector const& vs) { + m_imp->m_bvalues.reset(); + m_imp->m_bvalues.append(vs); + m_imp->m_bvalues.resize(m_imp->m_atoms.size(), l_undef); + } var solver::mk_var(bool is_int) { return m_imp->mk_var(is_int); @@ -2631,10 +2845,21 @@ namespace nlsat { m_imp->display(out, l); } + void solver::display(std::ostream & out, unsigned n, literal const* ls) const { + for (unsigned i = 0; i < n; ++i) { + display(out, ls[i]); + out << "; "; + } + } + void solver::display(std::ostream & out, var x) const { m_imp->m_display_var(out, x); } + void solver::display(std::ostream & out, atom const& a) const { + m_imp->display(out, a, m_imp->m_display_var); + } + display_var_proc const & solver::display_proc() const { return m_imp->m_display_var; } diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index 2875bbc8c..eec5ba19f 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -28,6 +28,9 @@ Revision History: namespace nlsat { + class evaluator; + class explain; + class solver { struct imp; imp * m_imp; @@ -63,7 +66,9 @@ namespace nlsat { nonlinear arithmetic atom. */ bool_var mk_bool_var(); - + + literal mk_true(); + /** \brief Create a real/integer variable. */ @@ -121,6 +126,48 @@ namespace nlsat { */ atom * bool_var2atom(bool_var b); + /** + \brief extract free variables from literal. + */ + void vars(literal l, var_vector& vs); + + /** + \brief provide access to atoms. Used by explain. + */ + atom_vector const& get_atoms(); + + /** + \brief Access var -> asserted equality. + */ + + atom_vector const& get_var2eq(); + + /** + \brief expose evaluator. + */ + + evaluator& get_evaluator(); + + /** + \brief Access explanation module. + */ + explain& get_explain(); + + /** + \brief Access assignments to variables. + */ + void get_rvalues(assignment& as); + void set_rvalues(assignment const& as); + + void get_bvalues(svector& vs); + void set_bvalues(svector const& vs); + + /** + \brief reorder variables. + */ + void reorder(unsigned sz, var const* permutation); + void restore_order(); + /** \brief Return number of integer/real variables */ @@ -135,6 +182,8 @@ namespace nlsat { // ----------------------- lbool check(); + lbool check(literal_vector& assumptions); + // ----------------------- // // Model @@ -154,6 +203,7 @@ namespace nlsat { void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); + void reset(); void collect_statistics(statistics & st); void reset_statistics(); void display_status(std::ostream & out) const; @@ -174,6 +224,10 @@ namespace nlsat { */ void display(std::ostream & out, literal l) const; + void display(std::ostream & out, unsigned n, literal const* ls) const; + + void display(std::ostream & out, atom const& a) const; + /** \brief Display variable */ diff --git a/src/nlsat/nlsat_types.h b/src/nlsat/nlsat_types.h index e6ab30c19..11e063a17 100644 --- a/src/nlsat/nlsat_types.h +++ b/src/nlsat/nlsat_types.h @@ -111,6 +111,21 @@ namespace nlsat { struct eq_proc { bool operator()(ineq_atom const * a1, ineq_atom const * a2) const; }; }; + inline std::ostream& operator<<(std::ostream& out, atom::kind k) { + switch (k) { + case atom::EQ: return out << "="; + case atom::LT: return out << "<"; + case atom::GT: return out << ">"; + case atom::ROOT_EQ: return out << "= root"; + case atom::ROOT_LT: return out << "< root"; + case atom::ROOT_LE: return out << "<= root"; + case atom::ROOT_GT: return out << "> root"; + case atom::ROOT_GE: return out << ">= root"; + default: return out << (int)k; + } + return out; + } + class root_atom : public atom { friend class solver; var m_x; diff --git a/src/nlsat/tactic/goal2nlsat.cpp b/src/nlsat/tactic/goal2nlsat.cpp index 00114cf05..a76cd1213 100644 --- a/src/nlsat/tactic/goal2nlsat.cpp +++ b/src/nlsat/tactic/goal2nlsat.cpp @@ -29,7 +29,9 @@ Notes: #include"expr2var.h" #include"arith_decl_plugin.h" #include"tactic.h" -#include"ast_smt2_pp.h" +#include"ast_pp.h" +#include"polynomial.h" +#include"algebraic_numbers.h" struct goal2nlsat::imp { struct nlsat_expr2polynomial : public expr2polynomial { @@ -124,7 +126,7 @@ struct goal2nlsat::imp { m_qm.neg(d2); polynomial_ref p(m_pm); p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2); - TRACE("goal2nlsat_bug", tout << "p: " << p << "\nk: " << k << "\n";); + TRACE("goal2nlsat_bug", tout << mk_pp(f, m) << " p: " << p << "\nk: " << k << "\n";); if (is_const(p)) { int sign; if (is_zero(p)) @@ -192,7 +194,7 @@ struct goal2nlsat::imp { switch (to_app(f)->get_decl_kind()) { case OP_TRUE: case OP_FALSE: - TRACE("goal2nlsat", tout << "f: " << mk_ismt2_pp(f, m) << "\n";); + TRACE("goal2nlsat", tout << "f: " << mk_pp(f, m) << "\n";); throw tactic_exception("apply simplify before applying nlsat"); case OP_AND: case OP_OR: @@ -257,6 +259,7 @@ struct goal2nlsat::imp { process(g.form(i), g.dep(i)); } } + }; struct goal2nlsat::scoped_set_imp { @@ -289,4 +292,154 @@ void goal2nlsat::operator()(goal const & g, params_ref const & p, nlsat::solver scoped_set_imp setter(*this, local_imp); local_imp(g); } + + +struct nlsat2goal::imp { + ast_manager& m; + arith_util a; + u_map const* m_x2t; +public: + imp(ast_manager& m):m(m),a(m) {} + + expr_ref operator()(nlsat::solver& s, u_map const& b2a, u_map const& x2t, nlsat::literal l) { + m_x2t = &x2t; + expr_ref result(m); + expr* t; + if (b2a.find(l.var(), t)) { + result = t; + } + else { + nlsat::atom const* at = s.bool_var2atom(l.var()); + SASSERT(at != 0); + if (at->is_ineq_atom()) { + nlsat::ineq_atom const* ia = to_ineq_atom(at); + unsigned sz = ia->size(); + expr_ref_vector ps(m); + bool is_int = true; + for (unsigned i = 0; is_int && i < sz; ++i) { + is_int = poly_is_int(ia->p(i)); + } + for (unsigned i = 0; i < sz; ++i) { + polynomial::polynomial* p = ia->p(i); + expr_ref t = poly2expr(s, p, is_int); + if (ia->is_even(i)) { + t = a.mk_power(t, a.mk_numeral(rational(2), a.is_int(t))); + } + ps.push_back(t); + } + result = a.mk_mul_simplify(ps); + expr_ref zero(m); + zero = a.mk_numeral(rational(0), a.is_int(result)); + switch (ia->get_kind()) { + case nlsat::atom::EQ: + result = m.mk_eq(result, zero); + break; + case nlsat::atom::LT: + if (l.sign()) { + l.neg(); + result = a.mk_ge(result, zero); + } + else { + result = a.mk_lt(result, zero); + } + break; + case nlsat::atom::GT: + if (l.sign()) { + l.neg(); + result = a.mk_le(result, zero); + } + else { + result = a.mk_gt(result, zero); + } + break; + default: + UNREACHABLE(); + } + } + else { + //nlsat::root_atom const* ra = nlsat::to_root_atom(at); + //ra->i(); + //expr_ref p = poly2expr(s, ra->p()); + //expr* x = m_x2t->find(ra->x()); + std::ostringstream strm; + s.display(strm, l.sign()?~l:l); + result = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); + } + } + + if (l.sign()) { + result = m.mk_not(result); + } + return result; + } + + expr_ref poly2expr(nlsat::solver& s, polynomial::polynomial* p, bool is_int) { + expr_ref result(m); + unsigned sz = polynomial::manager::size(p); + expr_ref_vector args(m); + for (unsigned i = 0; i < sz; ++i) { + args.push_back(mono2expr(s, + polynomial::manager::coeff(p, i), + polynomial::manager::get_monomial(p, i), is_int)); + } + result = a.mk_add_simplify(args); + return result; + } + + expr_ref mono2expr(nlsat::solver& s, polynomial::numeral const& c, polynomial::monomial* mon, bool is_int) { + expr_ref result(m); + expr_ref_vector args(m); + unsigned sz = polynomial::manager::size(mon); + for (unsigned i = 0; i < sz; ++i) { + unsigned d = polynomial::manager::degree(mon, i); + expr* t = m_x2t->find(polynomial::manager::get_var(mon, i)); + SASSERT(d >= 1); + if (d == 1) { + args.push_back(t); + } + else { + args.push_back(a.mk_power(t, a.mk_numeral(rational(d), a.is_int(t)))); + } + } + if (!s.pm().m().is_one(c)) { + args.push_back(a.mk_numeral(c, is_int)); + } + result = a.mk_mul_simplify(args); + return result; + } + + bool poly_is_int(polynomial::polynomial* p) { + bool is_int = true; + unsigned sz = polynomial::manager::size(p); + for (unsigned i = 0; is_int && i < sz; ++i) { + is_int = mono_is_int(polynomial::manager::get_monomial(p, i)); + } + return is_int; + } + + bool mono_is_int(polynomial::monomial* mon) { + bool is_int = true; + unsigned sz = polynomial::manager::size(mon); + for (unsigned i = 0; is_int && i < sz; ++i) { + is_int = a.is_int(m_x2t->find(polynomial::manager::get_var(mon, i))); + } + return is_int; + } +}; + + +nlsat2goal::nlsat2goal(ast_manager& m) { + m_imp = alloc(imp, m); +} + + +nlsat2goal::~nlsat2goal() { + dealloc(m_imp); +} + +expr_ref nlsat2goal::operator()(nlsat::solver& s, u_map const& b2a, u_map const& x2t, nlsat::literal l) { + return (*m_imp)(s, b2a, x2t, l); +} + + diff --git a/src/nlsat/tactic/goal2nlsat.h b/src/nlsat/tactic/goal2nlsat.h index ae9bbdb74..bdb71a6a3 100644 --- a/src/nlsat/tactic/goal2nlsat.h +++ b/src/nlsat/tactic/goal2nlsat.h @@ -57,17 +57,16 @@ class nlsat2goal { struct imp; imp * m_imp; public: - nlsat2goal(); + nlsat2goal(ast_manager& m); ~nlsat2goal(); static void collect_param_descrs(param_descrs & r); /** - \brief Translate the state of the nlsat engine back into a goal. - */ - void operator()(nlsat::solver const & s, expr2var const & a2b, expr2var const & t2x, - params_ref const & p, goal & g, model_converter_ref & mc); - + \brief Translate a literal into a formula. + */ + expr_ref operator()(nlsat::solver& s, u_map const& b2a, u_map const& x2t, nlsat::literal l); + }; #endif diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 6495b11ec..a93fa6fab 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -514,12 +514,10 @@ namespace opt { void context::init_solver() { setup_arith_solver(); - #pragma omp critical (opt_context) - { - m_opt_solver = alloc(opt_solver, m, m_params, m_fm); - m_opt_solver->set_logic(m_logic); - m_solver = m_opt_solver.get(); - } + m_opt_solver = alloc(opt_solver, m, m_params, m_fm); + m_opt_solver->set_logic(m_logic); + m_solver = m_opt_solver.get(); + if (opt_params(m_params).priority() == symbol("pareto") || (opt_params(m_params).priority() == symbol("lex") && m_objectives.size() > 1)) { m_opt_solver->ensure_pb(); @@ -556,10 +554,7 @@ namespace opt { for (unsigned i = 0; i < sz; ++i) { m_sat_solver->assert_expr(get_solver().get_assertion(i)); } - #pragma omp critical (opt_context) - { - m_solver = m_sat_solver.get(); - } + m_solver = m_sat_solver.get(); } void context::enable_sls(bool force) { @@ -649,10 +644,7 @@ namespace opt { void context::add_maxsmt(symbol const& id, unsigned index) { maxsmt* ms = alloc(maxsmt, *this, index); ms->updt_params(m_params); - #pragma omp critical (opt_context) - { - m_maxsmts.insert(id, ms); - } + m_maxsmts.insert(id, ms); } bool context::is_numeral(expr* e, rational & n) const { @@ -1277,10 +1269,7 @@ namespace opt { } void context::set_simplify(tactic* tac) { - #pragma omp critical (opt_context) - { - m_simplify = tac; - } + m_simplify = tac; } void context::clear_state() { @@ -1290,10 +1279,7 @@ namespace opt { } void context::set_pareto(pareto_base* p) { - #pragma omp critical (opt_context) - { - m_pareto = p; - } + m_pareto = p; } void context::collect_statistics(statistics& stats) const { diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp new file mode 100644 index 000000000..2846fd03e --- /dev/null +++ b/src/qe/nlqsat.cpp @@ -0,0 +1,928 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + nlqsat.cpp + +Abstract: + + Quantifier Satisfiability Solver for nlsat + +Author: + + Nikolaj Bjorner (nbjorner) 2015-10-17 + +Revision History: + + +--*/ + +#include "nlqsat.h" +#include "nlsat_solver.h" +#include "nlsat_explain.h" +#include "nlsat_assignment.h" +#include "qsat.h" +#include "quant_hoist.h" +#include "goal2nlsat.h" +#include "expr2var.h" +#include "uint_set.h" +#include "ast_util.h" +#include "tseitin_cnf_tactic.h" +#include "expr_safe_replace.h" +#include "ast_pp.h" + +namespace qe { + + enum qsat_mode_t { + qsat_t, + elim_t, + interp_t + }; + + class nlqsat : public tactic { + + typedef unsigned_vector assumption_vector; + typedef nlsat::scoped_literal_vector clause; + + struct stats { + unsigned m_num_rounds; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + ast_manager& m; + qsat_mode_t m_mode; + params_ref m_params; + nlsat::solver m_solver; + tactic_ref m_nftactic; + nlsat::literal_vector m_asms; + nlsat::literal_vector m_cached_asms; + unsigned_vector m_cached_asms_lim; + nlsat::literal m_is_true; + nlsat::assignment m_rmodel; + svector m_bmodel; + nlsat::assignment m_rmodel0; + svector m_bmodel0; + bool m_valid_model; + vector m_bound_rvars; + vector > m_bound_bvars; + vector m_preds; + svector m_rvar2level; + u_map m_bvar2level; + expr2var m_a2b, m_t2x; + u_map m_b2a, m_x2t; + volatile bool m_cancel; + stats m_stats; + statistics m_st; + obj_hashtable m_free_vars; + expr_ref_vector m_answer; + expr_safe_replace m_answer_simplify; + nlsat::literal_vector m_assumptions; + u_map m_asm2fml; + expr_ref_vector m_trail; + + lbool check_sat() { + while (true) { + ++m_stats.m_num_rounds; + check_cancel(); + init_assumptions(); + lbool res = m_solver.check(m_asms); + switch (res) { + case l_true: + TRACE("qe", display(tout); ); + save_model(); + push(); + break; + case l_false: + if (0 == level()) return l_false; + if (1 == level() && m_mode == qsat_t) return l_true; + project(); + break; + case l_undef: + return res; + } + } + return l_undef; + } + + void init_assumptions() { + unsigned lvl = level(); + m_asms.reset(); + m_asms.push_back(is_exists()?m_is_true:~m_is_true); + m_asms.append(m_assumptions); + TRACE("qe", tout << "model valid: " << m_valid_model << " level: " << lvl << " "; + display_assumptions(tout); + m_solver.display(tout);); + + if (!m_valid_model) { + m_asms.append(m_cached_asms); + return; + } + unsave_model(); + if (lvl == 0) { + SASSERT(m_cached_asms.empty()); + return; + } + if (lvl <= m_preds.size()) { + for (unsigned j = 0; j < m_preds[lvl - 1].size(); ++j) { + add_literal(m_cached_asms, m_preds[lvl - 1][j]); + } + } + m_asms.append(m_cached_asms); + + for (unsigned i = lvl + 1; i < m_preds.size(); i += 2) { + for (unsigned j = 0; j < m_preds[i].size(); ++j) { + nlsat::literal l = m_preds[i][j]; + max_level lv = m_bvar2level.find(l.var()); + bool use = + (lv.m_fa == i && (lv.m_ex == UINT_MAX || lv.m_ex < lvl)) || + (lv.m_ex == i && (lv.m_fa == UINT_MAX || lv.m_fa < lvl)); + if (use) { + add_literal(m_asms, l); + } + } + } + TRACE("qe", display(tout); + for (unsigned i = 0; i < m_asms.size(); ++i) { + m_solver.display(tout, m_asms[i]); tout << "\n"; + }); + save_model(); + } + + void add_literal(nlsat::literal_vector& lits, nlsat::literal l) { + lbool r = m_solver.value(l); + switch (r) { + case l_true: + lits.push_back(l); + break; + case l_false: + lits.push_back(~l); + break; + default: + UNREACHABLE(); + break; + } + } + + template + void insert_set(S& set, T const& vec) { + for (unsigned i = 0; i < vec.size(); ++i) { + set.insert(vec[i]); + } + } + + + void mbp(unsigned level, nlsat::scoped_literal_vector& result) { + nlsat::var_vector vars; + uint_set fvars; + extract_vars(level, vars, fvars); + mbp(vars, fvars, result); + } + + void extract_vars(unsigned level, nlsat::var_vector& vars, uint_set& fvars) { + for (unsigned i = 0; i < m_bound_rvars.size(); ++i) { + if (i < level) { + insert_set(fvars, m_bound_bvars[i]); + } + else { + vars.append(m_bound_rvars[i]); + } + } + } + + void mbp(nlsat::var_vector const& vars, uint_set const& fvars, clause& result) { + // + // Also project auxiliary variables from clausification. + // + unsave_model(); + nlsat::explain& ex = m_solver.get_explain(); + nlsat::scoped_literal_vector new_result(m_solver); + result.reset(); + // project quantified Boolean variables. + for (unsigned i = 0; i < m_asms.size(); ++i) { + nlsat::literal lit = m_asms[i]; + if (!m_b2a.contains(lit.var()) || fvars.contains(lit.var())) { + result.push_back(lit); + } + } + TRACE("qe", m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";); + // project quantified real variables. + // They are sorted by size, so we project the largest variables first to avoid + // renaming variables. + for (unsigned i = vars.size(); i > 0;) { + --i; + new_result.reset(); + ex.project(vars[i], result.size(), result.c_ptr(), new_result); + result.swap(new_result); + TRACE("qe", m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";); + } + negate_clause(result); + } + + void negate_clause(clause& result) { + for (unsigned i = 0; i < result.size(); ++i) { + result.set(i, ~result[i]); + } + } + + void save_model() { + m_solver.get_rvalues(m_rmodel); + m_solver.get_bvalues(m_bmodel); + m_valid_model = true; + if (is_exists(level())) { + m_rmodel0.copy(m_rmodel); + m_bmodel0.reset(); + m_bmodel0.append(m_bmodel); + } + } + + void unsave_model() { + SASSERT(m_valid_model); + m_solver.set_rvalues(m_rmodel); + m_solver.set_bvalues(m_bmodel); + } + + void clear_model() { + m_valid_model = false; + m_rmodel.reset(); + m_bmodel.reset(); + m_solver.set_rvalues(m_rmodel); + } + + unsigned level() const { + return m_cached_asms_lim.size(); + } + + void enforce_parity(clause& cl) { + cl.push_back(is_exists()?~m_is_true:m_is_true); + } + + void add_clause(clause& cl) { + if (cl.empty()) { + cl.push_back(~m_solver.mk_true()); + } + SASSERT(!cl.empty()); + nlsat::literal_vector lits(cl.size(), cl.c_ptr()); + m_solver.mk_clause(lits.size(), lits.c_ptr()); + } + + max_level get_level(clause const& cl) { + return get_level(cl.size(), cl.c_ptr()); + } + + max_level get_level(unsigned n, nlsat::literal const* ls) { + max_level level; + for (unsigned i = 0; i < n; ++i) { + level.merge(get_level(ls[i])); + } + return level; + } + + max_level get_level(nlsat::literal l) { + max_level level; + if (m_bvar2level.find(l.var(), level)) { + return level; + } + nlsat::var_vector vs; + m_solver.vars(l, vs); + TRACE("qe", m_solver.display(tout, l); tout << "\n";); + for (unsigned i = 0; i < vs.size(); ++i) { + level.merge(m_rvar2level[vs[i]]); + } + set_level(l.var(), level); + return level; + } + + void set_level(nlsat::bool_var v, max_level const& level) { + unsigned k = level.max(); + while (m_preds.size() <= k) { + m_preds.push_back(nlsat::scoped_literal_vector(m_solver)); + } + nlsat::literal l(v, false); + m_preds[k].push_back(l); + m_bvar2level.insert(v, level); + TRACE("qe", m_solver.display(tout, l); tout << ": " << level << "\n";); + } + + void project() { + TRACE("qe", display_assumptions(tout);); + if (!m_valid_model) { + pop(1); + return; + } + if (m_mode == elim_t) { + project_qe(); + return; + } + SASSERT(level() >= 2); + unsigned num_scopes; + clause cl(m_solver); + mbp(level()-1, cl); + + max_level clevel = get_level(cl); + enforce_parity(cl); + add_clause(cl); + + if (clevel.max() == UINT_MAX) { + num_scopes = 2*(level()/2); + } + else { + SASSERT(clevel.max() + 2 <= level()); + num_scopes = level() - clevel.max(); + if ((num_scopes % 2) != 0) { + --num_scopes; + } + SASSERT(num_scopes >= 2); + } + + TRACE("qe", tout << "backtrack: " << num_scopes << "\n";); + pop(num_scopes); + } + + void project_qe() { + SASSERT(level() >= 1 && m_mode == elim_t && m_valid_model); + clause cl(m_solver); + mbp(std::max(1u, level()-1), cl); + + expr_ref fml = clause2fml(cl); + TRACE("qe", tout << level() << ": " << fml << "\n";); + max_level clevel = get_level(cl); + if (level() == 1 || clevel.max() == 0) { + add_assumption_literal(cl, fml); + } + else { + enforce_parity(cl); + } + add_clause(cl); + + if (level() == 1) { // is_forall() && clevel.max() == 0 + add_to_answer(fml); + } + + if (level() == 1) { + pop(1); + } + else { + pop(2); + } + } + + void add_to_answer(expr_ref& fml) { + m_answer_simplify(fml); + expr* e; + if (m.is_not(fml, e)) { + m_answer_simplify.insert(e, m.mk_false()); + } + else { + m_answer_simplify.insert(fml, m.mk_true()); + } + m_answer.push_back(fml); + } + + expr_ref clause2fml(nlsat::scoped_literal_vector const& clause) { + expr_ref_vector fmls(m); + expr_ref fml(m); + expr* t; + nlsat2goal n2g(m); + for (unsigned i = 0; i < clause.size(); ++i) { + nlsat::literal l = clause[i]; + if (m_asm2fml.find(l.var(), t)) { + fml = t; + if (l.sign()) { + fml = push_not(fml); + } + SASSERT(l.sign()); + fmls.push_back(fml); + } + else { + fmls.push_back(n2g(m_solver, m_b2a, m_x2t, l)); + } + } + fml = mk_or(fmls); + return fml; + } + + void add_assumption_literal(clause& clause, expr* fml) { + nlsat::bool_var b = m_solver.mk_bool_var(); + clause.push_back(nlsat::literal(b, true)); + m_assumptions.push_back(nlsat::literal(b, false)); + m_asm2fml.insert(b, fml); + m_trail.push_back(fml); + m_bvar2level.insert(b, max_level()); + } + + bool is_exists() const { return is_exists(level()); } + bool is_forall() const { return is_forall(level()); } + bool is_exists(unsigned level) const { return (level % 2) == 0; } + bool is_forall(unsigned level) const { return is_exists(level+1); } + + void check_cancel() { + if (m_cancel) { + throw tactic_exception(TACTIC_CANCELED_MSG); + } + } + + void reset() { + //m_solver.reset(); + m_asms.reset(); + m_cached_asms.reset(); + m_cached_asms_lim.reset(); + m_is_true = nlsat::null_literal; + m_rmodel.reset(); + m_valid_model = false; + m_bound_rvars.reset(); + m_bound_bvars.reset(); + m_preds.reset(); + m_rvar2level.reset(); + m_bvar2level.reset(); + m_t2x.reset(); + m_a2b.reset(); + m_b2a.reset(); + m_x2t.reset(); + m_cancel = false; + m_st.reset(); + m_solver.collect_statistics(m_st); + m_free_vars.reset(); + m_answer.reset(); + m_answer_simplify.reset(); + m_assumptions.reset(); + m_asm2fml.reset(); + m_trail.reset(); + } + + void push() { + m_cached_asms_lim.push_back(m_cached_asms.size()); + } + + void pop(unsigned num_scopes) { + clear_model(); + unsigned new_level = level() - num_scopes; + m_cached_asms.shrink(m_cached_asms_lim[new_level]); + m_cached_asms_lim.shrink(new_level); + } + + void display(std::ostream& out) { + display_preds(out); + display_assumptions(out); + m_solver.display(out << "solver:\n"); + } + + void display_assumptions(std::ostream& out) { + m_solver.display(out << "assumptions: ", m_asms.size(), m_asms.c_ptr()); + out << "\n"; + } + + void display_preds(std::ostream& out) { + for (unsigned i = 0; i < m_preds.size(); ++i) { + m_solver.display(out << i << ": ", m_preds[i].size(), m_preds[i].c_ptr()); + out << "\n"; + } + } + + // expr -> nlsat::solver + + void hoist(expr_ref& fml) { + quantifier_hoister hoist(m); + vector qvars; + app_ref_vector vars(m); + bool is_forall = false; + pred_abs abs(m); + abs.get_free_vars(fml, vars); + insert_set(m_free_vars, vars); + qvars.push_back(vars); + vars.reset(); + if (m_mode == elim_t) { + is_forall = true; + hoist.pull_quantifier(is_forall, fml, vars); + qvars.push_back(vars); + } + else { + hoist.pull_quantifier(is_forall, fml, vars); + qvars.back().append(vars); + } + do { + is_forall = !is_forall; + vars.reset(); + hoist.pull_quantifier(is_forall, fml, vars); + qvars.push_back(vars); + } + while (!vars.empty()); + SASSERT(qvars.back().empty()); + init_expr2var(qvars); + + goal2nlsat g2s; + + expr_ref is_true(m), fml1(m), fml2(m); + is_true = m.mk_fresh_const("is_true", m.mk_bool_sort()); + fml = m.mk_iff(is_true, fml); + goal_ref g = alloc(goal, m); + g->assert_expr(fml); + proof_converter_ref pc; + expr_dependency_ref core(m); + model_converter_ref mc; + goal_ref_buffer result; + (*m_nftactic)(g, result, mc, pc, core); + SASSERT(result.size() == 1); + TRACE("qe", result[0]->display(tout);); + g2s(*result[0], m_params, m_solver, m_a2b, m_t2x); + + // insert variables and their levels. + for (unsigned i = 0; i < qvars.size(); ++i) { + m_bound_bvars.push_back(svector()); + m_bound_rvars.push_back(nlsat::var_vector()); + max_level lvl; + if (is_exists(i)) lvl.m_ex = i; else lvl.m_fa = i; + for (unsigned j = 0; j < qvars[i].size(); ++j) { + app* v = qvars[i][j].get(); + + if (m_a2b.is_var(v)) { + SASSERT(m.is_bool(v)); + nlsat::bool_var b = m_a2b.to_var(v); + m_bound_bvars.back().push_back(b); + set_level(b, lvl); + } + else if (m_t2x.is_var(v)) { + nlsat::var w = m_t2x.to_var(v); + TRACE("qe", tout << mk_pp(v, m) << " |-> " << w << "\n";); + m_bound_rvars.back().push_back(w); + m_rvar2level.setx(w, lvl, max_level()); + } + else { + TRACE("qe", tout << mk_pp(v, m) << " not found\n";); + } + } + } + init_var2expr(); + m_is_true = nlsat::literal(m_a2b.to_var(is_true), false); + // insert literals from arithmetical sub-formulas + nlsat::atom_vector const& atoms = m_solver.get_atoms(); + TRACE("qe", m_solver.display(tout); ); + for (unsigned i = 0; i < atoms.size(); ++i) { + if (atoms[i]) { + get_level(nlsat::literal(i, false)); + } + } + TRACE("qe", tout << fml << "\n";); + } + + void init_expr2var(vector const& qvars) { + for (unsigned i = 0; i < qvars.size(); ++i) { + init_expr2var(qvars[i]); + } + } + + void init_expr2var(app_ref_vector const& qvars) { + for (unsigned i = 0; i < qvars.size(); ++i) { + app* v = qvars[i]; + if (m.is_bool(v)) { + m_a2b.insert(v, m_solver.mk_bool_var()); + } + else { + // TODO: assert it is of type Real. + m_t2x.insert(v, m_solver.mk_var(false)); + } + } + } + + void init_var2expr() { + expr2var::iterator it = m_t2x.begin(), end = m_t2x.end(); + for (; it != end; ++it) { + m_x2t.insert(it->m_value, it->m_key); + } + it = m_a2b.begin(), end = m_a2b.end(); + for (; it != end; ++it) { + m_b2a.insert(it->m_value, it->m_key); + } + } + + + // Return false if nlsat assigned noninteger value to an integer variable. + // [copied from nlsat_tactic.cpp] + bool mk_model(model_converter_ref & mc) { + bool ok = true; + model_ref md = alloc(model, m); + arith_util util(m); + expr2var::iterator it = m_t2x.begin(), end = m_t2x.end(); + for (; it != end; ++it) { + nlsat::var x = it->m_value; + expr * t = it->m_key; + if (!is_uninterp_const(t) || !m_free_vars.contains(t)) + continue; + expr * v; + try { + v = util.mk_numeral(m_rmodel0.value(x), util.is_int(t)); + } + catch (z3_error & ex) { + throw ex; + } + catch (z3_exception &) { + v = util.mk_to_int(util.mk_numeral(m_rmodel0.value(x), false)); + ok = false; + } + md->register_decl(to_app(t)->get_decl(), v); + } + it = m_a2b.begin(), end = m_a2b.end(); + for (; it != end; ++it) { + expr * a = it->m_key; + nlsat::bool_var b = it->m_value; + if (a == 0 || !is_uninterp_const(a) || b == m_is_true.var() || !m_free_vars.contains(a)) + continue; + lbool val = m_bmodel0.get(b, l_undef); + if (val == l_undef) + continue; // don't care + md->register_decl(to_app(a)->get_decl(), val == l_true ? m.mk_true() : m.mk_false()); + } + mc = model2model_converter(md.get()); + return ok; + } + + public: + nlqsat(ast_manager& m, qsat_mode_t mode, params_ref const& p): + m(m), + m_mode(mode), + m_params(p), + m_solver(m.limit(), p), + m_nftactic(0), + m_rmodel(m_solver.am()), + m_rmodel0(m_solver.am()), + m_valid_model(false), + m_a2b(m), + m_t2x(m), + m_cancel(false), + m_answer(m), + m_answer_simplify(m), + m_trail(m) + { + m_solver.get_explain().set_signed_project(true); + m_nftactic = mk_tseitin_cnf_tactic(m); + } + + virtual ~nlqsat() { + } + + void updt_params(params_ref const & p) { + params_ref p2(p); + p2.set_bool("factor", false); + m_solver.updt_params(p2); + } + + void collect_param_descrs(param_descrs & r) { + } + + + void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result, + /* out */ model_converter_ref & mc, + /* out */ proof_converter_ref & pc, + /* out */ expr_dependency_ref & core) { + + tactic_report report("nlqsat-tactic", *in); + + ptr_vector fmls; + expr_ref fml(m); + mc = 0; pc = 0; core = 0; + in->get_formulas(fmls); + fml = mk_and(m, fmls.size(), fmls.c_ptr()); + if (m_mode == elim_t) { + fml = m.mk_not(fml); + } + reset(); + TRACE("qe", tout << fml << "\n";); + hoist(fml); + TRACE("qe", tout << "ex: " << fml << "\n";); + lbool is_sat = check_sat(); + + switch (is_sat) { + case l_false: + in->reset(); + in->inc_depth(); + if (m_mode == elim_t) { + fml = mk_and(m_answer); + } + else { + fml = m.mk_false(); + } + in->assert_expr(fml); + result.push_back(in.get()); + break; + case l_true: + SASSERT(m_mode == qsat_t); + in->reset(); + in->inc_depth(); + result.push_back(in.get()); + if (in->models_enabled()) { + VERIFY(mk_model(mc)); + } + break; + case l_undef: + result.push_back(in.get()); + std::string s = "search failed"; + throw tactic_exception(s.c_str()); + } + } + + + void collect_statistics(statistics & st) const { + st.copy(m_st); + st.update("qsat num rounds", m_stats.m_num_rounds); + } + + void reset_statistics() { + m_stats.reset(); + m_solver.reset_statistics(); + } + + void cleanup() { + reset(); + } + + void set_logic(symbol const & l) { + } + + void set_progress_callback(progress_callback * callback) { + } + + tactic * translate(ast_manager & m) { + return alloc(nlqsat, m, m_mode, m_params); + } + +#if 0 + + /** + + Algorithm: + I := true + while there is M, such that M |= ~B & I: + find P, such that M => P => exists y . ~B & I + ; forall y B => ~P + C := core of P with respect to A + ; A => ~ C => ~ P + I := I & ~C + + + Alternative Algorithm: + R := false + while there is M, such that M |= A & ~R: + find I, such that M => I => forall y . B + R := R | I + + */ + + lbool interpolate(expr* a, expr* b, expr_ref& result) { + SASSERT(m_mode == interp_t); + + reset(); + app_ref enableA(m), enableB(m); + expr_ref A(m), B(m), fml(m); + expr_ref_vector fmls(m), answer(m); + + // varsB are private to B. + nlsat::var_vector vars; + uint_set fvars; + private_vars(a, b, vars, fvars); + + enableA = m.mk_const(symbol("#A"), m.mk_bool_sort()); + enableB = m.mk_not(enableA); + A = m.mk_implies(enableA, a); + B = m.mk_implies(enableB, m.mk_not(b)); + fml = m.mk_and(A, B); + hoist(fml); + + + + nlsat::literal _enableB = nlsat::literal(m_a2b.to_var(enableB), false); + nlsat::literal _enableA = ~_enableB; + + while (true) { + m_mode = qsat_t; + // enable B + m_assumptions.reset(); + m_assumptions.push_back(_enableB); + lbool is_sat = check_sat(); + + switch (is_sat) { + case l_undef: + return l_undef; + case l_true: + break; + case l_false: + result = mk_and(answer); + return l_true; + } + + // disable B, enable A + m_assumptions.reset(); + m_assumptions.push_back(_enableA); + // add blocking clause to solver. + + nlsat::scoped_literal_vector core(m_solver); + + m_mode = elim_t; + + mbp(vars, fvars, core); + + // minimize core. + nlsat::literal_vector _core(core.size(), core.c_ptr()); + _core.push_back(_enableA); + is_sat = m_solver.check(_core); // TBD: only for quantifier-free A. Generalize output of elim_t to be a core. + switch (is_sat) { + case l_undef: + return l_undef; + case l_true: + UNREACHABLE(); + case l_false: + core.reset(); + core.append(_core.size(), _core.c_ptr()); + break; + } + negate_clause(core); + // keep polarity of enableA, such that clause is only + // used when checking satisfiability of B. + for (unsigned i = 0; i < core.size(); ++i) { + if (core[i].var() == _enableA.var()) core.set(i, ~core[i]); + } + add_clause(core); // Invariant is added as assumption for B. + answer.push_back(clause2fml(core)); // TBD: remove answer literal. + } + } + + /** + \brief extract variables that are private to a (not used in b). + vars cover the real variables, and fvars cover the Boolean variables. + + TBD: We want fvars to be the complement such that returned core contains + Shared boolean variables, but not the ones private to B. + */ + void private_vars(expr* a, expr* b, nlsat::var_vector& vars, uint_set& fvars) { + app_ref_vector varsA(m), varsB(m); + obj_hashtable varsAS; + pred_abs abs(m); + abs.get_free_vars(a, varsA); + abs.get_free_vars(b, varsB); + insert_set(varsAS, varsA); + for (unsigned i = 0; i < varsB.size(); ++i) { + if (varsAS.contains(varsB[i].get())) { + varsB[i] = varsB.back(); + varsB.pop_back(); + --i; + } + } + for (unsigned j = 0; j < varsB.size(); ++j) { + app* v = varsB[j].get(); + if (m_a2b.is_var(v)) { + fvars.insert(m_a2b.to_var(v)); + } + else if (m_t2x.is_var(v)) { + vars.push_back(m_t2x.to_var(v)); + } + } + } + + lbool maximize(app* _x, expr* _fml, scoped_anum& result, bool& unbounded) { + expr_ref fml(_fml, m); + reset(); + hoist(fml); + nlsat::literal_vector lits; + lbool is_sat = l_true; + nlsat::var x = m_t2x.to_var(_x); + m_mode = qsat_t; + while (is_sat == l_true) { + is_sat = check_sat(); + if (is_sat == l_true) { + // m_asms is minimized set of literals that satisfy formula. + nlsat::explain& ex = m_solver.get_explain(); + polynomial::manager& pm = m_solver.pm(); + anum_manager& am = m_solver.am(); + ex.maximize(x, m_asms.size(), m_asms.c_ptr(), result, unbounded); + if (unbounded) { + break; + } + // TBD: assert the new bound on x using the result. + bool is_even = false; + polynomial::polynomial_ref pr(pm); + pr = pm.mk_polynomial(x); + rational r; + am.get_upper(result, r); + // result is algebraic numeral, but polynomials are not defined over extension field. + polynomial::polynomial* p = pr; + nlsat::bool_var b = m_solver.mk_ineq_atom(nlsat::atom::GT, 1, &p, &is_even); + nlsat::literal bound(b, false); + m_solver.mk_clause(1, &bound); + } + } + return is_sat; + } +#endif + }; +}; + +tactic * mk_nlqsat_tactic(ast_manager & m, params_ref const& p) { + return alloc(qe::nlqsat, m, qe::qsat_t, p); +} + +tactic * mk_nlqe_tactic(ast_manager & m, params_ref const& p) { + return alloc(qe::nlqsat, m, qe::elim_t, p); +} + + diff --git a/src/qe/nlqsat.h b/src/qe/nlqsat.h new file mode 100644 index 000000000..9d0cb6af4 --- /dev/null +++ b/src/qe/nlqsat.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + nlqsat.h + +Abstract: + + Quantifier Satisfiability Solver for nlsat + +Author: + + Nikolaj Bjorner (nbjorner) 2015-10-17 + +Revision History: + + +--*/ + +#ifndef QE_NLQSAT_H__ +#define QE_NLQSAT_H__ + +#include "tactic.h" + + +tactic * mk_nlqsat_tactic(ast_manager & m, params_ref const& p = params_ref()); +tactic * mk_nlqe_tactic(ast_manager & m, params_ref const& p = params_ref()); + + +/* + ADD_TACTIC("nlqsat", "apply a NL-QSAT solver.", "mk_nlqsat_tactic(m, p)") + +*/ + +// TBD_TACTIC("nlqe", "apply a NL-QE solver.", "mk_nlqe_tactic(m, p)") + +#endif diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index 585944263..1df22e385 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -36,7 +36,6 @@ Revision History: #include "expr_functors.h" #include "quant_hoist.h" #include "bool_rewriter.h" -#include "qe_util.h" #include "th_rewriter.h" #include "smt_kernel.h" #include "model_evaluator.h" @@ -2281,7 +2280,7 @@ namespace qe { } } - static void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars) { + void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars) { ast_manager& m = new_body.get_manager(); expr_ref tmp(m); unsigned nd = q->get_num_decls(); diff --git a/src/qe/qe.h b/src/qe/qe.h index 0370ff3a0..a75af19c5 100644 --- a/src/qe/qe.h +++ b/src/qe/qe.h @@ -223,6 +223,8 @@ namespace qe { qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, smt_params& p); + void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars); + class def_vector { func_decl_ref_vector m_vars; expr_ref_vector m_defs; diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index bc4a824d1..2b00efba0 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2010 Microsoft Corporation +Copyright (c) 2015 Microsoft Corporation Module Name: @@ -16,188 +16,557 @@ Author: Revision History: + --*/ #include "qe_arith.h" -#include "qe_util.h" #include "ast_util.h" #include "arith_decl_plugin.h" #include "ast_pp.h" #include "th_rewriter.h" #include "expr_functors.h" +#include "model_v2_pp.h" +#include "expr_safe_replace.h" namespace qe { + + bool is_divides(arith_util& a, expr* e1, expr* e2, rational& k, expr_ref& p) { + expr* t1, *t2; + ast_manager& m = a.get_manager(); + if (a.is_mod(e2, t1, t2) && + a.is_numeral(e1, k) && + k.is_zero() && + a.is_numeral(t2, k)) { + p = t1; + return true; + } + return false; + } + + bool is_divides(arith_util& a, expr* e, rational& k, expr_ref& t) { + expr* e1, *e2; + if (!a.get_manager().is_eq(e, e1, e2)) { + return false; + } + return is_divides(a, e1, e2, k, t) || is_divides(a, e2, e1, k, t); + } - class arith_project_util { - ast_manager& m; - arith_util a; - th_rewriter m_rw; - expr_ref_vector m_ineq_terms; - vector m_ineq_coeffs; - svector m_ineq_strict; + struct arith_project_plugin::imp { + + enum ineq_type { + t_eq, + t_lt, + t_le + }; + ast_manager& m; + arith_util a; + th_rewriter m_rw; + expr_ref_vector m_ineq_terms; + vector m_ineq_coeffs; + svector m_ineq_types; + expr_ref_vector m_div_terms; + vector m_div_divisors, m_div_coeffs; + expr_ref_vector m_new_lits; + rational m_delta, m_u; scoped_ptr m_var; + unsigned m_num_pos, m_num_neg; + bool m_pos_is_unit, m_neg_is_unit; - struct cant_project {}; + sort* var_sort() const { return m.get_sort(m_var->x()); } - void is_linear(rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { - expr* t1, *t2; + bool is_int() const { return a.is_int(m_var->x()); } + + void display(std::ostream& out) const { + for (unsigned i = 0; i < num_ineqs(); ++i) { + display_ineq(out, i); + } + for (unsigned i = 0; i < num_divs(); ++i) { + display_div(out, i); + } + } + + void is_linear(model& model, rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { + expr* t1, *t2, *t3; rational mul1; + expr_ref val(m); if (t == m_var->x()) { c += mul; } - else if (a.is_mul(t, t1, t2) && a.is_numeral(t1, mul1)) { - is_linear(mul* mul1, t2, c, ts); + else if (a.is_mul(t, t1, t2) && is_numeral(model, t1, mul1)) { + is_linear(model, mul* mul1, t2, c, ts); } - else if (a.is_mul(t, t1, t2) && a.is_numeral(t2, mul1)) { - is_linear(mul* mul1, t1, c, ts); + else if (a.is_mul(t, t1, t2) && is_numeral(model, t2, mul1)) { + is_linear(model, mul* mul1, t1, c, ts); } else if (a.is_add(t)) { app* ap = to_app(t); for (unsigned i = 0; i < ap->get_num_args(); ++i) { - is_linear(mul, ap->get_arg(i), c, ts); + is_linear(model, mul, ap->get_arg(i), c, ts); } } else if (a.is_sub(t, t1, t2)) { - is_linear(mul, t1, c, ts); - is_linear(-mul, t2, c, ts); + is_linear(model, mul, t1, c, ts); + is_linear(model, -mul, t2, c, ts); } else if (a.is_uminus(t, t1)) { - is_linear(-mul, t1, c, ts); + is_linear(model, -mul, t1, c, ts); } else if (a.is_numeral(t, mul1)) { - ts.push_back(a.mk_numeral(mul*mul1, m.get_sort(t))); + ts.push_back(mk_num(mul*mul1)); + } + else if (extract_mod(model, t, val)) { + ts.push_back(mk_mul(mul, val)); + } + else if (m.is_ite(t, t1, t2, t3)) { + VERIFY(model.eval(t1, val)); + SASSERT(m.is_true(val) || m.is_false(val)); + if (m.is_true(val)) { + is_linear(model, mul, t2, c, ts); + } + else { + is_linear(model, mul, t3, c, ts); + } } else if ((*m_var)(t)) { - IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(t, m) << "\n";); + TRACE("qe", tout << "can't project:" << mk_pp(t, m) << "\n";); throw cant_project(); } - else if (mul.is_one()) { - ts.push_back(t); - } else { - ts.push_back(a.mk_mul(a.mk_numeral(mul, m.get_sort(t)), t)); + ts.push_back(mk_mul(mul, t)); } } - bool is_linear(expr* lit, rational& c, expr_ref& t, bool& is_strict) { - if (!(*m_var)(lit)) { - return false; - } + + bool is_linear(model& model, expr* lit, bool& found_eq) { + rational c(0), mul(1); + expr_ref t(m); + ineq_type ty = t_le; expr* e1, *e2; - c.reset(); - sort* s; expr_ref_vector ts(m); bool is_not = m.is_not(lit, lit); - rational mul(1); if (is_not) { mul.neg(); } SASSERT(!m.is_not(lit)); if (a.is_le(lit, e1, e2) || a.is_ge(lit, e2, e1)) { - is_linear( mul, e1, c, ts); - is_linear(-mul, e2, c, ts); - s = m.get_sort(e1); - is_strict = is_not; + is_linear(model, mul, e1, c, ts); + is_linear(model, -mul, e2, c, ts); + ty = is_not?t_lt:t_le; } else if (a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1)) { - is_linear( mul, e1, c, ts); - is_linear(-mul, e2, c, ts); - s = m.get_sort(e1); - is_strict = !is_not; + is_linear(model, mul, e1, c, ts); + is_linear(model, -mul, e2, c, ts); + ty = is_not?t_le:t_lt; + } + else if (m.is_eq(lit, e1, e2) && !is_not && is_arith(e1)) { + is_linear(model, mul, e1, c, ts); + is_linear(model, -mul, e2, c, ts); + ty = t_eq; + } + else if (m.is_distinct(lit) && !is_not && is_arith(to_app(lit)->get_arg(0))) { + expr_ref val(m); + rational r; + app* alit = to_app(lit); + vector > nums; + for (unsigned i = 0; i < alit->get_num_args(); ++i) { + VERIFY(model.eval(alit->get_arg(i), val) && a.is_numeral(val, r)); + nums.push_back(std::make_pair(alit->get_arg(i), r)); + } + std::sort(nums.begin(), nums.end(), compare_second()); + for (unsigned i = 0; i + 1 < nums.size(); ++i) { + SASSERT(nums[i].second < nums[i+1].second); + c.reset(); + ts.reset(); + is_linear(model, mul, nums[i+1].first, c, ts); + is_linear(model, -mul, nums[i].first, c, ts); + t = add(ts); + accumulate_linear(model, c, t, t_lt); + } + t = mk_num(0); + c.reset(); + return true; + } + else if (m.is_distinct(lit) && is_not && is_arith(to_app(lit)->get_arg(0))) { + expr_ref eq = project_plugin::pick_equality(m, model, to_app(lit)->get_arg(0)); + return is_linear(model, eq, found_eq); + } + else if (m.is_eq(lit, e1, e2) && is_not && is_arith(e1)) { + expr_ref val1(m), val2(m); + rational r1, r2; + VERIFY(model.eval(e1, val1) && a.is_numeral(val1, r1)); + VERIFY(model.eval(e2, val2) && a.is_numeral(val2, r2)); + SASSERT(r1 != r2); + if (r1 < r2) { + std::swap(e1, e2); + } + ty = t_lt; + is_linear(model, mul, e1, c, ts); + is_linear(model, -mul, e2, c, ts); } - else if (m.is_eq(lit, e1, e2) && !is_not) { - is_linear( mul, e1, c, ts); - is_linear(-mul, e2, c, ts); - s = m.get_sort(e1); - is_strict = false; - } else { - IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(lit, m) << "\n";); + TRACE("qe", tout << "can't project:" << mk_pp(lit, m) << "\n";); throw cant_project(); } - if (ts.empty()) { - t = a.mk_numeral(rational(0), s); + if (ty == t_lt && is_int()) { + ts.push_back(mk_num(1)); + ty = t_le; } - else { - t = a.mk_add(ts.size(), ts.c_ptr()); + t = add(ts); + if (ty == t_eq && c.is_neg()) { + t = mk_uminus(t); + c.neg(); } + if (ty == t_eq && c > rational(1)) { + m_ineq_coeffs.push_back(-c); + m_ineq_terms.push_back(mk_uminus(t)); + m_ineq_types.push_back(t_le); + m_num_neg++; + ty = t_le; + } + accumulate_linear(model, c, t, ty); + found_eq = !c.is_zero() && ty == t_eq; return true; } - void project(model& model, expr_ref_vector& lits) { - unsigned num_pos = 0; - unsigned num_neg = 0; - m_ineq_terms.reset(); - m_ineq_coeffs.reset(); - m_ineq_strict.reset(); - expr_ref_vector new_lits(m); - for (unsigned i = 0; i < lits.size(); ++i) { - rational c(0); - expr_ref t(m); - bool is_strict; - if (is_linear(lits[i].get(), c, t, is_strict)) { - m_ineq_coeffs.push_back(c); - m_ineq_terms.push_back(t); - m_ineq_strict.push_back(is_strict); - if (c.is_zero()) { - m_rw(lits[i].get(), t); - new_lits.push_back(t); - } - else if (c.is_pos()) { - ++num_pos; - } - else { - ++num_neg; - } + bool is_numeral(model& model, expr* t, rational& r) { + expr* t1, *t2, *t3; + rational r1, r2; + expr_ref val(m); + if (a.is_numeral(t, r)) return true; + + if (a.is_uminus(t, t1) && is_numeral(model, t1, r)) { + r.neg(); + return true; + } + else if (a.is_mul(t, t1, t2) && is_numeral(model, t1, r1) && is_numeral(model, t2, r2)) { + r = r1*r2; + return true; + } + else if (a.is_add(t)) { + app* ap = to_app(t); + r = rational(1); + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + if (!is_numeral(model, ap->get_arg(i), r1)) return false; + r *= r1; + } + return true; + } + else if (m.is_ite(t, t1, t2, t3)) { + VERIFY (model.eval(t1, val)); + if (m.is_true(val)) { + return is_numeral(model, t1, r); } else { - new_lits.push_back(lits[i].get()); + return is_numeral(model, t2, r); } } + else if (a.is_sub(t, t1, t2) && is_numeral(model, t1, r1) && is_numeral(model, t2, r2)) { + r = r1 - r2; + return true; + } + + return false; + } + + struct compare_second { + bool operator()(std::pair const& a, + std::pair const& b) const { + return a.second < b.second; + } + }; + + void accumulate_linear(model& model, rational const& c, expr_ref& t, ineq_type ty) { + if (c.is_zero()) { + switch (ty) { + case t_eq: + t = a.mk_eq(t, mk_num(0)); + break; + case t_lt: + t = a.mk_lt(t, mk_num(0)); + break; + case t_le: + t = a.mk_le(t, mk_num(0)); + break; + } + add_lit(model, m_new_lits, t); + } + else { + m_ineq_coeffs.push_back(c); + m_ineq_terms.push_back(t); + m_ineq_types.push_back(ty); + if (ty == t_eq) { + // skip + } + else if (c.is_pos()) { + ++m_num_pos; + m_pos_is_unit &= c.is_one(); + } + else { + ++m_num_neg; + m_neg_is_unit &= c.is_minus_one(); + } + } + } + + bool is_arith(expr* e) { + return a.is_int(e) || a.is_real(e); + } + + expr_ref add(expr_ref_vector const& ts) { + switch (ts.size()) { + case 0: + return mk_num(0); + case 1: + return expr_ref(ts[0], m); + default: + return expr_ref(a.mk_add(ts.size(), ts.c_ptr()), m); + } + } + + + // e is of the form (ax + t) mod k + bool is_mod(model& model, expr* e, rational& k, expr_ref& t, rational& c) { + expr* t1, *t2; + expr_ref_vector ts(m); + if (a.is_mod(e, t1, t2) && + a.is_numeral(t2, k) && + (*m_var)(t1)) { + c.reset(); + rational mul(1); + is_linear(model, mul, t1, c, ts); + t = add(ts); + return true; + } + return false; + } + + bool extract_mod(model& model, expr* e, expr_ref& val) { + rational c, k; + expr_ref t(m); + if (is_mod(model, e, k, t, c)) { + VERIFY (model.eval(e, val)); + SASSERT (a.is_numeral(val)); + TRACE("qe", tout << "extract: " << mk_pp(e, m) << " evals: " << val << " c: " << c << " t: " << t << "\n";); + if (!c.is_zero()) { + t = mk_sub(t, val); + m_div_terms.push_back(t); + m_div_divisors.push_back(k); + m_div_coeffs.push_back(c); + } + else { + t = m.mk_eq(a.mk_mod(t, mk_num(k)), val); + add_lit(model, m_new_lits, t); + } + return true; + } + return false; + } + + bool lit_is_true(model& model, expr* e) { + expr_ref val(m); + VERIFY(model.eval(e, val)); + CTRACE("qe", !m.is_true(val), tout << "eval: " << mk_pp(e, m) << " " << val << "\n";); + return m.is_true(val); + } + + expr_ref mk_num(unsigned n) { + rational r(n); + return mk_num(r); + } + + expr_ref mk_num(rational const& r) const { + return expr_ref(a.mk_numeral(r, var_sort()), m); + } + + expr_ref mk_divides(rational const& k, expr* t) { + return expr_ref(m.mk_eq(a.mk_mod(t, mk_num(abs(k))), mk_num(0)), m); + } + + void reset() { + reset_ineqs(); + reset_divs(); + m_delta = rational(1); + m_u = rational(0); + m_new_lits.reset(); + } + + void reset_divs() { + m_div_terms.reset(); + m_div_coeffs.reset(); + m_div_divisors.reset(); + } + + void reset_ineqs() { + m_ineq_terms.reset(); + m_ineq_coeffs.reset(); + m_ineq_types.reset(); + } + + expr* ineq_term(unsigned i) const { return m_ineq_terms[i]; } + rational const& ineq_coeff(unsigned i) const { return m_ineq_coeffs[i]; } + ineq_type ineq_ty(unsigned i) const { return m_ineq_types[i]; } + app_ref mk_ineq_pred(unsigned i) { + app_ref result(m); + result = to_app(mk_add(mk_mul(ineq_coeff(i), m_var->x()), ineq_term(i))); + switch (ineq_ty(i)) { + case t_lt: + result = a.mk_lt(result, mk_num(0)); + break; + case t_le: + result = a.mk_le(result, mk_num(0)); + break; + case t_eq: + result = m.mk_eq(result, mk_num(0)); + break; + } + return result; + } + void display_ineq(std::ostream& out, unsigned i) const { + out << mk_pp(ineq_term(i), m) << " " << ineq_coeff(i) << "*" << mk_pp(m_var->x(), m); + switch (ineq_ty(i)) { + case t_eq: out << " = 0\n"; break; + case t_le: out << " <= 0\n"; break; + case t_lt: out << " < 0\n"; break; + } + } + unsigned num_ineqs() const { return m_ineq_terms.size(); } + expr* div_term(unsigned i) const { return m_div_terms[i]; } + rational const& div_coeff(unsigned i) const { return m_div_coeffs[i]; } + rational const& div_divisor(unsigned i) const { return m_div_divisors[i]; } + void display_div(std::ostream& out, unsigned i) const { + out << div_divisor(i) << " | ( " << mk_pp(div_term(i), m) << " " << div_coeff(i) << "*" + << mk_pp(m_var->x(), m) << ")\n"; + } + unsigned num_divs() const { return m_div_terms.size(); } + + void project(model& model, expr_ref_vector& lits) { + TRACE("qe", + tout << "project: " << mk_pp(m_var->x(), m) << "\n"; + tout << lits; + model_v2_pp(tout, model); ); + + m_num_pos = 0; m_num_neg = 0; + m_pos_is_unit = true; m_neg_is_unit = true; + unsigned eq_index = 0; + reset(); + bool found_eq = false; + for (unsigned i = 0; i < lits.size(); ++i) { + bool found_eq0 = false; + expr* e = lits[i].get(); + if (!(*m_var)(e)) { + m_new_lits.push_back(e); + } + else if (!is_linear(model, e, found_eq0)) { + TRACE("qe", tout << "can't project:" << mk_pp(e, m) << "\n";); + throw cant_project(); + } + if (found_eq0 && !found_eq) { + found_eq = true; + eq_index = num_ineqs()-1; + } + } + TRACE("qe", display(tout << mk_pp(m_var->x(), m) << ":\n"); + tout << "found eq: " << found_eq << " @ " << eq_index << "\n"; + tout << "num pos: " << m_num_pos << " num neg: " << m_num_neg << " num divs " << num_divs() << "\n"; + ); lits.reset(); - lits.append(new_lits); - if (num_pos == 0 || num_neg == 0) { + lits.append(m_new_lits); + if (found_eq) { + apply_equality(model, eq_index, lits); return; } - bool use_pos = num_pos < num_neg; - unsigned max_t = find_max(model, use_pos); - - for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { - if (i != max_t) { - if (m_ineq_coeffs[i].is_pos() == use_pos) { - lits.push_back(mk_le(i, max_t)); + if (num_divs() == 0 && (m_num_pos == 0 || m_num_neg == 0)) { + return; + } + if (num_divs() > 0) { + apply_divides(model, lits); + TRACE("qe", display(tout << "after division " << mk_pp(m_var->x(), m) << "\n");); + } + if (m_num_pos == 0 || m_num_neg == 0) { + return; + } + if ((m_num_pos <= 2 || m_num_neg <= 2) && + (m_num_pos == 1 || m_num_neg == 1 || (m_num_pos <= 3 && m_num_neg <= 3)) && + (!is_int() || m_pos_is_unit || m_neg_is_unit)) { + + unsigned index1 = num_ineqs(); + unsigned index2 = num_ineqs(); + bool is_pos = m_num_pos <= m_num_neg; + for (unsigned i = 0; i < num_ineqs(); ++i) { + if (ineq_coeff(i).is_pos() == is_pos) { + if (index1 == num_ineqs()) { + index1 = i; + } + else { + SASSERT(index2 == num_ineqs()); + index2 = i; + } } - else { - lits.push_back(mk_lt(i, max_t)); + } + for (unsigned i = 0; i < num_ineqs(); ++i) { + if (ineq_coeff(i).is_pos() != is_pos) { + SASSERT(index1 != num_ineqs()); + mk_lt(model, lits, i, index1); + if (index2 != num_ineqs()) { + mk_lt(model, lits, i, index2); + } } } } + else { + expr_ref t(m); + bool use_pos = m_num_pos < m_num_neg; + unsigned max_t = find_max(model, use_pos); + + for (unsigned i = 0; i < num_ineqs(); ++i) { + if (i != max_t) { + if (ineq_coeff(i).is_pos() == use_pos) { + t = mk_le(i, max_t); + add_lit(model, lits, t); + } + else { + mk_lt(model, lits, i, max_t); + } + } + } + } + TRACE("qe", tout << lits;); } unsigned find_max(model& mdl, bool do_pos) { unsigned result; - bool found = false; - rational found_val(0), r, found_c; + bool new_max = true; + rational max_r, r; expr_ref val(m); - for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { + bool is_int = a.is_int(m_var->x()); + for (unsigned i = 0; i < num_ineqs(); ++i) { rational const& ac = m_ineq_coeffs[i]; + SASSERT(!is_int || t_le == ineq_ty(i)); + + // + // ac*x + t < 0 + // ac > 0: x + max { t/ac | ac > 0 } < 0 <=> x < - max { t/ac | ac > 0 } + // ac < 0: x + t/ac > 0 <=> x > max { - t/ac | ac < 0 } = max { t/|ac| | ac < 0 } + // if (ac.is_pos() == do_pos) { - VERIFY(mdl.eval(m_ineq_terms[i].get(), val)); + VERIFY(mdl.eval(ineq_term(i), val)); VERIFY(a.is_numeral(val, r)); r /= abs(ac); - IF_VERBOSE(1, verbose_stream() << "max: " << mk_pp(m_ineq_terms[i].get(), m) << " " << r << " " << (!found || r > found_val) << "\n";); - if (!found || r > found_val) { + new_max = + new_max || + (r > max_r) || + (r == max_r && t_lt == ineq_ty(i)) || + (r == max_r && is_int && ac.is_one()); + TRACE("qe", tout << "max: " << mk_pp(ineq_term(i), m) << "/" << abs(ac) << " := " << r << " " + << (new_max?"":"not ") << "new max\n";); + if (new_max) { result = i; - found_val = r; - found_c = ac; - found = true; + max_r = r; } + new_max = false; } } - SASSERT(found); - if (a.is_int(m_var->x()) && !found_c.is_one()) { - throw cant_project(); - } + SASSERT(!new_max); return result; } @@ -206,102 +575,310 @@ namespace qe { // a and b have different signs. // Infer: a|b|x + |b|t + |a|bx + |a|s <= 0 // e.g. |b|t + |a|s <= 0 - expr_ref mk_lt(unsigned i, unsigned j) { - rational const& ac = m_ineq_coeffs[i]; - rational const& bc = m_ineq_coeffs[j]; + void mk_lt(model& model, expr_ref_vector& lits, unsigned i, unsigned j) { + rational const& ac = ineq_coeff(i); + rational const& bc = ineq_coeff(j); SASSERT(ac.is_pos() != bc.is_pos()); SASSERT(ac.is_neg() != bc.is_neg()); - expr* t = m_ineq_terms[i].get(); - expr* s = m_ineq_terms[j].get(); + + TRACE("qe", display_ineq(tout, i); display_ineq(tout, j);); + + if (is_int() && !abs(ac).is_one() && !abs(bc).is_one()) { + return mk_int_lt(model, lits, i, j); + } + expr* t = ineq_term(i); + expr* s = ineq_term(j); expr_ref bt = mk_mul(abs(bc), t); expr_ref as = mk_mul(abs(ac), s); expr_ref ts = mk_add(bt, as); - expr* z = a.mk_numeral(rational(0), m.get_sort(t)); - expr_ref result1(m), result2(m); - if (m_ineq_strict[i] || m_ineq_strict[j]) { - result1 = a.mk_lt(ts, z); + expr_ref z = mk_num(0); + expr_ref fml(m); + if (t_lt == ineq_ty(i) || t_lt == ineq_ty(j)) { + fml = a.mk_lt(ts, z); } else { - result1 = a.mk_le(ts, z); + fml = a.mk_le(ts, z); } - m_rw(result1, result2); - return result2; + add_lit(model, lits, fml); + } + + void mk_int_lt(model& model, expr_ref_vector& lits, unsigned i, unsigned j) { + TRACE("qe", display_ineq(tout, i); display_ineq(tout, j);); + + expr* t = ineq_term(i); + expr* s = ineq_term(j); + rational ac = ineq_coeff(i); + rational bc = ineq_coeff(j); + expr_ref tmp(m); + SASSERT(t_le == ineq_ty(i) && t_le == ineq_ty(j)); + SASSERT(ac.is_pos() == bc.is_neg()); + rational abs_a = abs(ac); + rational abs_b = abs(bc); + expr_ref as(mk_mul(abs_a, s), m); + expr_ref bt(mk_mul(abs_b, t), m); + + rational slack = (abs_a - rational(1))*(abs_b-rational(1)); + rational sval, tval; + VERIFY (model.eval(ineq_term(i), tmp) && a.is_numeral(tmp, tval)); + VERIFY (model.eval(ineq_term(j), tmp) && a.is_numeral(tmp, sval)); + bool use_case1 = ac*sval + bc*tval + slack <= rational(0); + if (use_case1) { + expr_ref_vector ts(m); + ts.push_back(as); + ts.push_back(bt); + ts.push_back(mk_num(-slack)); + tmp = a.mk_le(add(ts), mk_num(0)); + add_lit(model, lits, tmp); + return; + } + + if (abs_a < abs_b) { + std::swap(abs_a, abs_b); + std::swap(ac, bc); + std::swap(s, t); + std::swap(as, bt); + std::swap(sval, tval); + } + SASSERT(abs_a >= abs_b); + + // create finite disjunction for |b|. + // exists x, z in [0 .. |b|-2] . b*x + s + z = 0 && ax + t <= 0 && bx + s <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && ax + t <= 0 && bx + s <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && bx + s <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z - s + s <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a*n_sign(b)(s + z) + |b|t <= 0 + // <=> + // exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 + // + + rational z = mod(sval, abs_b); + if (!z.is_zero()) z = abs_b - z; + expr_ref s_plus_z(mk_add(z, s), m); + + tmp = mk_divides(abs_b, s_plus_z); + add_lit(model, lits, tmp); + tmp = a.mk_le(mk_add(mk_mul(ac*n_sign(bc), s_plus_z), + mk_mul(abs_b, t)), mk_num(0)); + add_lit(model, lits, tmp); + } + + rational n_sign(rational const& b) { + return rational(b.is_pos()?-1:1); } // ax + t <= 0 // bx + s <= 0 // a and b have same signs. - // encode:// t/|a| <= s/|b| + // encode: + // t/|a| <= s/|b| // e.g. |b|t <= |a|s expr_ref mk_le(unsigned i, unsigned j) { - rational const& ac = m_ineq_coeffs[i]; - rational const& bc = m_ineq_coeffs[j]; + rational const& ac = ineq_coeff(i); + rational const& bc = ineq_coeff(j); SASSERT(ac.is_pos() == bc.is_pos()); SASSERT(ac.is_neg() == bc.is_neg()); - expr* t = m_ineq_terms[i].get(); - expr* s = m_ineq_terms[j].get(); + expr* t = ineq_term(i); + expr* s = ineq_term(j); expr_ref bt = mk_mul(abs(bc), t); expr_ref as = mk_mul(abs(ac), s); - expr_ref result1(m), result2(m); - if (m_ineq_strict[j] && !m_ineq_strict[i]) { - result1 = a.mk_lt(bt, as); + if (t_lt == ineq_ty(i) && t_le == ineq_ty(j)) { + return expr_ref(a.mk_lt(bt, as), m); } else { - result1 = a.mk_le(bt, as); + return expr_ref(a.mk_le(bt, as), m); } - m_rw(result1, result2); - return result2; } - expr_ref mk_add(expr* t1, expr* t2) { + rational r; + if (a.is_numeral(t1, r) && r.is_zero()) return expr_ref(t2, m); + if (a.is_numeral(t2, r) && r.is_zero()) return expr_ref(t1, m); return expr_ref(a.mk_add(t1, t2), m); } - expr_ref mk_mul(rational const& r, expr* t2) { - expr* t1 = a.mk_numeral(r, m.get_sort(t2)); - return expr_ref(a.mk_mul(t1, t2), m); + expr_ref mk_add(rational const& r, expr* e) { + if (r.is_zero()) return expr_ref(e, m); + return mk_add(mk_num(r), e); + } + + expr_ref mk_mul(rational const& r, expr* t) { + if (r.is_one()) return expr_ref(t, m); + return expr_ref(a.mk_mul(mk_num(r), t), m); } - public: - arith_project_util(ast_manager& m): - m(m), a(m), m_rw(m), m_ineq_terms(m) {} + expr_ref mk_sub(expr* t1, expr* t2) { + rational r1, r2; + if (a.is_numeral(t2, r2) && r2.is_zero()) return expr_ref(t1, m); + if (a.is_numeral(t1, r1) && a.is_numeral(t2, r2)) return mk_num(r1 - r2); + return expr_ref(a.mk_sub(t1, t2), m); + } - expr_ref operator()(model& model, app_ref_vector& vars, expr_ref_vector const& lits) { - app_ref_vector new_vars(m); - expr_ref_vector result(lits); - for (unsigned i = 0; i < vars.size(); ++i) { - app* v = vars[i].get(); - m_var = alloc(contains_app, m, v); - try { - project(model, result); - TRACE("qe", tout << "projected: " << mk_pp(v, m) << " "; - for (unsigned i = 0; i < result.size(); ++i) { - tout << mk_pp(result[i].get(), m) << "\n"; - }); - } - catch (cant_project) { - IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(v, m) << "\n";); - new_vars.push_back(v); + expr_ref mk_uminus(expr* t) { + rational r; + if (a.is_numeral(t, r)) { + return mk_num(-r); + } + return expr_ref(a.mk_uminus(t), m); + } + + void add_lit(model& model, expr_ref_vector& lits, expr* e) { + expr_ref orig(e, m), result(m); + m_rw(orig, result); + TRACE("qe", tout << mk_pp(orig, m) << " -> " << result << "\n";); + SASSERT(lit_is_true(model, orig)); + SASSERT(lit_is_true(model, result)); + if (!m.is_true(result)) { + lits.push_back(result); + } + } + + + // 3x + t = 0 & 7 | (c*x + s) & ax <= u + // 3 | -t & 21 | (-ct + 3s) & a-t <= 3u + + void apply_equality(model& model, unsigned eq_index, expr_ref_vector& lits) { + rational c = ineq_coeff(eq_index); + expr* t = ineq_term(eq_index); + SASSERT(c.is_pos()); + if (is_int()) { + add_lit(model, lits, mk_divides(c, ineq_term(eq_index))); + } + + for (unsigned i = 0; i < num_divs(); ++i) { + add_lit(model, lits, mk_divides(c*div_divisor(i), + mk_sub(mk_mul(c, div_term(i)), mk_mul(div_coeff(i), t)))); + } + for (unsigned i = 0; i < num_ineqs(); ++i) { + if (eq_index != i) { + expr_ref lhs(m), val(m); + lhs = mk_sub(mk_mul(c, ineq_term(i)), mk_mul(ineq_coeff(i), t)); + switch (ineq_ty(i)) { + case t_lt: lhs = a.mk_lt(lhs, mk_num(0)); break; + case t_le: lhs = a.mk_le(lhs, mk_num(0)); break; + case t_eq: lhs = m.mk_eq(lhs, mk_num(0)); break; + } + TRACE("qe", tout << lhs << "\n";); + SASSERT (model.eval(lhs, val) && m.is_true(val)); + add_lit(model, lits, lhs); } } - vars.reset(); - vars.append(new_vars); - return qe::mk_and(result); - } + } + + // + // compute D and u. + // + // D = lcm(d1, d2) + // u = eval(x) mod D + // + // d1 | (a1x + t1) & d2 | (a2x + t2) + // = + // D | (D/d1)(a1x + t1) & D | (D/d2)(a2x + t2) + // = + // D | D1(a1*u + t1) & D | D2(a2*u + t2) & x = D*x' + u & 0 <= u < D + // = + // D | D1(a1*u + t1) & D | D2(a2*u + t2) & x = D*x' + u & 0 <= u < D + // + // x := D*x' + u + // + void apply_divides(model& model, expr_ref_vector& lits) { + SASSERT(m_delta.is_one()); + unsigned n = num_divs(); + if (n == 0) { + return; + } + for (unsigned i = 0; i < n; ++i) { + m_delta = lcm(m_delta, div_divisor(i)); + } + expr_ref val(m); + rational r; + VERIFY (model.eval(m_var->x(), val) && a.is_numeral(val, r)); + m_u = mod(r, m_delta); + SASSERT(m_u < m_delta && rational(0) <= m_u); + for (unsigned i = 0; i < n; ++i) { + add_lit(model, lits, mk_divides(div_divisor(i), + mk_add(mk_num(div_coeff(i) * m_u), div_term(i)))); + } + reset_divs(); + // + // update inequalities such that u is added to t and + // D is multiplied to coefficient of x. + // the interpretation of the new version of x is (x-u)/D + // + // a*x + t <= 0 + // a*(D*x' + u) + t <= 0 + // a*D*x' + a*u + t <= 0 + for (unsigned i = 0; i < num_ineqs(); ++i) { + if (!m_u.is_zero()) { + m_ineq_terms[i] = a.mk_add(ineq_term(i), mk_num(m_ineq_coeffs[i]*m_u)); + } + m_ineq_coeffs[i] *= m_delta; + } + r = (r - m_u) / m_delta; + SASSERT(r.is_int()); + val = a.mk_numeral(r, true); + model.register_decl(m_var->x()->get_decl(), val); + TRACE("qe", model_v2_pp(tout, model);); + } + + imp(ast_manager& m): + m(m), a(m), m_rw(m), m_ineq_terms(m), m_div_terms(m), m_new_lits(m) { + params_ref params; + params.set_bool("gcd_rouding", true); + m_rw.updt_params(params); + } + + ~imp() { + } + + bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return false; + } + + bool operator()(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) { + SASSERT(a.is_real(v) || a.is_int(v)); + m_var = alloc(contains_app, m, v); + try { + project(model, lits); + } + catch (cant_project) { + TRACE("qe", tout << "can't project:" << mk_pp(v, m) << "\n";); + return false; + } + return true; + } }; - expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits) { - ast_manager& m = vars.get_manager(); - arith_project_util ap(m); - return ap(model, vars, lits); + arith_project_plugin::arith_project_plugin(ast_manager& m) { + m_imp = alloc(imp, m); } - expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml) { - ast_manager& m = vars.get_manager(); - arith_project_util ap(m); - expr_ref_vector lits(m); - flatten_and(fml, lits); - return ap(model, vars, lits); + arith_project_plugin::~arith_project_plugin() { + dealloc(m_imp); } + bool arith_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + return (*m_imp)(model, var, vars, lits); + } + + bool arith_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return m_imp->solve(model, vars, lits); + } + + family_id arith_project_plugin::get_family_id() { + return m_imp->a.get_family_id(); + } + + bool arith_project(model& model, app* var, expr_ref_vector& lits) { + ast_manager& m = lits.get_manager(); + arith_project_plugin ap(m); + app_ref_vector vars(m); + return ap(model, var, vars, lits); + } } diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index 16a89794f..be5415a45 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -9,16 +9,33 @@ Copyright (c) 2015 Microsoft Corporation #define QE_ARITH_H_ #include "model.h" +#include "arith_decl_plugin.h" +#include "qe_mbp.h" namespace qe { + /** Loos-Weispfenning model-based projection for a basic conjunction. Lits is a vector of literals. return vector of variables that could not be projected. */ - expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits); - expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml); + class arith_project_plugin : public project_plugin { + struct imp; + imp* m_imp; + public: + arith_project_plugin(ast_manager& m); + virtual ~arith_project_plugin(); + virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); + virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); + virtual family_id get_family_id(); + }; + + bool arith_project(model& model, app* var, expr_ref_vector& lits); + + // match e := t mod k = 0. + bool is_divides(arith_util& a, expr* e, rational& k, expr_ref& t); + }; #endif diff --git a/src/qe/qe_arith_plugin.cpp b/src/qe/qe_arith_plugin.cpp index 75cba61fe..8bb3fa6e2 100644 --- a/src/qe/qe_arith_plugin.cpp +++ b/src/qe/qe_arith_plugin.cpp @@ -32,6 +32,7 @@ Revision History: #include "nlarith_util.h" #include "model_evaluator.h" #include "smt_kernel.h" +#include "qe_arith.h" namespace qe { @@ -266,23 +267,8 @@ namespace qe { // // match 0 == p mod k, p mod k == 0 // - bool is_divides(app* e, numeral& k, expr_ref& p) { - expr* e1, *e2; - if (!m.is_eq(e, e1, e2)) { - return false; - } - return is_divides(e1, e2, k, p) || is_divides(e2, e1, k, p); - } - - bool is_divides(expr* e1, expr* e2, numeral& k, expr_ref& p) { - if (m_arith.is_mod(e2) && - m_arith.is_numeral(e1, k) && - k.is_zero() && - m_arith.is_numeral(to_app(e2)->get_arg(1), k)) { - p = to_app(e2)->get_arg(0); - return true; - } - return false; + bool is_divides(expr* e, numeral& k, expr_ref& p) { + return qe::is_divides(m_arith, e, k, p); } bool is_not_divides(app* e, app_ref& n, numeral& k, expr_ref& p) { diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp new file mode 100644 index 000000000..4f2ce81c5 --- /dev/null +++ b/src/qe/qe_arrays.cpp @@ -0,0 +1,427 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qe_arrays.cpp + +Abstract: + + Model based projection for arrays + +Author: + + Nikolaj Bjorner (nbjorner) 2015-06-13 + +Revision History: + +--*/ + + +#include "qe_arrays.h" +#include "rewriter_def.h" +#include "expr_functors.h" +#include "expr_safe_replace.h" +#include "lbool.h" +#include "ast_util.h" +#include "ast_pp.h" + +namespace qe { + + struct array_project_plugin::imp { + + // rewriter or direct procedure. + struct rw_cfg : public default_rewriter_cfg { + ast_manager& m; + array_util& a; + expr_ref_vector m_lits; + model* m_model; + imp* m_imp; + + rw_cfg(ast_manager& m, array_util& a): + m(m), a(a), m_lits(m), m_model(0) {} + + br_status reduce_app(func_decl* f, unsigned n, expr* const* args, expr_ref& result, proof_ref & pr) { + if (a.is_select(f) && a.is_store(args[0])) { + expr_ref val1(m), val2(m); + app* b = to_app(args[0]); + SASSERT(b->get_num_args() == n + 1); + for (unsigned i = 1; i < n; ++i) { + expr* arg1 = args[i]; + expr* arg2 = b->get_arg(i); + if (arg1 == arg2) { + val1 = val2 = arg1; + } + else { + VERIFY(m_model->eval(arg1, val1)); + VERIFY(m_model->eval(arg2, val2)); + } + switch(compare(val1, val2)) { + case l_true: + if (arg1 != arg2) { + m_lits.push_back(m.mk_eq(arg1, arg2)); + } + break; + case l_false: { + ptr_vector new_args; + if (i > 0) { + m_lits.resize(m_lits.size() - i); + } + m_lits.push_back(m.mk_not(m.mk_eq(arg1, arg2))); + new_args.push_back(b->get_arg(0)); + new_args.append(n-1, args+1); + result = m.mk_app(f, n, new_args.c_ptr()); + return BR_REWRITE1; + } + case l_undef: + return BR_FAILED; + } + } + result = b->get_arg(n); + return BR_DONE; + } + return BR_FAILED; + } + + lbool compare(expr* x, expr* y) { + NOT_IMPLEMENTED_YET(); + return l_undef; + } + }; + + struct indices { + expr_ref_vector m_values; + expr* const* m_vars; + + indices(ast_manager& m, model& model, unsigned n, expr* const* vars): + m_values(m), m_vars(vars) { + expr_ref val(m); + for (unsigned i = 0; i < n; ++i) { + VERIFY(model.eval(vars[i], val)); + m_values.push_back(val); + } + } + }; + + ast_manager& m; + array_util a; + scoped_ptr m_var; + + imp(ast_manager& m): m(m), a(m) {} + ~imp() {} + + virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return false; + } + + bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + + TRACE("qe", tout << mk_pp(var, m) << "\n" << lits;); + m_var = alloc(contains_app, m, var); + + // reduce select-store redeces based on model. + // rw_cfg rw(m); + // rw(lits); + + // try first to solve for var. + if (solve_eq(model, vars, lits)) { + return true; + } + + app_ref_vector selects(m); + + // check that only non-select occurrences are in disequalities. + if (!check_diseqs(lits, selects)) { + TRACE("qe", tout << "Could not project " << mk_pp(var, m) << " for:\n" << lits << "\n";); + return false; + } + + // remove disequalities. + elim_diseqs(lits); + + // Ackerman reduction on remaining select occurrences + // either replace occurrences by model value or other node + // that is congruent to model value. + + ackermanize_select(model, selects, vars, lits); + + TRACE("qe", tout << selects << "\n" << lits << "\n";); + return true; + } + + void ackermanize_select(model& model, app_ref_vector const& selects, app_ref_vector& vars, expr_ref_vector& lits) { + expr_ref_vector vals(m), reps(m); + expr_ref val(m); + expr_safe_replace sub(m); + + if (selects.empty()) { + return; + } + + app_ref sel(m); + for (unsigned i = 0; i < selects.size(); ++i) { + sel = m.mk_fresh_const("sel", m.get_sort(selects[i])); + VERIFY (model.eval(selects[i], val)); + model.register_decl(sel->get_decl(), val); + vals.push_back(to_app(val)); + reps.push_back(val); // TODO: direct pass could handle nested selects. + vars.push_back(sel); + sub.insert(selects[i], val); + } + + sub(lits); + remove_true(lits); + project_plugin::partition_args(model, selects, lits); + project_plugin::partition_values(model, reps, lits); + } + + void remove_true(expr_ref_vector& lits) { + for (unsigned i = 0; i < lits.size(); ++i) { + if (m.is_true(lits[i].get())) { + project_plugin::erase(lits, i); + } + } + } + + bool contains_x(expr* e) { + return (*m_var)(e); + } + + void mk_eq(indices& x, indices y, expr_ref_vector& lits) { + unsigned n = x.m_values.size(); + for (unsigned j = 0; j < n; ++j) { + lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); + } + } + + // check that x occurs only under selects or in disequalities. + bool check_diseqs(expr_ref_vector const& lits, app_ref_vector& selects) { + expr_mark mark; + ptr_vector todo; + app* e; + for (unsigned i = 0; i < lits.size(); ++i) { + e = to_app(lits[i]); + if (is_diseq_x(e)) { + continue; + } + if (contains_x(e)) { + todo.push_back(e); + } + } + while (!todo.empty()) { + e = todo.back(); + todo.pop_back(); + if (mark.is_marked(e)) { + continue; + } + mark.mark(e); + if (m_var->x() == e) { + return false; + } + unsigned start = 0; + if (a.is_select(e)) { + if (e->get_arg(0) == m_var->x()) { + start = 1; + selects.push_back(e); + } + } + for (unsigned i = start; i < e->get_num_args(); ++i) { + todo.push_back(to_app(e->get_arg(i))); + } + } + return true; + } + + void elim_diseqs(expr_ref_vector& lits) { + for (unsigned i = 0; i < lits.size(); ++i) { + if (is_diseq_x(lits[i].get())) { + project_plugin::erase(lits, i); + } + } + } + + bool is_update_x(app* e) { + do { + if (m_var->x() == e) { + return true; + } + if (a.is_store(e) && contains_x(e->get_arg(0))) { + for (unsigned i = 1; i < e->get_num_args(); ++i) { + if (contains_x(e->get_arg(i))) { + return false; + } + } + e = to_app(e->get_arg(0)); + continue; + } + } + while (false); + return false; + } + + bool is_diseq_x(expr* e) { + expr *f, * s, *t; + if (m.is_not(e, f) && m.is_eq(f, s, t)) { + if (contains_x(s) && !contains_x(t) && is_update_x(to_app(s))) return true; + if (contains_x(t) && !contains_x(s) && is_update_x(to_app(t))) return true; + } + return false; + } + + bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + // find an equality to solve for. + expr* s, *t; + for (unsigned i = 0; i < lits.size(); ++i) { + if (m.is_eq(lits[i].get(), s, t)) { + vector idxs; + expr_ref save(m), back(m); + save = lits[i].get(); + back = lits.back(); + lits[i] = back; + lits.pop_back(); + unsigned sz = lits.size(); + if (contains_x(s) && !contains_x(t) && is_app(s)) { + if (solve(model, to_app(s), t, idxs, vars, lits)) { + return true; + } + } + else if (contains_x(t) && !contains_x(s) && is_app(t)) { + if (solve(model, to_app(t), s, idxs, vars, lits)) { + return true; + } + } + // put back the equality literal. + lits.resize(sz); + lits.push_back(back); + lits[i] = save; + } + // TBD: not distinct? + } + return false; + } + + bool solve(model& model, app* s, expr* t, vector& idxs, app_ref_vector& vars, expr_ref_vector& lits) { + SASSERT(contains_x(s)); + SASSERT(!contains_x(t)); + + if (s == m_var->x()) { + expr_ref result(s, m); + expr_ref_vector args(m); + sort* range = get_array_range(m.get_sort(s)); + for (unsigned i = 0; i < idxs.size(); ++i) { + app_ref var(m); + var = m.mk_fresh_const("value", range); + vars.push_back(var); + args.reset(); + args.push_back(result); + args.append(idxs[i].m_values.size(), idxs[i].m_vars); + args.push_back(var); + result = a.mk_store(args.size(), args.c_ptr()); + } + expr_safe_replace sub(m); + sub.insert(s, result); + for (unsigned i = 0; i < lits.size(); ++i) { + sub(lits[i].get(), result); + lits[i] = result; + } + return true; + } + + if (a.is_store(s)) { + unsigned n = s->get_num_args()-2; + indices idx(m, model, n, s->get_args()+1); + for (unsigned i = 1; i < n; ++i) { + if (contains_x(s->get_arg(i))) { + return false; + } + } + unsigned i; + expr_ref_vector args(m); + switch (contains(idx, idxs, i)) { + case l_true: + mk_eq(idx, idxs[i], lits); + return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); + case l_false: + for (unsigned i = 0; i < idxs.size(); ++i) { + expr_ref_vector eqs(m); + mk_eq(idx, idxs[i], eqs); + lits.push_back(m.mk_not(mk_and(eqs))); // TBD: extract single index of difference based on model. + } + args.push_back(t); + args.append(n, s->get_args()+1); + lits.push_back(m.mk_eq(a.mk_select(args.size(), args.c_ptr()), s->get_arg(n+1))); + idxs.push_back(idx); + return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); + case l_undef: + return false; + } + } + return false; + } + + lbool contains(indices const& idx, vector const& idxs, unsigned& j) { + for (unsigned i = 0; i < idxs.size(); ++i) { + switch (compare(idx, idxs[i])) { + case l_true: + j = i; + return l_true; + case l_false: + break; + case l_undef: + return l_undef; + } + } + return l_false; + } + + lbool compare(indices const& idx1, indices const& idx2) { + unsigned n = idx1.m_values.size(); + for (unsigned i = 0; i < n; ++i) { + switch (compare(idx1.m_values[i], idx2.m_values[i])) { + case l_true: + break; + case l_false: + return l_false; + case l_undef: + return l_undef; + } + } + return l_true; + } + + lbool compare(expr* val1, expr* val2) { + if (val1 == val2) { + return l_true; + } + if (is_uninterp(val1) || + is_uninterp(val2)) { + // TBD chase definition of nested array. + return l_undef; + } + return l_true; + } + }; + + + array_project_plugin::array_project_plugin(ast_manager& m) { + m_imp = alloc(imp, m); + } + + array_project_plugin::~array_project_plugin() { + dealloc(m_imp); + } + + bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + return (*m_imp)(model, var, vars, lits); + } + + bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return m_imp->solve(model, vars, lits); + } + + family_id array_project_plugin::get_family_id() { + return m_imp->a.get_family_id(); + } + +}; + diff --git a/src/qe/qe_arrays.h b/src/qe/qe_arrays.h new file mode 100644 index 000000000..182d82255 --- /dev/null +++ b/src/qe/qe_arrays.h @@ -0,0 +1,42 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qe_arrays.h + +Abstract: + + Model based projection for arrays + +Author: + + Nikolaj Bjorner (nbjorner) 2015-06-13 + +Revision History: + +--*/ + + +#ifndef __QE_ARRAYS_H_ +#define __QE_ARRAYS_H_ + +#include "array_decl_plugin.h" +#include "qe_mbp.h" + +namespace qe { + + class array_project_plugin : public project_plugin { + struct imp; + imp* m_imp; + public: + array_project_plugin(ast_manager& m); + virtual ~array_project_plugin(); + virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); + virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); + virtual family_id get_family_id(); + }; + +}; + +#endif diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp new file mode 100644 index 000000000..81001dea5 --- /dev/null +++ b/src/qe/qe_datatypes.cpp @@ -0,0 +1,312 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qe_datatypes.cpp + +Abstract: + + Simple projection function for algebraic datatypes. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-06-13 + +Revision History: + +--*/ + +#include "qe_arith.h" +#include "ast_pp.h" +#include "th_rewriter.h" +#include "expr_functors.h" +#include "model_v2_pp.h" +#include "expr_safe_replace.h" +#include "obj_pair_hashtable.h" +#include "qe_datatypes.h" + +namespace qe { + + struct datatype_project_plugin::imp { + ast_manager& m; + datatype_util dt; + app_ref m_val; + scoped_ptr m_var; + + imp(ast_manager& m): + m(m), dt(m), m_val(m) {} + + virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return lift_foreign(vars, lits); + } + + bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + expr_ref val(m); + VERIFY(model.eval(var, val)); + SASSERT(is_app(val)); + TRACE("qe", tout << mk_pp(var, m) << " := " << val << "\n";); + m_val = to_app(val); + if (!dt.is_constructor(m_val)) { + // assert: var does not occur in lits. + return true; + } + m_var = alloc(contains_app, m, var); + + + try { + if (dt.is_recursive(m.get_sort(var))) { + project_rec(model, vars, lits); + } + else { + project_nonrec(model, vars, lits); + } + } + catch (cant_project) { + TRACE("qe", tout << "can't project:" << mk_pp(var, m) << "\n";); + return false; + } + return true; + } + + void project_nonrec(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + expr_ref tmp(m), val(m); + expr_ref_vector args(m); + app_ref arg(m); + SASSERT(dt.is_constructor(m_val)); + func_decl* f = m_val->get_decl(); + ptr_vector const& acc = *dt.get_constructor_accessors(f); + for (unsigned i = 0; i < acc.size(); ++i) { + arg = m.mk_fresh_const(acc[i]->get_name().str().c_str(), acc[i]->get_range()); + model.register_decl(arg->get_decl(), m_val->get_arg(i)); + args.push_back(arg); + } + val = m.mk_app(f, args.size(), args.c_ptr()); + TRACE("qe", tout << mk_pp(m_var->x(), m) << " |-> " << val << "\n";); + reduce(val, lits); + } + + void project_rec(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + func_decl* f = m_val->get_decl(); + expr_ref rhs(m); + expr_ref_vector eqs(m); + for (unsigned i = 0; i < lits.size(); ++i) { + if (solve(model, vars, lits[i].get(), rhs, eqs)) { + project_plugin::erase(lits, i); + reduce(rhs, lits); + lits.append(eqs); + return; + } + } + + // otherwise, unfold the constructor associated with m_var + // once according to the model. In this way, selector-constructor + // redexes are reduced and disequalities are eventually solved + // by virtue of the constructors being different. + project_nonrec(model, vars, lits); + } + + void reduce(expr* val, expr_ref_vector& lits) { + expr_safe_replace sub(m); + th_rewriter rw(m); + expr_ref tmp(m); + sub.insert(m_var->x(), val); + TRACE("qe", tout << mk_pp(m_var->x(), m) << " = " << mk_pp(val, m) << "\n"; + tout << lits << "\n";); + for (unsigned i = 0; i < lits.size(); ++i) { + sub(lits[i].get(), tmp); + rw(tmp); + lits[i] = tmp; + } + } + + bool contains_x(expr* e) { + return (*m_var)(e); + } + + bool solve(model& model, app_ref_vector& vars, expr* fml, expr_ref& t, expr_ref_vector& eqs) { + expr* t1, *t2; + if (m.is_eq(fml, t1, t2)) { + if (contains_x(t1) && !contains_x(t2) && is_app(t1)) { + return solve(model, vars, to_app(t1), t2, t, eqs); + } + if (contains_x(t2) && !contains_x(t1) && is_app(t2)) { + return solve(model, vars, to_app(t2), t1, t, eqs); + } + } + if (m.is_not(fml, t1) && m.is_distinct(t1)) { + expr_ref eq = project_plugin::pick_equality(m, model, t1); + return solve(model, vars, eq, t, eqs); + } + return false; + } + + bool solve(model& model, app_ref_vector& vars, app* a, expr* b, expr_ref& t, expr_ref_vector& eqs) { + SASSERT(contains_x(a)); + SASSERT(!contains_x(b)); + if (m_var->x() == a) { + t = b; + return true; + } + if (!dt.is_constructor(a)) { + return false; + } + func_decl* c = a->get_decl(); + func_decl* rec = dt.get_constructor_recognizer(c); + ptr_vector const & acc = *dt.get_constructor_accessors(c); + SASSERT(acc.size() == a->get_num_args()); + // + // It suffices to solve just the first available equality. + // The others are determined by the first. + // + for (unsigned i = 0; i < a->get_num_args(); ++i) { + expr* l = a->get_arg(i); + if (is_app(l) && contains_x(l)) { + expr_ref r(m); + r = access(c, i, acc, b); + if (solve(model, vars, to_app(l), r, t, eqs)) { + for (unsigned j = 0; j < c->get_arity(); ++j) { + if (i != j) { + eqs.push_back(m.mk_eq(access(c, j, acc, b), a->get_arg(j))); + } + } + if (!is_app_of(b, c)) { + eqs.push_back(m.mk_app(rec, b)); + } + + return true; + } + } + } + return false; + } + + + expr* access(func_decl* c, unsigned i, ptr_vector const& acc, expr* e) { + if (is_app_of(e,c)) { + return to_app(e)->get_arg(i); + } + else { + return m.mk_app(acc[i], e); + } + } + + bool lift_foreign(app_ref_vector const& vars, expr_ref_vector& lits) { + + bool reduced = false; + expr_mark visited; + expr_mark has_var; + bool inserted = false; + for (unsigned i = 0; i < vars.size(); ++i) { + if (m.is_bool(vars[i])) continue; + if (dt.is_datatype(m.get_sort(vars[i]))) continue; + inserted = true; + has_var.mark(vars[i]); + visited.mark(vars[i]); + } + if (inserted) { + for (unsigned i = 0; i < lits.size(); ++i) { + expr* e = lits[i].get(), *l, *r; + if (m.is_eq(e, l, r) && reduce_eq(visited, has_var, l, r, lits)) { + project_plugin::erase(lits, i); + reduced = true; + } + } + CTRACE("qe", reduced, tout << vars << "\n" << lits << "\n";); + } + return reduced; + } + + bool reduce_eq(expr_mark& has_var, expr_mark& visited, expr* l, expr* r, expr_ref_vector& lits) { + if (!is_app(l) || !is_app(r)) { + return false; + } + bool reduce = false; + if (dt.is_constructor(to_app(r)) && contains_foreign(has_var, visited, r)) { + std::swap(l, r); + reduce = true; + } + reduce |= dt.is_constructor(to_app(l)) && contains_foreign(has_var, visited, l); + if (!reduce) { + return false; + } + func_decl* c = to_app(l)->get_decl(); + ptr_vector const& acc = *dt.get_constructor_accessors(c); + if (!is_app_of(r, c)) { + lits.push_back(m.mk_app(dt.get_constructor_recognizer(c), r)); + } + for (unsigned i = 0; i < acc.size(); ++i) { + lits.push_back(m.mk_eq(to_app(l)->get_arg(i), access(c, i, acc, r))); + } + return true; + } + + + ptr_vector todo; + + bool contains_foreign(expr_mark& has_var, expr_mark& visited, expr* e) { + todo.push_back(e); + while (!todo.empty()) { + expr* _f = todo.back(); + if (visited.is_marked(_f)) { + todo.pop_back(); + continue; + } + if (!is_app(_f)) { + visited.mark(_f); + todo.pop_back(); + continue; + } + app* f = to_app(_f); + bool has_new = false, has_v = false; + for (unsigned i = 0; i < f->get_num_args(); ++i) { + expr* arg = f->get_arg(i); + if (!visited.is_marked(arg)) { + todo.push_back(arg); + has_new = true; + } + else { + has_v |= has_var.is_marked(arg); + } + } + if (has_new) { + continue; + } + todo.pop_back(); + if (has_v) { + has_var.mark(f); + } + TRACE("qe", tout << "contains: " << mk_pp(f, m) << " " << has_var.is_marked(f) << "\n";); + visited.mark(f); + } + TRACE("qe", tout << "contains: " << mk_pp(e, m) << " " << has_var.is_marked(e) << "\n";); + return has_var.is_marked(e); + } + + }; + + datatype_project_plugin::datatype_project_plugin(ast_manager& m) { + m_imp = alloc(imp, m); + } + + datatype_project_plugin::~datatype_project_plugin() { + dealloc(m_imp); + } + + bool datatype_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + return (*m_imp)(model, var, vars, lits); + } + + bool datatype_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return m_imp->solve(model, vars, lits); + } + + + family_id datatype_project_plugin::get_family_id() { + return m_imp->dt.get_family_id(); + } + +} + + + diff --git a/src/qe/qe_datatypes.h b/src/qe/qe_datatypes.h new file mode 100644 index 000000000..f616e7f2e --- /dev/null +++ b/src/qe/qe_datatypes.h @@ -0,0 +1,42 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qe_datatypes.h + +Abstract: + + Model based projection for algebraic datatypes + +Author: + + Nikolaj Bjorner (nbjorner) 2015-06-13 + +Revision History: + +--*/ + + +#ifndef __QE_DATATYPES_H_ +#define __QE_DATATYPES_H_ + +#include "datatype_decl_plugin.h" +#include "qe_mbp.h" + +namespace qe { + + class datatype_project_plugin : public project_plugin { + struct imp; + imp* m_imp; + public: + datatype_project_plugin(ast_manager& m); + virtual ~datatype_project_plugin(); + virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); + virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); + virtual family_id get_family_id(); + }; + +}; + +#endif diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 9a981cc9b..aad802a2a 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -31,7 +31,6 @@ Revision History: #include "var_subst.h" #include "uint_set.h" #include "ast_util.h" -#include "qe_util.h" #include "th_rewriter.h" #include "for_each_expr.h" #include "expr_safe_replace.h" diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp new file mode 100644 index 000000000..c8ec34726 --- /dev/null +++ b/src/qe/qe_mbp.cpp @@ -0,0 +1,369 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qe_mbp.cpp + +Abstract: + + Model-based projection utilities + +Author: + + Nikolaj Bjorner (nbjorner) 2015-5-29 + +Revision History: + + +--*/ + +#include "qe_mbp.h" +#include "qe_arith.h" +#include "qe_arrays.h" +#include "qe_datatypes.h" +#include "expr_safe_replace.h" +#include "ast_pp.h" +#include "ast_util.h" +#include "th_rewriter.h" +#include "model_v2_pp.h" +#include "expr_functors.h" + + +using namespace qe; + + +/** + \brief return two terms that are equal in the model. + The distinct term t is false in model, so there + are at least two arguments of t that are equal in the model. +*/ +expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { + SASSERT(m.is_distinct(t)); + expr_ref val(m); + expr_ref_vector vals(m); + obj_map val2expr; + app* alit = to_app(t); + for (unsigned i = 0; i < alit->get_num_args(); ++i) { + expr* e1 = alit->get_arg(i), *e2; + VERIFY(model.eval(e1, val)); + if (val2expr.find(val, e2)) { + return expr_ref(m.mk_eq(e1, e2), m); + } + val2expr.insert(val, e1); + vals.push_back(val); + } + UNREACHABLE(); + return expr_ref(0, m); +} + +void project_plugin::partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits) { + ast_manager& m = vals.get_manager(); + expr_ref val(m); + expr_ref_vector trail(m), reps(m); + obj_map roots; + for (unsigned i = 0; i < vals.size(); ++i) { + expr* v = vals[i], *root; + VERIFY (model.eval(v, val)); + if (roots.find(val, root)) { + lits.push_back(m.mk_eq(v, root)); + } + else { + roots.insert(val, v); + trail.push_back(val); + reps.push_back(v); + } + } + if (reps.size() > 1) { + lits.push_back(mk_distinct(reps)); + } +} + +void project_plugin::partition_args(model& model, app_ref_vector const& selects, expr_ref_vector& lits) { + ast_manager& m = selects.get_manager(); + if (selects.empty()) return; + unsigned num_args = selects[0]->get_decl()->get_arity(); + for (unsigned j = 1; j < num_args; ++j) { + expr_ref_vector args(m); + for (unsigned i = 0; i < selects.size(); ++i) { + args.push_back(selects[i]->get_arg(j)); + } + project_plugin::partition_values(model, args, lits); + } +} + +void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { + lits[i] = lits.back(); + lits.pop_back(); + --i; +} + +void project_plugin::push_back(expr_ref_vector& lits, expr* e) { + if (lits.get_manager().is_true(e)) return; + lits.push_back(e); +} + + +class mbp::impl { + ast_manager& m; + ptr_vector m_plugins; + + void add_plugin(project_plugin* p) { + family_id fid = p->get_family_id(); + SASSERT(!m_plugins.get(fid, 0)); + m_plugins.setx(fid, p, 0); + } + + project_plugin* get_plugin(app* var) { + family_id fid = m.get_sort(var)->get_family_id(); + return m_plugins.get(fid, 0); + } + + bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + expr_mark is_var, is_rem; + if (vars.empty()) { + return false; + } + bool reduced = false; + for (unsigned i = 0; i < vars.size(); ++i) { + is_var.mark(vars[i].get()); + } + expr_ref tmp(m), t(m), v(m); + th_rewriter rw(m); + for (unsigned i = 0; i < lits.size(); ++i) { + expr* e = lits[i].get(), *l, *r; + if (m.is_eq(e, l, r) && reduce_eq(is_var, l, r, v, t)) { + reduced = true; + lits[i] = lits.back(); + lits.pop_back(); + --i; + expr_safe_replace sub(m); + sub.insert(v, t); + is_rem.mark(v); + for (unsigned j = 0; j < lits.size(); ++j) { + sub(lits[j].get(), tmp); + rw(tmp); + lits[j] = tmp; + } + } + } + if (reduced) { + for (unsigned i = 0; i < vars.size(); ++i) { + if (is_rem.is_marked(vars[i].get())) { + vars[i] = vars.back(); + vars.pop_back(); + } + } + } + return reduced; + } + + bool reduce_eq(expr_mark& is_var, expr* l, expr* r, expr_ref& v, expr_ref& t) { + if (is_var.is_marked(r)) { + std::swap(l, r); + } + if (is_var.is_marked(l)) { + contains_app cont(m, to_app(l)); + if (!cont(r)) { + v = to_app(l); + t = r; + return true; + } + } + return false; + } + +public: + + void extract_literals(model& model, expr_ref_vector& fmls) { + expr_ref val(m); + for (unsigned i = 0; i < fmls.size(); ++i) { + expr* fml = fmls[i].get(), *nfml, *f1, *f2, *f3; + if (m.is_not(fml, nfml) && m.is_distinct(nfml)) { + fmls[i] = project_plugin::pick_equality(m, model, nfml); + --i; + } + else if (m.is_or(fml)) { + for (unsigned j = 0; j < to_app(fml)->get_num_args(); ++j) { + VERIFY (model.eval(to_app(fml)->get_arg(j), val)); + if (m.is_true(val)) { + fmls[i] = to_app(fml)->get_arg(j); + --i; + break; + } + } + } + else if (m.is_and(fml)) { + fmls.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); + project_plugin::erase(fmls, i); + } + else if (m.is_iff(fml, f1, f2) || (m.is_not(fml, nfml) && m.is_xor(nfml, f1, f2))) { + VERIFY (model.eval(f1, val)); + if (m.is_false(val)) { + f1 = mk_not(m, f1); + f2 = mk_not(m, f2); + } + project_plugin::push_back(fmls, f1); + project_plugin::push_back(fmls, f2); + project_plugin::erase(fmls, i); + } + else if (m.is_implies(fml, f1, f2)) { + VERIFY (model.eval(f2, val)); + if (m.is_true(val)) { + project_plugin::push_back(fmls, f2); + } + else { + project_plugin::push_back(fmls, mk_not(m, f1)); + } + project_plugin::erase(fmls, i); + } + else if (m.is_ite(fml, f1, f2, f3)) { + VERIFY (model.eval(f1, val)); + if (m.is_true(val)) { + project_plugin::push_back(fmls, f2); + } + else { + project_plugin::push_back(fmls, f3); + } + project_plugin::erase(fmls, i); + } + else if (m.is_not(fml, nfml) && m.is_not(nfml, nfml)) { + project_plugin::push_back(fmls, nfml); + project_plugin::erase(fmls, i); + } + else if (m.is_not(fml, nfml) && m.is_and(nfml)) { + for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) { + VERIFY (model.eval(to_app(nfml)->get_arg(j), val)); + if (m.is_false(val)) { + fmls[i] = mk_not(m, to_app(nfml)->get_arg(j)); + --i; + break; + } + } + } + else if (m.is_not(fml, nfml) && m.is_or(nfml)) { + for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) { + project_plugin::push_back(fmls, mk_not(m, to_app(nfml)->get_arg(j))); + } + project_plugin::erase(fmls, i); + } + else if ((m.is_not(fml, nfml) && m.is_iff(nfml, f1, f2)) || m.is_xor(fml, f1, f2)) { + VERIFY (model.eval(f1, val)); + if (m.is_true(val)) { + f2 = mk_not(m, f2); + } + else { + f1 = mk_not(m, f1); + } + project_plugin::push_back(fmls, f1); + project_plugin::push_back(fmls, f2); + project_plugin::erase(fmls, i); + } + else if (m.is_not(fml, nfml) && m.is_implies(nfml, f1, f2)) { + project_plugin::push_back(fmls, f1); + project_plugin::push_back(fmls, mk_not(m, f2)); + project_plugin::erase(fmls, i); + } + else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) { + VERIFY (model.eval(f1, val)); + if (m.is_true(val)) { + project_plugin::push_back(fmls, mk_not(m, f2)); + } + else { + project_plugin::push_back(fmls, mk_not(m, f3)); + } + project_plugin::erase(fmls, i); + } + else { + // TBD other Boolean operations. + } + } + } + + impl(ast_manager& m):m(m) { + add_plugin(alloc(arith_project_plugin, m)); + add_plugin(alloc(datatype_project_plugin, m)); + add_plugin(alloc(array_project_plugin, m)); + } + + ~impl() { + std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); + } + + void preprocess_solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + extract_literals(model, fmls); + bool change = true; + while (change && !vars.empty()) { + change = solve(model, vars, fmls); + for (unsigned i = 0; i < m_plugins.size(); ++i) { + if (m_plugins[i] && m_plugins[i]->solve(model, vars, fmls)) { + change = true; + } + } + } + } + + void operator()(bool force_elim, app_ref_vector& vars, model& model, expr_ref_vector& fmls) { + expr_ref val(m), tmp(m); + app_ref var(m); + th_rewriter rw(m); + bool progress = true; + while (progress && !vars.empty()) { + preprocess_solve(model, vars, fmls); + app_ref_vector new_vars(m); + progress = false; + while (!vars.empty()) { + var = vars.back(); + vars.pop_back(); + project_plugin* p = get_plugin(var); + if (p && (*p)(model, var, vars, fmls)) { + progress = true; + } + else { + new_vars.push_back(var); + } + } + if (!progress && !new_vars.empty() && force_elim) { + var = new_vars.back(); + new_vars.pop_back(); + expr_safe_replace sub(m); + VERIFY(model.eval(var, val)); + sub.insert(var, val); + for (unsigned i = 0; i < fmls.size(); ++i) { + sub(fmls[i].get(), tmp); + rw(tmp); + if (m.is_true(tmp)) { + fmls[i] = fmls.back(); + fmls.pop_back(); + --i; + } + else { + fmls[i] = tmp; + } + } + progress = true; + } + vars.append(new_vars); + } + } +}; + +mbp::mbp(ast_manager& m) { + m_impl = alloc(impl, m); +} + +mbp::~mbp() { + dealloc(m_impl); +} + +void mbp::operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls) { + (*m_impl)(force_elim, vars, mdl, fmls); +} + +void mbp::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + m_impl->preprocess_solve(model, vars, fmls); +} + +void mbp::extract_literals(model& model, expr_ref_vector& lits) { + m_impl->extract_literals(model, lits); +} diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h new file mode 100644 index 000000000..5b4c59762 --- /dev/null +++ b/src/qe/qe_mbp.h @@ -0,0 +1,76 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qe_mbp.h + +Abstract: + + Model-based projection utilities + +Author: + + Nikolaj Bjorner (nbjorner) 2015-5-28 + +Revision History: + + +--*/ + +#ifndef __QE_MBP_H__ +#define __QE_MBP_H__ + +#include "ast.h" +#include "params.h" +#include "model.h" + + +namespace qe { + + struct cant_project {}; + + class project_plugin { + public: + virtual ~project_plugin() {} + virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) = 0; + virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0; + virtual family_id get_family_id() = 0; + + static expr_ref pick_equality(ast_manager& m, model& model, expr* t); + static void partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits); + static void partition_args(model& model, app_ref_vector const& sels, expr_ref_vector& lits); + static void erase(expr_ref_vector& lits, unsigned& i); + static void push_back(expr_ref_vector& lits, expr* lit); + }; + + class mbp { + class impl; + impl * m_impl; + public: + mbp(ast_manager& m); + + ~mbp(); + + /** + \brief + Apply model-based qe on constants provided as vector of variables. + Return the updated formula and updated set of variables that were not eliminated. + */ + void operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls); + + /** + \brief + Solve as many variables as possible using "cheap" quantifier elimination" + */ + void solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); + + /** + \brief + Extract literals from formulas based on model. + */ + void extract_literals(model& model, expr_ref_vector& lits); + }; +} + +#endif diff --git a/src/qe/qe_tactic.cpp b/src/qe/qe_tactic.cpp index c8660b916..935af8550 100644 --- a/src/qe/qe_tactic.cpp +++ b/src/qe/qe_tactic.cpp @@ -22,6 +22,7 @@ Revision History: #include"qe.h" class qe_tactic : public tactic { + statistics m_st; struct imp { ast_manager & m; smt_params m_fparams; @@ -78,10 +79,19 @@ class qe_tactic : public tactic { g->update(i, new_f, new_pr, g->dep(i)); } g->inc_depth(); + g->elim_true(); result.push_back(g.get()); TRACE("qe", g->display(tout);); SASSERT(g->is_well_sorted()); } + + void collect_statistics(statistics & st) const { + m_qe.collect_statistics(st); + } + + void reset_statistics() { + } + }; imp * m_imp; @@ -117,7 +127,19 @@ public: proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); + m_st.reset(); + m_imp->collect_statistics(m_st); + } + + virtual void collect_statistics(statistics & st) const { + st.copy(m_st); + } + + virtual void reset_statistics() { + m_st.reset(); + } + virtual void cleanup() { ast_manager & m = m_imp->m; diff --git a/src/qe/qe_util.cpp b/src/qe/qe_util.cpp deleted file mode 100644 index 2bcda608f..000000000 --- a/src/qe/qe_util.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -#include "qe_util.h" -#include "bool_rewriter.h" - -namespace qe { - - expr_ref mk_and(expr_ref_vector const& fmls) { - ast_manager& m = fmls.get_manager(); - expr_ref result(m); - bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), result); - return result; - } - - expr_ref mk_or(expr_ref_vector const& fmls) { - ast_manager& m = fmls.get_manager(); - expr_ref result(m); - bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), result); - return result; - } - -} diff --git a/src/qe/qe_util.h b/src/qe/qe_util.h deleted file mode 100644 index ea082d78c..000000000 --- a/src/qe/qe_util.h +++ /dev/null @@ -1,31 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - qe_util.h - -Abstract: - - Utilities for quantifiers. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-08-28 - -Revision History: - ---*/ -#ifndef QE_UTIL_H_ -#define QE_UTIL_H_ - -#include "ast.h" - -namespace qe { - - expr_ref mk_and(expr_ref_vector const& fmls); - - expr_ref mk_or(expr_ref_vector const& fmls); - -} -#endif diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp new file mode 100644 index 000000000..b080b71a2 --- /dev/null +++ b/src/qe/qsat.cpp @@ -0,0 +1,1177 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qsat.cpp + +Abstract: + + Quantifier Satisfiability Solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-5-28 + +Revision History: + +Notes: + + +--*/ + +#include "smt_kernel.h" +#include "qe_mbp.h" +#include "smt_params.h" +#include "ast_util.h" +#include "quant_hoist.h" +#include "ast_pp.h" +#include "model_v2_pp.h" +#include "qsat.h" +#include "expr_abstract.h" +#include "qe.h" +#include "label_rewriter.h" + + +namespace qe { + + pred_abs::pred_abs(ast_manager& m): + m(m), + m_asms(m), + m_trail(m), + m_fmc(alloc(filter_model_converter, m)) + { + } + + filter_model_converter* pred_abs::fmc() { + return m_fmc.get(); + } + + void pred_abs::reset() { + m_trail.reset(); + dec_keys(m_pred2lit); + dec_keys(m_lit2pred); + dec_keys(m_asm2pred); + dec_keys(m_pred2asm); + m_lit2pred.reset(); + m_pred2lit.reset(); + m_asm2pred.reset(); + m_pred2asm.reset(); + m_elevel.reset(); + m_asms.reset(); + m_asms_lim.reset(); + m_preds.reset(); + } + + max_level pred_abs::compute_level(app* e) { + unsigned sz0 = todo.size(); + todo.push_back(e); + while (sz0 != todo.size()) { + app* a = to_app(todo.back()); + if (m_elevel.contains(a)) { + todo.pop_back(); + continue; + } + max_level lvl, lvl0; + bool has_new = false; + if (m_flevel.find(a->get_decl(), lvl)) { + lvl0.merge(lvl); + } + for (unsigned i = 0; i < a->get_num_args(); ++i) { + app* arg = to_app(a->get_arg(i)); + if (m_elevel.find(arg, lvl)) { + lvl0.merge(lvl); + } + else { + todo.push_back(arg); + has_new = true; + } + } + if (!has_new) { + m_elevel.insert(a, lvl0); + todo.pop_back(); + } + } + return m_elevel.find(e); + } + + void pred_abs::add_pred(app* p, app* lit) { + m.inc_ref(p); + m_pred2lit.insert(p, lit); + add_lit(p, lit); + } + + void pred_abs::add_lit(app* p, app* lit) { + if (!m_lit2pred.contains(lit)) { + m.inc_ref(lit); + m_lit2pred.insert(lit, p); + } + } + + void pred_abs::add_asm(app* p, expr* assum) { + SASSERT(!m_asm2pred.contains(assum)); + m.inc_ref(p); + m.inc_ref(assum); + m_asm2pred.insert(assum, p); + m_pred2asm.insert(p, assum); + } + + void pred_abs::push() { + m_asms_lim.push_back(m_asms.size()); + } + + void pred_abs::pop(unsigned num_scopes) { + unsigned l = m_asms_lim.size() - num_scopes; + m_asms.resize(m_asms_lim[l]); + m_asms_lim.shrink(l); + } + + void pred_abs::insert(app* a, max_level const& lvl) { + unsigned l = lvl.max(); + if (l == UINT_MAX) l = 0; + while (m_preds.size() <= l) { + m_preds.push_back(app_ref_vector(m)); + } + m_preds[l].push_back(a); + } + + bool pred_abs::is_predicate(app* a, unsigned l) { + max_level lvl1; + return m_flevel.find(a->get_decl(), lvl1) && lvl1.max() < l; + } + + void pred_abs::get_assumptions(model* mdl, expr_ref_vector& asms) { + unsigned level = m_asms_lim.size(); + if (level > m_preds.size()) { + level = m_preds.size(); + } + if (!mdl) { + asms.append(m_asms); + return; + } + if (level == 0) { + return; + } + expr_ref val(m); + for (unsigned j = 0; j < m_preds[level - 1].size(); ++j) { + app* p = m_preds[level - 1][j].get(); + TRACE("qe", tout << "process level: " << level - 1 << ": " << mk_pp(p, m) << "\n";); + + VERIFY(mdl->eval(p, val)); + + if (m.is_false(val)) { + m_asms.push_back(m.mk_not(p)); + } + else { + SASSERT(m.is_true(val)); + m_asms.push_back(p); + } + } + asms.append(m_asms); + + for (unsigned i = level + 1; i < m_preds.size(); i += 2) { + for (unsigned j = 0; j < m_preds[i].size(); ++j) { + app* p = m_preds[i][j].get(); + max_level lvl = m_elevel.find(p); + bool use = + (lvl.m_fa == i && (lvl.m_ex == UINT_MAX || lvl.m_ex < level)) || + (lvl.m_ex == i && (lvl.m_fa == UINT_MAX || lvl.m_fa < level)); + if (use) { + VERIFY(mdl->eval(p, val)); + if (m.is_false(val)) { + asms.push_back(m.mk_not(p)); + } + else { + asms.push_back(p); + } + } + } + } + TRACE("qe", tout << "level: " << level << "\n"; + model_v2_pp(tout, *mdl); + display(tout, asms);); + } + + void pred_abs::set_expr_level(app* v, max_level const& lvl) { + m_elevel.insert(v, lvl); + } + + void pred_abs::set_decl_level(func_decl* f, max_level const& lvl) { + m_flevel.insert(f, lvl); + } + + void pred_abs::abstract_atoms(expr* fml, expr_ref_vector& defs) { + max_level level; + abstract_atoms(fml, level, defs); + } + /** + \brief create propositional abstraction of formula by replacing atomic sub-formulas by fresh + propositional variables, and adding definitions for each propositional formula on the side. + Assumption is that the formula is quantifier-free. + */ + void pred_abs::abstract_atoms(expr* fml, max_level& level, expr_ref_vector& defs) { + expr_mark mark; + ptr_vector args; + app_ref r(m), eq(m); + app* p; + unsigned sz0 = todo.size(); + todo.push_back(fml); + m_trail.push_back(fml); + max_level l; + while (sz0 != todo.size()) { + app* a = to_app(todo.back()); + todo.pop_back(); + if (mark.is_marked(a)) { + continue; + } + + mark.mark(a); + if (m_lit2pred.find(a, p)) { + TRACE("qe", tout << mk_pp(a, m) << " " << mk_pp(p, m) << "\n";); + level.merge(m_elevel.find(p)); + continue; + } + + if (is_uninterp_const(a) && m.is_bool(a)) { + l = m_elevel.find(a); + level.merge(l); + if (!m_pred2lit.contains(a)) { + add_pred(a, a); + insert(a, l); + } + continue; + } + + unsigned sz = a->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + expr* f = a->get_arg(i); + if (!mark.is_marked(f)) { + todo.push_back(f); + } + } + + bool is_boolop = + (a->get_family_id() == m.get_basic_family_id()) && + (!m.is_eq(a) || m.is_bool(a->get_arg(0))) && + (!m.is_distinct(a) || m.is_bool(a->get_arg(0))); + + if (!is_boolop && m.is_bool(a)) { + TRACE("qe", tout << mk_pp(a, m) << "\n";); + r = fresh_bool("p"); + max_level l = compute_level(a); + add_pred(r, a); + m_elevel.insert(r, l); + eq = m.mk_eq(r, a); + defs.push_back(eq); + if (!is_predicate(a, l.max())) { + insert(r, l); + } + level.merge(l); + } + } + } + + app_ref pred_abs::fresh_bool(char const* name) { + app_ref r(m.mk_fresh_const(name, m.mk_bool_sort()), m); + m_fmc->insert(r->get_decl()); + return r; + } + + + // optional pass to replace atoms by predicates + // so that SMT core works on propositional + // abstraction only. + expr_ref pred_abs::mk_abstract(expr* fml) { + expr_ref_vector trail(m), args(m); + obj_map cache; + app* b; + expr_ref r(m); + unsigned sz0 = todo.size(); + todo.push_back(fml); + while (sz0 != todo.size()) { + app* a = to_app(todo.back()); + if (cache.contains(a)) { + todo.pop_back(); + continue; + } + if (m_lit2pred.find(a, b)) { + cache.insert(a, b); + todo.pop_back(); + continue; + } + unsigned sz = a->get_num_args(); + bool diff = false; + args.reset(); + for (unsigned i = 0; i < sz; ++i) { + expr* f = a->get_arg(i), *f1; + if (cache.find(f, f1)) { + args.push_back(f1); + diff |= f != f1; + } + else { + todo.push_back(f); + } + } + if (sz == args.size()) { + if (diff) { + r = m.mk_app(a->get_decl(), sz, args.c_ptr()); + trail.push_back(r); + } + else { + r = a; + } + cache.insert(a, r); + todo.pop_back(); + } + } + return expr_ref(cache.find(fml), m); + } + + expr_ref pred_abs::mk_assumption_literal(expr* a, model* mdl, max_level const& lvl, expr_ref_vector& defs) { + expr_ref A(m); + A = pred2asm(a); + a = A; + app_ref p(m); + expr_ref q(m), fml(m); + app *b; + expr *c, *d; + max_level lvl2; + TRACE("qe", tout << mk_pp(a, m) << " " << lvl << "\n";); + if (m_asm2pred.find(a, b)) { + q = b; + } + else if (m.is_not(a, c) && m_asm2pred.find(c, b)) { + q = m.mk_not(b); + } + else if (m_pred2asm.find(a, d)) { + q = a; + } + else if (m.is_not(a, c) && m_pred2asm.find(c, d)) { + q = a; + } + else { + p = fresh_bool("def"); + if (m.is_not(a, a)) { + if (mdl) + mdl->register_decl(p->get_decl(), m.mk_false()); + q = m.mk_not(p); + } + else { + if (mdl) + mdl->register_decl(p->get_decl(), m.mk_true()); + q = p; + } + m_elevel.insert(p, lvl); + insert(p, lvl); + fml = a; + abstract_atoms(fml, lvl2, defs); + fml = mk_abstract(fml); + defs.push_back(m.mk_eq(p, fml)); + add_asm(p, a); + TRACE("qe", tout << mk_pp(a, m) << " |-> " << p << "\n";); + } + return q; + } + + void pred_abs::mk_concrete(expr_ref_vector& fmls, obj_map const& map) { + obj_map cache; + expr_ref_vector trail(m); + expr* p; + app_ref r(m); + ptr_vector args; + unsigned sz0 = todo.size(); + todo.append(fmls.size(), (expr*const*)fmls.c_ptr()); + while (sz0 != todo.size()) { + app* a = to_app(todo.back()); + if (cache.contains(a)) { + todo.pop_back(); + continue; + } + if (map.find(a, p)) { + cache.insert(a, p); + todo.pop_back(); + continue; + } + unsigned sz = a->get_num_args(); + args.reset(); + bool diff = false; + for (unsigned i = 0; i < sz; ++i) { + expr* f = a->get_arg(i), *f1; + if (cache.find(f, f1)) { + args.push_back(f1); + diff |= f != f1; + } + else { + todo.push_back(f); + } + } + if (args.size() == sz) { + if (diff) { + r = m.mk_app(a->get_decl(), sz, args.c_ptr()); + } + else { + r = to_app(a); + } + cache.insert(a, r); + trail.push_back(r); + todo.pop_back(); + } + } + for (unsigned i = 0; i < fmls.size(); ++i) { + fmls[i] = to_app(cache.find(fmls[i].get())); + } + } + + void pred_abs::pred2lit(expr_ref_vector& fmls) { + mk_concrete(fmls, m_pred2lit); + } + + expr_ref pred_abs::pred2asm(expr* fml) { + expr_ref_vector fmls(m); + fmls.push_back(fml); + mk_concrete(fmls, m_pred2asm); + return mk_and(fmls); + } + + void pred_abs::collect_statistics(statistics& st) const { + st.update("qsat num predicates", m_pred2lit.size()); + } + + void pred_abs::display(std::ostream& out) const { + out << "pred2lit:\n"; + obj_map::iterator it = m_pred2lit.begin(), end = m_pred2lit.end(); + for (; it != end; ++it) { + out << mk_pp(it->m_key, m) << " |-> " << mk_pp(it->m_value, m) << "\n"; + } + for (unsigned i = 0; i < m_preds.size(); ++i) { + out << "level " << i << "\n"; + for (unsigned j = 0; j < m_preds[i].size(); ++j) { + app* p = m_preds[i][j]; + expr* e; + if (m_pred2lit.find(p, e)) { + out << mk_pp(p, m) << " := " << mk_pp(e, m) << "\n"; + } + else { + out << mk_pp(p, m) << "\n"; + } + } + } + } + + void pred_abs::display(std::ostream& out, expr_ref_vector const& asms) const { + max_level lvl; + for (unsigned i = 0; i < asms.size(); ++i) { + expr* e = asms[i]; + bool is_not = m.is_not(asms[i], e); + out << mk_pp(asms[i], m); + if (m_elevel.find(e, lvl)) { + lvl.display(out << " - "); + } + if (m_pred2lit.find(e, e)) { + out << " : " << (is_not?"!":"") << mk_pp(e, m); + } + out << "\n"; + } + } + + void pred_abs::get_free_vars(expr* fml, app_ref_vector& vars) { + ast_fast_mark1 mark; + unsigned sz0 = todo.size(); + todo.push_back(fml); + while (sz0 != todo.size()) { + expr* e = todo.back(); + todo.pop_back(); + if (mark.is_marked(e) || is_var(e)) { + continue; + } + mark.mark(e); + if (is_quantifier(e)) { + todo.push_back(to_quantifier(e)->get_expr()); + continue; + } + SASSERT(is_app(e)); + app* a = to_app(e); + if (is_uninterp_const(a)) { // TBD generalize for uninterpreted functions. + vars.push_back(a); + } + for (unsigned i = 0; i < a->get_num_args(); ++i) { + todo.push_back(a->get_arg(i)); + } + } + } + + class qsat : public tactic { + + struct stats { + unsigned m_num_rounds; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + class kernel { + ast_manager& m; + smt_params m_smtp; + smt::kernel m_kernel; + + public: + kernel(ast_manager& m): + m(m), + m_kernel(m, m_smtp) + { + m_smtp.m_model = true; + m_smtp.m_relevancy_lvl = 0; + m_smtp.m_case_split_strategy = CS_ACTIVITY_WITH_CACHE; + } + + smt::kernel& k() { return m_kernel; } + smt::kernel const& k() const { return m_kernel; } + + void assert_expr(expr* e) { + m_kernel.assert_expr(e); + } + + void get_core(expr_ref_vector& core) { + unsigned sz = m_kernel.get_unsat_core_size(); + core.reset(); + for (unsigned i = 0; i < sz; ++i) { + core.push_back(m_kernel.get_unsat_core_expr(i)); + } + TRACE("qe", tout << "core: " << core << "\n"; + m_kernel.display(tout); + tout << "\n"; + ); + } + }; + + ast_manager& m; + params_ref m_params; + stats m_stats; + statistics m_st; + qe::mbp m_mbp; + kernel m_fa; + kernel m_ex; + pred_abs m_pred_abs; + expr_ref_vector m_answer; + expr_ref_vector m_asms; + vector m_vars; // variables from alternating prefixes. + unsigned m_level; + model_ref m_model; + bool m_qelim; // perform quantifier elimination + bool m_force_elim; // force elimination of variables during projection. + app_ref_vector m_avars; // variables to project + app_ref_vector m_free_vars; + + + /** + \brief check alternating satisfiability. + Even levels are existential, odd levels are universal. + */ + lbool check_sat() { + while (true) { + ++m_stats.m_num_rounds; + check_cancel(); + expr_ref_vector asms(m_asms); + m_pred_abs.get_assumptions(m_model.get(), asms); + TRACE("qe", tout << asms << "\n";); + smt::kernel& k = get_kernel(m_level).k(); + lbool res = k.check(asms); + switch (res) { + case l_true: + k.get_model(m_model); + SASSERT(validate_model(asms)); + TRACE("qe", k.display(tout); display(tout << "\n", *m_model.get()); display(tout, asms); ); + push(); + break; + case l_false: + switch (m_level) { + case 0: return l_false; + case 1: + if (!m_qelim) return l_true; + if (m_model.get()) { + project_qe(asms); + } + else { + pop(1); + } + break; + default: + if (m_model.get()) { + project(asms); + } + else { + pop(1); + } + break; + } + break; + case l_undef: + return res; + } + } + return l_undef; + } + + kernel& get_kernel(unsigned j) { + if (is_exists(j)) { + return m_ex; + } + else { + return m_fa; + } + } + + bool is_exists(unsigned level) const { + return (level % 2) == 0; + } + + bool is_forall(unsigned level) const { + return is_exists(level+1); + } + + void push() { + m_level++; + m_pred_abs.push(); + } + + void pop(unsigned num_scopes) { + m_model.reset(); + SASSERT(num_scopes <= m_level); + m_pred_abs.pop(num_scopes); + m_level -= num_scopes; + } + + void reset() { + m_st.reset(); + m_fa.k().collect_statistics(m_st); + m_ex.k().collect_statistics(m_st); + m_pred_abs.collect_statistics(m_st); + m_level = 0; + m_answer.reset(); + m_asms.reset(); + m_pred_abs.reset(); + m_vars.reset(); + m_model = 0; + m_fa.k().reset(); + m_ex.k().reset(); + m_free_vars.reset(); + } + + /** + \brief create a quantifier prefix formula. + */ + void hoist(expr_ref& fml) { + proof_ref pr(m); + label_rewriter rw(m); + rw.remove_labels(fml, pr); + quantifier_hoister hoist(m); + app_ref_vector vars(m); + bool is_forall = false; + m_pred_abs.get_free_vars(fml, vars); + m_vars.push_back(vars); + vars.reset(); + if (m_qelim) { + is_forall = true; + hoist.pull_quantifier(is_forall, fml, vars); + m_vars.push_back(vars); + } + else { + hoist.pull_quantifier(is_forall, fml, vars); + m_vars.back().append(vars); + } + do { + is_forall = !is_forall; + vars.reset(); + hoist.pull_quantifier(is_forall, fml, vars); + m_vars.push_back(vars); + } + while (!vars.empty()); + SASSERT(m_vars.back().empty()); + initialize_levels(); + TRACE("qe", tout << fml << "\n";); + } + + void initialize_levels() { + // initialize levels. + for (unsigned i = 0; i < m_vars.size(); ++i) { + max_level lvl; + if (is_exists(i)) { + lvl.m_ex = i; + } + else { + lvl.m_fa = i; + } + for (unsigned j = 0; j < m_vars[i].size(); ++j) { + m_pred_abs.set_expr_level(m_vars[i][j].get(), lvl); + } + } + } + + void get_core(expr_ref_vector& core, unsigned level) { + get_kernel(level).get_core(core); + m_pred_abs.pred2lit(core); + } + + void check_cancel() { + if (m.canceled()) { + throw tactic_exception(m.limit().get_cancel_msg()); + } + } + + void display(std::ostream& out) const { + out << "level: " << m_level << "\n"; + for (unsigned i = 0; i < m_vars.size(); ++i) { + for (unsigned j = 0; j < m_vars[i].size(); ++j) { + expr* v = m_vars[i][j]; + out << mk_pp(v, m) << " "; + } + out << "\n"; + } + m_pred_abs.display(out); + } + + void display(std::ostream& out, model& model) const { + display(out); + model_v2_pp(out, model); + } + + void display(std::ostream& out, expr_ref_vector const& asms) const { + m_pred_abs.display(out, asms); + } + + void add_assumption(expr* fml) { + expr_ref eq(m); + app_ref b = m_pred_abs.fresh_bool("b"); + m_asms.push_back(b); + eq = m.mk_eq(b, fml); + m_ex.assert_expr(eq); + m_fa.assert_expr(eq); + m_pred_abs.add_pred(b, to_app(fml)); + max_level lvl; + m_pred_abs.set_expr_level(b, lvl); + } + + void project_qe(expr_ref_vector& core) { + SASSERT(m_level == 1); + expr_ref fml(m); + model& mdl = *m_model.get(); + get_core(core, m_level); + SASSERT(validate_core(core)); + get_vars(m_level); + m_mbp(m_force_elim, m_avars, mdl, core); + fml = negate_core(core); + add_assumption(fml); + m_answer.push_back(fml); + m_free_vars.append(m_avars); + pop(1); + } + + void project(expr_ref_vector& core) { + get_core(core, m_level); + TRACE("qe", display(tout); display(tout << "core\n", core);); + SASSERT(validate_core(core)); + SASSERT(m_level >= 2); + expr_ref fml(m); + expr_ref_vector defs(m), core_save(m); + max_level level; + model& mdl = *m_model.get(); + + get_vars(m_level-1); + SASSERT(validate_project(mdl, core)); + m_mbp(m_force_elim, m_avars, mdl, core); + m_free_vars.append(m_avars); + fml = negate_core(core); + unsigned num_scopes = 0; + + m_pred_abs.abstract_atoms(fml, level, defs); + m_ex.assert_expr(mk_and(defs)); + m_fa.assert_expr(mk_and(defs)); + if (level.max() == UINT_MAX) { + num_scopes = 2*(m_level/2); + } + else if (m_qelim && !m_force_elim) { + num_scopes = 2; + } + else { + SASSERT(level.max() + 2 <= m_level); + num_scopes = m_level - level.max(); + SASSERT(num_scopes >= 2); + if ((num_scopes % 2) != 0) { + --num_scopes; + } + } + + pop(num_scopes); + TRACE("qe", tout << "backtrack: " << num_scopes << " new level: " << m_level << "\nproject:\n" << core << "\n|->\n" << fml << "\n";); + if (m_level == 0 && m_qelim) { + add_assumption(fml); + } + else { + fml = m_pred_abs.mk_abstract(fml); + get_kernel(m_level).assert_expr(fml); + } + } + + void get_vars(unsigned level) { + m_avars.reset(); + for (unsigned i = level; i < m_vars.size(); ++i) { + m_avars.append(m_vars[i]); + } + } + + expr_ref negate_core(expr_ref_vector& core) { + return ::push_not(::mk_and(core)); + } + + expr_ref elim_rec(expr* fml) { + expr_ref tmp(m); + expr_ref_vector trail(m); + obj_map visited; + ptr_vector todo; + trail.push_back(fml); + todo.push_back(fml); + expr* e = 0, *r = 0; + + while (!todo.empty()) { + check_cancel(); + + e = todo.back(); + if (visited.contains(e)) { + todo.pop_back(); + continue; + } + + switch(e->get_kind()) { + case AST_APP: { + app* a = to_app(e); + expr_ref_vector args(m); + unsigned num_args = a->get_num_args(); + bool all_visited = true; + for (unsigned i = 0; i < num_args; ++i) { + if (visited.find(a->get_arg(i), r)) { + args.push_back(r); + } + else { + todo.push_back(a->get_arg(i)); + all_visited = false; + } + } + if (all_visited) { + r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); + todo.pop_back(); + trail.push_back(r); + visited.insert(e, r); + } + break; + } + case AST_QUANTIFIER: { + app_ref_vector vars(m); + quantifier* q = to_quantifier(e); + bool is_fa = q->is_forall(); + tmp = q->get_expr(); + extract_vars(q, tmp, vars); + TRACE("qe", tout << vars << " " << mk_pp(q, m) << " " << tmp << "\n";); + tmp = elim_rec(tmp); + if (is_fa) { + tmp = ::push_not(tmp); + } + tmp = elim(vars, tmp); + if (is_fa) { + tmp = ::push_not(tmp); + } + trail.push_back(tmp); + visited.insert(e, tmp); + todo.pop_back(); + break; + } + default: + UNREACHABLE(); + break; + } + } + VERIFY (visited.find(fml, e)); + return expr_ref(e, m); + } + + expr_ref elim(app_ref_vector const& vars, expr* _fml) { + expr_ref fml(_fml, m); + reset(); + m_vars.push_back(app_ref_vector(m)); + m_vars.push_back(vars); + initialize_levels(); + fml = push_not(fml); + + TRACE("qe", tout << vars << " " << fml << "\n";); + expr_ref_vector defs(m); + m_pred_abs.abstract_atoms(fml, defs); + fml = m_pred_abs.mk_abstract(fml); + m_ex.assert_expr(mk_and(defs)); + m_fa.assert_expr(mk_and(defs)); + m_ex.assert_expr(fml); + m_fa.assert_expr(m.mk_not(fml)); + TRACE("qe", tout << "ex: " << fml << "\n";); + lbool is_sat = check_sat(); + fml = ::mk_and(m_answer); + TRACE("qe", tout << "ans: " << fml << "\n"; + tout << "Free vars: " << m_free_vars << "\n";); + if (is_sat == l_false) { + obj_hashtable vars; + for (unsigned i = 0; i < m_free_vars.size(); ++i) { + app* v = m_free_vars[i].get(); + if (vars.contains(v)) { + m_free_vars[i] = m_free_vars.back(); + m_free_vars.pop_back(); + --i; + } + else { + vars.insert(v); + } + } + fml = mk_exists(m, m_free_vars.size(), m_free_vars.c_ptr(), fml); + return fml; + } + else { + return expr_ref(_fml, m); + } + } + + bool validate_core(expr_ref_vector const& core) { + TRACE("qe", tout << "Validate core\n";); + smt::kernel& k = get_kernel(m_level).k(); + expr_ref_vector fmls(m); + fmls.append(core.size(), core.c_ptr()); + fmls.append(k.size(), k.get_formulas()); + return check_fmls(fmls); + } + + bool check_fmls(expr_ref_vector const& fmls) { + smt_params p; + smt::kernel solver(m, p); + for (unsigned i = 0; i < fmls.size(); ++i) { + solver.assert_expr(fmls[i]); + } + lbool is_sat = solver.check(); + CTRACE("qe", is_sat != l_false, + tout << fmls << "\nare not unsat\n";); + return (is_sat == l_false); + } + + bool validate_model(expr_ref_vector const& asms) { + TRACE("qe", tout << "Validate model\n";); + smt::kernel& k = get_kernel(m_level).k(); + return + validate_model(*m_model, asms.size(), asms.c_ptr()) && + validate_model(*m_model, k.size(), k.get_formulas()); + } + + bool validate_model(model& mdl, unsigned sz, expr* const* fmls) { + expr_ref val(m); + for (unsigned i = 0; i < sz; ++i) { + if (!m_model->eval(fmls[i], val)) { + TRACE("qe", tout << "Formula does not evaluate in model: " << mk_pp(fmls[i], m) << "\n";); + return false; + } + if (!m.is_true(val)) { + TRACE("qe", tout << mk_pp(fmls[i], m) << " evaluates to " << val << " in model\n";); + return false; + } + } + return true; + } + + // validate the following: + // proj is true in model. + // core is true in model. + // proj does not contain vars. + // proj => exists vars . core + // (core[model(vars)/vars] => proj) + + bool validate_project(model& mdl, expr_ref_vector const& core) { + TRACE("qe", tout << "Validate projection\n";); + if (!validate_model(mdl, core.size(), core.c_ptr())) return false; + + expr_ref_vector proj(core); + app_ref_vector vars(m_avars); + m_mbp(false, vars, mdl, proj); + if (!vars.empty()) { + TRACE("qe", tout << "Not validating partial projection\n";); + return true; + } + if (!validate_model(mdl, proj.size(), proj.c_ptr())) { + TRACE("qe", tout << "Projection is false in model\n";); + return false; + } + for (unsigned i = 0; i < m_avars.size(); ++i) { + contains_app cont(m, m_avars[i].get()); + if (cont(proj)) { + TRACE("qe", tout << "Projection contains free variable: " << mk_pp(m_avars[i].get(), m) << "\n";); + return false; + } + } + + // + // TBD: proj => exists vars . core, + // e.g., extract and use realizer functions from mbp. + // + + // (core[model(vars)/vars] => proj) + expr_ref_vector fmls(m); + fmls.append(core.size(), core.c_ptr()); + for (unsigned i = 0; i < m_avars.size(); ++i) { + expr_ref val(m); + VERIFY(mdl.eval(m_avars[i].get(), val)); + fmls.push_back(m.mk_eq(m_avars[i].get(), val)); + } + fmls.push_back(m.mk_not(mk_and(proj))); + if (!check_fmls(fmls)) { + TRACE("qe", tout << "implication check failed, could be due to turning != into >\n";); + } + return true; + } + + public: + + qsat(ast_manager& m, params_ref const& p, bool qelim, bool force_elim): + m(m), + m_mbp(m), + m_fa(m), + m_ex(m), + m_pred_abs(m), + m_answer(m), + m_asms(m), + m_level(0), + m_qelim(qelim), + m_force_elim(force_elim), + m_avars(m), + m_free_vars(m) + { + reset(); + } + + virtual ~qsat() { + reset(); + } + + void updt_params(params_ref const & p) { + } + + void collect_param_descrs(param_descrs & r) { + } + + + void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result, + /* out */ model_converter_ref & mc, + /* out */ proof_converter_ref & pc, + /* out */ expr_dependency_ref & core) { + tactic_report report("qsat-tactic", *in); + ptr_vector fmls; + expr_ref_vector defs(m); + expr_ref fml(m); + mc = 0; pc = 0; core = 0; + in->get_formulas(fmls); + fml = mk_and(m, fmls.size(), fmls.c_ptr()); + + // for now: + // fail if cores. (TBD) + // fail if proofs. (TBD) + + if (!m_force_elim) { + fml = elim_rec(fml); + in->reset(); + in->inc_depth(); + in->assert_expr(fml); + result.push_back(in.get()); + return; + } + + reset(); + TRACE("qe", tout << fml << "\n";); + if (m_qelim) { + fml = push_not(fml); + } + hoist(fml); + m_pred_abs.abstract_atoms(fml, defs); + fml = m_pred_abs.mk_abstract(fml); + m_ex.assert_expr(mk_and(defs)); + m_fa.assert_expr(mk_and(defs)); + m_ex.assert_expr(fml); + m_fa.assert_expr(m.mk_not(fml)); + TRACE("qe", tout << "ex: " << fml << "\n";); + lbool is_sat = check_sat(); + + switch (is_sat) { + case l_false: + in->reset(); + in->inc_depth(); + if (m_qelim) { + fml = ::mk_and(m_answer); + in->assert_expr(fml); + } + else { + in->assert_expr(m.mk_false()); + } + result.push_back(in.get()); + break; + case l_true: + in->reset(); + in->inc_depth(); + result.push_back(in.get()); + if (in->models_enabled()) { + mc = model2model_converter(m_model.get()); + mc = concat(m_pred_abs.fmc(), mc.get()); + } + break; + case l_undef: + result.push_back(in.get()); + std::string s = m_ex.k().last_failure_as_string(); + if (s == "ok") { + s = m_fa.k().last_failure_as_string(); + } + throw tactic_exception(s.c_str()); + } + } + + void collect_statistics(statistics & st) const { + st.copy(m_st); + st.update("qsat num rounds", m_stats.m_num_rounds); + m_pred_abs.collect_statistics(st); + } + + void reset_statistics() { + m_stats.reset(); + m_fa.k().reset_statistics(); + m_ex.k().reset_statistics(); + } + + void cleanup() { + reset(); + } + + void set_logic(symbol const & l) { + } + + void set_progress_callback(progress_callback * callback) { + } + + tactic * translate(ast_manager & m) { + return alloc(qsat, m, m_params, m_qelim, m_force_elim); + } + + + }; + +}; + +tactic * mk_qsat_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, false, true); +} + +tactic * mk_qe2_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, true, true); +} + +tactic * mk_qe_rec_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, true, false); +} + + diff --git a/src/qe/qsat.h b/src/qe/qsat.h new file mode 100644 index 000000000..ee9f9e6ad --- /dev/null +++ b/src/qe/qsat.h @@ -0,0 +1,135 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qsat.h + +Abstract: + + Quantifier Satisfiability Solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-5-28 + +Revision History: + + +--*/ + +#ifndef QE_QSAT_H__ +#define QE_QSAT_H__ + +#include "tactic.h" +#include "filter_model_converter.h" + +namespace qe { + + struct max_level { + unsigned m_ex, m_fa; + max_level(): m_ex(UINT_MAX), m_fa(UINT_MAX) {} + void merge(max_level const& other) { + merge(m_ex, other.m_ex); + merge(m_fa, other.m_fa); + } + static unsigned max(unsigned a, unsigned b) { + if (a == UINT_MAX) return b; + if (b == UINT_MAX) return a; + return std::max(a, b); + } + unsigned max() const { + return max(m_ex, m_fa); + } + void merge(unsigned& lvl, unsigned other) { + lvl = max(lvl, other); + } + std::ostream& display(std::ostream& out) const { + if (m_ex != UINT_MAX) out << "e" << m_ex << " "; + if (m_fa != UINT_MAX) out << "a" << m_fa << " "; + return out; + } + + bool operator==(max_level const& other) const { + return + m_ex == other.m_ex && + m_fa == other.m_fa; + } + }; + + inline std::ostream& operator<<(std::ostream& out, max_level const& lvl) { + return lvl.display(out); + } + + class pred_abs { + ast_manager& m; + vector m_preds; + expr_ref_vector m_asms; + unsigned_vector m_asms_lim; + obj_map m_pred2lit; // maintain definitions of predicates. + obj_map m_lit2pred; // maintain reverse mapping to predicates + obj_map m_asm2pred; // maintain map from assumptions to predicates + obj_map m_pred2asm; // predicates |-> assumptions + expr_ref_vector m_trail; + filter_model_converter_ref m_fmc; + ptr_vector todo; + obj_map m_elevel; + obj_map m_flevel; + + template + void dec_keys(obj_map& map) { + typename obj_map::iterator it = map.begin(), end = map.end(); + for (; it != end; ++it) { + m.dec_ref(it->m_key); + } + } + void add_lit(app* p, app* lit); + void add_asm(app* p, expr* lit); + bool is_predicate(app* a, unsigned l); + void mk_concrete(expr_ref_vector& fmls, obj_map const& map); + public: + + pred_abs(ast_manager& m); + filter_model_converter* fmc(); + void reset(); + max_level compute_level(app* e); + void push(); + void pop(unsigned num_scopes); + void insert(app* a, max_level const& lvl); + void get_assumptions(model* mdl, expr_ref_vector& asms); + void set_expr_level(app* v, max_level const& lvl); + void set_decl_level(func_decl* v, max_level const& lvl); + void abstract_atoms(expr* fml, max_level& level, expr_ref_vector& defs); + void abstract_atoms(expr* fml, expr_ref_vector& defs); + expr_ref mk_abstract(expr* fml); + void pred2lit(expr_ref_vector& fmls); + expr_ref pred2asm(expr* fml); + void get_free_vars(expr* fml, app_ref_vector& vars); + expr_ref mk_assumption_literal(expr* a, model* mdl, max_level const& lvl, expr_ref_vector& defs); + void add_pred(app* p, app* lit); + app_ref fresh_bool(char const* name); + void display(std::ostream& out) const; + void display(std::ostream& out, expr_ref_vector const& asms) const; + void collect_statistics(statistics& st) const; + }; + + +} + +tactic * mk_qsat_tactic(ast_manager & m, params_ref const& p = params_ref()); + +tactic * mk_qe2_tactic(ast_manager & m, params_ref const& p = params_ref()); + +tactic * mk_qe_rec_tactic(ast_manager & m, params_ref const& p = params_ref()); + + +/* + ADD_TACTIC("qsat", "apply a QSAT solver.", "mk_qsat_tactic(m, p)") + + ADD_TACTIC("qe2", "apply a QSAT based quantifier elimination.", "mk_qe2_tactic(m, p)") + + ADD_TACTIC("qe_rec", "apply a QSAT based quantifier elimination recursively.", "mk_qe_rec_tactic(m, p)") + +*/ + +#endif diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 355c52f1e..6d8b8c1d9 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -257,8 +257,10 @@ public: if (m_preprocess) { m_preprocess->reset(); } + if (!m_bb_rewriter) { + m_bb_rewriter = alloc(bit_blaster_rewriter, m, m_params); + } params_ref simp2_p = m_params; - m_bb_rewriter = alloc(bit_blaster_rewriter, m, m_params); simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index b82f00a92..62c491b62 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -509,7 +509,7 @@ struct sat2goal::imp { // This information may be stored as a vector of pairs. // The mapping is only created during the model conversion. expr_ref_vector m_var2expr; - ref m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion + filter_model_converter_ref m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion sat_model_converter(ast_manager & m): m_var2expr(m) { diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 609f5d47b..22e9770e0 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -66,7 +66,7 @@ void display_usage() { #ifdef Z3GITHASH std::cout << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH); #endif - std::cout << "]. (C) Copyright 2006-2014 Microsoft Corp.\n"; + std::cout << "]. (C) Copyright 2006-2016 Microsoft Corp.\n"; std::cout << "Usage: z3 [options] [-file:]file\n"; std::cout << "\nInput format:\n"; std::cout << " -smt use parser for SMT input format.\n"; diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index df4b528fd..749e6037c 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -797,13 +797,7 @@ MK_SIMPLIFIER(apply_bit2int, bit2int& functor = m_bit2int, "bit2int", "propagate MK_SIMPLIFIER(cheap_quant_fourier_motzkin, elim_bounds_star functor(m_manager), "elim_bounds", "cheap-fourier-motzkin", true); -// MK_SIMPLIFIER(quant_elim, qe::expr_quant_elim_star1 &functor = m_quant_elim, -// "quantifiers", "quantifier elimination procedures", true); -bool asserted_formulas::quant_elim() { - throw default_exception("QUANT_ELIM option is deprecated, please consider using the 'qe' tactic."); - return false; -} MK_SIMPLIFIER(elim_bvs_from_quantifiers, bv_elim_star functor(m_manager), "bv_elim", "eliminate-bit-vectors-from-quantifiers", true); diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h index f4658f101..75fb86703 100644 --- a/src/smt/asserted_formulas.h +++ b/src/smt/asserted_formulas.h @@ -55,7 +55,6 @@ class asserted_formulas { maximise_bv_sharing m_bv_sharing; bool m_inconsistent; - // qe::expr_quant_elim_star1 m_quant_elim; struct scope { unsigned m_asserted_formulas_lim; @@ -84,7 +83,6 @@ class asserted_formulas { void eliminate_and(); void refine_inj_axiom(); bool cheap_quant_fourier_motzkin(); - bool quant_elim(); void apply_distribute_forall(); bool apply_bit2int(); void lift_ite(); diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index cb6b8c68d..cfc037a68 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -25,19 +25,17 @@ Revision History: #include"well_sorted.h" #include"used_symbols.h" #include"model_v2_pp.h" -#include"basic_simplifier_plugin.h" -proto_model::proto_model(ast_manager & m, simplifier & s, params_ref const & p): +proto_model::proto_model(ast_manager & m, params_ref const & p): model_core(m), - m_simplifier(s), m_afid(m.mk_family_id(symbol("array"))), - m_eval(*this) { + m_eval(*this), + m_rewrite(m) { register_factory(alloc(basic_factory, m)); m_user_sort_factory = alloc(user_sort_factory, m); register_factory(m_user_sort_factory); m_model_partial = model_params(p).partial(); - m_use_new_eval = model_params(p).new_eval(); } @@ -85,61 +83,6 @@ expr * proto_model::mk_some_interp_for(func_decl * d) { return r; } -// Auxiliary function for computing fi(args[0], ..., args[fi.get_arity() - 1]). -// The result is stored in result. -// Return true if succeeded, and false otherwise. -// It uses the simplifier s during the computation. -bool eval(func_interp & fi, simplifier & s, expr * const * args, expr_ref & result) { - bool actuals_are_values = true; - - if (fi.num_entries() != 0) { - for (unsigned i = 0; actuals_are_values && i < fi.get_arity(); i++) { - actuals_are_values = fi.m().is_value(args[i]); - } - } - - func_entry * entry = fi.get_entry(args); - if (entry != 0) { - result = entry->get_result(); - return true; - } - - TRACE("func_interp", tout << "failed to find entry for: "; - for(unsigned i = 0; i < fi.get_arity(); i++) - tout << mk_pp(args[i], fi.m()) << " "; - tout << "\nis partial: " << fi.is_partial() << "\n";); - - if (!fi.eval_else(args, result)) { - return false; - } - - if (actuals_are_values && fi.args_are_values()) { - // cheap case... we are done - return true; - } - - // build symbolic result... the actuals may be equal to the args of one of the entries. - basic_simplifier_plugin * bs = static_cast(s.get_plugin(fi.m().get_basic_family_id())); - for (unsigned k = 0; k < fi.num_entries(); k++) { - func_entry const * curr = fi.get_entry(k); - SASSERT(!curr->eq_args(fi.m(), fi.get_arity(), args)); - if (!actuals_are_values || !curr->args_are_values()) { - expr_ref_buffer eqs(fi.m()); - unsigned i = fi.get_arity(); - while (i > 0) { - --i; - expr_ref new_eq(fi.m()); - bs->mk_eq(curr->get_arg(i), args[i], new_eq); - eqs.push_back(new_eq); - } - SASSERT(eqs.size() == fi.get_arity()); - expr_ref new_cond(fi.m()); - bs->mk_and(eqs.size(), eqs.c_ptr(), new_cond); - bs->mk_ite(new_cond, curr->get_result(), result, result); - } - } - return true; -} bool proto_model::is_select_of_model_value(expr* e) const { return @@ -159,192 +102,16 @@ bool proto_model::is_select_of_model_value(expr* e) const { So, if model_completion == true, the evaluator never fails if it doesn't contain quantifiers. */ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { - if (m_use_new_eval) { - m_eval.set_model_completion(model_completion); - try { - m_eval(e, result); - return true; - } - catch (model_evaluator_exception & ex) { - (void)ex; - TRACE("model_evaluator", tout << ex.msg() << "\n";); - return false; - } + m_eval.set_model_completion(model_completion); + try { + m_eval(e, result); + return true; } - - bool is_ok = true; - SASSERT(is_well_sorted(m_manager, e)); - TRACE("model_eval", tout << mk_pp(e, m_manager) << "\n"; - tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n";); - - obj_map eval_cache; - expr_ref_vector trail(m_manager); - sbuffer, 128> todo; - ptr_buffer args; - expr * null = static_cast(0); - todo.push_back(std::make_pair(e, null)); - - - expr * a; - expr * expanded_a; - while (!todo.empty()) { - std::pair & p = todo.back(); - a = p.first; - expanded_a = p.second; - if (expanded_a != 0) { - expr * r = 0; - eval_cache.find(expanded_a, r); - SASSERT(r != 0); - todo.pop_back(); - eval_cache.insert(a, r); - TRACE("model_eval", - tout << "orig:\n" << mk_pp(a, m_manager) << "\n"; - tout << "after beta reduction:\n" << mk_pp(expanded_a, m_manager) << "\n"; - tout << "new:\n" << mk_pp(r, m_manager) << "\n";); - } - else { - switch(a->get_kind()) { - case AST_APP: { - app * t = to_app(a); - bool visited = true; - args.reset(); - unsigned num_args = t->get_num_args(); - for (unsigned i = 0; i < num_args; ++i) { - expr * arg = 0; - if (!eval_cache.find(t->get_arg(i), arg)) { - visited = false; - todo.push_back(std::make_pair(t->get_arg(i), null)); - } - else { - args.push_back(arg); - } - } - if (!visited) { - continue; - } - SASSERT(args.size() == t->get_num_args()); - expr_ref new_t(m_manager); - func_decl * f = t->get_decl(); - - if (!has_interpretation(f)) { - // the model does not assign an interpretation to f. - SASSERT(new_t.get() == 0); - if (f->get_family_id() == null_family_id) { - if (model_completion) { - // create an interpretation for f. - new_t = mk_some_interp_for(f); - } - else { - TRACE("model_eval", tout << f->get_name() << " is uninterpreted\n";); - is_ok = false; - } - } - if (new_t.get() == 0) { - // t is interpreted or model completion is disabled. - m_simplifier.mk_app(f, num_args, args.c_ptr(), new_t); - TRACE("model_eval", tout << mk_pp(t, m_manager) << " -> " << new_t << "\n";); - trail.push_back(new_t); - if (!is_app(new_t) || to_app(new_t)->get_decl() != f || is_select_of_model_value(new_t)) { - // if the result is not of the form (f ...), then assume we must simplify it. - expr * new_new_t = 0; - if (!eval_cache.find(new_t.get(), new_new_t)) { - todo.back().second = new_t; - todo.push_back(std::make_pair(new_t, null)); - continue; - } - else { - new_t = new_new_t; - } - } - } - } - else { - // the model has an interpretaion for f. - if (num_args == 0) { - // t is a constant - new_t = get_const_interp(f); - } - else { - // t is a function application - SASSERT(new_t.get() == 0); - // try to use function graph first - func_interp * fi = get_func_interp(f); - SASSERT(fi->get_arity() == num_args); - expr_ref r1(m_manager); - // fi may be partial... - if (!::eval(*fi, m_simplifier, args.c_ptr(), r1)) { - SASSERT(fi->is_partial()); // fi->eval only fails when fi is partial. - if (model_completion) { - expr * r = get_some_value(f->get_range()); - fi->set_else(r); - SASSERT(!fi->is_partial()); - new_t = r; - } - else { - // f is an uninterpreted function, there is no need to use m_simplifier.mk_app - new_t = m_manager.mk_app(f, num_args, args.c_ptr()); - trail.push_back(new_t); - TRACE("model_eval", tout << f->get_name() << " is uninterpreted\n";); - is_ok = false; - } - } - else { - SASSERT(r1); - trail.push_back(r1); - TRACE("model_eval", tout << mk_pp(a, m_manager) << "\nevaluates to: " << r1 << "\n";); - expr * r2 = 0; - if (!eval_cache.find(r1.get(), r2)) { - todo.back().second = r1; - todo.push_back(std::make_pair(r1, null)); - continue; - } - else { - new_t = r2; - } - } - } - } - TRACE("model_eval", - tout << "orig:\n" << mk_pp(t, m_manager) << "\n"; - tout << "new:\n" << mk_pp(new_t, m_manager) << "\n";); - todo.pop_back(); - SASSERT(new_t.get() != 0); - eval_cache.insert(t, new_t); - break; - } - case AST_VAR: - SASSERT(a != 0); - eval_cache.insert(a, a); - todo.pop_back(); - break; - case AST_QUANTIFIER: - TRACE("model_eval", tout << "found quantifier\n" << mk_pp(a, m_manager) << "\n";); - is_ok = false; // evaluator does not handle quantifiers. - SASSERT(a != 0); - eval_cache.insert(a, a); - todo.pop_back(); - break; - default: - UNREACHABLE(); - break; - } - } + catch (model_evaluator_exception & ex) { + (void)ex; + TRACE("model_evaluator", tout << ex.msg() << "\n";); + return false; } - - if (!eval_cache.find(e, a)) { - TRACE("model_eval", tout << "FAILED e: " << mk_bounded_pp(e, m_manager) << "\n";); - UNREACHABLE(); - } - - result = a; - TRACE("model_eval", - ast_ll_pp(tout << "original: ", m_manager, e); - ast_ll_pp(tout << "evaluated: ", m_manager, a); - ast_ll_pp(tout << "reduced: ", m_manager, result.get()); - tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n"; - ); - SASSERT(is_well_sorted(m_manager, result.get())); - return is_ok; } /** @@ -400,7 +167,7 @@ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_au if (m_aux_decls.contains(f)) found_aux_fs.insert(f); expr_ref new_t(m_manager); - m_simplifier.mk_app(f, num_args, args.c_ptr(), new_t); + new_t = m_rewrite.mk_app(f, num_args, args.c_ptr()); if (t != new_t.get()) trail.push_back(new_t); todo.pop_back(); diff --git a/src/smt/proto_model/proto_model.h b/src/smt/proto_model/proto_model.h index 2c27ac39a..78dc457f5 100644 --- a/src/smt/proto_model/proto_model.h +++ b/src/smt/proto_model/proto_model.h @@ -32,23 +32,22 @@ Revision History: #include"model_evaluator.h" #include"value_factory.h" #include"plugin_manager.h" -#include"simplifier.h" #include"arith_decl_plugin.h" #include"func_decl_dependencies.h" #include"model.h" #include"params.h" +#include"th_rewriter.h" class proto_model : public model_core { plugin_manager m_factories; user_sort_factory * m_user_sort_factory; - simplifier & m_simplifier; family_id m_afid; //!< array family id: hack for displaying models in V1.x style func_decl_set m_aux_decls; ptr_vector m_tmp; model_evaluator m_eval; + th_rewriter m_rewrite; bool m_model_partial; - bool m_use_new_eval; expr * mk_some_interp_for(func_decl * d); @@ -62,7 +61,7 @@ class proto_model : public model_core { bool is_select_of_model_value(expr* e) const; public: - proto_model(ast_manager & m, simplifier & s, params_ref const & p = params_ref()); + proto_model(ast_manager & m, params_ref const & p = params_ref()); virtual ~proto_model() {} void register_factory(value_factory * f) { m_factories.register_plugin(f); } diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index be06e5ea5..bf559a775 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -122,6 +122,10 @@ namespace smt { */ lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0); + lbool check(expr_ref_vector const& asms) { return check(asms.size(), asms.c_ptr()); } + + lbool check(app_ref_vector const& asms) { return check(asms.size(), (expr* const*)asms.c_ptr()); } + /** \brief Return the model associated with the last check command. */ diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 42fb8f1b5..21a310a42 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -49,7 +49,7 @@ namespace smt { void model_generator::init_model() { SASSERT(!m_model); // PARAM-TODO smt_params ---> params_ref - m_model = alloc(proto_model, m_manager, m_context->get_simplifier()); // , m_context->get_fparams()); + m_model = alloc(proto_model, m_manager); // , m_context->get_fparams()); ptr_vector::const_iterator it = m_context->begin_theories(); ptr_vector::const_iterator end = m_context->end_theories(); for (; it != end; ++it) { diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index 96a7ac84c..cee36535f 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -315,10 +315,7 @@ namespace smt { return m_var2enode_lim[m_var2enode_lim.size() - num_scopes]; } - virtual void display(std::ostream & out) const { - out << "Theory " << static_cast(get_id()) << typeid(*this).name() << " does not have a display method\n"; - display_var2enode(out); - } + virtual void display(std::ostream & out) const = 0; virtual void display_var2enode(std::ostream & out) const; diff --git a/src/smt/theory_dummy.h b/src/smt/theory_dummy.h index c9b0cd6c6..fc25fc0b7 100644 --- a/src/smt/theory_dummy.h +++ b/src/smt/theory_dummy.h @@ -43,6 +43,7 @@ namespace smt { virtual bool build_models() const { return false; } + virtual void display(std::ostream& out) const {} public: theory_dummy(family_id fid, char const * name); diff --git a/src/smt/theory_seq_empty.h b/src/smt/theory_seq_empty.h index 69f8b54b7..be17fe5ac 100644 --- a/src/smt/theory_seq_empty.h +++ b/src/smt/theory_seq_empty.h @@ -122,6 +122,7 @@ namespace smt { virtual void new_diseq_eh(theory_var, theory_var) {} virtual theory* mk_fresh(context* new_ctx) { return alloc(theory_seq_empty, new_ctx->get_manager()); } virtual char const * get_name() const { return "seq-empty"; } + virtual void display(std::ostream& out) const {} public: theory_seq_empty(ast_manager& m):theory(m.mk_family_id("seq")), m_used(false) {} virtual void init_model(model_generator & mg) { diff --git a/src/smt/theory_wmaxsat.h b/src/smt/theory_wmaxsat.h index 1fe4dbcee..b0c556c0e 100644 --- a/src/smt/theory_wmaxsat.h +++ b/src/smt/theory_wmaxsat.h @@ -94,6 +94,7 @@ namespace smt { virtual bool internalize_term(app * term) { return false; } virtual void new_eq_eh(theory_var v1, theory_var v2) { } virtual void new_diseq_eh(theory_var v1, theory_var v2) { } + virtual void display(std::ostream& out) const {} virtual void collect_statistics(::statistics & st) const { st.update("wmaxsat num blocks", m_stats.m_num_blocks); diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index 858885d9d..e19d7a1e3 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -194,7 +194,7 @@ public: m_use_solver1_results = false; if (get_num_assumptions() != 0 || - num_assumptions > 0 || // assumptions were provided + num_assumptions > 0 || // assumptions were provided m_ignore_solver1) { // must use incremental solver switch_inc_mode(); diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index 01c45ee44..704038ffd 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -23,12 +23,11 @@ Notes: #include"ast_smt2_pp.h" solver_na2as::solver_na2as(ast_manager & m): - m_manager(m) { + m(m), + m_assumptions(m) { } -solver_na2as::~solver_na2as() { - restore_assumptions(0); -} +solver_na2as::~solver_na2as() {} void solver_na2as::assert_expr(expr * t, expr * a) { if (a == 0) { @@ -36,20 +35,19 @@ void solver_na2as::assert_expr(expr * t, expr * a) { } else { SASSERT(is_uninterp_const(a)); - SASSERT(m_manager.is_bool(a)); - TRACE("solver_na2as", tout << "asserting\n" << mk_ismt2_pp(t, m_manager) << "\n" << mk_ismt2_pp(a, m_manager) << "\n";); - m_manager.inc_ref(a); + SASSERT(m.is_bool(a)); + TRACE("solver_na2as", tout << "asserting\n" << mk_ismt2_pp(t, m) << "\n" << mk_ismt2_pp(a, m) << "\n";); m_assumptions.push_back(a); - expr_ref new_t(m_manager); - new_t = m_manager.mk_implies(a, t); + expr_ref new_t(m); + new_t = m.mk_implies(a, t); assert_expr(new_t); } } struct append_assumptions { - ptr_vector & m_assumptions; + expr_ref_vector & m_assumptions; unsigned m_old_sz; - append_assumptions(ptr_vector & _m_assumptions, + append_assumptions(expr_ref_vector & _m_assumptions, unsigned num_assumptions, expr * const * assumptions): m_assumptions(_m_assumptions) { @@ -82,10 +80,6 @@ void solver_na2as::pop(unsigned n) { } void solver_na2as::restore_assumptions(unsigned old_sz) { - // SASSERT(old_sz == 0); - for (unsigned i = old_sz; i < m_assumptions.size(); i++) { - m_manager.dec_ref(m_assumptions[i]); - } m_assumptions.shrink(old_sz); } diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index 644d217ee..019468fd7 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -25,8 +25,8 @@ Notes: #include"solver.h" class solver_na2as : public solver { - ast_manager & m_manager; - ptr_vector m_assumptions; + ast_manager & m; + expr_ref_vector m_assumptions; unsigned_vector m_scopes; void restore_assumptions(unsigned old_sz); public: diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index 085cb4de3..1399d6549 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -60,7 +60,7 @@ class nla2bv_tactic : public tactic { expr_ref_vector m_trail; unsigned m_num_bits; unsigned m_default_bv_size; - ref m_fmc; + filter_model_converter_ref m_fmc; public: imp(ast_manager & m, params_ref const& p): diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index 25127cb90..c010ed0af 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -60,7 +60,7 @@ struct bv_size_reduction_tactic::imp { obj_map m_unsigned_lowers; obj_map m_unsigned_uppers; ref m_mc; - ref m_fmc; + filter_model_converter_ref m_fmc; scoped_ptr m_replacer; bool m_produce_models; diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index d266548d5..90beabf24 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -26,7 +26,7 @@ Revision History: class propagate_values_tactic : public tactic { struct imp { - ast_manager & m_manager; + ast_manager & m; th_rewriter m_r; scoped_ptr m_subst; goal * m_goal; @@ -36,13 +36,15 @@ class propagate_values_tactic : public tactic { bool m_modified; imp(ast_manager & m, params_ref const & p): - m_manager(m), + m(m), m_r(m, p), m_goal(0), m_occs(m, true /* track atoms */) { updt_params_core(p); } + ~imp() { } + void updt_params_core(params_ref const & p) { m_max_rounds = p.get_uint("max_rounds", 4); } @@ -51,32 +53,28 @@ class propagate_values_tactic : public tactic { m_r.updt_params(p); updt_params_core(p); } - - ast_manager & m() const { return m_manager; } bool is_shared(expr * t) { return m_occs.is_shared(t); } - bool is_shared_neg(expr * t, expr * & atom) { - if (!m().is_not(t)) + bool is_shared_neg(expr * t, expr * & atom) { + if (!m.is_not(t, atom)) return false; - atom = to_app(t)->get_arg(0); return is_shared(atom); } bool is_shared_eq(expr * t, expr * & lhs, expr * & value) { - if (!m().is_eq(t)) + expr* arg1, *arg2; + if (!m.is_eq(t, arg1, arg2)) return false; - expr * arg1 = to_app(t)->get_arg(0); - expr * arg2 = to_app(t)->get_arg(1); - if (m().is_value(arg1) && is_shared(arg2)) { + if (m.is_value(arg1) && is_shared(arg2)) { lhs = arg2; value = arg1; return true; } - if (m().is_value(arg2) && is_shared(arg1)) { + if (m.is_value(arg2) && is_shared(arg1)) { lhs = arg1; value = arg2; return true; @@ -87,15 +85,15 @@ class propagate_values_tactic : public tactic { void push_result(expr * new_curr, proof * new_pr) { if (m_goal->proofs_enabled()) { proof * pr = m_goal->pr(m_idx); - new_pr = m().mk_modus_ponens(pr, new_pr); + new_pr = m.mk_modus_ponens(pr, new_pr); } - expr_dependency_ref new_d(m()); + expr_dependency_ref new_d(m); if (m_goal->unsat_core_enabled()) { new_d = m_goal->dep(m_idx); expr_dependency * used_d = m_r.get_used_dependencies(); if (used_d != 0) { - new_d = m().mk_join(new_d, used_d); + new_d = m.mk_join(new_d, used_d); m_r.reset_used_dependencies(); } } @@ -103,34 +101,34 @@ class propagate_values_tactic : public tactic { m_goal->update(m_idx, new_curr, new_pr, new_d); if (is_shared(new_curr)) { - m_subst->insert(new_curr, m().mk_true(), m().mk_iff_true(new_pr), new_d); + m_subst->insert(new_curr, m.mk_true(), m.mk_iff_true(new_pr), new_d); } expr * atom; if (is_shared_neg(new_curr, atom)) { - m_subst->insert(atom, m().mk_false(), m().mk_iff_false(new_pr), new_d); + m_subst->insert(atom, m.mk_false(), m.mk_iff_false(new_pr), new_d); } expr * lhs, * value; if (is_shared_eq(new_curr, lhs, value)) { - TRACE("shallow_context_simplifier_bug", tout << "found eq:\n" << mk_ismt2_pp(new_curr, m()) << "\n";); + TRACE("shallow_context_simplifier_bug", tout << "found eq:\n" << mk_ismt2_pp(new_curr, m) << "\n";); m_subst->insert(lhs, value, new_pr, new_d); } } void process_current() { expr * curr = m_goal->form(m_idx); - expr_ref new_curr(m()); - proof_ref new_pr(m()); + expr_ref new_curr(m); + proof_ref new_pr(m); if (!m_subst->empty()) { m_r(curr, new_curr, new_pr); } else { new_curr = curr; - if (m().proofs_enabled()) - new_pr = m().mk_reflexivity(curr); + if (m.proofs_enabled()) + new_pr = m.mk_reflexivity(curr); } - TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m()) << "\n---->\n" << mk_ismt2_pp(new_curr, m()) << "\n";); + TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n";); push_result(new_curr, new_pr); if (new_curr != curr) @@ -148,25 +146,26 @@ class propagate_values_tactic : public tactic { m_goal = g.get(); bool forward = true; - expr_ref new_curr(m()); - proof_ref new_pr(m()); + expr_ref new_curr(m); + proof_ref new_pr(m); unsigned size = m_goal->size(); m_idx = 0; m_modified = false; unsigned round = 0; + if (m_goal->inconsistent()) goto end; if (m_max_rounds == 0) goto end; - m_subst = alloc(expr_substitution, m(), g->unsat_core_enabled(), g->proofs_enabled()); + m_subst = alloc(expr_substitution, m, g->unsat_core_enabled(), g->proofs_enabled()); m_r.set_substitution(m_subst.get()); m_occs(*m_goal); while (true) { - TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display(tout);); + TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display_with_dependencies(tout);); if (forward) { for (; m_idx < size; m_idx++) { process_current(); @@ -255,15 +254,14 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m(); - imp * d = alloc(imp, m, m_params); - std::swap(d, m_imp); - dealloc(d); + ast_manager & m = m_imp->m; + dealloc(m_imp); + m_imp = alloc(imp, m, m_params); } }; tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(propagate_values_tactic, m, p)); + return alloc(propagate_values_tactic, m, p); } diff --git a/src/tactic/smtlogics/nra_tactic.cpp b/src/tactic/smtlogics/nra_tactic.cpp index 845b6bfec..75fbcc3f4 100644 --- a/src/tactic/smtlogics/nra_tactic.cpp +++ b/src/tactic/smtlogics/nra_tactic.cpp @@ -22,7 +22,9 @@ Notes: #include"smt_tactic.h" #include"nnf_tactic.h" #include"qe_tactic.h" +#include"nlqsat.h" #include"qfnra_nlsat_tactic.h" +#include"qe_lite.h" #include"probe_arith.h" tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) { @@ -36,12 +38,19 @@ tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) { return and_then(mk_simplify_tactic(m, p), mk_nnf_tactic(m, p), mk_propagate_values_tactic(m, p), + //mk_qe_lite_tactic(m), mk_qe_tactic(m, p), cond(mk_is_qfnra_probe(), or_else(try_for(mk_qfnra_nlsat_tactic(m, p), 5000), try_for(mk_qfnra_nlsat_tactic(m, p1), 10000), mk_qfnra_nlsat_tactic(m, p2)), - mk_smt_tactic(p))); +#if 0 + or_else(mk_nlqsat_tactic(m, p), + mk_smt_tactic(p)) +#else + mk_smt_tactic(p) +#endif + )); } diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index 8714b055f..8760d255e 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -23,6 +23,9 @@ Revision History: #include"elim_uncnstr_tactic.h" #include"qe_tactic.h" #include"qe_sat_tactic.h" +#include"qe_lite.h" +#include"qsat.h" +#include"nlqsat.h" #include"ctx_simplify_tactic.h" #include"smt_tactic.h" @@ -37,10 +40,12 @@ static tactic * mk_quant_preprocessor(ast_manager & m, bool disable_gaussian = f ctx_simp_p.set_uint("max_steps", 5000000); tactic * solve_eqs; - if (disable_gaussian) + if (disable_gaussian) { solve_eqs = mk_skip_tactic(); - else + } + else { solve_eqs = when(mk_not(mk_has_pattern_probe()), mk_solve_eqs_tactic(m)); + } // remark: investigate if gaussian elimination is useful when patterns are not provided. return and_then(mk_simplify_tactic(m), @@ -49,6 +54,7 @@ static tactic * mk_quant_preprocessor(ast_manager & m, bool disable_gaussian = f using_params(mk_simplify_tactic(m), pull_ite_p), solve_eqs, mk_elim_uncnstr_tactic(m), + mk_qe_lite_tactic(m), mk_simplify_tactic(m)); } @@ -98,13 +104,18 @@ tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p) { } tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { +#if 0 tactic * st = and_then(mk_quant_preprocessor(m), or_else(try_for(mk_smt_tactic(), 100), try_for(qe::mk_sat_tactic(m), 1000), try_for(mk_smt_tactic(), 1000), and_then(mk_qe_tactic(m), mk_smt_tactic()) )); - +#else + tactic * st = and_then(mk_quant_preprocessor(m), + or_else(mk_qsat_tactic(m, p), + and_then(mk_qe_tactic(m), mk_smt_tactic()))); +#endif st->updt_params(p); return st; } @@ -116,3 +127,4 @@ tactic * mk_lia_tactic(ast_manager & m, params_ref const & p) { tactic * mk_lira_tactic(ast_manager & m, params_ref const & p) { return mk_lra_tactic(m, p); } + diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index d79bd2ed1..0d276ba72 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -131,19 +131,19 @@ public: SASSERT(r1_size > 0); if (r1_size == 1) { if (r1[0]->is_decided()) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; + result.push_back(r1[0]); + if (models_enabled) mc = mc1; SASSERT(!pc); SASSERT(!core); return; } - goal_ref r1_0 = r1[0]; - m_t2->operator()(r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc1.get(), mc.get()); - if (proofs_enabled) pc = concat(pc1.get(), pc.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); - } - else { - if (cores_enabled) core = core1; + goal_ref r1_0 = r1[0]; + m_t2->operator()(r1_0, result, mc, pc, core); + if (models_enabled) mc = concat(mc1.get(), mc.get()); + if (proofs_enabled) pc = concat(pc1.get(), pc.get()); + if (cores_enabled) core = m.mk_join(core1.get(), core); + } + else { + if (cores_enabled) core = core1; proof_converter_ref_buffer pc_buffer; model_converter_ref_buffer mc_buffer; sbuffer sz_buffer; @@ -179,16 +179,16 @@ public: SASSERT(!core2); if (models_enabled) mc_buffer.push_back(0); if (proofs_enabled) pc_buffer.push_back(proof2proof_converter(m, r2[0]->pr(0))); - if (models_enabled || proofs_enabled) sz_buffer.push_back(0); + if (models_enabled || proofs_enabled) sz_buffer.push_back(0); if (cores_enabled) core = m.mk_join(core.get(), r2[0]->dep(0)); } } else { - result.append(r2.size(), r2.c_ptr()); - if (models_enabled) mc_buffer.push_back(mc2.get()); - if (proofs_enabled) pc_buffer.push_back(pc2.get()); - if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size()); - if (cores_enabled) core = m.mk_join(core.get(), core2.get()); + result.append(r2.size(), r2.c_ptr()); + if (models_enabled) mc_buffer.push_back(mc2.get()); + if (proofs_enabled) pc_buffer.push_back(pc2.get()); + if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size()); + if (cores_enabled) core = m.mk_join(core.get(), core2.get()); } } @@ -651,12 +651,12 @@ public: } goal_ref r1_0 = r1[0]; m_t2->operator()(r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc1.get(), mc.get()); - if (proofs_enabled) pc = concat(pc1.get(), pc.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); + if (models_enabled) mc = concat(mc1.get(), mc.get()); + if (proofs_enabled) pc = concat(pc1.get(), pc.get()); + if (cores_enabled) core = m.mk_join(core1.get(), core); } else { - if (cores_enabled) core = core1; + if (cores_enabled) core = core1; scoped_ptr_vector managers; tactic_ref_vector ts2; @@ -670,8 +670,8 @@ public: ts2.push_back(m_t2->translate(*new_m)); } - proof_converter_ref_buffer pc_buffer; - model_converter_ref_buffer mc_buffer; + proof_converter_ref_buffer pc_buffer; + model_converter_ref_buffer mc_buffer; scoped_ptr_vector core_buffer; scoped_ptr_vector goals_vect; @@ -687,7 +687,7 @@ public: std::string ex_msg; #pragma omp parallel for - for (int i = 0; i < static_cast(r1_size); i++) { + for (int i = 0; i < static_cast(r1_size); i++) { ast_manager & new_m = *(managers[i]); goal_ref new_g = g_copies[i]; @@ -772,7 +772,7 @@ public: md = alloc(model, m); apply(mc2, md, 0); apply(mc1, md, i); - mc = model2model_converter(md.get()); + mc = model2model_converter(md.get()); } SASSERT(!pc); SASSERT(!core); } @@ -969,9 +969,9 @@ class repeat_tactical : public unary_tactical { m_t->operator()(in, r1, mc1, pc1, core1); if (is_equal(orig_in, *(in.get()))) { result.push_back(r1[0]); - if (models_enabled) mc = mc1; - if (proofs_enabled) pc = pc1; - if (cores_enabled) core = core1; + if (models_enabled) mc = mc1; + if (proofs_enabled) pc = pc1; + if (cores_enabled) core = core1; return; } } @@ -980,18 +980,18 @@ class repeat_tactical : public unary_tactical { if (r1_size == 1) { if (r1[0]->is_decided()) { result.push_back(r1[0]); - if (models_enabled) mc = mc1; + if (models_enabled) mc = mc1; SASSERT(!pc); SASSERT(!core); return; } goal_ref r1_0 = r1[0]; - operator()(depth+1, r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc.get(), mc1.get()); - if (proofs_enabled) pc = concat(pc.get(), pc1.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); + operator()(depth+1, r1_0, result, mc, pc, core); + if (models_enabled) mc = concat(mc.get(), mc1.get()); + if (proofs_enabled) pc = concat(pc.get(), pc1.get()); + if (cores_enabled) core = m.mk_join(core1.get(), core); } - else { - if (cores_enabled) core = core1; + else { + if (cores_enabled) core = core1; proof_converter_ref_buffer pc_buffer; model_converter_ref_buffer mc_buffer; sbuffer sz_buffer; diff --git a/src/test/nlsat.cpp b/src/test/nlsat.cpp index 7777a6c42..27a0aa1da 100644 --- a/src/test/nlsat.cpp +++ b/src/test/nlsat.cpp @@ -21,6 +21,8 @@ Notes: #include"nlsat_evaluator.h" #include"nlsat_solver.h" #include"util.h" +#include"nlsat_explain.h" +#include"polynomial_cache.h" #include"rlimit.h" nlsat::interval_set_ref tst_interval(nlsat::interval_set_ref const & s1, @@ -29,7 +31,6 @@ nlsat::interval_set_ref tst_interval(nlsat::interval_set_ref const & s1, bool check_num_intervals = true) { nlsat::interval_set_manager & ism = s1.m(); nlsat::interval_set_ref r(ism); - std::cout << "------------------\n"; std::cout << "s1: " << s1 << "\n"; std::cout << "s2: " << s2 << "\n"; r = ism.mk_union(s1, s2); @@ -277,10 +278,12 @@ static void tst5() { nlsat::assignment as(am); small_object_allocator allocator; nlsat::interval_set_manager ism(am, allocator); - nlsat::evaluator ev(as, pm, allocator); + nlsat::evaluator ev(s, as, pm, allocator); nlsat::var x0, x1; x0 = pm.mk_var(); x1 = pm.mk_var(); + nlsat::interval_set_ref i(ism); + polynomial_ref p(pm); polynomial_ref _x0(pm), _x1(pm); _x0 = pm.mk_polynomial(x0); @@ -291,7 +294,6 @@ static void tst5() { nlsat::bool_var b = s.mk_ineq_atom(nlsat::atom::GT, 1, _p, is_even); nlsat::atom * a = s.bool_var2atom(b); SASSERT(a != 0); - nlsat::interval_set_ref i(ism); scoped_anum zero(am); am.set(zero, 0); as.set(0, zero); @@ -302,8 +304,414 @@ static void tst5() { std::cout << "2) " << i << "\n"; } + + +static void project(nlsat::solver& s, nlsat::explain& ex, nlsat::var x, unsigned num, nlsat::literal const* lits) { + std::cout << "Project "; + s.display(std::cout, num, lits); + nlsat::scoped_literal_vector result(s); + ex.project(x, num, lits, result); + s.display(std::cout << "\n==>\n", result.size(), result.c_ptr()); + std::cout << "\n"; +} + +static nlsat::literal mk_gt(nlsat::solver& s, nlsat::poly* p) { + nlsat::poly * _p[1] = { p }; + bool is_even[1] = { false }; + return s.mk_ineq_literal(nlsat::atom::GT, 1, _p, is_even); +} + +static nlsat::literal mk_eq(nlsat::solver& s, nlsat::poly* p) { + nlsat::poly * _p[1] = { p }; + bool is_even[1] = { false }; + return s.mk_ineq_literal(nlsat::atom::EQ, 1, _p, is_even); +} + +static void tst6() { + params_ref ps; + reslimit rlim; + nlsat::solver s(rlim, ps); + anum_manager & am = s.am(); + nlsat::pmanager & pm = s.pm(); + nlsat::assignment as(am); + nlsat::explain& ex = s.get_explain(); + nlsat::var x0, x1, x2, a, b, c, d; + a = s.mk_var(false); + b = s.mk_var(false); + c = s.mk_var(false); + d = s.mk_var(false); + x0 = s.mk_var(false); + x1 = s.mk_var(false); + x2 = s.mk_var(false); + + polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm); + polynomial_ref _x0(pm), _x1(pm), _x2(pm); + polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm); + _x0 = pm.mk_polynomial(x0); + _x1 = pm.mk_polynomial(x1); + _x2 = pm.mk_polynomial(x2); + _a = pm.mk_polynomial(a); + _b = pm.mk_polynomial(b); + _c = pm.mk_polynomial(c); + _d = pm.mk_polynomial(d); + + p1 = (_a*(_x0^2)) + _x2 + 2; + p2 = (_b*_x1) - (2*_x2) - _x0 + 8; + nlsat::scoped_literal_vector lits(s); + lits.push_back(mk_gt(s, p1)); + lits.push_back(mk_gt(s, p2)); + lits.push_back(mk_gt(s, (_c*_x0) + _x2 + 1)); + lits.push_back(mk_gt(s, (_d*_x0) - _x1 + 5*_x2)); + + scoped_anum zero(am), one(am), two(am); + am.set(zero, 0); + am.set(one, 1); + am.set(two, 2); + as.set(0, one); + as.set(1, one); + as.set(2, two); + as.set(3, two); + as.set(4, two); + as.set(5, one); + as.set(6, one); + s.set_rvalues(as); + + + project(s, ex, x0, 2, lits.c_ptr()); + project(s, ex, x1, 3, lits.c_ptr()); + project(s, ex, x2, 3, lits.c_ptr()); + project(s, ex, x2, 2, lits.c_ptr()); + project(s, ex, x2, 4, lits.c_ptr()); + project(s, ex, x2, 3, lits.c_ptr()+1); + + +} + +static void tst7() { + params_ref ps; + reslimit rlim; + nlsat::solver s(rlim, ps); + anum_manager & am = s.am(); + nlsat::pmanager & pm = s.pm(); + nlsat::var x0, x1, x2, a, b, c, d; + a = s.mk_var(false); + b = s.mk_var(false); + c = s.mk_var(false); + d = s.mk_var(false); + x0 = s.mk_var(false); + x1 = s.mk_var(false); + x2 = s.mk_var(false); + polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm); + polynomial_ref _x0(pm), _x1(pm), _x2(pm); + polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm); + _x0 = pm.mk_polynomial(x0); + _x1 = pm.mk_polynomial(x1); + _x2 = pm.mk_polynomial(x2); + _a = pm.mk_polynomial(a); + _b = pm.mk_polynomial(b); + _c = pm.mk_polynomial(c); + _d = pm.mk_polynomial(d); + + p1 = _x0 + _x1; + p2 = _x2 - _x0; + p3 = (-1*_x0) - _x1; + + nlsat::scoped_literal_vector lits(s); + lits.push_back(mk_gt(s, p1)); + lits.push_back(mk_gt(s, p2)); + lits.push_back(mk_gt(s, p3)); + + nlsat::literal_vector litsv(lits.size(), lits.c_ptr()); + lbool res = s.check(litsv); + SASSERT(res == l_false); + for (unsigned i = 0; i < litsv.size(); ++i) { + s.display(std::cout, litsv[i]); + std::cout << " "; + } + std::cout << "\n"; + + litsv.reset(); + litsv.append(2, lits.c_ptr()); + res = s.check(litsv); + SASSERT(res == l_true); + s.display(std::cout); + s.am().display(std::cout, s.value(x0)); std::cout << "\n"; + s.am().display(std::cout, s.value(x1)); std::cout << "\n"; + s.am().display(std::cout, s.value(x2)); std::cout << "\n"; + +} + +static void tst8() { + params_ref ps; + reslimit rlim; + nlsat::solver s(rlim, ps); + anum_manager & am = s.am(); + nlsat::pmanager & pm = s.pm(); + nlsat::assignment as(am); + nlsat::explain& ex = s.get_explain(); + nlsat::var x0, x1, x2, a, b, c, d; + a = s.mk_var(false); + b = s.mk_var(false); + c = s.mk_var(false); + d = s.mk_var(false); + x0 = s.mk_var(false); + x1 = s.mk_var(false); + x2 = s.mk_var(false); + + polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm); + polynomial_ref _x0(pm), _x1(pm), _x2(pm); + polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm); + _x0 = pm.mk_polynomial(x0); + _x1 = pm.mk_polynomial(x1); + _x2 = pm.mk_polynomial(x2); + _a = pm.mk_polynomial(a); + _b = pm.mk_polynomial(b); + _c = pm.mk_polynomial(c); + _d = pm.mk_polynomial(d); + + scoped_anum zero(am), one(am), two(am), six(am); + am.set(zero, 0); + am.set(one, 1); + am.set(two, 2); + am.set(six, 6); + as.set(0, two); // a + as.set(1, one); // b + as.set(2, six); // c + as.set(3, zero); // d + as.set(4, zero); // x0 + as.set(5, zero); // x1 + as.set(6, two); // x2 + s.set_rvalues(as); + + nlsat::scoped_literal_vector lits(s); + lits.push_back(mk_eq(s, (_a*_x2*_x2) - (_b*_x2) - _c)); + project(s, ex, x2, 1, lits.c_ptr()); +} + + + +static void tst9() { + params_ref ps; + reslimit rlim; + nlsat::solver s(rlim, ps); + anum_manager & am = s.am(); + nlsat::pmanager & pm = s.pm(); + nlsat::assignment as(am); + nlsat::explain& ex = s.get_explain(); + int num_lo = 4; + int num_hi = 5; + svector los, his; + for (int i = 0; i < num_lo; ++i) { + los.push_back(s.mk_var(false)); + scoped_anum num(am); + am.set(num, - i - 1); + as.set(i, num); + } + for (int i = 0; i < num_hi; ++i) { + his.push_back(s.mk_var(false)); + scoped_anum num(am); + am.set(num, i + 1); + as.set(num_lo + i, num); + } + nlsat::var _z = s.mk_var(false); + nlsat::var _x = s.mk_var(false); + polynomial_ref x(pm), z(pm); + x = pm.mk_polynomial(_x); + scoped_anum val(am); + am.set(val, 0); + as.set(num_lo + num_hi, val); + as.set(num_lo + num_hi + 1, val); + s.set_rvalues(as); + nlsat::scoped_literal_vector lits(s); + + for (int i = 0; i < num_lo; ++i) { + polynomial_ref y(pm); + y = pm.mk_polynomial(los[i]); + lits.push_back(mk_gt(s, x - y)); + } + for (int i = 0; i < num_hi; ++i) { + polynomial_ref y(pm); + y = pm.mk_polynomial(his[i]); + lits.push_back(mk_gt(s, y - x)); + } + z = pm.mk_polynomial(_z); + lits.push_back(mk_eq(s, x - z)); + +#define TEST_ON_OFF() \ + std::cout << "Off "; \ + ex.set_signed_project(false); \ + project(s, ex, _x, lits.size()-1, lits.c_ptr()); \ + std::cout << "On "; \ + ex.set_signed_project(true); \ + project(s, ex, _x, lits.size()-1, lits.c_ptr()); \ + std::cout << "Off "; \ + ex.set_signed_project(false); \ + project(s, ex, _x, lits.size(), lits.c_ptr()); \ + std::cout << "On "; \ + ex.set_signed_project(true); \ + project(s, ex, _x, lits.size(), lits.c_ptr()) \ + + TEST_ON_OFF(); + + lits.reset(); + polynomial_ref u(pm); + u = pm.mk_polynomial(his[1]); + for (int i = 0; i < num_lo; ++i) { + polynomial_ref y(pm); + y = pm.mk_polynomial(los[i]); + lits.push_back(mk_gt(s, u*x - y)); + } + for (int i = 0; i < num_hi; ++i) { + polynomial_ref y(pm); + y = pm.mk_polynomial(his[i]); + lits.push_back(mk_gt(s, y - u*x)); + } + z = pm.mk_polynomial(_z); + lits.push_back(mk_eq(s, u*x - z)); + + TEST_ON_OFF(); + + lits.reset(); + u = pm.mk_polynomial(los[1]); + for (int i = 0; i < num_lo; ++i) { + polynomial_ref y(pm); + y = pm.mk_polynomial(los[i]); + lits.push_back(mk_gt(s, u*x - y)); + } + for (int i = 0; i < num_hi; ++i) { + polynomial_ref y(pm); + y = pm.mk_polynomial(his[i]); + lits.push_back(mk_gt(s, y - u*x)); + } + z = pm.mk_polynomial(_z); + lits.push_back(mk_eq(s, x - z)); + + TEST_ON_OFF(); +} + + +#if 0 + + +#endif + +static void test_root_literal(nlsat::solver& s, nlsat::explain& ex, nlsat::var x, nlsat::atom::kind k, unsigned i, nlsat::poly* p) { + nlsat::scoped_literal_vector result(s); + ex.test_root_literal(k, x, 1, p, result); + nlsat::bool_var b = s.mk_root_atom(k, x, i, p); + s.display(std::cout, nlsat::literal(b, false)); + s.display(std::cout << " ==> ", result.size(), result.c_ptr()); + std::cout << "\n"; +} + +static bool satisfies_root(nlsat::solver& s, nlsat::atom::kind k, nlsat::poly* p) { + nlsat::pmanager & pm = s.pm(); + anum_manager & am = s.am(); + nlsat::assignment as(am); + s.get_rvalues(as); + polynomial_ref pr(p, pm); + switch (k) { + case nlsat::atom::ROOT_EQ: return am.eval_sign_at(pr, as) == 0; + case nlsat::atom::ROOT_LE: return am.eval_sign_at(pr, as) <= 0; + case nlsat::atom::ROOT_LT: return am.eval_sign_at(pr, as) < 0; + case nlsat::atom::ROOT_GE: return am.eval_sign_at(pr, as) >= 0; + case nlsat::atom::ROOT_GT: return am.eval_sign_at(pr, as) > 0; + default: + UNREACHABLE(); + return false; + } +} + +static void tst10() { + params_ref ps; + reslimit rlim; + nlsat::solver s(rlim, ps); + anum_manager & am = s.am(); + nlsat::pmanager & pm = s.pm(); + nlsat::assignment as(am); + nlsat::explain& ex = s.get_explain(); + nlsat::var _a = s.mk_var(false); + nlsat::var _b = s.mk_var(false); + nlsat::var _c = s.mk_var(false); + nlsat::var _x = s.mk_var(false); + + polynomial_ref x(pm), a(pm), b(pm), c(pm), p(pm); + x = pm.mk_polynomial(_x); + a = pm.mk_polynomial(_a); + b = pm.mk_polynomial(_b); + c = pm.mk_polynomial(_c); + p = a*x*x + b*x + c; + scoped_anum one(am), two(am), three(am), mone(am), mtwo(am), mthree(am), zero(am), one_a_half(am); + am.set(zero, 0); + am.set(one, 1); + am.set(two, 2); + am.set(three, 3); + am.set(mone, -1); + am.set(mtwo, -2); + am.set(mthree, -3); + rational oah(1,2); + am.set(one_a_half, oah.to_mpq()); + + + scoped_anum_vector nums(am); + nums.push_back(one); + nums.push_back(two); + nums.push_back(one_a_half); + nums.push_back(mone); + nums.push_back(three); + + // a = 1, b = -3, c = 2: + // has roots x = 2, x = 1: + // 2^2 - 3*2 + 2 = 0 + // 1 - 3 + 2 = 0 + as.set(_a, one); + as.set(_b, mthree); + as.set(_c, two); + + for (unsigned i = 0; i < nums.size(); ++i) { + as.set(_x, nums[i]); + s.set_rvalues(as); + std::cout << p << "\n"; + as.display(std::cout); + for (unsigned k = nlsat::atom::ROOT_EQ; k <= nlsat::atom::ROOT_GE; ++k) { + if (satisfies_root(s, (nlsat::atom::kind) k, p)) { + test_root_literal(s, ex, _x, (nlsat::atom::kind) k, 1, p); + } + } + } + as.set(_a, mone); + as.set(_b, three); + as.set(_c, mtwo); + for (unsigned i = 0; i < nums.size(); ++i) { + as.set(_x, nums[i]); + s.set_rvalues(as); + std::cout << p << "\n"; + as.display(std::cout); + for (unsigned k = nlsat::atom::ROOT_EQ; k <= nlsat::atom::ROOT_GE; ++k) { + if (satisfies_root(s, (nlsat::atom::kind) k, p)) { + test_root_literal(s, ex, _x, (nlsat::atom::kind) k, 1, p); + } + } + } + std::cout << "\n"; +} + void tst_nlsat() { + tst10(); + std::cout << "------------------\n"; + exit(0); + tst9(); + std::cout << "------------------\n"; + exit(0); + tst8(); + std::cout << "------------------\n"; + tst7(); + std::cout << "------------------\n"; + tst6(); + std::cout << "------------------\n"; tst5(); + std::cout << "------------------\n"; tst4(); + std::cout << "------------------\n"; tst3(); } diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index 40ecbd8c7..a1b5c2859 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -12,9 +12,9 @@ Copyright (c) 2015 Microsoft Corporation #include "reg_decl_plugins.h" #include "arith_rewriter.h" #include "ast_pp.h" -#include "qe_util.h" #include "smt_context.h" #include "expr_abstract.h" +#include "expr_safe_replace.h" static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); @@ -29,6 +29,11 @@ static expr_ref parse_fml(ast_manager& m, char const* str) { << "(declare-const t Real)\n" << "(declare-const a Real)\n" << "(declare-const b Real)\n" + << "(declare-const i Int)\n" + << "(declare-const j Int)\n" + << "(declare-const k Int)\n" + << "(declare-const l Int)\n" + << "(declare-const m Int)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); @@ -42,6 +47,8 @@ static char const* example2 = "(and (<= z x) (<= x 3.0) (<= (* 3.0 x) y) (<= z y static char const* example3 = "(and (<= z x) (<= x 3.0) (< (* 3.0 x) y) (<= z y))"; static char const* example4 = "(and (<= z x) (<= x 3.0) (not (>= (* 3.0 x) y)) (<= z y))"; static char const* example5 = "(and (<= y x) (<= z x) (<= x u) (<= x v) (<= x t))"; +static char const* example7 = "(and (<= x y) (<= x z) (<= u x) (< v x))"; +static char const* example8 = "(and (<= (* 2 i) k) (<= j (* 3 i)))"; static char const* example6 = "(and (<= 0 (+ x z))\ (>= y x) \ @@ -51,30 +58,175 @@ static char const* example6 = "(and (<= 0 (+ x z))\ (>= x 0.0)\ (<= x 1.0))"; +// phi[M] => result => E x . phi[x] -static void test(char const *ex) { +static void test(app* var, expr_ref& fml) { + + ast_manager& m = fml.get_manager(); smt_params params; params.m_model = true; + + symbol x_name(var->get_decl()->get_name()); + sort* x_sort = m.get_sort(var); + + expr_ref pr(m); + expr_ref_vector lits(m); + flatten_and(fml, lits); + + model_ref md; + { + smt::context ctx(m, params); + ctx.assert_expr(fml); + lbool result = ctx.check(); + if (result != l_true) return; + ctx.get_model(md); + } + VERIFY(qe::arith_project(*md, var, lits)); + pr = mk_and(lits); + + std::cout << "original: " << mk_pp(fml, m) << "\n"; + std::cout << "projected: " << mk_pp(pr, m) << "\n"; + + // projection is consistent with model. + expr_ref tmp(m); + VERIFY(md->eval(pr, tmp) && m.is_true(tmp)); + + // projection implies E x. fml + { + qe::expr_quant_elim qelim(m, params); + expr_ref result(m), efml(m); + expr* x = var; + expr_abstract(m, 0, 1, &x, fml, efml); + efml = m.mk_exists(1, &x_sort, &x_name, efml); + qelim(m.mk_true(), efml, result); + + smt::context ctx(m, params); + ctx.assert_expr(pr); + ctx.assert_expr(m.mk_not(result)); + std::cout << "exists: " << pr << " =>\n" << result << "\n"; + VERIFY(l_false == ctx.check()); + } + + std::cout << "\n"; +} + + + +static void testR(char const *ex) { ast_manager m; reg_decl_plugins(m); arith_util a(m); expr_ref fml = parse_fml(m, ex); - app_ref_vector vars(m); - expr_ref_vector lits(m); - vars.push_back(m.mk_const(symbol("x"), a.mk_real())); - flatten_and(fml, lits); + symbol x_name("x"); + sort_ref x_sort(a.mk_real(), m); + app_ref var(m.mk_const(x_name, x_sort), m); + test(var, fml); +} - smt::context ctx(m, params); - ctx.assert_expr(fml); - lbool result = ctx.check(); - SASSERT(result == l_true); - ref md; - ctx.get_model(md); - expr_ref pr = qe::arith_project(*md, vars, lits); - - std::cout << mk_pp(fml, m) << "\n"; - std::cout << mk_pp(pr, m) << "\n"; +static void testI(char const *ex) { + ast_manager m; + reg_decl_plugins(m); + arith_util a(m); + expr_ref fml = parse_fml(m, ex); + symbol x_name("i"); + sort_ref x_sort(a.mk_int(), m); + app_ref var(m.mk_const(x_name, x_sort), m); + test(var, fml); +} + +static expr_ref_vector mk_ineqs(app* x, app* y, app_ref_vector const& nums) { + ast_manager& m = nums.get_manager(); + arith_util a(m); + expr_ref_vector result(m); + for (unsigned i = 0; i < nums.size(); ++i) { + expr_ref ax(a.mk_mul(nums[i], x), m); + result.push_back(a.mk_le(ax, y)); + result.push_back(m.mk_not(a.mk_ge(ax, y))); + result.push_back(m.mk_not(a.mk_gt(y, ax))); + result.push_back(a.mk_lt(y, ax)); + } + return result; +} + +static app_ref generate_ineqs(ast_manager& m, sort* s, vector& cs, bool mods_too) { + arith_util a(m); + app_ref_vector vars(m), nums(m); + vars.push_back(m.mk_const(symbol("x"), s)); + vars.push_back(m.mk_const(symbol("y"), s)); + vars.push_back(m.mk_const(symbol("z"), s)); + vars.push_back(m.mk_const(symbol("u"), s)); + vars.push_back(m.mk_const(symbol("v"), s)); + vars.push_back(m.mk_const(symbol("w"), s)); + nums.push_back(a.mk_numeral(rational(1), s)); + nums.push_back(a.mk_numeral(rational(2), s)); + nums.push_back(a.mk_numeral(rational(3), s)); + app* x = vars[0].get(); + app* y = vars[1].get(); + app* z = vars[2].get(); + // + // ax <= by, ax < by, not (ax >= by), not (ax > by) + // + cs.push_back(mk_ineqs(x, vars[1].get(), nums)); + cs.push_back(mk_ineqs(x, vars[2].get(), nums)); + cs.push_back(mk_ineqs(x, vars[3].get(), nums)); + cs.push_back(mk_ineqs(x, vars[4].get(), nums)); + cs.push_back(mk_ineqs(x, vars[5].get(), nums)); + + if (mods_too) { + expr_ref_vector mods(m); + expr_ref zero(a.mk_numeral(rational(0), s), m); + mods.push_back(m.mk_true()); + for (unsigned j = 0; j < nums.size(); ++j) { + mods.push_back(m.mk_eq(a.mk_mod(a.mk_add(a.mk_mul(nums[j].get(),x), y), nums[1].get()), zero)); + } + cs.push_back(mods); + mods.resize(1); + for (unsigned j = 0; j < nums.size(); ++j) { + mods.push_back(m.mk_eq(a.mk_mod(a.mk_add(a.mk_mul(nums[j].get(),x), y), nums[2].get()), zero)); + } + cs.push_back(mods); + } + return app_ref(x, m); +} + + +static void test_c(app* x, expr_ref_vector const& c) { + ast_manager& m = c.get_manager(); + expr_ref fml(m); + fml = m.mk_and(c.size(), c.c_ptr()); + test(x, fml); +} + +static void test_cs(app* x, expr_ref_vector& c, vector const& cs) { + if (c.size() == cs.size()) { + test_c(x, c); + return; + } + expr_ref_vector const& c1 = cs[c.size()]; + for (unsigned i = 0; i < c1.size(); ++i) { + c.push_back(c1[i]); + test_cs(x, c, cs); + c.pop_back(); + } +} + + +static void test_ineqs(ast_manager& m, sort* s, bool mods_too) { + vector ineqs; + app_ref x = generate_ineqs(m, s, ineqs, mods_too); + expr_ref_vector cs(m); + test_cs(x, cs, ineqs); +} + +static void test_ineqs() { + ast_manager m; + reg_decl_plugins(m); + arith_util a(m); + sort* s_int = a.mk_int(); + sort* s_real = a.mk_real(); + test_ineqs(m, s_int, true); + test_ineqs(m, s_real, false); } static void test2(char const *ex) { @@ -102,7 +254,7 @@ static void test2(char const *ex) { std::cout << mk_pp(fml, m) << "\n"; - expr_ref pr2(m), fml2(m); + expr_ref pr1(m), pr2(m), fml2(m); expr_ref_vector bound(m); ptr_vector sorts; svector names; @@ -114,7 +266,10 @@ static void test2(char const *ex) { expr_abstract(m, 0, bound.size(), bound.c_ptr(), fml, fml2); fml2 = m.mk_exists(bound.size(), sorts.c_ptr(), names.c_ptr(), fml2); qe::expr_quant_elim qe(m, params); - expr_ref pr1 = qe::arith_project(*md, vars, lits); + for (unsigned i = 0; i < vars.size(); ++i) { + VERIFY(qe::arith_project(*md, vars[i].get(), lits)); + } + pr1 = mk_and(lits); qe(m.mk_true(), fml2, pr2); std::cout << mk_pp(pr1, m) << "\n"; std::cout << mk_pp(pr2, m) << "\n"; @@ -131,11 +286,17 @@ static void test2(char const *ex) { } void tst_qe_arith() { +// enable_trace("qe"); + testI(example8); + testR(example7); + test_ineqs(); + return; + testR(example1); + testR(example2); + testR(example3); + testR(example4); + testR(example5); + return; test2(example6); return; - test(example1); - test(example2); - test(example3); - test(example4); - test(example5); } diff --git a/src/util/obj_pair_set.h b/src/util/obj_pair_set.h index 4c8ed698a..29139a51d 100644 --- a/src/util/obj_pair_set.h +++ b/src/util/obj_pair_set.h @@ -45,6 +45,7 @@ public: bool contains(T1 * t1, T2 * t2) const { return m_set.contains(obj_pair(t1, t2)); } bool contains(obj_pair const & p) const { return m_set.contains(p); } void reset() { m_set.reset(); } + bool empty() const { return m_set.empty(); } }; #endif diff --git a/src/util/small_object_allocator.cpp b/src/util/small_object_allocator.cpp index 6ad1af112..aee84c1f0 100644 --- a/src/util/small_object_allocator.cpp +++ b/src/util/small_object_allocator.cpp @@ -69,6 +69,7 @@ void small_object_allocator::reset() { #define MASK ((1 << PTR_ALIGNMENT) - 1) void small_object_allocator::deallocate(size_t size, void * p) { + if (size == 0) return; #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly memory::deallocate(p); @@ -91,6 +92,7 @@ void small_object_allocator::deallocate(size_t size, void * p) { } void * small_object_allocator::allocate(size_t size) { + if (size == 0) return 0; #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly return memory::allocate(size);