3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-28 03:15:50 +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
################################################################################
option(ENABLE_TRACING OFF "Enable tracing")
if (ENABLE_TRACING)
option(ENABLE_TRACING_FOR_NON_DEBUG "Enable tracing in non-debug builds." OFF)
if (ENABLE_TRACING_FOR_NON_DEBUG)
list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_TRACE")
else()
# Tracing is always enabled in debug builds
list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:Debug>:_TRACE>)
endif()
# Should we always enable tracing when doing a debug build?
#list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:Debug>:_TRACE>)
################################################################################
# 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_LIBDIR`` - STRING. The path to install z3 libraries (relative to ``CMAKE_INSTALL_PREFIX``), e.g. ``lib``.
* ``CMAKE_INSTALL_PREFIX`` - STRING. The install prefix to use (e.g. ``/usr/local/``).
* ``ENABLE_TRACING`` - BOOL. If set to ``TRUE`` enable tracing, if set to ``FALSE`` disable tracing.
* ``CMAKE_INSTALL_PYTHON_PKG_DIR`` - STRING. The path to install the z3 python bindings. This can be relative (to ``CMAKE_INSTALL_PREFIX``) or absolute.
* ``ENABLE_TRACING_FOR_NON_DEBUG`` - BOOL. If set to ``TRUE`` enable tracing in non-debug builds, if set to ``FALSE`` disable tracing in non-debug builds. Note in debug builds tracing is always enabled.
* ``BUILD_LIBZ3_SHARED`` - BOOL. If set to ``TRUE`` build libz3 as a shared library otherwise build as a static library.
* ``ENABLE_EXAMPLE_TARGETS`` - BOOL. If set to ``TRUE`` add the build targets for building the API examples.
* ``USE_OPENMP`` - BOOL. If set to ``TRUE`` and OpenMP support is detected build with OpenMP support.

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)
if (INSTALL_PYTHON_BINDINGS)
# Determine the installation path for the bindings
message(STATUS "Emitting rules to install Z3 python bindings")
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" "-c"
"import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())"
RESULT_VARIABLE exit_code
OUTPUT_VARIABLE python_pkg_dir
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Try to guess the installation path for the bindings
if (NOT DEFINED CMAKE_INSTALL_PYTHON_PKG_DIR)
message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR not set. Trying to guess")
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" "-c"
"import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())"
RESULT_VARIABLE exit_code
OUTPUT_VARIABLE CMAKE_INSTALL_PYTHON_PKG_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (NOT ("${exit_code}" EQUAL 0))
message(FATAL_ERROR "Failed to determine your Python package directory")
if (NOT ("${exit_code}" EQUAL 0))
message(FATAL_ERROR "Failed to determine your Python package directory")
endif()
message(STATUS "Detected Python package directory: \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\"")
# Set a cache variable that the user can modify if needed
set(CMAKE_INSTALL_PYTHON_PKG_DIR
"${CMAKE_INSTALL_PYTHON_PKG_DIR}"
CACHE PATH
"Path to install python bindings. This can be relative or absolute.")
mark_as_advanced(CMAKE_INSTALL_PYTHON_PKG_DIR)
else()
message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR already set (\"${CMAKE_INSTALL_PYTHON_PKG_DIR}\")"
". Not trying to guess.")
endif()
message(STATUS "Detected Python package directory: \"${python_pkg_dir}\"")
# Check if path exists under the install prefix
set(python_install_dir "")
string(FIND "${python_pkg_dir}" "${CMAKE_INSTALL_PREFIX}" position)
if (NOT ("${position}" EQUAL 0))
message(WARNING "The detected Python package directory \"${python_pkg_dir}\" "
"does not exist under the install prefix \"${CMAKE_INSTALL_PREFIX}\"."
" Running the install target may lead to a broken installation."
)
# Check if path exists under the install prefix if it is absolute. If the
# path is relative it will be installed under the install prefix so there
# if nothing to check
if (IS_ABSOLUTE "${CMAKE_INSTALL_PYTHON_PKG_DIR}")
string(FIND "${CMAKE_INSTALL_PYTHON_PKG_DIR}" "${CMAKE_INSTALL_PREFIX}" position)
if (NOT ("${position}" EQUAL 0))
message(WARNING "The directory to install the python bindings \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\" "
"is not under the install prefix \"${CMAKE_INSTALL_PREFIX}\"."
" Running the install target may lead to a broken installation. "
"To change the install directory modify the CMAKE_INSTALL_PYTHON_PKG_DIR cache variable."
)
endif()
endif()
# Using DESTDIR still seems to work even if we use an absolute path
set(python_install_dir "${python_pkg_dir}")
message(STATUS "Python bindings will be installed to \"${python_install_dir}\"")
message(STATUS "Python bindings will be installed to \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\"")
install(FILES ${build_z3_python_bindings_target_depends}
DESTINATION "${python_install_dir}"
DESTINATION "${CMAKE_INSTALL_PYTHON_PKG_DIR}"
)
else()
message(STATUS "Not emitting rules to install Z3 python bindings")

View file

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

View file

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

View file

