3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

Merge remote-tracking branch 'origin/master' into polysat

This commit is contained in:
Jakob Rath 2023-12-07 16:00:15 +01:00
commit cd50f2ea88
378 changed files with 13563 additions and 7320 deletions

View file

@ -220,7 +220,7 @@ if (MSVC)
set(dll_module_exports_file "${CMAKE_CURRENT_BINARY_DIR}/api_dll.def")
add_custom_command(OUTPUT "${dll_module_exports_file}"
COMMAND
"${PYTHON_EXECUTABLE}"
"${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/mk_def_file.py"
"${dll_module_exports_file}"
"libz3"

View file

@ -18,6 +18,7 @@
#include "ast/bv_decl_plugin.h"
#include "ast/array_decl_plugin.h"
#include "ast/ast_ll_pp.h"
class ackr_helper {
public:
@ -40,10 +41,8 @@ public:
inline bool is_uninterp_fn(app const * a) const {
if (is_uninterp(a))
return true;
else {
decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id());
return p->is_considered_uninterpreted(a->get_decl());
}
decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id());
return p->is_considered_uninterpreted(a->get_decl());
}
/**
@ -64,9 +63,8 @@ public:
}
}
else {
for (expr* arg : *a) {
for (expr* arg : *a)
non_select.mark(arg, true);
}
}
}
@ -112,7 +110,8 @@ public:
}
void insert(fun2terms_map& f2t, sel2terms_map& s2t, app* a) {
if (a->get_num_args() == 0) return;
if (a->get_num_args() == 0)
return;
ast_manager& m = m_bvutil.get_manager();
app_set* ts = nullptr;
bool is_const_args = true;
@ -129,21 +128,18 @@ public:
ts = alloc(app_set);
f2t.insert(fd, ts);
}
is_const_args = m.is_value(a->get_arg(0));
is_const_args = m.is_unique_value(a->get_arg(0));
}
else {
else
return;
}
for (unsigned i = 1; is_const_args && i < a->get_num_args(); ++i) {
is_const_args &= m.is_value(a->get_arg(i));
}
if (is_const_args) {
for (unsigned i = 1; is_const_args && i < a->get_num_args(); ++i)
is_const_args &= m.is_unique_value(a->get_arg(i));
if (is_const_args)
ts->const_args.insert(a);
}
else {
else
ts->var_args.insert(a);
}
}
private:

View file

@ -22,6 +22,8 @@
#include "ackermannization/ackr_info.h"
#include "ast/for_each_expr.h"
#include "ast/ast_util.h"
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "model/model_smt2_pp.h"
lackr::lackr(ast_manager& m, const params_ref& p, lackr_stats& st,
@ -142,10 +144,10 @@ bool lackr::ackr(app * const t1, app * const t2) {
// Introduce the ackermann lemma for each pair of terms.
//
void lackr::eager_enc() {
TRACE("ackermannize", tout << "#funs: " << m_fun2terms.size() << "#sels: " << m_sel2terms.size() << std::endl;);
for (auto const& kv : m_fun2terms) {
TRACE("ackermannize", tout << "#funs: " << m_fun2terms.size() << " #sels: " << m_sel2terms.size() << std::endl;);
for (auto const& [k,v] : m_fun2terms) {
checkpoint();
ackr(kv.get_value());
ackr(v);
}
for (auto const& kv : m_sel2terms) {
checkpoint();
@ -172,14 +174,13 @@ void lackr::ackr(app_set const* ts) {
}
void lackr::abstract_fun(fun2terms_map const& apps) {
for (auto const& kv : apps) {
func_decl* fd = kv.m_key;
for (app * t : kv.m_value->var_args) {
for (auto const& [fd, v] : apps) {
for (app * t : v->var_args) {
app * fc = m.mk_fresh_const(fd->get_name(), t->get_sort());
SASSERT(t->get_decl() == fd);
m_info->set_abstr(t, fc);
}
for (app * t : kv.m_value->const_args) {
for (app * t : v->const_args) {
app * fc = m.mk_fresh_const(fd->get_name(), t->get_sort());
SASSERT(t->get_decl() == fd);
m_info->set_abstr(t, fc);

View file

@ -18,7 +18,7 @@ foreach (gen_file ${generated_files})
endforeach()
add_custom_command(OUTPUT ${generated_files}
COMMAND "${PYTHON_EXECUTABLE}"
COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/update_api.py"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"--api_output_dir"

View file

@ -29,6 +29,7 @@ Revision History:
#include "ast/ast_ll_pp.h"
#include "ast/ast_smt_pp.h"
#include "ast/ast_smt2_pp.h"
#include "ast/polymorphism_util.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/rewriter/var_subst.h"
#include "ast/rewriter/expr_safe_replace.h"
@ -88,6 +89,16 @@ extern "C" {
Z3_CATCH_RETURN(nullptr);
}
Z3_sort Z3_API Z3_mk_type_variable(Z3_context c, Z3_symbol name) {
Z3_TRY;
LOG_Z3_mk_type_variable(c, name);
RESET_ERROR_CODE();
sort* ty = mk_c(c)->m().mk_type_var(to_symbol(name));
mk_c(c)->save_ast_trail(ty);
RETURN_Z3(of_sort(ty));
Z3_CATCH_RETURN(nullptr);
}
bool Z3_API Z3_is_eq_ast(Z3_context c, Z3_ast s1, Z3_ast s2) {
RESET_ERROR_CODE();
return s1 == s2;
@ -180,7 +191,20 @@ extern "C" {
arg_list.push_back(to_expr(args[i]));
}
func_decl* _d = reinterpret_cast<func_decl*>(d);
app* a = mk_c(c)->m().mk_app(_d, num_args, arg_list.data());
ast_manager& m = mk_c(c)->m();
if (_d->is_polymorphic()) {
polymorphism::util u(m);
polymorphism::substitution sub(m);
ptr_buffer<sort> domain;
for (unsigned i = 0; i < num_args; ++i) {
if (!sub.match(_d->get_domain(i), arg_list[i]->get_sort()))
SET_ERROR_CODE(Z3_INVALID_ARG, "failed to match argument of polymorphic function");
domain.push_back(arg_list[i]->get_sort());
}
sort_ref range = sub(_d->get_range());
_d = m.instantiate_polymorphic(_d, num_args, domain.data(), range);
}
app* a = m.mk_app(_d, num_args, arg_list.data());
mk_c(c)->save_ast_trail(a);
check_sorts(c, a);
RETURN_Z3(of_ast(a));
@ -728,6 +752,9 @@ extern "C" {
else if (fid == mk_c(c)->get_char_fid() && k == CHAR_SORT) {
return Z3_CHAR_SORT;
}
else if (fid == poly_family_id) {
return Z3_TYPE_VAR;
}
else {
return Z3_UNKNOWN_SORT;
}

View file

@ -227,6 +227,9 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \
Z3_ast Z3_API Z3_mk_bvadd_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2) {
Z3_TRY;
RESET_ERROR_CODE();
// l1 := t1 <s 0
// l2 := t2 <s 0
// l1 & l2 => t1 + t2 <s 0
Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1));
Z3_inc_ref(c, zero);
Z3_ast r = Z3_mk_bvadd(c, t1, t2);
@ -255,6 +258,10 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \
Z3_ast Z3_API Z3_mk_bvsub_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2) {
Z3_TRY;
RESET_ERROR_CODE();
// x := t2 = min_int
// y := t1 <s 0
// z := no_overflow(t1 + -t2)
// if x y z
Z3_ast minus_t2 = Z3_mk_bvneg(c, t2);
Z3_inc_ref(c, minus_t2);
Z3_sort s = Z3_get_sort(c, t2);
@ -284,6 +291,9 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \
Z3_TRY;
RESET_ERROR_CODE();
if (is_signed) {
// x := 0 <s -t2
// y := no_underflow(x + -t2)
// x => y
Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1));
Z3_inc_ref(c, zero);
Z3_ast minus_t2 = Z3_mk_bvneg(c, t2);

View file

@ -78,6 +78,11 @@ namespace api {
m().dec_ref(a);
}
// flush_objects can only be called in the main thread.
// This ensures that the calls to m().dec_ref() and dealloc(o)
// only happens in the main thread.
// Calls to dec_ref are allowed in other threads when m_concurrent_dec_ref is
// set to true.
void context::flush_objects() {
#ifndef SINGLE_THREAD
if (!m_concurrent_dec_ref)
@ -157,6 +162,9 @@ namespace api {
flush_objects();
for (auto& kv : m_allocated_objects) {
api::object* val = kv.m_value;
#ifdef SINGLE_THREAD
# define m_concurrent_dec_ref false
#endif
DEBUG_CODE(if (!m_concurrent_dec_ref) warning_msg("Uncollected memory: %d: %s", kv.m_key, typeid(*val).name()););
dealloc(val);
}

View file

@ -241,6 +241,20 @@ extern "C" {
Z3_CATCH_RETURN(nullptr);
}
unsigned Z3_API Z3_constructor_num_fields(Z3_context c, Z3_constructor constr) {
Z3_TRY;
LOG_Z3_constructor_num_fields(c, constr);
RESET_ERROR_CODE();
mk_c(c)->reset_last_result();
if (!constr) {
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return 0;
}
constructor* c = reinterpret_cast<constructor*>(constr);
return c->m_field_names.size();
Z3_CATCH_RETURN(0);
}
void Z3_API Z3_query_constructor(Z3_context c,
Z3_constructor constr,

View file

@ -29,13 +29,12 @@ bool is_numeral_sort(Z3_context c, Z3_sort ty) {
if (!ty) return false;
sort * _ty = to_sort(ty);
family_id fid = _ty->get_family_id();
if (fid != mk_c(c)->get_arith_fid() &&
fid != mk_c(c)->get_bv_fid() &&
fid != mk_c(c)->get_datalog_fid() &&
fid != mk_c(c)->get_fpa_fid()) {
return false;
}
return true;
return
fid == mk_c(c)->get_arith_fid() ||
fid == mk_c(c)->get_bv_fid() ||
fid == mk_c(c)->get_datalog_fid() ||
fid == mk_c(c)->get_fpa_fid();
}
static bool check_numeral_sort(Z3_context c, Z3_sort ty) {
@ -152,7 +151,7 @@ extern "C" {
mk_c(c)->bvutil().is_numeral(e) ||
mk_c(c)->fpautil().is_numeral(e) ||
mk_c(c)->fpautil().is_rm_numeral(e) ||
mk_c(c)->datalog_util().is_numeral_ext(e);
mk_c(c)->datalog_util().is_numeral(e);
Z3_CATCH_RETURN(false);
}

View file

@ -383,6 +383,36 @@ extern "C" {
Z3_CATCH_RETURN(0);
}
Z3_symbol Z3_API Z3_get_quantifier_skolem_id(Z3_context c, Z3_ast a) {
Z3_TRY;
LOG_Z3_get_quantifier_skolem_id(c, a);
RESET_ERROR_CODE();
ast * _a = to_ast(a);
if (_a->get_kind() == AST_QUANTIFIER) {
return of_symbol(to_quantifier(_a)->get_skid());
}
else {
SET_ERROR_CODE(Z3_SORT_ERROR, nullptr);
return of_symbol(symbol::null);
}
Z3_CATCH_RETURN(of_symbol(symbol::null));
}
Z3_symbol Z3_API Z3_get_quantifier_id(Z3_context c, Z3_ast a) {
Z3_TRY;
LOG_Z3_get_quantifier_skolem_id(c, a);
RESET_ERROR_CODE();
ast * _a = to_ast(a);
if (_a->get_kind() == AST_QUANTIFIER) {
return of_symbol(to_quantifier(_a)->get_qid());
}
else {
SET_ERROR_CODE(Z3_SORT_ERROR, nullptr);
return of_symbol(symbol::null);
}
Z3_CATCH_RETURN(of_symbol(symbol::null));
}
unsigned Z3_API Z3_get_quantifier_num_patterns(Z3_context c, Z3_ast a) {
Z3_TRY;
LOG_Z3_get_quantifier_num_patterns(c, a);

View file

@ -17,7 +17,7 @@ Author:
Leonardo de Moura (leonardo) 2012-01-05
Notes:
--*/
#include "api/z3.h"
#include "api/api_log_macros.h"
@ -32,12 +32,12 @@ static void reset_rcf_cancel(Z3_context c) {
// no-op
}
static Z3_rcf_num from_rcnumeral(rcnumeral a) {
return reinterpret_cast<Z3_rcf_num>(a.data());
static Z3_rcf_num from_rcnumeral(rcnumeral a) {
return reinterpret_cast<Z3_rcf_num>(a.data());
}
static rcnumeral to_rcnumeral(Z3_rcf_num a) {
return rcnumeral::mk(a);
static rcnumeral to_rcnumeral(Z3_rcf_num a) {
return rcnumeral::mk(a);
}
extern "C" {
@ -179,7 +179,7 @@ extern "C" {
RETURN_Z3(from_rcnumeral(r));
Z3_CATCH_RETURN(nullptr);
}
Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a) {
Z3_TRY;
LOG_Z3_rcf_neg(c, a);
@ -302,4 +302,139 @@ extern "C" {
Z3_CATCH;
}
bool Z3_API Z3_rcf_is_rational(Z3_context c, Z3_rcf_num a) {
Z3_TRY;
LOG_Z3_rcf_is_rational(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).is_rational(to_rcnumeral(a));
Z3_CATCH_RETURN(false);
}
bool Z3_API Z3_rcf_is_algebraic(Z3_context c, Z3_rcf_num a) {
Z3_TRY;
LOG_Z3_rcf_is_algebraic(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).is_algebraic(to_rcnumeral(a));
Z3_CATCH_RETURN(false);
}
bool Z3_API Z3_rcf_is_infinitesimal(Z3_context c, Z3_rcf_num a) {
Z3_TRY;
LOG_Z3_rcf_is_infinitesimal(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).is_infinitesimal(to_rcnumeral(a));
Z3_CATCH_RETURN(false);
}
bool Z3_API Z3_rcf_is_transcendental(Z3_context c, Z3_rcf_num a) {
Z3_TRY;
LOG_Z3_rcf_is_transcendental(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).is_transcendental(to_rcnumeral(a));
Z3_CATCH_RETURN(false);
}
unsigned Z3_API Z3_rcf_extension_index(Z3_context c, Z3_rcf_num a) {
Z3_TRY;
LOG_Z3_rcf_extension_index(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).extension_index(to_rcnumeral(a));
Z3_CATCH_RETURN(false);
}
Z3_symbol Z3_API Z3_rcf_transcendental_name(Z3_context c, Z3_rcf_num a) {
Z3_TRY;
LOG_Z3_rcf_transcendental_name(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return of_symbol(rcfm(c).transcendental_name(to_rcnumeral(a)));
Z3_CATCH_RETURN(of_symbol(symbol::null));
}
Z3_symbol Z3_API Z3_rcf_infinitesimal_name(Z3_context c, Z3_rcf_num a) {
Z3_TRY;
LOG_Z3_rcf_infinitesimal_name(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return of_symbol(rcfm(c).infinitesimal_name(to_rcnumeral(a)));
Z3_CATCH_RETURN(of_symbol(symbol::null));
}
unsigned Z3_API Z3_rcf_num_coefficients(Z3_context c, Z3_rcf_num a)
{
Z3_TRY;
LOG_Z3_rcf_num_coefficients(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).num_coefficients(to_rcnumeral(a));
Z3_CATCH_RETURN(0);
}
Z3_rcf_num Z3_API Z3_rcf_coefficient(Z3_context c, Z3_rcf_num a, unsigned i)
{
Z3_TRY;
LOG_Z3_rcf_coefficient(c, a, i);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return from_rcnumeral(rcfm(c).get_coefficient(to_rcnumeral(a), i));
Z3_CATCH_RETURN(nullptr);
}
int Z3_API Z3_rcf_interval(Z3_context c, Z3_rcf_num a, int * lower_is_inf, int * lower_is_open, Z3_rcf_num * lower, int * upper_is_inf, int * upper_is_open, Z3_rcf_num * upper) {
Z3_TRY;
LOG_Z3_rcf_interval(c, a, lower_is_inf, lower_is_open, lower, upper_is_inf, upper_is_open, upper);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
rcnumeral r_lower, r_upper;
bool r = rcfm(c).get_interval(to_rcnumeral(a), *lower_is_inf, *lower_is_open, r_lower, *upper_is_inf, *upper_is_open, r_upper);
*lower = from_rcnumeral(r_lower);
*upper = from_rcnumeral(r_upper);
return r;
Z3_CATCH_RETURN(0);
}
unsigned Z3_API Z3_rcf_num_sign_conditions(Z3_context c, Z3_rcf_num a)
{
Z3_TRY;
LOG_Z3_rcf_num_sign_conditions(c, a);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).num_sign_conditions(to_rcnumeral(a));
Z3_CATCH_RETURN(0);
}
int Z3_API Z3_rcf_sign_condition_sign(Z3_context c, Z3_rcf_num a, unsigned i)
{
Z3_TRY;
LOG_Z3_rcf_sign_condition_sign(c, a, i);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).get_sign_condition_sign(to_rcnumeral(a), i);
Z3_CATCH_RETURN(0);
}
unsigned Z3_API Z3_rcf_num_sign_condition_coefficients(Z3_context c, Z3_rcf_num a, unsigned i)
{
Z3_TRY;
LOG_Z3_rcf_num_sign_condition_coefficients(c, a, i);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return rcfm(c).num_sign_condition_coefficients(to_rcnumeral(a), i);
Z3_CATCH_RETURN(0);
}
Z3_rcf_num Z3_API Z3_rcf_sign_condition_coefficient(Z3_context c, Z3_rcf_num a, unsigned i, unsigned j)
{
Z3_TRY;
LOG_Z3_rcf_sign_condition_coefficient(c, a, i, j);
RESET_ERROR_CODE();
reset_rcf_cancel(c);
return from_rcnumeral(rcfm(c).get_sign_condition_coefficient(to_rcnumeral(a), i, j));
Z3_CATCH_RETURN(nullptr);
}
};

View file

@ -984,14 +984,14 @@ extern "C" {
Z3_TRY;
RESET_ERROR_CODE();
init_solver(c, s);
user_propagator::on_clause_eh_t _on_clause = [=](void* user_ctx, expr* proof, unsigned n, expr* const* _literals) {
user_propagator::on_clause_eh_t _on_clause = [=](void* user_ctx, expr* proof, unsigned nd, unsigned const* deps, unsigned n, expr* const* _literals) {
Z3_ast_vector_ref * literals = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(literals);
expr_ref pr(proof, mk_c(c)->m());
scoped_ast_vector _sc(literals);
for (unsigned i = 0; i < n; ++i)
literals->m_ast_vector.push_back(_literals[i]);
on_clause_eh(user_ctx, of_expr(pr.get()), of_ast_vector(literals));
on_clause_eh(user_ctx, of_expr(pr.get()), nd, deps, of_ast_vector(literals));
};
to_solver_ref(s)->register_on_clause(user_context, _on_clause);
auto& solver = *to_solver(s);

View file

@ -368,7 +368,7 @@ namespace z3 {
func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range);
/**
* \brief add function definition body to declaration decl. decl needs to be declared using context::<recfun>.
* \brief add function definition body to declaration decl. decl needs to be declared using context::recfun.
* @param decl
* @param args
* @param body
@ -4214,17 +4214,20 @@ namespace z3 {
return expr(ctx(), r);
}
typedef std::function<void(expr const& proof, expr_vector const& clause)> on_clause_eh_t;
typedef std::function<void(expr const& proof, std::vector<unsigned> const& deps, expr_vector const& clause)> on_clause_eh_t;
class on_clause {
context& c;
on_clause_eh_t m_on_clause;
static void _on_clause_eh(void* _ctx, Z3_ast _proof, Z3_ast_vector _literals) {
static void _on_clause_eh(void* _ctx, Z3_ast _proof, unsigned n, unsigned const* dep, Z3_ast_vector _literals) {
on_clause* ctx = static_cast<on_clause*>(_ctx);
expr_vector lits(ctx->c, _literals);
expr proof(ctx->c, _proof);
ctx->m_on_clause(proof, lits);
std::vector<unsigned> deps;
for (unsigned i = 0; i < n; ++i)
deps.push_back(dep[i]);
ctx->m_on_clause(proof, deps, lits);
}
public:
on_clause(solver& s, on_clause_eh_t& on_clause_eh): c(s.ctx()) {

View file

@ -9,7 +9,7 @@ set(VER_TWEAK "${Z3_VERSION_TWEAK}")
# Generate Native.cs
set(Z3_DOTNET_NATIVE_FILE "${CMAKE_CURRENT_BINARY_DIR}/Native.cs")
add_custom_command(OUTPUT "${Z3_DOTNET_NATIVE_FILE}"
COMMAND "${PYTHON_EXECUTABLE}"
COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/update_api.py"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"--dotnet-output-dir"
@ -25,7 +25,7 @@ add_custom_command(OUTPUT "${Z3_DOTNET_NATIVE_FILE}"
# Generate Enumerations.cs
set(Z3_DOTNET_CONST_FILE "${CMAKE_CURRENT_BINARY_DIR}/Enumerations.cs")
add_custom_command(OUTPUT "${Z3_DOTNET_CONST_FILE}"
COMMAND "${PYTHON_EXECUTABLE}"
COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"--dotnet-output-dir"

View file

@ -91,7 +91,13 @@ namespace Microsoft.Z3
/// </summary>
~Constructor()
{
Native.Z3_del_constructor(Context.nCtx, NativeObject);
if (Context.nCtx != IntPtr.Zero) {
lock (Context)
{
if (Context.nCtx != IntPtr.Zero)
Native.Z3_del_constructor(Context.nCtx, NativeObject);
}
}
}
#region Internal

View file

@ -7,6 +7,8 @@
<AssemblyName>Microsoft.Z3</AssemblyName>
<RootNamespace>Microsoft.Z3</RootNamespace>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Title>Z3 .NET Interface</Title>
<AssemblyTitle>Z3 .NET Interface</AssemblyTitle>
@ -15,8 +17,8 @@
<Description>Z3 is a satisfiability modulo theories solver from Microsoft Research.</Description>
<AssemblyDescription>.NET Interface to the Z3 Theorem Prover</AssemblyDescription>
<Copyright>Copyright (C) 2006-2019 Microsoft Corporation</Copyright>
<AssemblyCopyright>Copyright (C) 2006-2019 Microsoft Corporation</AssemblyCopyright>
<Copyright>Copyright (C) 2006- Microsoft Corporation</Copyright>
<AssemblyCopyright>Copyright (C) 2006- Microsoft Corporation</AssemblyCopyright>
<Company>Microsoft Corporation</Company>
<AssemblyCompany>Microsoft Corporation</AssemblyCompany>
@ -65,6 +67,11 @@
${Z3_DOTNET_COMPILE_ITEMS}
</ItemGroup>
<!-- Readme -->
<ItemGroup>
<None Include="${CMAKE_CURRENT_LIST_DIR}/README.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<!-- Legacy .NET framework native library helper routines -->
<ItemGroup>
<Content Include="${CMAKE_CURRENT_LIST_DIR}/Microsoft.Z3.props">

View file

@ -30,6 +30,7 @@ namespace Microsoft.Z3
using Z3_context = System.IntPtr;
using Z3_solver = System.IntPtr;
using voidp = System.IntPtr;
using uintp = System.IntPtr;
using Z3_ast = System.IntPtr;
using Z3_ast_vector = System.IntPtr;
@ -60,7 +61,7 @@ namespace Microsoft.Z3
Native.Z3_on_clause_eh on_clause_eh;
static void _on_clause(voidp ctx, Z3_ast _proof_hint, Z3_ast_vector _clause)
static void _on_clause(voidp ctx, Z3_ast _proof_hint, uint n, uint[] deps, Z3_ast_vector _clause)
{
var onc = (OnClause)GCHandle.FromIntPtr(ctx).Target;
using var proof_hint = Expr.Create(onc.ctx, _proof_hint);

View file

@ -220,7 +220,7 @@ namespace Microsoft.Z3
/// <summary>
/// Check satisfiability of asserted constraints.
/// Produce a model that (when the objectives are bounded and
/// don't use strict inequalities) meets the objectives.
/// don't use strict inequalities) is optimal.
/// </summary>
///
public Status Check(params Expr[] assumptions)

3
src/api/dotnet/README.md Normal file
View file

@ -0,0 +1,3 @@
# Z3 Nuget Package
For more information see [the Z3 github page](https://github.com/z3prover/z3.git)

View file

@ -16,7 +16,7 @@ set(Z3_JAVA_PACKAGE_NAME "com.microsoft.z3")
set(Z3_JAVA_NATIVE_JAVA "${CMAKE_CURRENT_BINARY_DIR}/Native.java")
set(Z3_JAVA_NATIVE_CPP "${CMAKE_CURRENT_BINARY_DIR}/Native.cpp")
add_custom_command(OUTPUT "${Z3_JAVA_NATIVE_JAVA}" "${Z3_JAVA_NATIVE_CPP}"
COMMAND "${PYTHON_EXECUTABLE}"
COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/update_api.py"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"--java-input-dir"
@ -74,7 +74,7 @@ foreach (enum_file ${Z3_JAVA_ENUMERATION_PACKAGE_FILES})
)
endforeach()
add_custom_command(OUTPUT ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH}
COMMAND "${PYTHON_EXECUTABLE}"
COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"--java-output-dir"

View file

@ -193,7 +193,7 @@ public class Optimize extends Z3Object {
/**
* Check satisfiability of asserted constraints.
* Produce a model that (when the objectives are bounded and
* don't use strict inequalities) meets the objectives.
* don't use strict inequalities) is optimal.
**/
public Status Check(Expr<BoolSort>... assumptions)
{

View file

@ -1,7 +1,8 @@
{
"name": "z3-solver",
"requires": true,
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@ampproject/remapping": {
"version": "2.2.0",
@ -109,25 +110,6 @@
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
"dev": true
},
"@babel/helper-function-name": {
"version": "7.19.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
"integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
"dev": true,
"requires": {
"@babel/template": "^7.18.10",
"@babel/types": "^7.19.0"
}
},
"@babel/helper-hoist-variables": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
"dev": true,
"requires": {
"@babel/types": "^7.18.6"
}
},
"@babel/helper-module-imports": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
@ -361,21 +343,141 @@
}
},
"@babel/traverse": {
"version": "7.19.4",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz",
"integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==",
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.18.6",
"@babel/generator": "^7.19.4",
"@babel/helper-environment-visitor": "^7.18.9",
"@babel/helper-function-name": "^7.19.0",
"@babel/helper-hoist-variables": "^7.18.6",
"@babel/helper-split-export-declaration": "^7.18.6",
"@babel/parser": "^7.19.4",
"@babel/types": "^7.19.4",
"@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.23.0",
"@babel/types": "^7.23.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
"dependencies": {
"@babel/code-frame": {
"version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dev": true,
"requires": {
"@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
}
},
"@babel/generator": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"dev": true,
"requires": {
"@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
}
},
"@babel/helper-environment-visitor": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"dev": true
},
"@babel/helper-function-name": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"dev": true,
"requires": {
"@babel/template": "^7.22.15",
"@babel/types": "^7.23.0"
}
},
"@babel/helper-hoist-variables": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
"dev": true,
"requires": {
"@babel/types": "^7.22.5"
}
},
"@babel/helper-split-export-declaration": {
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
"dev": true,
"requires": {
"@babel/types": "^7.22.5"
}
},
"@babel/helper-string-parser": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true
},
"@babel/helper-validator-identifier": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"dev": true
},
"@babel/highlight": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0"
}
},
"@babel/parser": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"dev": true
},
"@babel/template": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.22.13",
"@babel/parser": "^7.22.15",
"@babel/types": "^7.22.15"
}
},
"@babel/types": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"dev": true,
"requires": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0"
}
},
"@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
"dev": true,
"requires": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
}
}
}
},
"@babel/types": {

View file

@ -69,7 +69,7 @@ const fns = JSON.stringify(exportedFuncs());
const methods = '["ccall","FS","allocate","UTF8ToString","intArrayFromString","ALLOC_NORMAL"]';
const libz3a = path.normalize('../../../build/libz3.a');
spawnSync(
`emcc build/async-fns.cc ${libz3a} --std=c++20 --pre-js src/low-level/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=${methods} -s EXPORTED_FUNCTIONS=${fns} -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s DEMANGLE_SUPPORT=1 -s TOTAL_MEMORY=1GB -I z3/src/api/ -o build/z3-built.js`,
`emcc build/async-fns.cc ${libz3a} --std=c++20 --pre-js src/low-level/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=${methods} -s EXPORTED_FUNCTIONS=${fns} -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s DEMANGLE_SUPPORT=1 -s TOTAL_MEMORY=1GB -s TOTAL_STACK=20MB -I z3/src/api/ -o build/z3-built.js`,
);
fs.rmSync(ccWrapperPath);

View file

@ -26,7 +26,7 @@ struct
let (major, minor, build, revision) = Z3native.get_version ()
let full_version : string = Z3native.get_full_version()
let to_string =
string_of_int major ^ "." ^
string_of_int minor ^ "." ^
@ -45,12 +45,12 @@ let mk_list f n =
let check_int32 v = v = Int32.to_int (Int32.of_int v)
let mk_int_expr ctx v ty =
let mk_int_expr ctx v ty =
if not (check_int32 v) then
Z3native.mk_numeral ctx (string_of_int v) ty
else
Z3native.mk_int ctx v ty
let mk_context (settings:(string * string) list) =
let cfg = Z3native.mk_config () in
let f e = Z3native.set_param_value cfg (fst e) (snd e) in
@ -62,6 +62,9 @@ let mk_context (settings:(string * string) list) =
Z3native.enable_concurrent_dec_ref res;
res
let interrupt (ctx:context) =
Z3native.interrupt ctx
module Symbol =
struct
type symbol = Z3native.symbol
@ -721,7 +724,7 @@ struct
let mk_exists = _internal_mk_quantifier ~universal:false
let mk_exists_const = _internal_mk_quantifier_const ~universal:false
let mk_lambda_const ctx bound body = Z3native.mk_lambda_const ctx (List.length bound) bound body
let mk_lambda ctx bound body =
let mk_lambda ctx bound body =
let names = List.map (fun (x,_) -> x) bound in
let sorts = List.map (fun (_,y) -> y) bound in
Z3native.mk_lambda ctx (List.length bound) sorts names body
@ -855,15 +858,6 @@ struct
module Constructor =
struct
type constructor = Z3native.constructor
module FieldNumTable = Hashtbl.Make(struct
type t = AST.ast
let equal x y = AST.compare x y = 0
let hash = AST.hash
end)
let _field_nums = FieldNumTable.create 0
let create (ctx:context) (name:Symbol.symbol) (recognizer:Symbol.symbol) (field_names:Symbol.symbol list) (sorts:Sort.sort option list) (sort_refs:int list) =
let n = List.length field_names in
if n <> List.length sorts then
@ -879,10 +873,9 @@ struct
(let f x = match x with None -> Z3native.mk_null_ast ctx | Some s -> s in
List.map f sorts)
sort_refs in
FieldNumTable.add _field_nums no n;
no
let get_num_fields (x:constructor) = FieldNumTable.find _field_nums x
let get_num_fields (x:constructor) = Z3native.constructor_num_fields (gc x) x
let get_constructor_decl (x:constructor) =
let (a, _, _) = (Z3native.query_constructor (gc x) x (get_num_fields x)) in
@ -917,10 +910,10 @@ struct
let mk_sort_s (ctx:context) (name:string) (constructors:Constructor.constructor list) =
mk_sort ctx (Symbol.mk_string ctx name) constructors
let mk_sort_ref (ctx: context) (name:Symbol.symbol) =
Z3native.mk_datatype_sort ctx name
let mk_sort_ref_s (ctx: context) (name: string) =
mk_sort_ref ctx (Symbol.mk_string ctx name)
@ -1249,7 +1242,7 @@ end
module Seq =
struct
let mk_seq_sort = Z3native.mk_seq_sort
let is_seq_sort = Z3native.is_seq_sort
let is_seq_sort = Z3native.is_seq_sort
let mk_re_sort = Z3native.mk_re_sort
let is_re_sort = Z3native.is_re_sort
let mk_string_sort = Z3native.mk_string_sort
@ -1264,7 +1257,7 @@ struct
let mk_seq_concat ctx args = Z3native.mk_seq_concat ctx (List.length args) args
let mk_seq_prefix = Z3native.mk_seq_prefix
let mk_seq_suffix = Z3native.mk_seq_suffix
let mk_seq_contains = Z3native.mk_seq_contains
let mk_seq_contains = Z3native.mk_seq_contains
let mk_seq_extract = Z3native.mk_seq_extract
let mk_seq_replace = Z3native.mk_seq_replace
let mk_seq_at = Z3native.mk_seq_at
@ -1509,13 +1502,15 @@ struct
in
Z3native.apply_result_inc_ref (gc x) arn;
let sg = Z3native.apply_result_get_num_subgoals (gc x) arn in
let res = if sg = 0 then
raise (Error "No subgoals")
else
Z3native.apply_result_get_subgoal (gc x) arn 0 in
Z3native.apply_result_dec_ref (gc x) arn;
Z3native.tactic_dec_ref (gc x) tn;
res
if sg = 0 then (
Z3native.apply_result_dec_ref (gc x) arn;
Z3native.tactic_dec_ref (gc x) tn;
raise (Error "No subgoals"))
else
let res:goal = Z3native.apply_result_get_subgoal (gc x) arn 0 in
Z3native.apply_result_dec_ref (gc x) arn;
Z3native.tactic_dec_ref (gc x) tn;
res
let mk_goal = Z3native.mk_goal
@ -1889,9 +1884,9 @@ struct
| _ -> UNKNOWN
let get_model x =
try
try
let q = Z3native.solver_get_model (gc x) x in
if Z3native.is_null_model q then None else Some q
if Z3native.is_null_model q then None else Some q
with | _ -> None
let get_proof x =
@ -1916,6 +1911,9 @@ struct
let add_simplifier = Z3native.solver_add_simplifier
let translate x = Z3native.solver_translate (gc x) x
let to_string x = Z3native.solver_to_string (gc x) x
let interrupt (ctx:context) (s:solver) =
Z3native.solver_interrupt ctx s
end
@ -2077,6 +2075,123 @@ struct
end
module RCF =
struct
type rcf_num = Z3native.rcf_num
let del (ctx:context) (a:rcf_num) = Z3native.rcf_del ctx a
let del_list (ctx:context) (ns:rcf_num list) = List.iter (fun a -> Z3native.rcf_del ctx a) ns
let mk_rational (ctx:context) (v:string) = Z3native.rcf_mk_rational ctx v
let mk_small_int (ctx:context) (v:int) = Z3native.rcf_mk_small_int ctx v
let mk_pi (ctx:context) = Z3native.rcf_mk_pi ctx
let mk_e (ctx:context) = Z3native.rcf_mk_e ctx
let mk_infinitesimal (ctx:context) = Z3native.rcf_mk_infinitesimal ctx
let mk_roots (ctx:context) (a:rcf_num list) =
let n, r = Z3native.rcf_mk_roots ctx (List.length a) a in
List.init n (fun x -> List.nth r x)
let add (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_add ctx a b
let sub (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_sub ctx a b
let mul (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_mul ctx a b
let div (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_div ctx a b
let neg (ctx:context) (a:rcf_num) = Z3native.rcf_neg ctx a
let inv (ctx:context) (a:rcf_num) = Z3native.rcf_neg ctx a
let power (ctx:context) (a:rcf_num) (k:int) = Z3native.rcf_power ctx a k
let lt (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_lt ctx a b
let gt (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_gt ctx a b
let le (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_le ctx a b
let ge (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_ge ctx a b
let eq (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_eq ctx a b
let neq (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_neq ctx a b
let num_to_string (ctx:context) (a:rcf_num) (compact:bool) (html:bool) = Z3native.rcf_num_to_string ctx a compact html
let num_to_decimal_string (ctx:context) (a:rcf_num) (prec:int) = Z3native.rcf_num_to_decimal_string ctx a prec
let get_numerator_denominator (ctx:context) (a:rcf_num) = Z3native.rcf_get_numerator_denominator ctx a
let is_rational (ctx:context) (a:rcf_num) = Z3native.rcf_is_rational ctx a
let is_algebraic (ctx:context) (a:rcf_num) = Z3native.rcf_is_algebraic ctx a
let is_infinitesimal (ctx:context) (a:rcf_num) = Z3native.rcf_is_infinitesimal ctx a
let is_transcendental (ctx:context) (a:rcf_num) = Z3native.rcf_is_transcendental ctx a
let extension_index (ctx:context) (a:rcf_num) = Z3native.rcf_extension_index ctx a
let transcendental_name (ctx:context) (a:rcf_num) = Z3native.rcf_transcendental_name ctx a
let infinitesimal_name (ctx:context) (a:rcf_num) = Z3native.rcf_infinitesimal_name ctx a
let num_coefficients (ctx:context) (a:rcf_num) = Z3native.rcf_num_coefficients ctx a
let get_coefficient (ctx:context) (a:rcf_num) (i:int) = Z3native.rcf_coefficient ctx a i
let coefficients (ctx:context) (a:rcf_num) =
List.init (num_coefficients ctx a) (fun i -> Z3native.rcf_coefficient ctx a i)
type interval = {
lower_is_inf : bool;
lower_is_open : bool;
lower : rcf_num;
upper_is_inf : bool;
upper_is_open : bool;
upper : rcf_num;
}
let root_interval (ctx:context) (a:rcf_num) =
let ok, linf, lopen, l, uinf, uopen, u = Z3native.rcf_interval ctx a in
let i:interval = {
lower_is_inf = linf != 0;
lower_is_open = lopen != 0;
lower = l;
upper_is_inf = uinf != 0;
upper_is_open = uopen != 0;
upper = u } in
if ok != 0 then Some i else None
let sign_condition_sign (ctx:context) (a:rcf_num) (i:int) = Z3native.rcf_sign_condition_sign ctx a i
let sign_condition_coefficient (ctx:context) (a:rcf_num) (i:int) (j:int) = Z3native.rcf_sign_condition_coefficient ctx a i j
let num_sign_condition_coefficients (ctx:context) (a:rcf_num) (i:int) = Z3native.rcf_num_sign_condition_coefficients ctx a i
let sign_condition_coefficients (ctx:context) (a:rcf_num) (i:int) =
let n = Z3native.rcf_num_sign_condition_coefficients ctx a i in
List.init n (fun j -> Z3native.rcf_sign_condition_coefficient ctx a i j)
let sign_conditions (ctx:context) (a:rcf_num) =
let n = Z3native.rcf_num_sign_conditions ctx a in
List.init n (fun i ->
(let nc = Z3native.rcf_num_sign_condition_coefficients ctx a i in
List.init nc (fun j -> Z3native.rcf_sign_condition_coefficient ctx a i j)),
Z3native.rcf_sign_condition_sign ctx a i)
type root = {
obj : rcf_num;
polynomial : rcf_num list;
interval : interval option;
sign_conditions : (rcf_num list * int) list;
}
let roots (ctx:context) (a:rcf_num list) =
let rs = mk_roots ctx a in
List.map
(fun r -> {
obj = r;
polynomial = coefficients ctx r;
interval = root_interval ctx r;
sign_conditions = sign_conditions ctx r})
rs
let del_root (ctx:context) (r:root) =
del ctx r.obj;
List.iter (fun n -> del ctx n) r.polynomial;
List.iter (fun (ns, _) -> del_list ctx ns) r.sign_conditions
let del_roots (ctx:context) (rs:root list) =
List.iter (fun r -> del_root ctx r) rs
end
let set_global_param = Z3native.global_param_set
let get_global_param id =

View file

@ -48,6 +48,12 @@ type context
*)
val mk_context : (string * string) list -> context
(** Interrupt the execution of a Z3 procedure.
This procedure can be used to interrupt: solvers, simplifiers and tactics.
Note: Tactic.interrupt is an alias for this. *)
val interrupt: context -> unit
(** Interaction logging for Z3
Interaction logs are used to record calls into the API into a text file.
The text file can be replayed using z3. It has to be the same version of z3
@ -1068,13 +1074,13 @@ sig
if the corresponding sort reference is 0, then the value in sort_refs should be an index
referring to one of the recursive datatypes that is declared. *)
val mk_constructor_s : context -> string -> Symbol.symbol -> Symbol.symbol list -> Sort.sort option list -> int list -> Constructor.constructor
(* Create a forward reference to a recursive datatype being declared.
The forward reference can be used in a nested occurrence: the range of an array
or as element sort of a sequence. The forward reference should only be used when
used in an accessor for a recursive datatype that gets declared. *)
val mk_sort_ref : context -> Symbol.symbol -> Sort.sort
(* [mk_sort_ref_s ctx s] is [mk_sort_ref ctx (Symbol.mk_string ctx s)] *)
val mk_sort_ref_s : context -> string -> Sort.sort
@ -1653,8 +1659,8 @@ sig
- The \c ceiling of [t1/t2] if \c t2 is different from zero, and [t1*t2 < 0].
If [t2] is zero, then the result is is not uniquely specified.
It can be set to any value that satisfies the constraints
If [t2] is zero, then the result is is not uniquely specified.
It can be set to any value that satisfies the constraints
where signed division is used.
The arguments must have the same bit-vector sort. *)
val mk_sdiv : context -> Expr.expr -> Expr.expr -> Expr.expr
@ -1662,8 +1668,8 @@ sig
(** Unsigned remainder.
It is defined as [t1 - (t1 /u t2) * t2], where [/u] represents unsigned division.
If [t2] is zero, then the result is not uniquely specified.
It can be set to any value that satisfies the constraints
If [t2] is zero, then the result is not uniquely specified.
It can be set to any value that satisfies the constraints
where unsigned remainder is used.
The arguments must have the same bit-vector sort. *)
val mk_urem : context -> Expr.expr -> Expr.expr -> Expr.expr
@ -1673,16 +1679,16 @@ sig
It is defined as [t1 - (t1 /s t2) * t2], where [/s] represents signed division.
The most significant bit (sign) of the result is equal to the most significant bit of \c t1.
If [t2] is zero, then the result is not uniquely specified.
It can be set to any value that satisfies the constraints
If [t2] is zero, then the result is not uniquely specified.
It can be set to any value that satisfies the constraints
where signed remainder is used.
The arguments must have the same bit-vector sort. *)
val mk_srem : context -> Expr.expr -> Expr.expr -> Expr.expr
(** Two's complement signed remainder (sign follows divisor).
If [t2] is zero, then the result is not uniquely specified.
It can be set to any value that satisfies the constraints
If [t2] is zero, then the result is not uniquely specified.
It can be set to any value that satisfies the constraints
where two's complement signed remainder is used.
The arguments must have the same bit-vector sort. *)
val mk_smod : context -> Expr.expr -> Expr.expr -> Expr.expr
@ -1864,7 +1870,7 @@ sig
end
(** Sequences, Strings and Regular Expressions **)
module Seq :
module Seq :
sig
(** create a sequence sort *)
val mk_seq_sort : context -> Sort.sort -> Sort.sort
@ -1872,9 +1878,9 @@ sig
(** test if sort is a sequence sort *)
val is_seq_sort : context -> Sort.sort -> bool
(** create regular expression sorts over sequences of the argument sort *)
(** create regular expression sorts over sequences of the argument sort *)
val mk_re_sort : context -> Sort.sort -> Sort.sort
(** test if sort is a regular expression sort *)
val is_re_sort : context -> Sort.sort -> bool
@ -1885,7 +1891,7 @@ sig
val mk_char_sort : context -> Sort.sort
(** test if sort is a string sort (a sequence of 8-bit bit-vectors) *)
val is_string_sort : context -> Sort.sort -> bool
val is_string_sort : context -> Sort.sort -> bool
(** test if sort is a char sort *)
val is_char_sort : context -> Sort.sort -> bool
@ -1894,51 +1900,51 @@ sig
val mk_string : context -> string -> Expr.expr
(** test if expression is a string *)
val is_string : context -> Expr.expr -> bool
val is_string : context -> Expr.expr -> bool
(** retrieve string from string Expr.expr *)
val get_string : context -> Expr.expr -> string
val get_string : context -> Expr.expr -> string
(** the empty sequence over base sort *)
val mk_seq_empty : context -> Sort.sort -> Expr.expr
val mk_seq_empty : context -> Sort.sort -> Expr.expr
(** a unit sequence *)
val mk_seq_unit : context -> Expr.expr -> Expr.expr
val mk_seq_unit : context -> Expr.expr -> Expr.expr
(** sequence concatenation *)
val mk_seq_concat : context -> Expr.expr list -> Expr.expr
val mk_seq_concat : context -> Expr.expr list -> Expr.expr
(** predicate if the first argument is a prefix of the second *)
val mk_seq_prefix : context -> Expr.expr -> Expr.expr -> Expr.expr
val mk_seq_prefix : context -> Expr.expr -> Expr.expr -> Expr.expr
(** predicate if the first argument is a suffix of the second *)
val mk_seq_suffix : context -> Expr.expr -> Expr.expr -> Expr.expr
val mk_seq_suffix : context -> Expr.expr -> Expr.expr -> Expr.expr
(** predicate if the first argument contains the second *)
val mk_seq_contains : context -> Expr.expr -> Expr.expr -> Expr.expr
val mk_seq_contains : context -> Expr.expr -> Expr.expr -> Expr.expr
(** extract sub-sequence starting at index given by second argument and of length provided by third argument *)
val mk_seq_extract : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
val mk_seq_extract : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** replace first occurrence of second argument by third *)
val mk_seq_replace : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
val mk_seq_replace : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** a unit sequence at index provided by second argument *)
val mk_seq_at : context -> Expr.expr -> Expr.expr -> Expr.expr
val mk_seq_at : context -> Expr.expr -> Expr.expr -> Expr.expr
(** length of a sequence *)
val mk_seq_length : context -> Expr.expr -> Expr.expr
(** [mk_seq_nth ctx s index] retrieves from [s] the element at position [index].
val mk_seq_length : context -> Expr.expr -> Expr.expr
(** [mk_seq_nth ctx s index] retrieves from [s] the element at position [index].
The function is under-specified if the index is out of bounds. *)
val mk_seq_nth : context -> Expr.expr -> Expr.expr -> Expr.expr
(** index of the first occurrence of the second argument in the first *)
val mk_seq_index : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
val mk_seq_index : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** [mk_seq_last_index ctx s substr] occurence of [substr] in the sequence [s] *)
val mk_seq_last_index : context -> Expr.expr -> Expr.expr -> Expr.expr
(** retrieve integer expression encoded in string *)
val mk_str_to_int : context -> Expr.expr -> Expr.expr
@ -1950,7 +1956,7 @@ sig
val mk_str_lt : context -> Expr.expr -> Expr.expr -> Expr.expr
(** convert an integer expression to a string *)
val mk_int_to_str : context -> Expr.expr -> Expr.expr
val mk_int_to_str : context -> Expr.expr -> Expr.expr
(** [mk_string_to_code ctx s] convert a unit length string [s] to integer code *)
val mk_string_to_code : context -> Expr.expr -> Expr.expr
@ -1965,43 +1971,43 @@ sig
val mk_sbv_to_str : context -> Expr.expr -> Expr.expr
(** create regular expression that accepts the argument sequence *)
val mk_seq_to_re : context -> Expr.expr -> Expr.expr
val mk_seq_to_re : context -> Expr.expr -> Expr.expr
(** regular expression membership predicate *)
val mk_seq_in_re : context -> Expr.expr -> Expr.expr -> Expr.expr
val mk_seq_in_re : context -> Expr.expr -> Expr.expr -> Expr.expr
(** regular expression plus *)
val mk_re_plus : context -> Expr.expr -> Expr.expr
val mk_re_plus : context -> Expr.expr -> Expr.expr
(** regular expression star *)
val mk_re_star : context -> Expr.expr -> Expr.expr
val mk_re_star : context -> Expr.expr -> Expr.expr
(** optional regular expression *)
val mk_re_option : context -> Expr.expr -> Expr.expr
val mk_re_option : context -> Expr.expr -> Expr.expr
(** union of regular expressions *)
val mk_re_union : context -> Expr.expr list -> Expr.expr
val mk_re_union : context -> Expr.expr list -> Expr.expr
(** concatenation of regular expressions *)
val mk_re_concat : context -> Expr.expr list -> Expr.expr
val mk_re_concat : context -> Expr.expr list -> Expr.expr
(** regular expression for the range between two characters *)
val mk_re_range : context -> Expr.expr -> Expr.expr -> Expr.expr
val mk_re_range : context -> Expr.expr -> Expr.expr -> Expr.expr
(** bounded loop regular expression *)
val mk_re_loop : context -> Expr.expr -> int -> int -> Expr.expr
val mk_re_loop : context -> Expr.expr -> int -> int -> Expr.expr
(** intersection of regular expressions *)
val mk_re_intersect : context -> Expr.expr list -> Expr.expr
(** the regular expression complement *)
val mk_re_complement : context -> Expr.expr -> Expr.expr
val mk_re_complement : context -> Expr.expr -> Expr.expr
(** the regular expression that accepts no sequences *)
val mk_re_empty : context -> Sort.sort -> Expr.expr
val mk_re_empty : context -> Sort.sort -> Expr.expr
(** the regular expression that accepts all sequences *)
val mk_re_full : context -> Sort.sort -> Expr.expr
val mk_re_full : context -> Sort.sort -> Expr.expr
(** [mk_char ctx i] converts an integer to a character *)
val mk_char : context -> int -> Expr.expr
@ -2339,7 +2345,7 @@ sig
(** Retrieves the sign of a floating-point literal. *)
val get_numeral_sign : context -> Expr.expr -> bool * int
(** Return the sign of a floating-point numeral as a bit-vector expression.
(** Return the sign of a floating-point numeral as a bit-vector expression.
Remark: NaN's do not have a bit-vector sign, so they are invalid arguments. *)
val get_numeral_sign_bv : context -> Expr.expr -> Expr.expr
@ -2349,11 +2355,11 @@ sig
(** Return the exponent value of a floating-point numeral as a signed integer *)
val get_numeral_exponent_int : context -> Expr.expr -> bool -> bool * int64
(** Return the exponent of a floating-point numeral as a bit-vector expression.
(** Return the exponent of a floating-point numeral as a bit-vector expression.
Remark: NaN's do not have a bit-vector exponent, so they are invalid arguments. *)
val get_numeral_exponent_bv : context -> Expr.expr -> bool -> Expr.expr
(** Return the significand value of a floating-point numeral as a bit-vector expression.
(** Return the significand value of a floating-point numeral as a bit-vector expression.
Remark: NaN's do not have a bit-vector significand, so they are invalid arguments. *)
val get_numeral_significand_bv : context -> Expr.expr -> Expr.expr
@ -2386,7 +2392,7 @@ sig
(** Indicates whether a floating-point numeral is negative. *)
val is_numeral_negative : context -> Expr.expr -> bool
(** Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. *)
val mk_to_ieee_bv : context -> Expr.expr -> Expr.expr
@ -3260,7 +3266,7 @@ sig
(** Assert multiple constraints (cs) into the solver, and track them (in the
unsat) core using the Boolean constants in ps.
This API is an alternative to {!check} with assumptions for extracting unsat cores.
Both APIs can be used in the same solver. The unsat core will contain a combination
of the Boolean variables provided using {!assert_and_track} and the Boolean literals
@ -3269,10 +3275,10 @@ sig
(** Assert a constraint (c) into the solver, and track it (in the unsat) core
using the Boolean constant p.
This API is an alternative to {!check} with assumptions for extracting unsat cores.
Both APIs can be used in the same solver. The unsat core will contain a combination
of the Boolean variables provided using {!assert_and_track} and the Boolean literals
This API is an alternative to {!check} with assumptions for extracting unsat cores.
Both APIs can be used in the same solver. The unsat core will contain a combination
of the Boolean variables provided using {!assert_and_track} and the Boolean literals
provided using {!check} with assumptions. *)
val assert_and_track : solver -> Expr.expr -> Expr.expr -> unit
@ -3342,6 +3348,15 @@ sig
(** A string representation of the solver. *)
val to_string : solver -> string
(** Solver local interrupt.
Normally you should use Z3_interrupt to cancel solvers because only
one solver is enabled concurrently per context.
However, per GitHub issue #1006, there are use cases where
it is more convenient to cancel a specific solver. Solvers
that are not selected for interrupts are left alone.*)
val interrupt: context -> solver -> unit
end
(** Fixedpoint solving *)
@ -3466,7 +3481,7 @@ sig
(** Add minimization objective. *)
val minimize : optimize -> Expr.expr -> handle
(** Checks whether the assertions in the context are satisfiable and solves objectives. *)
(** Check consistency and produce optimal values. *)
val check : optimize -> Solver.status
(** Retrieve model from satisfiable context *)
@ -3496,23 +3511,23 @@ sig
val get_statistics : optimize -> Statistics.statistics
(** Parse an SMT-LIB2 file with assertions, soft constraints and optimization
objectives. Add the parsed constraints and objectives to the optimization
objectives. Add the parsed constraints and objectives to the optimization
context. *)
val from_file : optimize -> string -> unit
(** Parse an SMT-LIB2 string with assertions, soft constraints and optimization
objectives. Add the parsed constraints and objectives to the optimization
(** Parse an SMT-LIB2 string with assertions, soft constraints and optimization
objectives. Add the parsed constraints and objectives to the optimization
context. *)
val from_string : optimize -> string -> unit
(** Return the set of asserted formulas on the optimization context. *)
(** Return the set of asserted formulas on the optimization context. *)
val get_assertions : optimize -> Expr.expr list
(** Return objectives on the optimization context. If the objective function
is a max-sat objective it is returned as a Pseudo-Boolean (minimization)
sum of the form (+ (if f1 w1 0) (if f2 w2 0) ...). If the objective
function is entered as a maximization objective, then return the
corresponding minimization objective. In this way the resulting
(** Return objectives on the optimization context. If the objective function
is a max-sat objective it is returned as a Pseudo-Boolean (minimization)
sum of the form (+ (if f1 w1 0) (if f2 w2 0) ...). If the objective
function is entered as a maximization objective, then return the
corresponding minimization objective. In this way the resulting
objective function is always returned as a minimization objective. *)
val get_objectives : optimize -> Expr.expr list
end
@ -3535,6 +3550,151 @@ sig
val parse_smtlib2_file : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> AST.ASTVector.ast_vector
end
(** Real closed field *)
module RCF :
sig
type rcf_num
(** Delete a RCF numeral created using the RCF API. *)
val del : context -> rcf_num -> unit
(** Delete RCF numerals created using the RCF API. *)
val del_list : context -> rcf_num list -> unit
(** Return a RCF rational using the given string. *)
val mk_rational : context -> string -> rcf_num
(** Return a RCF small integer. *)
val mk_small_int : context -> int -> rcf_num
(** Return Pi *)
val mk_pi : context -> rcf_num
(** Return e (Euler's constant) *)
val mk_e : context -> rcf_num
(** Return a new infinitesimal that is smaller than all elements in the Z3 field. *)
val mk_infinitesimal : context -> rcf_num
(** Extract the roots of a polynomial. Precondition: The input polynomial is not the zero polynomial. *)
val mk_roots : context -> rcf_num list -> rcf_num list
(** Addition *)
val add : context -> rcf_num -> rcf_num -> rcf_num
(** Subtraction *)
val sub : context -> rcf_num -> rcf_num -> rcf_num
(** Multiplication *)
val mul : context -> rcf_num -> rcf_num -> rcf_num
(** Division *)
val div : context -> rcf_num -> rcf_num -> rcf_num
(** Negation *)
val neg : context -> rcf_num -> rcf_num
(** Multiplicative Inverse *)
val inv : context -> rcf_num -> rcf_num
(** Power *)
val power : context -> rcf_num -> int -> rcf_num
(** less-than *)
val lt : context -> rcf_num -> rcf_num -> bool
(** greater-than *)
val gt : context -> rcf_num -> rcf_num -> bool
(** less-than or equal *)
val le : context -> rcf_num -> rcf_num -> bool
(** greater-than or equal *)
val ge : context -> rcf_num -> rcf_num -> bool
(** equality *)
val eq : context -> rcf_num -> rcf_num -> bool
(** not equal *)
val neq : context -> rcf_num -> rcf_num -> bool
(** Convert the RCF numeral into a string. *)
val num_to_string : context -> rcf_num -> bool -> bool -> string
(** Convert the RCF numeral into a string in decimal notation. *)
val num_to_decimal_string : context -> rcf_num -> int -> string
(** Extract the "numerator" and "denominator" of the given RCF numeral.
We have that \ccode{a = n/d}, moreover \c n and \c d are not represented using rational functions. *)
val get_numerator_denominator : context -> rcf_num -> (rcf_num * rcf_num)
(** Return \c true if \c a represents a rational number. *)
val is_rational : context -> rcf_num -> bool
(** Return \c true if \c a represents an algebraic number. *)
val is_algebraic : context -> rcf_num -> bool
(** Return \c true if \c a represents an infinitesimal. *)
val is_infinitesimal : context -> rcf_num -> bool
(** Return \c true if \c a represents a transcendental number. *)
val is_transcendental : context -> rcf_num -> bool
(** Return the index of a field extension. *)
val extension_index : context -> rcf_num -> int
(** Return the name of a transcendental. *)
val transcendental_name : context -> rcf_num -> Symbol.symbol
(** Return the name of an infinitesimal. *)
val infinitesimal_name : context -> rcf_num -> Symbol.symbol
(** Return the number of coefficients in an algebraic number. *)
val num_coefficients : context -> rcf_num -> int
(** Extract a coefficient from an algebraic number. *)
val get_coefficient : context -> rcf_num -> int -> rcf_num
(** Extract the coefficients from an algebraic number. *)
val coefficients : context -> rcf_num -> rcf_num list
(** Extract the sign of a sign condition from an algebraic number. *)
val sign_condition_sign : context -> rcf_num -> int -> int
(** Return the size of a sign condition polynomial. *)
val num_sign_condition_coefficients : context -> rcf_num -> int -> int
(** Extract a sign condition polynomial coefficient from an algebraic number. *)
val sign_condition_coefficient : context -> rcf_num -> int -> int -> rcf_num
(** Extract sign conditions from an algebraic number. *)
val sign_conditions : context -> rcf_num -> (rcf_num list * int) list
(** Extract the interval from an algebraic number. *)
type interval = {
lower_is_inf : bool;
lower_is_open : bool;
lower : rcf_num;
upper_is_inf : bool;
upper_is_open : bool;
upper : rcf_num;
}
val root_interval : context -> rcf_num -> interval option
type root = {
obj : rcf_num;
polynomial : rcf_num list;
interval : interval option;
sign_conditions : (rcf_num list * int) list;
}
val roots : context -> rcf_num list -> root list
val del_root : context -> root -> unit
val del_roots : context -> root list -> unit
end
(** Set a global (or module) parameter, which is shared by all Z3 contexts.

View file

@ -2,6 +2,12 @@
#include <string.h>
#include <assert.h>
#ifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#else
#define _Atomic(T) T
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -118,7 +124,7 @@ int compare_pointers(void* pt1, void* pt2) {
blocks that get copied. */
typedef struct {
Z3_context ctx;
unsigned long obj_count;
_Atomic(unsigned long) obj_count;
} Z3_context_plus_data;
/* A context is wrapped to an OCaml value by storing a pointer

View file

@ -33,7 +33,7 @@ endforeach()
# Generate z3core.py
add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3core.py"
COMMAND "${PYTHON_EXECUTABLE}"
COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/update_api.py"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"--z3py-output-dir"
@ -49,7 +49,7 @@ list(APPEND build_z3_python_bindings_target_depends "${z3py_bindings_build_dest}
# Generate z3consts.py
add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3consts.py"
COMMAND "${PYTHON_EXECUTABLE}"
COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"--z3py-output-dir"
@ -96,7 +96,7 @@ if (Z3_INSTALL_PYTHON_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"
COMMAND "${Python3_EXECUTABLE}" "-c"
"import sysconfig; print(sysconfig.get_path('purelib'))"
RESULT_VARIABLE exit_code
OUTPUT_VARIABLE CMAKE_INSTALL_PYTHON_PKG_DIR

View file

@ -18,7 +18,7 @@ from setuptools.command.bdist_egg import bdist_egg as _bdist_egg
build_env = dict(os.environ)
build_env['PYTHON'] = sys.executable
build_env['CXXFLAGS'] = build_env.get('CXXFLAGS', '') + " -std=c++11"
build_env['CXXFLAGS'] = build_env.get('CXXFLAGS', '') + " -std=c++17"
# determine where we're building and where sources are
ROOT_DIR = os.path.abspath(os.path.dirname(__file__))

View file

@ -683,6 +683,8 @@ def _to_sort_ref(s, ctx):
return SeqSortRef(s, ctx)
elif k == Z3_CHAR_SORT:
return CharSortRef(s, ctx)
elif k == Z3_TYPE_VAR:
return TypeVarRef(s, ctx)
return SortRef(s, ctx)
@ -708,6 +710,26 @@ def DeclareSort(name, ctx=None):
ctx = _get_ctx(ctx)
return SortRef(Z3_mk_uninterpreted_sort(ctx.ref(), to_symbol(name, ctx)), ctx)
class TypeVarRef(SortRef):
"""Type variable reference"""
def subsort(self, other):
return True
def cast(self, val):
return val
def DeclareTypeVar(name, ctx=None):
"""Create a new type variable named `name`.
If `ctx=None`, then the new sort is declared in the global Z3Py context.
"""
ctx = _get_ctx(ctx)
return TypeVarRef(Z3_mk_type_variable(ctx.ref(), to_symbol(name, ctx)), ctx)
#########################################
#
# Function Declarations
@ -1549,6 +1571,14 @@ class BoolRef(ExprRef):
def sort(self):
return BoolSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx)
def __add__(self, other):
if isinstance(other, BoolRef):
other = If(other, 1, 0)
return If(self, 1, 0) + other
def __radd__(self, other):
return self + other
def __rmul__(self, other):
return self * other
@ -1562,6 +1592,20 @@ class BoolRef(ExprRef):
if isinstance(other, BoolRef):
other = If(other, 1, 0)
return If(self, other, 0)
def __and__(self, other):
return And(self, other)
def __or__(self, other):
return Or(self, other)
def __xor__(self, other):
return Xor(self, other)
def __invert__(self):
return Not(self)
def is_bool(a):
@ -2059,6 +2103,16 @@ class QuantifierRef(BoolRef):
"""
return int(Z3_get_quantifier_weight(self.ctx_ref(), self.ast))
def skolem_id(self):
"""Return the skolem id of `self`.
"""
return _symbol2py(self.ctx, Z3_get_quantifier_skolem_id(self.ctx_ref(), self.ast))
def qid(self):
"""Return the quantifier id of `self`.
"""
return _symbol2py(self.ctx, Z3_get_quantifier_id(self.ctx_ref(), self.ast))
def num_patterns(self):
"""Return the number of patterns (i.e., quantifier instantiation hints) in `self`.
@ -6909,6 +6963,13 @@ class Solver(Z3PPObject):
if self.solver is not None and self.ctx.ref() is not None and Z3_solver_dec_ref is not None:
Z3_solver_dec_ref(self.ctx.ref(), self.solver)
def __enter__(self):
self.push()
return self
def __exit__(self, *exc_info):
self.pop()
def set(self, *args, **keys):
"""Set a configuration option.
The method `help()` return a string containing all available options.
@ -7993,7 +8054,7 @@ class Optimize(Z3PPObject):
Z3_optimize_pop(self.ctx.ref(), self.optimize)
def check(self, *assumptions):
"""Check satisfiability while optimizing objective functions."""
"""Check consistency and produce optimal values."""
assumptions = _get_args(assumptions)
num = len(assumptions)
_assumptions = (Ast * num)()
@ -11416,11 +11477,12 @@ def to_AstVectorObj(ptr,):
# for UserPropagator we use a global dictionary, which isn't great code.
_my_hacky_class = None
def on_clause_eh(ctx, p, clause):
def on_clause_eh(ctx, p, n, dep, clause):
onc = _my_hacky_class
p = _to_expr_ref(to_Ast(p), onc.ctx)
clause = AstVector(to_AstVectorObj(clause), onc.ctx)
onc.on_clause(p, clause)
deps = [dep[i] for i in range(n)]
onc.on_clause(p, deps, clause)
_on_clause_eh = Z3_on_clause_eh(on_clause_eh)

View file

@ -20,7 +20,6 @@ Notes:
#pragma once
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include "z3_macros.h"

View file

@ -5,7 +5,6 @@
#pragma once
DEFINE_TYPE(Z3_symbol);
DEFINE_TYPE(Z3_literals);
DEFINE_TYPE(Z3_config);
DEFINE_TYPE(Z3_context);
DEFINE_TYPE(Z3_sort);
@ -151,6 +150,7 @@ typedef enum
Z3_SEQ_SORT,
Z3_RE_SORT,
Z3_CHAR_SORT,
Z3_TYPE_VAR,
Z3_UNKNOWN_SORT = 1000
} Z3_sort_kind;
@ -1397,7 +1397,6 @@ typedef enum
def_Type('FUNC_DECL', 'Z3_func_decl', 'FuncDecl')
def_Type('PATTERN', 'Z3_pattern', 'Pattern')
def_Type('MODEL', 'Z3_model', 'ModelObj')
def_Type('LITERALS', 'Z3_literals', 'Literals')
def_Type('CONSTRUCTOR', 'Z3_constructor', 'Constructor')
def_Type('CONSTRUCTOR_LIST', 'Z3_constructor_list', 'ConstructorList')
def_Type('SOLVER', 'Z3_solver', 'SolverObj')
@ -1436,7 +1435,7 @@ Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, Z3_as
Z3_DECLARE_CLOSURE(Z3_final_eh, void, (void* ctx, Z3_solver_callback cb));
Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t));
Z3_DECLARE_CLOSURE(Z3_decide_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t, unsigned idx, bool phase));
Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, Z3_ast_vector literals));
Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, unsigned n, unsigned const* deps, Z3_ast_vector literals));
/**
@ -1883,6 +1882,17 @@ extern "C" {
*/
Z3_sort Z3_API Z3_mk_uninterpreted_sort(Z3_context c, Z3_symbol s);
/**
\brief Create a type variable.
Functions using type variables can be applied to instantiations that match the signature
of the function. Assertions using type variables correspond to assertions over all possible
instantiations.
def_API('Z3_mk_type_variable', SORT, (_in(CONTEXT), _in(SYMBOL)))
*/
Z3_sort Z3_API Z3_mk_type_variable(Z3_context c, Z3_symbol s);
/**
\brief Create the Boolean type.
@ -2072,6 +2082,16 @@ extern "C" {
unsigned sort_refs[]
);
/**
\brief Retrieve the number of fields of a constructor
\param c logical context.
\param constr constructor.
def_API('Z3_constructor_num_fields', UINT, (_in(CONTEXT), _in(CONSTRUCTOR)))
*/
unsigned Z3_API Z3_constructor_num_fields(Z3_context c, Z3_constructor constr);
/**
\brief Reclaim memory allocated to constructor.
@ -5186,6 +5206,24 @@ extern "C" {
*/
unsigned Z3_API Z3_get_quantifier_weight(Z3_context c, Z3_ast a);
/**
\brief Obtain skolem id of quantifier.
\pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST
def_API('Z3_get_quantifier_skolem_id', SYMBOL, (_in(CONTEXT), _in(AST)))
*/
Z3_symbol Z3_API Z3_get_quantifier_skolem_id(Z3_context c, Z3_ast a);
/**
\brief Obtain id of quantifier.
\pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST
def_API('Z3_get_quantifier_id', SYMBOL, (_in(CONTEXT), _in(AST)))
*/
Z3_symbol Z3_API Z3_get_quantifier_id(Z3_context c, Z3_ast a);
/**
\brief Return number of patterns used in quantifier.
@ -5584,14 +5622,14 @@ extern "C" {
void Z3_API Z3_add_const_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast a);
/**
\brief Increment the reference counter of the given Z3_func_interp object.
\brief Increment the reference counter of the given \c Z3_func_interp object.
def_API('Z3_func_interp_inc_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP)))
*/
void Z3_API Z3_func_interp_inc_ref(Z3_context c, Z3_func_interp f);
/**
\brief Decrement the reference counter of the given Z3_func_interp object.
\brief Decrement the reference counter of the given \c Z3_func_interp object.
def_API('Z3_func_interp_dec_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP)))
*/

View file

@ -32,6 +32,12 @@ extern "C" {
\param c logical context
\sa Z3_mk_fpa_round_nearest_ties_to_away or Z3_mk_fpa_rna
\sa Z3_mk_fpa_round_nearest_ties_to_even or Z3_mk_fpa_rne
\sa Z3_mk_fpa_round_toward_negative or Z3_mk_fpa_rtn
\sa Z3_mk_fpa_round_toward_positive or Z3_mk_fpa_rtp
\sa Z3_mk_fpa_round_toward_zero or Z3_mk_fpa_rtz
def_API('Z3_mk_fpa_rounding_mode_sort', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_rounding_mode_sort(Z3_context c);
@ -39,8 +45,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode.
This is the same as #Z3_mk_fpa_rne.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_round_nearest_ties_to_away
\sa Z3_mk_fpa_round_toward_negative
\sa Z3_mk_fpa_round_toward_positive
\sa Z3_mk_fpa_round_toward_zero
def_API('Z3_mk_fpa_round_nearest_ties_to_even', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(Z3_context c);
@ -48,8 +62,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode.
This is the same as #Z3_mk_fpa_round_nearest_ties_to_even.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_rna
\sa Z3_mk_fpa_rtn
\sa Z3_mk_fpa_rtp
\sa Z3_mk_fpa_rtz
def_API('Z3_mk_fpa_rne', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_rne(Z3_context c);
@ -57,8 +79,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode.
This is the same as #Z3_mk_fpa_rna.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_round_nearest_ties_to_even
\sa Z3_mk_fpa_round_toward_negative
\sa Z3_mk_fpa_round_toward_positive
\sa Z3_mk_fpa_round_toward_zero
def_API('Z3_mk_fpa_round_nearest_ties_to_away', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(Z3_context c);
@ -66,8 +96,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode.
This is the same as #Z3_mk_fpa_round_nearest_ties_to_away.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_rne
\sa Z3_mk_fpa_rtn
\sa Z3_mk_fpa_rtp
\sa Z3_mk_fpa_rtz
def_API('Z3_mk_fpa_rna', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_rna(Z3_context c);
@ -75,8 +113,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode.
This is the same as #Z3_mk_fpa_rtp.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_round_nearest_ties_to_away
\sa Z3_mk_fpa_round_nearest_ties_to_even
\sa Z3_mk_fpa_round_toward_negative
\sa Z3_mk_fpa_round_toward_zero
def_API('Z3_mk_fpa_round_toward_positive', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(Z3_context c);
@ -84,8 +130,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode.
This is the same as #Z3_mk_fpa_round_toward_positive.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_rna
\sa Z3_mk_fpa_rne
\sa Z3_mk_fpa_rtn
\sa Z3_mk_fpa_rtz
def_API('Z3_mk_fpa_rtp', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_rtp(Z3_context c);
@ -93,8 +147,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode.
This is the same as #Z3_mk_fpa_rtn.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_round_nearest_ties_to_away
\sa Z3_mk_fpa_round_nearest_ties_to_even
\sa Z3_mk_fpa_round_toward_positive
\sa Z3_mk_fpa_round_toward_zero
def_API('Z3_mk_fpa_round_toward_negative', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(Z3_context c);
@ -102,8 +164,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode.
This is the same as #Z3_mk_fpa_round_toward_negative.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_rna
\sa Z3_mk_fpa_rne
\sa Z3_mk_fpa_rtp
\sa Z3_mk_fpa_rtz
def_API('Z3_mk_fpa_rtn', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_rtn(Z3_context c);
@ -111,8 +181,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode.
This is the same as #Z3_mk_fpa_rtz.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_round_nearest_ties_to_away
\sa Z3_mk_fpa_round_nearest_ties_to_even
\sa Z3_mk_fpa_round_toward_negative
\sa Z3_mk_fpa_round_toward_positive
def_API('Z3_mk_fpa_round_toward_zero', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(Z3_context c);
@ -120,8 +198,16 @@ extern "C" {
/**
\brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode.
This is the same as #Z3_mk_fpa_round_toward_zero.
\param c logical context
\sa Z3_mk_fpa_rounding_mode_sort
\sa Z3_mk_fpa_rna
\sa Z3_mk_fpa_rne
\sa Z3_mk_fpa_rtn
\sa Z3_mk_fpa_rtp
def_API('Z3_mk_fpa_rtz', AST, (_in(CONTEXT),))
*/
Z3_ast Z3_API Z3_mk_fpa_rtz(Z3_context c);
@ -135,6 +221,11 @@ extern "C" {
\remark \c ebits must be larger than 1 and \c sbits must be larger than 2.
\sa Z3_mk_fpa_sort_half or Z3_mk_fpa_sort_16
\sa Z3_mk_fpa_sort_single or Z3_mk_fpa_sort_32
\sa Z3_mk_fpa_sort_double or Z3_mk_fpa_sort_64
\sa Z3_mk_fpa_sort_quadruple or Z3_mk_fpa_sort_128
def_API('Z3_mk_fpa_sort', SORT, (_in(CONTEXT), _in(UINT), _in(UINT)))
*/
Z3_sort Z3_API Z3_mk_fpa_sort(Z3_context c, unsigned ebits, unsigned sbits);
@ -142,8 +233,15 @@ extern "C" {
/**
\brief Create the half-precision (16-bit) FloatingPoint sort.
This is the same as #Z3_mk_fpa_sort_16.
\param c logical context
\sa Z3_mk_fpa_sort
\sa Z3_mk_fpa_sort_single
\sa Z3_mk_fpa_sort_double
\sa Z3_mk_fpa_sort_quadruple
def_API('Z3_mk_fpa_sort_half', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_sort_half(Z3_context c);
@ -151,8 +249,15 @@ extern "C" {
/**
\brief Create the half-precision (16-bit) FloatingPoint sort.
This is the same as #Z3_mk_fpa_sort_half.
\param c logical context
\sa Z3_mk_fpa_sort
\sa Z3_mk_fpa_sort_32
\sa Z3_mk_fpa_sort_64
\sa Z3_mk_fpa_sort_128
def_API('Z3_mk_fpa_sort_16', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_sort_16(Z3_context c);
@ -160,8 +265,15 @@ extern "C" {
/**
\brief Create the single-precision (32-bit) FloatingPoint sort.
This is the same as #Z3_mk_fpa_sort_32.
\param c logical context.
\sa Z3_mk_fpa_sort
\sa Z3_mk_fpa_sort_half
\sa Z3_mk_fpa_sort_double
\sa Z3_mk_fpa_sort_quadruple
def_API('Z3_mk_fpa_sort_single', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_sort_single(Z3_context c);
@ -169,8 +281,15 @@ extern "C" {
/**
\brief Create the single-precision (32-bit) FloatingPoint sort.
This is the same as #Z3_mk_fpa_sort_single.
\param c logical context
\sa Z3_mk_fpa_sort
\sa Z3_mk_fpa_sort_16
\sa Z3_mk_fpa_sort_64
\sa Z3_mk_fpa_sort_128
def_API('Z3_mk_fpa_sort_32', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_sort_32(Z3_context c);
@ -178,8 +297,15 @@ extern "C" {
/**
\brief Create the double-precision (64-bit) FloatingPoint sort.
This is the same as #Z3_mk_fpa_sort_64.
\param c logical context
\sa Z3_mk_fpa_sort
\sa Z3_mk_fpa_sort_half
\sa Z3_mk_fpa_sort_single
\sa Z3_mk_fpa_sort_quadruple
def_API('Z3_mk_fpa_sort_double', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_sort_double(Z3_context c);
@ -187,8 +313,15 @@ extern "C" {
/**
\brief Create the double-precision (64-bit) FloatingPoint sort.
This is the same as #Z3_mk_fpa_sort_double.
\param c logical context
\sa Z3_mk_fpa_sort
\sa Z3_mk_fpa_sort_16
\sa Z3_mk_fpa_sort_32
\sa Z3_mk_fpa_sort_128
def_API('Z3_mk_fpa_sort_64', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_sort_64(Z3_context c);
@ -196,8 +329,15 @@ extern "C" {
/**
\brief Create the quadruple-precision (128-bit) FloatingPoint sort.
This is the same as #Z3_mk_fpa_sort_128.
\param c logical context
\sa Z3_mk_fpa_sort
\sa Z3_mk_fpa_sort_half
\sa Z3_mk_fpa_sort_single
\sa Z3_mk_fpa_sort_double
def_API('Z3_mk_fpa_sort_quadruple', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_sort_quadruple(Z3_context c);
@ -205,8 +345,15 @@ extern "C" {
/**
\brief Create the quadruple-precision (128-bit) FloatingPoint sort.
This is the same as #Z3_mk_fpa_sort_quadruple.
\param c logical context
\sa Z3_mk_fpa_sort
\sa Z3_mk_fpa_sort_16
\sa Z3_mk_fpa_sort_32
\sa Z3_mk_fpa_sort_64
def_API('Z3_mk_fpa_sort_128', SORT, (_in(CONTEXT),))
*/
Z3_sort Z3_API Z3_mk_fpa_sort_128(Z3_context c);
@ -218,6 +365,7 @@ extern "C" {
\param s target sort
\sa Z3_mk_fpa_inf
\sa Z3_mk_fpa_is_nan
\sa Z3_mk_fpa_zero
def_API('Z3_mk_fpa_nan', AST, (_in(CONTEXT),_in(SORT)))
@ -233,6 +381,7 @@ extern "C" {
When \c negative is \c true, -oo will be generated instead of +oo.
\sa Z3_mk_fpa_is_infinite
\sa Z3_mk_fpa_nan
\sa Z3_mk_fpa_zero
@ -250,6 +399,7 @@ extern "C" {
When \c negative is \c true, -zero will be generated instead of +zero.
\sa Z3_mk_fpa_inf
\sa Z3_mk_fpa_is_zero
\sa Z3_mk_fpa_nan
def_API('Z3_mk_fpa_zero', AST, (_in(CONTEXT),_in(SORT),_in(BOOL)))
@ -397,6 +547,10 @@ extern "C" {
\param c logical context
\param t term of FloatingPoint sort
\sa Z3_mk_fpa_is_negative
\sa Z3_mk_fpa_is_positive
\sa Z3_mk_fpa_neg
def_API('Z3_mk_fpa_abs', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_abs(Z3_context c, Z3_ast t);
@ -407,6 +561,10 @@ extern "C" {
\param c logical context
\param t term of FloatingPoint sort
\sa Z3_mk_fpa_abs
\sa Z3_mk_fpa_is_negative
\sa Z3_mk_fpa_is_positive
def_API('Z3_mk_fpa_neg', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_neg(Z3_context c, Z3_ast t);
@ -533,6 +691,8 @@ extern "C" {
\c t1, \c t2 must have the same FloatingPoint sort.
\sa Z3_mk_fpa_max
def_API('Z3_mk_fpa_min', AST, (_in(CONTEXT),_in(AST),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_min(Z3_context c, Z3_ast t1, Z3_ast t2);
@ -546,6 +706,8 @@ extern "C" {
\c t1, \c t2 must have the same FloatingPoint sort.
\sa Z3_mk_fpa_min
def_API('Z3_mk_fpa_max', AST, (_in(CONTEXT),_in(AST),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_max(Z3_context c, Z3_ast t1, Z3_ast t2);
@ -559,6 +721,11 @@ extern "C" {
\c t1 and \c t2 must have the same FloatingPoint sort.
\sa Z3_mk_fpa_eq
\sa Z3_mk_fpa_geq
\sa Z3_mk_fpa_gt
\sa Z3_mk_fpa_lt
def_API('Z3_mk_fpa_leq', AST, (_in(CONTEXT),_in(AST),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_leq(Z3_context c, Z3_ast t1, Z3_ast t2);
@ -572,6 +739,11 @@ extern "C" {
\c t1 and \c t2 must have the same FloatingPoint sort.
\sa Z3_mk_fpa_eq
\sa Z3_mk_fpa_geq
\sa Z3_mk_fpa_gt
\sa Z3_mk_fpa_leq
def_API('Z3_mk_fpa_lt', AST, (_in(CONTEXT),_in(AST),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_lt(Z3_context c, Z3_ast t1, Z3_ast t2);
@ -585,6 +757,11 @@ extern "C" {
\c t1 and \c t2 must have the same FloatingPoint sort.
\sa Z3_mk_fpa_eq
\sa Z3_mk_fpa_gt
\sa Z3_mk_fpa_leq
\sa Z3_mk_fpa_lt
def_API('Z3_mk_fpa_geq', AST, (_in(CONTEXT),_in(AST),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_geq(Z3_context c, Z3_ast t1, Z3_ast t2);
@ -598,6 +775,11 @@ extern "C" {
\c t1 and \c t2 must have the same FloatingPoint sort.
\sa Z3_mk_fpa_eq
\sa Z3_mk_fpa_geq
\sa Z3_mk_fpa_leq
\sa Z3_mk_fpa_lt
def_API('Z3_mk_fpa_gt', AST, (_in(CONTEXT),_in(AST),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_gt(Z3_context c, Z3_ast t1, Z3_ast t2);
@ -613,6 +795,11 @@ extern "C" {
\c t1 and \c t2 must have the same FloatingPoint sort.
\sa Z3_mk_fpa_geq
\sa Z3_mk_fpa_gt
\sa Z3_mk_fpa_leq
\sa Z3_mk_fpa_lt
def_API('Z3_mk_fpa_eq', AST, (_in(CONTEXT),_in(AST),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_eq(Z3_context c, Z3_ast t1, Z3_ast t2);
@ -625,6 +812,11 @@ extern "C" {
\c t must have FloatingPoint sort.
\sa Z3_mk_fpa_is_infinite
\sa Z3_mk_fpa_is_nan
\sa Z3_mk_fpa_is_subnormal
\sa Z3_mk_fpa_is_zero
def_API('Z3_mk_fpa_is_normal', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_is_normal(Z3_context c, Z3_ast t);
@ -637,6 +829,11 @@ extern "C" {
\c t must have FloatingPoint sort.
\sa Z3_mk_fpa_is_infinite
\sa Z3_mk_fpa_is_nan
\sa Z3_mk_fpa_is_normal
\sa Z3_mk_fpa_is_zero
def_API('Z3_mk_fpa_is_subnormal', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_is_subnormal(Z3_context c, Z3_ast t);
@ -649,6 +846,12 @@ extern "C" {
\c t must have FloatingPoint sort.
\sa Z3_mk_fpa_is_infinite
\sa Z3_mk_fpa_is_nan
\sa Z3_mk_fpa_is_normal
\sa Z3_mk_fpa_is_subnormal
\sa Z3_mk_fpa_zero
def_API('Z3_mk_fpa_is_zero', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_is_zero(Z3_context c, Z3_ast t);
@ -661,6 +864,12 @@ extern "C" {
\c t must have FloatingPoint sort.
\sa Z3_mk_fpa_inf
\sa Z3_mk_fpa_is_nan
\sa Z3_mk_fpa_is_normal
\sa Z3_mk_fpa_is_subnormal
\sa Z3_mk_fpa_is_zero
def_API('Z3_mk_fpa_is_infinite', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_is_infinite(Z3_context c, Z3_ast t);
@ -673,6 +882,12 @@ extern "C" {
\c t must have FloatingPoint sort.
\sa Z3_mk_fpa_is_infinite
\sa Z3_mk_fpa_is_normal
\sa Z3_mk_fpa_is_subnormal
\sa Z3_mk_fpa_is_zero
\sa Z3_mk_fpa_nan
def_API('Z3_mk_fpa_is_nan', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_is_nan(Z3_context c, Z3_ast t);
@ -685,6 +900,10 @@ extern "C" {
\c t must have FloatingPoint sort.
\sa Z3_mk_fpa_abs
\sa Z3_mk_fpa_is_positive
\sa Z3_mk_fpa_neg
def_API('Z3_mk_fpa_is_negative', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_is_negative(Z3_context c, Z3_ast t);
@ -697,6 +916,10 @@ extern "C" {
\c t must have FloatingPoint sort.
\sa Z3_mk_fpa_abs
\sa Z3_mk_fpa_is_negative
\sa Z3_mk_fpa_neg
def_API('Z3_mk_fpa_is_positive', AST, (_in(CONTEXT),_in(AST)))
*/
Z3_ast Z3_API Z3_mk_fpa_is_positive(Z3_context c, Z3_ast t);
@ -848,6 +1071,8 @@ extern "C" {
\param c logical context
\param s FloatingPoint sort
\sa Z3_fpa_get_sbits
def_API('Z3_fpa_get_ebits', UINT, (_in(CONTEXT),_in(SORT)))
*/
unsigned Z3_API Z3_fpa_get_ebits(Z3_context c, Z3_sort s);
@ -858,6 +1083,8 @@ extern "C" {
\param c logical context
\param s FloatingPoint sort
\sa Z3_fpa_get_ebits
def_API('Z3_fpa_get_sbits', UINT, (_in(CONTEXT),_in(SORT)))
*/
unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s);
@ -868,6 +1095,11 @@ extern "C" {
\param c logical context
\param t a floating-point numeral
\sa Z3_fpa_is_numeral_inf
\sa Z3_fpa_is_numeral_normal
\sa Z3_fpa_is_numeral_subnormal
\sa Z3_fpa_is_numeral_zero
def_API('Z3_fpa_is_numeral_nan', BOOL, (_in(CONTEXT), _in(AST)))
*/
bool Z3_API Z3_fpa_is_numeral_nan(Z3_context c, Z3_ast t);
@ -878,6 +1110,11 @@ extern "C" {
\param c logical context
\param t a floating-point numeral
\sa Z3_fpa_is_numeral_nan
\sa Z3_fpa_is_numeral_normal
\sa Z3_fpa_is_numeral_subnormal
\sa Z3_fpa_is_numeral_zero
def_API('Z3_fpa_is_numeral_inf', BOOL, (_in(CONTEXT), _in(AST)))
*/
bool Z3_API Z3_fpa_is_numeral_inf(Z3_context c, Z3_ast t);
@ -888,6 +1125,11 @@ extern "C" {
\param c logical context
\param t a floating-point numeral
\sa Z3_fpa_is_numeral_inf
\sa Z3_fpa_is_numeral_nan
\sa Z3_fpa_is_numeral_normal
\sa Z3_fpa_is_numeral_subnormal
def_API('Z3_fpa_is_numeral_zero', BOOL, (_in(CONTEXT), _in(AST)))
*/
bool Z3_API Z3_fpa_is_numeral_zero(Z3_context c, Z3_ast t);
@ -898,6 +1140,11 @@ extern "C" {
\param c logical context
\param t a floating-point numeral
\sa Z3_fpa_is_numeral_inf
\sa Z3_fpa_is_numeral_nan
\sa Z3_fpa_is_numeral_subnormal
\sa Z3_fpa_is_numeral_zero
def_API('Z3_fpa_is_numeral_normal', BOOL, (_in(CONTEXT), _in(AST)))
*/
bool Z3_API Z3_fpa_is_numeral_normal(Z3_context c, Z3_ast t);
@ -908,6 +1155,11 @@ extern "C" {
\param c logical context
\param t a floating-point numeral
\sa Z3_fpa_is_numeral_inf
\sa Z3_fpa_is_numeral_nan
\sa Z3_fpa_is_numeral_normal
\sa Z3_fpa_is_numeral_zero
def_API('Z3_fpa_is_numeral_subnormal', BOOL, (_in(CONTEXT), _in(AST)))
*/
bool Z3_API Z3_fpa_is_numeral_subnormal(Z3_context c, Z3_ast t);
@ -918,6 +1170,8 @@ extern "C" {
\param c logical context
\param t a floating-point numeral
\sa Z3_fpa_is_numeral_negative
def_API('Z3_fpa_is_numeral_positive', BOOL, (_in(CONTEXT), _in(AST)))
*/
bool Z3_API Z3_fpa_is_numeral_positive(Z3_context c, Z3_ast t);
@ -928,6 +1182,8 @@ extern "C" {
\param c logical context
\param t a floating-point numeral
\sa Z3_fpa_is_numeral_positive
def_API('Z3_fpa_is_numeral_negative', BOOL, (_in(CONTEXT), _in(AST)))
*/
bool Z3_API Z3_fpa_is_numeral_negative(Z3_context c, Z3_ast t);

View file

@ -193,8 +193,124 @@ extern "C" {
We have that \ccode{a = n/d}, moreover \c n and \c d are not represented using rational functions.
def_API('Z3_rcf_get_numerator_denominator', VOID, (_in(CONTEXT), _in(RCF_NUM), _out(RCF_NUM), _out(RCF_NUM)))
*/
void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d);
*/
void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d);
/**
\brief Return \c true if \c a represents a rational number.
def_API('Z3_rcf_is_rational', BOOL, (_in(CONTEXT), _in(RCF_NUM)))
*/
bool Z3_API Z3_rcf_is_rational(Z3_context c, Z3_rcf_num a);
/**
\brief Return \c true if \c a represents an algebraic number.
def_API('Z3_rcf_is_algebraic', BOOL, (_in(CONTEXT), _in(RCF_NUM)))
*/
bool Z3_API Z3_rcf_is_algebraic(Z3_context c, Z3_rcf_num a);
/**
\brief Return \c true if \c a represents an infinitesimal.
def_API('Z3_rcf_is_infinitesimal', BOOL, (_in(CONTEXT), _in(RCF_NUM)))
*/
bool Z3_API Z3_rcf_is_infinitesimal(Z3_context c, Z3_rcf_num a);
/**
\brief Return \c true if \c a represents a transcendental number.
def_API('Z3_rcf_is_transcendental', BOOL, (_in(CONTEXT), _in(RCF_NUM)))
*/
bool Z3_API Z3_rcf_is_transcendental(Z3_context c, Z3_rcf_num a);
/**
\brief Return the index of a field extension.
def_API('Z3_rcf_extension_index', UINT, (_in(CONTEXT), _in(RCF_NUM)))
*/
unsigned Z3_API Z3_rcf_extension_index(Z3_context c, Z3_rcf_num a);
/**
\brief Return the name of a transcendental.
\pre Z3_rcf_is_transcendtal(ctx, a);
def_API('Z3_rcf_transcendental_name', SYMBOL, (_in(CONTEXT), _in(RCF_NUM)))
*/
Z3_symbol Z3_API Z3_rcf_transcendental_name(Z3_context c, Z3_rcf_num a);
/**
\brief Return the name of an infinitesimal.
\pre Z3_rcf_is_infinitesimal(ctx, a);
def_API('Z3_rcf_infinitesimal_name', SYMBOL, (_in(CONTEXT), _in(RCF_NUM)))
*/
Z3_symbol Z3_API Z3_rcf_infinitesimal_name(Z3_context c, Z3_rcf_num a);
/**
\brief Return the number of coefficients in an algebraic number.
\pre Z3_rcf_is_algebraic(ctx, a);
def_API('Z3_rcf_num_coefficients', UINT, (_in(CONTEXT), _in(RCF_NUM)))
*/
unsigned Z3_API Z3_rcf_num_coefficients(Z3_context c, Z3_rcf_num a);
/**
\brief Extract a coefficient from an algebraic number.
\pre Z3_rcf_is_algebraic(ctx, a);
def_API('Z3_rcf_coefficient', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT)))
*/
Z3_rcf_num Z3_API Z3_rcf_coefficient(Z3_context c, Z3_rcf_num a, unsigned i);
/**
\brief Extract an interval from an algebraic number.
\pre Z3_rcf_is_algebraic(ctx, a);
def_API('Z3_rcf_interval', INT, (_in(CONTEXT), _in(RCF_NUM), _out(INT), _out(INT), _out(RCF_NUM), _out(INT), _out(INT), _out(RCF_NUM)))
*/
int Z3_API Z3_rcf_interval(Z3_context c, Z3_rcf_num a, int * lower_is_inf, int * lower_is_open, Z3_rcf_num * lower, int * upper_is_inf, int * upper_is_open, Z3_rcf_num * upper);
/**
\brief Return the number of sign conditions of an algebraic number.
\pre Z3_rcf_is_algebraic(ctx, a);
def_API('Z3_rcf_num_sign_conditions', UINT, (_in(CONTEXT), _in(RCF_NUM)))
*/
unsigned Z3_API Z3_rcf_num_sign_conditions(Z3_context c, Z3_rcf_num a);
/**
\brief Extract the sign of a sign condition from an algebraic number.
\pre Z3_rcf_is_algebraic(ctx, a);
def_API('Z3_rcf_sign_condition_sign', INT, (_in(CONTEXT), _in(RCF_NUM), _in(UINT)))
*/
int Z3_API Z3_rcf_sign_condition_sign(Z3_context c, Z3_rcf_num a, unsigned i);
/**
\brief Return the number of sign condition polynomial coefficients of an algebraic number.
\pre Z3_rcf_is_algebraic(ctx, a);
def_API('Z3_rcf_num_sign_condition_coefficients', UINT, (_in(CONTEXT), _in(RCF_NUM), _in(UINT)))
*/
unsigned Z3_API Z3_rcf_num_sign_condition_coefficients(Z3_context c, Z3_rcf_num a, unsigned i);
/**
\brief Extract the j-th polynomial coefficient of the i-th sign condition.
\pre Z3_rcf_is_algebraic(ctx, a);
def_API('Z3_rcf_sign_condition_coefficient', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT), _in(UINT)))
*/
Z3_rcf_num Z3_API Z3_rcf_sign_condition_coefficient(Z3_context c, Z3_rcf_num a, unsigned i, unsigned j);
/**@}*/
/**@}*/

View file

@ -3,6 +3,7 @@ z3_add_component(ast
act_cache.cpp
arith_decl_plugin.cpp
array_decl_plugin.cpp
array_peq.cpp
ast.cpp
ast_ll_pp.cpp
ast_lt.cpp
@ -37,6 +38,8 @@ z3_add_component(ast
num_occurs.cpp
occurs.cpp
pb_decl_plugin.cpp
polymorphism_inst.cpp
polymorphism_util.cpp
pp.cpp
quantifier_stat.cpp
recfun_decl_plugin.cpp

View file

@ -801,6 +801,29 @@ expr_ref arith_util::mk_add_simplify(unsigned sz, expr* const* args) {
return result;
}
bool arith_util::is_considered_partially_interpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out) {
if (is_decl_of(f, arith_family_id, OP_DIV) && n == 2 && !is_numeral(args[1])) {
f_out = mk_div0();
return true;
}
if (is_decl_of(f, arith_family_id, OP_IDIV) && n == 2 && !is_numeral(args[1])) {
sort* rs[2] = { mk_int(), mk_int() };
f_out = m_manager.mk_func_decl(arith_family_id, OP_IDIV0, 0, nullptr, 2, rs, mk_int());
return true;
}
if (is_decl_of(f, arith_family_id, OP_MOD) && n == 2 && !is_numeral(args[1])) {
sort* rs[2] = { mk_int(), mk_int() };
f_out = m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int());
return true;
}
if (is_decl_of(f, arith_family_id, OP_REM) && n == 2 && !is_numeral(args[1])) {
sort* rs[2] = { mk_int(), mk_int() };
f_out = m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int());
return true;
}
return false;
}
bool arith_util::is_considered_uninterpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out) {
rational r;
if (is_decl_of(f, arith_family_id, OP_DIV) && n == 2 && is_numeral(args[1], r) && r.is_zero()) {

View file

@ -517,6 +517,8 @@ public:
bool is_considered_uninterpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out);
bool is_considered_partially_interpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out);
bool is_underspecified(expr* e) const;
bool is_bounded(expr* e) const;

View file

@ -633,6 +633,12 @@ bool array_decl_plugin::is_value(app * _e) const {
}
}
bool array_decl_plugin::is_unique_value(app* _e) const {
array_util u(*m_manager);
expr* e = _e;
return u.is_const(e, e) && m_manager->is_unique_value(e);
}
func_decl * array_recognizers::get_as_array_func_decl(expr * n) const {
SASSERT(is_as_array(n));

View file

@ -137,6 +137,8 @@ class array_decl_plugin : public decl_plugin {
bool is_value(app * e) const override;
bool is_unique_value(app* e) const override;
};
class array_recognizers {
@ -184,6 +186,21 @@ public:
bool is_store_ext(expr* e, expr_ref& a, expr_ref_vector& args, expr_ref& value);
bool is_select1(expr* n) const { return is_select(n) && to_app(n)->get_num_args() == 2; }
bool is_select1(expr* n, expr*& a, expr*& i) const {
return is_select1(n) && (a = to_app(n)->get_arg(0), i = to_app(n)->get_arg(1), true);
}
bool is_store1(expr* n) const { return is_store(n) && to_app(n)->get_num_args() == 3; }
bool is_store1(expr* n, expr*& a, expr*& i, expr*& v) const {
app* _n;
return is_store1(n) && (_n = to_app(n), a = _n->get_arg(0), i = _n->get_arg(1), v = _n->get_arg(2), true);
}
MATCH_BINARY(is_subset);
};
@ -211,6 +228,11 @@ public:
return mk_store(args.size(), args.data());
}
app * mk_select(expr* a, expr* i) const {
expr* args[2] = { a, i };
return mk_select(2, args);
}
app * mk_select(unsigned num_args, expr * const * args) const {
return m_manager.mk_app(m_fid, OP_SELECT, 0, nullptr, num_args, args);
}

107
src/ast/array_peq.cpp Normal file
View file

@ -0,0 +1,107 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
array_peq.cpp
Abstract:
Partial equality for arrays
Author:
Nikolaj Bjorner (nbjorner) 2015-06-13
Hari Govind V K
Revision History:
--*/
#include "ast/array_peq.h"
#define PARTIAL_EQ "!partial_eq"
bool is_partial_eq(const func_decl *f) {
SASSERT(f);
return f->get_name() == PARTIAL_EQ;
}
bool is_partial_eq(const app *a) {
SASSERT(a);
return is_partial_eq(a->get_decl());
}
app_ref mk_peq(expr *e0, expr *e1, vector<expr_ref_vector> const &indices,
ast_manager &m) {
peq p(e0, e1, indices, m);
return p.mk_peq();
}
app_ref peq::mk_eq(app_ref_vector &aux_consts, bool stores_on_rhs) {
if (!m_eq) {
expr_ref lhs(m_lhs, m), rhs(m_rhs, m);
if (!stores_on_rhs) { std::swap(lhs, rhs); }
// lhs = (...(store (store rhs i0 v0) i1 v1)...)
sort *val_sort = get_array_range(lhs->get_sort());
for (expr_ref_vector const &diff : m_diff_indices) {
ptr_vector<expr> store_args;
store_args.push_back(rhs);
store_args.append(diff.size(), diff.data());
app_ref val(m.mk_fresh_const("diff", val_sort), m);
store_args.push_back(val);
aux_consts.push_back(val);
rhs = m_arr_u.mk_store(store_args);
}
m_eq = m.mk_eq(lhs, rhs);
}
return m_eq;
}
app_ref peq::mk_peq() {
if (!m_peq) {
ptr_vector<expr> args;
args.push_back(m_lhs);
args.push_back(m_rhs);
for (auto const &v : m_diff_indices) {
args.append(v.size(), v.data());
}
m_peq = m.mk_app(m_decl, args.size(), args.data());
}
return m_peq;
}
peq::peq(expr *lhs, expr *rhs, vector<expr_ref_vector> const &diff_indices,
ast_manager &m)
: m(m), m_lhs(lhs, m), m_rhs(rhs, m), m_diff_indices(diff_indices),
m_decl(m), m_peq(m), m_eq(m), m_arr_u(m) {
SASSERT(m_arr_u.is_array(lhs));
SASSERT(m_arr_u.is_array(rhs));
SASSERT(lhs->get_sort() == rhs->get_sort());
ptr_vector<sort> sorts;
sorts.push_back(m_lhs->get_sort());
sorts.push_back(m_rhs->get_sort());
for (auto const &v : diff_indices) {
SASSERT(v.size() == get_array_arity(m_lhs->get_sort()));
for (expr *e : v) sorts.push_back(e->get_sort());
}
m_decl = m.mk_func_decl(symbol(PARTIAL_EQ), sorts.size(), sorts.data(),
m.mk_bool_sort());
}
peq::peq(app *p, ast_manager &m)
: m(m), m_lhs(p->get_arg(0), m), m_rhs(p->get_arg(1), m),
m_decl(p->get_decl(), m), m_peq(p, m), m_eq(m), m_arr_u(m),
m_name(symbol(PARTIAL_EQ)) {
SASSERT(is_partial_eq(p));
SASSERT(m_arr_u.is_array(m_lhs));
SASSERT(m_arr_u.is_array(m_rhs));
SASSERT(m_lhs->get_sort() == m_rhs->get_sort());
unsigned arity = get_array_arity(m_lhs->get_sort());
for (unsigned i = 2; i < p->get_num_args(); i += arity) {
SASSERT(arity + i <= p->get_num_args());
expr_ref_vector vec(m);
vec.append(arity, p->get_args() + i);
m_diff_indices.push_back(std::move(vec));
}
}

91
src/ast/array_peq.h Normal file
View file

@ -0,0 +1,91 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
array_peq.h
Abstract:
Partial equality for arrays
Author:
Nikolaj Bjorner (nbjorner) 2015-06-13
Hari Govind V K
Revision History:
--*/
#pragma once
#include "ast/array_decl_plugin.h"
#include "ast/ast.h"
/**
* \brief utility class for partial equalities
*
* A partial equality (a ==I b), for two arrays a, b and a finite set of indices
* I holds iff (forall i :: i \not\in I => a[i] == b[i]). In other words, peq is
* a restricted form of the extensionality axiom
*
* Using this class, we denote (a =I b) as f(a,b,i0,i1,...),
* where f is an uninterpreted predicate with the name PARTIAL_EQ and
* I = {i0,i1,...}
*/
class peq {
ast_manager &m;
expr_ref m_lhs;
expr_ref m_rhs;
vector<expr_ref_vector> m_diff_indices;
func_decl_ref m_decl; // the partial equality declaration
app_ref m_peq; // partial equality application
app_ref m_eq; // equivalent std equality using def. of partial eq
array_util m_arr_u;
symbol m_name;
public:
peq(app *p, ast_manager &m);
peq(expr *lhs, expr *rhs, vector<expr_ref_vector> const &diff_indices,
ast_manager &m);
expr_ref lhs() { return m_lhs; }
expr_ref rhs() { return m_rhs; }
void get_diff_indices(vector<expr_ref_vector> &result) {
result.append(m_diff_indices);
}
/** Convert peq into a peq expression */
app_ref mk_peq();
/** Convert peq into an equality
For peq of the form (a =I b) returns (a = b[i0 := v0, i1 := v1, ...])
where i0, i1 \in I, and v0, v1 are fresh skolem constants
Skolems are returned in aux_consts
The left and right hand arguments are reversed when stores_on_rhs is
false
*/
app_ref mk_eq(app_ref_vector &aux_consts, bool stores_on_rhs = true);
};
/**
* mk (e0 ==indices e1)
*
* result has stores if either e0 or e1 or an index term has stores
*/
app_ref mk_peq(expr *e0, expr *e1, vector<expr_ref_vector> const &indices,
ast_manager &m);
bool is_partial_eq(const func_decl *f);
bool is_partial_eq(const app *a);
inline bool is_peq(const func_decl *f) { return is_partial_eq(f); }
inline bool is_peq(const app *a) { return is_partial_eq(a); }

View file

@ -202,8 +202,7 @@ unsigned decl_info::hash() const {
bool decl_info::operator==(decl_info const & info) const {
return m_family_id == info.m_family_id && m_kind == info.m_kind &&
m_parameters.size() == info.m_parameters.size() &&
compare_arrays<parameter>(m_parameters.begin(), info.m_parameters.begin(), m_parameters.size());
m_parameters == info.m_parameters;
}
std::ostream & operator<<(std::ostream & out, decl_info const & info) {
@ -255,7 +254,8 @@ func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_pa
m_injective(false),
m_idempotent(false),
m_skolem(false),
m_lambda(false) {
m_lambda(false),
m_polymorphic(false) {
}
bool func_decl_info::operator==(func_decl_info const & info) const {
@ -283,6 +283,7 @@ std::ostream & operator<<(std::ostream & out, func_decl_info const & info) {
if (info.is_idempotent()) out << " :idempotent ";
if (info.is_skolem()) out << " :skolem ";
if (info.is_lambda()) out << " :lambda ";
if (info.is_polymorphic()) out << " :polymorphic ";
return out;
}
@ -447,7 +448,7 @@ bool compare_nodes(ast const * n1, ast const * n2) {
if (to_sort(n1)->get_info() != nullptr && !(*to_sort(n1)->get_info() == *to_sort(n2)->get_info())) {
return false;
}
return to_sort(n1)->get_name() == to_sort(n2)->get_name();
return to_sort(n1)->get_name() == to_sort(n2)->get_name();
case AST_FUNC_DECL:
if ((to_func_decl(n1)->get_info() == nullptr) != (to_func_decl(n2)->get_info() == nullptr)) {
return false;
@ -477,15 +478,15 @@ bool compare_nodes(ast const * n1, ast const * n2) {
return
q1->get_kind() == q2->get_kind() &&
q1->get_num_decls() == q2->get_num_decls() &&
q1->get_expr() == q2->get_expr() &&
q1->get_weight() == q2->get_weight() &&
q1->get_num_patterns() == q2->get_num_patterns() &&
compare_arrays(q1->get_decl_sorts(),
q2->get_decl_sorts(),
q1->get_num_decls()) &&
compare_arrays(q1->get_decl_names(),
q2->get_decl_names(),
q1->get_num_decls()) &&
q1->get_expr() == q2->get_expr() &&
q1->get_weight() == q2->get_weight() &&
q1->get_num_patterns() == q2->get_num_patterns() &&
((q1->get_qid().is_numerical() && q2->get_qid().is_numerical()) ||
(q1->get_qid() == q2->get_qid())) &&
compare_arrays(q1->get_patterns(),
@ -540,22 +541,6 @@ inline unsigned ast_array_hash(T * const * array, unsigned size, unsigned init_v
} }
}
unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init) {
return ast_array_hash<ast>(ns, sz, init);
}
unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init) {
return ast_array_hash<app>(ns, sz, init);
}
unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init) {
return ast_array_hash<expr>(ns, sz, init);
}
unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init) {
return ast_array_hash<sort>(ns, sz, init);
}
unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init) {
return ast_array_hash<func_decl>(ns, sz, init);
}
unsigned get_node_hash(ast const * n) {
unsigned a, b, c;
@ -1309,10 +1294,7 @@ ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_form
m_expr_array_manager(*this, m_alloc),
m_expr_dependency_manager(*this, m_alloc),
m_expr_dependency_array_manager(*this, m_alloc),
m_proof_mode(m),
m_trace_stream(nullptr),
m_trace_stream_owner(false),
m_lambda_def(":lambda-def") {
m_proof_mode(m) {
if (trace_file) {
m_trace_stream = alloc(std::fstream, trace_file, std::ios_base::out);
@ -1333,9 +1315,7 @@ ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_
m_expr_dependency_manager(*this, m_alloc),
m_expr_dependency_array_manager(*this, m_alloc),
m_proof_mode(m),
m_trace_stream(trace_stream),
m_trace_stream_owner(false),
m_lambda_def(":lambda-def") {
m_trace_stream(trace_stream) {
if (!is_format_manager)
m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true);
@ -1350,9 +1330,7 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs):
m_expr_dependency_manager(*this, m_alloc),
m_expr_dependency_array_manager(*this, m_alloc),
m_proof_mode(disable_proofs ? PGM_DISABLED : src.m_proof_mode),
m_trace_stream(src.m_trace_stream),
m_trace_stream_owner(false),
m_lambda_def(":lambda-def") {
m_trace_stream(src.m_trace_stream) {
SASSERT(!src.is_format_manager());
m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true);
init();
@ -1880,6 +1858,8 @@ void ast_manager::delete_node(ast * n) {
break;
case AST_FUNC_DECL: {
func_decl* f = to_func_decl(n);
if (f->is_polymorphic())
m_poly_roots.erase(f);
if (f->m_info != nullptr) {
func_decl_info * info = f->get_info();
if (info->is_lambda()) {
@ -2020,10 +2000,6 @@ sort * ast_manager::mk_uninterpreted_sort(symbol const & name, unsigned num_para
return plugin->mk_sort(kind, num_parameters, parameters);
}
sort * ast_manager::mk_type_var(symbol const& name) {
sort_info si(poly_family_id, 0);
return mk_sort(name, &si);
}
func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
bool assoc, bool comm, bool inj) {
@ -2035,13 +2011,30 @@ func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort
}
func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info) {
SASSERT(arity == 1 || info == 0 || !info->is_injective());
SASSERT(arity == 2 || info == 0 || !info->is_associative());
SASSERT(arity == 2 || info == 0 || !info->is_commutative());
SASSERT(arity == 1 || !info || !info->is_injective());
SASSERT(arity == 2 || !info || !info->is_associative());
SASSERT(arity == 2 || !info || !info->is_commutative());
unsigned sz = func_decl::get_obj_size(arity);
void * mem = allocate_node(sz);
func_decl * new_node = new (mem) func_decl(name, arity, domain, range, info);
return register_node(new_node);
// determine if function is a polymorphic root object.
// instances of polymorphic functions are automatically tagged as polymorphic and
// inserted into the m_poly_roots table.
bool is_polymorphic_root = false;
func_decl_info info0;
if (has_type_var(arity, domain, range)) {
if (!info)
info = &info0;
if (!info->is_polymorphic()) {
info->set_polymorphic(true);
is_polymorphic_root = true;
}
}
func_decl* new_node = new (mem) func_decl(name, arity, domain, range, info);
new_node = register_node(new_node);
if (is_polymorphic_root)
m_poly_roots.insert(new_node, new_node);
return new_node;
}
void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * const * args) const {
@ -2306,9 +2299,10 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const
func_decl_info info(null_family_id, null_decl_kind);
info.m_skolem = skolem;
SASSERT(skolem == info.is_skolem());
func_decl_info* infop = skolem ? &info : nullptr;
func_decl * d;
if (prefix == symbol::null && suffix == symbol::null) {
d = mk_func_decl(symbol(m_fresh_id), arity, domain, range, &info);
d = mk_func_decl(symbol(m_fresh_id), arity, domain, range, infop);
}
else {
string_buffer<64> buffer;
@ -2320,10 +2314,10 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const
if (suffix != symbol::null)
buffer << suffix << "!";
buffer << m_fresh_id;
d = mk_func_decl(symbol(buffer.c_str()), arity, domain, range, &info);
d = mk_func_decl(symbol(buffer.c_str()), arity, domain, range, infop);
}
m_fresh_id++;
SASSERT(d->get_info());
SASSERT(!skolem || d->get_info());
SASSERT(skolem == d->is_skolem());
return d;
}
@ -2725,6 +2719,49 @@ bool ast_manager::is_fully_interp(sort * s) const {
return false;
}
// -----------------------------------------
// Polymorphism
// -----------------------------------------
sort * ast_manager::mk_type_var(symbol const& name) {
m_has_type_vars = true;
sort_info si(poly_family_id, 0);
return mk_sort(name, &si);
}
bool ast_manager::has_type_var(sort* s) const {
if (is_type_var(s))
return true;
for (parameter const& p : s->parameters())
if (p.is_ast() && is_sort(p.get_ast()) && has_type_var(to_sort(p.get_ast())))
return true;
return false;
}
bool ast_manager::has_type_var(func_decl* f) const {
return has_type_var(f->get_arity(), f->get_domain(), f->get_range());
}
bool ast_manager::has_type_var(unsigned n, sort* const* domain, sort* range) const {
if (!has_type_vars())
return false;
for (unsigned i = n; i-- > 0; )
if (has_type_var(domain[i]))
return true;
return has_type_var(range);
}
/**
* \brief create an instantiation of polymorphic function f.
*/
func_decl* ast_manager::instantiate_polymorphic(func_decl* f, unsigned arity, sort * const* domain, sort * range) {
SASSERT(f->is_polymorphic());
func_decl* g = mk_func_decl(f->get_name(), arity, domain, range, f->get_info());
m_poly_roots.insert(g, f);
// SASSERT(g->is_polymorphic());
return g;
}
// -----------------------------------
//
// Proof generation
@ -2847,29 +2884,40 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) {
SASSERT(has_fact(p2));
SASSERT(is_app(get_fact(p1)));
SASSERT(is_app(get_fact(p2)));
SASSERT(to_app(get_fact(p1))->get_num_args() == 2);
SASSERT(to_app(get_fact(p2))->get_num_args() == 2);
CTRACE("mk_transitivity", to_app(get_fact(p1))->get_decl() != to_app(get_fact(p2))->get_decl(),
tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n";
tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n";
tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";);
SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() ||
( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) &&
(is_eq(get_fact(p2)) || is_oeq(get_fact(p2)))));
CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0),
tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n";
app* fact1 = to_app(get_fact(p1));
app* fact2 = to_app(get_fact(p2));
SASSERT(fact1->get_num_args() == 2);
SASSERT(fact2->get_num_args() == 2);
CTRACE("mk_transitivity", fact1->get_decl() != fact2->get_decl(),
tout << mk_pp(fact1, *this) << "\n\n" << mk_pp(fact2, *this) << "\n";
tout << mk_pp(fact1->get_decl(), *this) << "\n";
tout << mk_pp(fact2->get_decl(), *this) << "\n";);
SASSERT(fact1->get_decl() == fact2->get_decl() ||
( (is_eq(fact1) || is_oeq(fact1)) &&
(is_eq(fact2) || is_oeq(fact2))));
CTRACE("mk_transitivity", fact1->get_arg(1) != fact2->get_arg(0),
tout << mk_pp(fact1, *this) << "\n\n" << mk_pp(fact2, *this) << "\n";
tout << p1->get_id() << ": " << mk_bounded_pp(p1, *this, 5) << "\n\n";
tout << p2->get_id() << ": " << mk_bounded_pp(p2, *this, 5) << "\n\n";
);
SASSERT(to_app(get_fact(p1))->get_arg(1) == to_app(get_fact(p2))->get_arg(0));
if (is_reflexivity(p1))
return p2;
if (is_reflexivity(p2))
return p1;
// local fixup to admit inline simplifications of not(not(e)) to e
expr* e;
if (is_not(fact1->get_arg(1), e) && is_not(e, e) && e == fact2->get_arg(0))
p1 = mk_transitivity(p1, mk_rewrite(fact1->get_arg(1), fact2->get_arg(0)));
else if (is_not(fact2->get_arg(0), e) && is_not(e, e) && e == fact1->get_arg(1))
p1 = mk_transitivity(p1, mk_rewrite(fact1->get_arg(1), fact2->get_arg(0)));
else {
SASSERT(fact1->get_arg(1) == fact2->get_arg(0));
}
// OEQ is compatible with EQ for transitivity.
func_decl* f = to_app(get_fact(p1))->get_decl();
if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl();
return mk_app(basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, to_app(get_fact(p1))->get_arg(0), to_app(get_fact(p2))->get_arg(1)));
func_decl* f = fact1->get_decl();
if (is_oeq(fact2))
f = fact2->get_decl();
return mk_app(basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, fact1->get_arg(0), fact2->get_arg(1)));
}

View file

@ -400,6 +400,7 @@ struct func_decl_info : public decl_info {
bool m_idempotent:1;
bool m_skolem:1;
bool m_lambda:1;
bool m_polymorphic:1;
func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr);
@ -414,6 +415,7 @@ struct func_decl_info : public decl_info {
bool is_idempotent() const { return m_idempotent; }
bool is_skolem() const { return m_skolem; }
bool is_lambda() const { return m_lambda; }
bool is_polymorphic() const { return m_polymorphic; }
void set_associative(bool flag = true) { m_left_assoc = flag; m_right_assoc = flag; }
void set_left_associative(bool flag = true) { m_left_assoc = flag; }
@ -426,6 +428,7 @@ struct func_decl_info : public decl_info {
void set_idempotent(bool flag = true) { m_idempotent = flag; }
void set_skolem(bool flag = true) { m_skolem = flag; }
void set_lambda(bool flag = true) { m_lambda = flag; }
void set_polymorphic(bool flag = true) { m_polymorphic = flag; }
bool operator==(func_decl_info const & info) const;
@ -655,6 +658,7 @@ public:
bool is_skolem() const { return get_info() != nullptr && get_info()->is_skolem(); }
bool is_lambda() const { return get_info() != nullptr && get_info()->is_lambda(); }
bool is_idempotent() const { return get_info() != nullptr && get_info()->is_idempotent(); }
bool is_polymorphic() const { return get_info() != nullptr && get_info()->is_polymorphic(); }
unsigned get_arity() const { return m_arity; }
sort * get_domain(unsigned idx) const { SASSERT(idx < get_arity()); return m_domain[idx]; }
sort * const * get_domain() const { return m_domain; }
@ -966,11 +970,6 @@ inline quantifier const * to_quantifier(ast const * n) { SASSERT(is_quantifier(n
unsigned get_node_hash(ast const * n);
bool compare_nodes(ast const * n1, ast const * n2);
unsigned get_node_size(ast const * n);
unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init);
unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init);
unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init);
unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init);
unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init);
// This is the internal comparison functor for hash-consing AST nodes.
struct ast_eq_proc {
@ -1511,13 +1510,15 @@ protected:
unsigned m_fresh_id;
bool m_debug_ref_count;
u_map<unsigned> m_debug_free_indices;
std::fstream* m_trace_stream;
bool m_trace_stream_owner;
std::fstream* m_trace_stream = nullptr;
bool m_trace_stream_owner = false;
bool m_has_type_vars = false;
#ifdef Z3DEBUG
bool slow_not_contains(ast const * n);
#endif
ast_manager * m_format_manager; // hack for isolating format objects in a different manager.
symbol m_lambda_def;
symbol m_lambda_def = symbol(":lambda-def");
obj_map<func_decl, func_decl*> m_poly_roots;
void init();
@ -1734,12 +1735,27 @@ public:
bool is_uninterp(sort const * s) const { return s->get_family_id() == null_family_id || s->get_family_id() == user_sort_family_id; }
bool is_type_var(sort const* s) const { return s->get_family_id() == poly_family_id; }
bool has_type_vars() const { return m_has_type_vars; }
func_decl* poly_root(func_decl* f) const { SASSERT(f->is_polymorphic()); return m_poly_roots[f]; }
func_decl* instantiate_polymorphic(func_decl* f, unsigned arity, sort * const* domain, sort * range);
/**
\brief A sort is "fully" interpreted if it is interpreted,
and doesn't depend on other uninterpreted sorts.
*/
bool is_fully_interp(sort * s) const;
bool has_type_var(sort* s) const;
bool has_type_var(func_decl* f) const;
bool has_type_var(unsigned n, sort* const* domain, sort* range) const;
func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range = nullptr);

View file

@ -65,6 +65,13 @@ void ast_translation::collect_decl_extra_children(decl * d) {
}
void ast_translation::push_frame(ast * n) {
// ensure poly roots are pushed first.
if (m_from_manager.has_type_vars() && n->get_kind() == AST_FUNC_DECL && to_func_decl(n)->is_polymorphic()) {
func_decl* g = m_from_manager.poly_root(to_func_decl(n));
if (n != g && m_cache.contains(g)) {
m_frame_stack.push_back(frame(n, 0, m_extra_children_stack.size(), m_result_stack.size()));
}
}
m_frame_stack.push_back(frame(n, 0, m_extra_children_stack.size(), m_result_stack.size()));
switch (n->get_kind()) {
case AST_SORT:
@ -153,6 +160,10 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) {
new_domain,
new_range);
}
else if (f->is_polymorphic() && m_from_manager.poly_root(f) != f) {
func_decl* fr = to_func_decl(m_cache[m_from_manager.poly_root(f)]);
new_f = m_to_manager.instantiate_polymorphic(fr, f->get_arity(), new_domain, new_range);
}
else {
buffer<parameter> ps;
copy_params(f, fr.m_rpos, ps);

View file

@ -24,7 +24,8 @@ Notes:
* Add or overwrite value in model.
*/
void model_converter::display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e) {
VERIFY(e);
if (!e)
return;
VERIFY(f->get_range() == e->get_sort());
ast_smt2_pp_rev(out, f, e, env, params_ref(), 0, "model-add") << "\n";
}

View file

@ -39,8 +39,10 @@ namespace euf {
}
m_expr2enode.setx(f->get_id(), n, nullptr);
push_node(n);
for (unsigned i = 0; i < num_args; ++i)
set_cgc_enabled(args[i], true);
for (unsigned i = 0; i < num_args; ++i) {
set_cgc_enabled(args[i], true);
args[i]->get_root()->set_is_shared(l_undef);
}
return n;
}
@ -70,7 +72,7 @@ namespace euf {
}
enode_bool_pair egraph::insert_table(enode* p) {
TRACE("euf", tout << bpp(p) << "\n");
TRACE("euf", tout << "insert_table " << bpp(p) << "\n");
//SASSERT(!m_table.contains_ptr(p));
auto rc = m_table.insert(p);
p->m_cg = rc.first;
@ -82,9 +84,14 @@ namespace euf {
}
void egraph::reinsert_equality(enode* p) {
SASSERT(p->is_equality());
SASSERT(p->is_equality());
if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root())
add_literal(p, nullptr);
queue_literal(p, nullptr);
}
void egraph::queue_literal(enode* p, enode* ante) {
if (m_on_propagate_literal)
m_to_merge.push_back(to_merge(p, ante));
}
void egraph::force_push() {
@ -128,9 +135,8 @@ namespace euf {
if (n2 == n)
update_children(n);
else
push_merge(n, n2, justification::congruence(comm, 0));
// merge(n, n2, justification::congruence(comm, m_congruence_timestamp++));
push_merge(n, n2, comm);
return n;
}
@ -145,18 +151,9 @@ namespace euf {
memory::deallocate(m_tmp_node);
}
void egraph::add_plugins() {
if (!m_plugins.empty())
return;
auto insert = [&](plugin* p) {
m_plugins.reserve(p->get_id() + 1);
m_plugins.set(p->get_id(), p);
};
insert(alloc(bv_plugin, *this));
insert(alloc(arith_plugin, *this));
insert(alloc(specrel_plugin, *this));
void egraph::add_plugin(plugin* p) {
m_plugins.reserve(p->get_id() + 1);
m_plugins.set(p->get_id(), p);
}
void egraph::propagate_plugins() {
@ -188,25 +185,21 @@ namespace euf {
}
void egraph::add_literal(enode* n, enode* ante) {
TRACE("euf", tout << "propagate " << bpp(n) << " " << bpp(ante) << "\n");
if (!m_on_propagate_literal)
return;
if (!ante) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
if (!ante)
m_on_propagate_literal(n, ante);
else if (m.is_true(ante->get_expr()) || m.is_false(ante->get_expr())) {
for (enode* k : enode_class(n)) {
if (k != ante) {
//verbose_stream() << "eq: " << k->value() << " " <<ante->value() << "\n";
m_on_propagate_literal(k, ante);
}
}
for (enode* k : enode_class(n))
if (k != ante)
m_on_propagate_literal(k, ante);
}
else {
for (enode* k : enode_class(n)) {
if (k->value() != ante->value()) {
//verbose_stream() << "eq: " << k->value() << " " <<ante->value() << "\n";
m_on_propagate_literal(k, ante);
}
if (k->value() != ante->value())
m_on_propagate_literal(k, ante);
}
}
}
@ -516,6 +509,7 @@ namespace euf {
c->m_root = r2;
std::swap(r1->m_next, r2->m_next);
r2->inc_class_size(r1->class_size());
r2->set_is_shared(l_undef);
merge_th_eq(r1, r2, j);
reinsert_parents(r1, r2);
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr())))
@ -532,6 +526,7 @@ namespace euf {
void egraph::remove_parents(enode* r) {
TRACE("euf", tout << bpp(r) << "\n");
SASSERT(all_of(enode_parents(r), [&](enode* p) { return !p->is_marked1(); }));
for (enode* p : enode_parents(r)) {
if (p->is_marked1())
continue;
@ -558,7 +553,7 @@ namespace euf {
if (p->cgc_enabled()) {
auto [p_other, comm] = insert_table(p);
SASSERT(m_table.contains_ptr(p) == (p_other == p));
TRACE("euf", tout << "other " << bpp(p_other) << "\n";);
CTRACE("euf", p_other != p, tout << "reinsert " << bpp(p) << " == " << bpp(p_other) << " " << p->value() << " " << p_other->value() << "\n");
if (p_other != p)
m_to_merge.push_back(to_merge(p_other, p, comm));
else
@ -594,6 +589,7 @@ namespace euf {
enode* r2 = r1->get_root();
TRACE("euf", tout << "undo-eq old-root: " << bpp(r1) << " current-root " << bpp(r2) << " node: " << bpp(n1) << "\n";);
r2->dec_class_size(r1->class_size());
r2->set_is_shared(l_undef);
std::swap(r1->m_next, r2->m_next);
auto begin = r2->begin_parents() + r2_num_parents, end = r2->end_parents();
for (auto it = begin; it != end; ++it) {
@ -615,18 +611,29 @@ namespace euf {
unmerge_justification(n1);
}
bool egraph::propagate() {
bool egraph::propagate() {
force_push();
propagate_plugins();
for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) {
auto const& w = m_to_merge[i];
if (w.j.is_congruence())
merge(w.a, w.b, justification::congruence(w.j.is_commutative(), m_congruence_timestamp++));
else
merge(w.a, w.b, w.j);
if (i + 1 == m_to_merge.size())
propagate_plugins();
unsigned i = 0;
bool change = true;
while (change) {
change = false;
propagate_plugins();
for (; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) {
auto const& w = m_to_merge[i];
switch (w.t) {
case to_merge_plain:
case to_merge_comm:
merge(w.a, w.b, justification::congruence(w.commutativity(), m_congruence_timestamp++));
break;
case to_justified:
merge(w.a, w.b, w.j);
break;
case to_add_literal:
add_literal(w.a, w.b);
break;
}
}
}
m_to_merge.reset();
return
@ -642,7 +649,7 @@ namespace euf {
m_updates.push_back(update_record(false, update_record::inconsistent()));
m_n1 = n1;
m_n2 = n2;
TRACE("euf", tout << "conflict " << bpp(n1) << " " << bpp(n2) << " " << j << "\n");
TRACE("euf", tout << "conflict " << bpp(n1) << " " << bpp(n2) << " " << j << " " << n1->get_root()->value() << " " << n2->get_root()->value() << "\n");
m_justification = j;
}
@ -920,7 +927,7 @@ namespace euf {
for (enode* n : m_nodes)
max_args = std::max(max_args, n->num_args());
for (enode* n : m_nodes)
display(out, max_args, n);
display(out, max_args, n);
for (auto* p : m_plugins)
if (p)
p->display(out);

View file

@ -88,11 +88,15 @@ namespace euf {
typedef ptr_vector<trail> trail_stack;
enum to_merge_t { to_merge_plain, to_merge_comm, to_justified, to_add_literal };
struct to_merge {
enode* a, * b;
to_merge_t t;
justification j;
to_merge(enode* a, enode* b, bool c) : a(a), b(b), j(justification::congruence(c, 0)) {}
to_merge(enode* a, enode* b, justification j) : a(a), b(b), j(j) {}
bool commutativity() const { return t == to_merge_comm; }
to_merge(enode* a, enode* b, bool c) : a(a), b(b), t(c ? to_merge_comm : to_merge_plain) {}
to_merge(enode* a, enode* b, justification j): a(a), b(b), t(to_justified), j(j) {}
to_merge(enode* p, enode* ante): a(p), b(ante), t(to_add_literal) {}
};
struct stats {
@ -215,6 +219,7 @@ namespace euf {
// plugin related methods
void push_plugin_undo(unsigned th_id) { m_updates.push_back(update_record(th_id, update_record::plugin_undo())); }
void push_merge(enode* a, enode* b, justification j) { m_to_merge.push_back({ a, b, j }); }
void push_merge(enode* a, enode* b, bool comm) { m_to_merge.push_back({ a, b, comm }); }
void propagate_plugins();
void add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r);
@ -222,6 +227,7 @@ namespace euf {
void add_th_diseqs(theory_id id, theory_var v1, enode* r);
bool th_propagates_diseqs(theory_id id) const;
void add_literal(enode* n, enode* ante);
void queue_literal(enode* n, enode* ante);
void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents);
void undo_add_th_var(enode* n, theory_id id);
enode* mk_enode(expr* f, unsigned generation, unsigned num_args, enode * const* args);
@ -256,8 +262,10 @@ namespace euf {
public:
egraph(ast_manager& m);
~egraph();
void add_plugins();
void add_plugin(plugin* p);
plugin* get_plugin(family_id fid) const { return m_plugins.get(fid, nullptr); }
enode* find(expr* f) const { return m_expr2enode.get(f->get_id(), nullptr); }
enode* find(expr* f, unsigned n, enode* const* args);
enode* mk(expr* f, unsigned generation, unsigned n, enode *const* args);
@ -284,6 +292,7 @@ namespace euf {
of new equalities.
*/
bool propagate();
bool can_propagate() const { return !m_to_merge.empty(); }
bool inconsistent() const { return m_inconsistent; }
/**

View file

@ -52,6 +52,7 @@ namespace euf {
bool m_merge_tf_enabled = false;
bool m_is_equality = false; // Does the expression represent an equality
bool m_is_relevant = false;
lbool m_is_shared = l_undef;
lbool m_value = l_undef; // Assignment by SAT solver for Boolean node
sat::bool_var m_bool_var = sat::null_bool_var; // SAT solver variable associated with Boolean node
unsigned m_class_size = 1; // Size of the equivalence class if the enode is the root.
@ -96,6 +97,7 @@ namespace euf {
for (unsigned i = 0; i < num_args; ++i) {
SASSERT(to_app(f)->get_arg(i) == args[i]->get_expr());
n->m_args[i] = args[i];
n->m_args[i]->get_root()->set_is_shared(l_undef);
}
return n;
}
@ -181,6 +183,9 @@ namespace euf {
void unmark3() { m_mark3 = false; }
bool is_marked3() { return m_mark3; }
lbool is_shared() const { return m_is_shared; }
void set_is_shared(lbool s) { m_is_shared = s; }
template<bool m> void mark1_targets() {
enode* n = this;
while (n) {

View file

@ -237,6 +237,8 @@ namespace euf {
UNTAG(table*, t)->erase(n);
break;
}
CTRACE("euf", contains_ptr(n), display(tout));
SASSERT(!contains_ptr(n));
}
bool etable::contains(enode* n) const {

View file

@ -109,9 +109,9 @@ bool has_skolem_functions(expr * n) {
subterms::subterms(expr_ref_vector const& es, bool include_bound, ptr_vector<expr>* esp, expr_mark* vp): m_include_bound(include_bound), m_es(es), m_esp(esp), m_vp(vp) {}
subterms::subterms(expr_ref const& e, bool include_bound, ptr_vector<expr>* esp, expr_mark* vp) : m_include_bound(include_bound), m_es(e.m()), m_esp(esp), m_vp(vp) { if (e) m_es.push_back(e); }
subterms::iterator subterms::begin() { return iterator(* this, m_esp, m_vp, true); }
subterms::iterator subterms::end() { return iterator(*this, nullptr, nullptr, false); }
subterms::iterator::iterator(subterms& f, ptr_vector<expr>* esp, expr_mark* vp, bool start): m_include_bound(f.m_include_bound), m_esp(esp), m_visitedp(vp) {
subterms::iterator subterms::begin() const { return iterator(* this, m_esp, m_vp, true); }
subterms::iterator subterms::end() const { return iterator(*this, nullptr, nullptr, false); }
subterms::iterator::iterator(subterms const& f, ptr_vector<expr>* esp, expr_mark* vp, bool start): m_include_bound(f.m_include_bound), m_esp(esp), m_visitedp(vp) {
if (!esp)
m_esp = &m_es;
else

View file

@ -186,7 +186,7 @@ public:
expr_mark m_visited;
expr_mark* m_visitedp = nullptr;
public:
iterator(subterms& f, ptr_vector<expr>* esp, expr_mark* vp, bool start);
iterator(subterms const& f, ptr_vector<expr>* esp, expr_mark* vp, bool start);
expr* operator*();
iterator operator++(int);
iterator& operator++();
@ -198,8 +198,8 @@ public:
static subterms ground(expr_ref const& e, ptr_vector<expr>* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, false, esp, vp); }
static subterms all(expr_ref_vector const& e, ptr_vector<expr>* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, true, esp, vp); }
static subterms ground(expr_ref_vector const& e, ptr_vector<expr>* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, false, esp, vp); }
iterator begin();
iterator end();
iterator begin() const;
iterator end() const;
};
class subterms_postorder {

View file

@ -324,7 +324,7 @@ func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl *
expr_ref else_value(m.mk_app(to_bv_i, dom.size(), dom.data()), m);
result->set_else(else_value);
}
else if (m_fpa_util.is_to_real(f)) {
else if (m_fpa_util.is_to_real(f)) {
SASSERT(dom.size() == 1);
func_decl_ref to_real_i(m.mk_func_decl(fid, OP_FPA_TO_REAL_I, 0, nullptr, dom.size(), dom.data()), m);
expr_ref else_value(m.mk_app(to_real_i, dom.size(), dom.data()), m);
@ -508,7 +508,7 @@ void bv2fpa_converter::convert_uf2bvuf(model_core * mc, model_core * target_mode
}
}
TRACE("bv2fpa", tout << "Target model: " << *target_model; );
TRACE("bv2fpa", tout << "Target model: " << *target_model << std::endl; );
}
void bv2fpa_converter::display(std::ostream & out) {

View file

@ -147,36 +147,11 @@ void fpa2bv_converter::mk_distinct(func_decl * f, unsigned num, expr * const * a
void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
SASSERT(num == 0);
sort* s = f->get_range();
if (f->get_num_parameters() == 1) {
SASSERT(f->get_parameter(0).is_external());
unsigned p_id = f->get_parameter(0).get_ext_id();
mpf const& v = m_plugin->get_value(p_id);
mk_numeral(s, v, result);
return;
}
scoped_mpf v(m_mpf_manager);
unsigned ebits = m_util.get_ebits(s), sbits = m_util.get_sbits(s);
switch (f->get_decl_kind()) {
case OP_FPA_PLUS_INF:
m_util.fm().mk_pinf(ebits, sbits, v);
break;
case OP_FPA_MINUS_INF:
m_util.fm().mk_ninf(ebits, sbits, v);
break;
case OP_FPA_NAN:
m_util.fm().mk_nan(ebits, sbits, v);
break;
case OP_FPA_PLUS_ZERO:
m_util.fm().mk_pzero(ebits, sbits, v);
break;
case OP_FPA_MINUS_ZERO:
m_util.fm().mk_nzero(ebits, sbits, v);
break;
default:
UNREACHABLE();
}
mk_numeral(s, v, result);
scoped_mpf v(m_mpf_manager);
expr_ref a(m);
a = m.mk_app(f, num, args);
m_util.is_numeral(a, v);
mk_numeral(f->get_range(), v, result);
}
void fpa2bv_converter::mk_numeral(sort * s, mpf const & v, expr_ref & result) {
@ -941,8 +916,8 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref &
dbg_decouple("fpa2bv_div_y_is_pos", y_is_pos);
dbg_decouple("fpa2bv_div_y_is_inf", y_is_inf);
expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m);
expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m);
expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m), c8(m);
expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m), v9(m);
// (x is NaN) || (y is NaN) -> NaN
m_simp.mk_or(x_is_nan, y_is_nan, c1);
@ -998,6 +973,9 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref &
a_sig_ext = m_bv_util.mk_concat(a_sig, m_bv_util.mk_numeral(0, sbits + extra_bits));
b_sig_ext = m_bv_util.mk_zero_extend(sbits + extra_bits, b_sig);
dbg_decouple("fpa2bv_div_a_sig_ext", a_sig_ext);
dbg_decouple("fpa2bv_div_b_sig_ext", b_sig_ext);
expr_ref a_exp_ext(m), b_exp_ext(m);
a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp);
b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp);
@ -1017,14 +995,21 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref &
expr_ref quotient(m);
// b_sig_ext can't be 0 here, so it's safe to use OP_BUDIV_I
quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV_I, a_sig_ext, b_sig_ext);
dbg_decouple("fpa2bv_div_quotient", quotient);
SASSERT(m_bv_util.get_bv_size(quotient) == (sbits + sbits + extra_bits));
expr_ref sticky(m);
expr_ref sticky(m), upper(m), upper_reduced(m), too_large(m);
sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient));
res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky);
upper = m_bv_util.mk_extract(sbits + sbits + extra_bits-1, extra_bits+sbits+2, quotient);
upper_reduced = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, upper.get());
too_large = m.mk_eq(upper_reduced, m_bv_util.mk_numeral(1, 1));
c8 = too_large;
mk_ite(signs_xor, ninf, pinf, v8);
dbg_decouple("fpa2bv_div_res_sig_p4", res_sig);
dbg_decouple("fpa2bv_div_upper", upper);
dbg_decouple("fpa2bv_div_too_large", too_large);
SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4));
@ -1042,10 +1027,14 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref &
m_simp.mk_ite(shift_cond, res_sig, res_sig_shifted, res_sig);
m_simp.mk_ite(shift_cond, res_exp, res_exp_shifted, res_exp);
round(s, rm, res_sgn, res_sig, res_exp, v8);
dbg_decouple("fpa2bv_div_res_sig", res_sig);
dbg_decouple("fpa2bv_div_res_exp", res_exp);
round(s, rm, res_sgn, res_sig, res_exp, v9);
// And finally, we tie them together.
mk_ite(c7, v7, v8, result);
mk_ite(c8, v8, v9, result);
mk_ite(c7, v7, result, result);
mk_ite(c6, v6, result, result);
mk_ite(c5, v5, result, result);
mk_ite(c4, v4, result, result);
@ -2809,8 +2798,46 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr *
expr * e = m.mk_eq(m_util.mk_to_real(result), x);
m_extra_assertions.push_back(e);
// x = 0 -> result = 0+
m_extra_assertions.push_back(m.mk_implies(m.mk_eq(x, zero), m.mk_eq(result, m_util.mk_pzero(result->get_sort()))));
expr_ref r_is_nan(m);
mk_is_nan(result, r_is_nan);
m_extra_assertions.push_back(m.mk_not(r_is_nan));
rational min_real, max_real;
const mpz& max_exp_z = m_mpf_manager.m_powers2.m1(ebits-1);
SASSERT(m_mpz_manager.is_uint(max_exp_z));
unsigned max_exp = m_mpz_manager.get_uint(max_exp_z);
rational max_sig = m_mpf_manager.m_powers2.m1(sbits) / m_mpf_manager.m_powers2(sbits-1);
max_real = max_sig * rational(m_mpf_manager.m_powers2(max_exp));
TRACE("fpa2bv_to_real", tout << "max exp: " << max_exp << " max real: " << max_real << std::endl;);
expr_ref r_is_pinf(m), r_is_ninf(m);
mk_is_pinf(result, r_is_pinf);
mk_is_ninf(result, r_is_ninf);
expr_ref e_max_real(m), e_max_real_neg(m);
e_max_real = m_arith_util.mk_numeral(max_real, false);
e_max_real_neg = m_arith_util.mk_numeral(-max_real, false);
expr_ref rm_nta(m), rm_nte(m), rm_tp(m), rm_tn(m), rm_tz(m);
mk_is_rm(bv_rm, BV_RM_TIES_TO_AWAY, rm_nta);
mk_is_rm(bv_rm, BV_RM_TIES_TO_EVEN, rm_nte);
mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_tp);
mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_tn);
mk_is_rm(bv_rm, BV_RM_TO_ZERO, rm_tz);
expr_ref implies_gt_max_real(m), implies_lt_min_real(m);
implies_gt_max_real = m.mk_implies(r_is_pinf, m.mk_and(rm_tp, m_arith_util.mk_gt(x, e_max_real)));
implies_lt_min_real = m.mk_implies(r_is_ninf, m.mk_and(rm_tn, m_arith_util.mk_lt(x, e_max_real_neg)));
m_extra_assertions.push_back(implies_gt_max_real);
m_extra_assertions.push_back(implies_lt_min_real);
// x = 0 -> result = +0/-0
expr_ref pzero(m), nzero(m);
mk_pzero(result->get_sort(), pzero);
mk_nzero(result->get_sort(), nzero);
m_extra_assertions.push_back(m.mk_implies(m.mk_eq(x, zero), m.mk_or(m.mk_eq(result, pzero), m.mk_eq(result, nzero))));
}
SASSERT(is_well_sorted(m, result));
@ -2854,19 +2881,13 @@ void fpa2bv_converter::mk_to_fp_real_int(func_decl * f, unsigned num, expr * con
m_mpf_manager.set(tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, e.to_mpq().numerator(), q.to_mpq());
m_mpf_manager.set(tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, e.to_mpq().numerator(), q.to_mpq());
app_ref a_nte(m), a_nta(m), a_tp(m), a_tn(m), a_tz(m);
a_nte = m_plugin->mk_numeral(nte);
a_nta = m_plugin->mk_numeral(nta);
a_tp = m_plugin->mk_numeral(tp);
a_tn = m_plugin->mk_numeral(tn);
a_tz = m_plugin->mk_numeral(tz);
expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m);
mk_numeral(a_nte->get_decl(), 0, nullptr, bv_nte);
mk_numeral(a_nta->get_decl(), 0, nullptr, bv_nta);
mk_numeral(a_tp->get_decl(), 0, nullptr, bv_tp);
mk_numeral(a_tn->get_decl(), 0, nullptr, bv_tn);
mk_numeral(a_tz->get_decl(), 0, nullptr, bv_tz);
sort *s = f->get_range();
mk_numeral(s, nte, bv_nte);
mk_numeral(s, nta, bv_nta);
mk_numeral(s, tp, bv_tp);
mk_numeral(s, tn, bv_tn);
mk_numeral(s, tz, bv_tz);
expr_ref c1(m), c2(m), c3(m), c4(m);
c1 = m.mk_eq(bv_rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3));
@ -3003,27 +3024,34 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const
unsigned bv_sz = m_bv_util.get_bv_size(x);
SASSERT(m_bv_util.get_bv_size(rm) == 3);
expr_ref rm_is_to_neg(m);
mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg);
expr_ref bv1_1(m), bv0_sz(m);
bv1_1 = m_bv_util.mk_numeral(1, 1);
bv0_sz = m_bv_util.mk_numeral(0, bv_sz);
expr_ref is_zero(m), pzero(m);
expr_ref is_zero(m), pzero(m), nzero(m);
is_zero = m.mk_eq(x, bv0_sz);
mk_pzero(f, pzero);
mk_nzero(f, nzero);
// Special case: x == 0 -> p/n zero
// Special case: x == 0 -> +zero
expr_ref c1(m), v1(m);
c1 = is_zero;
v1 = pzero;
v1 = pzero; // No -zero (IEEE754)
// Special case: x != 0
expr_ref is_neg_bit(m), exp_too_large(m), sig_4(m), exp_2(m);
expr_ref sign_bit(m), exp_too_large(m), sig_4(m), exp_2(m), rest(m);
expr_ref is_neg(m), x_abs(m), neg_x(m);
is_neg_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x);
is_neg = m.mk_eq(is_neg_bit, bv1_1);
neg_x = m_bv_util.mk_bv_neg(x); // overflow problem?
sign_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x);
rest = m_bv_util.mk_extract(bv_sz - 2, 0, x);
dbg_decouple("fpa2bv_to_fp_signed_rest", rest);
is_neg = m.mk_eq(sign_bit, bv1_1);
neg_x = m_bv_util.mk_bv_neg(x); // overflow ok, x_abs is now unsigned.
x_abs = m.mk_ite(is_neg, neg_x, x);
dbg_decouple("fpa2bv_to_fp_signed_is_neg", is_neg);
dbg_decouple("fpa2bv_to_fp_signed_x_abs", x_abs);
// x_abs has an extra bit in the front.
// x_abs is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2)
// bv_sz-2 is the "1.0" bit for the rounder.
@ -3075,7 +3103,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const
TRACE("fpa2bv_to_fp_signed", tout << "exp worst case sz: " << exp_worst_case_sz << std::endl;);
if (exp_sz < exp_worst_case_sz) {
if (exp_sz <= exp_worst_case_sz) {
// exp_sz < exp_worst_case_sz and exp >= 0.
// Take the maximum legal exponent; this
// allows us to keep the most precision.
@ -3093,7 +3121,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const
dbg_decouple("fpa2bv_to_fp_signed_exp_too_large", exp_too_large);
expr_ref sgn(m), sig(m), exp(m);
sgn = is_neg_bit;
sgn = sign_bit;
sig = sig_4;
exp = exp_2;
@ -3132,6 +3160,9 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con
rm = to_app(args[0])->get_arg(0);
x = args[1];
expr_ref rm_is_to_neg(m);
mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg);
dbg_decouple("fpa2bv_to_fp_unsigned_x", x);
unsigned ebits = m_util.get_ebits(f->get_range());
@ -3143,14 +3174,15 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con
bv0_1 = m_bv_util.mk_numeral(0, 1);
bv0_sz = m_bv_util.mk_numeral(0, bv_sz);
expr_ref is_zero(m), pzero(m);
expr_ref is_zero(m), pzero(m), nzero(m);
is_zero = m.mk_eq(x, bv0_sz);
mk_pzero(f, pzero);
mk_nzero(f, nzero);
// Special case: x == 0 -> p/n zero
// Special case: x == 0 -> +zero
expr_ref c1(m), v1(m);
c1 = is_zero;
v1 = pzero;
v1 = pzero; // No -zero (IEEE754)
// Special case: x != 0
expr_ref exp_too_large(m), sig_4(m), exp_2(m);
@ -3194,7 +3226,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con
unsigned exp_sz = ebits + 2; // (+2 for rounder)
exp_2 = m_bv_util.mk_extract(exp_sz - 1, 0, s_exp);
// the remaining bits are 0 if ebits is large enough.
exp_too_large = m.mk_false(); // This is always in range.
exp_too_large = m.mk_false();
// The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits.
// exp < bv_sz (+sign bit which is [0])

View file

@ -208,6 +208,7 @@ protected:
private:
void mk_nan(sort * s, expr_ref & result);
void mk_nzero(sort * s, expr_ref & result);
void mk_pzero(sort * s, expr_ref & result);
void mk_zero(sort * s, expr_ref & sgn, expr_ref & result);

View file

@ -18,6 +18,7 @@ Revision History:
--*/
#include "ast/normal_forms/elim_term_ite.h"
#include "ast/ast_smt2_pp.h"
#include "ast/rewriter/rewriter_def.h"
br_status elim_term_ite_cfg::reduce_app(func_decl* f, unsigned n, expr * const* args, expr_ref& result, proof_ref& result_pr) {
if (!m.is_term_ite(f)) {
@ -38,3 +39,4 @@ br_status elim_term_ite_cfg::reduce_app(func_decl* f, unsigned n, expr * const*
return BR_DONE;
}
template class rewriter_tpl<elim_term_ite_cfg>;

View file

@ -28,6 +28,7 @@ Notes:
#include "ast/rewriter/var_subst.h"
#include "ast/normal_forms/name_exprs.h"
#include "ast/ast_smt2_pp.h"
#include "ast/ast_pp.h"
#include <array>
/**
@ -149,7 +150,7 @@ class skolemizer {
p = nullptr;
if (m_proofs_enabled) {
if (q->get_kind() == forall_k)
p = m.mk_skolemization(mk_not(m, q), m.mk_not(r));
p = m.mk_skolemization(mk_not(m, q), mk_not(m, r));
else
p = m.mk_skolemization(q, r);
}

View file

@ -19,6 +19,7 @@ Revision History:
#include "ast/occurs.h"
#include "ast/for_each_expr.h"
#include "ast/for_each_ast.h"
// -----------------------------------
//
@ -49,6 +50,15 @@ namespace {
void operator()(quantifier const * n) { }
};
struct sort_proc {
sort* m_s;
sort_proc(sort* s) :m_s(s) {}
void operator()(sort const* s2) { if (m_s == s2) throw found(); }
void operator()(ast*) {}
};
}
// Return true if n1 occurs in n2
@ -74,6 +84,17 @@ bool occurs(func_decl * d, expr * n) {
return false;
}
bool occurs(sort* s1, sort* s2) {
sort_proc p(s1);
try {
for_each_ast(p, s2, true);
}
catch (const found&) {
return true;
}
return false;
}
void mark_occurs(ptr_vector<expr>& to_check, expr* v, expr_mark& occ) {
expr_fast_mark2 visited;
occ.mark(v, true);
@ -116,4 +137,4 @@ void mark_occurs(ptr_vector<expr>& to_check, expr* v, expr_mark& occ) {
to_check.pop_back();
}
}
}
}

View file

@ -31,6 +31,11 @@ bool occurs(expr * n1, expr * n2);
*/
bool occurs(func_decl * d, expr * n);
/**
* \brief Return true if s1 occurs in s2
*/
bool occurs(sort* s1, sort* s2);
/**
* \brief Mark sub-expressions of to_check by whether v occurs in these.
*/

View file

@ -7,7 +7,7 @@ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/database.h")
endif()
add_custom_command(OUTPUT "database.h"
COMMAND "${PYTHON_EXECUTABLE}"
COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/scripts/mk_pat_db.py"
"${CMAKE_CURRENT_SOURCE_DIR}/database.smt2"
"${CMAKE_CURRENT_BINARY_DIR}/database.h"

View file

@ -624,9 +624,11 @@ bool pattern_inference_cfg::reduce_quantifier(
proof_ref & result_pr) {
TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";);
if (!is_forall(q)) {
if (!m_params.m_pi_enabled)
return false;
if (!is_forall(q))
return false;
}
int weight = q->get_weight();
@ -653,9 +655,8 @@ bool pattern_inference_cfg::reduce_quantifier(
}
}
if (q->get_num_patterns() > 0) {
if (q->get_num_patterns() > 0)
return false;
}
if (m_params.m_pi_nopat_weight >= 0)
weight = m_params.m_pi_nopat_weight;

View file

@ -0,0 +1,139 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
polymorphism_inst.cpp
Abstract:
Utilities for instantiating polymorphic assertions.
Author:
Nikolaj Bjorner (nbjorner) 2023-7-8
--*/
#include "ast/polymorphism_inst.h"
#include "ast/ast_pp.h"
namespace polymorphism {
void inst::add(expr* e) {
if (!m.has_type_vars())
return;
if (m_from_instantiation.contains(e))
return;
instances inst;
u.collect_poly_instances(e, inst.m_poly_fns);
if (inst.m_poly_fns.empty())
return;
if (m_instances.contains(e))
return;
add_instantiations(e, inst.m_poly_fns);
if (!u.has_type_vars(e))
return;
// insert e into the occurs list for polymorphic roots
ast_mark seen;
for (auto* f : inst.m_poly_fns) {
f = m.poly_root(f);
if (seen.is_marked(f))
continue;
seen.mark(f, true);
if (!m_occurs.contains(f)) {
m_occurs.insert(f, ptr_vector<expr>());
t.push(insert_map(m_occurs, f));
}
auto& es = m_occurs.find(f);
es.push_back(e);
t.push(remove_back(m_occurs, f));
}
m_assertions.push_back(e);
t.push(push_back_vector(m_assertions));
u.collect_type_vars(e, inst.m_tvs);
inst.m_subst = alloc(substitutions);
inst.m_subst->insert(alloc(substitution, m));
m_instances.insert(e, inst);
t.push(new_obj_trail(inst.m_subst));
t.push(insert_map(m_instances, e));
}
void inst::collect_instantiations(expr* e) {
ptr_vector<func_decl> instances;
u.collect_poly_instances(e, instances);
add_instantiations(e, instances);
}
void inst::add_instantiations(expr* e, ptr_vector<func_decl> const& instances) {
for (auto* f : instances) {
if (m_in_decl_queue.is_marked(f))
continue;
m_in_decl_queue.mark(f, true);
m_decl_queue.push_back(f);
t.push(add_decl_queue(*this));
}
}
void inst::instantiate(vector<instantiation>& instances) {
unsigned num_decls = m_decl_queue.size();
if (m_assertions_qhead < m_assertions.size()) {
t.push(value_trail(m_assertions_qhead));
for (; m_assertions_qhead < m_assertions.size(); ++m_assertions_qhead) {
expr* e = m_assertions.get(m_assertions_qhead);
for (unsigned i = 0; i < num_decls; ++i)
instantiate(m_decl_queue.get(i), e, instances);
}
}
if (m_decl_qhead < num_decls) {
t.push(value_trail(m_decl_qhead));
for (; m_decl_qhead < num_decls; ++m_decl_qhead) {
func_decl* p = m_decl_queue.get(m_decl_qhead);
for (expr* e : m_occurs[m.poly_root(p)])
instantiate(p, e, instances);
}
}
}
void inst::instantiate(func_decl* f1, expr* e, vector<instantiation>& instances) {
auto const& [tv, fns, substs] = m_instances[e];
for (auto* f2 : fns) {
substitution sub1(m), new_sub(m);
if (!u.unify(f1, f2, sub1))
continue;
if (substs->contains(&sub1))
continue;
substitutions new_substs;
for (auto* sub2 : *substs) {
if (!u.unify(sub1, *sub2, new_sub))
continue;
if (substs->contains(&new_sub))
continue;
if (new_substs.contains(&new_sub))
continue;
expr_ref e_inst = new_sub(e);
if (!m_from_instantiation.contains(e_inst)) {
collect_instantiations(e_inst);
auto* new_sub1 = alloc(substitution, new_sub);
instances.push_back(instantiation(e, e_inst, new_sub1));
new_substs.insert(new_sub1);
m_from_instantiation.insert(e_inst);
m.inc_ref(e_inst);
t.push(insert_ref_map(m, m_from_instantiation, e_inst));
}
}
for (auto* sub2 : new_substs) {
SASSERT(!substs->contains(sub2));
substs->insert(sub2);
t.push(new_obj_trail(sub2));
t.push(insert_map(*substs, sub2));
}
}
}
}

View file

@ -0,0 +1,91 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
polymorphism_inst.h
Abstract:
Utilities for instantiating polymorphic assertions.
Author:
Nikolaj Bjorner (nbjorner) 2023-7-8
--*/
#pragma once
#include "util/trail.h"
#include "ast/ast.h"
#include "ast/polymorphism_util.h"
namespace polymorphism {
struct instantiation {
expr* orig;
expr_ref inst;
substitution* sub;
instantiation(expr* orig, expr_ref& inst, substitution* s):
orig(orig), inst(inst), sub(s) {}
};
class inst {
ast_manager& m;
trail_stack& t;
util u;
struct instances {
ptr_vector<sort> m_tvs;
ptr_vector<func_decl> m_poly_fns;
substitutions* m_subst = nullptr;
};
func_decl_ref_vector m_poly_roots;
obj_map<func_decl, ptr_vector<expr>> m_occurs;
obj_map<expr, instances> m_instances;
func_decl_ref_vector m_decl_queue;
unsigned m_decl_qhead = 0;
ast_mark m_in_decl_queue;
expr_ref_vector m_assertions;
unsigned m_assertions_qhead = 0;
obj_hashtable<expr> m_from_instantiation;
struct add_decl_queue : public trail {
inst& i;
add_decl_queue(inst& i): i(i) {}
void undo() override {
i.m_in_decl_queue.mark(i.m_decl_queue.back(), false);
i.m_decl_queue.pop_back();
};
};
struct remove_back : public trail {
obj_map<func_decl, ptr_vector<expr>>& occ;
func_decl* f;
remove_back(obj_map<func_decl, ptr_vector<expr>>& occ, func_decl* f):
occ(occ), f(f) {}
void undo() override {
occ.find(f).pop_back();
}
};
void instantiate(func_decl* p, expr* e, vector<instantiation>& instances);
void collect_instantiations(expr* e);
void add_instantiations(expr* e, ptr_vector<func_decl> const& insts);
public:
inst(ast_manager& m, trail_stack& t):
m(m), t(t), u(m), m_poly_roots(m), m_decl_queue(m), m_assertions(m) {}
void add(expr* e);
void instantiate(vector<instantiation>& instances);
bool pending() const { return m_decl_qhead < m_decl_queue.size() || m_assertions_qhead < m_assertions.size(); }
};
}

View file

@ -0,0 +1,353 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
polymorphism_util.cpp
Abstract:
Utilities for supporting polymorphic type signatures.
Author:
Nikolaj Bjorner (nbjorner) 2023-7-8
--*/
#include "ast/polymorphism_util.h"
#include "ast/for_each_ast.h"
#include "ast/occurs.h"
#include "ast/ast_pp.h"
namespace polymorphism {
sort_ref_vector substitution::operator()(sort_ref_vector const& s) {
sort_ref_vector r(m);
for (auto* srt : s)
r.push_back((*this)(srt));
return r;
}
sort_ref substitution::operator()(sort* s) {
if (!m.has_type_var(s))
return sort_ref(s, m);
if (s->is_type_var()) {
if (m_sub.find(s, s))
return (*this)(s);
return sort_ref(s, m);
}
unsigned n = s->get_num_parameters();
vector<parameter> ps;
for (unsigned i = 0; i < n; ++i) {
auto p = s->get_parameter(i);
if (p.is_ast() && is_sort(p.get_ast())) {
sort_ref s = (*this)(to_sort(p.get_ast()));
ps.push_back(parameter(s.get()));
}
else
ps.push_back(p);
}
sort_info si(s->get_family_id(), s->get_decl_kind(), n, ps.data(), s->private_parameters());
return sort_ref(m.mk_sort(s->get_name(), si), m);
}
expr_ref substitution::operator()(expr* e) {
ptr_vector<expr> todo;
expr_ref_vector result(m);
todo.push_back(e);
auto in_cache = [&](expr* a) {
return result.size() > a->get_id() && result.get(a->get_id());
};
ptr_buffer<expr> args;
sort_ref_buffer domain(m);
while (!todo.empty()) {
expr* a = todo.back();
if (in_cache(a)) {
todo.pop_back();
continue;
}
if (is_var(a)) {
if (m.has_type_var(a->get_sort()))
result.setx(a->get_id(), m.mk_var(to_var(a)->get_idx(), (*this)(a->get_sort())));
else
result.setx(a->get_id(), a);
todo.pop_back();
}
else if (is_quantifier(a)) {
quantifier* q = to_quantifier(a);
bool pending = false;
if (!in_cache(q->get_expr())) {
todo.push_back(q->get_expr());
pending = true;
}
ptr_buffer<expr> patterns, no_patterns;
unsigned np = q->get_num_patterns();
for (unsigned i = 0; i < np; ++i) {
if (!in_cache(q->get_pattern(i))) {
todo.push_back(q->get_pattern(i));
pending = true;
}
else
patterns.push_back(result.get(q->get_pattern(i)->get_id()));
}
np = q->get_num_no_patterns();
for (unsigned i = 0; i < np; ++i) {
if (!in_cache(q->get_no_pattern(i))) {
todo.push_back(q->get_no_pattern(i));
pending = true;
}
else
no_patterns.push_back(result.get(q->get_no_pattern(i)->get_id()));
}
if (pending)
continue;
todo.pop_back();
domain.reset();
for (unsigned i = 0; i < q->get_num_decls(); ++i)
domain.push_back((*this)(q->get_decl_sort(i)));
quantifier* q2 =
m.mk_quantifier(q->get_kind(), q->get_num_decls(), domain.data(), q->get_decl_names(), result.get(q->get_expr()->get_id()),
q->get_weight(),
q->get_qid(), q->get_skid(),
q->get_num_patterns(), patterns.data(), q->get_num_no_patterns(), no_patterns.data()
);
result.setx(q->get_id(), q2);
}
else if (is_app(a)) {
args.reset();
unsigned n = todo.size();
for (expr* arg : *to_app(a)) {
if (!in_cache(arg))
todo.push_back(arg);
else
args.push_back(result.get(arg->get_id()));
}
if (n < todo.size())
continue;
func_decl* f = to_app(a)->get_decl();
if (f->is_polymorphic()) {
domain.reset();
for (unsigned i = 0; i < f->get_arity(); ++i)
domain.push_back((*this)(f->get_domain(i)));
sort_ref range = (*this)(f->get_range());
f = m.instantiate_polymorphic(f, f->get_arity(), domain.data(), range);
}
result.setx(a->get_id(), m.mk_app(f, args));
todo.pop_back();
}
}
return expr_ref(result.get(e->get_id()), m);
}
bool substitution::unify(sort* s1, sort* s2) {
if (s1 == s2)
return true;
if (s1->is_type_var() && m_sub.find(s1, s1))
return unify(s1, s2);
if (s2->is_type_var() && m_sub.find(s2, s2))
return unify(s1, s2);
if (s2->is_type_var() && !s1->is_type_var())
std::swap(s1, s2);
if (s1->is_type_var()) {
auto s22 = (*this)(s2);
if (occurs(s1, s22))
return false;
m_trail.push_back(s22);
m_trail.push_back(s1);
m_sub.insert(s1, s22);
return true;
}
if (s1->get_family_id() != s2->get_family_id())
return false;
if (s1->get_decl_kind() != s2->get_decl_kind())
return false;
if (s1->get_name() != s2->get_name())
return false;
if (s1->get_num_parameters() != s2->get_num_parameters())
return false;
for (unsigned i = s1->get_num_parameters(); i-- > 0;) {
auto p1 = s1->get_parameter(i);
auto p2 = s2->get_parameter(i);
if (p1.is_ast() && is_sort(p1.get_ast())) {
if (!p2.is_ast())
return false;
if (!is_sort(p2.get_ast()))
return false;
if (!unify(to_sort(p1.get_ast()), to_sort(p2.get_ast())))
return false;
continue;
}
if (p1 != p2)
return false;
}
return true;
}
bool substitution::match(sort* s1, sort* s2) {
if (s1 == s2)
return true;
if (s1->is_type_var() && m_sub.find(s1, s1))
return match(s1, s2);
if (s1->is_type_var()) {
m_trail.push_back(s2);
m_trail.push_back(s1);
m_sub.insert(s1, s2);
return true;
}
if (s1->get_family_id() != s2->get_family_id())
return false;
if (s1->get_decl_kind() != s2->get_decl_kind())
return false;
if (s1->get_name() != s2->get_name())
return false;
if (s1->get_num_parameters() != s2->get_num_parameters())
return false;
for (unsigned i = s1->get_num_parameters(); i-- > 0;) {
auto p1 = s1->get_parameter(i);
auto p2 = s2->get_parameter(i);
if (p1.is_ast() && is_sort(p1.get_ast())) {
if (!p2.is_ast())
return false;
if (!is_sort(p2.get_ast()))
return false;
if (!match(to_sort(p1.get_ast()), to_sort(p2.get_ast())))
return false;
continue;
}
if (p1 != p2)
return false;
}
return true;
}
// util
bool util::unify(sort* s1, sort* s2, substitution& sub) {
return sub.unify(s1, s2);
}
bool util::unify(func_decl* f1, func_decl* f2, substitution& sub) {
if (f1 == f2)
return true;
if (!f1->is_polymorphic() || !f2->is_polymorphic())
return false;
if (m.poly_root(f1) != m.poly_root(f2))
return false;
for (unsigned i = f1->get_arity(); i-- > 0; )
if (!sub.unify(fresh(f1->get_domain(i)), f2->get_domain(i)))
return false;
return sub.unify(fresh(f1->get_range()), f2->get_range());
}
bool util::unify(substitution const& s1, substitution const& s2,
substitution& sub) {
sort* v2;
for (auto const& [k, v] : s1)
sub.insert(k, v);
for (auto const& [k, v] : s2) {
if (sub.find(k, v2)) {
if (!sub.unify(sub(v), v2))
return false;
}
else
sub.insert(k, sub(v));
}
return true;
}
bool util::match(substitution& sub, sort* s1, sort* s_ground) {
return sub.match(s1, s_ground);
}
/**
* Create fresh variables, but with caching.
* So "fresh" variables are not truly fresh globally.
* This can block some unifications and therefore block some instantiations of
* polymorphic assertions. A different caching scheme could be created to
* ensure that fresh variables are introduced at the right time, or use other
* tricks such as creating variable/offset pairs to distinguish name spaces without
* incurring costs.
*/
sort_ref util::fresh(sort* s) {
sort* s1;
if (m_fresh.find(s, s1))
return sort_ref(s1, m);
if (m.is_type_var(s)) {
s1 = m.mk_type_var(symbol("fresh!" + std::to_string(m_counter)));
m_trail.push_back(s1);
m_trail.push_back(s);
m_fresh.insert(s, s1);
return sort_ref(s1, m);
}
vector<parameter> params;
for (unsigned i = 0; i < s->get_num_parameters(); ++i) {
parameter p = s->get_parameter(i);
if (p.is_ast() && is_sort(p.get_ast())) {
sort_ref fs = fresh(to_sort(p.get_ast()));
params.push_back(parameter(fs.get()));
}
else
params.push_back(p);
}
sort_info info(s->get_family_id(), s->get_decl_kind(), params.size(), params.data(), s->private_parameters());
s1 = m.mk_sort(s->get_name(), info);
m_trail.push_back(s1);
m_trail.push_back(s);
m_fresh.insert(s, s1);
return sort_ref(s1, m);
}
sort_ref_vector util::fresh(unsigned n, sort* const* s) {
sort_ref_vector r(m);
for (unsigned i = 0; i < n; ++i)
r.push_back(fresh(s[i]));
return r;
}
void util::collect_poly_instances(expr* e, ptr_vector<func_decl>& instances) {
struct proc {
ast_manager& m;
ptr_vector<func_decl>& instances;
proc(ast_manager& m, ptr_vector<func_decl>& instances) : m(m), instances(instances) {}
void operator()(func_decl* f) {
if (f->is_polymorphic() && !m.is_eq(f) && !is_decl_of(f, pattern_family_id, OP_PATTERN))
instances.push_back(f);
}
void operator()(ast* a) {}
};
proc proc(m, instances);
for_each_ast(proc, e, false);
}
bool util::has_type_vars(expr* e) {
struct proc {
ast_manager& m;
bool found = false;
proc(ast_manager& m) : m(m) {}
void operator()(sort* f) {
if (m.has_type_var(f))
found = true;
}
void operator()(ast* a) {}
};
proc proc(m);
for_each_ast(proc, e, false);
return proc.found;
}
void util::collect_type_vars(expr* e, ptr_vector<sort>& tvs) {
struct proc {
ast_manager& m;
ptr_vector<sort>& tvs;
proc(ast_manager& m, ptr_vector<sort>& tvs) : m(m), tvs(tvs) {}
void operator()(sort* s) {
if (m.is_type_var(s))
tvs.push_back(s);
}
void operator()(ast* a) {}
};
proc proc(m, tvs);
for_each_ast(proc, e, true);
}
}

112
src/ast/polymorphism_util.h Normal file
View file

@ -0,0 +1,112 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
polymorphism_util.h
Abstract:
Utilities for supporting polymorphic type signatures.
Author:
Nikolaj Bjorner (nbjorner) 2023-7-8
--*/
#pragma once
#include "ast/ast.h"
#include "util/hashtable.h"
namespace polymorphism {
class substitution {
ast_manager& m;
obj_map<sort, sort*> m_sub;
sort_ref_vector m_trail;
public:
substitution(ast_manager& m): m(m), m_trail(m) {}
sort_ref_vector operator()(sort_ref_vector const& s);
sort_ref operator()(sort* s);
expr_ref operator()(expr* e);
bool unify(sort* s1, sort* s2);
bool match(sort* s1, sort* s_ground);
obj_map<sort, sort*>::iterator begin() const { return m_sub.begin(); }
obj_map<sort, sort*>::iterator end() const { return m_sub.end(); }
void insert(sort* v, sort* t) { m_trail.push_back(v).push_back(t); m_sub.insert(v, t); }
bool find(sort* v, sort*& t) const { return m_sub.find(v, t); }
unsigned size() const { return m_sub.size(); }
/**
* weak equality: strong equality considers applying substitutions recursively in range
* because substitutions may be in triangular form.
*/
struct eq {
bool operator()(substitution const* s1, substitution const* s2) const {
if (s1->size() != s2->size())
return false;
sort* v2;
for (auto const& [k, v] : *s1) {
if (!s2->find(k, v2))
return false;
if (v != v2)
return false;
}
return true;
}
};
struct hash {
unsigned operator()(substitution const* s) const {
unsigned hash = 0xfabc1234 + s->size();
for (auto const& [k, v] : *s)
hash ^= k->hash() + 2 * v->hash();
return hash;
}
};
};
typedef hashtable<substitution*, substitution::hash, substitution::eq> substitutions;
class util {
ast_manager& m;
sort_ref_vector m_trail;
obj_map<sort, sort*> m_fresh;
unsigned m_counter = 0;
sort_ref fresh(sort* s);
sort_ref_vector fresh(unsigned n, sort* const* s);
public:
util(ast_manager& m): m(m), m_trail(m) {}
bool unify(sort* s1, sort* s2, substitution& sub);
bool unify(func_decl* f1, func_decl* f2, substitution& sub);
bool unify(substitution const& s1, substitution const& s2,
substitution& sub);
bool match(substitution& sub, sort* s1, sort* s_ground);
// collect instantiations of polymorphic functions
void collect_poly_instances(expr* e, ptr_vector<func_decl>& instances);
// test if expression contains polymorphic variable.
bool has_type_vars(expr* e);
void collect_type_vars(expr* e, ptr_vector<sort>& tvs);
};
}

View file

@ -23,7 +23,6 @@ z3_add_component(rewriter
factor_rewriter.cpp
fpa_rewriter.cpp
func_decl_replace.cpp
hoist_rewriter.cpp
inj_axiom.cpp
label_rewriter.cpp
macro_replacer.cpp

View file

@ -551,25 +551,10 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
}
if (m_anum_simp) {
if (is_numeral(arg1, a1) && m_util.is_irrational_algebraic_numeral(arg2)) {
anum_manager & am = m_util.am();
scoped_anum v1(am);
am.set(v1, a1.to_mpq());
anum const & v2 = m_util.to_irrational_algebraic_numeral(arg2);
ANUM_LE_GE_EQ();
}
if (m_util.is_irrational_algebraic_numeral(arg1) && is_numeral(arg2, a2)) {
anum_manager & am = m_util.am();
anum const & v1 = m_util.to_irrational_algebraic_numeral(arg1);
scoped_anum v2(am);
am.set(v2, a2.to_mpq());
ANUM_LE_GE_EQ();
}
if (m_util.is_irrational_algebraic_numeral(arg1) && m_util.is_irrational_algebraic_numeral(arg2)) {
anum_manager & am = m_util.am();
anum const & v1 = m_util.to_irrational_algebraic_numeral(arg1);
anum const & v2 = m_util.to_irrational_algebraic_numeral(arg2);
ANUM_LE_GE_EQ();
auto& am = m_util.am();
scoped_anum v1(am), v2(am);
if (is_algebraic_numeral(arg1, v1) && is_algebraic_numeral(arg2, v2)) {
ANUM_LE_GE_EQ();
}
}
br_status st1 = is_separated(arg1, arg2, kind, result);
@ -669,6 +654,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
return BR_FAILED;
}
br_status arith_rewriter::mk_le_core(expr * arg1, expr * arg2, expr_ref & result) {
return mk_le_ge_eq_core(arg1, arg2, LE, result);
}
@ -744,18 +730,26 @@ bool arith_rewriter::mk_eq_mod(expr* arg1, expr* arg2, expr_ref& result) {
if (g == 1) {
expr_ref nb(m_util.mk_numeral(b, true), m);
result = m.mk_eq(m_util.mk_mod(u, y),
m_util.mk_mod(m_util.mk_mul(nb, arg2), y));
m_util.mk_mod(m_util.mk_mul(nb, arg2), y));
return true;
}
}
return false;
}
expr_ref arith_rewriter::neg_monomial(expr* e) const {
expr_ref arith_rewriter::neg_monomial(expr* e) {
expr_ref_vector args(m);
rational a1;
if (m_util.is_numeral(e, a1))
args.push_back(m_util.mk_numeral(-a1, e->get_sort()));
else if (m_util.is_irrational_algebraic_numeral(e)) {
auto& n = m_util.to_irrational_algebraic_numeral(e);
auto& am = m_util.am();
scoped_anum new_n(am);
am.set(new_n, n);
am.neg(new_n);
args.push_back(m_util.mk_numeral(am, new_n, m_util.is_int(e)));
}
else if (is_app(e) && m_util.is_mul(e)) {
if (is_numeral(to_app(e)->get_arg(0), a1)) {
if (!a1.is_minus_one()) {
@ -780,7 +774,7 @@ expr_ref arith_rewriter::neg_monomial(expr* e) const {
}
}
bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) const {
bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) {
rational r;
if (m_util.is_mul(t) && is_numeral(to_app(t)->get_arg(0), r) && r.is_neg()) {
neg = neg_monomial(t);
@ -824,6 +818,36 @@ bool arith_rewriter::is_anum_simp_target(unsigned num_args, expr * const * args)
return false;
}
bool arith_rewriter::is_algebraic_numeral(expr* n, scoped_anum& a) {
auto& am = m_util.am();
expr* x, *y;
rational r;
if (m_util.is_mul(n, x, y)) {
scoped_anum ax(am), ay(am);
if (is_algebraic_numeral(x, ax) && is_algebraic_numeral(y, ay)) {
am.mul(ax, ay, a);
return true;
}
}
else if (m_util.is_add(n, x, y)) {
scoped_anum ax(am), ay(am);
if (is_algebraic_numeral(x, ax) && is_algebraic_numeral(y, ay)) {
am.add(ax, ay, a);
return true;
}
}
else if (m_util.is_numeral(n, r)) {
am.set(a, r.to_mpq());
return true;
}
else if (m_util.is_irrational_algebraic_numeral(n)) {
am.set(a, m_util.to_irrational_algebraic_numeral(n));
return true;
}
return false;
}
br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
if (is_anum_simp_target(num_args, args)) {
expr_ref_buffer new_args(m);

View file

@ -21,6 +21,7 @@ Notes:
#include "ast/rewriter/poly_rewriter.h"
#include "ast/arith_decl_plugin.h"
#include "ast/seq_decl_plugin.h"
#include "math/polynomial/algebraic_numbers.h"
class arith_rewriter_core {
protected:
@ -80,6 +81,7 @@ class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
void updt_local_params(params_ref const & p);
bool is_anum_simp_target(unsigned num_args, expr * const * args);
bool is_algebraic_numeral(expr* n, scoped_anum& a);
br_status mk_div_irrat_rat(expr * arg1, expr * arg2, expr_ref & result);
br_status mk_div_rat_irrat(expr * arg1, expr * arg2, expr_ref & result);
@ -97,8 +99,8 @@ class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
bool is_2_pi_integer_offset(expr * t, expr * & m);
bool is_pi_integer(expr * t);
bool is_pi_integer_offset(expr * t, expr * & m);
bool is_neg_poly(expr* e, expr_ref& neg) const;
expr_ref neg_monomial(expr * e) const;
bool is_neg_poly(expr* e, expr_ref& neg);
expr_ref neg_monomial(expr * e);
expr * mk_sin_value(rational const & k);
app * mk_sqrt(rational const & k);
bool divides(expr* d, expr* n, expr_ref& result);

View file

@ -24,6 +24,7 @@ Notes:
#include "ast/rewriter/var_subst.h"
#include "params/array_rewriter_params.hpp"
#include "util/util.h"
#include "ast/array_peq.h"
void array_rewriter::updt_params(params_ref const & _p) {
array_rewriter_params p(_p);
@ -40,8 +41,48 @@ void array_rewriter::get_param_descrs(param_descrs & r) {
}
br_status array_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
br_status st = BR_FAILED;
// BEGIN: rewrite rules for PEQs
if (is_partial_eq(f)) {
SASSERT(num_args >= 2);
expr *e0, *e1;
e0 = args[0];
e1 = args[1];
expr_ref a(m()), val(m());
expr_ref_vector vindex(m());
if (e0 == e1) {
// t peq t --> true
result = m().mk_true();
st = BR_DONE;
}
else if (m_util.is_store_ext(e0, a, vindex, val)) {
if (num_args == 2 && a == e1) {
// (a[i := x] peq_{\emptyset} a) ---> a[i] == x
mk_select(vindex.size(), vindex.data(), result);
result = m().mk_eq(result, val);
st = BR_REWRITE_FULL;
}
else if (a == e1 && vindex.size() == num_args + 2) {
// a [i: = x] peq_{i} a -- > true
bool all_eq = true;
for (unsigned i = 0, sz = vindex.size(); all_eq && i < sz;
++i) {
all_eq &= vindex.get(i) == args[2+i];
}
if (all_eq) {
result = m().mk_true();
st = BR_DONE;
}
}
}
return st;
}
// END: rewrite rules for PEQs
SASSERT(f->get_family_id() == get_fid());
br_status st;
switch (f->get_decl_kind()) {
case OP_SELECT:
st = mk_select_core(num_args, args, result);

View file

@ -21,6 +21,7 @@ Notes:
#include "ast/rewriter/bit_blaster/bit_blaster_tpl_def.h"
#include "ast/rewriter/rewriter_def.h"
#include "ast/rewriter/bool_rewriter.h"
#include "ast/rewriter/th_rewriter.h"
#include "util/ref_util.h"
#include "ast/ast_smt2_pp.h"
@ -549,10 +550,19 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
case OP_INT2BV:
case OP_BV2INT:
return BR_FAILED;
default:
default:
TRACE("bit_blaster", tout << "non-supported operator: " << f->get_name() << "\n";
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;);
{
expr_ref r(m().mk_app(f, num, args), m());
result = r;
th_rewriter rw(m());
rw(result);
if (!is_app(result) || to_app(result)->get_decl() != f)
return BR_REWRITE_FULL;
}
throw_unsupported(f);
}
}

View file

@ -34,7 +34,6 @@ void bool_rewriter::updt_params(params_ref const & _p) {
m_blast_distinct = p.blast_distinct();
m_blast_distinct_threshold = p.blast_distinct_threshold();
m_ite_extra_rules = p.ite_extra_rules();
m_hoist.set_elim_and(m_elim_and);
}
void bool_rewriter::get_param_descrs(param_descrs & r) {
@ -270,28 +269,6 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args
return BR_DONE;
}
#if 1
br_status st;
expr_ref r(m());
st = m_hoist.mk_or(buffer.size(), buffer.data(), r);
if (st != BR_FAILED) {
m_counts1.reserve(m().get_num_asts() + 1);
m_counts2.reserve(m().get_num_asts() + 1);
get_num_internal_exprs(m_counts1, m_todo1, r);
for (unsigned i = 0; i < num_args; ++i)
get_num_internal_exprs(m_counts2, m_todo2, args[i]);
unsigned count1 = count_internal_nodes(m_counts1, m_todo1);
unsigned count2 = count_internal_nodes(m_counts2, m_todo2);
if (count1 > count2)
st = BR_FAILED;
}
if (st != BR_FAILED)
result = r;
if (st == BR_DONE)
return BR_REWRITE1;
if (st != BR_FAILED)
return st;
#endif
if (s) {
if (m_sort_disjunctions) {
ast_lt lt;

View file

@ -20,7 +20,6 @@ Notes:
#include "ast/ast.h"
#include "ast/rewriter/rewriter.h"
#include "ast/rewriter/hoist_rewriter.h"
#include "util/params.h"
/**
@ -51,7 +50,6 @@ Notes:
*/
class bool_rewriter {
ast_manager & m_manager;
hoist_rewriter m_hoist;
bool m_flat_and_or = false;
bool m_sort_disjunctions = true;
bool m_local_ctx = false;
@ -84,7 +82,7 @@ class bool_rewriter {
void push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits);
public:
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) {
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) {
updt_params(p);
}
ast_manager & m() const { return m_manager; }

View file

@ -217,7 +217,7 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
case OP_BUSUB_OVFL:
return mk_bvusub_underflow(num_args, args, result);
case OP_BSSUB_OVFL:
return mk_bvssub_overflow(num_args, args, result);
return mk_bvssub_under_overflow(num_args, args, result);
default:
return BR_FAILED;
}
@ -3085,19 +3085,29 @@ br_status bv_rewriter::mk_bvusub_underflow(unsigned num, expr * const * args, ex
return status;
}
br_status bv_rewriter::mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result) {
//
// no_overflow := if t2 = min_int then t1 <s 0 else no_overflow(t1 + -t2)
// no_underflow := 0 <s -t2 => no_underflow(t1 + -t2)
// over_underflow := 0 <s -t2 & under_overflow+(t1 + -t2) || t2 = min_int & t1 >=s 0 || t2 != min_int & under_overflow+(t1 + -t2)
// := if t2 == min_int then t1 >=s 0 else under_overflow+(t1 + -t2)
// because when 0 <s min_int = false
//
br_status bv_rewriter::mk_bvssub_under_overflow(unsigned num, expr * const * args, expr_ref & result) {
SASSERT(num == 2);
SASSERT(get_bv_size(args[0]) == get_bv_size(args[1]));
auto sz = get_bv_size(args[0]);
auto minSigned = mk_numeral(rational::power_of_two(sz-1), sz);
expr_ref bvsaddo {m};
expr * args2[2] = { args[0], m_util.mk_bv_neg(args[1]) };
auto bvsaddo_stat = mk_bvsadd_overflow(2, args2, bvsaddo);
auto bvsaddo_stat = mk_bvsadd_over_underflow(2, args2, bvsaddo);
SASSERT(bvsaddo_stat != BR_FAILED); (void)bvsaddo_stat;
auto first_arg_ge_zero = m_util.mk_sle(mk_zero(sz), args[0]);
result = m.mk_ite(m.mk_eq(args[1], minSigned), first_arg_ge_zero, bvsaddo);
return BR_REWRITE_FULL;
}
//br_status bv_rewriter::mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result) {
//}
br_status bv_rewriter::mk_bvsdiv_overflow(unsigned num, expr * const * args, expr_ref & result) {
SASSERT(num == 2);
SASSERT(get_bv_size(args[0]) == get_bv_size(args[1]));

View file

@ -153,7 +153,8 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
br_status mk_bvsadd_over_underflow(unsigned num, expr * const * args, expr_ref & result);
br_status mk_bvusub_underflow(unsigned num, expr * const * args, expr_ref & result);
br_status mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result);
// br_status mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result);
br_status mk_bvssub_under_overflow(unsigned num, expr * const * args, expr_ref & result);
bool is_minus_one_times_t(expr * arg);
void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result);

View file

@ -21,7 +21,8 @@ Notes:
br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(f->get_family_id() == get_fid());
switch(f->get_decl_kind()) {
case OP_DT_CONSTRUCTOR: return BR_FAILED;
case OP_DT_CONSTRUCTOR:
return BR_FAILED;
case OP_DT_RECOGNISER:
SASSERT(num_args == 1);
result = m_util.mk_is(m_util.get_recognizer_constructor(f), args[0]);

View file

@ -17,8 +17,6 @@ Revision History:
--*/
#ifndef ELIM_BOUNDS_H_
#define ELIM_BOUNDS_H_
#include "ast/used_vars.h"
#include "util/obj_hashtable.h"
@ -201,4 +199,3 @@ bool elim_bounds_cfg::reduce_quantifier(quantifier * q,
return true;
}
#endif /* ELIM_BOUNDS_H_ */

View file

@ -1,248 +0,0 @@
/*++
Copyright (c) 2019 Microsoft Corporation
Module Name:
hoist_rewriter.cpp
Abstract:
Hoist predicates over disjunctions
Author:
Nikolaj Bjorner (nbjorner) 2019-2-4
--*/
#include "ast/rewriter/hoist_rewriter.h"
#include "ast/rewriter/bool_rewriter.h"
#include "ast/ast_util.h"
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p):
m(m), m_args1(m), m_args2(m), m_refs(m), m_subst(m) {
updt_params(p);
}
expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) {
if (m_elim_and) {
expr_ref_vector negs(m);
for (expr* a : args)
if (m.is_false(a))
return expr_ref(m.mk_false(), m);
else if (m.is_true(a))
continue;
else
negs.push_back(::mk_not(m, a));
return ::mk_not(mk_or(negs));
}
else
return ::mk_and(args);
}
expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) {
return ::mk_or(args);
}
br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) {
if (num_args < 2)
return BR_FAILED;
for (unsigned i = 0; i < num_args; ++i)
if (!is_and(es[i], nullptr))
return BR_FAILED;
bool turn = false;
m_preds1.reset();
m_preds2.reset();
m_uf1.reset();
m_uf2.reset();
m_expr2var.reset();
m_var2expr.reset();
basic_union_find* uf[2] = { &m_uf1, &m_uf2 };
obj_hashtable<expr>* preds[2] = { &m_preds1, &m_preds2 };
expr_ref_vector* args[2] = { &m_args1, &m_args2 };
VERIFY(is_and(es[0], args[turn]));
expr* e1, *e2;
for (expr* e : *(args[turn])) {
if (m.is_eq(e, e1, e2))
(*uf)[turn].merge(mk_var(e1), mk_var(e2));
else
(*preds)[turn].insert(e);
}
unsigned round = 0;
for (unsigned j = 1; j < num_args; ++j) {
++round;
m_es.reset();
m_mark.reset();
bool last = turn;
turn = !turn;
(*preds)[turn].reset();
reset(m_uf0);
VERIFY(is_and(es[j], args[turn]));
for (expr* e : *args[turn]) {
if (m.is_eq(e, e1, e2)) {
m_es.push_back(e1);
m_uf0.merge(mk_var(e1), mk_var(e2));
}
else if ((*preds)[last].contains(e))
(*preds)[turn].insert(e);
}
if ((*preds)[turn].empty() && m_es.empty())
return BR_FAILED;
m_eqs.reset();
for (expr* e : m_es) {
if (m_mark.is_marked(e))
continue;
unsigned u = mk_var(e);
unsigned v = u;
m_roots.reset();
do {
m_mark.mark(e);
unsigned r = (*uf)[last].find(v);
if (m_roots.find(r, e2))
m_eqs.push_back({e, e2});
else
m_roots.insert(r, e);
v = m_uf0.next(v);
e = mk_expr(v);
}
while (u != v);
}
reset((*uf)[turn]);
for (auto const& [e1, e2] : m_eqs)
(*uf)[turn].merge(mk_var(e1), mk_var(e2));
if ((*preds)[turn].empty() && m_eqs.empty())
return BR_FAILED;
}
if (m_eqs.empty()) {
result = hoist_predicates((*preds)[turn], num_args, es);
return BR_DONE;
}
// p & eqs & (or fmls)
expr_ref_vector fmls(m);
m_subst.reset();
for (expr * p : (*preds)[turn]) {
expr* q = nullptr;
if (m.is_not(p, q))
m_subst.insert(q, m.mk_false());
else
m_subst.insert(p, m.mk_true());
fmls.push_back(p);
}
for (auto& p : m_eqs) {
if (m.is_value(p.first))
std::swap(p.first, p.second);
m_subst.insert(p.first, p.second);
fmls.push_back(m.mk_eq(p.first, p.second));
}
expr_ref ors(::mk_or(m, num_args, es), m);
m_subst(ors);
fmls.push_back(ors);
result = mk_and(fmls);
TRACE("hoist", tout << ors << " => " << result << "\n";);
return BR_DONE;
}
unsigned hoist_rewriter::mk_var(expr* e) {
unsigned v = 0;
if (m_expr2var.find(e, v))
return v;
m_uf1.mk_var();
v = m_uf2.mk_var();
SASSERT(v == m_var2expr.size());
m_expr2var.insert(e, v);
m_var2expr.push_back(e);
return v;
}
expr_ref hoist_rewriter::hoist_predicates(obj_hashtable<expr> const& preds, unsigned num_args, expr* const* es) {
expr_ref_vector args(m), args1(m), fmls(m);
for (unsigned i = 0; i < num_args; ++i) {
VERIFY(is_and(es[i], &args1));
fmls.reset();
for (expr* e : args1)
if (!preds.contains(e))
fmls.push_back(e);
args.push_back(mk_and(fmls));
}
fmls.reset();
fmls.push_back(mk_or(args));
for (auto* p : preds)
fmls.push_back(p);
return mk_and(fmls);
}
br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
switch (f->get_decl_kind()) {
case OP_OR:
return mk_or(num_args, args, result);
default:
return BR_FAILED;
}
}
bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) {
#if 0
if (!args)
return m.is_and(e) || (m.is_not(e, e) && m.is_or(e));
expr_fast_mark1 visited;
args->reset();
args->push_back(e);
m_refs.reset();
for (unsigned i = 0; i < args->size(); ++i) {
e = args->get(i);
if (visited.is_marked(e))
goto drop;
m_refs.push_back(e);
visited.mark(e, true);
if (m.is_and(e))
args->append(to_app(e)->get_num_args(), to_app(e)->get_args());
else if (m.is_not(e, e) && m.is_or(e))
for (expr* arg : *to_app(e))
args->push_back(::mk_not(m, arg));
else
continue;
drop:
(*args)[i] = args->back();
args->pop_back();
--i;
}
return args->size() > 1;
#else
if (m.is_and(e)) {
if (args) {
args->reset();
args->append(to_app(e)->get_num_args(), to_app(e)->get_args());
}
return true;
}
if (m.is_not(e, e) && m.is_or(e)) {
if (args) {
args->reset();
for (expr* arg : *to_app(e))
args->push_back(::mk_not(m, arg));
TRACE("hoist", tout << args << " " << * args << "\n");
}
return true;
}
#endif
return false;
}
void hoist_rewriter::reset(basic_union_find& uf) {
uf.reset();
for (expr* e : m_var2expr) {
(void)e;
uf.mk_var();
}
}

View file

@ -1,87 +0,0 @@
/*++
Copyright (c) 2019 Microsoft Corporation
Module Name:
hoist_rewriter.h
Abstract:
Hoist predicates over disjunctions
Author:
Nikolaj Bjorner (nbjorner) 2019-2-4
Notes:
--*/
#pragma once
#include "ast/ast.h"
#include "ast/rewriter/rewriter.h"
#include "ast/rewriter/expr_safe_replace.h"
#include "util/params.h"
#include "util/union_find.h"
#include "util/obj_hashtable.h"
class bool_rewriter;
class hoist_rewriter {
ast_manager & m;
expr_ref_vector m_args1, m_args2, m_refs;
obj_hashtable<expr> m_preds1, m_preds2;
basic_union_find m_uf1, m_uf2, m_uf0;
ptr_vector<expr> m_es;
svector<std::pair<expr*,expr*>> m_eqs;
u_map<expr*> m_roots;
expr_safe_replace m_subst;
obj_map<expr, unsigned> m_expr2var;
ptr_vector<expr> m_var2expr;
expr_mark m_mark;
bool m_elim_and = false;
bool is_and(expr* e, expr_ref_vector* args);
expr_ref mk_and(expr_ref_vector const& args);
expr_ref mk_or(expr_ref_vector const& args);
bool is_var(expr* e) { return m_expr2var.contains(e); }
expr* mk_expr(unsigned v) { return m_var2expr[v]; }
unsigned mk_var(expr* e);
void reset(basic_union_find& uf);
expr_ref hoist_predicates(obj_hashtable<expr> const& p, unsigned num_args, expr* const* args);
public:
hoist_rewriter(ast_manager & m, params_ref const & p = params_ref());
family_id get_fid() const { return m.get_basic_family_id(); }
bool is_eq(expr * t) const { return m.is_eq(t); }
void updt_params(params_ref const & p) {}
static void get_param_descrs(param_descrs & r) {}
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result);
void set_elim_and(bool b) { m_elim_and = b; }
};
struct hoist_rewriter_cfg : public default_rewriter_cfg {
hoist_rewriter m_r;
bool rewrite_patterns() const { return false; }
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
result_pr = nullptr;
if (f->get_family_id() != m_r.get_fid())
return BR_FAILED;
return m_r.mk_app_core(f, num, args, result);
}
hoist_rewriter_cfg(ast_manager & m, params_ref const & p):m_r(m, p) {}
};
class hoist_rewriter_star : public rewriter_tpl<hoist_rewriter_cfg> {
hoist_rewriter_cfg m_cfg;
public:
hoist_rewriter_star(ast_manager & m, params_ref const & p = params_ref()):
rewriter_tpl<hoist_rewriter_cfg>(m, false, m_cfg),
m_cfg(m, p) {}
};

View file

@ -17,6 +17,8 @@ Notes:
--*/
#include "ast/rewriter/rewriter_def.h"
#include "ast/rewriter/push_app_ite.h"
#include "ast/rewriter/elim_bounds.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_pp.h"
#include "ast/ast_smt2_pp.h"
@ -417,3 +419,6 @@ void inv_var_shifter::process_var(var * v) {
}
template class rewriter_tpl<beta_reducer_cfg>;
template class rewriter_tpl<ng_push_app_ite_cfg>;
template class rewriter_tpl<push_app_ite_cfg>;
template class rewriter_tpl<elim_bounds_cfg>;

View file

@ -33,6 +33,18 @@ enum br_status {
BR_FAILED // no builtin rewrite is available
};
inline std::ostream& operator<<(std::ostream& out, br_status st) {
switch (st) {
case BR_REWRITE1: return out << "rewrite1";
case BR_REWRITE2: return out << "rewrite2";
case BR_REWRITE3: return out << "rewrite3";
case BR_REWRITE_FULL: return out << "rewrite_full";
case BR_DONE: return out << "done";
case BR_FAILED: return out << "failed";
default: return out << "unknown";
}
}
#define RW_UNBOUNDED_DEPTH 3
inline br_status unsigned2br_status(unsigned u) {
br_status r = u >= RW_UNBOUNDED_DEPTH ? BR_REWRITE_FULL : static_cast<br_status>(u);

View file

@ -538,6 +538,7 @@ namespace seq {
expr_ref t_eq_empty = mk_eq_empty(t);
expr_ref xsy = mk_concat(x, s, y);
// add_clause(~mk_eq(t, s), i_eq_0);
add_clause(cnt, i_eq_m1);
add_clause(~t_eq_empty, s_eq_empty, i_eq_m1);
add_clause(~s_eq_empty, mk_eq(i, mk_len(t)));

View file

@ -828,6 +828,7 @@ br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) {
br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
zstring b;
rational r;
m_es.reset();
str().get_concat(a, m_es);
unsigned len = 0;
@ -867,6 +868,16 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
result = str().mk_length(z);
return BR_REWRITE1;
}
// len(extract(x, 0, z)) = min(z, len(x))
if (str().is_extract(a, x, y, z) &&
m_autil.is_numeral(y, r) && r.is_zero() &&
m_autil.is_numeral(z, r) && r >= 0) {
expr* len_x = str().mk_length(x);
result = m().mk_ite(m_autil.mk_le(len_x, z), len_x, z);
// expr* zero = m_autil.mk_int(0);
// result = m().mk_ite(m_autil.mk_le(z, zero), zero, result);
return BR_REWRITE_FULL;
}
#if 0
expr* s = nullptr, *offset = nullptr, *length = nullptr;
if (str().is_extract(a, s, offset, length)) {
@ -1209,6 +1220,11 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu
constantPos &= pos.is_unsigned();
constantLen &= len.is_unsigned();
if (constantPos && constantLen && len == 1) {
result = str().mk_at(a, b);
return BR_REWRITE1;
}
if (constantPos && constantLen && constantBase) {
unsigned _pos = pos.get_unsigned();
unsigned _len = len.get_unsigned();
@ -1245,6 +1261,15 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu
result = str().mk_substr(a1, m_autil.mk_add(b1, b), m_autil.mk_sub(c1, b));
return BR_REWRITE3;
}
rational r1, r2;
if (str().is_extract(a, a1, b1, c1) &&
m_autil.is_numeral(b1, r1) && r1.is_unsigned() &&
m_autil.is_numeral(c1, r2) && r2.is_unsigned() &&
constantPos && constantLen &&
r1 == 0 && r2 >= pos + len) {
result = str().mk_substr(a1, b, c);
return BR_REWRITE1;
}
if (str().is_extract(a, a1, b1, c1) &&
is_prefix(a1, b1, c1) && is_prefix(a, b, c)) {
@ -1539,9 +1564,17 @@ bool seq_rewriter::reduce_by_char(expr_ref& r, expr* ch, unsigned depth) {
*/
br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) {
zstring c;
rational r;
rational r, offset_r, len_r;
expr* offset, *a1, *len;
expr_ref_vector lens(m());
sort* sort_a = a->get_sort();
if (str().is_extract(a, a1, offset, len) &&
m_autil.is_numeral(offset, offset_r) && offset_r.is_zero() &&
m_autil.is_numeral(len, len_r) && m_autil.is_numeral(b, r) &&
r < len_r) {
result = str().mk_at(a1, b);
return BR_REWRITE1;
}
if (!get_lengths(b, lens, r)) {
return BR_FAILED;
}
@ -1716,6 +1749,10 @@ br_status seq_rewriter::mk_seq_last_index(expr* a, expr* b, expr_ref& result) {
result = m_autil.mk_numeral(rational(idx), true);
return BR_DONE;
}
if (a == b) {
result = m_autil.mk_int(0);
return BR_DONE;
}
return BR_FAILED;
}

View file

@ -39,6 +39,7 @@ Notes:
#include "ast/ast_util.h"
#include "ast/well_sorted.h"
#include "ast/for_each_expr.h"
#include "ast/array_peq.h"
namespace {
struct th_rewriter_cfg : public default_rewriter_cfg {
@ -75,6 +76,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
bool m_ignore_patterns_on_ground_qbody = true;
bool m_rewrite_patterns = true;
bool m_enable_der = true;
bool m_nested_der = false;
ast_manager & m() const { return m_b_rw.m(); }
@ -91,6 +93,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
m_ignore_patterns_on_ground_qbody = p.ignore_patterns_on_ground_qbody();
m_rewrite_patterns = p.rewrite_patterns();
m_enable_der = p.enable_der();
m_nested_der = _p.get_bool("nested_der", false);
}
void updt_params(params_ref const & p) {
@ -644,6 +647,10 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
else
st = pull_ite(result);
}
if (st == BR_FAILED && f->get_family_id() == null_family_id && is_partial_eq(f)) {
st = m_ar_rw.mk_app_core(f, num, args, result);
}
CTRACE("th_rewriter_step", st != BR_FAILED,
tout << f->get_name() << "\n";
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";
@ -838,8 +845,11 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
result = r;
}
if (der_change) {
if (der_change && !m_nested_der) {
th_rewriter rw(m());
params_ref p;
p.set_bool("nested_der", true);
rw.updt_params(p);
rw(result, r, p2);
if (m().proofs_enabled() && result.get() != r.get())
result_pr = m().mk_transitivity(result_pr, p2);

View file

@ -37,6 +37,7 @@ class bound_simplifier : public dependent_expr_simplifier {
unsynch_mpq_manager nm;
small_object_allocator m_alloc;
bound_propagator bp;
u_dependency_manager m_dep_manager;
dep_intervals m_interval;
ptr_vector<expr> m_var2expr;
unsigned_vector m_expr2var;
@ -105,7 +106,7 @@ public:
a(m),
m_rewriter(m),
bp(nm, m_alloc, p),
m_interval(m.limit()),
m_interval(m_dep_manager, m.limit()),
m_trail(m),
m_num_buffer(nm) {
updt_params(p);

View file

@ -74,6 +74,8 @@ public:
virtual bool inconsistent() = 0;
virtual model_reconstruction_trail& model_trail() = 0;
virtual void flatten_suffix() {}
virtual bool updated() = 0;
virtual void reset_updated() = 0;
trail_stack m_trail;
void push() {
@ -109,6 +111,9 @@ public:
virtual void add(dependent_expr const& j) { throw default_exception("unexpected addition"); }
virtual bool inconsistent() { return false; }
virtual model_reconstruction_trail& model_trail() { throw default_exception("unexpected access to model reconstruction"); }
virtual bool updated() { return false; }
virtual void reset_updated() {}
};
inline std::ostream& operator<<(std::ostream& out, dependent_expr_state& st) {
@ -147,7 +152,7 @@ protected:
index_set indices() { return index_set(*this); }
proof* mp(proof* a, proof* b) { return (a && b) ? m.mk_modus_ponens(a, b) : nullptr; }
proof* tr(proof* a, proof* b) { return m.mk_transitivity(a, b); }
public:
dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s), m_trail(s.m_trail) {}
virtual ~dependent_expr_simplifier() {}

View file

@ -243,8 +243,8 @@ namespace euf {
void solve_context_eqs::collect_nested_equalities(dependent_expr const& df, expr_mark& visited, dep_eq_vector& eqs) {
svector<std::tuple<bool,unsigned,expr*>> todo;
todo.push_back({ false, 0, df.fml()});
svector<std::tuple<bool,unsigned,expr*, unsigned>> todo;
todo.push_back({ false, 0, df.fml(), 0});
// even depth is conjunctive context, odd is disjunctive
// when alternating between conjunctive and disjunctive context, increment depth.
@ -255,37 +255,85 @@ namespace euf {
return (0 == depth % 2) ? depth : depth + 1;
};
while (!todo.empty()) {
auto [s, depth, f] = todo.back();
todo.pop_back();
for (unsigned i = 0; i < todo.size(); ++i) {
auto [s, depth, f, p] = todo[i];
if (visited.is_marked(f))
continue;
visited.mark(f, true);
if (s && m.is_and(f)) {
for (auto* arg : *to_app(f))
todo.push_back({ s, inc_or(depth), arg });
todo.push_back({ s, inc_or(depth), arg, i });
}
else if (!s && m.is_or(f)) {
for (auto* arg : *to_app(f))
todo.push_back({ s, inc_or(depth), arg });
todo.push_back({ s, inc_or(depth), arg, i });
}
if (!s && m.is_and(f)) {
for (auto* arg : *to_app(f))
todo.push_back({ s, inc_and(depth), arg });
todo.push_back({ s, inc_and(depth), arg, i });
}
else if (s && m.is_or(f)) {
for (auto* arg : *to_app(f))
todo.push_back({ s, inc_and(depth), arg });
todo.push_back({ s, inc_and(depth), arg, i });
}
else if (m.is_not(f, f))
todo.push_back({ !s, depth, f });
todo.push_back({ !s, depth, f, i });
else if (!s && 1 <= depth) {
unsigned sz = eqs.size();
for (extract_eq* ex : m_solve_eqs.m_extract_plugins) {
ex->set_allow_booleans(false);
ex->get_eqs(dependent_expr(m, f, nullptr, df.dep()), eqs);
ex->set_allow_booleans(true);
}
// prune eqs for solutions that are not safe in df.fml()
for (; sz < eqs.size(); ++sz) {
if (!is_safe_var(eqs[sz].var, i, df.fml(), todo)) {
eqs[sz] = eqs.back();
--sz;
eqs.pop_back();
}
}
}
}
}
bool solve_context_eqs::is_safe_var(expr* x, unsigned i, expr* f, svector<std::tuple<bool,unsigned,expr*,unsigned>> const& todo) {
m_contains_v.reset();
m_todo.push_back(f);
mark_occurs(m_todo, x, m_contains_v);
SASSERT(m_todo.empty());
auto is_parent = [&](unsigned p, unsigned i) {
while (p != i && i != 0) {
auto [_s,_depth, _f, _p] = todo[i];
i = _p;
}
return p == i;
};
// retrieve oldest parent of i within the same alternation of and
unsigned pi = i;
auto [_s, _depth, _f, _p] = todo[i];
while (pi != 0) {
auto [s, depth, f, p] = todo[pi];
if (depth != _depth)
break;
pi = p;
}
// determine if j and j have common conjunctive parent
// for every j in todo.
for (unsigned j = 0; j < todo.size(); ++j) {
auto [s, depth, f, p] = todo[j];
if (i == j || !m_contains_v.is_marked(f))
continue;
if (is_parent(j, i)) // j is a parent if i
continue;
if (is_parent(pi, j)) // pi is a parent of j
continue;
return false;
}
return true;
}
}

View file

@ -45,7 +45,9 @@ namespace euf {
bool contains_conjunctively(expr* f, bool sign, expr* e, signed_expressions& conjuncts);
bool is_conjunction(bool sign, expr* f) const;
void collect_nested_equalities(dependent_expr const& f, expr_mark& visited, dep_eq_vector& eqs);
void collect_nested_equalities(dependent_expr const& f, expr_mark& visited, dep_eq_vector& eqs);
bool is_safe_var(expr* x, unsigned i, expr* f, svector<std::tuple<bool,unsigned,expr*,unsigned>> const& todo);
public:

View file

@ -212,12 +212,13 @@ namespace euf {
auto [f, p, d] = m_fmls[i]();
auto [new_f, new_dep] = rp->replace_with_dep(f);
proof_ref new_pr(m);
m_rewriter(new_f, new_f, new_pr);
if (new_f == f)
expr_ref tmp(m);
m_rewriter(new_f, tmp, new_pr);
if (tmp == f)
continue;
new_dep = m.mk_join(d, new_dep);
old_fmls.push_back(m_fmls[i]);
m_fmls.update(i, dependent_expr(m, new_f, mp(p, new_pr), new_dep));
m_fmls.update(i, dependent_expr(m, tmp, mp(p, new_pr), new_dep));
}
}

View file

@ -51,6 +51,10 @@ class then_simplifier : public dependent_expr_simplifier {
}
};
protected:
bool m_bail_on_no_change = false;
public:
then_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
@ -72,9 +76,17 @@ public:
break;
s->reset_statistics();
collect_stats _cs(*s);
s->reduce();
m_fmls.flatten_suffix();
m_fmls.reset_updated();
try {
s->reduce();
m_fmls.flatten_suffix();
}
catch (rewriter_exception &) {
break;
}
TRACE("simplifier", tout << s->name() << "\n" << m_fmls);
if (m_bail_on_no_change && !m_fmls.updated())
break;
}
}
@ -108,3 +120,14 @@ public:
s->pop(n);
}
};
class if_change_simplifier : public then_simplifier {
public:
if_change_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
then_simplifier(m, p, fmls) {
m_bail_on_no_change = true;
}
char const* name() const override { return "if-change-then"; }
};

View file

@ -42,6 +42,7 @@ Notes:
#include "ast/for_each_expr.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/rewriter/recfun_replace.h"
#include "ast/polymorphism_util.h"
#include "model/model_evaluator.h"
#include "model/model_smt2_pp.h"
#include "model/model_v2_pp.h"
@ -223,12 +224,48 @@ bool func_decls::check_signature(ast_manager& m, func_decl* f, unsigned arity, s
return true;
}
func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * domain, sort * range) const {
bool func_decls::check_poly_signature(ast_manager& m, func_decl* f, unsigned arity, sort* const* domain, sort* range, func_decl*& g) {
polymorphism::substitution sub(m);
arith_util au(m);
sort_ref range_ref(range, m);
if (range != nullptr && !sub.match(f->get_range(), range))
return false;
if (f->get_arity() != arity)
return false;
for (unsigned i = 0; i < arity; i++)
if (!sub.match(f->get_domain(i), domain[i]))
return false;
if (!range)
range_ref = sub(f->get_range());
recfun::util u(m);
auto& p = u.get_plugin();
if (!u.has_def(f)) {
g = m.instantiate_polymorphic(f, arity, domain, range_ref);
return true;
}
// this is an instantiation of a recursive polymorphic function.
// create a self-contained polymorphic definition for the instantiation.
auto def = u.get_def(f);
auto promise_def = p.mk_def(f->get_name(), arity, domain, range_ref, false);
recfun_replace replace(m);
expr_ref tt = sub(def.get_rhs());
p.set_definition(replace, promise_def, def.is_macro(), def.get_vars().size(), def.get_vars().data(), tt);
g = promise_def.get_def()->get_decl();
insert(m, g);
return true;
}
func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * domain, sort * range) {
bool coerced = false;
func_decl* g = nullptr;
if (!more_than_one()) {
func_decl* f = first();
if (check_signature(m, f, arity, domain, range, coerced))
return f;
return f;
if (check_poly_signature(m, f, arity, domain, range, g))
return g;
return nullptr;
}
func_decl_set * fs = UNTAG(func_decl_set *, m_decls);
@ -241,10 +278,15 @@ func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * doma
return f;
}
}
return best_f;
if (best_f != nullptr)
return best_f;
for (func_decl* f : *fs)
if (check_poly_signature(m, f, arity, domain, range, g))
return g;
return nullptr;
}
func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const {
func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) {
if (!more_than_one())
first();
ptr_buffer<sort> sorts;
@ -376,12 +418,13 @@ void cmd_context::erase_macro(symbol const& s) {
decls.erase_last(m());
}
bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr*& t) const {
bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr_ref& t) {
macro_decls decls;
if (!m_macros.find(s, decls))
return false;
for (macro_decl const& d : decls) {
if (d.m_domain.size() != n) continue;
if (d.m_domain.size() != n)
continue;
bool eq = true;
coerced_args.reset();
for (unsigned i = 0; eq && i < n; ++i) {
@ -406,6 +449,26 @@ bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, exp
return true;
}
}
for (macro_decl const& d : decls) {
if (d.m_domain.size() != n)
continue;
polymorphism::substitution sub(m());
bool eq = true;
for (unsigned i = 0; eq && i < n; ++i) {
if (!sub.match(d.m_domain[i], args[i]->get_sort()))
eq = false;
}
if (eq) {
t = d.m_body;
t = sub(t);
verbose_stream() << "macro " << t << "\n";
ptr_buffer<sort> domain;
for (unsigned i = 0; i < n; ++i)
domain.push_back(args[i]->get_sort());
insert_macro(s, n, domain.data(), t);
return true;
}
}
return false;
}
@ -939,18 +1002,16 @@ void cmd_context::insert(cmd * c) {
void cmd_context::insert_user_tactic(symbol const & s, sexpr * d) {
sm().inc_ref(d);
sexpr * old_d;
if (m_user_tactic_decls.find(s, old_d)) {
sm().dec_ref(old_d);
}
if (m_user_tactic_decls.find(s, old_d))
sm().dec_ref(old_d);
m_user_tactic_decls.insert(s, d);
}
void cmd_context::insert(symbol const & s, object_ref * r) {
r->inc_ref(*this);
object_ref * old_r = nullptr;
if (m_object_refs.find(s, old_r)) {
old_r->dec_ref(*this);
}
if (m_object_refs.find(s, old_r))
old_r->dec_ref(*this);
m_object_refs.insert(s, r);
}
@ -1054,16 +1115,17 @@ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family
}
func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices,
unsigned arity, sort * const * domain, sort * range) const {
unsigned arity, sort * const * domain, sort * range) {
if (domain && contains_macro(s, arity, domain))
throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s);
func_decl * f = nullptr;
func_decls fs;
if (num_indices == 0 && m_func_decls.find(s, fs))
if (num_indices == 0 && m_func_decls.contains(s)) {
auto& fs = m_func_decls.find(s);
f = fs.find(m(), arity, domain, range);
if (f)
}
if (f)
return f;
builtin_decl d;
if ((arity == 0 || domain) && m_builtin_decls.find(s, d)) {
@ -1089,11 +1151,12 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices,
throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s);
return f;
}
if (num_indices > 0 && m_func_decls.find(s, fs))
if (num_indices > 0 && m_func_decls.contains(s)) {
auto& fs = m_func_decls.find(s);
f = fs.find(m(), arity, domain, range);
if (f)
}
if (f)
return f;
throw cmd_exception("invalid function declaration reference, unknown indexed function ", s);
}
@ -1125,7 +1188,7 @@ object_ref * cmd_context::find_object_ref(symbol const & s) const {
#define CHECK_SORT(T) if (well_sorted_check_enabled()) m().check_sorts_core(T)
void cmd_context::mk_const(symbol const & s, expr_ref & result) const {
void cmd_context::mk_const(symbol const & s, expr_ref & result) {
mk_app(s, 0, nullptr, 0, nullptr, nullptr, result);
}
@ -1153,9 +1216,10 @@ bool cmd_context::try_mk_builtin_app(symbol const & s, unsigned num_args, expr *
bool cmd_context::try_mk_declared_app(symbol const & s, unsigned num_args, expr * const * args,
unsigned num_indices, parameter const * indices, sort * range,
func_decls& fs, expr_ref & result) const {
if (!m_func_decls.find(s, fs))
expr_ref & result) {
if (!m_func_decls.contains(s))
return false;
func_decls& fs = m_func_decls.find(s);
if (num_args == 0 && !range) {
if (fs.more_than_one())
@ -1180,8 +1244,8 @@ bool cmd_context::try_mk_declared_app(symbol const & s, unsigned num_args, expr
bool cmd_context::try_mk_macro_app(symbol const & s, unsigned num_args, expr * const * args,
unsigned num_indices, parameter const * indices, sort * range,
expr_ref & result) const {
expr* _t;
expr_ref & result) {
expr_ref _t(m());
expr_ref_vector coerced_args(m());
if (macros_find(s, num_args, args, coerced_args, _t)) {
TRACE("macro_bug", tout << "well_sorted_check_enabled(): " << well_sorted_check_enabled() << "\n";
@ -1256,19 +1320,21 @@ bool cmd_context::try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * c
void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args,
unsigned num_indices, parameter const * indices, sort * range,
expr_ref & result) const {
expr_ref & result) {
func_decls fs;
if (try_mk_macro_app(s, num_args, args, num_indices, indices, range, result))
return;
if (try_mk_declared_app(s, num_args, args, num_indices, indices, range, fs, result))
return;
if (try_mk_declared_app(s, num_args, args, num_indices, indices, range, result))
return;
if (try_mk_builtin_app(s, num_args, args, num_indices, indices, range, result))
return;
if (!range && try_mk_pdecl_app(s, num_args, args, num_indices, indices, result))
return;
func_decls fs;
m_func_decls.find(s, fs);
std::ostringstream buffer;
buffer << "unknown constant " << s;
if (num_args > 0) {
@ -1612,6 +1678,8 @@ void cmd_context::restore_assertions(unsigned old_sz) {
SASSERT(m_assertions.empty());
return;
}
if (m_assertions.empty())
return;
if (old_sz == m_assertions.size())
return;
SASSERT(old_sz < m_assertions.size());
@ -2230,6 +2298,8 @@ vector<std::pair<expr*,expr*>> cmd_context::tracked_assertions() {
}
void cmd_context::reset_tracked_assertions() {
for (expr* a : m_assertion_names)
m().dec_ref(a);
m_assertion_names.reset();
for (expr* a : m_assertions)
m().dec_ref(a);

View file

@ -58,11 +58,12 @@ public:
bool clash(func_decl * f) const;
bool empty() const { return m_decls == nullptr; }
func_decl * first() const;
func_decl * find(ast_manager & m, unsigned arity, sort * const * domain, sort * range) const;
func_decl * find(ast_manager & m, unsigned arity, expr * const * args, sort * range) const;
func_decl * find(ast_manager & m, unsigned arity, sort * const * domain, sort * range);
func_decl * find(ast_manager & m, unsigned arity, expr * const * args, sort * range);
unsigned get_num_entries() const;
func_decl * get_entry(unsigned inx);
bool check_signature(ast_manager& m, func_decl* f, unsigned arityh, sort * const* domain, sort * range, bool& coerced) const;
bool check_poly_signature(ast_manager& m, func_decl* f, unsigned arity, sort* const* domain, sort* range, func_decl*& g);
};
struct macro_decl {
@ -355,7 +356,7 @@ protected:
bool contains_macro(symbol const& s, unsigned arity, sort *const* domain) const;
void insert_macro(symbol const& s, unsigned arity, sort*const* domain, expr* t);
void erase_macro(symbol const& s);
bool macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr*& t) const;
bool macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr_ref& t);
recfun::decl::plugin& get_recfun_plugin();
@ -449,22 +450,22 @@ public:
void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e);
func_decl * find_func_decl(symbol const & s) const;
func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices,
unsigned arity, sort * const * domain, sort * range) const;
unsigned arity, sort * const * domain, sort * range);
recfun::promise_def decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range);
psort_decl * find_psort_decl(symbol const & s) const;
cmd * find_cmd(symbol const & s) const;
sexpr * find_user_tactic(symbol const & s) const;
object_ref * find_object_ref(symbol const & s) const;
void mk_const(symbol const & s, expr_ref & result) const;
void mk_const(symbol const & s, expr_ref & result);
void mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range,
expr_ref & r) const;
expr_ref & r);
bool try_mk_macro_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range,
expr_ref & r) const;
expr_ref & r);
bool try_mk_builtin_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range,
expr_ref & r) const;
bool try_mk_declared_app(symbol const & s, unsigned num_args, expr * const * args,
unsigned num_indices, parameter const * indices, sort * range,
func_decls& fs, expr_ref & result) const;
expr_ref & result);
bool try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, expr_ref & r) const;
void erase_cmd(symbol const & s);
void erase_func_decl(symbol const & s);

View file

@ -16,6 +16,7 @@ Notes:
--*/
#include<iomanip>
#include "ast/ast.h"
#include "cmd_context/cmd_context.h"
#include "cmd_context/cmd_util.h"
#include "ast/rewriter/rewriter.h"
@ -34,7 +35,9 @@ Notes:
#include "qe/qe_mbp.h"
#include "qe/qe_mbi.h"
#include "qe/mbp/mbp_term_graph.h"
#include "qe/mbp/mbp_qel.h"
#include "qe/lite/qe_lite_tactic.h"
#include "qe/lite/qel.h"
BINARY_SYM_CMD(get_quantifier_body_cmd,
"dbg-get-qbody",
@ -369,7 +372,7 @@ public:
}
vars.push_back(to_app(v));
}
qe::mbproj mbp(m);
qe::mbproj mbp(m, gparams::get_module("smt"));
expr_ref fml(m_fml, m);
mbp.spacer(vars, *mdl.get(), fml);
ctx.regular_stream() << fml << "\n";
@ -572,8 +575,192 @@ public:
};
class mbp_qel_cmd : public cmd {
unsigned m_arg_index;
ptr_vector<expr> m_lits;
ptr_vector<expr> m_vars;
void install_dbg_cmds(cmd_context & ctx) {
public:
mbp_qel_cmd() : cmd("mbp-qel"){};
char const *get_usage() const override { return "(exprs) (vars)"; }
char const *get_descr(cmd_context &ctx) const override {
return "Model based projection using e-graphs";
}
unsigned get_arity() const override { return 2; }
cmd_arg_kind next_arg_kind(cmd_context &ctx) const override {
return CPK_EXPR_LIST;
}
void set_next_arg(cmd_context &ctx, unsigned num,
expr *const *args) override {
if (m_arg_index == 0) {
m_lits.append(num, args);
m_arg_index = 1;
}
else { m_vars.append(num, args); }
}
void prepare(cmd_context &ctx) override {
m_arg_index = 0;
m_lits.reset();
m_vars.reset();
}
void execute(cmd_context &ctx) override {
ast_manager &m = ctx.m();
app_ref_vector vars(m);
expr_ref fml(m);
expr_ref_vector lits(m);
for (expr *v : m_vars) vars.push_back(to_app(v));
for (expr *e : m_lits) lits.push_back(e);
fml = mk_and(lits);
solver_factory &sf = ctx.get_solver_factory();
params_ref pa;
solver_ref s = sf(m, pa, false, true, true, symbol::null);
s->assert_expr(fml);
lbool r = s->check_sat();
if (r != l_true) return;
model_ref mdl;
s->get_model(mdl);
mbp::mbp_qel mbptg(m, pa);
mbptg(vars, fml, *mdl.get());
ctx.regular_stream() << "------------------------------ " << std::endl;
ctx.regular_stream() << "Orig tg: " << mk_and(lits) << std::endl;
ctx.regular_stream() << "To elim: ";
for (expr *v : m_vars) {
ctx.regular_stream() << to_app(v)->get_decl()->get_name() << " ";
}
ctx.regular_stream() << std::endl;
ctx.regular_stream() << "output " << fml << std::endl;
}
};
class qel_cmd : public cmd {
unsigned m_arg_index;
ptr_vector<expr> m_lits;
ptr_vector<func_decl> m_vars;
public:
qel_cmd() : cmd("qel"){};
char const *get_usage() const override { return "(lits) (vars)"; }
char const *get_descr(cmd_context &ctx) const override {
return "QE lite over e-graphs";
}
unsigned get_arity() const override { return 2; }
cmd_arg_kind next_arg_kind(cmd_context &ctx) const override {
if (m_arg_index == 0) return CPK_EXPR_LIST;
return CPK_FUNC_DECL_LIST;
}
void set_next_arg(cmd_context &ctx, unsigned num,
expr *const *args) override {
m_lits.append(num, args);
m_arg_index = 1;
}
void set_next_arg(cmd_context &ctx, unsigned num,
func_decl *const *ts) override {
m_vars.append(num, ts);
}
void prepare(cmd_context &ctx) override {
m_arg_index = 0;
m_lits.reset();
m_vars.reset();
}
void execute(cmd_context &ctx) override {
ast_manager &m = ctx.m();
func_decl_ref_vector vars(m);
app_ref_vector vars_apps(m);
expr_ref_vector lits(m);
ctx.regular_stream() << "------------------------------ " << std::endl;
for (func_decl *v : m_vars) {
vars.push_back(v);
vars_apps.push_back(m.mk_const(v));
}
for (expr *e : m_lits) lits.push_back(e);
expr_ref fml(m.mk_and(lits), m);
ctx.regular_stream() << "[tg] Before: " << fml << std::endl
<< "[tg] Vars: ";
for (app *a : vars_apps) ctx.regular_stream() << app_ref(a, m) << " ";
ctx.regular_stream() << std::endl;
params_ref pa;
// the following is the same code as in qe_mbp in spacer
qel qe(m, pa);
qe(vars_apps, fml);
ctx.regular_stream() << "[tg] After: " << fml << std::endl
<< "[tg] Vars: ";
for (app *a : vars_apps) ctx.regular_stream() << app_ref(a, m) << " ";
ctx.regular_stream() << std::endl;
}
};
class qe_lite_cmd : public cmd {
unsigned m_arg_index;
ptr_vector<expr> m_lits;
ptr_vector<func_decl> m_vars;
public:
qe_lite_cmd() : cmd("qe-lite"){};
char const *get_usage() const override { return "(lits) (vars)"; }
char const *get_descr(cmd_context &ctx) const override {
return "QE lite over e-graphs";
}
unsigned get_arity() const override { return 2; }
cmd_arg_kind next_arg_kind(cmd_context &ctx) const override {
if (m_arg_index == 0) return CPK_EXPR_LIST;
return CPK_FUNC_DECL_LIST;
}
void set_next_arg(cmd_context &ctx, unsigned num,
expr *const *args) override {
m_lits.append(num, args);
m_arg_index = 1;
}
void set_next_arg(cmd_context &ctx, unsigned num,
func_decl *const *ts) override {
m_vars.append(num, ts);
}
void prepare(cmd_context &ctx) override {
m_arg_index = 0;
m_lits.reset();
m_vars.reset();
}
void execute(cmd_context &ctx) override {
ast_manager &m = ctx.m();
func_decl_ref_vector vars(m);
app_ref_vector vars_apps(m);
expr_ref_vector lits(m);
ctx.regular_stream() << "------------------------------ " << std::endl;
for (func_decl *v : m_vars) {
vars.push_back(v);
vars_apps.push_back(m.mk_const(v));
}
for (expr *e : m_lits) lits.push_back(e);
expr_ref fml(m.mk_and(lits), m);
ctx.regular_stream() << "[der] Before: " << fml << std::endl
<< "[der] Vars: ";
for (app *a : vars_apps) ctx.regular_stream() << app_ref(a, m) << " ";
ctx.regular_stream() << std::endl;
params_ref pa;
// the following is the same code as in qe_mbp in spacer
qe_lite qe(m, pa, false);
qe(vars_apps, fml);
ctx.regular_stream() << "[der] After: " << fml << std::endl
<< "[der] Vars: ";
for (app *a : vars_apps) ctx.regular_stream() << app_ref(a, m) << " ";
ctx.regular_stream() << std::endl;
}
};
void install_dbg_cmds(cmd_context &ctx) {
ctx.insert(alloc(print_dimacs_cmd));
ctx.insert(alloc(get_quantifier_body_cmd));
ctx.insert(alloc(set_cmd));
@ -598,7 +785,10 @@ void install_dbg_cmds(cmd_context & ctx) {
ctx.insert(alloc(set_next_id));
ctx.insert(alloc(get_interpolant_cmd));
ctx.insert(alloc(mbp_cmd));
ctx.insert(alloc(mbp_qel_cmd));
ctx.insert(alloc(mbi_cmd));
ctx.insert(alloc(euf_project_cmd));
ctx.insert(alloc(eufi_cmd));
ctx.insert(alloc(qel_cmd));
ctx.insert(alloc(qe_lite_cmd));
}

View file

@ -126,7 +126,7 @@ public:
if (m_empty)
return;
if (hint && !is_rup(hint) && m_checker.check(hint)) {
if (hint && !is_rup(hint)) {
auto clause1 = m_checker.clause(hint);
if (clause1.size() != clause.size()) {
mk_clause(clause1);
@ -239,8 +239,10 @@ public:
class proof_cmds_imp : public proof_cmds {
cmd_context& ctx;
ast_manager& m;
arith_util m_arith;
expr_ref_vector m_lits;
app_ref m_proof_hint;
unsigned_vector m_deps;
bool m_check = true;
bool m_save = false;
bool m_trim = false;
@ -266,11 +268,24 @@ class proof_cmds_imp : public proof_cmds {
m_del = m.mk_app(symbol("del"), 0, nullptr, m.mk_proof_sort());
return m_del;
}
bool is_dep(expr* e) {
return m.is_proof(e) && symbol("deps") == to_app(e)->get_name();
}
void get_deps(expr* e) {
rational n;
bool is_int = false;
for (expr* arg : *to_app(e))
if (m_arith.is_numeral(arg, n, is_int) && n.is_unsigned())
m_deps.push_back(n.get_unsigned());
}
public:
proof_cmds_imp(cmd_context& ctx):
ctx(ctx),
m(ctx.m()),
m(ctx.m()),
m_arith(m),
m_lits(m),
m_proof_hint(m),
m_assumption(m),
@ -280,7 +295,9 @@ public:
void add_literal(expr* e) override {
if (m.is_proof(e)) {
if (!m_proof_hint)
if (is_dep(e))
get_deps(e);
else if (!m_proof_hint)
m_proof_hint = to_app(e);
}
else if (!m.is_bool(e))
@ -297,9 +314,10 @@ public:
if (m_trim)
trim().assume(m_lits);
if (m_on_clause_eh)
m_on_clause_eh(m_on_clause_ctx, assumption(), m_lits.size(), m_lits.data());
m_on_clause_eh(m_on_clause_ctx, assumption(), m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data());
m_lits.reset();
m_proof_hint.reset();
m_deps.reset();
}
void end_infer() override {
@ -310,9 +328,10 @@ public:
if (m_trim)
trim().infer(m_lits, m_proof_hint);
if (m_on_clause_eh)
m_on_clause_eh(m_on_clause_ctx, m_proof_hint, m_lits.size(), m_lits.data());
m_on_clause_eh(m_on_clause_ctx, m_proof_hint, m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data());
m_lits.reset();
m_proof_hint.reset();
m_deps.reset();
}
void end_deleted() override {
@ -323,16 +342,17 @@ public:
if (m_trim)
trim().del(m_lits);
if (m_on_clause_eh)
m_on_clause_eh(m_on_clause_ctx, del(), m_lits.size(), m_lits.data());
m_on_clause_eh(m_on_clause_ctx, del(), m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data());
m_lits.reset();
m_proof_hint.reset();
m_deps.reset();
}
void updt_params(params_ref const& p) override {
solver_params sp(p);
m_check = sp.proof_check();
m_save = sp.proof_save();
m_trim = sp.proof_trim();
m_check = sp.proof_check() && !m_trim && !m_save && !m_on_clause_eh;
if (m_trim)
trim().updt_params(p);
}
@ -340,6 +360,8 @@ public:
void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause_eh) override {
m_on_clause_ctx = ctx;
m_on_clause_eh = on_clause_eh;
if (m_on_clause_eh)
m_check = false;
}
};

View file

@ -838,14 +838,18 @@ struct pdecl_manager::app_sort_info : public pdecl_manager::sort_info {
}
format * pp(smt2_pp_environment& env, pdecl_manager const & m) const override {
symbol s = m_decl->get_name();
std::string name = s.str();
if (is_smt2_quoted_symbol(s))
name = mk_smt2_quoted_symbol(s);
if (m_args.empty()) {
return mk_string(m.m(), m_decl->get_name().str());
return mk_string(m.m(), name);
}
else {
ptr_buffer<format> b;
for (auto arg : m_args)
b.push_back(m.pp(env, arg));
return mk_seq1(m.m(), b.begin(), b.end(), f2f(), m_decl->get_name().str());
return mk_seq1(m.m(), b.begin(), b.end(), f2f(), name);
}
}
};
@ -874,12 +878,17 @@ struct pdecl_manager::indexed_sort_info : public pdecl_manager::sort_info {
}
format * pp(smt2_pp_environment& env, pdecl_manager const & m) const override {
symbol s = m_decl->get_name();
std::string name = s.str();
if (is_smt2_quoted_symbol(s))
name = mk_smt2_quoted_symbol(s);
if (m_indices.empty()) {
return mk_string(m.m(), m_decl->get_name().str());
return mk_string(m.m(), name);
}
else {
ptr_buffer<format> b;
b.push_back(mk_string(m.m(), m_decl->get_name().str()));
b.push_back(mk_string(m.m(), name));
for (auto idx : m_indices)
b.push_back(mk_unsigned(m.m(), idx));
return mk_seq1(m.m(), b.begin(), b.end(), f2f(), "_");

View file

@ -62,6 +62,10 @@ namespace dd {
init_nodes(level2var);
}
void pdd_manager::set_max_num_nodes(unsigned n) {
m_max_num_nodes = n + m_level2var.size();
}
void pdd_manager::init_nodes(unsigned_vector const& l2v) {
// add dummy nodes for operations, and 0, 1 pdds.
for (unsigned i = 0; i < pdd_no_op; ++i) {
@ -1614,7 +1618,8 @@ namespace dd {
for (unsigned i = m_nodes.size(); i-- > pdd_no_op; ) {
if (!reachable[i]) {
if (is_val(i)) {
if (m_freeze_value == val(i)) continue;
if (m_freeze_value == val(i))
continue;
m_free_values.push_back(m_mpq_table.find(val(i)).m_value_index);
m_mpq_table.remove(val(i));
}
@ -1629,20 +1634,17 @@ namespace dd {
ptr_vector<op_entry> to_delete, to_keep;
for (auto* e : m_op_cache) {
if (e->m_result != null_pdd) {
to_delete.push_back(e);
}
else {
to_keep.push_back(e);
}
if (e->m_result != null_pdd)
to_delete.push_back(e);
else
to_keep.push_back(e);
}
m_op_cache.reset();
for (op_entry* e : to_delete) {
for (op_entry* e : to_delete)
m_alloc.deallocate(sizeof(*e), e);
}
for (op_entry* e : to_keep) {
m_op_cache.insert(e);
}
for (op_entry* e : to_keep)
m_op_cache.insert(e);
m_factor_cache.reset();

View file

@ -327,8 +327,9 @@ namespace dd {
semantics get_semantics() const { return m_semantics; }
void reset(unsigned_vector const& level2var);
void set_max_num_nodes(unsigned n) { m_max_num_nodes = n; }
void set_max_num_nodes(unsigned n);
unsigned_vector const& get_level2var() const { return m_level2var; }
unsigned num_nodes() const { return m_nodes.size() - m_free_nodes.size(); }
pdd mk_var(unsigned i);
pdd mk_val(rational const& r);

View file

@ -75,7 +75,7 @@ namespace dd {
}
}
catch (pdd_manager::mem_out) {
IF_VERBOSE(2, verbose_stream() << "simplifier memout\n");
IF_VERBOSE(1, verbose_stream() << "simplifier memout\n");
// done reduce
DEBUG_CODE(s.invariant(););
}
@ -89,12 +89,13 @@ namespace dd {
bool simplifier::simplify_linear_step(bool binary) {
TRACE("dd.solver", tout << "binary " << binary << "\n";);
IF_VERBOSE(2, verbose_stream() << "binary " << binary << "\n");
IF_VERBOSE(3, verbose_stream() << "binary " << binary << "\n");
equation_vector linear;
for (equation* e : s.m_to_simplify) {
pdd p = e->poly();
if (binary) {
if (p.is_binary()) linear.push_back(e);
if (p.is_binary())
linear.push_back(e);
}
else if (p.is_linear()) {
linear.push_back(e);
@ -112,29 +113,33 @@ namespace dd {
use_list_t use_list = get_use_list();
compare_top_var ctv;
std::stable_sort(linear.begin(), linear.end(), ctv);
equation_vector trivial;
struct trivial {
solver& s;
equation_vector elems;
trivial(solver& s) : s(s) {}
~trivial () {
for (auto* e : elems)
s.del_equation(e);
}
};
trivial trivial(s);
unsigned j = 0;
bool has_conflict = false;
for (equation* src : linear) {
if (has_conflict) {
break;
}
if (s.is_trivial(*src)) {
continue;
}
if (has_conflict)
break;
if (s.is_trivial(*src))
continue;
unsigned v = src->poly().var();
equation_vector const& uses = use_list[v];
TRACE("dd.solver",
s.display(tout << "uses of: ", *src) << "\n";
for (equation* e : uses) {
s.display(tout, *e) << "\n";
});
for (equation* e : uses) s.display(tout, *e) << "\n";);
bool changed_leading_term;
bool all_reduced = true;
for (equation* dst : uses) {
if (src == dst || s.is_trivial(*dst)) {
continue;
}
if (src == dst || s.is_trivial(*dst))
continue;
pdd q = dst->poly();
if (!src->poly().is_binary() && !q.is_linear()) {
all_reduced = false;
@ -142,9 +147,8 @@ namespace dd {
}
remove_from_use(dst, use_list, v);
s.simplify_using(*dst, *src, changed_leading_term);
if (s.is_trivial(*dst)) {
trivial.push_back(dst);
}
if (s.is_trivial(*dst))
trivial.elems.push_back(dst);
else if (s.is_conflict(dst)) {
s.pop_equation(dst);
s.set_conflict(dst);
@ -158,9 +162,8 @@ namespace dd {
// SASSERT(!dst->poly().free_vars().contains(v));
add_to_use(dst, use_list);
}
if (all_reduced) {
linear[j++] = src;
}
if (all_reduced)
linear[j++] = src;
}
if (!has_conflict) {
linear.shrink(j);
@ -169,9 +172,7 @@ namespace dd {
s.push_equation(solver::solved, src);
}
}
for (equation* e : trivial) {
s.del_equation(e);
}
DEBUG_CODE(s.invariant(););
return j > 0 || has_conflict;
}
@ -184,11 +185,12 @@ namespace dd {
*/
bool simplifier::simplify_cc_step() {
TRACE("dd.solver", tout << "cc\n";);
IF_VERBOSE(2, verbose_stream() << "cc\n");
IF_VERBOSE(3, verbose_stream() << "cc\n");
u_map<equation*> los;
bool reduced = false;
unsigned j = 0;
for (equation* eq1 : s.m_to_simplify) {
solver::scoped_update sc(s.m_to_simplify);
for (; sc.i < sc.sz; ++sc.i) {
auto* eq1 = sc.get();
SASSERT(eq1->state() == solver::to_simplify);
pdd p = eq1->poly();
equation* eq2 = los.insert_if_not_there(p.lo().index(), eq1);
@ -201,14 +203,11 @@ namespace dd {
s.retire(eq1);
continue;
}
else if (s.check_conflict(*eq1)) {
continue;
}
else if (s.check_conflict(*eq1))
continue;
}
s.m_to_simplify[j] = eq1;
eq1->set_index(j++);
sc.nextj();
}
s.m_to_simplify.shrink(j);
return reduced;
}
@ -217,7 +216,7 @@ namespace dd {
*/
bool simplifier::simplify_leaf_step() {
TRACE("dd.solver", tout << "leaf\n";);
IF_VERBOSE(2, verbose_stream() << "leaf\n");
IF_VERBOSE(3, verbose_stream() << "leaf\n");
use_list_t use_list = get_use_list();
equation_vector leaves;
for (unsigned i = 0; i < s.m_to_simplify.size(); ++i) {
@ -225,15 +224,12 @@ namespace dd {
pdd p = e->poly();
if (p.is_val())
continue;
if (!p.hi().is_val()) {
continue;
}
if (!p.hi().is_val())
continue;
leaves.reset();
for (equation* e2 : use_list[p.var()]) {
if (e != e2 && e2->poly().var_is_leaf(p.var())) {
leaves.push_back(e2);
}
}
for (equation* e2 : use_list[p.var()])
if (e != e2 && e2->poly().var_is_leaf(p.var()))
leaves.push_back(e2);
for (equation* e2 : leaves) {
bool changed_leading_term;
remove_from_use(e2, use_list);
@ -262,24 +258,21 @@ namespace dd {
*/
bool simplifier::simplify_elim_pure_step() {
TRACE("dd.solver", tout << "pure\n";);
IF_VERBOSE(2, verbose_stream() << "pure\n");
use_list_t use_list = get_use_list();
unsigned j = 0;
for (equation* e : s.m_to_simplify) {
IF_VERBOSE(3, verbose_stream() << "pure\n");
use_list_t use_list = get_use_list();
solver::scoped_update sc(s.m_to_simplify);
bool has_solved = false;
for (; sc.i < sc.sz; ++sc.i) {
equation* e = sc.get();
pdd p = e->poly();
if (!p.is_val() && p.hi().is_val() && use_list[p.var()].size() == 1) {
s.push_equation(solver::solved, e);
has_solved = true;
}
else {
s.m_to_simplify[j] = e;
e->set_index(j++);
}
else
sc.nextj();
}
if (j != s.m_to_simplify.size()) {
s.m_to_simplify.shrink(j);
return true;
}
return false;
return has_solved;
}
/**
@ -288,63 +281,59 @@ namespace dd {
*/
bool simplifier::simplify_elim_dual_step() {
use_list_t use_list = get_use_list();
unsigned j = 0;
bool reduced = false;
for (unsigned i = 0; i < s.m_to_simplify.size(); ++i) {
equation* e = s.m_to_simplify[i];
pdd p = e->poly();
// check that e is linear in top variable.
if (e->state() != solver::to_simplify) {
reduced = true;
}
else if (!s.done() && !s.is_trivial(*e) && p.hi().is_val() && use_list[p.var()].size() == 2) {
for (equation* e2 : use_list[p.var()]) {
if (e2 == e) continue;
bool changed_leading_term;
remove_from_use(e2, use_list);
s.simplify_using(*e2, *e, changed_leading_term);
if (s.is_conflict(e2)) {
s.pop_equation(e2);
s.set_conflict(e2);
}
// when e2 is trivial, leading term is changed
SASSERT(!s.is_trivial(*e2) || changed_leading_term);
if (changed_leading_term) {
s.pop_equation(e2);
s.push_equation(solver::to_simplify, e2);
}
add_to_use(e2, use_list);
break;
{
solver::scoped_update sc(s.m_to_simplify);
for (; sc.i < sc.sz; ++sc.i) {
equation* e = sc.get();
pdd p = e->poly();
// check that e is linear in top variable.
if (e->state() != solver::to_simplify) {
reduced = true;
}
reduced = true;
s.push_equation(solver::solved, e);
}
else {
s.m_to_simplify[j] = e;
e->set_index(j++);
else if (!s.done() && !s.is_trivial(*e) && p.hi().is_val() && use_list[p.var()].size() == 2) {
for (equation* e2 : use_list[p.var()]) {
if (e2 == e)
continue;
bool changed_leading_term;
remove_from_use(e2, use_list);
s.simplify_using(*e2, *e, changed_leading_term);
if (s.is_conflict(e2)) {
s.pop_equation(e2);
s.set_conflict(e2);
}
// when e2 is trivial, leading term is changed
SASSERT(!s.is_trivial(*e2) || changed_leading_term);
if (changed_leading_term) {
s.pop_equation(e2);
s.push_equation(solver::to_simplify, e2);
}
add_to_use(e2, use_list);
break;
}
reduced = true;
s.push_equation(solver::solved, e);
}
else
sc.nextj();
}
}
if (reduced) {
// clean up elements in s.m_to_simplify
// they may have moved.
s.m_to_simplify.shrink(j);
j = 0;
for (equation* e : s.m_to_simplify) {
if (s.is_trivial(*e)) {
s.retire(e);
}
else if (e->state() == solver::to_simplify) {
s.m_to_simplify[j] = e;
e->set_index(j++);
}
solver::scoped_update sc(s.m_to_simplify);
for (; sc.i < sc.sz; ++sc.i) {
equation* e = sc.get();
if (s.is_trivial(*e))
s.retire(e);
else if (e->state() == solver::to_simplify)
sc.nextj();
}
s.m_to_simplify.shrink(j);
return true;
}
else {
return false;
}
else
return false;
}
void simplifier::add_to_use(equation* e, use_list_t& use_list) {

View file

@ -59,9 +59,10 @@ namespace dd {
*/
solver::solver(reslimit& lim, pdd_manager& m) :
solver::solver(reslimit& lim, u_dependency_manager& dm, pdd_manager& m) :
m(m),
m_limit(lim)
m_limit(lim),
m_dep_manager(dm)
{}
solver::~solver() {
@ -89,11 +90,9 @@ namespace dd {
}
void solver::saturate() {
simplify();
if (done()) {
return;
}
init_saturate();
if (done())
return;
init_saturate();
TRACE("dd.solver", display(tout););
try {
while (!done() && step()) {
@ -104,7 +103,7 @@ namespace dd {
DEBUG_CODE(invariant(););
}
catch (pdd_manager::mem_out) {
IF_VERBOSE(2, verbose_stream() << "mem-out\n");
IF_VERBOSE(1, verbose_stream() << "mem-out saturate\n");
// don't reduce further
}
}
@ -123,8 +122,7 @@ namespace dd {
solver::scoped_process::~scoped_process() {
if (e) {
pdd p = e->poly();
SASSERT(!p.is_val());
SASSERT(!e->poly().is_val());
g.push_equation(processed, e);
}
}
@ -136,9 +134,8 @@ namespace dd {
void solver::superpose(equation const & eq) {
for (equation* target : m_processed) {
superpose(eq, *target);
}
for (equation* target : m_processed)
superpose(eq, *target);
}
/*
@ -165,32 +162,28 @@ namespace dd {
TRACE("dd.solver", display(tout << "simplification result: ", eq););
}
void solver::well_formed() {
auto& set = m_to_simplify;
for (unsigned k = 0; k < set.size(); ++k)
for (unsigned l = k + 1; l < set.size(); ++l) {
if (!set[l] || !set[k] || set[k] != set[l])
continue;
verbose_stream() << k << " " << l << " " << set[k] << "\n";
for (auto* s : set)
verbose_stream() << s->idx() << "\n";
VERIFY(set[k] != set[l]);
}
}
/*
Use the given equation to simplify equations in set
*/
void solver::simplify_using(equation_vector& set, std::function<bool(equation&, bool&)>& simplifier) {
struct scoped_update {
equation_vector& set;
unsigned i, j, sz;
scoped_update(equation_vector& set): set(set), i(0), j(0), sz(set.size()) {}
void nextj() {
set[j] = set[i];
set[i]->set_index(j++);
}
~scoped_update() {
for (; i < sz; ++i)
nextj();
set.shrink(j);
}
};
void solver::simplify_using(equation_vector& set, std::function<bool(equation&, bool&)>& simplifier) {
scoped_update sr(set);
for (; sr.i < sr.sz; ++sr.i) {
equation& target = *set[sr.i];
bool changed_leading_term = false;
bool simplified = true;
simplified = !done() && simplifier(target, changed_leading_term);
if (simplified && is_trivial(target))
retire(&target);
@ -285,21 +278,32 @@ namespace dd {
m_stats.m_compute_steps++;
IF_VERBOSE(3, if (m_stats.m_compute_steps % 100 == 0) verbose_stream() << "compute steps = " << m_stats.m_compute_steps << "\n";);
equation* e = pick_next();
if (!e) return false;
if (!e)
return false;
scoped_process sd(*this, e);
equation& eq = *e;
SASSERT(eq.state() == to_simplify);
simplify_using(eq, m_processed);
if (is_trivial(eq)) { sd.e = nullptr; retire(e); return true; }
if (check_conflict(eq)) { sd.e = nullptr; return false; }
if (is_trivial(eq)) {
sd.e = nullptr;
retire(e);
return true;
}
if (check_conflict(eq)) {
sd.e = nullptr;
return false;
}
m_too_complex = false;
simplify_using(m_processed, eq);
if (done()) return false;
if (done())
return false;
TRACE("dd.solver", display(tout << "eq = ", eq););
superpose(eq);
simplify_using(m_to_simplify, eq);
if (done()) return false;
if (!m_too_complex) sd.done();
if (done())
return false;
if (!m_too_complex)
sd.done();
return true;
}
@ -344,9 +348,9 @@ namespace dd {
}
void solver::reset() {
for (equation* e : m_solved) dealloc(e);
for (equation* e : m_to_simplify) dealloc(e);
for (equation* e : m_processed) dealloc(e);
for (equation* e : m_solved) dealloc(e);
for (equation* e : m_to_simplify) dealloc(e);
for (equation* e : m_processed) dealloc(e);
m_subst.reset();
m_solved.reset();
m_processed.reset();
@ -444,7 +448,6 @@ namespace dd {
#endif
}
void solver::pop_equation(equation& eq) {
equation_vector& v = get_queue(eq);
unsigned idx = eq.idx();

View file

@ -49,30 +49,17 @@ public:
};
struct config {
unsigned m_eqs_threshold;
unsigned m_expr_size_limit;
unsigned m_expr_degree_limit;
unsigned m_max_steps;
unsigned m_max_simplified;
unsigned m_random_seed;
bool m_enable_exlin;
unsigned m_eqs_growth;
unsigned m_expr_size_growth;
unsigned m_expr_degree_growth;
unsigned m_number_of_conflicts_to_report;
config() :
m_eqs_threshold(UINT_MAX),
m_expr_size_limit(UINT_MAX),
m_expr_degree_limit(UINT_MAX),
m_max_steps(UINT_MAX),
m_max_simplified(UINT_MAX),
m_random_seed(0),
m_enable_exlin(false),
m_eqs_growth(10),
m_expr_size_growth(10),
m_expr_degree_growth(5),
m_number_of_conflicts_to_report(1)
{}
unsigned m_eqs_threshold = UINT_MAX;
unsigned m_expr_size_limit = UINT_MAX;
unsigned m_expr_degree_limit = UINT_MAX;
unsigned m_max_steps = UINT_MAX;
unsigned m_max_simplified = UINT_MAX;
unsigned m_random_seed = 0;
bool m_enable_exlin = false;
unsigned m_eqs_growth = 10;
unsigned m_expr_size_growth = 10;
unsigned m_expr_degree_growth = 5;
unsigned m_number_of_conflicts_to_report = 1;
};
enum eq_state {
@ -82,18 +69,14 @@ public:
};
class equation {
eq_state m_state;
unsigned m_idx; //!< unique index
eq_state m_state = to_simplify;
unsigned m_idx = 0; //!< unique index
pdd m_poly; //!< polynomial in pdd form
u_dependency * m_dep; //!< justification for the equality
public:
equation(pdd const& p, u_dependency* d):
m_state(to_simplify),
m_idx(0),
m_poly(p),
m_dep(d)
{
m_dep(d) {
}
const pdd& poly() const { return m_poly; }
@ -105,13 +88,38 @@ public:
void set_state(eq_state st) { m_state = st; }
void set_index(unsigned idx) { m_idx = idx; }
};
private:
typedef ptr_vector<equation> equation_vector;
struct scoped_update {
equation_vector& set;
unsigned i = 0;
unsigned j = 0;
unsigned sz;
scoped_update(equation_vector& set) :
set(set), sz(set.size()) {
}
~scoped_update() {
for (; i < sz; ++i)
nextj();
set.shrink(j);
}
equation* get() { return set[i]; }
void nextj() {
set[j] = set[i];
set[i]->set_index(j++);
}
};
private:
typedef std::function<void (u_dependency* d, std::ostream& out)> print_dep_t;
pdd_manager& m;
reslimit& m_limit;
u_dependency_manager& m_dep_manager;
stats m_stats;
config m_config;
print_dep_t m_print_dep;
@ -119,12 +127,11 @@ private:
equation_vector m_processed;
equation_vector m_to_simplify;
vector<std::tuple<unsigned, pdd, u_dependency*>> m_subst;
mutable u_dependency_manager m_dep_manager;
equation_vector m_all_eqs;
equation* m_conflict = nullptr;
bool m_too_complex;
public:
solver(reslimit& lim, pdd_manager& m);
solver(reslimit& lim, u_dependency_manager& dm, pdd_manager& m);
~solver();
pdd_manager& get_manager() { return m; }
@ -144,7 +151,6 @@ public:
void saturate();
equation_vector const& equations();
u_dependency_manager& dep() const { return m_dep_manager; }
void collect_statistics(statistics & st) const;
std::ostream& display(std::ostream& out, const equation& eq) const;
@ -192,6 +198,7 @@ private:
void push_equation(eq_state st, equation& eq);
void push_equation(eq_state st, equation* eq) { push_equation(st, *eq); }
void well_formed();
void invariant() const;
struct scoped_process {
solver& g;

View file

@ -27,6 +27,7 @@
#include "math/interval/interval.h"
class dep_intervals {
public:
enum with_deps_t { with_deps, without_deps };
@ -142,8 +143,9 @@ private:
public:
typedef interval_manager<im_config>::interval interval;
u_dependency_manager& m_dep_manager;
mutable unsynch_mpq_manager m_num_manager;
mutable u_dependency_manager m_dep_manager;
im_config m_config;
mutable interval_manager<im_config> m_imanager;
@ -158,9 +160,10 @@ public:
public:
u_dependency_manager& dep_manager() { return m_dep_manager; }
dep_intervals(reslimit& lim) :
m_config(m_num_manager, m_dep_manager),
m_imanager(lim, im_config(m_num_manager, m_dep_manager))
dep_intervals(u_dependency_manager& dm, reslimit& lim) :
m_dep_manager(dm),
m_config(m_num_manager, dm),
m_imanager(lim, im_config(m_num_manager, dm))
{}
std::ostream& display(std::ostream& out, const interval& i) const;
@ -172,6 +175,8 @@ public:
void set_upper_is_inf(interval& a, bool inf) const { m_config.set_upper_is_inf(a, inf); }
void set_lower_dep(interval& a, u_dependency* d) const { m_config.set_lower_dep(a, d); }
void set_upper_dep(interval& a, u_dependency* d) const { m_config.set_upper_dep(a, d); }
u_dependency* get_lower_dep(interval const& a) const { return a.m_lower_dep; }
u_dependency* get_upper_dep(interval const& a) const { return a.m_upper_dep; }
void reset(interval& a) const { set_lower_is_inf(a, true); set_upper_is_inf(a, true); }
void set_value(interval& a, rational const& n) const {
set_lower(a, n);
@ -335,15 +340,17 @@ public:
bool is_empty(interval const& a) const;
void set_interval_for_scalar(interval&, const rational&);
template <typename T>
void linearize(u_dependency* dep, T& expl) const {
vector<unsigned, false> v;
m_dep_manager.linearize(dep, v);
for (unsigned ci: v)
for (auto ci: v)
expl.push_back(ci);
}
void reset() { m_dep_manager.reset(); }
void reset() { }
void del(interval& i) { m_imanager.del(i); }

View file

@ -1,4 +1,5 @@
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 0
NamespaceIndentation: All
NamespaceIndentation: All
BreakBeforeBraces: Stroustrup

View file

@ -19,7 +19,6 @@ Revision History:
--*/
// clang-format off
#pragma once
#include "util/vector.h"
@ -27,6 +26,8 @@ Revision History:
#include "math/lp/test_bound_analyzer.h"
namespace lp {
template <typename C, typename B> // C plays a role of a container, B - lp_bound_propagator
class bound_analyzer_on_row {
const C& m_row;
@ -92,39 +93,17 @@ private:
}
bool bound_is_available(unsigned j, bool lower_bound) {
return (lower_bound && lower_bound_is_available(j)) ||
(!lower_bound && upper_bound_is_available(j));
}
bool upper_bound_is_available(unsigned j) const {
switch (m_bp.get_column_type(j)) {
case column_type::fixed:
case column_type::boxed:
case column_type::upper_bound:
return true;
default:
return false;
}
}
bool lower_bound_is_available(unsigned j) const {
switch (m_bp.get_column_type(j)) {
case column_type::fixed:
case column_type::boxed:
case column_type::lower_bound:
return true;
default:
return false;
}
return (lower_bound && m_bp.lower_bound_is_available(j)) ||
(!lower_bound && m_bp.upper_bound_is_available(j));
}
const impq & ub(unsigned j) const {
lp_assert(upper_bound_is_available(j));
lp_assert(m_bp.upper_bound_is_available(j));
return m_bp.get_upper_bound(j);
}
const impq & lb(unsigned j) const {
lp_assert(lower_bound_is_available(j));
lp_assert(m_bp.lower_bound_is_available(j));
return m_bp.get_lower_bound(j);
}
@ -302,10 +281,33 @@ private:
// */
// }
void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){
m_bp.try_add_bound(u, j, is_lower_bound, coeff_before_j_is_pos, m_row_index, strict);
void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict)
{
unsigned row_index = this->m_row_index;
auto* lar = &m_bp.lp();
auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index, lar]() {
(void) strict;
TRACE("bound_analyzer", tout << "explain_bound_on_var_on_coeff, bound_j = " << bound_j << ", coeff_before_j_is_pos = " << coeff_before_j_is_pos << ", is_lower_bound = " << is_lower_bound << ", strict = " << strict << ", row_index = " << row_index << "\n";);
int bound_sign = (is_lower_bound ? 1 : -1);
int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign;
SASSERT(!tv::is_term(bound_j));
u_dependency* ret = nullptr;
for (auto const& r : lar->get_row(row_index)) {
unsigned j = r.var();
if (j == bound_j)
continue;
mpq const& a = r.coeff();
int a_sign = is_pos(a) ? 1 : -1;
int sign = j_sign * a_sign;
u_dependency* witness = sign > 0 ? lar->get_column_upper_bound_witness(j) : lar->get_column_lower_bound_witness(j);
ret = lar->join_deps(ret, witness);
}
return ret;
};
m_bp.add_bound(u, bound_j, is_lower_bound, strict, explain);
}
void advance_u(unsigned j) {
m_column_of_u = (m_column_of_u == -1) ? j : -2;
}
@ -336,6 +338,9 @@ private:
break;
}
}
};
}

Some files were not shown because too many files have changed in this diff Show more