3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-28 11:25:51 +00:00

Merge branch 'delcypher-cmake_fix_enable_tracing_in_debug_mode'

This commit is contained in:
Christoph M. Wintersteiger 2016-03-22 15:02:40 +00:00
commit 38fc49a05d
97 changed files with 6621 additions and 845 deletions

View file

@ -289,12 +289,14 @@ endif()
################################################################################ ################################################################################
# Tracing # Tracing
################################################################################ ################################################################################
option(ENABLE_TRACING OFF "Enable tracing") option(ENABLE_TRACING_FOR_NON_DEBUG "Enable tracing in non-debug builds." OFF)
if (ENABLE_TRACING) if (ENABLE_TRACING_FOR_NON_DEBUG)
list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_TRACE") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_TRACE")
else()
# Tracing is always enabled in debug builds
list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:Debug>:_TRACE>)
endif() endif()
# Should we always enable tracing when doing a debug build? # Should we always enable tracing when doing a debug build?
#list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:Debug>:_TRACE>)
################################################################################ ################################################################################
# Postion indepdent code # Postion indepdent code

View file

@ -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_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_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/``). * ``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. * ``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. * ``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. * ``USE_OPENMP`` - BOOL. If set to ``TRUE`` and OpenMP support is detected build with OpenMP support.

View file

@ -74,35 +74,50 @@ add_custom_target(build_z3_python_bindings
############################################################################### ###############################################################################
option(INSTALL_PYTHON_BINDINGS "Install Python bindings when invoking install target" ON) option(INSTALL_PYTHON_BINDINGS "Install Python bindings when invoking install target" ON)
if (INSTALL_PYTHON_BINDINGS) if (INSTALL_PYTHON_BINDINGS)
# Determine the installation path for the bindings
message(STATUS "Emitting rules to install Z3 python bindings") message(STATUS "Emitting rules to install Z3 python bindings")
execute_process( # Try to guess the installation path for the bindings
COMMAND "${PYTHON_EXECUTABLE}" "-c" if (NOT DEFINED CMAKE_INSTALL_PYTHON_PKG_DIR)
"import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())" message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR not set. Trying to guess")
RESULT_VARIABLE exit_code execute_process(
OUTPUT_VARIABLE python_pkg_dir COMMAND "${PYTHON_EXECUTABLE}" "-c"
OUTPUT_STRIP_TRAILING_WHITESPACE "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)) if (NOT ("${exit_code}" EQUAL 0))
message(FATAL_ERROR "Failed to determine your Python package directory") 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() endif()
message(STATUS "Detected Python package directory: \"${python_pkg_dir}\"")
# Check if path exists under the install prefix # Check if path exists under the install prefix if it is absolute. If the
set(python_install_dir "") # path is relative it will be installed under the install prefix so there
string(FIND "${python_pkg_dir}" "${CMAKE_INSTALL_PREFIX}" position) # if nothing to check
if (NOT ("${position}" EQUAL 0)) if (IS_ABSOLUTE "${CMAKE_INSTALL_PYTHON_PKG_DIR}")
message(WARNING "The detected Python package directory \"${python_pkg_dir}\" " string(FIND "${CMAKE_INSTALL_PYTHON_PKG_DIR}" "${CMAKE_INSTALL_PREFIX}" position)
"does not exist under the install prefix \"${CMAKE_INSTALL_PREFIX}\"." if (NOT ("${position}" EQUAL 0))
" Running the install target may lead to a broken installation." 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() endif()
# Using DESTDIR still seems to work even if we use an absolute path # 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 \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\"")
message(STATUS "Python bindings will be installed to \"${python_install_dir}\"")
install(FILES ${build_z3_python_bindings_target_depends} install(FILES ${build_z3_python_bindings_target_depends}
DESTINATION "${python_install_dir}" DESTINATION "${CMAKE_INSTALL_PYTHON_PKG_DIR}"
) )
else() else()
message(STATUS "Not emitting rules to install Z3 python bindings") message(STATUS "Not emitting rules to install Z3 python bindings")

View file

@ -12,6 +12,7 @@ z3_add_component(rewriter
expr_safe_replace.cpp expr_safe_replace.cpp
factor_rewriter.cpp factor_rewriter.cpp
fpa_rewriter.cpp fpa_rewriter.cpp
label_rewriter.cpp
mk_simplified_app.cpp mk_simplified_app.cpp
pb_rewriter.cpp pb_rewriter.cpp
quant_hoist.cpp quant_hoist.cpp

View file

@ -1,21 +1,28 @@
z3_add_component(qe z3_add_component(qe
SOURCES SOURCES
nlarith_util.cpp nlarith_util.cpp
nlqsat.cpp
qe_arith.cpp qe_arith.cpp
qe_arith_plugin.cpp qe_arith_plugin.cpp
qe_array_plugin.cpp qe_array_plugin.cpp
qe_arrays.cpp
qe_bool_plugin.cpp qe_bool_plugin.cpp
qe_bv_plugin.cpp qe_bv_plugin.cpp
qe_cmd.cpp qe_cmd.cpp
qe.cpp qe.cpp
qe_datatype_plugin.cpp qe_datatype_plugin.cpp
qe_datatypes.cpp
qe_dl_plugin.cpp qe_dl_plugin.cpp
qe_lite.cpp qe_lite.cpp
qe_mbp.cpp
qe_sat_tactic.cpp qe_sat_tactic.cpp
qe_tactic.cpp qe_tactic.cpp
qe_util.cpp qsat.cpp
vsubst_tactic.cpp vsubst_tactic.cpp
COMPONENT_DEPENDENCIES COMPONENT_DEPENDENCIES
nlsat_tactic
nlsat
sat sat
smt smt
tactic
) )

View file

@ -57,7 +57,7 @@ def sorted_headers_by_component(l):
_logger.debug("get_key({})".format(path)) _logger.debug("get_key({})".format(path))
path_components = [] path_components = []
stripped_path = path 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`` # Keep stripping off directory components until we hit ``src``
while os.path.basename(stripped_path) != 'src': while os.path.basename(stripped_path) != 'src':
path_components.append(os.path.basename(stripped_path)) path_components.append(os.path.basename(stripped_path))

View file

@ -57,7 +57,7 @@ def init_project_def():
add_lib('fuzzing', ['ast'], 'test/fuzzing') add_lib('fuzzing', ['ast'], 'test/fuzzing')
add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('smt_tactic', ['smt'], 'smt/tactic')
add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') 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('duality', ['smt', 'interp', 'qe'])
add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base')
add_lib('dataflow', ['muz'], 'muz/dataflow') add_lib('dataflow', ['muz'], 'muz/dataflow')

View file

@ -604,8 +604,16 @@ namespace z3 {
/** /**
\brief Return true if this expression is a numeral. \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() 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. \brief Return true if this expression is an application.
*/ */
@ -625,7 +633,7 @@ namespace z3 {
/** /**
\brief Return true if expression is an algebraic number. \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). \brief Return true if this expression is well sorted (aka type correct).
@ -650,11 +658,10 @@ namespace z3 {
\pre is_numeral() \pre is_numeral()
*/ */
int get_numeral_int() const { int get_numeral_int() const {
assert(is_numeral());
int result; int result;
Z3_bool state = Z3_get_numeral_int(ctx(), m_ast, &result); if (!is_numeral_i(result)) {
if (state != Z3_TRUE)
throw exception("numeral does not fit in machine int"); throw exception("numeral does not fit in machine int");
}
return result; return result;
} }
@ -667,9 +674,9 @@ namespace z3 {
unsigned get_numeral_uint() const { unsigned get_numeral_uint() const {
assert(is_numeral()); assert(is_numeral());
unsigned result; unsigned result;
Z3_bool state = Z3_get_numeral_uint(ctx(), m_ast, &result); if (!is_numeral_u(result)) {
if (state != Z3_TRUE)
throw exception("numeral does not fit in machine uint"); throw exception("numeral does not fit in machine uint");
}
return result; return result;
} }
@ -682,9 +689,9 @@ namespace z3 {
__int64 get_numeral_int64() const { __int64 get_numeral_int64() const {
assert(is_numeral()); assert(is_numeral());
__int64 result; __int64 result;
Z3_bool state = Z3_get_numeral_int64(ctx(), m_ast, &result); if (!is_numeral_i64(result)) {
if (state != Z3_TRUE)
throw exception("numeral does not fit in machine __int64"); throw exception("numeral does not fit in machine __int64");
}
return result; return result;
} }
@ -697,9 +704,9 @@ namespace z3 {
__uint64 get_numeral_uint64() const { __uint64 get_numeral_uint64() const {
assert(is_numeral()); assert(is_numeral());
__uint64 result; __uint64 result;
Z3_bool state = Z3_get_numeral_uint64(ctx(), m_ast, &result); if (!is_numeral_u64(result)) {
if (state != Z3_TRUE)
throw exception("numeral does not fit in machine __uint64"); throw exception("numeral does not fit in machine __uint64");
}
return result; return result;
} }

View file

@ -4162,6 +4162,7 @@ def Select(a, i):
_z3_assert(is_array(a), "First argument must be a Z3 array expression") _z3_assert(is_array(a), "First argument must be a Z3 array expression")
return a[i] return a[i]
def Map(f, *args): def Map(f, *args):
"""Return a Z3 map array expression. """Return a Z3 map array expression.

View file

@ -664,3 +664,45 @@ algebraic_numbers::anum const & arith_util::to_irrational_algebraic_numeral(expr
SASSERT(is_irrational_algebraic_numeral(n)); SASSERT(is_irrational_algebraic_numeral(n));
return plugin().aw().to_anum(to_app(n)->get_decl()); 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;
}

View file

@ -149,7 +149,6 @@ protected:
func_decl * m_mod_0_decl; func_decl * m_mod_0_decl;
func_decl * m_u_asin_decl; func_decl * m_u_asin_decl;
func_decl * m_u_acos_decl; func_decl * m_u_acos_decl;
ptr_vector<app> m_small_ints; ptr_vector<app> m_small_ints;
ptr_vector<app> m_small_reals; ptr_vector<app> m_small_reals;
@ -416,6 +415,11 @@ public:
return m_manager.mk_eq(lhs, rhs); 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_ */ #endif /* ARITH_DECL_PLUGIN_H_ */

View file

@ -1200,6 +1200,14 @@ std::ostream& operator<<(std::ostream& out, app_ref const& e) {
return out << mk_ismt2_pp(e.get(), e.get_manager()); 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) { std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) {
for (unsigned i = 0; i < e.size(); ++i) { for (unsigned i = 0; i < e.size(); ++i) {
out << mk_ismt2_pp(e[i], e.get_manager()); 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; 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 #ifdef Z3DEBUG
void pp(expr const * n, ast_manager & m) { void pp(expr const * n, ast_manager & m) {
std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl; std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl;

View file

@ -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, expr_ref const& e);
std::ostream& operator<<(std::ostream& out, app_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, expr_ref_vector const& e);
std::ostream& operator<<(std::ostream& out, app_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 #endif

View file

@ -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)); return to_app(mk_and(m, num_args, (expr* const*) args));
} }
expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) { expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) {
if (num_args == 0) if (num_args == 0)
return m.mk_false(); return m.mk_false();
@ -188,10 +187,43 @@ expr * mk_not(ast_manager & m, expr * arg) {
expr * atom; expr * atom;
if (m.is_not(arg, atom)) if (m.is_not(arg, atom))
return atom; return atom;
else if (m.is_true(arg))
return m.mk_false();
else if (m.is_false(arg))
return m.mk_true();
else else
return m.mk_not(arg); 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 * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) {
expr_ref_buffer new_diseqs(m); expr_ref_buffer new_diseqs(m);
for (unsigned i = 0; i < num_args; i++) { 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()); 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) { void flatten_and(expr_ref_vector& result) {
ast_manager& m = result.get_manager(); ast_manager& m = result.get_manager();
expr* e1, *e2, *e3; expr* e1, *e2, *e3;

View file

@ -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 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()); } 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) Return a if arg = (not a)
Retur (not arg) otherwise Retur (not arg) otherwise
*/ */
expr * mk_not(ast_manager & m, expr * arg); 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]))) 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); 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. \brief Collect top-level conjunctions and disjunctions.
*/ */

View file

@ -865,6 +865,12 @@ sort * bv_util::mk_sort(unsigned bv_size) {
return m_manager.mk_sort(get_fid(), BV_SORT, 1, p); 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<unsigned>(sz);
}
app * bv_util::mk_bv2int(expr* e) { app * bv_util::mk_bv2int(expr* e) {
sort* s = m_manager.mk_sort(m_manager.mk_family_id("arith"), INT_SORT); sort* s = m_manager.mk_sort(m_manager.mk_family_id("arith"), INT_SORT);
parameter p(s); parameter p(s);

View file

@ -234,7 +234,6 @@ protected:
func_decl * mk_mkbv(unsigned arity, sort * const * domain); 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); 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); 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) { virtual bool is_considered_uninterpreted(func_decl * f) {
if (f->get_family_id() != get_family_id()) if (f->get_family_id() != get_family_id())
return false; return false;
@ -390,6 +391,8 @@ public:
return static_cast<unsigned>(s->get_parameter(0).get_int()); return static_cast<unsigned>(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_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_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); } 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_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); } app * mk_bv(unsigned n, expr* const* es) { return m_manager.mk_app(get_fid(), OP_MKBV, n, es); }
}; };
#endif /* BV_DECL_PLUGIN_H_ */ #endif /* BV_DECL_PLUGIN_H_ */

View file

@ -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);
}