@ -57,7 +57,7 @@ def sorted_headers_by_component(l):
_logger.debug("get_key({})".format(path))
path_components = []
stripped_path = path
assert 'src' in stripped_path.split(os.path.sep)
assert 'src' in stripped_path.split(os.path.sep) or 'src' in stripped_path.split('/')
# Keep stripping off directory components until we hit ``src``
while os.path.basename(stripped_path) != 'src':
path_components.append(os.path.basename(stripped_path))
@ -214,7 +214,7 @@ def mk_gparams_register_modules_internal(component_src_dirs, path):
"""
assert isinstance(component_src_dirs, list)
assert check_dir_exists(path)
cmds = []
cmds = []
mod_cmds = []
mod_descrs = []
fullname = os.path.join(path, 'gparams_register_modules.cpp')

View file

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

View file

@ -604,8 +604,16 @@ namespace z3 {
/**
\brief Return true if this expression is a numeral.
Specialized functions also return representations for the numerals as
small integers, 64 bit integers or rational or decimal strings.
*/
bool is_numeral() const { return kind() == Z3_NUMERAL_AST; }
bool is_numeral_i64(__int64& i) const { bool r = 0 != Z3_get_numeral_int64(ctx(), m_ast, &i); check_error(); return r;}
bool is_numeral_u64(__uint64& i) const { bool r = 0 != Z3_get_numeral_uint64(ctx(), m_ast, &i); check_error(); return r;}
bool is_numeral_i(int& i) const { bool r = 0 != Z3_get_numeral_int(ctx(), m_ast, &i); check_error(); return r;}
bool is_numeral_u(unsigned& i) const { bool r = 0 != Z3_get_numeral_uint(ctx(), m_ast, &i); check_error(); return r;}
bool is_numeral(std::string& s) const { if (!is_numeral()) return false; s = Z3_get_numeral_string(ctx(), m_ast); check_error(); return true; }
bool is_numeral(std::string& s, unsigned precision) const { if (!is_numeral()) return false; s = Z3_get_numeral_decimal_string(ctx(), m_ast, precision); check_error(); return true; }
/**
\brief Return true if this expression is an application.
*/
@ -625,7 +633,7 @@ namespace z3 {
/**
\brief Return true if expression is an algebraic number.
*/
bool is_algebraic() const { return Z3_is_algebraic_number(ctx(), m_ast); }
bool is_algebraic() const { return 0 != Z3_is_algebraic_number(ctx(), m_ast); }
/**
\brief Return true if this expression is well sorted (aka type correct).
@ -649,12 +657,11 @@ namespace z3 {
\pre is_numeral()
*/
int get_numeral_int() const {
assert(is_numeral());
int get_numeral_int() const {
int result;
Z3_bool state = Z3_get_numeral_int(ctx(), m_ast, &result);
if (state != Z3_TRUE)
if (!is_numeral_i(result)) {
throw exception("numeral does not fit in machine int");
}
return result;
}
@ -667,9 +674,9 @@ namespace z3 {
unsigned get_numeral_uint() const {
assert(is_numeral());
unsigned result;
Z3_bool state = Z3_get_numeral_uint(ctx(), m_ast, &result);
if (state != Z3_TRUE)
if (!is_numeral_u(result)) {
throw exception("numeral does not fit in machine uint");
}
return result;
}
@ -682,9 +689,9 @@ namespace z3 {
__int64 get_numeral_int64() const {
assert(is_numeral());
__int64 result;
Z3_bool state = Z3_get_numeral_int64(ctx(), m_ast, &result);
if (state != Z3_TRUE)
if (!is_numeral_i64(result)) {
throw exception("numeral does not fit in machine __int64");
}
return result;
}
@ -697,9 +704,9 @@ namespace z3 {
__uint64 get_numeral_uint64() const {
assert(is_numeral());
__uint64 result;
Z3_bool state = Z3_get_numeral_uint64(ctx(), m_ast, &result);
if (state != Z3_TRUE)
if (!is_numeral_u64(result)) {
throw exception("numeral does not fit in machine __uint64");
}
return result;
}

View file

@ -3940,7 +3940,7 @@ class ArraySortRef(SortRef):
>>> A.range()
Bool
"""
return _to_sort_ref(Z3_get_array_sort_range(self.ctx_ref(), self.ast), self.ctx)
return _to_sort_ref(Z3_get_array_sort_range(self.ctx_ref(), self.ast), self.ctx)
class ArrayRef(ExprRef):
"""Array expressions. """
@ -4162,6 +4162,7 @@ def Select(a, i):
_z3_assert(is_array(a), "First argument must be a Z3 array expression")
return a[i]
def Map(f, *args):
"""Return a Z3 map array expression.

View file

@ -664,3 +664,45 @@ algebraic_numbers::anum const & arith_util::to_irrational_algebraic_numeral(expr
SASSERT(is_irrational_algebraic_numeral(n));
return plugin().aw().to_anum(to_app(n)->get_decl());
}
expr_ref arith_util::mk_mul_simplify(expr_ref_vector const& args) {
return mk_mul_simplify(args.size(), args.c_ptr());
}
expr_ref arith_util::mk_mul_simplify(unsigned sz, expr* const* args) {
expr_ref result(m_manager);
switch (sz) {
case 0:
result = mk_numeral(rational(1), true);
break;
case 1:
result = args[0];
break;
default:
result = mk_mul(sz, args);
break;
}
return result;
}
expr_ref arith_util::mk_add_simplify(expr_ref_vector const& args) {
return mk_add_simplify(args.size(), args.c_ptr());
}
expr_ref arith_util::mk_add_simplify(unsigned sz, expr* const* args) {
expr_ref result(m_manager);
switch (sz) {
case 0:
result = mk_numeral(rational(0), true);
break;
case 1:
result = args[0];
break;
default:
result = mk_add(sz, args);
break;
}
return result;
}

View file

@ -149,7 +149,6 @@ protected:
func_decl * m_mod_0_decl;
func_decl * m_u_asin_decl;
func_decl * m_u_acos_decl;
ptr_vector<app> m_small_ints;
ptr_vector<app> m_small_reals;
@ -416,6 +415,11 @@ public:
return m_manager.mk_eq(lhs, rhs);
}
expr_ref mk_mul_simplify(expr_ref_vector const& args);
expr_ref mk_mul_simplify(unsigned sz, expr* const* args);
expr_ref mk_add_simplify(expr_ref_vector const& args);
expr_ref mk_add_simplify(unsigned sz, expr* const* args);
};
#endif /* ARITH_DECL_PLUGIN_H_ */

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());
}
std::ostream& operator<<(std::ostream& out, func_decl_ref const& e) {
return out << mk_ismt2_pp(e.get(), e.get_manager());
}
std::ostream& operator<<(std::ostream& out, sort_ref const& e) {
return out << mk_ismt2_pp(e.get(), e.get_manager());
}
std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) {
for (unsigned i = 0; i < e.size(); ++i) {
out << mk_ismt2_pp(e[i], e.get_manager());
@ -1216,6 +1224,18 @@ std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) {
return out;
}
std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e) {
for (unsigned i = 0; i < e.size(); ++i)
out << mk_ismt2_pp(e[i], e.get_manager()) << "\n";
return out;
}
std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e) {
for (unsigned i = 0; i < e.size(); ++i)
out << mk_ismt2_pp(e[i], e.get_manager()) << "\n";
return out;
}
#ifdef Z3DEBUG
void pp(expr const * n, ast_manager & m) {
std::cout << mk_ismt2_pp(const_cast<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, app_ref const& e);
std::ostream& operator<<(std::ostream& out, func_decl_ref const& e);
std::ostream& operator<<(std::ostream& out, sort_ref const& e);
std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e);
std::ostream& operator<<(std::ostream& out, app_ref_vector const& e);
std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e);
std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e);
#endif

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));
}
expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) {
if (num_args == 0)
return m.mk_false();
@ -188,10 +187,43 @@ expr * mk_not(ast_manager & m, expr * arg) {
expr * atom;
if (m.is_not(arg, atom))
return atom;
else if (m.is_true(arg))
return m.mk_false();
else if (m.is_false(arg))
return m.mk_true();
else
return m.mk_not(arg);
}
expr_ref push_not(const expr_ref& e) {
ast_manager& m = e.get_manager();
if (!is_app(e)) {
return expr_ref(m.mk_not(e), m);
}
app* a = to_app(e);
if (m.is_and(a)) {
if (a->get_num_args() == 0) {
return expr_ref(m.mk_false(), m);
}
expr_ref_vector args(m);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
args.push_back(push_not(expr_ref(a->get_arg(i), m)));
}
return mk_or(args);
}
if (m.is_or(a)) {
if (a->get_num_args() == 0) {
return expr_ref(m.mk_true(), m);
}
expr_ref_vector args(m);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
args.push_back(push_not(expr_ref(a->get_arg(i), m)));
}
return mk_and(args);
}
return expr_ref(mk_not(m, e), m);
}
expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) {
expr_ref_buffer new_diseqs(m);
for (unsigned i = 0; i < num_args; i++) {
@ -201,6 +233,24 @@ expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args)
return mk_and(m, new_diseqs.size(), new_diseqs.c_ptr());
}
expr* mk_distinct(ast_manager& m, unsigned num_args, expr * const * args) {
switch (num_args) {
case 0:
case 1:
return m.mk_true();
case 2:
return m.mk_not(m.mk_eq(args[0], args[1]));
default:
return m.mk_distinct(num_args, args);
}
}
expr_ref mk_distinct(expr_ref_vector const& args) {
ast_manager& m = args.get_manager();
return expr_ref(mk_distinct(m, args.size(), args.c_ptr()), m);
}
void flatten_and(expr_ref_vector& result) {
ast_manager& m = result.get_manager();
expr* e1, *e2, *e3;

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 expr_ref mk_or(expr_ref_vector const& args) { return expr_ref(mk_or(args.get_manager(), args.size(), args.c_ptr()), args.get_manager()); }
/**
Return a if arg = (not a)
Retur (not arg) otherwise
*/
expr * mk_not(ast_manager & m, expr * arg);
/**
Negate and push over conjunction or disjunction.
*/
expr_ref push_not(const expr_ref& arg);
/**
Return the expression (and (not (= args[0] args[1])) (not (= args[0] args[2])) ... (not (= args[num_args-2] args[num_args-1])))
*/
expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args);
/**
Create simplified distinct term. Binary distinct becomes a single disequality.
*/
expr * mk_distinct(ast_manager& m, unsigned num_args, expr * const * args);
expr_ref mk_distinct(expr_ref_vector const& args);
/**
\brief Collect top-level conjunctions and disjunctions.
*/

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);
}
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) {
sort* s = m_manager.mk_sort(m_manager.mk_family_id("arith"), INT_SORT);
parameter p(s);

View file

@ -234,7 +234,6 @@ protected:
func_decl * mk_mkbv(unsigned arity, sort * const * domain);
bool get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result);
func_decl * mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity);
@ -267,6 +266,8 @@ public:
virtual expr * get_some_value(sort * s);
bool get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result);
virtual bool is_considered_uninterpreted(func_decl * f) {
if (f->get_family_id() != get_family_id())
return false;
@ -390,6 +391,8 @@ public:
return static_cast<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_int2bv_size(parameter const& p);
app * mk_ule(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_ULEQ, arg1, arg2); }
app * mk_sle(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_SLEQ, arg1, arg2); }
@ -427,6 +430,7 @@ public:
app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); }
app * mk_bv(unsigned n, expr* const* es) { return m_manager.mk_app(get_fid(), OP_MKBV, n, es); }
};
#endif /* BV_DECL_PLUGIN_H_ */

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);
family_id get_family_id() const { return m_family_id; }
bool are_siblings(sort * s1, sort * s2);
bool is_func_decl(datatype_op_kind k, unsigned num_params, parameter const* params, func_decl* f);
bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f);
void reset();
void display_datatype(sort *s, std::ostream& strm);

View file

@ -22,6 +22,10 @@ Notes:
void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) {
if (num_bound == 0) {
result = n;
return;
}
expr * curr = 0, *b = 0;
SASSERT(n->get_ref_count() > 0);
@ -106,3 +110,27 @@ void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* cons
expr_abstractor abs(m);
abs(base, num_bound, bound, n, result);
}
expr_ref mk_quantifier(bool is_forall, ast_manager& m, unsigned num_bound, app* const* bound, expr* n) {
expr_ref result(m);
expr_abstract(m, 0, num_bound, (expr* const*)bound, n, result);
if (num_bound > 0) {
ptr_vector<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);
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

View file

@ -30,6 +30,14 @@ void expr_safe_replace::insert(expr* src, expr* dst) {
m_subst.insert(src, dst);
}
void expr_safe_replace::operator()(expr_ref_vector& es) {
expr_ref val(m);
for (unsigned i = 0; i < es.size(); ++i) {
(*this)(es[i].get(), val);
es[i] = val;
}
}
void expr_safe_replace::operator()(expr* e, expr_ref& res) {
m_todo.push_back(e);
expr* a, *b;

View file

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

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

View file

@ -578,6 +578,16 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
return st;
}
expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args) {
expr_ref result(m());
proof_ref pr(m());
if (BR_FAILED == reduce_app(f, num_args, args, result, pr)) {
result = m().mk_app(f, num_args, args);
}
return result;
}
bool reduce_quantifier(quantifier * old_q,
expr * new_body,
expr * const * new_patterns,
@ -695,6 +705,9 @@ struct th_rewriter::imp : public rewriter_tpl<th_rewriter_cfg> {
rewriter_tpl<th_rewriter_cfg>(m, m.proofs_enabled(), m_cfg),
m_cfg(m, p) {
}
expr_ref mk_app(func_decl* f, unsigned sz, expr* const* args) {
return m_cfg.mk_app(f, sz, args);
}
};
th_rewriter::th_rewriter(ast_manager & m, params_ref const & p):
@ -776,3 +789,7 @@ void th_rewriter::reset_used_dependencies() {
m_imp->cfg().m_used_dependencies = 0;
}
}
expr_ref th_rewriter::mk_app(func_decl* f, unsigned num_args, expr* const* args) {
return m_imp->mk_app(f, num_args, args);
}

View file

@ -45,6 +45,8 @@ public:
void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result);
expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args);
void cleanup();
void reset();

View file

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

View file

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

View file

@ -4,6 +4,5 @@ def_module_params('model',
('v1', BOOL, False, 'use Z3 version 1.x pretty printer'),
('v2', BOOL, False, 'use Z3 version 2.x (x <= 16) pretty printer'),
('compact', BOOL, False, 'try to compact function graph (i.e., function interpretations that are lookup tables)'),
('new_eval', BOOL, True, 'use new evaluator (temporary parameter for testing)'),
))

View file

@ -55,8 +55,7 @@ namespace datalog {
m_args(m),
m_hnf(m),
m_qe(m),
m_cfg(m),
m_rwr(m, false, m_cfg),
m_rwr(m),
m_ufproc(m) {}
void rule_manager::inc_ref(rule * r) {
@ -76,28 +75,8 @@ namespace datalog {
}
}
rule_manager::remove_label_cfg::~remove_label_cfg() {}
br_status rule_manager::remove_label_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result,
proof_ref & result_pr)
{
if (is_decl_of(f, m_label_fid, OP_LABEL)) {
SASSERT(num == 1);
result = args[0];
return BR_DONE;
}
return BR_FAILED;
}
void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) {
expr_ref tmp(m);
m_rwr(fml, tmp);
if (pr && fml != tmp) {
pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp));
}
fml = tmp;
m_rwr.remove_labels(fml, pr);
}
var_idx_set& rule_manager::collect_vars(expr* e) {
@ -1092,5 +1071,4 @@ namespace datalog {
};
template class rewriter_tpl<datalog::rule_manager::remove_label_cfg>;

View file

@ -32,6 +32,7 @@ Revision History:
#include"qe_lite.h"
#include"var_subst.h"
#include"datatype_decl_plugin.h"
#include"label_rewriter.h"
namespace datalog {
@ -102,16 +103,6 @@ namespace datalog {
*/
class rule_manager
{
class remove_label_cfg : public default_rewriter_cfg {
family_id m_label_fid;
public:
remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {}
virtual ~remove_label_cfg();
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result,
proof_ref & result_pr);
};
ast_manager& m;
context& m_ctx;
rule_counter m_counter;
@ -124,8 +115,7 @@ namespace datalog {
svector<bool> m_neg;
hnf m_hnf;
qe_lite m_qe;
remove_label_cfg m_cfg;
rewriter_tpl<remove_label_cfg> m_rwr;
label_rewriter m_rwr;
mutable uninterpreted_function_finder_proc m_ufproc;
mutable quantifier_finder_proc m_qproc;
mutable expr_sparse_mark m_visited;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -39,6 +39,17 @@ namespace nlsat {
m_values.swap(other.m_values);
m_assigned.swap(other.m_assigned);
}
void copy(assignment const& other) {
m_assigned.reset();
m_assigned.append(other.m_assigned);
m_values.reserve(m_assigned.size(), anum());
for (unsigned i = 0; i < m_assigned.size(); ++i) {
if (is_assigned(i)) {
am().set(m_values[i], other.value(i));
}
}
}
void set_core(var x, anum & v) {
m_values.reserve(x+1, anum());
m_assigned.reserve(x+1, false);
@ -52,11 +63,26 @@ namespace nlsat {
am().set(m_values[x], v);
}
void reset(var x) { if (x < m_assigned.size()) m_assigned[x] = false; }
void reset() { m_assigned.reset(); }
bool is_assigned(var x) const { return m_assigned.get(x, false); }
anum const & value(var x) const { return m_values[x]; }
virtual anum_manager & m() const { return am(); }
virtual bool contains(var x) const { return is_assigned(x); }
virtual anum const & operator()(var x) const { SASSERT(is_assigned(x)); return value(x); }
void swap(var x, var y) {
SASSERT(x < m_values.size() && y < m_values.size());
std::swap(m_assigned[x], m_assigned[y]);
std::swap(m_values[x], m_values[y]);
}
void display(std::ostream& out) const {
for (unsigned i = 0; i < m_assigned.size(); ++i) {
if (m_assigned[i]) {
out << "x" << i << " := ";
m_values.m().display(out, m_values[i]);
out << "\n";
}
}
}
};
/**

View file

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

View file

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

View file

@ -36,6 +36,7 @@ namespace nlsat {
polynomial::cache & m_cache;
pmanager & m_pm;
polynomial_ref_vector m_ps;
polynomial_ref_vector m_ps2;
polynomial_ref_vector m_psc_tmp;
polynomial_ref_vector m_factors;
scoped_anum_vector m_roots_tmp;
@ -43,6 +44,7 @@ namespace nlsat {
bool m_full_dimensional;
bool m_minimize_cores;
bool m_factor;
bool m_signed_project;
struct todo_set {
polynomial::cache & m_cache;
@ -137,6 +139,7 @@ namespace nlsat {
m_cache(u),
m_pm(u.pm()),
m_ps(m_pm),
m_ps2(m_pm),
m_psc_tmp(m_pm),
m_factors(m_pm),
m_roots_tmp(m_am),
@ -148,6 +151,7 @@ namespace nlsat {
m_simplify_cores = false;
m_full_dimensional = false;
m_minimize_cores = false;
m_signed_project = false;
}
~imp() {
@ -202,7 +206,7 @@ namespace nlsat {
void reset_already_added() {
SASSERT(m_result != 0);
unsigned sz = m_result->size();
for (unsigned i = 0; i < sz; i++)
for (unsigned i = 0; i < sz; i++)
m_already_added_literal[(*m_result)[i].index()] = false;
}
@ -212,7 +216,7 @@ namespace nlsat {
max_var(p) must be assigned in the current interpretation.
*/
int sign(polynomial_ref const & p) {
TRACE("nlsat_explain", tout << "p: " << p << "\n";);
TRACE("nlsat_explain", tout << "p: " << p << " var: " << max_var(p) << "\n";);
SASSERT(max_var(p) == null_var || m_assignment.is_assigned(max_var(p)));
return m_am.eval_sign_at(p, m_assignment);
}
@ -697,39 +701,163 @@ namespace nlsat {
}
}
void test_root_literal(atom::kind k, var y, unsigned i, poly * p, scoped_literal_vector& result) {
m_result = &result;
add_root_literal(k, y, i, p);
reset_already_added();
m_result = 0;
}
void add_root_literal(atom::kind k, var y, unsigned i, poly * p) {
polynomial_ref pr(p, m_pm);
TRACE("nlsat_explain",
display(tout << "x" << y << " " << k << "[" << i << "](", pr); tout << ")\n";);
if (!mk_linear_root(k, y, i, p) &&
//!mk_plinear_root(k, y, i, p) &&
!mk_quadratic_root(k, y, i, p)&&
true) {
bool_var b = m_solver.mk_root_atom(k, y, i, p);
literal l(b, true);
TRACE("nlsat_explain", tout << "adding literal\n"; display(tout, l); tout << "\n";);
add_literal(l);
}
}
/**
* literal can be expressed using a linear ineq_atom
*/
bool mk_linear_root(atom::kind k, var y, unsigned i, poly * p) {
scoped_mpz c(m_pm.m());
bool_var b;
bool lsign = false;
if (m_pm.degree(p, y) == 1 && m_pm.const_coeff(p, y, 1, c)) {
SASSERT(!m_pm.m().is_zero(c));
// literal can be expressed using a linear ineq_atom
polynomial_ref p_prime(m_pm);
p_prime = p;
if (m_pm.m().is_neg(c))
p_prime = neg(p_prime);
p = p_prime.get();
switch (k) {
case atom::ROOT_EQ: k = atom::EQ; lsign = false; break;
case atom::ROOT_LT: k = atom::LT; lsign = false; break;
case atom::ROOT_GT: k = atom::GT; lsign = false; break;
case atom::ROOT_LE: k = atom::GT; lsign = true; break;
case atom::ROOT_GE: k = atom::LT; lsign = true; break;
default:
UNREACHABLE();
break;
}
bool is_even = false;
b = m_solver.mk_ineq_atom(k, 1, &p, &is_even);
mk_linear_root(k, y, i, p, m_pm.m().is_neg(c));
return true;
}
else {
b = m_solver.mk_root_atom(k, y, i, p);
lsign = false;
return false;
}
/**
Create pseudo-linear root depending on the sign of the coefficient to y.
*/
bool mk_plinear_root(atom::kind k, var y, unsigned i, poly * p) {
if (m_pm.degree(p, y) != 1) {
return false;
}
lsign = !lsign; // adding as an assumption
literal l(b, lsign);
TRACE("nlsat_explain", tout << "adding literal\n"; display(tout, l); tout << "\n";);
add_literal(l);
polynomial_ref c(m_pm);
c = m_pm.coeff(p, y, 1);
int s = sign(c);
if (s == 0) {
return false;
}
ensure_sign(c);
mk_linear_root(k, y, i, p, s < 0);
return true;
}
/**
Encode root conditions for quadratic polynomials.
Basically implements Thom's theorem. The roots are characterized by the sign of polynomials and their derivatives.
b^2 - 4ac = 0:
- there is only one root, which is -b/2a.
- relation to root is a function of the sign of
- 2ax + b
b^2 - 4ac > 0:
- assert i == 1 or i == 2
- relation to root is a function of the signs of:
- 2ax + b
- ax^2 + bx + c
*/
bool mk_quadratic_root(atom::kind k, var y, unsigned i, poly * p) {
if (m_pm.degree(p, y) != 2) {
return false;
}
if (i != 1 && i != 2) {
return false;
}
SASSERT(m_assignment.is_assigned(y));
polynomial_ref A(m_pm), B(m_pm), C(m_pm), q(m_pm), p_diff(m_pm), yy(m_pm);
A = m_pm.coeff(p, y, 2);
B = m_pm.coeff(p, y, 1);
C = m_pm.coeff(p, y, 0);
// TBD throttle based on degree of q?
q = (B*B) - (4*A*C);
yy = m_pm.mk_polynomial(y);
p_diff = 2*A*yy + B;
p_diff = m_pm.normalize(p_diff);
int sq = ensure_sign(q);
if (sq < 0) {
return false;
}
int sa = ensure_sign(A);
if (sa == 0) {
q = B*yy + C;
return mk_plinear_root(k, y, i, q);
}
ensure_sign(p_diff);
if (sq > 0) {
polynomial_ref pr(p, m_pm);
ensure_sign(pr);
}
return true;
}
int ensure_sign(polynomial_ref & p) {
#if 0
polynomial_ref f(m_pm);
factor(p, m_factors);
m_is_even.reset();
unsigned num_factors = m_factors.size();
int s = 1;
for (unsigned i = 0; i < num_factors; i++) {
f = m_factors.get(i);
s *= sign(f);
m_is_even.push_back(false);
}
if (num_factors > 0) {
atom::kind k = atom::EQ;
if (s == 0) k = atom::EQ;
if (s < 0) k = atom::LT;
if (s > 0) k = atom::GT;
bool_var b = m_solver.mk_ineq_atom(k, num_factors, m_factors.c_ptr(), m_is_even.c_ptr());
add_literal(literal(b, true));
}
return s;
#else
int s = sign(p);
if (!is_const(p)) {
add_simple_assumption(s == 0 ? atom::EQ : (s < 0 ? atom::LT : atom::GT), p);
}
return s;
#endif
}
/**
Auxiliary function to linear roots.
*/
void mk_linear_root(atom::kind k, var y, unsigned i, poly * p, bool mk_neg) {
polynomial_ref p_prime(m_pm);
p_prime = p;
bool lsign = false;
if (mk_neg)
p_prime = neg(p_prime);
p = p_prime.get();
switch (k) {
case atom::ROOT_EQ: k = atom::EQ; lsign = false; break;
case atom::ROOT_LT: k = atom::LT; lsign = false; break;
case atom::ROOT_GT: k = atom::GT; lsign = false; break;
case atom::ROOT_LE: k = atom::GT; lsign = true; break;
case atom::ROOT_GE: k = atom::LT; lsign = true; break;
default:
UNREACHABLE();
break;
}
add_simple_assumption(k, p, lsign);
}
/**
@ -1332,10 +1460,333 @@ namespace nlsat {
TRACE("nlsat_explain", tout << "[explain] result\n"; display(tout, result););
CASSERT("nlsat", check_already_added());
}
void project(var x, unsigned num, literal const * ls, scoped_literal_vector & result) {
m_result = &result;
svector<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,
evaluator & ev) {
explain::explain(solver & s, assignment const & x2v, polynomial::cache & u,
atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev) {
m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev);
}
@ -1364,10 +1815,26 @@ namespace nlsat {
m_imp->m_factor = f;
}
void explain::set_signed_project(bool f) {
m_imp->m_signed_project = f;
}
void explain::operator()(unsigned n, literal const * ls, scoped_literal_vector & result) {
(*m_imp)(n, ls, result);
}
void explain::project(var x, unsigned n, literal const * ls, scoped_literal_vector & result) {
m_imp->project(x, n, ls, result);
}
void explain::maximize(var x, unsigned n, literal const * ls, scoped_anum& val, bool& unbounded) {
m_imp->maximize(x, n, ls, val, unbounded);
}
void explain::test_root_literal(atom::kind k, var y, unsigned i, poly* p, scoped_literal_vector & result) {
m_imp->test_root_literal(k, y, i, p, result);
}
};
#ifdef Z3DEBUG
@ -1398,3 +1865,4 @@ void pp_lit(nlsat::explain::imp & ex, nlsat::literal l) {
std::cout << std::endl;
}
#endif

View file

@ -22,9 +22,11 @@ Revision History:
#include"nlsat_solver.h"
#include"nlsat_scoped_literal_vector.h"
#include"polynomial_cache.h"
#include"algebraic_numbers.h"
namespace nlsat {
class evaluator;
class explain {
public:
@ -32,8 +34,8 @@ namespace nlsat {
private:
imp * m_imp;
public:
explain(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq,
evaluator & ev);
explain(solver & s, assignment const & x2v, polynomial::cache & u,
atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev);
~explain();
void reset();
@ -41,6 +43,7 @@ namespace nlsat {
void set_full_dimensional(bool f);
void set_minimize_cores(bool f);
void set_factor(bool f);
void set_signed_project(bool f);
/**
\brief Given a set of literals ls[0], ... ls[n-1] s.t.
@ -60,6 +63,48 @@ namespace nlsat {
- s_1, ..., s_m are false in the current interpretation
*/
void operator()(unsigned n, literal const * ls, scoped_literal_vector & result);
/**
\brief projection for a given variable.
Given: M |= l1[x] /\ ... /\ ln[x]
Find: M |= s1, ..., sm
Such that: |= ~s1 \/ ... \/ ~sm \/ E x. l1[x] /\ ... /\ ln[x]
Contrast this with with the core-based projection above:
Given: M |= A x . l1[x] \/ ... \/ ln[x]
Find: M |= ~s1, ..., ~sm.
Such that: |= s1 \/ ... \/ sm \/ A x . l1[x] \/ ... \/ ln[x]
Claim: the two compute the same solutions if the projection operators are independent of the value of x.
Claim: A complete, convergent projection operator can be obtained from M in a way that is independent of x.
*/
void project(var x, unsigned n, literal const * ls, scoped_literal_vector & result);
/**
Maximize the value of x (locally) under the current assignment to other variables and
while maintaining the assignment to the literals ls.
Set unbounded to 'true' if the value of x is unbounded.
Precondition: the set of literals are true in the current model.
By local optimization we understand that x is increased to the largest value within
the signs delineated by the roots of the polynomials in ls.
*/
void maximize(var x, unsigned n, literal const * ls, scoped_anum& val, bool& unbounded);
/**
Unit test routine.
*/
void test_root_literal(atom::kind k, var y, unsigned i, poly* p, scoped_literal_vector & result);
};
};

View file

@ -64,6 +64,13 @@ namespace nlsat {
for (unsigned i = 0; i < sz; i++)
push_back(ls[i]);
}
void append(scoped_literal_vector const& ls) {
append(ls.size(), ls.c_ptr());
}
void swap(scoped_literal_vector& other) {
SASSERT(&m_solver == &other.m_solver);
m_lits.swap(other.m_lits);
}
};

View file

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

View file

@ -28,6 +28,9 @@ Revision History:
namespace nlsat {
class evaluator;
class explain;
class solver {
struct imp;
imp * m_imp;
@ -63,7 +66,9 @@ namespace nlsat {
nonlinear arithmetic atom.
*/
bool_var mk_bool_var();
literal mk_true();
/**
\brief Create a real/integer variable.
*/
@ -121,6 +126,48 @@ namespace nlsat {
*/
atom * bool_var2atom(bool_var b);
/**
\brief extract free variables from literal.
*/
void vars(literal l, var_vector& vs);
/**
\brief provide access to atoms. Used by explain.
*/
atom_vector const& get_atoms();
/**
\brief Access var -> asserted equality.
*/
atom_vector const& get_var2eq();
/**
\brief expose evaluator.
*/
evaluator& get_evaluator();
/**
\brief Access explanation module.
*/
explain& get_explain();
/**
\brief Access assignments to variables.
*/
void get_rvalues(assignment& as);
void set_rvalues(assignment const& as);
void get_bvalues(svector<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
*/
@ -135,6 +182,8 @@ namespace nlsat {
// -----------------------
lbool check();
lbool check(literal_vector& assumptions);
// -----------------------
//
// Model
@ -154,6 +203,7 @@ namespace nlsat {
void updt_params(params_ref const & p);
static void collect_param_descrs(param_descrs & d);
void reset();
void collect_statistics(statistics & st);
void reset_statistics();
void display_status(std::ostream & out) const;
@ -174,6 +224,10 @@ namespace nlsat {
*/
void display(std::ostream & out, literal l) const;
void display(std::ostream & out, unsigned n, literal const* ls) const;
void display(std::ostream & out, atom const& a) const;
/**
\brief Display variable
*/

View file

@ -111,6 +111,21 @@ namespace nlsat {
struct eq_proc { bool operator()(ineq_atom const * a1, ineq_atom const * a2) const; };
};
inline std::ostream& operator<<(std::ostream& out, atom::kind k) {
switch (k) {
case atom::EQ: return out << "=";
case atom::LT: return out << "<";
case atom::GT: return out << ">";
case atom::ROOT_EQ: return out << "= root";
case atom::ROOT_LT: return out << "< root";
case atom::ROOT_LE: return out << "<= root";
case atom::ROOT_GT: return out << "> root";
case atom::ROOT_GE: return out << ">= root";
default: return out << (int)k;
}
return out;
}
class root_atom : public atom {
friend class solver;
var m_x;

View file

@ -29,7 +29,9 @@ Notes:
#include"expr2var.h"
#include"arith_decl_plugin.h"
#include"tactic.h"
#include"ast_smt2_pp.h"
#include"ast_pp.h"
#include"polynomial.h"
#include"algebraic_numbers.h"
struct goal2nlsat::imp {
struct nlsat_expr2polynomial : public expr2polynomial {
@ -124,7 +126,7 @@ struct goal2nlsat::imp {
m_qm.neg(d2);
polynomial_ref p(m_pm);
p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2);
TRACE("goal2nlsat_bug", tout << "p: " << p << "\nk: " << k << "\n";);
TRACE("goal2nlsat_bug", tout << mk_pp(f, m) << " p: " << p << "\nk: " << k << "\n";);
if (is_const(p)) {
int sign;
if (is_zero(p))
@ -192,7 +194,7 @@ struct goal2nlsat::imp {
switch (to_app(f)->get_decl_kind()) {
case OP_TRUE:
case OP_FALSE:
TRACE("goal2nlsat", tout << "f: " << mk_ismt2_pp(f, m) << "\n";);
TRACE("goal2nlsat", tout << "f: " << mk_pp(f, m) << "\n";);
throw tactic_exception("apply simplify before applying nlsat");
case OP_AND:
case OP_OR:
@ -257,6 +259,7 @@ struct goal2nlsat::imp {
process(g.form(i), g.dep(i));
}
}
};
struct goal2nlsat::scoped_set_imp {
@ -289,4 +292,154 @@ void goal2nlsat::operator()(goal const & g, params_ref const & p, nlsat::solver
scoped_set_imp setter(*this, local_imp);
local_imp(g);
}
struct nlsat2goal::imp {
ast_manager& m;
arith_util a;
u_map<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,17 +57,16 @@ class nlsat2goal {
struct imp;
imp * m_imp;
public:
nlsat2goal();
nlsat2goal(ast_manager& m);
~nlsat2goal();
static void collect_param_descrs(param_descrs & r);
/**
\brief Translate the state of the nlsat engine back into a goal.
*/
void operator()(nlsat::solver const & s, expr2var const & a2b, expr2var const & t2x,
params_ref const & p, goal & g, model_converter_ref & mc);
\brief Translate a literal into a formula.
*/
expr_ref operator()(nlsat::solver& s, u_map<expr*> const& b2a, u_map<expr*> const& x2t, nlsat::literal l);
};
#endif

View file

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

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 "quant_hoist.h"
#include "bool_rewriter.h"
#include "qe_util.h"
#include "th_rewriter.h"
#include "smt_kernel.h"
#include "model_evaluator.h"
@ -2281,7 +2280,7 @@ namespace qe {
}
}
static void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars) {
void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars) {
ast_manager& m = new_body.get_manager();
expr_ref tmp(m);
unsigned nd = q->get_num_decls();

View file

@ -223,6 +223,8 @@ namespace qe {
qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, smt_params& p);
void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars);
class def_vector {
func_decl_ref_vector m_vars;
expr_ref_vector m_defs;

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_
#include "model.h"
#include "arith_decl_plugin.h"
#include "qe_mbp.h"
namespace qe {
/**
Loos-Weispfenning model-based projection for a basic conjunction.
Lits is a vector of literals.
return vector of variables that could not be projected.
*/
expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits);
expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml);
class arith_project_plugin : public project_plugin {
struct imp;
imp* m_imp;
public:
arith_project_plugin(ast_manager& m);
virtual ~arith_project_plugin();
virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits);
virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits);
virtual family_id get_family_id();
};
bool arith_project(model& model, app* var, expr_ref_vector& lits);
// match e := t mod k = 0.
bool is_divides(arith_util& a, expr* e, rational& k, expr_ref& t);
};
#endif

View file

@ -32,6 +32,7 @@ Revision History:
#include "nlarith_util.h"
#include "model_evaluator.h"
#include "smt_kernel.h"
#include "qe_arith.h"
namespace qe {
@ -266,23 +267,8 @@ namespace qe {
//
// match 0 == p mod k, p mod k == 0
//
bool is_divides(app* e, numeral& k, expr_ref& p) {
expr* e1, *e2;
if (!m.is_eq(e, e1, e2)) {
return false;
}
return is_divides(e1, e2, k, p) || is_divides(e2, e1, k, p);
}
bool is_divides(expr* e1, expr* e2, numeral& k, expr_ref& p) {
if (m_arith.is_mod(e2) &&
m_arith.is_numeral(e1, k) &&
k.is_zero() &&
m_arith.is_numeral(to_app(e2)->get_arg(1), k)) {
p = to_app(e2)->get_arg(0);
return true;
}
return false;
bool is_divides(expr* e, numeral& k, expr_ref& p) {
return qe::is_divides(m_arith, e, k, p);
}
bool is_not_divides(app* e, app_ref& n, numeral& k, expr_ref& p) {

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 "uint_set.h"
#include "ast_util.h"
#include "qe_util.h"
#include "th_rewriter.h"
#include "for_each_expr.h"
#include "expr_safe_replace.h"

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

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

View file

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

View file

@ -66,7 +66,7 @@ void display_usage() {
#ifdef Z3GITHASH
std::cout << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH);
#endif
std::cout << "]. (C) Copyright 2006-2014 Microsoft Corp.\n";
std::cout << "]. (C) Copyright 2006-2016 Microsoft Corp.\n";
std::cout << "Usage: z3 [options] [-file:]file\n";
std::cout << "\nInput format:\n";
std::cout << " -smt use parser for SMT input format.\n";

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(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);

View file

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

View file

@ -25,19 +25,17 @@ Revision History:
#include"well_sorted.h"
#include"used_symbols.h"
#include"model_v2_pp.h"
#include"basic_simplifier_plugin.h"
proto_model::proto_model(ast_manager & m, simplifier & s, params_ref const & p):
proto_model::proto_model(ast_manager & m, params_ref const & p):
model_core(m),
m_simplifier(s),
m_afid(m.mk_family_id(symbol("array"))),
m_eval(*this) {
m_eval(*this),
m_rewrite(m) {
register_factory(alloc(basic_factory, m));
m_user_sort_factory = alloc(user_sort_factory, m);
register_factory(m_user_sort_factory);
m_model_partial = model_params(p).partial();
m_use_new_eval = model_params(p).new_eval();
}
@ -85,61 +83,6 @@ expr * proto_model::mk_some_interp_for(func_decl * d) {
return r;
}
// Auxiliary function for computing fi(args[0], ..., args[fi.get_arity() - 1]).
// The result is stored in result.
// Return true if succeeded, and false otherwise.
// It uses the simplifier s during the computation.
bool eval(func_interp & fi, simplifier & s, expr * const * args, expr_ref & result) {
bool actuals_are_values = true;
if (fi.num_entries() != 0) {
for (unsigned i = 0; actuals_are_values && i < fi.get_arity(); i++) {
actuals_are_values = fi.m().is_value(args[i]);
}
}
func_entry * entry = fi.get_entry(args);
if (entry != 0) {
result = entry->get_result();
return true;
}
TRACE("func_interp", tout << "failed to find entry for: ";
for(unsigned i = 0; i < fi.get_arity(); i++)
tout << mk_pp(args[i], fi.m()) << " ";
tout << "\nis partial: " << fi.is_partial() << "\n";);
if (!fi.eval_else(args, result)) {
return false;
}
if (actuals_are_values && fi.args_are_values()) {
// cheap case... we are done
return true;
}
// build symbolic result... the actuals may be equal to the args of one of the entries.
basic_simplifier_plugin * bs = static_cast<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 {
return
@ -159,192 +102,16 @@ bool proto_model::is_select_of_model_value(expr* e) const {
So, if model_completion == true, the evaluator never fails if it doesn't contain quantifiers.
*/
bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) {
if (m_use_new_eval) {
m_eval.set_model_completion(model_completion);
try {
m_eval(e, result);
return true;
}
catch (model_evaluator_exception & ex) {
(void)ex;
TRACE("model_evaluator", tout << ex.msg() << "\n";);
return false;
}
m_eval.set_model_completion(model_completion);
try {
m_eval(e, result);
return true;
}
bool is_ok = true;
SASSERT(is_well_sorted(m_manager, e));
TRACE("model_eval", tout << mk_pp(e, m_manager) << "\n";
tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n";);
obj_map<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;
}
}
catch (model_evaluator_exception & ex) {
(void)ex;
TRACE("model_evaluator", tout << ex.msg() << "\n";);
return false;
}
if (!eval_cache.find(e, a)) {
TRACE("model_eval", tout << "FAILED e: " << mk_bounded_pp(e, m_manager) << "\n";);
UNREACHABLE();
}
result = a;
TRACE("model_eval",
ast_ll_pp(tout << "original: ", m_manager, e);
ast_ll_pp(tout << "evaluated: ", m_manager, a);
ast_ll_pp(tout << "reduced: ", m_manager, result.get());
tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n";
);
SASSERT(is_well_sorted(m_manager, result.get()));
return is_ok;
}
/**
@ -400,7 +167,7 @@ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_au
if (m_aux_decls.contains(f))
found_aux_fs.insert(f);
expr_ref new_t(m_manager);
m_simplifier.mk_app(f, num_args, args.c_ptr(), new_t);
new_t = m_rewrite.mk_app(f, num_args, args.c_ptr());
if (t != new_t.get())
trail.push_back(new_t);
todo.pop_back();

View file

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

View file

@ -122,6 +122,10 @@ namespace smt {
*/
lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0);
lbool check(expr_ref_vector const& asms) { return check(asms.size(), asms.c_ptr()); }
lbool check(app_ref_vector const& asms) { return check(asms.size(), (expr* const*)asms.c_ptr()); }
/**
\brief Return the model associated with the last check command.
*/

View file

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

View file

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

View file

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

View file

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

View file

@ -94,6 +94,7 @@ namespace smt {
virtual bool internalize_term(app * term) { return false; }
virtual void new_eq_eh(theory_var v1, theory_var v2) { }
virtual void new_diseq_eh(theory_var v1, theory_var v2) { }
virtual void display(std::ostream& out) const {}
virtual void collect_statistics(::statistics & st) const {
st.update("wmaxsat num blocks", m_stats.m_num_blocks);

View file

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

View file

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

View file

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

View file

@ -60,7 +60,7 @@ class nla2bv_tactic : public tactic {
expr_ref_vector m_trail;
unsigned m_num_bits;
unsigned m_default_bv_size;
ref<filter_model_converter> m_fmc;
filter_model_converter_ref m_fmc;
public:
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_uppers;
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;
bool m_produce_models;

View file

@ -26,7 +26,7 @@ Revision History:
class propagate_values_tactic : public tactic {
struct imp {
ast_manager & m_manager;
ast_manager & m;
th_rewriter m_r;
scoped_ptr<expr_substitution> m_subst;
goal * m_goal;
@ -36,13 +36,15 @@ class propagate_values_tactic : public tactic {
bool m_modified;
imp(ast_manager & m, params_ref const & p):
m_manager(m),
m(m),
m_r(m, p),
m_goal(0),
m_occs(m, true /* track atoms */) {
updt_params_core(p);
}
~imp() { }
void updt_params_core(params_ref const & p) {
m_max_rounds = p.get_uint("max_rounds", 4);
}
@ -51,32 +53,28 @@ class propagate_values_tactic : public tactic {
m_r.updt_params(p);
updt_params_core(p);
}
ast_manager & m() const { return m_manager; }
bool is_shared(expr * t) {
return m_occs.is_shared(t);
}
bool is_shared_neg(expr * t, expr * & atom) {
if (!m().is_not(t))
bool is_shared_neg(expr * t, expr * & atom) {
if (!m.is_not(t, atom))
return false;
atom = to_app(t)->get_arg(0);
return is_shared(atom);
}
bool is_shared_eq(expr * t, expr * & lhs, expr * & value) {
if (!m().is_eq(t))
expr* arg1, *arg2;
if (!m.is_eq(t, arg1, arg2))
return false;
expr * arg1 = to_app(t)->get_arg(0);
expr * arg2 = to_app(t)->get_arg(1);
if (m().is_value(arg1) && is_shared(arg2)) {
if (m.is_value(arg1) && is_shared(arg2)) {
lhs = arg2;
value = arg1;
return true;
}
if (m().is_value(arg2) && is_shared(arg1)) {
if (m.is_value(arg2) && is_shared(arg1)) {
lhs = arg1;
value = arg2;
return true;
@ -87,15 +85,15 @@ class propagate_values_tactic : public tactic {
void push_result(expr * new_curr, proof * new_pr) {
if (m_goal->proofs_enabled()) {
proof * pr = m_goal->pr(m_idx);
new_pr = m().mk_modus_ponens(pr, new_pr);
new_pr = m.mk_modus_ponens(pr, new_pr);
}
expr_dependency_ref new_d(m());
expr_dependency_ref new_d(m);
if (m_goal->unsat_core_enabled()) {
new_d = m_goal->dep(m_idx);
expr_dependency * used_d = m_r.get_used_dependencies();
if (used_d != 0) {
new_d = m().mk_join(new_d, used_d);
new_d = m.mk_join(new_d, used_d);
m_r.reset_used_dependencies();
}
}
@ -103,34 +101,34 @@ class propagate_values_tactic : public tactic {
m_goal->update(m_idx, new_curr, new_pr, new_d);
if (is_shared(new_curr)) {
m_subst->insert(new_curr, m().mk_true(), m().mk_iff_true(new_pr), new_d);
m_subst->insert(new_curr, m.mk_true(), m.mk_iff_true(new_pr), new_d);
}
expr * atom;
if (is_shared_neg(new_curr, atom)) {
m_subst->insert(atom, m().mk_false(), m().mk_iff_false(new_pr), new_d);
m_subst->insert(atom, m.mk_false(), m.mk_iff_false(new_pr), new_d);
}
expr * lhs, * value;
if (is_shared_eq(new_curr, lhs, value)) {
TRACE("shallow_context_simplifier_bug", tout << "found eq:\n" << mk_ismt2_pp(new_curr, m()) << "\n";);
TRACE("shallow_context_simplifier_bug", tout << "found eq:\n" << mk_ismt2_pp(new_curr, m) << "\n";);
m_subst->insert(lhs, value, new_pr, new_d);
}
}
void process_current() {
expr * curr = m_goal->form(m_idx);
expr_ref new_curr(m());
proof_ref new_pr(m());
expr_ref new_curr(m);
proof_ref new_pr(m);
if (!m_subst->empty()) {
m_r(curr, new_curr, new_pr);
}
else {
new_curr = curr;
if (m().proofs_enabled())
new_pr = m().mk_reflexivity(curr);
if (m.proofs_enabled())
new_pr = m.mk_reflexivity(curr);
}
TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m()) << "\n---->\n" << mk_ismt2_pp(new_curr, m()) << "\n";);
TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n";);
push_result(new_curr, new_pr);
if (new_curr != curr)
@ -148,25 +146,26 @@ class propagate_values_tactic : public tactic {
m_goal = g.get();
bool forward = true;
expr_ref new_curr(m());
proof_ref new_pr(m());
expr_ref new_curr(m);
proof_ref new_pr(m);
unsigned size = m_goal->size();
m_idx = 0;
m_modified = false;
unsigned round = 0;
if (m_goal->inconsistent())
goto end;
if (m_max_rounds == 0)
goto end;
m_subst = alloc(expr_substitution, m(), g->unsat_core_enabled(), g->proofs_enabled());
m_subst = alloc(expr_substitution, m, g->unsat_core_enabled(), g->proofs_enabled());
m_r.set_substitution(m_subst.get());
m_occs(*m_goal);
while (true) {
TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display(tout););
TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display_with_dependencies(tout););
if (forward) {
for (; m_idx < size; m_idx++) {
process_current();
@ -255,15 +254,14 @@ public:
}
virtual void cleanup() {
ast_manager & m = m_imp->m();
imp * d = alloc(imp, m, m_params);
std::swap(d, m_imp);
dealloc(d);
ast_manager & m = m_imp->m;
dealloc(m_imp);
m_imp = alloc(imp, m, m_params);
}
};
tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(propagate_values_tactic, m, p));
return alloc(propagate_values_tactic, m, p);
}

View file

@ -22,7 +22,9 @@ Notes:
#include"smt_tactic.h"
#include"nnf_tactic.h"
#include"qe_tactic.h"
#include"nlqsat.h"
#include"qfnra_nlsat_tactic.h"
#include"qe_lite.h"
#include"probe_arith.h"
tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) {
@ -36,12 +38,19 @@ tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) {
return and_then(mk_simplify_tactic(m, p),
mk_nnf_tactic(m, p),
mk_propagate_values_tactic(m, p),
//mk_qe_lite_tactic(m),
mk_qe_tactic(m, p),
cond(mk_is_qfnra_probe(),
or_else(try_for(mk_qfnra_nlsat_tactic(m, p), 5000),
try_for(mk_qfnra_nlsat_tactic(m, p1), 10000),
mk_qfnra_nlsat_tactic(m, p2)),
mk_smt_tactic(p)));
#if 0
or_else(mk_nlqsat_tactic(m, p),
mk_smt_tactic(p))
#else
mk_smt_tactic(p)
#endif
));
}

View file

@ -23,6 +23,9 @@ Revision History:
#include"elim_uncnstr_tactic.h"
#include"qe_tactic.h"
#include"qe_sat_tactic.h"
#include"qe_lite.h"
#include"qsat.h"
#include"nlqsat.h"
#include"ctx_simplify_tactic.h"
#include"smt_tactic.h"
@ -37,10 +40,12 @@ static tactic * mk_quant_preprocessor(ast_manager & m, bool disable_gaussian = f
ctx_simp_p.set_uint("max_steps", 5000000);
tactic * solve_eqs;
if (disable_gaussian)
if (disable_gaussian) {
solve_eqs = mk_skip_tactic();
else
}
else {
solve_eqs = when(mk_not(mk_has_pattern_probe()), mk_solve_eqs_tactic(m));
}
// remark: investigate if gaussian elimination is useful when patterns are not provided.
return and_then(mk_simplify_tactic(m),
@ -49,6 +54,7 @@ static tactic * mk_quant_preprocessor(ast_manager & m, bool disable_gaussian = f
using_params(mk_simplify_tactic(m), pull_ite_p),
solve_eqs,
mk_elim_uncnstr_tactic(m),
mk_qe_lite_tactic(m),
mk_simplify_tactic(m));
}
@ -98,13 +104,18 @@ tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p) {
}
tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) {
#if 0
tactic * st = and_then(mk_quant_preprocessor(m),
or_else(try_for(mk_smt_tactic(), 100),
try_for(qe::mk_sat_tactic(m), 1000),
try_for(mk_smt_tactic(), 1000),
and_then(mk_qe_tactic(m), mk_smt_tactic())
));
#else
tactic * st = and_then(mk_quant_preprocessor(m),
or_else(mk_qsat_tactic(m, p),
and_then(mk_qe_tactic(m), mk_smt_tactic())));
#endif
st->updt_params(p);
return st;
}
@ -116,3 +127,4 @@ tactic * mk_lia_tactic(ast_manager & m, params_ref const & p) {
tactic * mk_lira_tactic(ast_manager & m, params_ref const & p) {
return mk_lra_tactic(m, p);
}

View file

@ -131,19 +131,19 @@ public:
SASSERT(r1_size > 0);
if (r1_size == 1) {
if (r1[0]->is_decided()) {
result.push_back(r1[0]);
if (models_enabled) mc = mc1;
result.push_back(r1[0]);
if (models_enabled) mc = mc1;
SASSERT(!pc); SASSERT(!core);
return;
}
goal_ref r1_0 = r1[0];
m_t2->operator()(r1_0, result, mc, pc, core);
if (models_enabled) mc = concat(mc1.get(), mc.get());
if (proofs_enabled) pc = concat(pc1.get(), pc.get());
if (cores_enabled) core = m.mk_join(core1.get(), core);
}
else {
if (cores_enabled) core = core1;
goal_ref r1_0 = r1[0];
m_t2->operator()(r1_0, result, mc, pc, core);
if (models_enabled) mc = concat(mc1.get(), mc.get());
if (proofs_enabled) pc = concat(pc1.get(), pc.get());
if (cores_enabled) core = m.mk_join(core1.get(), core);
}
else {
if (cores_enabled) core = core1;
proof_converter_ref_buffer pc_buffer;
model_converter_ref_buffer mc_buffer;
sbuffer<unsigned> sz_buffer;
@ -179,16 +179,16 @@ public:
SASSERT(!core2);
if (models_enabled) mc_buffer.push_back(0);
if (proofs_enabled) pc_buffer.push_back(proof2proof_converter(m, r2[0]->pr(0)));
if (models_enabled || proofs_enabled) sz_buffer.push_back(0);
if (models_enabled || proofs_enabled) sz_buffer.push_back(0);
if (cores_enabled) core = m.mk_join(core.get(), r2[0]->dep(0));
}
}
else {
result.append(r2.size(), r2.c_ptr());
if (models_enabled) mc_buffer.push_back(mc2.get());
if (proofs_enabled) pc_buffer.push_back(pc2.get());
if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size());
if (cores_enabled) core = m.mk_join(core.get(), core2.get());
result.append(r2.size(), r2.c_ptr());
if (models_enabled) mc_buffer.push_back(mc2.get());
if (proofs_enabled) pc_buffer.push_back(pc2.get());
if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size());
if (cores_enabled) core = m.mk_join(core.get(), core2.get());
}
}
@ -651,12 +651,12 @@ public:
}
goal_ref r1_0 = r1[0];
m_t2->operator()(r1_0, result, mc, pc, core);
if (models_enabled) mc = concat(mc1.get(), mc.get());
if (proofs_enabled) pc = concat(pc1.get(), pc.get());
if (cores_enabled) core = m.mk_join(core1.get(), core);
if (models_enabled) mc = concat(mc1.get(), mc.get());
if (proofs_enabled) pc = concat(pc1.get(), pc.get());
if (cores_enabled) core = m.mk_join(core1.get(), core);
}
else {
if (cores_enabled) core = core1;
if (cores_enabled) core = core1;
scoped_ptr_vector<ast_manager> managers;
tactic_ref_vector ts2;
@ -670,8 +670,8 @@ public:
ts2.push_back(m_t2->translate(*new_m));
}
proof_converter_ref_buffer pc_buffer;
model_converter_ref_buffer mc_buffer;
proof_converter_ref_buffer pc_buffer;
model_converter_ref_buffer mc_buffer;
scoped_ptr_vector<expr_dependency_ref> core_buffer;
scoped_ptr_vector<goal_ref_buffer> goals_vect;
@ -687,7 +687,7 @@ public:
std::string ex_msg;
#pragma omp parallel for
for (int i = 0; i < static_cast<int>(r1_size); i++) {
for (int i = 0; i < static_cast<int>(r1_size); i++) {
ast_manager & new_m = *(managers[i]);
goal_ref new_g = g_copies[i];
@ -772,7 +772,7 @@ public:
md = alloc(model, m);
apply(mc2, md, 0);
apply(mc1, md, i);
mc = model2model_converter(md.get());
mc = model2model_converter(md.get());
}
SASSERT(!pc); SASSERT(!core);
}
@ -969,9 +969,9 @@ class repeat_tactical : public unary_tactical {
m_t->operator()(in, r1, mc1, pc1, core1);
if (is_equal(orig_in, *(in.get()))) {
result.push_back(r1[0]);
if (models_enabled) mc = mc1;
if (proofs_enabled) pc = pc1;
if (cores_enabled) core = core1;
if (models_enabled) mc = mc1;
if (proofs_enabled) pc = pc1;
if (cores_enabled) core = core1;
return;
}
}
@ -980,18 +980,18 @@ class repeat_tactical : public unary_tactical {
if (r1_size == 1) {
if (r1[0]->is_decided()) {
result.push_back(r1[0]);
if (models_enabled) mc = mc1;
if (models_enabled) mc = mc1;
SASSERT(!pc); SASSERT(!core);
return;
}
goal_ref r1_0 = r1[0];
operator()(depth+1, r1_0, result, mc, pc, core);
if (models_enabled) mc = concat(mc.get(), mc1.get());
if (proofs_enabled) pc = concat(pc.get(), pc1.get());
if (cores_enabled) core = m.mk_join(core1.get(), core);
operator()(depth+1, r1_0, result, mc, pc, core);
if (models_enabled) mc = concat(mc.get(), mc1.get());
if (proofs_enabled) pc = concat(pc.get(), pc1.get());
if (cores_enabled) core = m.mk_join(core1.get(), core);
}
else {
if (cores_enabled) core = core1;
else {
if (cores_enabled) core = core1;
proof_converter_ref_buffer pc_buffer;
model_converter_ref_buffer mc_buffer;
sbuffer<unsigned> sz_buffer;

View file

@ -21,6 +21,8 @@ Notes:
#include"nlsat_evaluator.h"
#include"nlsat_solver.h"
#include"util.h"
#include"nlsat_explain.h"
#include"polynomial_cache.h"
#include"rlimit.h"
nlsat::interval_set_ref tst_interval(nlsat::interval_set_ref const & s1,
@ -29,7 +31,6 @@ nlsat::interval_set_ref tst_interval(nlsat::interval_set_ref const & s1,
bool check_num_intervals = true) {
nlsat::interval_set_manager & ism = s1.m();
nlsat::interval_set_ref r(ism);
std::cout << "------------------\n";
std::cout << "s1: " << s1 << "\n";
std::cout << "s2: " << s2 << "\n";
r = ism.mk_union(s1, s2);
@ -277,10 +278,12 @@ static void tst5() {
nlsat::assignment as(am);
small_object_allocator allocator;
nlsat::interval_set_manager ism(am, allocator);
nlsat::evaluator ev(as, pm, allocator);
nlsat::evaluator ev(s, as, pm, allocator);
nlsat::var x0, x1;
x0 = pm.mk_var();
x1 = pm.mk_var();
nlsat::interval_set_ref i(ism);
polynomial_ref p(pm);
polynomial_ref _x0(pm), _x1(pm);
_x0 = pm.mk_polynomial(x0);
@ -291,7 +294,6 @@ static void tst5() {
nlsat::bool_var b = s.mk_ineq_atom(nlsat::atom::GT, 1, _p, is_even);
nlsat::atom * a = s.bool_var2atom(b);
SASSERT(a != 0);
nlsat::interval_set_ref i(ism);
scoped_anum zero(am);
am.set(zero, 0);
as.set(0, zero);
@ -302,8 +304,414 @@ static void tst5() {
std::cout << "2) " << i << "\n";
}
static void project(nlsat::solver& s, nlsat::explain& ex, nlsat::var x, unsigned num, nlsat::literal const* lits) {
std::cout << "Project ";
s.display(std::cout, num, lits);
nlsat::scoped_literal_vector result(s);
ex.project(x, num, lits, result);
s.display(std::cout << "\n==>\n", result.size(), result.c_ptr());
std::cout << "\n";
}
static nlsat::literal mk_gt(nlsat::solver& s, nlsat::poly* p) {
nlsat::poly * _p[1] = { p };
bool is_even[1] = { false };
return s.mk_ineq_literal(nlsat::atom::GT, 1, _p, is_even);
}
static nlsat::literal mk_eq(nlsat::solver& s, nlsat::poly* p) {
nlsat::poly * _p[1] = { p };
bool is_even[1] = { false };
return s.mk_ineq_literal(nlsat::atom::EQ, 1, _p, is_even);
}
static void tst6() {
params_ref ps;
reslimit rlim;
nlsat::solver s(rlim, ps);
anum_manager & am = s.am();
nlsat::pmanager & pm = s.pm();
nlsat::assignment as(am);
nlsat::explain& ex = s.get_explain();
nlsat::var x0, x1, x2, a, b, c, d;
a = s.mk_var(false);
b = s.mk_var(false);
c = s.mk_var(false);
d = s.mk_var(false);
x0 = s.mk_var(false);
x1 = s.mk_var(false);
x2 = s.mk_var(false);
polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm);
polynomial_ref _x0(pm), _x1(pm), _x2(pm);
polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm);
_x0 = pm.mk_polynomial(x0);
_x1 = pm.mk_polynomial(x1);
_x2 = pm.mk_polynomial(x2);
_a = pm.mk_polynomial(a);
_b = pm.mk_polynomial(b);
_c = pm.mk_polynomial(c);
_d = pm.mk_polynomial(d);
p1 = (_a*(_x0^2)) + _x2 + 2;
p2 = (_b*_x1) - (2*_x2) - _x0 + 8;
nlsat::scoped_literal_vector lits(s);
lits.push_back(mk_gt(s, p1));
lits.push_back(mk_gt(s, p2));
lits.push_back(mk_gt(s, (_c*_x0) + _x2 + 1));
lits.push_back(mk_gt(s, (_d*_x0) - _x1 + 5*_x2));
scoped_anum zero(am), one(am), two(am);
am.set(zero, 0);
am.set(one, 1);
am.set(two, 2);
as.set(0, one);
as.set(1, one);
as.set(2, two);
as.set(3, two);
as.set(4, two);
as.set(5, one);
as.set(6, one);
s.set_rvalues(as);
project(s, ex, x0, 2, lits.c_ptr());
project(s, ex, x1, 3, lits.c_ptr());
project(s, ex, x2, 3, lits.c_ptr());
project(s, ex, x2, 2, lits.c_ptr());
project(s, ex, x2, 4, lits.c_ptr());
project(s, ex, x2, 3, lits.c_ptr()+1);
}
static void tst7() {
params_ref ps;
reslimit rlim;
nlsat::solver s(rlim, ps);
anum_manager & am = s.am();
nlsat::pmanager & pm = s.pm();
nlsat::var x0, x1, x2, a, b, c, d;
a = s.mk_var(false);
b = s.mk_var(false);
c = s.mk_var(false);
d = s.mk_var(false);
x0 = s.mk_var(false);
x1 = s.mk_var(false);
x2 = s.mk_var(false);
polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm);
polynomial_ref _x0(pm), _x1(pm), _x2(pm);
polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm);
_x0 = pm.mk_polynomial(x0);
_x1 = pm.mk_polynomial(x1);
_x2 = pm.mk_polynomial(x2);
_a = pm.mk_polynomial(a);
_b = pm.mk_polynomial(b);
_c = pm.mk_polynomial(c);
_d = pm.mk_polynomial(d);
p1 = _x0 + _x1;
p2 = _x2 - _x0;
p3 = (-1*_x0) - _x1;
nlsat::scoped_literal_vector lits(s);
lits.push_back(mk_gt(s, p1));
lits.push_back(mk_gt(s, p2));
lits.push_back(mk_gt(s, p3));
nlsat::literal_vector litsv(lits.size(), lits.c_ptr());
lbool res = s.check(litsv);
SASSERT(res == l_false);
for (unsigned i = 0; i < litsv.size(); ++i) {
s.display(std::cout, litsv[i]);
std::cout << " ";
}
std::cout << "\n";
litsv.reset();
litsv.append(2, lits.c_ptr());
res = s.check(litsv);
SASSERT(res == l_true);
s.display(std::cout);
s.am().display(std::cout, s.value(x0)); std::cout << "\n";
s.am().display(std::cout, s.value(x1)); std::cout << "\n";
s.am().display(std::cout, s.value(x2)); std::cout << "\n";
}
static void tst8() {
params_ref ps;
reslimit rlim;
nlsat::solver s(rlim, ps);
anum_manager & am = s.am();
nlsat::pmanager & pm = s.pm();
nlsat::assignment as(am);
nlsat::explain& ex = s.get_explain();
nlsat::var x0, x1, x2, a, b, c, d;
a = s.mk_var(false);
b = s.mk_var(false);
c = s.mk_var(false);
d = s.mk_var(false);
x0 = s.mk_var(false);
x1 = s.mk_var(false);
x2 = s.mk_var(false);
polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm);
polynomial_ref _x0(pm), _x1(pm), _x2(pm);
polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm);
_x0 = pm.mk_polynomial(x0);
_x1 = pm.mk_polynomial(x1);
_x2 = pm.mk_polynomial(x2);
_a = pm.mk_polynomial(a);
_b = pm.mk_polynomial(b);
_c = pm.mk_polynomial(c);
_d = pm.mk_polynomial(d);
scoped_anum zero(am), one(am), two(am), six(am);
am.set(zero, 0);
am.set(one, 1);
am.set(two, 2);
am.set(six, 6);
as.set(0, two); // a
as.set(1, one); // b
as.set(2, six); // c
as.set(3, zero); // d
as.set(4, zero); // x0
as.set(5, zero); // x1
as.set(6, two); // x2
s.set_rvalues(as);
nlsat::scoped_literal_vector lits(s);
lits.push_back(mk_eq(s, (_a*_x2*_x2) - (_b*_x2) - _c));
project(s, ex, x2, 1, lits.c_ptr());
}
static void tst9() {
params_ref ps;
reslimit rlim;
nlsat::solver s(rlim, ps);
anum_manager & am = s.am();
nlsat::pmanager & pm = s.pm();
nlsat::assignment as(am);
nlsat::explain& ex = s.get_explain();
int num_lo = 4;
int num_hi = 5;
svector<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() {
tst10();
std::cout << "------------------\n";
exit(0);
tst9();
std::cout << "------------------\n";
exit(0);
tst8();
std::cout << "------------------\n";
tst7();
std::cout << "------------------\n";
tst6();
std::cout << "------------------\n";
tst5();
std::cout << "------------------\n";
tst4();
std::cout << "------------------\n";
tst3();
}

View file

@ -12,9 +12,9 @@ Copyright (c) 2015 Microsoft Corporation
#include "reg_decl_plugins.h"
#include "arith_rewriter.h"
#include "ast_pp.h"
#include "qe_util.h"
#include "smt_context.h"
#include "expr_abstract.h"
#include "expr_safe_replace.h"
static expr_ref parse_fml(ast_manager& m, char const* str) {
expr_ref result(m);
@ -29,6 +29,11 @@ static expr_ref parse_fml(ast_manager& m, char const* str) {
<< "(declare-const t Real)\n"
<< "(declare-const a Real)\n"
<< "(declare-const b Real)\n"
<< "(declare-const i Int)\n"
<< "(declare-const j Int)\n"
<< "(declare-const k Int)\n"
<< "(declare-const l Int)\n"
<< "(declare-const m Int)\n"
<< "(assert " << str << ")\n";
std::istringstream is(buffer.str());
VERIFY(parse_smt2_commands(ctx, is));
@ -42,6 +47,8 @@ static char const* example2 = "(and (<= z x) (<= x 3.0) (<= (* 3.0 x) y) (<= z y
static char const* example3 = "(and (<= z x) (<= x 3.0) (< (* 3.0 x) y) (<= z y))";
static char const* example4 = "(and (<= z x) (<= x 3.0) (not (>= (* 3.0 x) y)) (<= z y))";
static char const* example5 = "(and (<= y x) (<= z x) (<= x u) (<= x v) (<= x t))";
static char const* example7 = "(and (<= x y) (<= x z) (<= u x) (< v x))";
static char const* example8 = "(and (<= (* 2 i) k) (<= j (* 3 i)))";
static char const* example6 = "(and (<= 0 (+ x z))\
(>= y x) \
@ -51,30 +58,175 @@ static char const* example6 = "(and (<= 0 (+ x z))\
(>= x 0.0)\
(<= x 1.0))";
// phi[M] => result => E x . phi[x]
static void test(char const *ex) {
static void test(app* var, expr_ref& fml) {
ast_manager& m = fml.get_manager();
smt_params params;
params.m_model = true;
symbol x_name(var->get_decl()->get_name());
sort* x_sort = m.get_sort(var);
expr_ref pr(m);
expr_ref_vector lits(m);
flatten_and(fml, lits);
model_ref md;
{
smt::context ctx(m, params);
ctx.assert_expr(fml);
lbool result = ctx.check();
if (result != l_true) return;
ctx.get_model(md);
}
VERIFY(qe::arith_project(*md, var, lits));
pr = mk_and(lits);
std::cout << "original: " << mk_pp(fml, m) << "\n";
std::cout << "projected: " << mk_pp(pr, m) << "\n";
// projection is consistent with model.
expr_ref tmp(m);
VERIFY(md->eval(pr, tmp) && m.is_true(tmp));
// projection implies E x. fml
{
qe::expr_quant_elim qelim(m, params);
expr_ref result(m), efml(m);
expr* x = var;
expr_abstract(m, 0, 1, &x, fml, efml);
efml = m.mk_exists(1, &x_sort, &x_name, efml);
qelim(m.mk_true(), efml, result);
smt::context ctx(m, params);
ctx.assert_expr(pr);
ctx.assert_expr(m.mk_not(result));
std::cout << "exists: " << pr << " =>\n" << result << "\n";
VERIFY(l_false == ctx.check());
}
std::cout << "\n";
}
static void testR(char const *ex) {
ast_manager m;
reg_decl_plugins(m);
arith_util a(m);
expr_ref fml = parse_fml(m, ex);
app_ref_vector vars(m);
expr_ref_vector lits(m);
vars.push_back(m.mk_const(symbol("x"), a.mk_real()));
flatten_and(fml, lits);
symbol x_name("x");
sort_ref x_sort(a.mk_real(), m);
app_ref var(m.mk_const(x_name, x_sort), m);
test(var, fml);
}
smt::context ctx(m, params);
ctx.assert_expr(fml);
lbool result = ctx.check();
SASSERT(result == l_true);
ref<model> md;
ctx.get_model(md);
expr_ref pr = qe::arith_project(*md, vars, lits);
std::cout << mk_pp(fml, m) << "\n";
std::cout << mk_pp(pr, m) << "\n";
static void testI(char const *ex) {
ast_manager m;
reg_decl_plugins(m);
arith_util a(m);
expr_ref fml = parse_fml(m, ex);
symbol x_name("i");
sort_ref x_sort(a.mk_int(), m);
app_ref var(m.mk_const(x_name, x_sort), m);
test(var, fml);
}
static expr_ref_vector mk_ineqs(app* x, app* y, app_ref_vector const& nums) {
ast_manager& m = nums.get_manager();
arith_util a(m);
expr_ref_vector result(m);
for (unsigned i = 0; i < nums.size(); ++i) {
expr_ref ax(a.mk_mul(nums[i], x), m);
result.push_back(a.mk_le(ax, y));
result.push_back(m.mk_not(a.mk_ge(ax, y)));
result.push_back(m.mk_not(a.mk_gt(y, ax)));
result.push_back(a.mk_lt(y, ax));
}
return result;
}
static app_ref generate_ineqs(ast_manager& m, sort* s, vector<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) {
@ -102,7 +254,7 @@ static void test2(char const *ex) {
std::cout << mk_pp(fml, m) << "\n";
expr_ref pr2(m), fml2(m);
expr_ref pr1(m), pr2(m), fml2(m);
expr_ref_vector bound(m);
ptr_vector<sort> sorts;
svector<symbol> names;
@ -114,7 +266,10 @@ static void test2(char const *ex) {
expr_abstract(m, 0, bound.size(), bound.c_ptr(), fml, fml2);
fml2 = m.mk_exists(bound.size(), sorts.c_ptr(), names.c_ptr(), fml2);
qe::expr_quant_elim qe(m, params);
expr_ref pr1 = qe::arith_project(*md, vars, lits);
for (unsigned i = 0; i < vars.size(); ++i) {
VERIFY(qe::arith_project(*md, vars[i].get(), lits));
}
pr1 = mk_and(lits);
qe(m.mk_true(), fml2, pr2);
std::cout << mk_pp(pr1, m) << "\n";
std::cout << mk_pp(pr2, m) << "\n";
@ -131,11 +286,17 @@ static void test2(char const *ex) {
}
void tst_qe_arith() {
// enable_trace("qe");
testI(example8);
testR(example7);
test_ineqs();
return;
testR(example1);
testR(example2);
testR(example3);
testR(example4);
testR(example5);
return;
test2(example6);
return;
test(example1);
test(example2);
test(example3);
test(example4);
test(example5);
}

View file

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

View file

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