View file

@ -209,6 +209,8 @@ public:
func_decl * get_recognizer_constructor(func_decl * recognizer); func_decl * get_recognizer_constructor(func_decl * recognizer);
family_id get_family_id() const { return m_family_id; } family_id get_family_id() const { return m_family_id; }
bool are_siblings(sort * s1, sort * s2); 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 reset();
void display_datatype(sort *s, std::ostream& strm); void display_datatype(sort *s, std::ostream& strm);

View file

@ -22,6 +22,10 @@ Notes:
void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { 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; expr * curr = 0, *b = 0;
SASSERT(n->get_ref_count() > 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); expr_abstractor abs(m);
abs(base, num_bound, bound, n, result); 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<sort> sorts;
svector<symbol> 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);
}

View file

@ -33,6 +33,8 @@ public:
}; };
void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result);
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 #endif

View file

@ -30,6 +30,14 @@ void expr_safe_replace::insert(expr* src, expr* dst) {
m_subst.insert(src, 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) { void expr_safe_replace::operator()(expr* e, expr_ref& res) {
m_todo.push_back(e); m_todo.push_back(e);
expr* a, *b; expr* a, *b;

View file

@ -42,6 +42,8 @@ public:
void operator()(expr* src, expr_ref& e); void operator()(expr* src, expr_ref& e);
void operator()(expr_ref_vector& es);
void apply_substitution(expr* s, expr* def, expr_ref& t); void apply_substitution(expr* s, expr* def, expr_ref& t);
void reset(); void reset();

View file

@ -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<label_rewriter>;

View file

@ -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<label_rewriter> 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

View file

@ -73,7 +73,7 @@ public:
unsigned nd = q->get_num_decls(); unsigned nd = q->get_num_decls();
for (unsigned i = 0; i < nd; ++i) { for (unsigned i = 0; i < nd; ++i) {
sort* s = q->get_decl_sort(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); vars.push_back(a);
} }
expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd); expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd);
@ -154,9 +154,7 @@ private:
} }
quantifier_type& negate(quantifier_type& qt) { quantifier_type& negate(quantifier_type& qt) {
TRACE("qe", display(qt, tout); tout << "\n";);
qt = static_cast<quantifier_type>(qt ^0x1); qt = static_cast<quantifier_type>(qt ^0x1);
TRACE("qe", display(qt, tout); tout << "\n";);
return qt; return qt;
} }
@ -205,6 +203,7 @@ private:
case AST_APP: { case AST_APP: {
expr_ref_vector args(m); expr_ref_vector args(m);
expr_ref tmp(m); expr_ref tmp(m);
expr* t1, *t2, *t3;
unsigned num_args = 0; unsigned num_args = 0;
app* a = to_app(fml); app* a = to_app(fml);
if (m.is_and(fml)) { if (m.is_and(fml)) {
@ -228,16 +227,35 @@ private:
negate(qt); negate(qt);
result = m.mk_not(tmp); result = m.mk_not(tmp);
} }
else if (m.is_implies(fml)) { else if (m.is_implies(fml, t1, t2)) {
pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp); pull_quantifier(t1, negate(qt), vars, tmp);
negate(qt); 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); result = m.mk_implies(tmp, result);
} }
else if (m.is_ite(fml)) { else if (m.is_ite(fml, t1, t2, t3)) {
pull_quantifier(to_app(fml)->get_arg(1), qt, vars, tmp); expr_ref tt1(m), tt2(m), tt3(m), ntt1(m), nt1(m);
pull_quantifier(to_app(fml)->get_arg(2), qt, vars, result); pull_quantifier(t2, qt, vars, tt2);
result = m.mk_ite(to_app(fml)->get_arg(0), tmp, result); 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 { else {
// the formula contains a quantifier, but it is "inaccessible" // the formula contains a quantifier, but it is "inaccessible"

View file

@ -578,6 +578,16 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
return st; 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, bool reduce_quantifier(quantifier * old_q,
expr * new_body, expr * new_body,
expr * const * new_patterns, expr * const * new_patterns,
@ -695,6 +705,9 @@ struct th_rewriter::imp : public rewriter_tpl<th_rewriter_cfg> {
rewriter_tpl<th_rewriter_cfg>(m, m.proofs_enabled(), m_cfg), rewriter_tpl<th_rewriter_cfg>(m, m.proofs_enabled(), m_cfg),
m_cfg(m, p) { 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): 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; 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);
}

View file

@ -45,6 +45,8 @@ public:
void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result); 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 cleanup();
void reset(); void reset();

View file

@ -303,8 +303,8 @@ public:
goal_ref g = alloc(goal, m, ctx.produce_proofs(), ctx.produce_models(), ctx.produce_unsat_cores()); goal_ref g = alloc(goal, m, ctx.produce_proofs(), ctx.produce_models(), ctx.produce_unsat_cores());
assert_exprs_from(ctx, *g); assert_exprs_from(ctx, *g);
unsigned timeout = p.get_uint("timeout", UINT_MAX); unsigned timeout = p.get_uint("timeout", ctx.params().m_timeout);
unsigned rlimit = p.get_uint("rlimit", 0); unsigned rlimit = p.get_uint("rlimit", ctx.params().m_rlimit);
goal_ref_buffer result_goals; goal_ref_buffer result_goals;
model_converter_ref mc; model_converter_ref mc;

View file

@ -539,7 +539,6 @@ namespace algebraic_numbers {
} }
bool factor(scoped_upoly const & up, factors & r) { bool factor(scoped_upoly const & up, factors & r) {
// std::cout << "factor: "; upm().display(std::cout, up); std::cout << std::endl;
if (m_factor) { if (m_factor) {
return upm().factor(up, r, m_factor_params); return upm().factor(up, r, m_factor_params);
} }
@ -547,7 +546,7 @@ namespace algebraic_numbers {
scoped_upoly & up_sqf = m_isolate_tmp3; scoped_upoly & up_sqf = m_isolate_tmp3;
up_sqf.reset(); up_sqf.reset();
upm().square_free(up.size(), up.c_ptr(), up_sqf); 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); r.push_back(up_sqf, 1);
return false; return false;
} }
@ -566,6 +565,7 @@ namespace algebraic_numbers {
} }
void isolate_roots(scoped_upoly const & up, numeral_vector & roots) { void isolate_roots(scoped_upoly const & up, numeral_vector & roots) {
TRACE("algebraic", upm().display(tout, up); tout << "\n";);
if (up.empty()) if (up.empty())
return; // ignore the zero polynomial return; // ignore the zero polynomial
factors & fs = m_isolate_factors; factors & fs = m_isolate_factors;
@ -586,6 +586,7 @@ namespace algebraic_numbers {
upolynomial::numeral_vector const & f = fs[i]; upolynomial::numeral_vector const & f = fs[i];
// polynomial f contains the non zero roots // polynomial f contains the non zero roots
unsigned d = upm().degree(f); unsigned d = upm().degree(f);
TRACE("algebraic", tout << "factor " << i << " degree: " << d << "\n";);
if (d == 0) if (d == 0)
continue; // found all roots of f continue; // found all roots of f
scoped_mpq r(qm()); scoped_mpq r(qm());
@ -603,6 +604,7 @@ namespace algebraic_numbers {
upm().sqf_isolate_roots(f.size(), f.c_ptr(), bqm(), m_isolate_roots, m_isolate_lowers, m_isolate_uppers); 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(); unsigned sz = m_isolate_roots.size();
TRACE("algebraic", tout << "isolated roots: " << sz << "\n";);
for (unsigned i = 0; i < sz; i++) { for (unsigned i = 0; i < sz; i++) {
to_mpq(qm(), m_isolate_roots[i], r); to_mpq(qm(), m_isolate_roots[i], r);
roots.push_back(numeral(mk_basic_cell(r))); roots.push_back(numeral(mk_basic_cell(r)));

View file

@ -4,6 +4,5 @@ def_module_params('model',
('v1', BOOL, False, 'use Z3 version 1.x pretty printer'), ('v1', BOOL, False, 'use Z3 version 1.x pretty printer'),
('v2', BOOL, False, 'use Z3 version 2.x (x <= 16) 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)'), ('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)'),
)) ))

View file

@ -55,8 +55,7 @@ namespace datalog {
m_args(m), m_args(m),
m_hnf(m), m_hnf(m),
m_qe(m), m_qe(m),
m_cfg(m), m_rwr(m),
m_rwr(m, false, m_cfg),
m_ufproc(m) {} m_ufproc(m) {}
void rule_manager::inc_ref(rule * r) { 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) { void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) {
expr_ref tmp(m); m_rwr.remove_labels(fml, pr);
m_rwr(fml, tmp);
if (pr && fml != tmp) {
pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp));
}
fml = tmp;
} }
var_idx_set& rule_manager::collect_vars(expr* e) { var_idx_set& rule_manager::collect_vars(expr* e) {
@ -1092,5 +1071,4 @@ namespace datalog {
}; };
template class rewriter_tpl<datalog::rule_manager::remove_label_cfg>;

View file

@ -32,6 +32,7 @@ Revision History:
#include"qe_lite.h" #include"qe_lite.h"
#include"var_subst.h" #include"var_subst.h"
#include"datatype_decl_plugin.h" #include"datatype_decl_plugin.h"
#include"label_rewriter.h"
namespace datalog { namespace datalog {
@ -102,16 +103,6 @@ namespace datalog {
*/ */
class rule_manager 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; ast_manager& m;
context& m_ctx; context& m_ctx;
rule_counter m_counter; rule_counter m_counter;
@ -124,8 +115,7 @@ namespace datalog {
svector<bool> m_neg; svector<bool> m_neg;
hnf m_hnf; hnf m_hnf;
qe_lite m_qe; qe_lite m_qe;
remove_label_cfg m_cfg; label_rewriter m_rwr;
rewriter_tpl<remove_label_cfg> m_rwr;
mutable uninterpreted_function_finder_proc m_ufproc; mutable uninterpreted_function_finder_proc m_ufproc;
mutable quantifier_finder_proc m_qproc; mutable quantifier_finder_proc m_qproc;
mutable expr_sparse_mark m_visited; mutable expr_sparse_mark m_visited;

View file

@ -29,7 +29,6 @@ Revision History:
#include"ast_counter.h" #include"ast_counter.h"
#include"statistics.h" #include"statistics.h"
#include"lbool.h" #include"lbool.h"
#include"qe_util.h"
namespace datalog { namespace datalog {

View file

@ -20,6 +20,7 @@ Revision History:
#include "pdr_closure.h" #include "pdr_closure.h"
#include "pdr_context.h" #include "pdr_context.h"
#include "expr_safe_replace.h" #include "expr_safe_replace.h"
#include "ast_util.h"
namespace pdr { namespace pdr {
@ -147,7 +148,7 @@ namespace pdr {
for (unsigned i = 0; i < fmls.size(); ++i) { for (unsigned i = 0; i < fmls.size(); ++i) {
fmls[i] = close_fml(fmls[i].get()); 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) { expr_ref closure::relax(unsigned i, expr* fml) {
@ -169,7 +170,7 @@ namespace pdr {
for (unsigned i = 0; i < As.size(); ++i) { for (unsigned i = 0; i < As.size(); ++i) {
fmls.push_back(relax(i, As[i])); fmls.push_back(relax(i, As[i]));
} }
B = qe::mk_and(fmls); B = mk_and(fmls);
return B; return B;
} }

View file

@ -45,7 +45,6 @@ Notes:
#include "smt_value_sort.h" #include "smt_value_sort.h"
#include "proof_utils.h" #include "proof_utils.h"
#include "dl_boogie_proof.h" #include "dl_boogie_proof.h"
#include "qe_util.h"
#include "scoped_proof.h" #include "scoped_proof.h"
#include "blast_term_ite_tactic.h" #include "blast_term_ite_tactic.h"
#include "model_implicant.h" #include "model_implicant.h"

View file

@ -201,7 +201,7 @@ namespace pdr {
lits.push_back(extract_consequence(lo, hi)); lits.push_back(extract_consequence(lo, hi));
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"; } }); IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } });
#endif #endif
} }
@ -415,6 +415,7 @@ namespace pdr {
return false; return false;
} }
} }
}; };
class collect_pure_proc { class collect_pure_proc {

View file

@ -117,7 +117,7 @@ namespace pdr {
void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
ast_manager& m = n.pt().get_manager(); ast_manager& m = n.pt().get_manager();
if (core.empty()) return; 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); expr_ref_vector Bs(m);
flatten_or(B, Bs); flatten_or(B, Bs);
A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level()); 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)) { if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) {
TRACE("pdr", TRACE("pdr",
tout << "Old core:\n" << mk_pp(B, m) << "\n"; tout << "Old core:\n" << mk_pp(B, m) << "\n";
tout << "New core:\n" << mk_pp(qe::mk_and(lemmas), m) << "\n";); tout << "New core:\n" << mk_and(lemmas) << "\n";);
Bs[i] = qe::mk_and(lemmas); Bs[i] = mk_and(lemmas);
change = true; change = true;
} }
} }
if (change) { 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";); TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";);
core.reset(); core.reset();
flatten_and(C, core); flatten_and(C, core);
@ -186,7 +186,7 @@ namespace pdr {
} }
closure cl(n.pt(), m_is_closure); 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); expr_ref fml2 = n.pt().get_formulas(n.level(), false);
fml1_2.push_back(fml1); fml1_2.push_back(fml1);
fml1_2.push_back(0); fml1_2.push_back(0);
@ -205,7 +205,7 @@ namespace pdr {
if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) { if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) {
new_cores.push_back(std::make_pair(conv2, uses_level1)); new_cores.push_back(std::make_pair(conv2, uses_level1));
change = true; change = true;
expr_ref state1 = qe::mk_and(conv2); expr_ref state1 = mk_and(conv2);
TRACE("pdr", TRACE("pdr",
tout << mk_pp(state, m) << "\n"; tout << mk_pp(state, m) << "\n";
tout << "Generalized to:\n" << mk_pp(state1, 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++) { for (unsigned i = ut_size; i < t_size; i++) {
conj.push_back(rule.get_tail(i)); conj.push_back(rule.get_tail(i));
} }
result = qe::mk_and(conj); result = mk_and(conj);
if (!sub.empty()) { if (!sub.empty()) {
expr_ref tmp = result; expr_ref tmp = result;
var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), 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) { for (unsigned i = 0; i < rules.size(); ++i) {
fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[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";); TRACE("pdr", tout << mk_pp(fml, m) << "\n";);
return fml; 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";); TRACE("pdr", tout << mk_pp(result, m) << "\n";);
return result; return result;
} }

View file

@ -6,7 +6,6 @@ Copyright (c) 2015 Microsoft Corporation
#include "check_relation.h" #include "check_relation.h"
#include "dl_relation_manager.h" #include "dl_relation_manager.h"
#include "qe_util.h"
#include "ast_util.h" #include "ast_util.h"
#include "smt_kernel.h" #include "smt_kernel.h"
#include <typeinfo> #include <typeinfo>

View file

@ -23,7 +23,6 @@ Notes:
--*/ --*/
#include "udoc_relation.h" #include "udoc_relation.h"
#include "dl_relation_manager.h" #include "dl_relation_manager.h"
#include "qe_util.h"
#include "ast_util.h" #include "ast_util.h"
#include "smt_kernel.h" #include "smt_kernel.h"

View file

@ -39,6 +39,17 @@ namespace nlsat {
m_values.swap(other.m_values); m_values.swap(other.m_values);
m_assigned.swap(other.m_assigned); 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) { void set_core(var x, anum & v) {
m_values.reserve(x+1, anum()); m_values.reserve(x+1, anum());
m_assigned.reserve(x+1, false); m_assigned.reserve(x+1, false);
@ -52,11 +63,26 @@ namespace nlsat {
am().set(m_values[x], v); am().set(m_values[x], v);
} }
void reset(var x) { if (x < m_assigned.size()) m_assigned[x] = false; } 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); } bool is_assigned(var x) const { return m_assigned.get(x, false); }
anum const & value(var x) const { return m_values[x]; } anum const & value(var x) const { return m_values[x]; }
virtual anum_manager & m() const { return am(); } virtual anum_manager & m() const { return am(); }
virtual bool contains(var x) const { return is_assigned(x); } virtual bool contains(var x) const { return is_assigned(x); }
virtual anum const & operator()(var x) const { SASSERT(is_assigned(x)); return value(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";
}
}
}
}; };
/** /**

View file

@ -18,10 +18,12 @@ Revision History:
--*/ --*/
#include"nlsat_evaluator.h" #include"nlsat_evaluator.h"
#include"nlsat_solver.h"
namespace nlsat { namespace nlsat {
struct evaluator::imp { struct evaluator::imp {
solver& m_solver;
assignment const & m_assignment; assignment const & m_assignment;
pmanager & m_pm; pmanager & m_pm;
small_object_allocator & m_allocator; small_object_allocator & m_allocator;
@ -357,7 +359,8 @@ namespace nlsat {
sign_table m_sign_table_tmp; 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_assignment(x2v),
m_pm(pm), m_pm(pm),
m_allocator(allocator), m_allocator(allocator),
@ -420,9 +423,24 @@ namespace nlsat {
scoped_anum_vector & roots = m_tmp_values; scoped_anum_vector & roots = m_tmp_values;
roots.reset(); roots.reset();
m_am.isolate_roots(polynomial_ref(a->p(), m_pm), undef_var_assignment(m_assignment, a->x()), roots); 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); SASSERT(a->i() > 0);
if (a->i() > roots.size()) if (a->i() > roots.size()) {
return false; // p does have sufficient roots return neg;
}
int sign = m_am.compare(m_assignment.value(a->x()), roots[a->i() - 1]); int sign = m_am.compare(m_assignment.value(a->x()), roots[a->i() - 1]);
return satisfied(sign, k, neg); return satisfied(sign, k, neg);
} }
@ -649,8 +667,8 @@ namespace nlsat {
} }
}; };
evaluator::evaluator(assignment const & x2v, pmanager & pm, small_object_allocator & allocator) { evaluator::evaluator(solver& s, assignment const & x2v, pmanager & pm, small_object_allocator & allocator) {
m_imp = alloc(imp, x2v, pm, allocator); m_imp = alloc(imp, s, x2v, pm, allocator);
} }
evaluator::~evaluator() { evaluator::~evaluator() {

View file

@ -26,11 +26,13 @@ Revision History:
namespace nlsat { namespace nlsat {
class solver;
class evaluator { class evaluator {
struct imp; struct imp;
imp * m_imp; imp * m_imp;
public: public:
evaluator(assignment const & x2v, pmanager & pm, small_object_allocator & allocator); evaluator(solver& s, assignment const & x2v, pmanager & pm, small_object_allocator & allocator);
~evaluator(); ~evaluator();
interval_set_manager & ism() const; interval_set_manager & ism() const;

View file

@ -36,6 +36,7 @@ namespace nlsat {
polynomial::cache & m_cache; polynomial::cache & m_cache;
pmanager & m_pm; pmanager & m_pm;
polynomial_ref_vector m_ps; polynomial_ref_vector m_ps;
polynomial_ref_vector m_ps2;
polynomial_ref_vector m_psc_tmp; polynomial_ref_vector m_psc_tmp;
polynomial_ref_vector m_factors; polynomial_ref_vector m_factors;
scoped_anum_vector m_roots_tmp; scoped_anum_vector m_roots_tmp;
@ -43,6 +44,7 @@ namespace nlsat {
bool m_full_dimensional; bool m_full_dimensional;
bool m_minimize_cores; bool m_minimize_cores;
bool m_factor; bool m_factor;
bool m_signed_project;
struct todo_set { struct todo_set {
polynomial::cache & m_cache; polynomial::cache & m_cache;
@ -137,6 +139,7 @@ namespace nlsat {
m_cache(u), m_cache(u),
m_pm(u.pm()), m_pm(u.pm()),
m_ps(m_pm), m_ps(m_pm),
m_ps2(m_pm),
m_psc_tmp(m_pm), m_psc_tmp(m_pm),
m_factors(m_pm), m_factors(m_pm),
m_roots_tmp(m_am), m_roots_tmp(m_am),
@ -148,6 +151,7 @@ namespace nlsat {
m_simplify_cores = false; m_simplify_cores = false;
m_full_dimensional = false; m_full_dimensional = false;
m_minimize_cores = false; m_minimize_cores = false;
m_signed_project = false;
} }
~imp() { ~imp() {
@ -212,7 +216,7 @@ namespace nlsat {
max_var(p) must be assigned in the current interpretation. max_var(p) must be assigned in the current interpretation.
*/ */
int sign(polynomial_ref const & p) { 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))); SASSERT(max_var(p) == null_var || m_assignment.is_assigned(max_var(p)));
return m_am.eval_sign_at(p, m_assignment); 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) { 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()); 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)) { if (m_pm.degree(p, y) == 1 && m_pm.const_coeff(p, y, 1, c)) {
SASSERT(!m_pm.m().is_zero(c)); SASSERT(!m_pm.m().is_zero(c));
// literal can be expressed using a linear ineq_atom mk_linear_root(k, y, i, p, m_pm.m().is_neg(c));
polynomial_ref p_prime(m_pm); return true;
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);
} }
else { return false;
b = m_solver.mk_root_atom(k, y, i, p); }
lsign = 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 polynomial_ref c(m_pm);
literal l(b, lsign); c = m_pm.coeff(p, y, 1);
TRACE("nlsat_explain", tout << "adding literal\n"; display(tout, l); tout << "\n";); int s = sign(c);
add_literal(l); 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);); TRACE("nlsat_explain", tout << "[explain] result\n"; display(tout, result););
CASSERT("nlsat", check_already_added()); CASSERT("nlsat", check_already_added());
} }
void project(var x, unsigned num, literal const * ls, scoped_literal_vector & result) {
m_result = &result;
svector<literal> 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<var> 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<literal>& 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<literal> 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, explain::explain(solver & s, assignment const & x2v, polynomial::cache & u,
evaluator & ev) { atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev) {
m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev); m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev);
} }
@ -1364,10 +1815,26 @@ namespace nlsat {
m_imp->m_factor = f; 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) { void explain::operator()(unsigned n, literal const * ls, scoped_literal_vector & result) {
(*m_imp)(n, ls, 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 #ifdef Z3DEBUG
@ -1398,3 +1865,4 @@ void pp_lit(nlsat::explain::imp & ex, nlsat::literal l) {
std::cout << std::endl; std::cout << std::endl;
} }
#endif #endif

View file

@ -22,18 +22,20 @@ Revision History:
#include"nlsat_solver.h" #include"nlsat_solver.h"
#include"nlsat_scoped_literal_vector.h" #include"nlsat_scoped_literal_vector.h"
#include"polynomial_cache.h" #include"polynomial_cache.h"
#include"algebraic_numbers.h"
namespace nlsat { namespace nlsat {
class evaluator; class evaluator;
class explain { class explain {
public: public:
struct imp; struct imp;
private: private:
imp * m_imp; imp * m_imp;
public: public:
explain(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq, explain(solver & s, assignment const & x2v, polynomial::cache & u,
evaluator & ev); atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev);
~explain(); ~explain();
void reset(); void reset();
@ -41,6 +43,7 @@ namespace nlsat {
void set_full_dimensional(bool f); void set_full_dimensional(bool f);
void set_minimize_cores(bool f); void set_minimize_cores(bool f);
void set_factor(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. \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 - s_1, ..., s_m are false in the current interpretation
*/ */
void operator()(unsigned n, literal const * ls, scoped_literal_vector & result); 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);
}; };
}; };

View file

@ -64,6 +64,13 @@ namespace nlsat {
for (unsigned i = 0; i < sz; i++) for (unsigned i = 0; i < sz; i++)
push_back(ls[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);
}
}; };

View file

@ -66,7 +66,6 @@ namespace nlsat {
typedef polynomial::cache cache; typedef polynomial::cache cache;
typedef ptr_vector<interval_set> interval_set_vector; typedef ptr_vector<interval_set> interval_set_vector;
solver & m_solver;
reslimit& m_rlimit; reslimit& m_rlimit;
small_object_allocator m_allocator; small_object_allocator m_allocator;
unsynch_mpq_manager m_qm; unsynch_mpq_manager m_qm;
@ -159,8 +158,7 @@ namespace nlsat {
unsigned m_stages; unsigned m_stages;
unsigned m_irrational_assignments; // number of irrational witnesses unsigned m_irrational_assignments; // number of irrational witnesses
imp(solver & s, reslimit& rlim, params_ref const & p): imp(solver& s, reslimit& rlim, params_ref const & p):
m_solver(s),
m_rlimit(rlim), m_rlimit(rlim),
m_allocator("nlsat"), m_allocator("nlsat"),
m_pm(rlim, m_qm, &m_allocator), m_pm(rlim, m_qm, &m_allocator),
@ -168,7 +166,7 @@ namespace nlsat {
m_am(rlim, m_qm, p, &m_allocator), m_am(rlim, m_qm, p, &m_allocator),
m_asm(*this, m_allocator), m_asm(*this, m_allocator),
m_assignment(m_am), 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_ism(m_evaluator.ism()),
m_num_bool_vars(0), m_num_bool_vars(0),
m_display_var(m_perm), m_display_var(m_perm),
@ -183,12 +181,7 @@ namespace nlsat {
} }
~imp() { ~imp() {
m_explain.reset(); reset();
m_lemma.reset();
m_lazy_clause.reset();
undo_until_size(0);
del_clauses();
del_unref_atoms();
} }
void mk_true_bvar() { void mk_true_bvar() {
@ -216,6 +209,18 @@ namespace nlsat {
m_am.updt_params(p.p); 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() { void checkpoint() {
if (!m_rlimit.inc()) throw solver_exception(m_rlimit.get_cancel_msg()); 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); 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) { void inc_ref(bool_var b) {
TRACE("ref", tout << "inc: " << b << "\n";);
if (b == null_bool_var) if (b == null_bool_var)
return; return;
if (m_atoms[b] == 0) if (m_atoms[b] == 0)
@ -264,6 +270,7 @@ namespace nlsat {
} }
void dec_ref(bool_var b) { void dec_ref(bool_var b) {
TRACE("ref", tout << "dec: " << b << "\n";);
if (b == null_bool_var) if (b == null_bool_var)
return; return;
atom * a = m_atoms[b]; atom * a = m_atoms[b];
@ -412,6 +419,34 @@ namespace nlsat {
return x; return x;
} }
svector<bool> 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) { void deallocate(ineq_atom * a) {
unsigned obj_sz = ineq_atom::get_obj_size(a->size()); unsigned obj_sz = ineq_atom::get_obj_size(a->size());
a->~ineq_atom(); a->~ineq_atom();
@ -491,6 +526,7 @@ namespace nlsat {
TRACE("nlsat_table_bug", ineq_atom::hash_proc h; 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";); 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); 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); SASSERT(old_atom->max_var() == max);
if (old_atom != new_atom) { if (old_atom != new_atom) {
deallocate(new_atom); deallocate(new_atom);
@ -726,7 +762,7 @@ namespace nlsat {
template<typename Predicate> template<typename Predicate>
void undo_until(Predicate const & pred) { void undo_until(Predicate const & pred) {
while (pred()) { while (pred() && !m_trail.empty()) {
trail & t = m_trail.back(); trail & t = m_trail.back();
switch (t.m_kind) { switch (t.m_kind) {
case trail::BVAR_ASSIGNMENT: case trail::BVAR_ASSIGNMENT:
@ -803,6 +839,14 @@ namespace nlsat {
SASSERT(m_bvalues[b] == l_undef); 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 \brief Create a new scope level
*/ */
@ -868,10 +912,11 @@ namespace nlsat {
var max = a->max_var(); var max = a->max_var();
if (!m_assignment.is_assigned(max)) if (!m_assignment.is_assigned(max))
return l_undef; 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"; val = to_lbool(m_evaluator.eval(a, l.sign()));
display_assignment(tout); TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n";
display_bool_assignment(tout);); tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n";
return to_lbool(m_evaluator.eval(a, l.sign())); display_assignment(tout););
return val;
} }
/** /**
@ -880,8 +925,10 @@ namespace nlsat {
bool is_satisfied(clause const & cls) const { bool is_satisfied(clause const & cls) const {
unsigned sz = cls.size(); unsigned sz = cls.size();
for (unsigned i = 0; i < sz; i++) { for (unsigned i = 0; i < sz; i++) {
if (const_cast<imp*>(this)->value(cls[i]) == l_true) if (const_cast<imp*>(this)->value(cls[i]) == l_true) {
TRACE("value_bug:", tout << cls[i] << " := true\n";);
return true; return true;
}
} }
return false; return false;
} }
@ -984,8 +1031,10 @@ namespace nlsat {
If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 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) { 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 return true; // ignore lemmas in super lazy mode
}
SASSERT(m_xk == max_var(cls)); SASSERT(m_xk == max_var(cls));
unsigned num_undef = 0; // number of undefined literals unsigned num_undef = 0; // number of undefined literals
unsigned first_undef = UINT_MAX; // position of the first undefined literal 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 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) { 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)) if (is_satisfied(cls))
return true; return true;
if (m_xk == null_var) if (m_xk == null_var)
@ -1151,7 +1200,7 @@ namespace nlsat {
} }
TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";);
if (m_bk == null_bool_var && m_xk >= num_vars()) { 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. return l_true; // all variables were assigned, and all clauses were satisfied.
} }
TRACE("nlsat", tout << "processing variable "; TRACE("nlsat", tout << "processing variable ";
@ -1186,23 +1235,102 @@ namespace nlsat {
lbool check() { lbool check() {
TRACE("nlsat_smt2", display_smt2(tout);); TRACE("nlsat_smt2", display_smt2(tout););
TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); 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()); 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(); shuffle_vars();
reordered = true;
} }
else if (m_reorder) { else if (m_reorder) {
heuristic_reorder(); heuristic_reorder();
reordered = true;
} }
sort_watched_clauses(); sort_watched_clauses();
lbool r = search(); lbool r = search();
CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout); display_bool_assignment(tout);); CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout););
if (m_reorder) if (reordered)
restore_order(); 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; 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<assumption, false> 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<assumption, false> 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 // Conflict Resolution
@ -1447,7 +1575,7 @@ namespace nlsat {
TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause); tout << "\n"; 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 << "<null>"; tout << "\n"; tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << "<null>"; tout << "\n";
tout << "scope_lvl: " << scope_lvl() << "\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; // static unsigned counter = 0;
// counter++; // counter++;
@ -1588,7 +1716,7 @@ namespace nlsat {
conflict_clause = new_cls; conflict_clause = new_cls;
goto start; goto start;
} }
TRACE("nlsat_resolve_done", display_assignment(tout); display_bool_assignment(tout);); TRACE("nlsat_resolve_done", display_assignment(tout););
return true; return true;
} }
@ -1801,6 +1929,15 @@ namespace nlsat {
reorder(p.size(), p.c_ptr()); 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. \brief Reorder variables using the giving permutation.
p maps internal variables to their new positions p maps internal variables to their new positions
@ -1921,6 +2058,9 @@ namespace nlsat {
void reinit_cache() { void reinit_cache() {
reinit_cache(m_clauses); reinit_cache(m_clauses);
reinit_cache(m_learned); 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) { void reinit_cache(clause_vector const & cs) {
unsigned sz = cs.size(); unsigned sz = cs.size();
@ -1934,10 +2074,13 @@ namespace nlsat {
} }
void reinit_cache(literal l) { void reinit_cache(literal l) {
bool_var b = l.var(); bool_var b = l.var();
atom * a = m_atoms[b]; reinit_cache(m_atoms[b]);
if (a == 0) }
return; void reinit_cache(atom* a) {
if (a->is_ineq_atom()) { if (a == 0) {
}
else if (a->is_ineq_atom()) {
var max = 0; var max = 0;
unsigned sz = to_ineq_atom(a)->size(); unsigned sz = to_ineq_atom(a)->size();
for (unsigned i = 0; i < sz; i++) { 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++) { for (var x = 0; x < num_vars(); x++) {
if (m_assignment.is_assigned(x)) { if (m_assignment.is_assigned(x)) {
proc(out, 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(); unsigned sz = m_atoms.size();
for (bool_var b = 0; b < sz; b++) { for (bool_var b = 0; b < sz; b++) {
if (m_atoms[b] == 0 && m_bvalues[b] != l_undef) { if (m_atoms[b] == 0 && m_bvalues[b] != l_undef) {
@ -2112,12 +2255,13 @@ namespace nlsat {
return !first; return !first;
} }
void display_assignment(std::ostream & out) const { void display_num_assignment(std::ostream & out) const {
display_assignment(out, m_display_var); display_num_assignment(out, m_display_var);
} }
void display_bool_assignment(std::ostream & out) const { void display_assignment(std::ostream& out) const {
display_bool_assignment(out, m_display_var); 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 { 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 { void display(std::ostream & out) const {
display(out, m_display_var); display(out, m_display_var);
display_assignment(out);
} }
void display_vars(std::ostream & out) const { void display_vars(std::ostream & out) const {
@ -2562,6 +2707,20 @@ namespace nlsat {
return m_imp->check(); 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) { void solver::collect_param_descrs(param_descrs & d) {
algebraic_numbers::manager::collect_param_descrs(d); algebraic_numbers::manager::collect_param_descrs(d);
nlsat_params::collect_param_descrs(d); nlsat_params::collect_param_descrs(d);
@ -2583,6 +2742,10 @@ namespace nlsat {
m_imp->m_display_var.m_proc = &proc; 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 { bool solver::is_int(var x) const {
return m_imp->is_int(x); return m_imp->is_int(x);
} }
@ -2591,10 +2754,61 @@ namespace nlsat {
return m_imp->mk_bool_var(); return m_imp->mk_bool_var();
} }
literal solver::mk_true() {
return literal(0, false);
}
atom * solver::bool_var2atom(bool_var b) { atom * solver::bool_var2atom(bool_var b) {
return m_imp->m_atoms[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<lbool>& vs) {
vs.reset();
vs.append(m_imp->m_bvalues);
}
void solver::set_bvalues(svector<lbool> 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) { var solver::mk_var(bool is_int) {
return m_imp->mk_var(is_int); return m_imp->mk_var(is_int);
} }
@ -2631,10 +2845,21 @@ namespace nlsat {
m_imp->display(out, l); 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 { void solver::display(std::ostream & out, var x) const {
m_imp->m_display_var(out, x); 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 { display_var_proc const & solver::display_proc() const {
return m_imp->m_display_var; return m_imp->m_display_var;
} }

View file

@ -28,6 +28,9 @@ Revision History:
namespace nlsat { namespace nlsat {
class evaluator;
class explain;
class solver { class solver {
struct imp; struct imp;
imp * m_imp; imp * m_imp;
@ -64,6 +67,8 @@ namespace nlsat {
*/ */
bool_var mk_bool_var(); bool_var mk_bool_var();
literal mk_true();
/** /**
\brief Create a real/integer variable. \brief Create a real/integer variable.
*/ */
@ -121,6 +126,48 @@ namespace nlsat {
*/ */
atom * bool_var2atom(bool_var b); 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<lbool>& vs);
void set_bvalues(svector<lbool> const& vs);
/**
\brief reorder variables.
*/
void reorder(unsigned sz, var const* permutation);
void restore_order();
/** /**
\brief Return number of integer/real variables \brief Return number of integer/real variables
*/ */
@ -135,6 +182,8 @@ namespace nlsat {
// ----------------------- // -----------------------
lbool check(); lbool check();
lbool check(literal_vector& assumptions);
// ----------------------- // -----------------------
// //
// Model // Model
@ -154,6 +203,7 @@ namespace nlsat {
void updt_params(params_ref const & p); void updt_params(params_ref const & p);
static void collect_param_descrs(param_descrs & d); static void collect_param_descrs(param_descrs & d);
void reset();
void collect_statistics(statistics & st); void collect_statistics(statistics & st);
void reset_statistics(); void reset_statistics();
void display_status(std::ostream & out) const; 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, 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 \brief Display variable
*/ */

View file

@ -111,6 +111,21 @@ namespace nlsat {
struct eq_proc { bool operator()(ineq_atom const * a1, ineq_atom const * a2) const; }; 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 { class root_atom : public atom {
friend class solver; friend class solver;
var m_x; var m_x;

View file

@ -29,7 +29,9 @@ Notes:
#include"expr2var.h" #include"expr2var.h"
#include"arith_decl_plugin.h" #include"arith_decl_plugin.h"
#include"tactic.h" #include"tactic.h"
#include"ast_smt2_pp.h" #include"ast_pp.h"
#include"polynomial.h"
#include"algebraic_numbers.h"
struct goal2nlsat::imp { struct goal2nlsat::imp {
struct nlsat_expr2polynomial : public expr2polynomial { struct nlsat_expr2polynomial : public expr2polynomial {
@ -124,7 +126,7 @@ struct goal2nlsat::imp {
m_qm.neg(d2); m_qm.neg(d2);
polynomial_ref p(m_pm); polynomial_ref p(m_pm);
p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2); 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)) { if (is_const(p)) {
int sign; int sign;
if (is_zero(p)) if (is_zero(p))
@ -192,7 +194,7 @@ struct goal2nlsat::imp {
switch (to_app(f)->get_decl_kind()) { switch (to_app(f)->get_decl_kind()) {
case OP_TRUE: case OP_TRUE:
case OP_FALSE: 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"); throw tactic_exception("apply simplify before applying nlsat");
case OP_AND: case OP_AND:
case OP_OR: case OP_OR:
@ -257,6 +259,7 @@ struct goal2nlsat::imp {
process(g.form(i), g.dep(i)); process(g.form(i), g.dep(i));
} }
} }
}; };
struct goal2nlsat::scoped_set_imp { struct goal2nlsat::scoped_set_imp {
@ -290,3 +293,153 @@ void goal2nlsat::operator()(goal const & g, params_ref const & p, nlsat::solver
local_imp(g); local_imp(g);
} }
struct nlsat2goal::imp {
ast_manager& m;
arith_util a;
u_map<expr*> const* m_x2t;
public:
imp(ast_manager& m):m(m),a(m) {}
expr_ref operator()(nlsat::solver& s, u_map<expr*> const& b2a, u_map<expr*> 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<expr*> const& b2a, u_map<expr*> const& x2t, nlsat::literal l) {
return (*m_imp)(s, b2a, x2t, l);
}

View file

@ -57,16 +57,15 @@ class nlsat2goal {
struct imp; struct imp;
imp * m_imp; imp * m_imp;
public: public:
nlsat2goal(); nlsat2goal(ast_manager& m);
~nlsat2goal(); ~nlsat2goal();
static void collect_param_descrs(param_descrs & r); static void collect_param_descrs(param_descrs & r);
/** /**
\brief Translate the state of the nlsat engine back into a goal. \brief Translate a literal into a formula.
*/ */
void operator()(nlsat::solver const & s, expr2var const & a2b, expr2var const & t2x, expr_ref operator()(nlsat::solver& s, u_map<expr*> const& b2a, u_map<expr*> const& x2t, nlsat::literal l);
params_ref const & p, goal & g, model_converter_ref & mc);
}; };

View file

@ -514,12 +514,10 @@ namespace opt {
void context::init_solver() { void context::init_solver() {
setup_arith_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_opt_solver = alloc(opt_solver, m, m_params, m_fm); m_solver = m_opt_solver.get();
m_opt_solver->set_logic(m_logic);
m_solver = m_opt_solver.get();
}
if (opt_params(m_params).priority() == symbol("pareto") || if (opt_params(m_params).priority() == symbol("pareto") ||
(opt_params(m_params).priority() == symbol("lex") && m_objectives.size() > 1)) { (opt_params(m_params).priority() == symbol("lex") && m_objectives.size() > 1)) {
m_opt_solver->ensure_pb(); m_opt_solver->ensure_pb();
@ -556,10 +554,7 @@ namespace opt {
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
m_sat_solver->assert_expr(get_solver().get_assertion(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) { void context::enable_sls(bool force) {
@ -649,10 +644,7 @@ namespace opt {
void context::add_maxsmt(symbol const& id, unsigned index) { void context::add_maxsmt(symbol const& id, unsigned index) {
maxsmt* ms = alloc(maxsmt, *this, index); maxsmt* ms = alloc(maxsmt, *this, index);
ms->updt_params(m_params); 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 { bool context::is_numeral(expr* e, rational & n) const {
@ -1277,10 +1269,7 @@ namespace opt {
} }
void context::set_simplify(tactic* tac) { void context::set_simplify(tactic* tac) {
#pragma omp critical (opt_context) m_simplify = tac;
{
m_simplify = tac;
}
} }
void context::clear_state() { void context::clear_state() {
@ -1290,10 +1279,7 @@ namespace opt {
} }
void context::set_pareto(pareto_base* p) { 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 { void context::collect_statistics(statistics& stats) const {

928
src/qe/nlqsat.cpp Normal file
View file

@ -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<lbool> m_bmodel;
nlsat::assignment m_rmodel0;
svector<lbool> m_bmodel0;
bool m_valid_model;
vector<nlsat::var_vector> m_bound_rvars;
vector<svector<nlsat::bool_var> > m_bound_bvars;
vector<nlsat::scoped_literal_vector> m_preds;
svector<max_level> m_rvar2level;
u_map<max_level> m_bvar2level;
expr2var m_a2b, m_t2x;
u_map<expr*> m_b2a, m_x2t;
volatile bool m_cancel;
stats m_stats;
statistics m_st;
obj_hashtable<expr> m_free_vars;
expr_ref_vector m_answer;
expr_safe_replace m_answer_simplify;
nlsat::literal_vector m_assumptions;
u_map<expr*> 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<class S, class T>
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<app_ref_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<nlsat::bool_var>());
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<app_ref_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<expr> 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<expr> 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);
}

38
src/qe/nlqsat.h Normal file
View file

@ -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

View file

@ -36,7 +36,6 @@ Revision History:
#include "expr_functors.h" #include "expr_functors.h"
#include "quant_hoist.h" #include "quant_hoist.h"
#include "bool_rewriter.h" #include "bool_rewriter.h"
#include "qe_util.h"
#include "th_rewriter.h" #include "th_rewriter.h"
#include "smt_kernel.h" #include "smt_kernel.h"
#include "model_evaluator.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(); ast_manager& m = new_body.get_manager();
expr_ref tmp(m); expr_ref tmp(m);
unsigned nd = q->get_num_decls(); unsigned nd = q->get_num_decls();

View file

@ -223,6 +223,8 @@ namespace qe {
qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, smt_params& p); 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 { class def_vector {
func_decl_ref_vector m_vars; func_decl_ref_vector m_vars;
expr_ref_vector m_defs; expr_ref_vector m_defs;

File diff suppressed because it is too large Load diff

View file

@ -9,16 +9,33 @@ Copyright (c) 2015 Microsoft Corporation
#define QE_ARITH_H_ #define QE_ARITH_H_
#include "model.h" #include "model.h"
#include "arith_decl_plugin.h"
#include "qe_mbp.h"
namespace qe { namespace qe {
/** /**
Loos-Weispfenning model-based projection for a basic conjunction. Loos-Weispfenning model-based projection for a basic conjunction.
Lits is a vector of literals. Lits is a vector of literals.
return vector of variables that could not be projected. 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 #endif

View file

@ -32,6 +32,7 @@ Revision History:
#include "nlarith_util.h" #include "nlarith_util.h"
#include "model_evaluator.h" #include "model_evaluator.h"
#include "smt_kernel.h" #include "smt_kernel.h"
#include "qe_arith.h"
namespace qe { namespace qe {
@ -266,23 +267,8 @@ namespace qe {
// //
// match 0 == p mod k, p mod k == 0 // match 0 == p mod k, p mod k == 0
// //
bool is_divides(app* e, numeral& k, expr_ref& p) { bool is_divides(expr* e, numeral& k, expr_ref& p) {
expr* e1, *e2; return qe::is_divides(m_arith, e, k, p);
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_not_divides(app* e, app_ref& n, numeral& k, expr_ref& p) { bool is_not_divides(app* e, app_ref& n, numeral& k, expr_ref& p) {

427
src/qe/qe_arrays.cpp Normal file
View file

@ -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<expr> 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<contains_app> 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<app> 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<indices> 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<indices>& 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<indices> 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();
}
};

42
src/qe/qe_arrays.h Normal file
View file

@ -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

312
src/qe/qe_datatypes.cpp Normal file
View file

@ -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<contains_app> 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<func_decl> 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<func_decl> 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<func_decl> 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<func_decl> 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<expr> 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();
}
}

42
src/qe/qe_datatypes.h Normal file
View file

@ -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

View file

@ -31,7 +31,6 @@ Revision History:
#include "var_subst.h" #include "var_subst.h"
#include "uint_set.h" #include "uint_set.h"
#include "ast_util.h" #include "ast_util.h"
#include "qe_util.h"
#include "th_rewriter.h" #include "th_rewriter.h"
#include "for_each_expr.h" #include "for_each_expr.h"
#include "expr_safe_replace.h" #include "expr_safe_replace.h"

369
src/qe/qe_mbp.cpp Normal file
View file

@ -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<expr, expr*> 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<expr, expr*> 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<project_plugin> 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<project_plugin>());
}
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);
}

76
src/qe/qe_mbp.h Normal file
View file

@ -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

View file

@ -22,6 +22,7 @@ Revision History:
#include"qe.h" #include"qe.h"
class qe_tactic : public tactic { class qe_tactic : public tactic {
statistics m_st;
struct imp { struct imp {
ast_manager & m; ast_manager & m;
smt_params m_fparams; smt_params m_fparams;
@ -78,10 +79,19 @@ class qe_tactic : public tactic {
g->update(i, new_f, new_pr, g->dep(i)); g->update(i, new_f, new_pr, g->dep(i));
} }
g->inc_depth(); g->inc_depth();
g->elim_true();
result.push_back(g.get()); result.push_back(g.get());
TRACE("qe", g->display(tout);); TRACE("qe", g->display(tout););
SASSERT(g->is_well_sorted()); SASSERT(g->is_well_sorted());
} }
void collect_statistics(statistics & st) const {
m_qe.collect_statistics(st);
}
void reset_statistics() {
}
}; };
imp * m_imp; imp * m_imp;
@ -117,8 +127,20 @@ public:
proof_converter_ref & pc, proof_converter_ref & pc,
expr_dependency_ref & core) { expr_dependency_ref & core) {
(*m_imp)(in, result, mc, pc, 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() { virtual void cleanup() {
ast_manager & m = m_imp->m; ast_manager & m = m_imp->m;
dealloc(m_imp); dealloc(m_imp);

View file

@ -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;
}
}

View file

@ -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

1177
src/qe/qsat.cpp Normal file

File diff suppressed because it is too large Load diff

135
src/qe/qsat.h Normal file
View file

@ -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<app_ref_vector> m_preds;
expr_ref_vector m_asms;
unsigned_vector m_asms_lim;
obj_map<expr, expr*> m_pred2lit; // maintain definitions of predicates.
obj_map<expr, app*> m_lit2pred; // maintain reverse mapping to predicates
obj_map<expr, app*> m_asm2pred; // maintain map from assumptions to predicates
obj_map<expr, expr*> m_pred2asm; // predicates |-> assumptions
expr_ref_vector m_trail;
filter_model_converter_ref m_fmc;
ptr_vector<expr> todo;
obj_map<expr, max_level> m_elevel;
obj_map<func_decl, max_level> m_flevel;
template <typename T>
void dec_keys(obj_map<expr, T*>& map) {
typename obj_map<expr, T*>::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<expr, expr*> 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

View file

@ -257,8 +257,10 @@ public:
if (m_preprocess) { if (m_preprocess) {
m_preprocess->reset(); m_preprocess->reset();
} }
if (!m_bb_rewriter) {
m_bb_rewriter = alloc(bit_blaster_rewriter, m, m_params);
}
params_ref simp2_p = 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("som", true);
simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("pull_cheap_ite", true);
simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("push_ite_bv", false);

View file

@ -509,7 +509,7 @@ struct sat2goal::imp {
// This information may be stored as a vector of pairs. // This information may be stored as a vector of pairs.
// The mapping is only created during the model conversion. // The mapping is only created during the model conversion.
expr_ref_vector m_var2expr; expr_ref_vector m_var2expr;
ref<filter_model_converter> 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): sat_model_converter(ast_manager & m):
m_var2expr(m) { m_var2expr(m) {

View file

@ -66,7 +66,7 @@ void display_usage() {
#ifdef Z3GITHASH #ifdef Z3GITHASH
std::cout << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH); std::cout << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH);
#endif #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 << "Usage: z3 [options] [-file:]file\n";
std::cout << "\nInput format:\n"; std::cout << "\nInput format:\n";
std::cout << " -smt use parser for SMT input format.\n"; std::cout << " -smt use parser for SMT input format.\n";

View file

@ -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(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); MK_SIMPLIFIER(elim_bvs_from_quantifiers, bv_elim_star functor(m_manager), "bv_elim", "eliminate-bit-vectors-from-quantifiers", true);

View file

@ -55,7 +55,6 @@ class asserted_formulas {
maximise_bv_sharing m_bv_sharing; maximise_bv_sharing m_bv_sharing;
bool m_inconsistent; bool m_inconsistent;
// qe::expr_quant_elim_star1 m_quant_elim;
struct scope { struct scope {
unsigned m_asserted_formulas_lim; unsigned m_asserted_formulas_lim;
@ -84,7 +83,6 @@ class asserted_formulas {
void eliminate_and(); void eliminate_and();
void refine_inj_axiom(); void refine_inj_axiom();
bool cheap_quant_fourier_motzkin(); bool cheap_quant_fourier_motzkin();
bool quant_elim();
void apply_distribute_forall(); void apply_distribute_forall();
bool apply_bit2int(); bool apply_bit2int();
void lift_ite(); void lift_ite();

View file

@ -25,19 +25,17 @@ Revision History:
#include"well_sorted.h" #include"well_sorted.h"
#include"used_symbols.h" #include"used_symbols.h"
#include"model_v2_pp.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), model_core(m),
m_simplifier(s),
m_afid(m.mk_family_id(symbol("array"))), m_afid(m.mk_family_id(symbol("array"))),
m_eval(*this) { m_eval(*this),
m_rewrite(m) {
register_factory(alloc(basic_factory, m)); register_factory(alloc(basic_factory, m));
m_user_sort_factory = alloc(user_sort_factory, m); m_user_sort_factory = alloc(user_sort_factory, m);
register_factory(m_user_sort_factory); register_factory(m_user_sort_factory);
m_model_partial = model_params(p).partial(); 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; 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<basic_simplifier_plugin*>(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 { bool proto_model::is_select_of_model_value(expr* e) const {
return 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. 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) { bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) {
if (m_use_new_eval) { m_eval.set_model_completion(model_completion);
m_eval.set_model_completion(model_completion); try {
try { m_eval(e, result);
m_eval(e, result); return true;
return true;
}
catch (model_evaluator_exception & ex) {
(void)ex;
TRACE("model_evaluator", tout << ex.msg() << "\n";);
return false;
}
} }
catch (model_evaluator_exception & ex) {
bool is_ok = true; (void)ex;
SASSERT(is_well_sorted(m_manager, e)); TRACE("model_evaluator", tout << ex.msg() << "\n";);
TRACE("model_eval", tout << mk_pp(e, m_manager) << "\n"; return false;
tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n";);
obj_map<expr, expr*> eval_cache;
expr_ref_vector trail(m_manager);
sbuffer<std::pair<expr*, expr*>, 128> todo;
ptr_buffer<expr> args;
expr * null = static_cast<expr*>(0);
todo.push_back(std::make_pair(e, null));
expr * a;
expr * expanded_a;
while (!todo.empty()) {
std::pair<expr *, expr *> & 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;
}
}
} }
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)) if (m_aux_decls.contains(f))
found_aux_fs.insert(f); found_aux_fs.insert(f);
expr_ref new_t(m_manager); 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()) if (t != new_t.get())
trail.push_back(new_t); trail.push_back(new_t);
todo.pop_back(); todo.pop_back();

View file

@ -32,23 +32,22 @@ Revision History:
#include"model_evaluator.h" #include"model_evaluator.h"
#include"value_factory.h" #include"value_factory.h"
#include"plugin_manager.h" #include"plugin_manager.h"
#include"simplifier.h"
#include"arith_decl_plugin.h" #include"arith_decl_plugin.h"
#include"func_decl_dependencies.h" #include"func_decl_dependencies.h"
#include"model.h" #include"model.h"
#include"params.h" #include"params.h"
#include"th_rewriter.h"
class proto_model : public model_core { class proto_model : public model_core {
plugin_manager<value_factory> m_factories; plugin_manager<value_factory> m_factories;
user_sort_factory * m_user_sort_factory; 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 family_id m_afid; //!< array family id: hack for displaying models in V1.x style
func_decl_set m_aux_decls; func_decl_set m_aux_decls;
ptr_vector<expr> m_tmp; ptr_vector<expr> m_tmp;
model_evaluator m_eval; model_evaluator m_eval;
th_rewriter m_rewrite;
bool m_model_partial; bool m_model_partial;
bool m_use_new_eval;
expr * mk_some_interp_for(func_decl * d); 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; bool is_select_of_model_value(expr* e) const;
public: 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() {} virtual ~proto_model() {}
void register_factory(value_factory * f) { m_factories.register_plugin(f); } void register_factory(value_factory * f) { m_factories.register_plugin(f); }

View file

@ -122,6 +122,10 @@ namespace smt {
*/ */
lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0); 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. \brief Return the model associated with the last check command.
*/ */

View file

@ -49,7 +49,7 @@ namespace smt {
void model_generator::init_model() { void model_generator::init_model() {
SASSERT(!m_model); SASSERT(!m_model);
// PARAM-TODO smt_params ---> params_ref // 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<theory>::const_iterator it = m_context->begin_theories(); ptr_vector<theory>::const_iterator it = m_context->begin_theories();
ptr_vector<theory>::const_iterator end = m_context->end_theories(); ptr_vector<theory>::const_iterator end = m_context->end_theories();
for (; it != end; ++it) { for (; it != end; ++it) {

View file

@ -315,10 +315,7 @@ namespace smt {
return m_var2enode_lim[m_var2enode_lim.size() - num_scopes]; return m_var2enode_lim[m_var2enode_lim.size() - num_scopes];
} }
virtual void display(std::ostream & out) const { virtual void display(std::ostream & out) const = 0;
out << "Theory " << static_cast<int>(get_id()) << typeid(*this).name() << " does not have a display method\n";
display_var2enode(out);
}
virtual void display_var2enode(std::ostream & out) const; virtual void display_var2enode(std::ostream & out) const;

View file

@ -43,6 +43,7 @@ namespace smt {
virtual bool build_models() const { virtual bool build_models() const {
return false; return false;
} }
virtual void display(std::ostream& out) const {}
public: public:
theory_dummy(family_id fid, char const * name); theory_dummy(family_id fid, char const * name);

View file

@ -122,6 +122,7 @@ namespace smt {
virtual void new_diseq_eh(theory_var, theory_var) {} virtual void new_diseq_eh(theory_var, theory_var) {}
virtual theory* mk_fresh(context* new_ctx) { return alloc(theory_seq_empty, new_ctx->get_manager()); } virtual 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 char const * get_name() const { return "seq-empty"; }
virtual void display(std::ostream& out) const {}
public: public:
theory_seq_empty(ast_manager& m):theory(m.mk_family_id("seq")), m_used(false) {} theory_seq_empty(ast_manager& m):theory(m.mk_family_id("seq")), m_used(false) {}
virtual void init_model(model_generator & mg) { virtual void init_model(model_generator & mg) {

View file

@ -94,6 +94,7 @@ namespace smt {
virtual bool internalize_term(app * term) { return false; } virtual bool internalize_term(app * term) { return false; }
virtual void new_eq_eh(theory_var v1, theory_var v2) { } virtual void new_eq_eh(theory_var v1, theory_var v2) { }
virtual void new_diseq_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 { virtual void collect_statistics(::statistics & st) const {
st.update("wmaxsat num blocks", m_stats.m_num_blocks); st.update("wmaxsat num blocks", m_stats.m_num_blocks);

View file

@ -194,7 +194,7 @@ public:
m_use_solver1_results = false; m_use_solver1_results = false;
if (get_num_assumptions() != 0 || if (get_num_assumptions() != 0 ||
num_assumptions > 0 || // assumptions were provided num_assumptions > 0 || // assumptions were provided
m_ignore_solver1) { m_ignore_solver1) {
// must use incremental solver // must use incremental solver
switch_inc_mode(); switch_inc_mode();

View file

@ -23,12 +23,11 @@ Notes:
#include"ast_smt2_pp.h" #include"ast_smt2_pp.h"
solver_na2as::solver_na2as(ast_manager & m): solver_na2as::solver_na2as(ast_manager & m):
m_manager(m) { m(m),
m_assumptions(m) {
} }
solver_na2as::~solver_na2as() { solver_na2as::~solver_na2as() {}
restore_assumptions(0);
}
void solver_na2as::assert_expr(expr * t, expr * a) { void solver_na2as::assert_expr(expr * t, expr * a) {
if (a == 0) { if (a == 0) {
@ -36,20 +35,19 @@ void solver_na2as::assert_expr(expr * t, expr * a) {
} }
else { else {
SASSERT(is_uninterp_const(a)); SASSERT(is_uninterp_const(a));
SASSERT(m_manager.is_bool(a)); SASSERT(m.is_bool(a));
TRACE("solver_na2as", tout << "asserting\n" << mk_ismt2_pp(t, m_manager) << "\n" << mk_ismt2_pp(a, m_manager) << "\n";); TRACE("solver_na2as", tout << "asserting\n" << mk_ismt2_pp(t, m) << "\n" << mk_ismt2_pp(a, m) << "\n";);
m_manager.inc_ref(a);
m_assumptions.push_back(a); m_assumptions.push_back(a);
expr_ref new_t(m_manager); expr_ref new_t(m);
new_t = m_manager.mk_implies(a, t); new_t = m.mk_implies(a, t);
assert_expr(new_t); assert_expr(new_t);
} }
} }
struct append_assumptions { struct append_assumptions {
ptr_vector<expr> & m_assumptions; expr_ref_vector & m_assumptions;
unsigned m_old_sz; unsigned m_old_sz;
append_assumptions(ptr_vector<expr> & _m_assumptions, append_assumptions(expr_ref_vector & _m_assumptions,
unsigned num_assumptions, unsigned num_assumptions,
expr * const * assumptions): expr * const * assumptions):
m_assumptions(_m_assumptions) { m_assumptions(_m_assumptions) {
@ -82,10 +80,6 @@ void solver_na2as::pop(unsigned n) {
} }
void solver_na2as::restore_assumptions(unsigned old_sz) { 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); m_assumptions.shrink(old_sz);
} }

View file

@ -25,8 +25,8 @@ Notes:
#include"solver.h" #include"solver.h"
class solver_na2as : public solver { class solver_na2as : public solver {
ast_manager & m_manager; ast_manager & m;
ptr_vector<expr> m_assumptions; expr_ref_vector m_assumptions;
unsigned_vector m_scopes; unsigned_vector m_scopes;
void restore_assumptions(unsigned old_sz); void restore_assumptions(unsigned old_sz);
public: public:

View file

@ -60,7 +60,7 @@ class nla2bv_tactic : public tactic {
expr_ref_vector m_trail; expr_ref_vector m_trail;
unsigned m_num_bits; unsigned m_num_bits;
unsigned m_default_bv_size; unsigned m_default_bv_size;
ref<filter_model_converter> m_fmc; filter_model_converter_ref m_fmc;
public: public:
imp(ast_manager & m, params_ref const& p): imp(ast_manager & m, params_ref const& p):

View file

@ -60,7 +60,7 @@ struct bv_size_reduction_tactic::imp {
obj_map<app, numeral> m_unsigned_lowers; obj_map<app, numeral> m_unsigned_lowers;
obj_map<app, numeral> m_unsigned_uppers; obj_map<app, numeral> m_unsigned_uppers;
ref<bv_size_reduction_mc> m_mc; ref<bv_size_reduction_mc> m_mc;
ref<filter_model_converter> m_fmc; filter_model_converter_ref m_fmc;
scoped_ptr<expr_replacer> m_replacer; scoped_ptr<expr_replacer> m_replacer;
bool m_produce_models; bool m_produce_models;

View file

@ -26,7 +26,7 @@ Revision History:
class propagate_values_tactic : public tactic { class propagate_values_tactic : public tactic {
struct imp { struct imp {
ast_manager & m_manager; ast_manager & m;
th_rewriter m_r; th_rewriter m_r;
scoped_ptr<expr_substitution> m_subst; scoped_ptr<expr_substitution> m_subst;
goal * m_goal; goal * m_goal;
@ -36,13 +36,15 @@ class propagate_values_tactic : public tactic {
bool m_modified; bool m_modified;
imp(ast_manager & m, params_ref const & p): imp(ast_manager & m, params_ref const & p):
m_manager(m), m(m),
m_r(m, p), m_r(m, p),
m_goal(0), m_goal(0),
m_occs(m, true /* track atoms */) { m_occs(m, true /* track atoms */) {
updt_params_core(p); updt_params_core(p);
} }
~imp() { }
void updt_params_core(params_ref const & p) { void updt_params_core(params_ref const & p) {
m_max_rounds = p.get_uint("max_rounds", 4); m_max_rounds = p.get_uint("max_rounds", 4);
} }
@ -52,31 +54,27 @@ class propagate_values_tactic : public tactic {
updt_params_core(p); updt_params_core(p);
} }
ast_manager & m() const { return m_manager; }
bool is_shared(expr * t) { bool is_shared(expr * t) {
return m_occs.is_shared(t); return m_occs.is_shared(t);
} }
bool is_shared_neg(expr * t, expr * & atom) { bool is_shared_neg(expr * t, expr * & atom) {
if (!m().is_not(t)) if (!m.is_not(t, atom))
return false; return false;
atom = to_app(t)->get_arg(0);
return is_shared(atom); return is_shared(atom);
} }
bool is_shared_eq(expr * t, expr * & lhs, expr * & value) { 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; return false;
expr * arg1 = to_app(t)->get_arg(0); if (m.is_value(arg1) && is_shared(arg2)) {
expr * arg2 = to_app(t)->get_arg(1);
if (m().is_value(arg1) && is_shared(arg2)) {
lhs = arg2; lhs = arg2;
value = arg1; value = arg1;
return true; return true;
} }
if (m().is_value(arg2) && is_shared(arg1)) { if (m.is_value(arg2) && is_shared(arg1)) {
lhs = arg1; lhs = arg1;
value = arg2; value = arg2;
return true; return true;
@ -87,15 +85,15 @@ class propagate_values_tactic : public tactic {
void push_result(expr * new_curr, proof * new_pr) { void push_result(expr * new_curr, proof * new_pr) {
if (m_goal->proofs_enabled()) { if (m_goal->proofs_enabled()) {
proof * pr = m_goal->pr(m_idx); 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()) { if (m_goal->unsat_core_enabled()) {
new_d = m_goal->dep(m_idx); new_d = m_goal->dep(m_idx);
expr_dependency * used_d = m_r.get_used_dependencies(); expr_dependency * used_d = m_r.get_used_dependencies();
if (used_d != 0) { 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(); 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); m_goal->update(m_idx, new_curr, new_pr, new_d);
if (is_shared(new_curr)) { 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; expr * atom;
if (is_shared_neg(new_curr, 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; expr * lhs, * value;
if (is_shared_eq(new_curr, 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); m_subst->insert(lhs, value, new_pr, new_d);
} }
} }
void process_current() { void process_current() {
expr * curr = m_goal->form(m_idx); expr * curr = m_goal->form(m_idx);
expr_ref new_curr(m()); expr_ref new_curr(m);
proof_ref new_pr(m()); proof_ref new_pr(m);
if (!m_subst->empty()) { if (!m_subst->empty()) {
m_r(curr, new_curr, new_pr); m_r(curr, new_curr, new_pr);
} }
else { else {
new_curr = curr; new_curr = curr;
if (m().proofs_enabled()) if (m.proofs_enabled())
new_pr = m().mk_reflexivity(curr); 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); push_result(new_curr, new_pr);
if (new_curr != curr) if (new_curr != curr)
@ -148,25 +146,26 @@ class propagate_values_tactic : public tactic {
m_goal = g.get(); m_goal = g.get();
bool forward = true; bool forward = true;
expr_ref new_curr(m()); expr_ref new_curr(m);
proof_ref new_pr(m()); proof_ref new_pr(m);
unsigned size = m_goal->size(); unsigned size = m_goal->size();
m_idx = 0; m_idx = 0;
m_modified = false; m_modified = false;
unsigned round = 0; unsigned round = 0;
if (m_goal->inconsistent()) if (m_goal->inconsistent())
goto end; goto end;
if (m_max_rounds == 0) if (m_max_rounds == 0)
goto end; 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_r.set_substitution(m_subst.get());
m_occs(*m_goal); m_occs(*m_goal);
while (true) { 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) { if (forward) {
for (; m_idx < size; m_idx++) { for (; m_idx < size; m_idx++) {
process_current(); process_current();
@ -255,15 +254,14 @@ public:
} }
virtual void cleanup() { virtual void cleanup() {
ast_manager & m = m_imp->m(); ast_manager & m = m_imp->m;
imp * d = alloc(imp, m, m_params); dealloc(m_imp);
std::swap(d, m_imp); m_imp = alloc(imp, m, m_params);
dealloc(d);
} }
}; };
tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p) { 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);
} }

View file

@ -22,7 +22,9 @@ Notes:
#include"smt_tactic.h" #include"smt_tactic.h"
#include"nnf_tactic.h" #include"nnf_tactic.h"
#include"qe_tactic.h" #include"qe_tactic.h"
#include"nlqsat.h"
#include"qfnra_nlsat_tactic.h" #include"qfnra_nlsat_tactic.h"
#include"qe_lite.h"
#include"probe_arith.h" #include"probe_arith.h"
tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) { 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), return and_then(mk_simplify_tactic(m, p),
mk_nnf_tactic(m, p), mk_nnf_tactic(m, p),
mk_propagate_values_tactic(m, p), mk_propagate_values_tactic(m, p),
//mk_qe_lite_tactic(m),
mk_qe_tactic(m, p), mk_qe_tactic(m, p),
cond(mk_is_qfnra_probe(), cond(mk_is_qfnra_probe(),
or_else(try_for(mk_qfnra_nlsat_tactic(m, p), 5000), or_else(try_for(mk_qfnra_nlsat_tactic(m, p), 5000),
try_for(mk_qfnra_nlsat_tactic(m, p1), 10000), try_for(mk_qfnra_nlsat_tactic(m, p1), 10000),
mk_qfnra_nlsat_tactic(m, p2)), 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
));
} }

View file

@ -23,6 +23,9 @@ Revision History:
#include"elim_uncnstr_tactic.h" #include"elim_uncnstr_tactic.h"
#include"qe_tactic.h" #include"qe_tactic.h"
#include"qe_sat_tactic.h" #include"qe_sat_tactic.h"
#include"qe_lite.h"
#include"qsat.h"
#include"nlqsat.h"
#include"ctx_simplify_tactic.h" #include"ctx_simplify_tactic.h"
#include"smt_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); ctx_simp_p.set_uint("max_steps", 5000000);
tactic * solve_eqs; tactic * solve_eqs;
if (disable_gaussian) if (disable_gaussian) {
solve_eqs = mk_skip_tactic(); solve_eqs = mk_skip_tactic();
else }
else {
solve_eqs = when(mk_not(mk_has_pattern_probe()), mk_solve_eqs_tactic(m)); 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. // remark: investigate if gaussian elimination is useful when patterns are not provided.
return and_then(mk_simplify_tactic(m), 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), using_params(mk_simplify_tactic(m), pull_ite_p),
solve_eqs, solve_eqs,
mk_elim_uncnstr_tactic(m), mk_elim_uncnstr_tactic(m),
mk_qe_lite_tactic(m),
mk_simplify_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) { tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) {
#if 0
tactic * st = and_then(mk_quant_preprocessor(m), tactic * st = and_then(mk_quant_preprocessor(m),
or_else(try_for(mk_smt_tactic(), 100), or_else(try_for(mk_smt_tactic(), 100),
try_for(qe::mk_sat_tactic(m), 1000), try_for(qe::mk_sat_tactic(m), 1000),
try_for(mk_smt_tactic(), 1000), try_for(mk_smt_tactic(), 1000),
and_then(mk_qe_tactic(m), mk_smt_tactic()) 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); st->updt_params(p);
return st; 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) { tactic * mk_lira_tactic(ast_manager & m, params_ref const & p) {
return mk_lra_tactic(m, p); return mk_lra_tactic(m, p);
} }

View file

@ -21,6 +21,8 @@ Notes:
#include"nlsat_evaluator.h" #include"nlsat_evaluator.h"
#include"nlsat_solver.h" #include"nlsat_solver.h"
#include"util.h" #include"util.h"
#include"nlsat_explain.h"
#include"polynomial_cache.h"
#include"rlimit.h" #include"rlimit.h"
nlsat::interval_set_ref tst_interval(nlsat::interval_set_ref const & s1, 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) { bool check_num_intervals = true) {
nlsat::interval_set_manager & ism = s1.m(); nlsat::interval_set_manager & ism = s1.m();
nlsat::interval_set_ref r(ism); nlsat::interval_set_ref r(ism);
std::cout << "------------------\n";
std::cout << "s1: " << s1 << "\n"; std::cout << "s1: " << s1 << "\n";
std::cout << "s2: " << s2 << "\n"; std::cout << "s2: " << s2 << "\n";
r = ism.mk_union(s1, s2); r = ism.mk_union(s1, s2);
@ -277,10 +278,12 @@ static void tst5() {
nlsat::assignment as(am); nlsat::assignment as(am);
small_object_allocator allocator; small_object_allocator allocator;
nlsat::interval_set_manager ism(am, 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; nlsat::var x0, x1;
x0 = pm.mk_var(); x0 = pm.mk_var();
x1 = pm.mk_var(); x1 = pm.mk_var();
nlsat::interval_set_ref i(ism);
polynomial_ref p(pm); polynomial_ref p(pm);
polynomial_ref _x0(pm), _x1(pm); polynomial_ref _x0(pm), _x1(pm);
_x0 = pm.mk_polynomial(x0); _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::bool_var b = s.mk_ineq_atom(nlsat::atom::GT, 1, _p, is_even);
nlsat::atom * a = s.bool_var2atom(b); nlsat::atom * a = s.bool_var2atom(b);
SASSERT(a != 0); SASSERT(a != 0);
nlsat::interval_set_ref i(ism);
scoped_anum zero(am); scoped_anum zero(am);
am.set(zero, 0); am.set(zero, 0);
as.set(0, zero); as.set(0, zero);
@ -302,8 +304,414 @@ static void tst5() {
std::cout << "2) " << i << "\n"; 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<nlsat::var> 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() { 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(); tst5();
std::cout << "------------------\n";
tst4(); tst4();
std::cout << "------------------\n";
tst3(); tst3();
} }

View file

@ -12,9 +12,9 @@ Copyright (c) 2015 Microsoft Corporation
#include "reg_decl_plugins.h" #include "reg_decl_plugins.h"
#include "arith_rewriter.h" #include "arith_rewriter.h"
#include "ast_pp.h" #include "ast_pp.h"
#include "qe_util.h"
#include "smt_context.h" #include "smt_context.h"
#include "expr_abstract.h" #include "expr_abstract.h"
#include "expr_safe_replace.h"
static expr_ref parse_fml(ast_manager& m, char const* str) { static expr_ref parse_fml(ast_manager& m, char const* str) {
expr_ref result(m); 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 t Real)\n"
<< "(declare-const a Real)\n" << "(declare-const a Real)\n"
<< "(declare-const b 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"; << "(assert " << str << ")\n";
std::istringstream is(buffer.str()); std::istringstream is(buffer.str());
VERIFY(parse_smt2_commands(ctx, is)); 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* 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* 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* 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))\ static char const* example6 = "(and (<= 0 (+ x z))\
(>= y x) \ (>= y x) \
@ -51,30 +58,175 @@ static char const* example6 = "(and (<= 0 (+ x z))\
(>= x 0.0)\ (>= x 0.0)\
(<= x 1.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; smt_params params;
params.m_model = true; 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; ast_manager m;
reg_decl_plugins(m); reg_decl_plugins(m);
arith_util a(m); arith_util a(m);
expr_ref fml = parse_fml(m, ex); expr_ref fml = parse_fml(m, ex);
app_ref_vector vars(m); symbol x_name("x");
expr_ref_vector lits(m); sort_ref x_sort(a.mk_real(), m);
vars.push_back(m.mk_const(symbol("x"), a.mk_real())); app_ref var(m.mk_const(x_name, x_sort), m);
flatten_and(fml, lits); test(var, fml);
}
smt::context ctx(m, params); static void testI(char const *ex) {
ctx.assert_expr(fml); ast_manager m;
lbool result = ctx.check(); reg_decl_plugins(m);
SASSERT(result == l_true); arith_util a(m);
ref<model> md; expr_ref fml = parse_fml(m, ex);
ctx.get_model(md); symbol x_name("i");
expr_ref pr = qe::arith_project(*md, vars, lits); sort_ref x_sort(a.mk_int(), m);
app_ref var(m.mk_const(x_name, x_sort), m);
test(var, fml);
}
std::cout << mk_pp(fml, m) << "\n"; static expr_ref_vector mk_ineqs(app* x, app* y, app_ref_vector const& nums) {
std::cout << mk_pp(pr, m) << "\n"; 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<expr_ref_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<expr_ref_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<expr_ref_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) { static void test2(char const *ex) {
@ -102,7 +254,7 @@ static void test2(char const *ex) {
std::cout << mk_pp(fml, m) << "\n"; 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); expr_ref_vector bound(m);
ptr_vector<sort> sorts; ptr_vector<sort> sorts;
svector<symbol> names; svector<symbol> names;
@ -114,7 +266,10 @@ static void test2(char const *ex) {
expr_abstract(m, 0, bound.size(), bound.c_ptr(), fml, fml2); expr_abstract(m, 0, bound.size(), bound.c_ptr(), fml, fml2);
fml2 = m.mk_exists(bound.size(), sorts.c_ptr(), names.c_ptr(), fml2); fml2 = m.mk_exists(bound.size(), sorts.c_ptr(), names.c_ptr(), fml2);
qe::expr_quant_elim qe(m, params); 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); qe(m.mk_true(), fml2, pr2);
std::cout << mk_pp(pr1, m) << "\n"; std::cout << mk_pp(pr1, m) << "\n";
std::cout << mk_pp(pr2, m) << "\n"; std::cout << mk_pp(pr2, m) << "\n";
@ -131,11 +286,17 @@ static void test2(char const *ex) {
} }
void tst_qe_arith() { 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); test2(example6);
return; return;
test(example1);
test(example2);
test(example3);
test(example4);
test(example5);
} }

View file

@ -45,6 +45,7 @@ public:
bool contains(T1 * t1, T2 * t2) const { return m_set.contains(obj_pair(t1, t2)); } 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); } bool contains(obj_pair const & p) const { return m_set.contains(p); }
void reset() { m_set.reset(); } void reset() { m_set.reset(); }
bool empty() const { return m_set.empty(); }
}; };
#endif #endif

View file

@ -69,6 +69,7 @@ void small_object_allocator::reset() {
#define MASK ((1 << PTR_ALIGNMENT) - 1) #define MASK ((1 << PTR_ALIGNMENT) - 1)
void small_object_allocator::deallocate(size_t size, void * p) { void small_object_allocator::deallocate(size_t size, void * p) {
if (size == 0) return;
#if defined(Z3DEBUG) && !defined(_WINDOWS) #if defined(Z3DEBUG) && !defined(_WINDOWS)
// Valgrind friendly // Valgrind friendly
memory::deallocate(p); 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) { void * small_object_allocator::allocate(size_t size) {
if (size == 0) return 0;
#if defined(Z3DEBUG) && !defined(_WINDOWS) #if defined(Z3DEBUG) && !defined(_WINDOWS)
// Valgrind friendly // Valgrind friendly
return memory::allocate(size); return memory::allocate(size);