mirror of
https://github.com/Z3Prover/z3
synced 2025-06-26 07:43:41 +00:00
Merge branch 'master' into polysat
This commit is contained in:
commit
20b5455d08
669 changed files with 26145 additions and 20652 deletions
|
@ -47,12 +47,17 @@ add_subdirectory(math/subpaving)
|
|||
add_subdirectory(ast)
|
||||
add_subdirectory(params)
|
||||
add_subdirectory(ast/rewriter)
|
||||
add_subdirectory(ast/rewriter/bit_blaster)
|
||||
add_subdirectory(ast/normal_forms)
|
||||
add_subdirectory(ast/macros)
|
||||
add_subdirectory(model)
|
||||
add_subdirectory(tactic)
|
||||
add_subdirectory(ast/substitution)
|
||||
add_subdirectory(ast/euf)
|
||||
add_subdirectory(ast/converters)
|
||||
add_subdirectory(ast/substitution)
|
||||
add_subdirectory(ast/simplifiers)
|
||||
add_subdirectory(tactic)
|
||||
add_subdirectory(qe/mbp)
|
||||
add_subdirectory(qe/lite)
|
||||
add_subdirectory(smt/params)
|
||||
add_subdirectory(parsers/util)
|
||||
add_subdirectory(math/grobner)
|
||||
|
@ -68,11 +73,8 @@ add_subdirectory(math/polysat)
|
|||
add_subdirectory(cmd_context)
|
||||
add_subdirectory(cmd_context/extra_cmds)
|
||||
add_subdirectory(parsers/smt2)
|
||||
add_subdirectory(qe/mbp)
|
||||
add_subdirectory(qe/lite)
|
||||
add_subdirectory(solver/assertions)
|
||||
add_subdirectory(ast/pattern)
|
||||
add_subdirectory(ast/rewriter/bit_blaster)
|
||||
add_subdirectory(math/lp)
|
||||
add_subdirectory(sat/smt)
|
||||
add_subdirectory(sat/tactic)
|
||||
|
@ -138,7 +140,7 @@ if (MSVC AND Z3_BUILD_LIBZ3_MSVC_STATIC)
|
|||
set(${CompilerFlag} "${${CompilerFlag}}" CACHE STRING "msvc compiler flags" FORCE)
|
||||
message("MSVC flags: ${CompilerFlag}:${${CompilerFlag}}")
|
||||
endforeach()
|
||||
endif(MSVC)
|
||||
endif()
|
||||
add_library(libz3 ${lib_type} ${object_files})
|
||||
target_include_directories(libz3 INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/api>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "tactic/model_converter.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
#include "ackermannization/ackr_info.h"
|
||||
|
||||
model_converter * mk_ackermannize_bv_model_converter(ast_manager & m, const ackr_info_ref& info);
|
||||
|
|
|
@ -11,7 +11,37 @@ Author:
|
|||
|
||||
Mikolas Janota
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic ackernannize_bv
|
||||
|
||||
### Short Description
|
||||
|
||||
A tactic for performing Ackermann reduction for bit-vector formulas
|
||||
|
||||
### Long Description
|
||||
|
||||
The Ackermann reduction replaces uninterpreted functions $f(t_1), f(t_2)$
|
||||
by fresh variables $f_1, f_2$ and addes axioms $t_1 \simeq t_2 \implies f_1 \simeq f_2$.
|
||||
The reduction has the effect of eliminating uninterpreted functions. When the reduction
|
||||
produces a pure bit-vector benchmark, it allows Z3 to use a specialized SAT solver.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x (_ BitVec 32))
|
||||
(declare-const y (_ BitVec 32))
|
||||
(declare-fun f ((_ BitVec 32)) (_ BitVec 8))
|
||||
|
||||
(assert (not (= (f x) (f y))))
|
||||
(apply ackermannize_bv)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* does not support proofs, does not support unsatisfiable cores
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
|
|
@ -15,7 +15,7 @@ Revision History:
|
|||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "tactic/model_converter.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
#include "ackermannization/ackr_info.h"
|
||||
|
||||
model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref & info, model_ref & abstr_model);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "tactic/model_converter.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
#include "ackermannization/ackr_info.h"
|
||||
|
||||
model_converter * mk_lackr_model_converter_lazy(ast_manager & m, const ackr_info_ref& info, model_ref& abstr_model);
|
||||
|
|
|
@ -68,4 +68,5 @@ z3_add_component(api
|
|||
opt
|
||||
portfolio
|
||||
realclosure
|
||||
extra_cmds
|
||||
)
|
||||
|
|
|
@ -48,6 +48,20 @@ extern "C" {
|
|||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_real_int64(Z3_context c, int64_t num, int64_t den) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_real_int64(c, num, den);
|
||||
RESET_ERROR_CODE();
|
||||
if (den == 0) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
|
||||
RETURN_Z3(nullptr);
|
||||
}
|
||||
sort* s = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT);
|
||||
ast* a = mk_c(c)->mk_numeral_core(rational(num, rational::i64())/rational(den, rational::i64()), s);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_real(c, num, den);
|
||||
|
|
|
@ -137,7 +137,7 @@ extern "C" {
|
|||
ast_manager& m = mk_c(c)->m();
|
||||
recfun::decl::plugin& p = mk_c(c)->recfun().get_plugin();
|
||||
if (!p.has_def(d)) {
|
||||
std::string msg = "function " + mk_pp(d, m) + " needs to be defined using rec_func_decl";
|
||||
std::string msg = "function " + mk_pp(d, m) + " needs to be declared using rec_func_decl";
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, msg.c_str());
|
||||
return;
|
||||
}
|
||||
|
@ -158,6 +158,12 @@ extern "C" {
|
|||
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
|
||||
return;
|
||||
}
|
||||
if (!pd.get_def()->get_cases().empty()) {
|
||||
std::string msg = "function " + mk_pp(d, m) + " has already been given a definition";
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, msg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (abs_body->get_sort() != d->get_range()) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
|
||||
return;
|
||||
|
@ -654,11 +660,14 @@ extern "C" {
|
|||
LOG_Z3_get_domain(c, d, i);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_VALID_AST(d, nullptr);
|
||||
if (i >= to_func_decl(d)->get_arity()) {
|
||||
func_decl* _d = to_func_decl(d);
|
||||
if (_d->is_associative())
|
||||
i = 0;
|
||||
if (i >= _d->get_arity()) {
|
||||
SET_ERROR_CODE(Z3_IOB, nullptr);
|
||||
RETURN_Z3(nullptr);
|
||||
}
|
||||
Z3_sort r = of_sort(to_func_decl(d)->get_domain(i));
|
||||
Z3_sort r = of_sort(_d->get_domain(i));
|
||||
RETURN_Z3(r);
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ namespace api {
|
|||
}
|
||||
|
||||
void context::del_object(api::object* o) {
|
||||
if (!o)
|
||||
return;
|
||||
#ifndef SINGLE_THREAD
|
||||
if (m_concurrent_dec_ref) {
|
||||
lock_guard lock(m_mux);
|
||||
|
@ -149,6 +151,8 @@ namespace api {
|
|||
|
||||
|
||||
context::~context() {
|
||||
if (m_parser)
|
||||
smt2::free_parser(m_parser);
|
||||
m_last_obj = nullptr;
|
||||
flush_objects();
|
||||
for (auto& kv : m_allocated_objects) {
|
||||
|
|
|
@ -50,6 +50,11 @@ namespace realclosure {
|
|||
class manager;
|
||||
};
|
||||
|
||||
namespace smt2 {
|
||||
class parser;
|
||||
void free_parser(parser*);
|
||||
};
|
||||
|
||||
namespace api {
|
||||
|
||||
class seq_expr_solver : public expr_solver {
|
||||
|
@ -233,6 +238,19 @@ namespace api {
|
|||
|
||||
void check_sorts(ast * n);
|
||||
|
||||
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// State reused by calls to Z3_eval_smtlib2_string
|
||||
//
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// The m_parser field is reused by all calls of Z3_eval_smtlib2_string using this context.
|
||||
// It is an optimization to save the cost of recreating these objects on each invocation.
|
||||
//
|
||||
// See https://github.com/Z3Prover/z3/pull/6422 for the motivation
|
||||
smt2::parser* m_parser = nullptr;
|
||||
|
||||
// ------------------------
|
||||
//
|
||||
// Polynomial manager & caches
|
||||
|
|
|
@ -395,8 +395,7 @@ extern "C" {
|
|||
Z3_string s) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_fixedpoint_from_string(c, d, s);
|
||||
std::string str(s);
|
||||
std::istringstream is(str);
|
||||
std::istringstream is(s);
|
||||
RETURN_Z3(Z3_fixedpoint_from_stream(c, d, is));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
|
|
@ -102,6 +102,13 @@ extern "C" {
|
|||
sort* e;
|
||||
|
||||
ptr_vector<constructor_decl> constrs;
|
||||
symbol sname = to_symbol(name);
|
||||
|
||||
if (mk_c(c)->get_dt_plugin()->is_declared(sname)) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, "enumeration sort name is already declared");
|
||||
RETURN_Z3(nullptr);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
symbol e_name(to_symbol(enum_names[i]));
|
||||
std::string recognizer_s("is_");
|
||||
|
@ -112,8 +119,9 @@ extern "C" {
|
|||
}
|
||||
|
||||
|
||||
|
||||
{
|
||||
datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, n, constrs.data());
|
||||
datatype_decl * dt = mk_datatype_decl(dt_util, sname, 0, nullptr, n, constrs.data());
|
||||
bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, nullptr, sorts);
|
||||
del_datatype_decl(dt);
|
||||
|
||||
|
|
|
@ -383,8 +383,7 @@ extern "C" {
|
|||
Z3_string s) {
|
||||
Z3_TRY;
|
||||
//LOG_Z3_optimize_from_string(c, d, s);
|
||||
std::string str(s);
|
||||
std::istringstream is(str);
|
||||
std::istringstream is(s);
|
||||
Z3_optimize_from_stream(c, d, is, nullptr);
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ extern "C" {
|
|||
RESET_ERROR_CODE();
|
||||
std::ostringstream buffer;
|
||||
to_params(p)->m_params.display(buffer);
|
||||
return mk_c(c)->mk_external_string(buffer.str());
|
||||
return mk_c(c)->mk_external_string(std::move(buffer).str());
|
||||
Z3_CATCH_RETURN("");
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ extern "C" {
|
|||
buffer << to_param_descrs_ptr(p)->get_param_name(i);
|
||||
}
|
||||
buffer << ")";
|
||||
return mk_c(c)->mk_external_string(buffer.str());
|
||||
return mk_c(c)->mk_external_string(std::move(buffer).str());
|
||||
Z3_CATCH_RETURN("");
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ Revision History:
|
|||
#include "solver/solver_na2as.h"
|
||||
#include "muz/fp/dl_cmds.h"
|
||||
#include "opt/opt_cmds.h"
|
||||
#include "cmd_context/extra_cmds/proof_cmds.h"
|
||||
|
||||
|
||||
|
||||
|
@ -42,6 +43,7 @@ extern "C" {
|
|||
ast_manager& m = c.m();
|
||||
ctx = alloc(cmd_context, false, &(m));
|
||||
install_dl_cmds(*ctx.get());
|
||||
install_proof_cmds(*ctx.get());
|
||||
install_opt_cmds(*ctx.get());
|
||||
install_smt2_extra_cmds(*ctx.get());
|
||||
ctx->register_plist();
|
||||
|
@ -155,8 +157,7 @@ extern "C" {
|
|||
Z3_ast_vector Z3_API Z3_parser_context_from_string(Z3_context c, Z3_parser_context pc, Z3_string str) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_parser_context_from_string(c, pc, str);
|
||||
std::string s(str);
|
||||
std::istringstream is(s);
|
||||
std::istringstream is(str);
|
||||
auto& ctx = to_parser_context(pc)->ctx;
|
||||
Z3_ast_vector r = Z3_parser_context_parse_stream(c, ctx, false, is);
|
||||
RETURN_Z3(r);
|
||||
|
@ -175,6 +176,7 @@ extern "C" {
|
|||
ast_manager& m = mk_c(c)->m();
|
||||
scoped_ptr<cmd_context> ctx = alloc(cmd_context, false, &(m));
|
||||
install_dl_cmds(*ctx.get());
|
||||
install_proof_cmds(*ctx.get());
|
||||
install_opt_cmds(*ctx.get());
|
||||
install_smt2_extra_cmds(*ctx.get());
|
||||
ctx->register_plist();
|
||||
|
@ -199,8 +201,7 @@ extern "C" {
|
|||
Z3_func_decl const decls[]) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls);
|
||||
std::string s(str);
|
||||
std::istringstream is(s);
|
||||
std::istringstream is(str);
|
||||
Z3_ast_vector r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls);
|
||||
RETURN_Z3(r);
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
|
@ -233,19 +234,20 @@ extern "C" {
|
|||
auto* ctx = alloc(cmd_context, false, &(mk_c(c)->m()));
|
||||
mk_c(c)->cmd() = ctx;
|
||||
install_dl_cmds(*ctx);
|
||||
install_proof_cmds(*ctx);
|
||||
install_opt_cmds(*ctx);
|
||||
install_smt2_extra_cmds(*ctx);
|
||||
ctx->register_plist();
|
||||
ctx->set_solver_factory(mk_smt_strategic_solver_factory());
|
||||
}
|
||||
scoped_ptr<cmd_context>& ctx = mk_c(c)->cmd();
|
||||
std::string s(str);
|
||||
std::istringstream is(s);
|
||||
std::istringstream is(str);
|
||||
ctx->set_regular_stream(ous);
|
||||
ctx->set_diagnostic_stream(ous);
|
||||
cmd_context::scoped_redirect _redirect(*ctx);
|
||||
try {
|
||||
if (!parse_smt2_commands(*ctx.get(), is)) {
|
||||
// See api::context::m_parser for a motivation about the reuse of the parser
|
||||
if (!parse_smt2_commands_with_parser(mk_c(c)->m_parser, *ctx.get(), is)) {
|
||||
SET_ERROR_CODE(Z3_PARSER_ERROR, ous.str());
|
||||
RETURN_Z3(mk_c(c)->mk_external_string(ous.str()));
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ Notes:
|
|||
#include "api/api_model.h"
|
||||
#include "api/api_ast_map.h"
|
||||
#include "api/api_ast_vector.h"
|
||||
#include "qe/lite/qe_lite.h"
|
||||
#include "qe/lite/qe_lite_tactic.h"
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
|
||||
extern "C"
|
||||
|
|
|
@ -212,6 +212,8 @@ extern "C" {
|
|||
buffer.push_back('\\');
|
||||
buffer.push_back('u');
|
||||
buffer.push_back('{');
|
||||
if (ch == 0)
|
||||
buff.push_back('0');
|
||||
while (ch > 0) {
|
||||
unsigned d = ch & 0xF;
|
||||
if (d < 10)
|
||||
|
|
|
@ -43,6 +43,8 @@ Revision History:
|
|||
#include "sat/sat_solver.h"
|
||||
#include "sat/tactic/goal2sat.h"
|
||||
#include "sat/tactic/sat2goal.h"
|
||||
#include "cmd_context/extra_cmds/proof_cmds.h"
|
||||
#include "solver/simplifier_solver.h"
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
@ -231,12 +233,26 @@ extern "C" {
|
|||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_solver Z3_API Z3_solver_add_simplifier(Z3_context c, Z3_solver solver, Z3_simplifier simplifier) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_solver_add_simplifier(c, solver, simplifier);
|
||||
init_solver(c, solver);
|
||||
auto simp = to_simplifier_ref(simplifier);
|
||||
auto* slv = mk_simplifier_solver(to_solver_ref(solver), simp);
|
||||
Z3_solver_ref* sr = alloc(Z3_solver_ref, *mk_c(c), slv);
|
||||
mk_c(c)->save_object(sr);
|
||||
// ?? init_solver_log(c, sr)
|
||||
RETURN_Z3(of_solver(sr));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
|
||||
Z3_solver Z3_API Z3_solver_translate(Z3_context c, Z3_solver s, Z3_context target) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_solver_translate(c, s, target);
|
||||
RESET_ERROR_CODE();
|
||||
params_ref const& p = to_solver(s)->m_params;
|
||||
Z3_solver_ref * sr = alloc(Z3_solver_ref, *mk_c(target), nullptr);
|
||||
Z3_solver_ref * sr = alloc(Z3_solver_ref, *mk_c(target), (solver_factory *)nullptr);
|
||||
init_solver(c, s);
|
||||
sr->m_solver = to_solver(s)->m_solver->translate(mk_c(target)->m(), p);
|
||||
mk_c(target)->save_object(sr);
|
||||
|
@ -257,8 +273,10 @@ extern "C" {
|
|||
|
||||
void solver_from_stream(Z3_context c, Z3_solver s, std::istream& is) {
|
||||
auto& solver = *to_solver(s);
|
||||
if (!solver.m_cmd_context)
|
||||
if (!solver.m_cmd_context) {
|
||||
solver.m_cmd_context = alloc(cmd_context, false, &(mk_c(c)->m()));
|
||||
install_proof_cmds(*solver.m_cmd_context);
|
||||
}
|
||||
auto& ctx = solver.m_cmd_context;
|
||||
ctx->set_ignore_check(true);
|
||||
std::stringstream errstrm;
|
||||
|
@ -270,6 +288,7 @@ extern "C" {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
bool initialized = to_solver(s)->m_solver.get() != nullptr;
|
||||
if (!initialized)
|
||||
init_solver(c, s);
|
||||
|
@ -277,6 +296,10 @@ extern "C" {
|
|||
to_solver(s)->assert_expr(e);
|
||||
ctx->reset_tracked_assertions();
|
||||
to_solver_ref(s)->set_model_converter(ctx->get_model_converter());
|
||||
auto* ctx_s = ctx->get_solver();
|
||||
if (ctx_s && ctx_s->get_proof())
|
||||
to_solver_ref(s)->set_proof(ctx_s->get_proof());
|
||||
|
||||
}
|
||||
|
||||
static void solver_from_dimacs_stream(Z3_context c, Z3_solver s, std::istream& is) {
|
||||
|
@ -310,8 +333,7 @@ extern "C" {
|
|||
void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string c_str) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_solver_from_string(c, s, c_str);
|
||||
std::string str(c_str);
|
||||
std::istringstream is(str);
|
||||
std::istringstream is(c_str);
|
||||
if (is_dimacs_string(c_str)) {
|
||||
solver_from_dimacs_stream(c, s, is);
|
||||
}
|
||||
|
@ -399,7 +421,11 @@ extern "C" {
|
|||
params.validate(r);
|
||||
to_solver_ref(s)->updt_params(params);
|
||||
}
|
||||
to_solver(s)->m_params.append(params);
|
||||
auto& solver = *to_solver(s);
|
||||
solver.m_params.append(params);
|
||||
|
||||
if (solver.m_cmd_context && solver.m_cmd_context->get_proof_cmds())
|
||||
solver.m_cmd_context->get_proof_cmds()->updt_params(solver.m_params);
|
||||
|
||||
init_solver_log(c, s);
|
||||
|
||||
|
@ -684,7 +710,29 @@ extern "C" {
|
|||
RESET_ERROR_CODE();
|
||||
init_solver(c, s);
|
||||
expr_ref_vector core(mk_c(c)->m());
|
||||
to_solver_ref(s)->get_unsat_core(core);
|
||||
solver_params sp(to_solver(s)->m_params);
|
||||
unsigned timeout = mk_c(c)->get_timeout();
|
||||
timeout = to_solver(s)->m_params.get_uint("timeout", timeout);
|
||||
timeout = sp.timeout() != UINT_MAX ? sp.timeout() : timeout;
|
||||
unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit());
|
||||
bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", true);
|
||||
cancel_eh<reslimit> eh(mk_c(c)->m().limit());
|
||||
to_solver(s)->set_eh(&eh);
|
||||
{
|
||||
scoped_ctrl_c ctrlc(eh, false, use_ctrl_c);
|
||||
scoped_timer timer(timeout, &eh);
|
||||
scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit);
|
||||
try {
|
||||
to_solver_ref(s)->get_unsat_core(core);
|
||||
}
|
||||
catch (...) {
|
||||
to_solver_ref(s)->set_reason_unknown(eh);
|
||||
to_solver(s)->set_eh(nullptr);
|
||||
if (core.empty())
|
||||
throw;
|
||||
}
|
||||
}
|
||||
to_solver(s)->set_eh(nullptr);
|
||||
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
|
||||
mk_c(c)->save_object(v);
|
||||
for (expr* e : core) {
|
||||
|
@ -870,6 +918,26 @@ extern "C" {
|
|||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_solver_congruence_root(Z3_context c, Z3_solver s, Z3_ast a) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_solver_congruence_root(c, s, a);
|
||||
RESET_ERROR_CODE();
|
||||
init_solver(c, s);
|
||||
expr* r = to_solver_ref(s)->congruence_root(to_expr(a));
|
||||
RETURN_Z3(of_expr(r));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_solver_congruence_next(Z3_context c, Z3_solver s, Z3_ast a) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_solver_congruence_next(c, s, a);
|
||||
RESET_ERROR_CODE();
|
||||
init_solver(c, s);
|
||||
expr* sib = to_solver_ref(s)->congruence_next(to_expr(a));
|
||||
RETURN_Z3(of_expr(sib));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
class api_context_obj : public user_propagator::context_obj {
|
||||
api::context* c;
|
||||
public:
|
||||
|
@ -877,6 +945,45 @@ extern "C" {
|
|||
~api_context_obj() override { dealloc(c); }
|
||||
};
|
||||
|
||||
struct scoped_ast_vector {
|
||||
Z3_ast_vector_ref* v;
|
||||
scoped_ast_vector(Z3_ast_vector_ref* v): v(v) { v->inc_ref(); }
|
||||
~scoped_ast_vector() { v->dec_ref(); }
|
||||
};
|
||||
|
||||
void Z3_API Z3_solver_register_on_clause(
|
||||
Z3_context c,
|
||||
Z3_solver s,
|
||||
void* user_context,
|
||||
Z3_on_clause_eh on_clause_eh) {
|
||||
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) {
|
||||
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));
|
||||
};
|
||||
to_solver_ref(s)->register_on_clause(user_context, _on_clause);
|
||||
auto& solver = *to_solver(s);
|
||||
|
||||
if (!solver.m_cmd_context) {
|
||||
solver.m_cmd_context = alloc(cmd_context, false, &(mk_c(c)->m()));
|
||||
install_proof_cmds(*solver.m_cmd_context);
|
||||
}
|
||||
|
||||
if (!solver.m_cmd_context->get_proof_cmds()) {
|
||||
init_proof_cmds(*solver.m_cmd_context);
|
||||
solver.m_cmd_context->get_proof_cmds()->updt_params(solver.m_params);
|
||||
}
|
||||
solver.m_cmd_context->get_proof_cmds()->register_on_clause(user_context, _on_clause);
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
void Z3_API Z3_solver_propagate_init(
|
||||
Z3_context c,
|
||||
Z3_solver s,
|
||||
|
|
|
@ -52,6 +52,9 @@ struct Z3_solver_ref : public api::object {
|
|||
Z3_solver_ref(api::context& c, solver_factory * f):
|
||||
api::object(c), m_solver_factory(f), m_solver(nullptr), m_logic(symbol::null), m_eh(nullptr) {}
|
||||
|
||||
Z3_solver_ref(api::context& c, solver * s):
|
||||
api::object(c), m_solver_factory(nullptr), m_solver(s), m_logic(symbol::null), m_eh(nullptr) {}
|
||||
|
||||
void assert_expr(expr* e);
|
||||
void assert_expr(expr* e, expr* t);
|
||||
void set_eh(event_handler* eh);
|
||||
|
|
|
@ -20,9 +20,11 @@ Revision History:
|
|||
#include "api/api_context.h"
|
||||
#include "api/api_tactic.h"
|
||||
#include "api/api_model.h"
|
||||
#include "api/api_solver.h"
|
||||
#include "util/scoped_ctrl_c.h"
|
||||
#include "util/cancel_eh.h"
|
||||
#include "util/scoped_timer.h"
|
||||
#include "ast/simplifiers/seq_simplifier.h"
|
||||
|
||||
Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c) {
|
||||
}
|
||||
|
@ -45,6 +47,14 @@ extern "C" {
|
|||
RETURN_Z3(_result_); \
|
||||
}
|
||||
|
||||
#define RETURN_SIMPLIFIER(_t_) { \
|
||||
Z3_simplifier_ref * _ref_ = alloc(Z3_simplifier_ref, *mk_c(c)); \
|
||||
_ref_->m_simplifier = _t_; \
|
||||
mk_c(c)->save_object(_ref_); \
|
||||
Z3_simplifier _result_ = of_simplifier(_ref_); \
|
||||
RETURN_Z3(_result_); \
|
||||
}
|
||||
|
||||
Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_tactic(c, name);
|
||||
|
@ -517,6 +527,146 @@ extern "C" {
|
|||
RETURN_Z3(result);
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Z3_simplifier Z3_API Z3_mk_simplifier(Z3_context c, Z3_string name) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_simplifier(c, name);
|
||||
RESET_ERROR_CODE();
|
||||
simplifier_cmd * t = mk_c(c)->find_simplifier_cmd(symbol(name));
|
||||
if (t == nullptr) {
|
||||
std::stringstream err;
|
||||
err << "unknown simplifier " << name;
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, err.str());
|
||||
RETURN_Z3(nullptr);
|
||||
}
|
||||
simplifier_factory new_t = t->factory();
|
||||
RETURN_SIMPLIFIER(new_t);
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
void Z3_API Z3_simplifier_inc_ref(Z3_context c, Z3_simplifier t) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_simplifier_inc_ref(c, t);
|
||||
RESET_ERROR_CODE();
|
||||
to_simplifier(t)->inc_ref();
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
void Z3_API Z3_simplifier_dec_ref(Z3_context c, Z3_simplifier t) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_simplifier_dec_ref(c, t);
|
||||
if (t)
|
||||
to_simplifier(t)->dec_ref();
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
unsigned Z3_API Z3_get_num_simplifiers(Z3_context c) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_get_num_simplifiers(c);
|
||||
RESET_ERROR_CODE();
|
||||
return mk_c(c)->num_simplifiers();
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_string Z3_API Z3_get_simplifier_name(Z3_context c, unsigned idx) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_get_simplifier_name(c, idx);
|
||||
RESET_ERROR_CODE();
|
||||
if (idx >= mk_c(c)->num_simplifiers()) {
|
||||
SET_ERROR_CODE(Z3_IOB, nullptr);
|
||||
return "";
|
||||
}
|
||||
return mk_c(c)->mk_external_string(mk_c(c)->get_simplifier(idx)->get_name().str().c_str());
|
||||
Z3_CATCH_RETURN("");
|
||||
}
|
||||
|
||||
Z3_simplifier Z3_API Z3_simplifier_and_then(Z3_context c, Z3_simplifier t1, Z3_simplifier t2) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_simplifier_and_then(c, t1, t2);
|
||||
RESET_ERROR_CODE();
|
||||
auto fac1 = *to_simplifier_ref(t1);
|
||||
auto fac2 = *to_simplifier_ref(t2);
|
||||
auto new_s = [fac1, fac2](auto& m, auto& p, auto& st) {
|
||||
auto* r = alloc(seq_simplifier, m, p, st);
|
||||
r->add_simplifier(fac1(m, p, st));
|
||||
r->add_simplifier(fac2(m, p, st));
|
||||
return r;
|
||||
};
|
||||
RETURN_SIMPLIFIER(new_s);
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_simplifier Z3_API Z3_simplifier_using_params(Z3_context c, Z3_simplifier t, Z3_params p) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_simplifier_using_params(c, t, p);
|
||||
RESET_ERROR_CODE();
|
||||
param_descrs r;
|
||||
ast_manager& m = mk_c(c)->m();
|
||||
default_dependent_expr_state st(m);
|
||||
params_ref p1;
|
||||
auto fac = (*to_simplifier_ref(t));
|
||||
scoped_ptr<dependent_expr_simplifier> simp = fac(m, p1, st);
|
||||
simp->collect_param_descrs(r);
|
||||
auto params = to_param_ref(p);
|
||||
params.validate(r);
|
||||
auto new_s = [params, fac](auto& m, auto& p, auto& st) {
|
||||
params_ref pp;
|
||||
pp.append(params);
|
||||
pp.append(p);
|
||||
return fac(m, pp, st);
|
||||
};
|
||||
RETURN_SIMPLIFIER(new_s);
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
|
||||
Z3_string Z3_API Z3_simplifier_get_help(Z3_context c, Z3_simplifier t) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_simplifier_get_help(c, t);
|
||||
RESET_ERROR_CODE();
|
||||
std::ostringstream buffer;
|
||||
param_descrs descrs;
|
||||
ast_manager& m = mk_c(c)->m();
|
||||
default_dependent_expr_state st(m);
|
||||
params_ref p;
|
||||
scoped_ptr<dependent_expr_simplifier> simp = (*to_simplifier_ref(t))(m, p, st);
|
||||
simp->collect_param_descrs(descrs);
|
||||
descrs.display(buffer);
|
||||
return mk_c(c)->mk_external_string(buffer.str());
|
||||
Z3_CATCH_RETURN("");
|
||||
}
|
||||
|
||||
Z3_param_descrs Z3_API Z3_simplifier_get_param_descrs(Z3_context c, Z3_simplifier t) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_simplifier_get_param_descrs(c, t);
|
||||
RESET_ERROR_CODE();
|
||||
Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c));
|
||||
mk_c(c)->save_object(d);
|
||||
ast_manager& m = mk_c(c)->m();
|
||||
default_dependent_expr_state st(m);
|
||||
params_ref p;
|
||||
scoped_ptr<dependent_expr_simplifier> simp = (*to_simplifier_ref(t))(m, p, st);
|
||||
simp->collect_param_descrs(d->m_descrs);
|
||||
Z3_param_descrs r = of_param_descrs(d);
|
||||
RETURN_Z3(r);
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_string Z3_API Z3_simplifier_get_descr(Z3_context c, Z3_string name) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_simplifier_get_descr(c, name);
|
||||
RESET_ERROR_CODE();
|
||||
simplifier_cmd * t = mk_c(c)->find_simplifier_cmd(symbol(name));
|
||||
if (t == nullptr) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
|
||||
return "";
|
||||
}
|
||||
return t->get_descr();
|
||||
Z3_CATCH_RETURN("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ Revision History:
|
|||
|
||||
#include "api/api_goal.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/simplifiers/dependent_expr_state.h"
|
||||
|
||||
namespace api {
|
||||
class context;
|
||||
|
@ -35,10 +36,19 @@ struct Z3_probe_ref : public api::object {
|
|||
Z3_probe_ref(api::context& c):api::object(c) {}
|
||||
};
|
||||
|
||||
struct Z3_simplifier_ref : public api::object {
|
||||
simplifier_factory m_simplifier;
|
||||
Z3_simplifier_ref(api::context& c):api::object(c) {}
|
||||
};
|
||||
|
||||
inline Z3_tactic_ref * to_tactic(Z3_tactic g) { return reinterpret_cast<Z3_tactic_ref *>(g); }
|
||||
inline Z3_tactic of_tactic(Z3_tactic_ref * g) { return reinterpret_cast<Z3_tactic>(g); }
|
||||
inline tactic * to_tactic_ref(Z3_tactic g) { return g == nullptr ? nullptr : to_tactic(g)->m_tactic.get(); }
|
||||
|
||||
inline Z3_simplifier_ref * to_simplifier(Z3_simplifier g) { return reinterpret_cast<Z3_simplifier_ref *>(g); }
|
||||
inline Z3_simplifier of_simplifier(Z3_simplifier_ref * g) { return reinterpret_cast<Z3_simplifier>(g); }
|
||||
inline simplifier_factory * to_simplifier_ref(Z3_simplifier g) { return g == nullptr ? nullptr : &to_simplifier(g)->m_simplifier; }
|
||||
|
||||
inline Z3_probe_ref * to_probe(Z3_probe g) { return reinterpret_cast<Z3_probe_ref *>(g); }
|
||||
inline Z3_probe of_probe(Z3_probe_ref * g) { return reinterpret_cast<Z3_probe>(g); }
|
||||
inline probe * to_probe_ref(Z3_probe g) { return g == nullptr ? nullptr : to_probe(g)->m_probe.get(); }
|
||||
|
|
|
@ -63,6 +63,7 @@ namespace z3 {
|
|||
class solver;
|
||||
class goal;
|
||||
class tactic;
|
||||
class simplifier;
|
||||
class probe;
|
||||
class model;
|
||||
class func_interp;
|
||||
|
@ -158,7 +159,7 @@ namespace z3 {
|
|||
class context {
|
||||
private:
|
||||
friend class user_propagator_base;
|
||||
bool m_enable_exceptions;
|
||||
bool m_enable_exceptions = true;
|
||||
rounding_mode m_rounding_mode;
|
||||
Z3_context m_ctx = nullptr;
|
||||
void init(config & c) {
|
||||
|
@ -366,8 +367,14 @@ namespace z3 {
|
|||
void recdef(func_decl, expr_vector const& args, expr const& body);
|
||||
func_decl user_propagate_function(symbol const& name, sort_vector const& domain, sort const& range);
|
||||
|
||||
/**
|
||||
\brief create an uninterpreted constant.
|
||||
*/
|
||||
expr constant(symbol const & name, sort const & s);
|
||||
expr constant(char const * name, sort const & s);
|
||||
/**
|
||||
\brief create uninterpreted constants of a given sort.
|
||||
*/
|
||||
expr bool_const(char const * name);
|
||||
expr int_const(char const * name);
|
||||
expr real_const(char const * name);
|
||||
|
@ -378,6 +385,12 @@ namespace z3 {
|
|||
template<size_t precision>
|
||||
expr fpa_const(char const * name);
|
||||
|
||||
/**
|
||||
\brief create a de-Bruijn variable.
|
||||
*/
|
||||
expr variable(unsigned index, sort const& s);
|
||||
|
||||
|
||||
expr fpa_rounding_mode();
|
||||
|
||||
expr bool_val(bool b);
|
||||
|
@ -388,11 +401,11 @@ namespace z3 {
|
|||
expr int_val(uint64_t n);
|
||||
expr int_val(char const * n);
|
||||
|
||||
expr real_val(int n, int d);
|
||||
expr real_val(int n);
|
||||
expr real_val(unsigned n);
|
||||
expr real_val(int64_t n);
|
||||
expr real_val(uint64_t n);
|
||||
expr real_val(int64_t n, int64_t d);
|
||||
expr real_val(char const * n);
|
||||
|
||||
expr bv_val(int n, unsigned sz);
|
||||
|
@ -1566,6 +1579,11 @@ namespace z3 {
|
|||
*/
|
||||
expr substitute(expr_vector const& dst);
|
||||
|
||||
/**
|
||||
\brief Apply function substitution by macro definitions.
|
||||
*/
|
||||
expr substitute(func_decl_vector const& funs, expr_vector const& bodies);
|
||||
|
||||
|
||||
class iterator {
|
||||
expr& e;
|
||||
|
@ -1902,21 +1920,21 @@ namespace z3 {
|
|||
inline expr operator>(expr const & a, int b) { return a > a.ctx().num_val(b, a.get_sort()); }
|
||||
inline expr operator>(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) > b; }
|
||||
|
||||
inline expr operator&(expr const & a, expr const & b) { if (a.is_bool()) return a && b; check_context(a, b); Z3_ast r = Z3_mk_bvand(a.ctx(), a, b); return expr(a.ctx(), r); }
|
||||
inline expr operator&(expr const & a, expr const & b) { if (a.is_bool()) return a && b; check_context(a, b); Z3_ast r = Z3_mk_bvand(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); }
|
||||
inline expr operator&(expr const & a, int b) { return a & a.ctx().num_val(b, a.get_sort()); }
|
||||
inline expr operator&(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) & b; }
|
||||
|
||||
inline expr operator^(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = a.is_bool() ? Z3_mk_xor(a.ctx(), a, b) : Z3_mk_bvxor(a.ctx(), a, b); return expr(a.ctx(), r); }
|
||||
inline expr operator^(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = a.is_bool() ? Z3_mk_xor(a.ctx(), a, b) : Z3_mk_bvxor(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); }
|
||||
inline expr operator^(expr const & a, int b) { return a ^ a.ctx().num_val(b, a.get_sort()); }
|
||||
inline expr operator^(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) ^ b; }
|
||||
|
||||
inline expr operator|(expr const & a, expr const & b) { if (a.is_bool()) return a || b; check_context(a, b); Z3_ast r = Z3_mk_bvor(a.ctx(), a, b); return expr(a.ctx(), r); }
|
||||
inline expr operator|(expr const & a, expr const & b) { if (a.is_bool()) return a || b; check_context(a, b); Z3_ast r = Z3_mk_bvor(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); }
|
||||
inline expr operator|(expr const & a, int b) { return a | a.ctx().num_val(b, a.get_sort()); }
|
||||
inline expr operator|(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) | b; }
|
||||
|
||||
inline expr nand(expr const& a, expr const& b) { if (a.is_bool()) return !(a && b); check_context(a, b); Z3_ast r = Z3_mk_bvnand(a.ctx(), a, b); return expr(a.ctx(), r); }
|
||||
inline expr nor(expr const& a, expr const& b) { if (a.is_bool()) return !(a || b); check_context(a, b); Z3_ast r = Z3_mk_bvnor(a.ctx(), a, b); return expr(a.ctx(), r); }
|
||||
inline expr xnor(expr const& a, expr const& b) { if (a.is_bool()) return !(a ^ b); check_context(a, b); Z3_ast r = Z3_mk_bvxnor(a.ctx(), a, b); return expr(a.ctx(), r); }
|
||||
inline expr nand(expr const& a, expr const& b) { if (a.is_bool()) return !(a && b); check_context(a, b); Z3_ast r = Z3_mk_bvnand(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); }
|
||||
inline expr nor(expr const& a, expr const& b) { if (a.is_bool()) return !(a || b); check_context(a, b); Z3_ast r = Z3_mk_bvnor(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); }
|
||||
inline expr xnor(expr const& a, expr const& b) { if (a.is_bool()) return !(a ^ b); check_context(a, b); Z3_ast r = Z3_mk_bvxnor(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); }
|
||||
inline expr min(expr const& a, expr const& b) {
|
||||
check_context(a, b);
|
||||
Z3_ast r;
|
||||
|
@ -1930,6 +1948,7 @@ namespace z3 {
|
|||
assert(a.is_fpa());
|
||||
r = Z3_mk_fpa_min(a.ctx(), a, b);
|
||||
}
|
||||
a.check_error();
|
||||
return expr(a.ctx(), r);
|
||||
}
|
||||
inline expr max(expr const& a, expr const& b) {
|
||||
|
@ -1945,6 +1964,7 @@ namespace z3 {
|
|||
assert(a.is_fpa());
|
||||
r = Z3_mk_fpa_max(a.ctx(), a, b);
|
||||
}
|
||||
a.check_error();
|
||||
return expr(a.ctx(), r);
|
||||
}
|
||||
inline expr bvredor(expr const & a) {
|
||||
|
@ -2670,12 +2690,13 @@ namespace z3 {
|
|||
public:
|
||||
struct simple {};
|
||||
struct translate {};
|
||||
solver(context & c):object(c) { init(Z3_mk_solver(c)); }
|
||||
solver(context & c, simple):object(c) { init(Z3_mk_simple_solver(c)); }
|
||||
solver(context & c):object(c) { init(Z3_mk_solver(c)); check_error(); }
|
||||
solver(context & c, simple):object(c) { init(Z3_mk_simple_solver(c)); check_error(); }
|
||||
solver(context & c, Z3_solver s):object(c) { init(s); }
|
||||
solver(context & c, char const * logic):object(c) { init(Z3_mk_solver_for_logic(c, c.str_symbol(logic))); }
|
||||
solver(context & c, char const * logic):object(c) { init(Z3_mk_solver_for_logic(c, c.str_symbol(logic))); check_error(); }
|
||||
solver(context & c, solver const& src, translate): object(c) { Z3_solver s = Z3_solver_translate(src.ctx(), src, c); check_error(); init(s); }
|
||||
solver(solver const & s):object(s) { init(s.m_solver); }
|
||||
solver(solver const& s, simplifier const& simp);
|
||||
~solver() { Z3_solver_dec_ref(ctx(), m_solver); }
|
||||
operator Z3_solver() const { return m_solver; }
|
||||
solver & operator=(solver const & s) {
|
||||
|
@ -3046,6 +3067,47 @@ namespace z3 {
|
|||
return tactic(t1.ctx(), r);
|
||||
}
|
||||
|
||||
class simplifier : public object {
|
||||
Z3_simplifier m_simplifier;
|
||||
void init(Z3_simplifier s) {
|
||||
m_simplifier = s;
|
||||
Z3_simplifier_inc_ref(ctx(), s);
|
||||
}
|
||||
public:
|
||||
simplifier(context & c, char const * name):object(c) { Z3_simplifier r = Z3_mk_simplifier(c, name); check_error(); init(r); }
|
||||
simplifier(context & c, Z3_simplifier s):object(c) { init(s); }
|
||||
simplifier(simplifier const & s):object(s) { init(s.m_simplifier); }
|
||||
~simplifier() { Z3_simplifier_dec_ref(ctx(), m_simplifier); }
|
||||
operator Z3_simplifier() const { return m_simplifier; }
|
||||
simplifier & operator=(simplifier const & s) {
|
||||
Z3_simplifier_inc_ref(s.ctx(), s.m_simplifier);
|
||||
Z3_simplifier_dec_ref(ctx(), m_simplifier);
|
||||
object::operator=(s);
|
||||
m_simplifier = s.m_simplifier;
|
||||
return *this;
|
||||
}
|
||||
std::string help() const { char const * r = Z3_simplifier_get_help(ctx(), m_simplifier); check_error(); return r; }
|
||||
friend simplifier operator&(simplifier const & t1, simplifier const & t2);
|
||||
friend simplifier with(simplifier const & t, params const & p);
|
||||
param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_simplifier_get_param_descrs(ctx(), m_simplifier)); }
|
||||
};
|
||||
|
||||
inline solver::solver(solver const& s, simplifier const& simp):object(s) { init(Z3_solver_add_simplifier(s.ctx(), s, simp)); }
|
||||
|
||||
|
||||
inline simplifier operator&(simplifier const & t1, simplifier const & t2) {
|
||||
check_context(t1, t2);
|
||||
Z3_simplifier r = Z3_simplifier_and_then(t1.ctx(), t1, t2);
|
||||
t1.check_error();
|
||||
return simplifier(t1.ctx(), r);
|
||||
}
|
||||
|
||||
inline simplifier with(simplifier const & t, params const & p) {
|
||||
Z3_simplifier r = Z3_simplifier_using_params(t.ctx(), t, p);
|
||||
t.check_error();
|
||||
return simplifier(t.ctx(), r);
|
||||
}
|
||||
|
||||
class probe : public object {
|
||||
Z3_probe m_probe;
|
||||
void init(Z3_probe s) {
|
||||
|
@ -3575,6 +3637,11 @@ namespace z3 {
|
|||
return expr(*this, r);
|
||||
}
|
||||
inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); }
|
||||
inline expr context::variable(unsigned idx, sort const& s) {
|
||||
Z3_ast r = Z3_mk_bound(m_ctx, idx, s);
|
||||
check_error();
|
||||
return expr(*this, r);
|
||||
}
|
||||
inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); }
|
||||
inline expr context::int_const(char const * name) { return constant(name, int_sort()); }
|
||||
inline expr context::real_const(char const * name) { return constant(name, real_sort()); }
|
||||
|
@ -3606,7 +3673,7 @@ namespace z3 {
|
|||
inline expr context::int_val(uint64_t n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); }
|
||||
inline expr context::int_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, int_sort()); check_error(); return expr(*this, r); }
|
||||
|
||||
inline expr context::real_val(int n, int d) { Z3_ast r = Z3_mk_real(m_ctx, n, d); check_error(); return expr(*this, r); }
|
||||
inline expr context::real_val(int64_t n, int64_t d) { Z3_ast r = Z3_mk_real_int64(m_ctx, n, d); check_error(); return expr(*this, r); }
|
||||
inline expr context::real_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); }
|
||||
inline expr context::real_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); }
|
||||
inline expr context::real_val(int64_t n) { Z3_ast r = Z3_mk_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); }
|
||||
|
@ -4059,6 +4126,41 @@ namespace z3 {
|
|||
return expr(ctx(), r);
|
||||
}
|
||||
|
||||
inline expr expr::substitute(func_decl_vector const& funs, expr_vector const& dst) {
|
||||
array<Z3_ast> _dst(dst.size());
|
||||
array<Z3_func_decl> _funs(funs.size());
|
||||
if (dst.size() != funs.size()) {
|
||||
Z3_THROW(exception("length of argument lists don't align"));
|
||||
return expr(ctx(), nullptr);
|
||||
}
|
||||
for (unsigned i = 0; i < dst.size(); ++i) {
|
||||
_dst[i] = dst[i];
|
||||
_funs[i] = funs[i];
|
||||
}
|
||||
Z3_ast r = Z3_substitute_funs(ctx(), m_ast, dst.size(), _funs.ptr(), _dst.ptr());
|
||||
check_error();
|
||||
return expr(ctx(), r);
|
||||
}
|
||||
|
||||
typedef std::function<void(expr const& proof, 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) {
|
||||
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);
|
||||
}
|
||||
public:
|
||||
on_clause(solver& s, on_clause_eh_t& on_clause_eh): c(s.ctx()) {
|
||||
m_on_clause = on_clause_eh;
|
||||
Z3_solver_register_on_clause(c, s, this, _on_clause_eh);
|
||||
c.check_error();
|
||||
}
|
||||
};
|
||||
|
||||
class user_propagator_base {
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE
|
|||
NativeFuncInterp.cs
|
||||
NativeModel.cs
|
||||
NativeSolver.cs
|
||||
OnClause.cs
|
||||
Optimize.cs
|
||||
ParamDescrs.cs
|
||||
Params.cs
|
||||
|
|
102
src/api/dotnet/OnClause.cs
Normal file
102
src/api/dotnet/OnClause.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
OnClause.cs
|
||||
|
||||
Abstract:
|
||||
|
||||
Callback on clause inferences
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-19
|
||||
|
||||
Notes:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Z3
|
||||
{
|
||||
|
||||
using Z3_context = System.IntPtr;
|
||||
using Z3_solver = System.IntPtr;
|
||||
using voidp = System.IntPtr;
|
||||
using Z3_ast = System.IntPtr;
|
||||
using Z3_ast_vector = System.IntPtr;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// OnClause - clause inference callback
|
||||
/// </summary>
|
||||
public class OnClause : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate type for when clauses are inferred.
|
||||
/// An inference is a pair comprising of
|
||||
/// - a proof (hint). A partial (or comprehensive) derivation justifying the inference.
|
||||
/// - a clause (vector of literals)
|
||||
/// The life-time of the proof hint and clause vector is limited to the scope of the callback.
|
||||
/// should the callback want to store hints or clauses it will need to call Dup on the hints
|
||||
/// and/or extract literals from the clause, respectively.
|
||||
/// </summary>
|
||||
public delegate void OnClauseEh(Expr proof_hint, ASTVector clause);
|
||||
|
||||
|
||||
// access managed objects through a static array.
|
||||
// thread safety is ignored for now.
|
||||
GCHandle gch;
|
||||
Solver solver;
|
||||
Context ctx;
|
||||
OnClauseEh on_clause;
|
||||
|
||||
Native.Z3_on_clause_eh on_clause_eh;
|
||||
|
||||
static void _on_clause(voidp ctx, Z3_ast _proof_hint, Z3_ast_vector _clause)
|
||||
{
|
||||
var onc = (OnClause)GCHandle.FromIntPtr(ctx).Target;
|
||||
using var proof_hint = Expr.Create(onc.ctx, _proof_hint);
|
||||
using var clause = new ASTVector(onc.ctx, _clause);
|
||||
onc.on_clause(proof_hint, clause);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnClause constructor
|
||||
/// </summary>
|
||||
public OnClause(Solver s, OnClauseEh onc)
|
||||
{
|
||||
gch = GCHandle.Alloc(this);
|
||||
solver = s;
|
||||
ctx = solver.Context;
|
||||
on_clause = onc;
|
||||
on_clause_eh = _on_clause;
|
||||
Native.Z3_solver_register_on_clause(ctx.nCtx, solver.NativeObject, GCHandle.ToIntPtr(gch), on_clause_eh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release private memory.
|
||||
/// </summary>
|
||||
~OnClause()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Must be called. The object will not be garbage collected automatically even if the context is disposed
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (!gch.IsAllocated)
|
||||
return;
|
||||
gch.Free();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2110,7 +2110,7 @@ public class Context implements AutoCloseable {
|
|||
* Check if the string s1 is lexicographically strictly less than s2.
|
||||
*/
|
||||
|
||||
public BoolExpr MkStringLt(SeqSort<CharSort> s1, SeqSort<CharSort> s2)
|
||||
public BoolExpr MkStringLt(Expr<SeqSort<CharSort>> s1, Expr<SeqSort<CharSort>> s2)
|
||||
{
|
||||
checkContextMatch(s1, s2);
|
||||
return new BoolExpr(this, Native.mkStrLt(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
|
||||
|
@ -2119,7 +2119,7 @@ public class Context implements AutoCloseable {
|
|||
/**
|
||||
* Check if the string s1 is lexicographically less or equal to s2.
|
||||
*/
|
||||
public BoolExpr MkStringLe(SeqSort<CharSort> s1, SeqSort<CharSort> s2)
|
||||
public BoolExpr MkStringLe(Expr<SeqSort<CharSort>> s1, Expr<SeqSort<CharSort>> s2)
|
||||
{
|
||||
checkContextMatch(s1, s2);
|
||||
return new BoolExpr(this, Native.mkStrLe(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
|
||||
|
|
|
@ -11,6 +11,9 @@ You'll need to have emscripten set up, along with all of its dependencies. The e
|
|||
|
||||
Then run `npm i` to install dependencies, `npm run build:ts` to build the TypeScript wrapper, and `npm run build:wasm` to build the wasm artifact.
|
||||
|
||||
### Build on your own
|
||||
|
||||
Consult the file [build-wasm.ts](https://github.com/Z3Prover/z3/blob/master/src/api/js/scripts/build-wasm.ts) for configurations used for building wasm.
|
||||
|
||||
## Tests
|
||||
|
||||
|
|
8318
src/api/js/package-lock.json
generated
8318
src/api/js/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -35,7 +35,8 @@
|
|||
"contributors": [
|
||||
"Kevin Gibbons <bakkot@gmail.com>",
|
||||
"Nikolaj Bjorner",
|
||||
"Olaf Tomalka <olaf@tomalka.me>"
|
||||
"Olaf Tomalka <olaf@tomalka.me>",
|
||||
"Walden Yan <waldenyan20@gmail.com>"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.5.1",
|
||||
|
@ -49,6 +50,7 @@
|
|||
"prettier": "^2.5.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"ts-expect": "^1.3.0",
|
||||
"ts-jest": "^28.0.3",
|
||||
"ts-node": "^10.8.0",
|
||||
"typedoc": "^0.22.17",
|
||||
|
|
|
@ -45,6 +45,8 @@ const types = {
|
|||
__proto__: null,
|
||||
|
||||
// these are function types I can't be bothered to parse
|
||||
// NSB: They can be extracted automatically from z3_api.h thanks to the use
|
||||
// of a macro.
|
||||
Z3_error_handler: 'Z3_error_handler',
|
||||
Z3_push_eh: 'Z3_push_eh',
|
||||
Z3_pop_eh: 'Z3_pop_eh',
|
||||
|
@ -54,6 +56,7 @@ const types = {
|
|||
Z3_final_eh: 'Z3_final_eh',
|
||||
Z3_created_eh: 'Z3_created_eh',
|
||||
Z3_decide_eh: 'Z3_decide_eh',
|
||||
Z3_on_clause_eh: 'Z3_on_clause_eh',
|
||||
} as unknown as Record<string, string>;
|
||||
|
||||
export type ApiParam = { kind: string; sizeIndex?: number; type: string };
|
||||
|
|
|
@ -2,6 +2,7 @@ import assert from 'assert';
|
|||
import asyncToArray from 'iter-tools/methods/async-to-array';
|
||||
import { init, killThreads } from '../jest';
|
||||
import { Arith, Bool, Model, Z3AssertionError, Z3HighLevel } from './types';
|
||||
import { expectType } from "ts-expect";
|
||||
|
||||
/**
|
||||
* Generate all possible solutions from given assumptions.
|
||||
|
@ -113,7 +114,7 @@ describe('high-level', () => {
|
|||
const { Solver, Not, Int } = api.Context('main');
|
||||
const solver = new Solver();
|
||||
solver.fromString("(declare-const x Int) (assert (and (< x 2) (> x 0)))")
|
||||
expect(await solver.check()).toStrictEqual('sat')
|
||||
expect(await solver.check()).toStrictEqual('sat')
|
||||
const x = Int.const('x')
|
||||
solver.add(Not(x.eq(1)))
|
||||
expect(await solver.check()).toStrictEqual('unsat')
|
||||
|
@ -211,6 +212,7 @@ describe('high-level', () => {
|
|||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
const INSTANCE = toSudoku(`
|
||||
....94.3.
|
||||
...51...7
|
||||
|
@ -390,6 +392,106 @@ describe('high-level', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('arrays', () => {
|
||||
|
||||
it('Example 1', async () => {
|
||||
const Z3 = api.Context('main');
|
||||
|
||||
const arr = Z3.Array.const('arr', Z3.Int.sort(), Z3.Int.sort());
|
||||
const [idx, val] = Z3.Int.consts('idx val');
|
||||
|
||||
const conjecture = (arr.store(idx, val).select(idx).eq(val));
|
||||
await prove(conjecture);
|
||||
});
|
||||
|
||||
it('domain and range type inference', async () => {
|
||||
const { BitVec, Array, isArray, isArraySort } = api.Context('main');
|
||||
|
||||
const arr = Array.const('arr', BitVec.sort(160), BitVec.sort(256));
|
||||
|
||||
const domain = arr.domain();
|
||||
expect(domain.size()).toStrictEqual(160);
|
||||
expect(arr.domain_n(0).size()).toStrictEqual(160);
|
||||
const range = arr.range();
|
||||
expect(range.size()).toStrictEqual(256);
|
||||
|
||||
assert(isArray(arr) && isArraySort(arr.sort));
|
||||
|
||||
const arr2 = Array.const('arr2', BitVec.sort(1), BitVec.sort(2), BitVec.sort(3));
|
||||
const dom2 = arr2.domain_n(1);
|
||||
|
||||
// We can call size() on dom2 and see that it is two bits
|
||||
// purely from type inference
|
||||
expectType<2>(dom2.size());
|
||||
|
||||
// Won't let us create an array constant with only one of domain/range
|
||||
// and is detected at compile time
|
||||
// @ts-expect-error
|
||||
const arr3 = Array.const('arr3', BitVec.sort(1));
|
||||
})
|
||||
|
||||
it('can do simple proofs', async () => {
|
||||
const { BitVec, Array, isArray, isArraySort, isConstArray, Eq, Not } = api.Context('main');
|
||||
|
||||
const idx = BitVec.const('idx', 160);
|
||||
const val = BitVec.const('val', 256);
|
||||
|
||||
const FIVE_VAL = BitVec.val(5, 256);
|
||||
|
||||
const arr = Array.const('arr', BitVec.sort(160), BitVec.sort(256));
|
||||
|
||||
const constArr = Array.K(BitVec.sort(160), FIVE_VAL);
|
||||
assert(isArray(arr) && isArraySort(arr.sort) && isConstArray(constArr));
|
||||
|
||||
const arr2 = arr.store(0, 5);
|
||||
await prove(Eq(arr2.select(0), FIVE_VAL));
|
||||
await prove(Not(Eq(arr2.select(0), BitVec.val(6, 256))));
|
||||
await prove(Eq(arr2.store(idx, val).select(idx), constArr.store(idx, val).select(idx)));
|
||||
|
||||
// TODO: add in Quantifiers and better typing of arrays
|
||||
// await prove(
|
||||
// ForAll([idx], idx.add(1).ugt(idx).and(arr.select(idx.add(1)).ugt(arr.select(idx)))).implies(
|
||||
// arr.select(0).ult(arr.select(1000))
|
||||
// )
|
||||
// );
|
||||
});
|
||||
|
||||
it('Finds arrays that differ but that sum to the same', async () => {
|
||||
const Z3 = api.Context('main');
|
||||
const { Array, BitVec } = Z3;
|
||||
|
||||
const mod = 1n << 32n;
|
||||
|
||||
const arr1 = Array.const('arr', BitVec.sort(2), BitVec.sort(32));
|
||||
const arr2 = Array.const('arr2', BitVec.sort(2), BitVec.sort(32));
|
||||
|
||||
const same_sum = arr1.select(0)
|
||||
.add(arr1.select(1))
|
||||
.add(arr1.select(2))
|
||||
.add(arr1.select(3))
|
||||
.eq(
|
||||
arr2.select(0)
|
||||
.add(arr2.select(1))
|
||||
.add(arr2.select(2))
|
||||
.add(arr2.select(3))
|
||||
);
|
||||
|
||||
const different = arr1.select(0).neq(arr2.select(0))
|
||||
.or(arr1.select(1).neq(arr2.select(1)))
|
||||
.or(arr1.select(2).neq(arr2.select(2)))
|
||||
.or(arr1.select(3).neq(arr2.select(3)));
|
||||
|
||||
const model = await solve(same_sum.and(different));
|
||||
|
||||
const arr1Vals = [0, 1, 2, 3].map(i => model.eval(arr1.select(i)).value());
|
||||
const arr2Vals = [0, 1, 2, 3].map(i => model.eval(arr2.select(i)).value());
|
||||
expect((arr1Vals.reduce((a, b) => a + b, 0n) % mod) === arr2Vals.reduce((a, b) => a + b, 0n) % mod);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(arr1Vals[i] !== arr2Vals[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Solver', () => {
|
||||
it('can use push and pop', async () => {
|
||||
const { Solver, Int } = api.Context('main');
|
||||
|
|
|
@ -37,7 +37,7 @@ import {
|
|||
AnyExpr,
|
||||
AnySort,
|
||||
Arith,
|
||||
ArithSort,
|
||||
ArithSort, ArrayIndexType,
|
||||
Ast,
|
||||
AstMap,
|
||||
AstMapCtor,
|
||||
|
@ -48,7 +48,7 @@ import {
|
|||
BitVecSort,
|
||||
Bool,
|
||||
BoolSort,
|
||||
CheckSatResult,
|
||||
CheckSatResult, CoercibleFromMap,
|
||||
CoercibleRational,
|
||||
CoercibleToBitVec,
|
||||
CoercibleToExpr,
|
||||
|
@ -63,6 +63,8 @@ import {
|
|||
Model,
|
||||
Probe,
|
||||
RatNum,
|
||||
SMTArray,
|
||||
SMTArraySort,
|
||||
Solver,
|
||||
Sort,
|
||||
SortToExprMap,
|
||||
|
@ -86,12 +88,11 @@ function isCoercibleRational(obj: any): obj is CoercibleRational {
|
|||
(obj.denominator !== null &&
|
||||
(typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint'))
|
||||
);
|
||||
r &&
|
||||
assert(
|
||||
(typeof obj.numerator !== 'number' || Number.isSafeInteger(obj.numerator)) &&
|
||||
(typeof obj.denominator !== 'number' || Number.isSafeInteger(obj.denominator)),
|
||||
'Fraction numerator and denominator must be integers',
|
||||
);
|
||||
r && assert(
|
||||
(typeof obj!.numerator !== 'number' || Number.isSafeInteger(obj!.numerator)) &&
|
||||
(typeof obj!.denominator !== 'number' || Number.isSafeInteger(obj!.denominator)),
|
||||
'Fraction numerator and denominator must be integers',
|
||||
);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -151,7 +152,9 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
function createContext<Name extends string>(name: Name, options?: Record<string, any>): Context<Name> {
|
||||
const cfg = Z3.mk_config();
|
||||
if (options != null) {
|
||||
Object.entries(options).forEach(([key, value]) => check(Z3.set_param_value(cfg, key, value.toString())));
|
||||
Object.entries(options).forEach(
|
||||
([key, value]) => check(Z3.set_param_value(cfg, key, value.toString()))
|
||||
);
|
||||
}
|
||||
const contextPtr = Z3.mk_context_rc(cfg);
|
||||
Z3.set_ast_print_mode(contextPtr, Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT);
|
||||
|
@ -216,20 +219,22 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
return new ArithSortImpl(ast);
|
||||
case Z3_sort_kind.Z3_BV_SORT:
|
||||
return new BitVecSortImpl(ast);
|
||||
case Z3_sort_kind.Z3_ARRAY_SORT:
|
||||
return new ArraySortImpl(ast);
|
||||
default:
|
||||
return new SortImpl(ast);
|
||||
}
|
||||
}
|
||||
|
||||
function _toExpr(ast: Z3_ast): Bool<Name> | IntNum<Name> | RatNum<Name> | Arith<Name> | Expr<Name> {
|
||||
function _toExpr(ast: Z3_ast): AnyExpr<Name> {
|
||||
const kind = check(Z3.get_ast_kind(contextPtr, ast));
|
||||
if (kind === Z3_ast_kind.Z3_QUANTIFIER_AST) {
|
||||
if (Z3.is_quantifier_forall(contextPtr, ast))
|
||||
return new BoolImpl(ast);
|
||||
return new BoolImpl(ast);
|
||||
if (Z3.is_quantifier_exists(contextPtr, ast))
|
||||
return new BoolImpl(ast);
|
||||
return new BoolImpl(ast);
|
||||
if (Z3.is_lambda(contextPtr, ast))
|
||||
return new ExprImpl(ast);
|
||||
return new ExprImpl(ast);
|
||||
assert(false);
|
||||
}
|
||||
const sortKind = check(Z3.get_sort_kind(contextPtr, Z3.get_sort(contextPtr, ast)));
|
||||
|
@ -251,6 +256,8 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
return new BitVecNumImpl(ast);
|
||||
}
|
||||
return new BitVecImpl(ast);
|
||||
case Z3_sort_kind.Z3_ARRAY_SORT:
|
||||
return new ArrayImpl(ast);
|
||||
default:
|
||||
return new ExprImpl(ast);
|
||||
}
|
||||
|
@ -440,6 +447,22 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
return r;
|
||||
}
|
||||
|
||||
function isArraySort(obj: unknown): obj is SMTArraySort<Name> {
|
||||
const r = obj instanceof ArraySortImpl;
|
||||
r && _assertContext(obj);
|
||||
return r;
|
||||
}
|
||||
|
||||
function isArray(obj: unknown): obj is SMTArray<Name> {
|
||||
const r = obj instanceof ArrayImpl;
|
||||
r && _assertContext(obj);
|
||||
return r;
|
||||
}
|
||||
|
||||
function isConstArray(obj: unknown): boolean {
|
||||
return isAppOf(obj, Z3_decl_kind.Z3_OP_CONST_ARRAY);
|
||||
}
|
||||
|
||||
function isProbe(obj: unknown): obj is Probe<Name> {
|
||||
const r = obj instanceof ProbeImpl;
|
||||
r && _assertContext(obj);
|
||||
|
@ -508,9 +531,9 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
// expression simplification //
|
||||
///////////////////////////////
|
||||
|
||||
async function simplify(e : Expr<Name>) {
|
||||
const result = await Z3.simplify(contextPtr, e.ast)
|
||||
return _toExpr(check(result));
|
||||
async function simplify(e: Expr<Name>) {
|
||||
const result = await Z3.simplify(contextPtr, e.ast)
|
||||
return _toExpr(check(result));
|
||||
}
|
||||
|
||||
/////////////
|
||||
|
@ -677,6 +700,44 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
);
|
||||
},
|
||||
};
|
||||
const Array = {
|
||||
sort<DomainSort extends [AnySort<Name>, ...AnySort<Name>[]], RangeSort extends AnySort<Name>>(
|
||||
...sig: [...DomainSort, RangeSort]
|
||||
): SMTArraySort<Name, DomainSort, RangeSort> {
|
||||
const arity = sig.length - 1;
|
||||
const r = sig[arity];
|
||||
const d = sig[0];
|
||||
if (arity === 1) {
|
||||
return new ArraySortImpl(Z3.mk_array_sort(contextPtr, d.ptr, r.ptr));
|
||||
}
|
||||
const dom = sig.slice(0, arity);
|
||||
return new ArraySortImpl(Z3.mk_array_sort_n(contextPtr, dom.map(s => s.ptr), r.ptr));
|
||||
},
|
||||
const<DomainSort extends [AnySort<Name>, ...AnySort<Name>[]], RangeSort extends AnySort<Name>>(
|
||||
name: string, ...sig: [...DomainSort, RangeSort]
|
||||
): SMTArray<Name, DomainSort, RangeSort> {
|
||||
return new ArrayImpl<DomainSort, RangeSort>(
|
||||
check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr))
|
||||
);
|
||||
},
|
||||
consts<DomainSort extends [AnySort<Name>, ...AnySort<Name>[]], RangeSort extends AnySort<Name>>(
|
||||
names: string | string[],
|
||||
...sig: [...DomainSort, RangeSort]
|
||||
): SMTArray<Name, DomainSort, RangeSort>[] {
|
||||
if (typeof names === 'string') {
|
||||
names = names.split(' ');
|
||||
}
|
||||
return names.map(name => Array.const(name, ...sig));
|
||||
},
|
||||
K<DomainSort extends AnySort<Name>, RangeSort extends AnySort<Name>>(
|
||||
domain: DomainSort,
|
||||
value: SortToExprMap<RangeSort, Name>
|
||||
): SMTArray<Name, [DomainSort], RangeSort> {
|
||||
return new ArrayImpl<[DomainSort], RangeSort>(
|
||||
check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Operations //
|
||||
|
@ -948,6 +1009,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
|
||||
readonly ptr: Z3_solver;
|
||||
readonly ctx: Context<Name>;
|
||||
|
||||
constructor(ptr: Z3_solver | string = Z3.mk_solver(contextPtr)) {
|
||||
this.ctx = ctx;
|
||||
let myPtr: Z3_solver;
|
||||
|
@ -964,21 +1026,26 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
push() {
|
||||
Z3.solver_push(contextPtr, this.ptr);
|
||||
}
|
||||
|
||||
pop(num: number = 1) {
|
||||
Z3.solver_pop(contextPtr, this.ptr, num);
|
||||
}
|
||||
|
||||
numScopes() {
|
||||
return Z3.solver_get_num_scopes(contextPtr, this.ptr);
|
||||
}
|
||||
|
||||
reset() {
|
||||
Z3.solver_reset(contextPtr, this.ptr);
|
||||
}
|
||||
|
||||
add(...exprs: (Bool<Name> | AstVector<Name, Bool<Name>>)[]) {
|
||||
_flattenArgs(exprs).forEach(expr => {
|
||||
_assertContext(expr);
|
||||
check(Z3.solver_assert(contextPtr, this.ptr, expr.ast));
|
||||
});
|
||||
}
|
||||
|
||||
addAndTrack(expr: Bool<Name>, constant: Bool<Name> | string) {
|
||||
if (typeof constant === 'string') {
|
||||
constant = Bool.const(constant);
|
||||
|
@ -1019,7 +1086,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
return check(Z3.solver_to_string(contextPtr, this.ptr));
|
||||
}
|
||||
|
||||
fromString(s : string) {
|
||||
fromString(s: string) {
|
||||
Z3.solver_from_string(contextPtr, this.ptr, s);
|
||||
throwIfError();
|
||||
}
|
||||
|
@ -1043,20 +1110,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
return this.values();
|
||||
}
|
||||
|
||||
*entries(): IterableIterator<[number, FuncDecl<Name>]> {
|
||||
* entries(): IterableIterator<[number, FuncDecl<Name>]> {
|
||||
const length = this.length();
|
||||
for (let i = 0; i < length; i++) {
|
||||
yield [i, this.get(i)];
|
||||
}
|
||||
}
|
||||
|
||||
*keys(): IterableIterator<number> {
|
||||
* keys(): IterableIterator<number> {
|
||||
for (const [key] of this.entries()) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
|
||||
*values(): IterableIterator<FuncDecl<Name>> {
|
||||
* values(): IterableIterator<FuncDecl<Name>> {
|
||||
for (const [, value] of this.entries()) {
|
||||
yield value;
|
||||
}
|
||||
|
@ -1076,11 +1143,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
|
||||
eval(expr: Bool<Name>, modelCompletion?: boolean): Bool<Name>;
|
||||
eval(expr: Arith<Name>, modelCompletion?: boolean): Arith<Name>;
|
||||
eval<Bits extends number = number>(expr: BitVec<Bits, Name>, modelCompletion?: boolean): BitVecNum<Bits, Name>;
|
||||
eval(expr: Expr<Name>, modelCompletion: boolean = false) {
|
||||
_assertContext(expr);
|
||||
const r = check(Z3.model_eval(contextPtr, this.ptr, expr.ast, modelCompletion));
|
||||
if (r === null) {
|
||||
throw new Z3Error('Failed to evaluatio expression in the model');
|
||||
throw new Z3Error('Failed to evaluate expression in the model');
|
||||
}
|
||||
return _toExpr(r);
|
||||
}
|
||||
|
@ -1092,7 +1160,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
get(sort: Sort<Name>): AstVector<Name, AnyExpr<Name>>;
|
||||
get(
|
||||
i: number | FuncDecl<Name> | Expr<Name> | Sort<Name>,
|
||||
to?: number,
|
||||
to?: number
|
||||
): FuncDecl<Name> | FuncInterp<Name> | Expr<Name> | AstVector<Name, AnyAst<Name>> | FuncDecl<Name>[] {
|
||||
assert(to === undefined || typeof i === 'number');
|
||||
if (typeof i === 'number') {
|
||||
|
@ -1362,15 +1430,22 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
not(): Bool<Name> {
|
||||
return Not(this);
|
||||
}
|
||||
|
||||
and(other: Bool<Name> | boolean): Bool<Name> {
|
||||
return And(this, other);
|
||||
}
|
||||
|
||||
or(other: Bool<Name> | boolean): Bool<Name> {
|
||||
return Or(this, other);
|
||||
}
|
||||
|
||||
xor(other: Bool<Name> | boolean): Bool<Name> {
|
||||
return Xor(this, other);
|
||||
}
|
||||
|
||||
implies(other: Bool<Name> | boolean): Bool<Name> {
|
||||
return Implies(this, other);
|
||||
}
|
||||
}
|
||||
|
||||
class ProbeImpl implements Probe<Name> {
|
||||
|
@ -1571,27 +1646,35 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
add(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvadd(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
mul(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvmul(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
sub(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvsub(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
sdiv(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvsdiv(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
udiv(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvudiv(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
smod(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvsmod(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
urem(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvurem(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
srem(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvsrem(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
neg(): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvneg(contextPtr, this.ast)));
|
||||
}
|
||||
|
@ -1599,33 +1682,43 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
or(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvor(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
and(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvand(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
nand(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvnand(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
xor(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvxor(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
xnor(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvxnor(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
shr(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvashr(contextPtr, this.ast, this.sort.cast(count).ast)));
|
||||
}
|
||||
|
||||
lshr(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvlshr(contextPtr, this.ast, this.sort.cast(count).ast)));
|
||||
}
|
||||
|
||||
shl(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvshl(contextPtr, this.ast, this.sort.cast(count).ast)));
|
||||
}
|
||||
|
||||
rotateRight(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_ext_rotate_right(contextPtr, this.ast, this.sort.cast(count).ast)));
|
||||
}
|
||||
|
||||
rotateLeft(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_ext_rotate_left(contextPtr, this.ast, this.sort.cast(count).ast)));
|
||||
}
|
||||
|
||||
not(): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvnot(contextPtr, this.ast)));
|
||||
}
|
||||
|
@ -1633,12 +1726,15 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
extract(high: number, low: number): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_extract(contextPtr, high, low, this.ast)));
|
||||
}
|
||||
|
||||
signExt(count: number): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_sign_ext(contextPtr, count, this.ast)));
|
||||
}
|
||||
|
||||
zeroExt(count: number): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_zero_ext(contextPtr, count, this.ast)));
|
||||
}
|
||||
|
||||
repeat(count: number): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_repeat(contextPtr, count, this.ast)));
|
||||
}
|
||||
|
@ -1646,24 +1742,31 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
sle(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvsle(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
ule(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvule(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
slt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvslt(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
ult(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvult(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
sge(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvsge(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
uge(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvuge(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
sgt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
ugt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvugt(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
@ -1671,6 +1774,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
redAnd(): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvredand(contextPtr, this.ast)));
|
||||
}
|
||||
|
||||
redOr(): BitVec<Bits, Name> {
|
||||
return new BitVecImpl<Bits>(check(Z3.mk_bvredor(contextPtr, this.ast)));
|
||||
}
|
||||
|
@ -1678,24 +1782,31 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
addNoOverflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvadd_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
|
||||
}
|
||||
|
||||
addNoUnderflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvadd_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
subNoOverflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvsub_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
subNoUndeflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvsub_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
|
||||
}
|
||||
|
||||
sdivNoOverflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvsdiv_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
mulNoOverflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvmul_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
|
||||
}
|
||||
|
||||
mulNoUndeflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvmul_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast)));
|
||||
}
|
||||
|
||||
negNoOverflow(): Bool<Name> {
|
||||
return new BoolImpl(check(Z3.mk_bvneg_no_overflow(contextPtr, this.ast)));
|
||||
}
|
||||
|
@ -1703,6 +1814,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
|
||||
class BitVecNumImpl<Bits extends number> extends BitVecImpl<Bits> implements BitVecNum<Bits, Name> {
|
||||
declare readonly __typename: BitVecNum['__typename'];
|
||||
|
||||
value() {
|
||||
return BigInt(this.asString());
|
||||
}
|
||||
|
@ -1718,14 +1830,87 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
asString() {
|
||||
return Z3.get_numeral_string(contextPtr, this.ast);
|
||||
}
|
||||
|
||||
asBinaryString() {
|
||||
return Z3.get_numeral_binary_string(contextPtr, this.ast);
|
||||
}
|
||||
}
|
||||
|
||||
class ArraySortImpl<DomainSort extends [AnySort<Name>, ...AnySort<Name>[]] = [Sort<Name>, ...Sort<Name>[]],
|
||||
RangeSort extends AnySort<Name> = Sort<Name>>
|
||||
extends SortImpl
|
||||
implements SMTArraySort<Name, DomainSort, RangeSort> {
|
||||
declare readonly __typename: SMTArraySort['__typename'];
|
||||
|
||||
domain(): DomainSort[0] {
|
||||
return _toSort(check(Z3.get_array_sort_domain(contextPtr, this.ptr)));
|
||||
}
|
||||
|
||||
domain_n<T extends number>(i: T): DomainSort[T] {
|
||||
return _toSort(check(Z3.get_array_sort_domain_n(contextPtr, this.ptr, i)));
|
||||
}
|
||||
|
||||
range(): RangeSort {
|
||||
return _toSort(check(Z3.get_array_sort_range(contextPtr, this.ptr))) as RangeSort;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ArrayImpl<
|
||||
DomainSort extends [AnySort<Name>, ...AnySort<Name>[]] = [Sort<Name>, ...Sort<Name>[]],
|
||||
RangeSort extends AnySort<Name> = Sort<Name>
|
||||
> extends ExprImpl<Z3_ast, ArraySortImpl<DomainSort, RangeSort>>
|
||||
implements SMTArray<Name, DomainSort, RangeSort> {
|
||||
declare readonly __typename: SMTArray['__typename'];
|
||||
|
||||
domain(): DomainSort[0] {
|
||||
return this.sort.domain();
|
||||
}
|
||||
|
||||
domain_n<T extends number>(i: T): DomainSort[T] {
|
||||
return this.sort.domain_n(i);
|
||||
}
|
||||
|
||||
range(): RangeSort {
|
||||
return this.sort.range();
|
||||
}
|
||||
|
||||
select(...indices: ArrayIndexType<Name, DomainSort>): SortToExprMap<RangeSort, Name> {
|
||||
const args = indices.map((arg, i) => this.domain_n(i).cast(arg as any));
|
||||
if (args.length === 1) {
|
||||
return _toExpr(check(Z3.mk_select(contextPtr, this.ast, args[0].ast))) as SortToExprMap<RangeSort, Name>;
|
||||
}
|
||||
const _args = args.map(arg => arg.ast);
|
||||
return _toExpr(check(Z3.mk_select_n(contextPtr, this.ast, _args))) as SortToExprMap<RangeSort, Name>;
|
||||
}
|
||||
|
||||
store(
|
||||
...indicesAndValue: [
|
||||
...ArrayIndexType<Name, DomainSort>,
|
||||
CoercibleFromMap<SortToExprMap<RangeSort, Name>, Name>
|
||||
]
|
||||
): SMTArray<Name, DomainSort, RangeSort> {
|
||||
const args = indicesAndValue.map((arg, i) => {
|
||||
if (i === indicesAndValue.length - 1) {
|
||||
return this.range().cast(arg as CoercibleFromMap<SortToExprMap<RangeSort, Name>, Name>);
|
||||
}
|
||||
return this.domain_n(i).cast(arg as any);
|
||||
});
|
||||
if (args.length <= 1) {
|
||||
throw new Z3Error("Array store requires both index and value arguments");
|
||||
}
|
||||
if (args.length === 2) {
|
||||
return _toExpr(check(Z3.mk_store(contextPtr, this.ast, args[0].ast, args[1].ast))) as SMTArray<Name, DomainSort, RangeSort>;
|
||||
}
|
||||
const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast);
|
||||
return _toExpr(check(Z3.mk_store_n(contextPtr, this.ast, _idxs, args[args.length - 1].ast))) as SMTArray<Name, DomainSort, RangeSort>;
|
||||
}
|
||||
}
|
||||
|
||||
class AstVectorImpl<Item extends AnyAst<Name>> {
|
||||
declare readonly __typename: AstVector['__typename'];
|
||||
readonly ctx: Context<Name>;
|
||||
|
@ -1744,20 +1929,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
return this.values();
|
||||
}
|
||||
|
||||
*entries(): IterableIterator<[number, Item]> {
|
||||
* entries(): IterableIterator<[number, Item]> {
|
||||
const length = this.length();
|
||||
for (let i = 0; i < length; i++) {
|
||||
yield [i, this.get(i)];
|
||||
}
|
||||
}
|
||||
|
||||
*keys(): IterableIterator<number> {
|
||||
* keys(): IterableIterator<number> {
|
||||
for (let [key] of this.entries()) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
|
||||
*values(): IterableIterator<Item> {
|
||||
* values(): IterableIterator<Item> {
|
||||
for (let [, value] of this.entries()) {
|
||||
yield value;
|
||||
}
|
||||
|
@ -1842,7 +2027,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
return Z3.ast_map_size(contextPtr, this.ptr);
|
||||
}
|
||||
|
||||
*entries(): IterableIterator<[Key, Value]> {
|
||||
* entries(): IterableIterator<[Key, Value]> {
|
||||
for (const key of this.keys()) {
|
||||
yield [key, this.get(key)];
|
||||
}
|
||||
|
@ -1852,11 +2037,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
return new AstVectorImpl(Z3.ast_map_keys(contextPtr, this.ptr));
|
||||
}
|
||||
|
||||
*values(): IterableIterator<Value> {
|
||||
* values(): IterableIterator<Value> {
|
||||
for (const [_, value] of this.entries()) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
|
||||
get(key: Key): Value {
|
||||
return _toAst(check(Z3.ast_map_find(contextPtr, this.ptr, key.ast))) as Value;
|
||||
}
|
||||
|
@ -1928,6 +2114,9 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
isBitVecSort,
|
||||
isBitVec,
|
||||
isBitVecVal, // TODO fix ordering
|
||||
isArraySort,
|
||||
isArray,
|
||||
isConstArray,
|
||||
isProbe,
|
||||
isTactic,
|
||||
isAstVector,
|
||||
|
@ -1946,6 +2135,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
|
|||
Int,
|
||||
Real,
|
||||
BitVec,
|
||||
Array,
|
||||
|
||||
////////////////
|
||||
// Operations //
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -528,7 +528,7 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
|
|||
m.BINARY_OP(tactic, &, &);
|
||||
m.BINARY_OP(tactic, |, |);
|
||||
m.method("repeat", &repeat);
|
||||
m.method("with", &with);
|
||||
m.method("with", static_cast<tactic (*)(const tactic & , const params &)>(&with));
|
||||
m.method("try_for", &try_for);
|
||||
m.method("par_or", &par_or);
|
||||
m.method("par_and_then", &par_and_then);
|
||||
|
@ -692,7 +692,7 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
|
|||
.method("real_val", [](context &a, const jlcxx::StrictlyTypedNumber<unsigned> b) { return a.real_val(b.value); })
|
||||
.method("real_val", [](context &a, const jlcxx::StrictlyTypedNumber<int64_t> b) { return a.real_val(b.value); })
|
||||
.method("real_val", [](context &a, const jlcxx::StrictlyTypedNumber<uint64_t> b) { return a.real_val(b.value); })
|
||||
.method("real_val", static_cast<expr (context::*)(int, int)>(&context::real_val))
|
||||
.method("real_val", static_cast<expr (context::*)(int64_t, int64_t)>(&context::real_val))
|
||||
.method("real_val", static_cast<expr (context::*)(char const *)>(&context::real_val))
|
||||
//
|
||||
.method("bv_val", [](context &a, const jlcxx::StrictlyTypedNumber<int> b, unsigned c) { return a.bv_val(b.value, c); })
|
||||
|
|
|
@ -1734,6 +1734,39 @@ struct
|
|||
let interrupt = Z3native.interrupt
|
||||
end
|
||||
|
||||
module Simplifier =
|
||||
struct
|
||||
type simplifier = Z3native.simplifier
|
||||
let gc = Z3native.context_of_simplifier
|
||||
|
||||
let get_help (x:simplifier) = Z3native.simplifier_get_help (gc x) x
|
||||
|
||||
let get_param_descrs (x:simplifier) = Z3native.simplifier_get_param_descrs (gc x) x
|
||||
|
||||
let get_num_simplifiers = Z3native.get_num_simplifiers
|
||||
|
||||
let get_simplifier_names (ctx:context) =
|
||||
let n = get_num_simplifiers ctx in
|
||||
let f i = Z3native.get_simplifier_name ctx i in
|
||||
mk_list f n
|
||||
|
||||
let get_simplifier_description = Z3native.simplifier_get_descr
|
||||
|
||||
let mk_simplifier = Z3native.mk_simplifier
|
||||
|
||||
let and_then (ctx:context) (t1:simplifier) (t2:simplifier) (ts:simplifier list) =
|
||||
let f p c = (match p with
|
||||
| None -> Some c
|
||||
| Some(x) -> Some (Z3native.simplifier_and_then ctx c x)) in
|
||||
match (List.fold_left f None ts) with
|
||||
| None -> Z3native.simplifier_and_then ctx t1 t2
|
||||
| Some(x) -> let o = Z3native.simplifier_and_then ctx t2 x in
|
||||
Z3native.simplifier_and_then ctx t1 o
|
||||
|
||||
let using_params = Z3native.simplifier_using_params
|
||||
let with_ = using_params
|
||||
|
||||
end
|
||||
|
||||
module Statistics =
|
||||
struct
|
||||
|
@ -1868,6 +1901,7 @@ struct
|
|||
let mk_solver_s ctx logic = mk_solver ctx (Some (Symbol.mk_string ctx logic))
|
||||
let mk_simple_solver = Z3native.mk_simple_solver
|
||||
let mk_solver_t = Z3native.mk_solver_from_tactic
|
||||
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
|
||||
end
|
||||
|
|
|
@ -3102,6 +3102,38 @@ sig
|
|||
val interrupt : context -> unit
|
||||
end
|
||||
|
||||
module Simplifier :
|
||||
sig
|
||||
type simplifier
|
||||
|
||||
(** A string containing a description of parameters accepted by the simplifier. *)
|
||||
val get_help : simplifier -> string
|
||||
|
||||
(** Retrieves parameter descriptions for Simplifiers. *)
|
||||
val get_param_descrs : simplifier -> Params.ParamDescrs.param_descrs
|
||||
|
||||
(** The number of supported simplifiers. *)
|
||||
val get_num_simplifiers : context -> int
|
||||
|
||||
(** The names of all supported simplifiers. *)
|
||||
val get_simplifier_names : context -> string list
|
||||
|
||||
(** Returns a string containing a description of the simplifier with the given name. *)
|
||||
val get_simplifier_description : context -> string -> string
|
||||
|
||||
(** Creates a new Simplifier. *)
|
||||
val mk_simplifier : context -> string -> simplifier
|
||||
|
||||
(** Create a simplifier that applies one simplifier to a Goal and
|
||||
then another one to every subgoal produced by the first one. *)
|
||||
val and_then : context -> simplifier -> simplifier -> simplifier list -> simplifier
|
||||
|
||||
(** Create a simplifier that applies a simplifier using the given set of parameters. *)
|
||||
val using_params : context -> simplifier -> Params.params -> simplifier
|
||||
val with_ : context -> simplifier -> Params.params -> simplifier
|
||||
|
||||
end
|
||||
|
||||
(** Objects that track statistical information. *)
|
||||
module Statistics :
|
||||
sig
|
||||
|
@ -3265,6 +3297,9 @@ sig
|
|||
will always solve each check from scratch. *)
|
||||
val mk_solver_t : context -> Tactic.tactic -> solver
|
||||
|
||||
(** Create a solver with simplifying pre-processing **)
|
||||
val add_simplifier : context -> solver -> Simplifier.simplifier -> solver
|
||||
|
||||
(** Create a clone of the current solver with respect to a context. *)
|
||||
val translate : solver -> context -> solver
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ and solver = ptr
|
|||
and solver_callback = ptr
|
||||
and goal = ptr
|
||||
and tactic = ptr
|
||||
and simplifier = ptr
|
||||
and params = ptr
|
||||
and parser_context = ptr
|
||||
and probe = ptr
|
||||
|
|
|
@ -424,6 +424,7 @@ MK_PLUS_OBJ(func_interp, 32)
|
|||
MK_PLUS_OBJ(func_entry, 32)
|
||||
MK_PLUS_OBJ(goal, 64)
|
||||
MK_PLUS_OBJ(tactic, 64)
|
||||
MK_PLUS_OBJ(simplifier, 64)
|
||||
MK_PLUS_OBJ(probe, 64)
|
||||
MK_PLUS_OBJ(apply_result, 32)
|
||||
MK_PLUS_OBJ(solver, 20 * 1000)
|
||||
|
|
|
@ -277,7 +277,7 @@ if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv:
|
|||
# linux builds should be built in the centos 5 vm for maximum compatibility
|
||||
# see https://github.com/pypa/manylinux
|
||||
# see also https://github.com/angr/angr-dev/blob/master/admin/bdist.py
|
||||
plat_name = 'manylinux1_' + platform.machine()
|
||||
plat_name = 'manylinux2014_' + platform.machine()
|
||||
elif 'mingw' in name:
|
||||
if platform.architecture()[0] == '64bit':
|
||||
plat_name = 'win_amd64'
|
||||
|
@ -296,9 +296,9 @@ if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv:
|
|||
)
|
||||
elif distos == 'glibc':
|
||||
if arch == 'x64':
|
||||
plat_name = 'manylinux1_x86_64'
|
||||
plat_name = 'manylinux2014_x86_64'
|
||||
else:
|
||||
plat_name = 'manylinux1_i686'
|
||||
plat_name = 'manylinux2014_i686'
|
||||
elif distos == 'linux' and os_id == 'alpine':
|
||||
if arch == 'x64':
|
||||
plat_name = 'musllinux_1_1_x86_64'
|
||||
|
|
|
@ -763,8 +763,6 @@ class FuncDeclRef(AstRef):
|
|||
>>> f.domain(1)
|
||||
Real
|
||||
"""
|
||||
if z3_debug():
|
||||
_z3_assert(i < self.arity(), "Index out of bounds")
|
||||
return _to_sort_ref(Z3_get_domain(self.ctx_ref(), self.ast, i), self.ctx)
|
||||
|
||||
def range(self):
|
||||
|
@ -834,8 +832,6 @@ class FuncDeclRef(AstRef):
|
|||
"""
|
||||
args = _get_args(args)
|
||||
num = len(args)
|
||||
if z3_debug():
|
||||
_z3_assert(num == self.arity(), "Incorrect number of arguments to %s" % self)
|
||||
_args = (Ast * num)()
|
||||
saved = []
|
||||
for i in range(num):
|
||||
|
@ -1194,7 +1190,7 @@ def _coerce_expr_merge(s, a):
|
|||
else:
|
||||
if z3_debug():
|
||||
_z3_assert(s1.ctx == s.ctx, "context mismatch")
|
||||
_z3_assert(False, "sort mismatch")
|
||||
_z3_assert(False, "sort mismatch")
|
||||
else:
|
||||
return s
|
||||
|
||||
|
@ -1207,6 +1203,11 @@ def _coerce_exprs(a, b, ctx=None):
|
|||
a = StringVal(a, b.ctx)
|
||||
if isinstance(b, str) and isinstance(a, SeqRef):
|
||||
b = StringVal(b, a.ctx)
|
||||
if isinstance(a, float) and isinstance(b, ArithRef):
|
||||
a = RealVal(a, b.ctx)
|
||||
if isinstance(b, float) and isinstance(a, ArithRef):
|
||||
b = RealVal(b, a.ctx)
|
||||
|
||||
s = None
|
||||
s = _coerce_expr_merge(s, a)
|
||||
s = _coerce_expr_merge(s, b)
|
||||
|
@ -1464,7 +1465,9 @@ def FreshConst(sort, prefix="c"):
|
|||
|
||||
def Var(idx, s):
|
||||
"""Create a Z3 free variable. Free variables are used to create quantified formulas.
|
||||
|
||||
A free variable with index n is bound when it occurs within the scope of n+1 quantified
|
||||
declarations.
|
||||
|
||||
>>> Var(0, IntSort())
|
||||
Var(0)
|
||||
>>> eq(Var(0, IntSort()), Var(0, BoolSort()))
|
||||
|
@ -1552,13 +1555,15 @@ class BoolRef(ExprRef):
|
|||
def __mul__(self, other):
|
||||
"""Create the Z3 expression `self * other`.
|
||||
"""
|
||||
if other == 1:
|
||||
return self
|
||||
if other == 0:
|
||||
return 0
|
||||
if isinstance(other, int) and other == 1:
|
||||
return If(self, 1, 0)
|
||||
if isinstance(other, int) and other == 0:
|
||||
return IntVal(0, self.ctx)
|
||||
if isinstance(other, BoolRef):
|
||||
other = If(other, 1, 0)
|
||||
return If(self, other, 0)
|
||||
|
||||
|
||||
|
||||
def is_bool(a):
|
||||
"""Return `True` if `a` is a Z3 Boolean expression.
|
||||
|
||||
|
@ -4588,10 +4593,10 @@ class ArrayRef(ExprRef):
|
|||
|
||||
def _array_select(ar, arg):
|
||||
if isinstance(arg, tuple):
|
||||
args = [ar.domain_n(i).cast(arg[i]) for i in range(len(arg))]
|
||||
args = [ar.sort().domain_n(i).cast(arg[i]) for i in range(len(arg))]
|
||||
_args, sz = _to_ast_array(args)
|
||||
return _to_expr_ref(Z3_mk_select_n(ar.ctx_ref(), ar.as_ast(), sz, _args), ar.ctx)
|
||||
arg = ar.domain().cast(arg)
|
||||
arg = ar.sort().domain().cast(arg)
|
||||
return _to_expr_ref(Z3_mk_select(ar.ctx_ref(), ar.as_ast(), arg.as_ast()), ar.ctx)
|
||||
|
||||
|
||||
|
@ -6653,7 +6658,7 @@ class ModelRef(Z3PPObject):
|
|||
n = Z3_func_entry_get_num_args(x.ctx_ref(), e.entry)
|
||||
v = AstVector()
|
||||
for j in range(n):
|
||||
v.push(entry.arg_value(j))
|
||||
v.push(e.arg_value(j))
|
||||
val = Z3_func_entry_get_value(x.ctx_ref(), e.entry)
|
||||
Z3_func_interp_add_entry(x.ctx_ref(), fi2.f, v.vector, val)
|
||||
return
|
||||
|
@ -7232,6 +7237,22 @@ class Solver(Z3PPObject):
|
|||
cube are likely more useful to cube on."""
|
||||
return self.cube_vs
|
||||
|
||||
def root(self, t):
|
||||
t = _py2expr(t, self.ctx)
|
||||
"""Retrieve congruence closure root of the term t relative to the current search state
|
||||
The function primarily works for SimpleSolver. Terms and variables that are
|
||||
eliminated during pre-processing are not visible to the congruence closure.
|
||||
"""
|
||||
return _to_expr_ref(Z3_solver_congruence_root(self.ctx.ref(), self.solver, t.ast), self.ctx)
|
||||
|
||||
def next(self, t):
|
||||
t = _py2expr(t, self.ctx)
|
||||
"""Retrieve congruence closure sibling of the term t relative to the current search state
|
||||
The function primarily works for SimpleSolver. Terms and variables that are
|
||||
eliminated during pre-processing are not visible to the congruence closure.
|
||||
"""
|
||||
return _to_expr_ref(Z3_solver_congruence_next(self.ctx.ref(), self.solver, t.ast), self.ctx)
|
||||
|
||||
def proof(self):
|
||||
"""Return a proof for the last `check()`. Proof construction must be enabled."""
|
||||
return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx)
|
||||
|
@ -8148,6 +8169,64 @@ class ApplyResult(Z3PPObject):
|
|||
else:
|
||||
return Or([self[i].as_expr() for i in range(len(self))])
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Simplifiers
|
||||
#
|
||||
#########################################
|
||||
|
||||
class Simplifier:
|
||||
"""Simplifiers act as pre-processing utilities for solvers.
|
||||
Build a custom simplifier and add it to a solver"""
|
||||
|
||||
def __init__(self, simplifier, ctx=None):
|
||||
self.ctx = _get_ctx(ctx)
|
||||
self.simplifier = None
|
||||
if isinstance(simplifier, SimplifierObj):
|
||||
self.simplifier = simplifier
|
||||
elif isinstance(simplifier, list):
|
||||
simps = [Simplifier(s, ctx) for s in simplifier]
|
||||
self.simplifier = simps[0].simplifier
|
||||
for i in range(1, len(simps)):
|
||||
self.simplifier = Z3_simplifier_and_then(self.ctx.ref(), self.simplifier, simps[i].simplifier)
|
||||
Z3_simplifier_inc_ref(self.ctx.ref(), self.simplifier)
|
||||
return
|
||||
else:
|
||||
if z3_debug():
|
||||
_z3_assert(isinstance(simplifier, str), "simplifier name expected")
|
||||
try:
|
||||
self.simplifier = Z3_mk_simplifier(self.ctx.ref(), str(simplifier))
|
||||
except Z3Exception:
|
||||
raise Z3Exception("unknown simplifier '%s'" % simplifier)
|
||||
Z3_simplifier_inc_ref(self.ctx.ref(), self.simplifier)
|
||||
|
||||
def __deepcopy__(self, memo={}):
|
||||
return Simplifier(self.simplifier, self.ctx)
|
||||
|
||||
def __del__(self):
|
||||
if self.simplifier is not None and self.ctx.ref() is not None and Z3_simplifier_dec_ref is not None:
|
||||
Z3_simplifier_dec_ref(self.ctx.ref(), self.simplifier)
|
||||
|
||||
def using_params(self, *args, **keys):
|
||||
"""Return a simplifier that uses the given configuration options"""
|
||||
p = args2params(args, keys, self.ctx)
|
||||
return Simplifier(Z3_simplifier_using_params(self.ctx.ref(), self.simplifier, p.params), self.ctx)
|
||||
|
||||
def add(self, solver):
|
||||
"""Return a solver that applies the simplification pre-processing specified by the simplifier"""
|
||||
print(solver.solver)
|
||||
print(self.simplifier)
|
||||
return Solver(Z3_solver_add_simplifier(self.ctx.ref(), solver.solver, self.simplifier), self.ctx)
|
||||
|
||||
def help(self):
|
||||
"""Display a string containing a description of the available options for the `self` simplifier."""
|
||||
print(Z3_simplifier_get_help(self.ctx.ref(), self.simplifier))
|
||||
|
||||
def param_descrs(self):
|
||||
"""Return the parameter description set."""
|
||||
return ParamDescrsRef(Z3_simplifier_get_param_descrs(self.ctx.ref(), self.simplifier), self.ctx)
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Tactics
|
||||
|
@ -8832,7 +8911,7 @@ def substitute_vars(t, *m):
|
|||
return _to_expr_ref(Z3_substitute_vars(t.ctx.ref(), t.as_ast(), num, _to), t.ctx)
|
||||
|
||||
def substitute_funs(t, *m):
|
||||
"""Apply subistitution m on t, m is a list of pairs of a function and expression (from, to)
|
||||
"""Apply substitution m on t, m is a list of pairs of a function and expression (from, to)
|
||||
Every occurrence in to of the function from is replaced with the expression to.
|
||||
The expression to can have free variables, that refer to the arguments of from.
|
||||
For examples, see
|
||||
|
@ -10079,7 +10158,7 @@ def FPs(names, fpsort, ctx=None):
|
|||
>>> x.ebits()
|
||||
8
|
||||
>>> fpMul(RNE(), fpAdd(RNE(), x, y), z)
|
||||
fpMul(RNE(), fpAdd(RNE(), x, y), z)
|
||||
x + y * z
|
||||
"""
|
||||
ctx = _get_ctx(ctx)
|
||||
if isinstance(names, str):
|
||||
|
@ -10186,9 +10265,9 @@ def fpAdd(rm, a, b, ctx=None):
|
|||
>>> x = FP('x', s)
|
||||
>>> y = FP('y', s)
|
||||
>>> fpAdd(rm, x, y)
|
||||
fpAdd(RNE(), x, y)
|
||||
>>> fpAdd(RTZ(), x, y) # default rounding mode is RTZ
|
||||
x + y
|
||||
>>> fpAdd(RTZ(), x, y) # default rounding mode is RTZ
|
||||
fpAdd(RTZ(), x, y)
|
||||
>>> fpAdd(rm, x, y).sort()
|
||||
FPSort(8, 24)
|
||||
"""
|
||||
|
@ -10203,7 +10282,7 @@ def fpSub(rm, a, b, ctx=None):
|
|||
>>> x = FP('x', s)
|
||||
>>> y = FP('y', s)
|
||||
>>> fpSub(rm, x, y)
|
||||
fpSub(RNE(), x, y)
|
||||
x - y
|
||||
>>> fpSub(rm, x, y).sort()
|
||||
FPSort(8, 24)
|
||||
"""
|
||||
|
@ -10218,7 +10297,7 @@ def fpMul(rm, a, b, ctx=None):
|
|||
>>> x = FP('x', s)
|
||||
>>> y = FP('y', s)
|
||||
>>> fpMul(rm, x, y)
|
||||
fpMul(RNE(), x, y)
|
||||
x * y
|
||||
>>> fpMul(rm, x, y).sort()
|
||||
FPSort(8, 24)
|
||||
"""
|
||||
|
@ -10233,7 +10312,7 @@ def fpDiv(rm, a, b, ctx=None):
|
|||
>>> x = FP('x', s)
|
||||
>>> y = FP('y', s)
|
||||
>>> fpDiv(rm, x, y)
|
||||
fpDiv(RNE(), x, y)
|
||||
x / y
|
||||
>>> fpDiv(rm, x, y).sort()
|
||||
FPSort(8, 24)
|
||||
"""
|
||||
|
@ -11301,6 +11380,45 @@ def TransitiveClosure(f):
|
|||
"""
|
||||
return FuncDeclRef(Z3_mk_transitive_closure(f.ctx_ref(), f.ast), f.ctx)
|
||||
|
||||
def to_Ast(ptr,):
|
||||
ast = Ast(ptr)
|
||||
super(ctypes.c_void_p, ast).__init__(ptr)
|
||||
return ast
|
||||
|
||||
def to_ContextObj(ptr,):
|
||||
ctx = ContextObj(ptr)
|
||||
super(ctypes.c_void_p, ctx).__init__(ptr)
|
||||
return ctx
|
||||
|
||||
def to_AstVectorObj(ptr,):
|
||||
v = AstVectorObj(ptr)
|
||||
super(ctypes.c_void_p, v).__init__(ptr)
|
||||
return v
|
||||
|
||||
# NB. my-hacky-class only works for a single instance of OnClause
|
||||
# it should be replaced with a proper correlation between OnClause
|
||||
# and object references that can be passed over the FFI.
|
||||
# for UserPropagator we use a global dictionary, which isn't great code.
|
||||
|
||||
_my_hacky_class = None
|
||||
def on_clause_eh(ctx, p, 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)
|
||||
|
||||
_on_clause_eh = Z3_on_clause_eh(on_clause_eh)
|
||||
|
||||
class OnClause:
|
||||
def __init__(self, s, on_clause):
|
||||
self.s = s
|
||||
self.ctx = s.ctx
|
||||
self.on_clause = on_clause
|
||||
self.idx = 22
|
||||
global _my_hacky_class
|
||||
_my_hacky_class = self
|
||||
Z3_solver_register_on_clause(self.ctx.ref(), self.s.solver, self.idx, _on_clause_eh)
|
||||
|
||||
|
||||
class PropClosures:
|
||||
def __init__(self):
|
||||
|
@ -11358,11 +11476,6 @@ def user_prop_pop(ctx, cb, num_scopes):
|
|||
prop.cb = cb
|
||||
prop.pop(num_scopes)
|
||||
|
||||
def to_ContextObj(ptr,):
|
||||
ctx = ContextObj(ptr)
|
||||
super(ctypes.c_void_p, ctx).__init__(ptr)
|
||||
return ctx
|
||||
|
||||
|
||||
def user_prop_fresh(ctx, _new_ctx):
|
||||
_prop_closures.set_threaded()
|
||||
|
@ -11377,10 +11490,6 @@ def user_prop_fresh(ctx, _new_ctx):
|
|||
_prop_closures.set(new_prop.id, new_prop)
|
||||
return new_prop.id
|
||||
|
||||
def to_Ast(ptr,):
|
||||
ast = Ast(ptr)
|
||||
super(ctypes.c_void_p, ast).__init__(ptr)
|
||||
return ast
|
||||
|
||||
def user_prop_fixed(ctx, cb, id, value):
|
||||
prop = _prop_closures.get(ctx)
|
||||
|
@ -11442,6 +11551,7 @@ _user_prop_eq = Z3_eq_eh(user_prop_eq)
|
|||
_user_prop_diseq = Z3_eq_eh(user_prop_diseq)
|
||||
_user_prop_decide = Z3_decide_eh(user_prop_decide)
|
||||
|
||||
|
||||
def PropagateFunction(name, *sig):
|
||||
"""Create a function that gets tracked by user propagator.
|
||||
Every term headed by this function symbol is tracked.
|
||||
|
@ -11462,7 +11572,8 @@ def PropagateFunction(name, *sig):
|
|||
dom[i] = sig[i].ast
|
||||
ctx = rng.ctx
|
||||
return FuncDeclRef(Z3_solver_propagate_declare(ctx.ref(), to_symbol(name, ctx), arity, dom, rng.ast), ctx)
|
||||
|
||||
|
||||
|
||||
|
||||
class UserPropagateBase:
|
||||
|
||||
|
|
|
@ -120,6 +120,12 @@ class TacticObj(ctypes.c_void_p):
|
|||
def from_param(obj):
|
||||
return obj
|
||||
|
||||
class SimplifierObj(ctypes.c_void_p):
|
||||
def __init__(self, simplifier):
|
||||
self._as_parameter_ = simplifier
|
||||
|
||||
def from_param(obj):
|
||||
return obj
|
||||
|
||||
class ProbeObj(ctypes.c_void_p):
|
||||
def __init__(self, probe):
|
||||
|
|
161
src/api/z3_api.h
161
src/api/z3_api.h
|
@ -23,6 +23,7 @@ DEFINE_TYPE(Z3_param_descrs);
|
|||
DEFINE_TYPE(Z3_parser_context);
|
||||
DEFINE_TYPE(Z3_goal);
|
||||
DEFINE_TYPE(Z3_tactic);
|
||||
DEFINE_TYPE(Z3_simplifier);
|
||||
DEFINE_TYPE(Z3_probe);
|
||||
DEFINE_TYPE(Z3_stats);
|
||||
DEFINE_TYPE(Z3_solver);
|
||||
|
@ -69,6 +70,7 @@ DEFINE_TYPE(Z3_rcf_num);
|
|||
- \c Z3_ast_map: mapping from \c Z3_ast to \c Z3_ast objects.
|
||||
- \c Z3_goal: set of formulas that can be solved and/or transformed using tactics and solvers.
|
||||
- \c Z3_tactic: basic building block for creating custom solvers for specific problem domains.
|
||||
- \c Z3_simplifier: basic building block for creating custom pre-processing simplifiers.
|
||||
- \c Z3_probe: function/predicate used to inspect a goal and collect information that may be used to decide which solver and/or preprocessing step will be used.
|
||||
- \c Z3_apply_result: collection of subgoals resulting from applying of a tactic to a goal.
|
||||
- \c Z3_solver: (incremental) solver, possibly specialized by a particular tactic or logic.
|
||||
|
@ -1403,6 +1405,7 @@ typedef enum
|
|||
def_Type('PARSER_CONTEXT', 'Z3_parser_context', 'ParserContextObj')
|
||||
def_Type('GOAL', 'Z3_goal', 'GoalObj')
|
||||
def_Type('TACTIC', 'Z3_tactic', 'TacticObj')
|
||||
def_Type('SIMPLIFIER', 'Z3_simplifier', 'SimplifierObj')
|
||||
def_Type('PARAMS', 'Z3_params', 'Params')
|
||||
def_Type('PROBE', 'Z3_probe', 'ProbeObj')
|
||||
def_Type('STATS', 'Z3_stats', 'StatsObj')
|
||||
|
@ -1433,6 +1436,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, Z3_lbool* phase));
|
||||
Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, Z3_ast_vector literals));
|
||||
|
||||
|
||||
/**
|
||||
|
@ -3416,12 +3420,22 @@ extern "C" {
|
|||
|
||||
\sa Z3_mk_numeral
|
||||
\sa Z3_mk_int
|
||||
\sa Z3_mk_real_int64
|
||||
\sa Z3_mk_unsigned_int
|
||||
|
||||
def_API('Z3_mk_real', AST, (_in(CONTEXT), _in(INT), _in(INT)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den);
|
||||
|
||||
/**
|
||||
\brief Create a real from a fraction of int64.
|
||||
|
||||
\sa Z3_mk_real
|
||||
def_API('Z3_mk_real_int64', AST, (_in(CONTEXT), _in(INT64), _in(INT64)))
|
||||
*/
|
||||
|
||||
Z3_ast Z3_API Z3_mk_real_int64(Z3_context c, int64_t num, int64_t den);
|
||||
|
||||
/**
|
||||
\brief Create a numeral of an int, bit-vector, or finite-domain sort.
|
||||
|
||||
|
@ -3762,7 +3776,7 @@ extern "C" {
|
|||
If \c s does not contain \c substr, then the value is -1,
|
||||
def_API('Z3_mk_seq_last_index', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_seq_last_index(Z3_context c, Z3_ast, Z3_ast substr);
|
||||
Z3_ast Z3_API Z3_mk_seq_last_index(Z3_context c, Z3_ast s, Z3_ast substr);
|
||||
|
||||
/**
|
||||
\brief Convert string to integer.
|
||||
|
@ -3892,7 +3906,7 @@ extern "C" {
|
|||
|
||||
def_API('Z3_mk_re_power', AST, (_in(CONTEXT), _in(AST), _in(UINT)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_re_power(Z3_context c, Z3_ast, unsigned n);
|
||||
Z3_ast Z3_API Z3_mk_re_power(Z3_context c, Z3_ast re, unsigned n);
|
||||
|
||||
/**
|
||||
\brief Create the intersection of the regular languages.
|
||||
|
@ -4050,7 +4064,10 @@ extern "C" {
|
|||
Z3_pattern Z3_API Z3_mk_pattern(Z3_context c, unsigned num_patterns, Z3_ast const terms[]);
|
||||
|
||||
/**
|
||||
\brief Create a bound variable.
|
||||
\brief Create a variable.
|
||||
|
||||
Variables are intended to be bound by a scope created by a quantifier. So we call them bound variables
|
||||
even if they appear as free variables in the expression produced by \c Z3_mk_bound.
|
||||
|
||||
Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain
|
||||
the meaning of de-Bruijn indices by indicating the compilation process from
|
||||
|
@ -5317,8 +5334,9 @@ extern "C" {
|
|||
Z3_ast const to[]);
|
||||
|
||||
/**
|
||||
\brief Substitute the free variables in \c a with the expressions in \c to.
|
||||
\brief Substitute the variables in \c a with the expressions in \c to.
|
||||
For every \c i smaller than \c num_exprs, the variable with de-Bruijn index \c i is replaced with term \ccode{to[i]}.
|
||||
Note that a variable is created using the function \ref Z3_mk_bound.
|
||||
|
||||
def_API('Z3_substitute_vars', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST)))
|
||||
*/
|
||||
|
@ -5876,7 +5894,7 @@ extern "C" {
|
|||
def_API('Z3_eval_smtlib2_string', STRING, (_in(CONTEXT), _in(STRING),))
|
||||
*/
|
||||
|
||||
Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str);
|
||||
Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context c, Z3_string str);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -6192,7 +6210,7 @@ extern "C" {
|
|||
|
||||
/**@}*/
|
||||
|
||||
/** @name Tactics and Probes */
|
||||
/** @name Tactics, Simplifiers and Probes */
|
||||
/**@{*/
|
||||
/**
|
||||
\brief Return a tactic associated with the given name.
|
||||
|
@ -6344,6 +6362,97 @@ extern "C" {
|
|||
*/
|
||||
Z3_tactic Z3_API Z3_tactic_using_params(Z3_context c, Z3_tactic t, Z3_params p);
|
||||
|
||||
|
||||
/**
|
||||
\brief Return a simplifier associated with the given name.
|
||||
The complete list of simplifiers may be obtained using the procedures #Z3_get_num_simplifiers and #Z3_get_simplifier_name.
|
||||
It may also be obtained using the command \ccode{(help-simplifier)} in the SMT 2.0 front-end.
|
||||
|
||||
Simplifiers are the basic building block for creating custom solvers for specific problem domains.
|
||||
|
||||
def_API('Z3_mk_simplifier', SIMPLIFIER, (_in(CONTEXT), _in(STRING)))
|
||||
*/
|
||||
Z3_simplifier Z3_API Z3_mk_simplifier(Z3_context c, Z3_string name);
|
||||
|
||||
/**
|
||||
\brief Increment the reference counter of the given simplifier.
|
||||
|
||||
def_API('Z3_simplifier_inc_ref', VOID, (_in(CONTEXT), _in(SIMPLIFIER)))
|
||||
*/
|
||||
void Z3_API Z3_simplifier_inc_ref(Z3_context c, Z3_simplifier t);
|
||||
|
||||
/**
|
||||
\brief Decrement the reference counter of the given simplifier.
|
||||
|
||||
def_API('Z3_simplifier_dec_ref', VOID, (_in(CONTEXT), _in(SIMPLIFIER)))
|
||||
*/
|
||||
void Z3_API Z3_simplifier_dec_ref(Z3_context c, Z3_simplifier g);
|
||||
|
||||
/**
|
||||
\brief Attach simplifier to a solver. The solver will use the simplifier for incremental pre-processing.
|
||||
|
||||
def_API('Z3_solver_add_simplifier', SOLVER, (_in(CONTEXT), _in(SOLVER), _in(SIMPLIFIER)))
|
||||
*/
|
||||
Z3_solver Z3_API Z3_solver_add_simplifier(Z3_context c, Z3_solver solver, Z3_simplifier simplifier);
|
||||
|
||||
/**
|
||||
\brief Return a simplifier that applies \c t1 to a given goal and \c t2
|
||||
to every subgoal produced by \c t1.
|
||||
|
||||
def_API('Z3_simplifier_and_then', SIMPLIFIER, (_in(CONTEXT), _in(SIMPLIFIER), _in(SIMPLIFIER)))
|
||||
*/
|
||||
Z3_simplifier Z3_API Z3_simplifier_and_then(Z3_context c, Z3_simplifier t1, Z3_simplifier t2);
|
||||
|
||||
/**
|
||||
\brief Return a simplifier that applies \c t using the given set of parameters.
|
||||
|
||||
def_API('Z3_simplifier_using_params', SIMPLIFIER, (_in(CONTEXT), _in(SIMPLIFIER), _in(PARAMS)))
|
||||
*/
|
||||
Z3_simplifier Z3_API Z3_simplifier_using_params(Z3_context c, Z3_simplifier t, Z3_params p);
|
||||
|
||||
|
||||
/**
|
||||
\brief Return the number of builtin simplifiers available in Z3.
|
||||
|
||||
\sa Z3_get_simplifier_name
|
||||
|
||||
def_API('Z3_get_num_simplifiers', UINT, (_in(CONTEXT),))
|
||||
*/
|
||||
unsigned Z3_API Z3_get_num_simplifiers(Z3_context c);
|
||||
|
||||
/**
|
||||
\brief Return the name of the idx simplifier.
|
||||
|
||||
\pre i < Z3_get_num_simplifiers(c)
|
||||
|
||||
\sa Z3_get_num_simplifiers
|
||||
|
||||
def_API('Z3_get_simplifier_name', STRING, (_in(CONTEXT), _in(UINT)))
|
||||
*/
|
||||
Z3_string Z3_API Z3_get_simplifier_name(Z3_context c, unsigned i);
|
||||
|
||||
/**
|
||||
\brief Return a string containing a description of parameters accepted by the given simplifier.
|
||||
|
||||
def_API('Z3_simplifier_get_help', STRING, (_in(CONTEXT), _in(SIMPLIFIER)))
|
||||
*/
|
||||
Z3_string Z3_API Z3_simplifier_get_help(Z3_context c, Z3_simplifier t);
|
||||
|
||||
/**
|
||||
\brief Return the parameter description set for the given simplifier object.
|
||||
|
||||
def_API('Z3_simplifier_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(SIMPLIFIER)))
|
||||
*/
|
||||
Z3_param_descrs Z3_API Z3_simplifier_get_param_descrs(Z3_context c, Z3_simplifier t);
|
||||
|
||||
/**
|
||||
\brief Return a string containing a description of the simplifier with the given name.
|
||||
|
||||
def_API('Z3_simplifier_get_descr', STRING, (_in(CONTEXT), _in(STRING)))
|
||||
*/
|
||||
Z3_string Z3_API Z3_simplifier_get_descr(Z3_context c, Z3_string name);
|
||||
|
||||
|
||||
/**
|
||||
\brief Return a probe that always evaluates to val.
|
||||
|
||||
|
@ -6877,6 +6986,44 @@ extern "C" {
|
|||
*/
|
||||
void Z3_API Z3_solver_get_levels(Z3_context c, Z3_solver s, Z3_ast_vector literals, unsigned sz, unsigned levels[]);
|
||||
|
||||
/**
|
||||
\brief retrieve the congruence closure root of an expression.
|
||||
The root is retrieved relative to the state where the solver was in when it completed.
|
||||
If it completed during a set of case splits, the congruence roots are relative to these case splits.
|
||||
That is, the congruences are not consequences but they are true under the current state.
|
||||
|
||||
def_API('Z3_solver_congruence_root', AST, (_in(CONTEXT), _in(SOLVER), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_solver_congruence_root(Z3_context c, Z3_solver s, Z3_ast a);
|
||||
|
||||
|
||||
/**
|
||||
\brief retrieve the next expression in the congruence class. The set of congruent siblings form a cyclic list.
|
||||
Repeated calls on the siblings will result in returning to the original expression.
|
||||
|
||||
def_API('Z3_solver_congruence_next', AST, (_in(CONTEXT), _in(SOLVER), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_solver_congruence_next(Z3_context c, Z3_solver s, Z3_ast a);
|
||||
|
||||
|
||||
/**
|
||||
\brief register a callback to that retrieves assumed, inferred and deleted clauses during search.
|
||||
|
||||
\param c - context.
|
||||
\param s - solver object.
|
||||
\param user_context - a context used to maintain state for callbacks.
|
||||
\param on_clause_eh - a callback that is invoked by when a clause is
|
||||
- asserted to the CDCL engine (corresponding to an input clause after pre-processing)
|
||||
- inferred by CDCL(T) using either a SAT or theory conflict/propagation
|
||||
- deleted by the CDCL(T) engine
|
||||
|
||||
def_API('Z3_solver_register_on_clause', VOID, (_in(CONTEXT), _in(SOLVER), _in(VOID_PTR), _fnptr(Z3_on_clause_eh)))
|
||||
*/
|
||||
void Z3_API Z3_solver_register_on_clause(
|
||||
Z3_context c,
|
||||
Z3_solver s,
|
||||
void* user_context,
|
||||
Z3_on_clause_eh on_clause_eh);
|
||||
|
||||
/**
|
||||
\brief register a user-properator with the solver.
|
||||
|
@ -7006,7 +7153,7 @@ extern "C" {
|
|||
def_API('Z3_solver_propagate_consequence', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, AST), _in(UINT), _in_array(4, AST), _in_array(4, AST), _in(AST)))
|
||||
*/
|
||||
|
||||
void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback, unsigned num_fixed, Z3_ast const* fixed, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq);
|
||||
void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback cb, unsigned num_fixed, Z3_ast const* fixed, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq);
|
||||
|
||||
/**
|
||||
\brief Check whether the assertions in a given solver are consistent or not.
|
||||
|
|
|
@ -447,12 +447,17 @@ public:
|
|||
app * mk_add(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(arith_family_id, OP_ADD, arg1, arg2, arg3); }
|
||||
app * mk_add(expr_ref_vector const& args) const { return mk_add(args.size(), args.data()); }
|
||||
app * mk_add(expr_ref_buffer const& args) const { return mk_add(args.size(), args.data()); }
|
||||
app * mk_add(ptr_buffer<expr> const& args) const { return mk_add(args.size(), args.data()); }
|
||||
app * mk_add(ptr_vector<expr> const& args) const { return mk_add(args.size(), args.data()); }
|
||||
|
||||
app * mk_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(arith_family_id, OP_SUB, arg1, arg2); }
|
||||
app * mk_sub(unsigned num_args, expr * const * args) const { return m_manager.mk_app(arith_family_id, OP_SUB, num_args, args); }
|
||||
app * mk_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(arith_family_id, OP_MUL, arg1, arg2); }
|
||||
app * mk_mul(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(arith_family_id, OP_MUL, arg1, arg2, arg3); }
|
||||
app * mk_mul(unsigned num_args, expr * const * args) const { return num_args == 1 && is_app(args[0]) ? to_app(args[0]) : m_manager.mk_app(arith_family_id, OP_MUL, num_args, args); }
|
||||
app * mk_mul(ptr_buffer<expr> const& args) const { return mk_mul(args.size(), args.data()); }
|
||||
app * mk_mul(ptr_vector<expr> const& args) const { return mk_mul(args.size(), args.data()); }
|
||||
app * mk_mul(expr_ref_vector const& args) const { return mk_mul(args.size(), args.data()); }
|
||||
app * mk_uminus(expr * arg) const { return m_manager.mk_app(arith_family_id, OP_UMINUS, arg); }
|
||||
app * mk_div(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_DIV, arg1, arg2); }
|
||||
app * mk_idiv(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_IDIV, arg1, arg2); }
|
||||
|
|
|
@ -529,19 +529,6 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
|
|||
return nullptr;
|
||||
}
|
||||
return mk_array_ext(arity, domain, parameters[0].get_int());
|
||||
case OP_ARRAY_MAXDIFF:
|
||||
case OP_ARRAY_MINDIFF: {
|
||||
if (num_parameters != 0)
|
||||
m_manager->raise_exception("min/maxdiff don't take any parameters");
|
||||
if (arity != 2 || domain[0] != domain[1] || !is_array_sort(domain[0]) || 1 != get_array_arity(domain[0]))
|
||||
m_manager->raise_exception("min/maxdiff don't take two arrays of same sort and with integer index");
|
||||
sort* idx = get_array_domain(domain[0], 0);
|
||||
arith_util arith(*m_manager);
|
||||
if (!arith.is_int(idx))
|
||||
m_manager->raise_exception("min/maxdiff take integer index domain");
|
||||
return m_manager->mk_func_decl(k == OP_ARRAY_MAXDIFF ? symbol("maxdiff") : symbol("mindiff"),
|
||||
arity, domain, arith.mk_int(), func_decl_info(m_family_id, k));
|
||||
}
|
||||
case OP_ARRAY_DEFAULT:
|
||||
return mk_default(arity, domain);
|
||||
case OP_SET_UNION:
|
||||
|
@ -603,9 +590,6 @@ void array_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol con
|
|||
op_names.push_back(builtin_name("as-array", OP_AS_ARRAY));
|
||||
op_names.push_back(builtin_name("array-ext", OP_ARRAY_EXT));
|
||||
|
||||
op_names.push_back(builtin_name("mindiff", OP_ARRAY_MINDIFF));
|
||||
op_names.push_back(builtin_name("maxdiff", OP_ARRAY_MAXDIFF));
|
||||
|
||||
#if 0
|
||||
op_names.push_back(builtin_name("set-has-size", OP_SET_HAS_SIZE));
|
||||
op_names.push_back(builtin_name("card", OP_SET_CARD));
|
||||
|
|
|
@ -45,8 +45,6 @@ enum array_op_kind {
|
|||
OP_ARRAY_EXT,
|
||||
OP_ARRAY_DEFAULT,
|
||||
OP_ARRAY_MAP,
|
||||
OP_ARRAY_MAXDIFF,
|
||||
OP_ARRAY_MINDIFF,
|
||||
OP_SET_UNION,
|
||||
OP_SET_INTERSECT,
|
||||
OP_SET_DIFFERENCE,
|
||||
|
@ -161,8 +159,6 @@ public:
|
|||
bool is_complement(expr* n) const { return is_app_of(n, m_fid, OP_SET_COMPLEMENT); }
|
||||
bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); }
|
||||
bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); }
|
||||
bool is_maxdiff(expr const* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAXDIFF); }
|
||||
bool is_mindiff(expr const* n) const { return is_app_of(n, m_fid, OP_ARRAY_MINDIFF); }
|
||||
bool is_set_has_size(expr* e) const { return is_app_of(e, m_fid, OP_SET_HAS_SIZE); }
|
||||
bool is_set_card(expr* e) const { return is_app_of(e, m_fid, OP_SET_CARD); }
|
||||
bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); }
|
||||
|
@ -189,8 +185,6 @@ public:
|
|||
bool is_store_ext(expr* e, expr_ref& a, expr_ref_vector& args, expr_ref& value);
|
||||
|
||||
MATCH_BINARY(is_subset);
|
||||
MATCH_BINARY(is_maxdiff);
|
||||
MATCH_BINARY(is_mindiff);
|
||||
};
|
||||
|
||||
class array_util : public array_recognizers {
|
||||
|
@ -213,6 +207,10 @@ public:
|
|||
return mk_store(args.size(), args.data());
|
||||
}
|
||||
|
||||
app* mk_store(ptr_buffer<expr> const& args) const {
|
||||
return mk_store(args.size(), args.data());
|
||||
}
|
||||
|
||||
app * mk_select(unsigned num_args, expr * const * args) const {
|
||||
return m_manager.mk_app(m_fid, OP_SELECT, 0, nullptr, num_args, args);
|
||||
}
|
||||
|
|
|
@ -856,11 +856,11 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_paren
|
|||
case PR_MODUS_PONENS_OEQ: return mk_proof_decl("mp~", k, 2, m_mp_oeq_decl);
|
||||
case PR_TH_LEMMA: return mk_proof_decl("th-lemma", k, num_parents, m_th_lemma_decls);
|
||||
case PR_HYPER_RESOLVE: return mk_proof_decl("hyper-res", k, num_parents, m_hyper_res_decl0);
|
||||
case PR_ASSUMPTION_ADD: return mk_proof_decl("add-assume", k, num_parents, m_assumption_add_decl);
|
||||
case PR_LEMMA_ADD: return mk_proof_decl("add-lemma", k, num_parents, m_lemma_add_decl);
|
||||
case PR_TH_ASSUMPTION_ADD: return mk_proof_decl("add-th-assume", k, num_parents, m_th_assumption_add_decl);
|
||||
case PR_TH_LEMMA_ADD: return mk_proof_decl("add-th-lemma", k, num_parents, m_th_lemma_add_decl);
|
||||
case PR_REDUNDANT_DEL: return mk_proof_decl("del-redundant", k, num_parents, m_redundant_del_decl);
|
||||
case PR_ASSUMPTION_ADD: return mk_proof_decl("assume", k, num_parents, m_assumption_add_decl);
|
||||
case PR_LEMMA_ADD: return mk_proof_decl("infer", k, num_parents, m_lemma_add_decl);
|
||||
case PR_TH_ASSUMPTION_ADD: return mk_proof_decl("th-assume", k, num_parents, m_th_assumption_add_decl);
|
||||
case PR_TH_LEMMA_ADD: return mk_proof_decl("th-lemma", k, num_parents, m_th_lemma_add_decl);
|
||||
case PR_REDUNDANT_DEL: return mk_proof_decl("del", k, num_parents, m_redundant_del_decl);
|
||||
case PR_CLAUSE_TRAIL: return mk_proof_decl("proof-trail", k, num_parents, false);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -1673,6 +1673,7 @@ bool ast_manager::are_distinct(expr* a, expr* b) const {
|
|||
}
|
||||
|
||||
void ast_manager::add_lambda_def(func_decl* f, quantifier* q) {
|
||||
TRACE("model", tout << "add lambda def " << mk_pp(q, *this) << "\n");
|
||||
m_lambda_defs.insert(f, q);
|
||||
f->get_info()->set_lambda(true);
|
||||
inc_ref(q);
|
||||
|
@ -1969,6 +1970,14 @@ app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2,
|
|||
return mk_app(fid, k, 0, nullptr, 3, args);
|
||||
}
|
||||
|
||||
app * ast_manager::mk_app(symbol const& name, unsigned n, expr* const* args, sort* range) {
|
||||
ptr_buffer<sort> sorts;
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
sorts.push_back(args[i]->get_sort());
|
||||
return mk_app(mk_func_decl(name, n, sorts.data(), range), n, args);
|
||||
}
|
||||
|
||||
|
||||
sort * ast_manager::mk_sort(symbol const & name, sort_info * info) {
|
||||
unsigned sz = sort::get_obj_size();
|
||||
void * mem = allocate_node(sz);
|
||||
|
@ -2242,7 +2251,9 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar
|
|||
if (type_error) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "Wrong number of arguments (" << num_args
|
||||
<< ") passed to function " << mk_pp(decl, *this);
|
||||
<< ") passed to function " << mk_pp(decl, *this) << " ";
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
buffer << "\narg: " << mk_pp(args[i], *this) << "\n";
|
||||
throw ast_exception(std::move(buffer).str());
|
||||
}
|
||||
app * r = nullptr;
|
||||
|
|
|
@ -731,6 +731,8 @@ public:
|
|||
unsigned get_num_args() const { return m_num_args; }
|
||||
expr * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; }
|
||||
expr * const * get_args() const { return m_args; }
|
||||
std::tuple<expr*,expr*> args2() const { SASSERT(m_num_args == 2); return {get_arg(0), get_arg(1)}; }
|
||||
std::tuple<expr*,expr*,expr*> args3() const { SASSERT(m_num_args == 3); return {get_arg(0), get_arg(1), get_arg(2)}; }
|
||||
unsigned get_size() const { return get_obj_size(get_num_args()); }
|
||||
expr * const * begin() const { return m_args; }
|
||||
expr * const * end() const { return m_args + m_num_args; }
|
||||
|
@ -1385,6 +1387,7 @@ inline bool is_app_of(expr const * n, family_id fid, decl_kind k) { return n->ge
|
|||
inline bool is_sort_of(sort const * s, family_id fid, decl_kind k) { return s->is_sort_of(fid, k); }
|
||||
inline bool is_uninterp_const(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_num_args() == 0 && to_app(n)->get_family_id() == null_family_id; }
|
||||
inline bool is_uninterp(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_family_id() == null_family_id; }
|
||||
inline bool is_uninterp(func_decl const * n) { return n->get_family_id() == null_family_id; }
|
||||
inline bool is_decl_of(func_decl const * d, family_id fid, decl_kind k) { return d->get_family_id() == fid && d->get_decl_kind() == k; }
|
||||
inline bool is_ground(expr const * n) { return is_app(n) && to_app(n)->is_ground(); }
|
||||
inline bool is_non_ground(expr const * n) { return ( ! is_ground(n)); }
|
||||
|
@ -1628,6 +1631,7 @@ public:
|
|||
void add_lambda_def(func_decl* f, quantifier* q);
|
||||
quantifier* is_lambda_def(func_decl* f);
|
||||
quantifier* is_lambda_def(app* e) { return is_lambda_def(e->get_decl()); }
|
||||
obj_map<func_decl, quantifier*> const& lambda_defs() const { return m_lambda_defs; }
|
||||
|
||||
symbol const& lambda_def_qid() const { return m_lambda_def; }
|
||||
|
||||
|
@ -1881,6 +1885,8 @@ public:
|
|||
return mk_app(decl, 3, args);
|
||||
}
|
||||
|
||||
app * mk_app(symbol const& name, unsigned n, expr* const* args, sort* range);
|
||||
|
||||
app * mk_const(func_decl * decl) {
|
||||
SASSERT(decl->get_arity() == 0);
|
||||
return mk_app(decl, static_cast<unsigned>(0), static_cast<expr**>(nullptr));
|
||||
|
|
|
@ -86,6 +86,7 @@ class ll_printer {
|
|||
default:
|
||||
display_child_ref(n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -64,6 +64,17 @@ void ast_pp_util::display_decls(std::ostream& out) {
|
|||
m_rec_decls = n;
|
||||
}
|
||||
|
||||
void ast_pp_util::reset() {
|
||||
coll.reset();
|
||||
m_removed.reset();
|
||||
m_sorts.clear(0u);
|
||||
m_decls.clear(0u);
|
||||
m_rec_decls.clear(0u);
|
||||
m_is_defined.reset();
|
||||
m_defined.reset();
|
||||
m_defined_lim.reset();
|
||||
}
|
||||
|
||||
void ast_pp_util::display_skolem_decls(std::ostream& out) {
|
||||
ast_smt_pp pp(m);
|
||||
unsigned n = coll.get_num_decls();
|
||||
|
|
|
@ -40,8 +40,7 @@ class ast_pp_util {
|
|||
|
||||
ast_pp_util(ast_manager& m): m(m), m_env(m), m_rec_decls(0), m_decls(0), m_sorts(0), m_defined(m), coll(m) {}
|
||||
|
||||
void reset() { coll.reset(); m_removed.reset(); m_sorts.clear(0u); m_decls.clear(0u); m_rec_decls.clear(0u);
|
||||
m_is_defined.reset(); m_defined.reset(); m_defined_lim.reset(); }
|
||||
void reset();
|
||||
|
||||
void collect(expr* e);
|
||||
|
||||
|
|
|
@ -561,15 +561,18 @@ class smt2_printer {
|
|||
|
||||
void pp_var(var * v) {
|
||||
format * f;
|
||||
if (v->get_idx() < m_var_names.size()) {
|
||||
symbol s = m_var_names[m_var_names.size() - v->get_idx() - 1];
|
||||
unsigned idx = v->get_idx();
|
||||
if (idx < m_var_names.size()) {
|
||||
symbol s;
|
||||
if (m_reverse && idx < m_arity)
|
||||
s = m_var_names[m_var_names.size() - m_arity + idx];
|
||||
else
|
||||
s = m_var_names[m_var_names.size() - idx - 1];
|
||||
std::string vname;
|
||||
if (is_smt2_quoted_symbol (s)) {
|
||||
vname = mk_smt2_quoted_symbol (s);
|
||||
}
|
||||
else {
|
||||
vname = s.str();
|
||||
}
|
||||
if (is_smt2_quoted_symbol (s))
|
||||
vname = mk_smt2_quoted_symbol (s);
|
||||
else
|
||||
vname = s.str();
|
||||
f = mk_string(m(), vname);
|
||||
}
|
||||
else {
|
||||
|
@ -1139,9 +1142,13 @@ public:
|
|||
r = mk_seq1<format**, f2f>(m(), args, args+3, f2f(), cmd);
|
||||
}
|
||||
|
||||
bool m_reverse = false;
|
||||
unsigned m_arity = 0;
|
||||
|
||||
void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd) {
|
||||
void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd, bool reverse) {
|
||||
unsigned len;
|
||||
flet<bool> _reverse(m_reverse, reverse);
|
||||
m_arity = f->get_arity();
|
||||
format * fname = m_env.pp_fdecl_name(f, len);
|
||||
register_var_names(f->get_arity());
|
||||
format * args[4];
|
||||
|
@ -1202,9 +1209,9 @@ void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const &
|
|||
pr(f, r, cmd);
|
||||
}
|
||||
|
||||
void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) {
|
||||
void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd, bool reverse) {
|
||||
smt2_printer pr(env, p);
|
||||
pr(f, e, r, cmd);
|
||||
pr(f, e, r, cmd, reverse);
|
||||
}
|
||||
|
||||
void mk_smt2_format(unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p,
|
||||
|
@ -1251,7 +1258,6 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen
|
|||
if (!f) return out << "null";
|
||||
ast_manager & m = env.get_manager();
|
||||
format_ref r(fm(m));
|
||||
sbuffer<symbol> var_names;
|
||||
mk_smt2_format(f, env, p, r, cmd);
|
||||
if (indent > 0)
|
||||
r = mk_indent(m, indent, r.get());
|
||||
|
@ -1259,18 +1265,25 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen
|
|||
return out;
|
||||
}
|
||||
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) {
|
||||
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd, bool reverse) {
|
||||
if (!f) return out << "null";
|
||||
ast_manager & m = env.get_manager();
|
||||
format_ref r(fm(m));
|
||||
sbuffer<symbol> var_names;
|
||||
mk_smt2_format(f, e, env, p, r, cmd);
|
||||
mk_smt2_format(f, e, env, p, r, cmd, reverse);
|
||||
if (indent > 0)
|
||||
r = mk_indent(m, indent, r.get());
|
||||
pp(out, r.get(), m, p);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) {
|
||||
return ast_smt2_pp(out, f, e, env, p, indent, cmd, false);
|
||||
}
|
||||
|
||||
std::ostream & ast_smt2_pp_rev(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) {
|
||||
return ast_smt2_pp(out, f, e, env, p, indent, cmd, true);
|
||||
}
|
||||
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, unsigned indent,
|
||||
unsigned num_vars, char const * var_prefix) {
|
||||
|
|
|
@ -104,7 +104,8 @@ std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & e
|
|||
unsigned num_vars = 0, char const * var_prefix = nullptr);
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0);
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun");
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun");
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun", bool reverse = false);
|
||||
std::ostream & ast_smt2_pp_rev(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun");
|
||||
std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref());
|
||||
std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector<std::pair<func_decl*, expr*>> const& funs, smt2_pp_environment & env, params_ref const & p = params_ref());
|
||||
|
||||
|
|
|
@ -101,6 +101,8 @@ expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx);
|
|||
*/
|
||||
expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args);
|
||||
app * mk_and(ast_manager & m, unsigned num_args, app * const * args);
|
||||
inline expr * mk_and(ast_manager & m, ptr_vector<expr> const& args) { return mk_and(m, args.size(), args.data()); }
|
||||
inline expr * mk_and(ast_manager & m, ptr_buffer<expr> const& args) { return mk_and(m, args.size(), args.data()); }
|
||||
inline expr * mk_and(ast_manager & m, expr* a, expr* b) { expr* args[2] = { a, b }; return mk_and(m, 2, args); }
|
||||
inline app_ref mk_and(app_ref_vector const& args) { return app_ref(mk_and(args.get_manager(), args.size(), args.data()), args.get_manager()); }
|
||||
inline expr_ref mk_and(expr_ref_vector const& args) { return expr_ref(mk_and(args.get_manager(), args.size(), args.data()), args.get_manager()); }
|
||||
|
|
|
@ -411,6 +411,11 @@ public:
|
|||
app * mk_numeral(rational const & val, sort* s) const;
|
||||
app * mk_numeral(rational const & val, unsigned bv_size) const;
|
||||
app * mk_numeral(uint64_t u, unsigned bv_size) const { return mk_numeral(rational(u, rational::ui64()), bv_size); }
|
||||
app * mk_zero(sort* s) const { return mk_numeral(rational::zero(), s); }
|
||||
app * mk_zero(unsigned bv_size) const { return mk_numeral(rational::zero(), bv_size); }
|
||||
app * mk_one(sort* s) const { return mk_numeral(rational::one(), s); }
|
||||
app * mk_one(unsigned bv_size) const { return mk_numeral(rational::one(), bv_size); }
|
||||
|
||||
sort * mk_sort(unsigned bv_size);
|
||||
|
||||
unsigned get_bv_size(sort const * s) const {
|
||||
|
@ -430,6 +435,9 @@ public:
|
|||
}
|
||||
app * mk_concat(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_CONCAT, num, args); }
|
||||
app * mk_concat(expr_ref_vector const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
|
||||
app * mk_concat(expr_ref_buffer const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
|
||||
app * mk_concat(ptr_buffer<expr> const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
|
||||
app * mk_concat(ptr_vector<expr> const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
|
||||
app * mk_bv_or(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BOR, num, args); }
|
||||
app * mk_bv_and(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BAND, num, args); }
|
||||
app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); }
|
||||
|
@ -445,8 +453,17 @@ public:
|
|||
app * mk_bv_srem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSREM, arg1, arg2); }
|
||||
app * mk_bv_smod(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSMOD, arg1, arg2); }
|
||||
app * mk_bv_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BADD, arg1, arg2); }
|
||||
app * mk_bv_add(ptr_buffer<expr> const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); }
|
||||
app * mk_bv_add(ptr_vector<expr> const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); }
|
||||
app * mk_bv_add(expr_ref_vector const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); }
|
||||
app * mk_bv_add(expr_ref_buffer const & args) const { return m_manager.mk_app(get_fid(), OP_BADD, args.size(), args.data()); }
|
||||
app * mk_bv_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSUB, arg1, arg2); }
|
||||
app * mk_bv_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BMUL, arg1, arg2); }
|
||||
app * mk_bv_mul(unsigned n, expr* const* args) const { return m_manager.mk_app(get_fid(), OP_BMUL, n, args); }
|
||||
app* mk_bv_mul(ptr_buffer<expr> const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); }
|
||||
app* mk_bv_mul(ptr_vector<expr> const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); }
|
||||
app* mk_bv_mul(expr_ref_vector const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); }
|
||||
app* mk_bv_mul(expr_ref_buffer const& args) const { return m_manager.mk_app(get_fid(), OP_BMUL, args.size(), args.data()); }
|
||||
app * mk_bv_udiv(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUDIV, arg1, arg2); }
|
||||
app * mk_bv_udiv_i(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUDIV_I, arg1, arg2); }
|
||||
app * mk_bv_udiv0(expr * arg) const { return m_manager.mk_app(get_fid(), OP_BUDIV0, arg); }
|
||||
|
|
12
src/ast/converters/CMakeLists.txt
Normal file
12
src/ast/converters/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
z3_add_component(converters
|
||||
SOURCES
|
||||
expr_inverter.cpp
|
||||
equiv_proof_converter.cpp
|
||||
generic_model_converter.cpp
|
||||
horn_subsume_model_converter.cpp
|
||||
model_converter.cpp
|
||||
proof_converter.cpp
|
||||
replace_proof_converter.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
model
|
||||
)
|
|
@ -17,7 +17,7 @@ Revision History:
|
|||
|
||||
--*/
|
||||
|
||||
#include "tactic/equiv_proof_converter.h"
|
||||
#include "ast/converters/equiv_proof_converter.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
|
|
@ -23,7 +23,7 @@ Revision History:
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "tactic/replace_proof_converter.h"
|
||||
#include "ast/converters/replace_proof_converter.h"
|
||||
|
||||
class equiv_proof_converter : public proof_converter {
|
||||
ast_manager& m;
|
858
src/ast/converters/expr_inverter.cpp
Normal file
858
src/ast/converters/expr_inverter.cpp
Normal file
|
@ -0,0 +1,858 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
expr_inverter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
inverter interface and instance
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-11
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_ll_pp.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/converters/expr_inverter.h"
|
||||
|
||||
class basic_expr_inverter : public iexpr_inverter {
|
||||
iexpr_inverter& inv;
|
||||
|
||||
bool process_eq(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) {
|
||||
expr* v;
|
||||
expr* t;
|
||||
if (uncnstr(arg1))
|
||||
v = arg1, t = arg2;
|
||||
else if (uncnstr(arg2))
|
||||
v = arg2, t = arg1;
|
||||
else
|
||||
return false;
|
||||
|
||||
expr_ref d(m);
|
||||
if (!inv.mk_diff(t, d))
|
||||
return false;
|
||||
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc)
|
||||
add_def(v, m.mk_ite(r, t, d));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
basic_expr_inverter(ast_manager& m, iexpr_inverter& inv) : iexpr_inverter(m), inv(inv) {}
|
||||
|
||||
family_id get_fid() const override { return m.get_basic_family_id(); }
|
||||
|
||||
/**
|
||||
* if (c, x, x') -> fresh
|
||||
* x := fresh
|
||||
* x' := fresh
|
||||
*
|
||||
* if (x, x', e) -> fresh
|
||||
* x := true
|
||||
* x' := fresh
|
||||
*
|
||||
* if (x, t, x') -> fresh
|
||||
* x := false
|
||||
* x' := fresh
|
||||
*
|
||||
* not x -> fresh
|
||||
* x := not fresh
|
||||
*
|
||||
* x & x' -> fresh
|
||||
* x := fresh
|
||||
* x' := true
|
||||
*
|
||||
* x or x' -> fresh
|
||||
* x := fresh
|
||||
* x' := false
|
||||
*
|
||||
* x = t -> fresh
|
||||
* x := if(fresh, t, diff(t))
|
||||
* where diff is a diagnonalization function available in domains of size > 1.
|
||||
*
|
||||
*/
|
||||
|
||||
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
|
||||
SASSERT(f->get_family_id() == m.get_basic_family_id());
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_ITE:
|
||||
SASSERT(num == 3);
|
||||
if (uncnstr(args[1]) && uncnstr(args[2])) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
add_def(args[1], r);
|
||||
add_def(args[2], r);
|
||||
return true;
|
||||
}
|
||||
if (uncnstr(args[0]) && uncnstr(args[1])) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
add_def(args[0], m.mk_true());
|
||||
add_def(args[1], r);
|
||||
return true;
|
||||
}
|
||||
if (uncnstr(args[0]) && uncnstr(args[2])) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
add_def(args[0], m.mk_false());
|
||||
add_def(args[2], r);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case OP_NOT:
|
||||
SASSERT(num == 1);
|
||||
if (uncnstr(args[0])) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
add_def(args[0], m.mk_not(r));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case OP_AND:
|
||||
if (num > 0 && uncnstr(num, args)) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
add_defs(num, args, r, m.mk_true());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case OP_OR:
|
||||
if (num > 0 && uncnstr(num, args)) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
add_defs(num, args, r, m.mk_false());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case OP_EQ:
|
||||
SASSERT(num == 2);
|
||||
return process_eq(f, args[0], args[1], r);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mk_diff(expr* t, expr_ref& r) override {
|
||||
SASSERT(m.is_bool(t));
|
||||
r = mk_not(m, t);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class arith_expr_inverter : public iexpr_inverter {
|
||||
arith_util a;
|
||||
public:
|
||||
|
||||
arith_expr_inverter(ast_manager& m) : iexpr_inverter(m), a(m) {}
|
||||
|
||||
family_id get_fid() const override { return a.get_family_id(); }
|
||||
|
||||
bool process_le_ge(func_decl* f, expr* arg1, expr* arg2, bool le, expr_ref& r) {
|
||||
expr* v;
|
||||
expr* t;
|
||||
if (uncnstr(arg1)) {
|
||||
v = arg1;
|
||||
t = arg2;
|
||||
}
|
||||
else if (uncnstr(arg2)) {
|
||||
v = arg2;
|
||||
t = arg1;
|
||||
le = !le;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc) {
|
||||
// v = ite(u, t, t + 1) if le
|
||||
// v = ite(u, t, t - 1) if !le
|
||||
add_def(v, m.mk_ite(r, t, a.mk_add(t, a.mk_numeral(rational(le ? 1 : -1), arg1->get_sort()))));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process_add(unsigned num, expr* const* args, expr_ref& u) {
|
||||
if (num == 0)
|
||||
return false;
|
||||
unsigned i;
|
||||
expr* v = nullptr;
|
||||
for (i = 0; i < num; i++) {
|
||||
expr* arg = args[i];
|
||||
if (uncnstr(arg)) {
|
||||
v = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (v == nullptr)
|
||||
return false;
|
||||
mk_fresh_uncnstr_var_for(v->get_sort(), u);
|
||||
if (!m_mc)
|
||||
return true;
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned j = 0; j < num; j++)
|
||||
if (j != i)
|
||||
new_args.push_back(args[j]);
|
||||
|
||||
if (new_args.empty())
|
||||
add_def(v, u);
|
||||
else {
|
||||
expr* rest = a.mk_add(new_args);
|
||||
add_def(v, a.mk_sub(u, rest));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process_arith_mul(unsigned num, expr* const* args, expr_ref & u) {
|
||||
if (num == 0)
|
||||
return false;
|
||||
sort* s = args[0]->get_sort();
|
||||
if (uncnstr(num, args)) {
|
||||
mk_fresh_uncnstr_var_for(s, u);
|
||||
if (m_mc)
|
||||
add_defs(num, args, u, a.mk_numeral(rational(1), s));
|
||||
return true;
|
||||
}
|
||||
// c * v case for reals
|
||||
bool is_int;
|
||||
rational val;
|
||||
if (num == 2 && uncnstr(args[1]) && a.is_numeral(args[0], val, is_int) && !is_int) {
|
||||
if (val.is_zero())
|
||||
return false;
|
||||
mk_fresh_uncnstr_var_for(s, u);
|
||||
if (m_mc) {
|
||||
val = rational(1) / val;
|
||||
add_def(args[1], a.mk_mul(a.mk_numeral(val, false), u));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
|
||||
SASSERT(f->get_family_id() == a.get_family_id());
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_ADD:
|
||||
return process_add(num, args, r);
|
||||
case OP_MUL:
|
||||
return process_arith_mul(num, args, r);
|
||||
case OP_LE:
|
||||
SASSERT(num == 2);
|
||||
return process_le_ge(f, args[0], args[1], true, r);
|
||||
case OP_GE:
|
||||
SASSERT(num == 2);
|
||||
return process_le_ge(f, args[0], args[1], false, r);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool mk_diff(expr* t, expr_ref& r) override {
|
||||
SASSERT(a.is_int_real(t));
|
||||
r = a.mk_add(t, a.mk_numeral(rational(1), a.is_int(t)));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class bv_expr_inverter : public iexpr_inverter {
|
||||
bv_util bv;
|
||||
|
||||
bool process_add(unsigned num, expr* const* args, expr_ref& u) {
|
||||
if (num == 0)
|
||||
return false;
|
||||
unsigned i;
|
||||
expr* v = nullptr;
|
||||
for (i = 0; i < num; i++) {
|
||||
expr* arg = args[i];
|
||||
if (uncnstr(arg)) {
|
||||
v = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!v)
|
||||
return false;
|
||||
mk_fresh_uncnstr_var_for(v->get_sort(), u);
|
||||
if (!m_mc)
|
||||
return true;
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned j = 0; j < num; j++)
|
||||
if (j != i)
|
||||
new_args.push_back(args[j]);
|
||||
|
||||
if (new_args.empty())
|
||||
add_def(v, u);
|
||||
else {
|
||||
expr* rest = bv.mk_bv_add(new_args);
|
||||
add_def(v, bv.mk_bv_sub(u, rest));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process_bv_mul(func_decl* f, unsigned num, expr* const* args, expr_ref& r) {
|
||||
if (num == 0)
|
||||
return false;
|
||||
if (uncnstr(num, args)) {
|
||||
sort* s = args[0]->get_sort();
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc)
|
||||
add_defs(num, args, r, bv.mk_one(s));
|
||||
return true;
|
||||
}
|
||||
// c * v (c is odd) case
|
||||
unsigned sz;
|
||||
rational val;
|
||||
rational inv;
|
||||
if (num == 2 &&
|
||||
uncnstr(args[1]) &&
|
||||
bv.is_numeral(args[0], val, sz) &&
|
||||
val.mult_inverse(sz, inv)) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc)
|
||||
add_def(args[1], bv.mk_bv_mul(bv.mk_numeral(inv, sz), r));
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// x * K -> fresh[hi-sh-1:0] ++ 0...0
|
||||
// where sh = parity of K
|
||||
// then x -> J^-1*fresh
|
||||
// where J = K >> sh
|
||||
// Because x * K = fresh * K * J^-1 = fresh * 2^sh = fresh[hi-sh-1:0] ++ 0...0
|
||||
//
|
||||
if (num == 2 &&
|
||||
uncnstr(args[1]) &&
|
||||
bv.is_numeral(args[0], val, sz) &&
|
||||
val.is_pos()) {
|
||||
unsigned sh = 0;
|
||||
while (val.is_even()) {
|
||||
val /= rational(2);
|
||||
++sh;
|
||||
}
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (sh > 0)
|
||||
r = bv.mk_concat(bv.mk_extract(sz - sh - 1, 0, r), bv.mk_zero(sh));
|
||||
|
||||
if (m_mc) {
|
||||
rational inv_r;
|
||||
VERIFY(val.mult_inverse(sz, inv_r));
|
||||
add_def(args[1], bv.mk_bv_mul(bv.mk_numeral(inv_r, sz), r));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// assume x is unconstrained, we can handle general multiplication as follows:
|
||||
// x * y -> if y = 0 then y else fresh << parity(y)
|
||||
// and x -> if y = 0 then y else (y >> parity(y))^-1
|
||||
// parity can be defined using a "giant" ite expression.
|
||||
//
|
||||
|
||||
#if 0
|
||||
for (unsigned i = 0; i < num; ++i)
|
||||
if (uncnstr(args[i]))
|
||||
IF_VERBOSE(11, verbose_stream() << "MISSED mult-unconstrained " << mk_bounded_pp(args[i], m) << "\n");
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool process_extract(func_decl* f, expr* arg, expr_ref& r) {
|
||||
if (!uncnstr(arg))
|
||||
return false;
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (!m_mc)
|
||||
return true;
|
||||
unsigned high = bv.get_extract_high(f);
|
||||
unsigned low = bv.get_extract_low(f);
|
||||
unsigned bv_size = bv.get_bv_size(arg->get_sort());
|
||||
if (bv_size == high - low + 1)
|
||||
add_def(arg, r);
|
||||
else {
|
||||
ptr_buffer<expr> args;
|
||||
if (high < bv_size - 1)
|
||||
args.push_back(bv.mk_zero(bv_size - high - 1));
|
||||
args.push_back(r);
|
||||
if (low > 0)
|
||||
args.push_back(bv.mk_zero(low));
|
||||
add_def(arg, bv.mk_concat(args.size(), args.data()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process_bv_div(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) {
|
||||
if (uncnstr(arg1) && uncnstr(arg2)) {
|
||||
sort* s = arg1->get_sort();
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc) {
|
||||
add_def(arg1, r);
|
||||
add_def(arg2, bv.mk_one(s));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool process_concat(func_decl* f, unsigned num, expr* const* args, expr_ref& r) {
|
||||
if (num == 0)
|
||||
return false;
|
||||
if (!uncnstr(num, args))
|
||||
return false;
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc) {
|
||||
unsigned i = num;
|
||||
unsigned low = 0;
|
||||
while (i > 0) {
|
||||
--i;
|
||||
expr* arg = args[i];
|
||||
unsigned sz = bv.get_bv_size(arg);
|
||||
add_def(arg, bv.mk_extract(low + sz - 1, low, r));
|
||||
low += sz;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process_bv_le(func_decl* f, expr* arg1, expr* arg2, bool is_signed, expr_ref& r) {
|
||||
unsigned bv_sz = bv.get_bv_size(arg1);
|
||||
if (uncnstr(arg1) && uncnstr(arg2)) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc) {
|
||||
add_def(arg1, m.mk_ite(r, bv.mk_zero(bv_sz), bv.mk_one(bv_sz)));
|
||||
add_def(arg2, bv.mk_zero(bv_sz));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (uncnstr(arg1)) {
|
||||
// v <= t
|
||||
expr* v = arg1;
|
||||
expr* t = arg2;
|
||||
// v <= t ---> (u or t == MAX) u is fresh
|
||||
// add definition v = ite(u or t == MAX, t, t+1)
|
||||
|
||||
rational MAX;
|
||||
if (is_signed)
|
||||
MAX = rational::power_of_two(bv_sz - 1) - rational(1);
|
||||
else
|
||||
MAX = rational::power_of_two(bv_sz) - rational(1);
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
r = m.mk_or(r, m.mk_eq(t, bv.mk_numeral(MAX, bv_sz)));
|
||||
if (m_mc)
|
||||
add_def(v, m.mk_ite(r, t, bv.mk_bv_add(t, bv.mk_one(bv_sz))));
|
||||
return true;
|
||||
}
|
||||
if (uncnstr(arg2)) {
|
||||
// v >= t
|
||||
expr* v = arg2;
|
||||
expr* t = arg1;
|
||||
// v >= t ---> (u ot t == MIN) u is fresh
|
||||
// add definition v = ite(u or t == MIN, t, t-1)
|
||||
rational MIN;
|
||||
if (is_signed)
|
||||
MIN = -rational::power_of_two(bv_sz - 1);
|
||||
else
|
||||
MIN = rational(0);
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
r = m.mk_or(r, m.mk_eq(t, bv.mk_numeral(MIN, bv_sz)));
|
||||
if (m_mc)
|
||||
add_def(v, m.mk_ite(r, t, bv.mk_bv_sub(t, bv.mk_one(bv_sz))));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool process_bvnot(expr* e, expr_ref& r) {
|
||||
if (!uncnstr(e))
|
||||
return false;
|
||||
mk_fresh_uncnstr_var_for(e->get_sort(), r);
|
||||
if (m_mc)
|
||||
add_def(e, bv.mk_bv_not(r));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process_shift(func_decl* f, expr* arg1, expr* arg2, expr_ref& r) {
|
||||
if (uncnstr(arg1) && uncnstr(arg2)) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc) {
|
||||
add_def(arg1, r);
|
||||
add_def(arg2, bv.mk_zero(arg2->get_sort()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
bv_expr_inverter(ast_manager& m) : iexpr_inverter(m), bv(m) {}
|
||||
|
||||
family_id get_fid() const override { return bv.get_family_id(); }
|
||||
|
||||
/**
|
||||
* x + t -> fresh
|
||||
* x := fresh - t
|
||||
*
|
||||
* x * x' * x'' -> fresh
|
||||
* x := fresh
|
||||
* x', x'' := 1
|
||||
*
|
||||
* c * x -> fresh, c is odd
|
||||
* x := fresh*c^-1
|
||||
*
|
||||
* x[sz-1:0] -> fresh
|
||||
* x := fresh
|
||||
*
|
||||
* x[hi:lo] -> fresh
|
||||
* x := fresh1 ++ fresh ++ fresh2
|
||||
*
|
||||
* x udiv x', x sdiv x' -> fresh
|
||||
* x' := 1
|
||||
* x := fresh
|
||||
*
|
||||
* x ++ x' ++ x'' -> fresh
|
||||
* x := fresh[hi1:lo1]
|
||||
* x' := fresh[hi2:lo2]
|
||||
* x'' := fresh[hi3:lo3]
|
||||
*
|
||||
* x <= t -> fresh or t == MAX
|
||||
* x := if(fresh, t, t + 1)
|
||||
* t <= x -> fresh or t == MIN
|
||||
* x := if(fresh, t, t - 1)
|
||||
*
|
||||
* ~x -> fresh
|
||||
* x := ~fresh
|
||||
*
|
||||
* x | y -> fresh
|
||||
* x := fresh
|
||||
* y := 0
|
||||
*
|
||||
*/
|
||||
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
|
||||
SASSERT(f->get_family_id() == bv.get_family_id());
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_BADD:
|
||||
return process_add(num, args, r);
|
||||
case OP_BMUL:
|
||||
return process_bv_mul(f, num, args, r);
|
||||
case OP_BSDIV:
|
||||
case OP_BUDIV:
|
||||
case OP_BSDIV_I:
|
||||
case OP_BUDIV_I:
|
||||
SASSERT(num == 2);
|
||||
return process_bv_div(f, args[0], args[1], r);
|
||||
case OP_SLEQ:
|
||||
SASSERT(num == 2);
|
||||
return process_bv_le(f, args[0], args[1], true, r);
|
||||
case OP_ULEQ:
|
||||
SASSERT(num == 2);
|
||||
return process_bv_le(f, args[0], args[1], false, r);
|
||||
case OP_CONCAT:
|
||||
return process_concat(f, num, args, r);
|
||||
case OP_EXTRACT:
|
||||
SASSERT(num == 1);
|
||||
return process_extract(f, args[0], r);
|
||||
case OP_BNOT:
|
||||
SASSERT(num == 1);
|
||||
return process_bvnot(args[0], r);
|
||||
case OP_BOR:
|
||||
if (num > 0 && uncnstr(num, args)) {
|
||||
sort* s = args[0]->get_sort();
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc)
|
||||
add_defs(num, args, r, bv.mk_zero(s));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case OP_BAND:
|
||||
if (num > 0 && uncnstr(num, args)) {
|
||||
sort* s = args[0]->get_sort();
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc)
|
||||
add_defs(num, args, r, bv.mk_numeral(rational::power_of_two(bv.get_bv_size(s)) - 1, s));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case OP_BSHL:
|
||||
case OP_BASHR:
|
||||
case OP_BLSHR:
|
||||
return process_shift(f, args[0], args[1], r);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool mk_diff(expr* t, expr_ref& r) override {
|
||||
SASSERT(bv.is_bv(t));
|
||||
r = bv.mk_bv_not(t);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* F[select(x, i)] -> F[fresh]
|
||||
* x := const(fresh)
|
||||
|
||||
* F[store(x, ..., x')] -> F[fresh]
|
||||
* x' := select(x, ...)
|
||||
* x := fresh
|
||||
*/
|
||||
|
||||
class array_expr_inverter : public iexpr_inverter {
|
||||
array_util a;
|
||||
iexpr_inverter& inv;
|
||||
public:
|
||||
array_expr_inverter(ast_manager& m, iexpr_inverter& s) : iexpr_inverter(m), a(m), inv(s) {}
|
||||
|
||||
family_id get_fid() const override { return a.get_family_id(); }
|
||||
|
||||
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
|
||||
SASSERT(f->get_family_id() == a.get_family_id());
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_SELECT:
|
||||
if (uncnstr(args[0])) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
sort* s = args[0]->get_sort();
|
||||
if (m_mc)
|
||||
add_def(args[0], a.mk_const_array(s, r));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case OP_STORE:
|
||||
if (uncnstr(args[0]) && uncnstr(args[num - 1])) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
if (m_mc) {
|
||||
add_def(args[num - 1], m.mk_app(a.get_family_id(), OP_SELECT, num - 1, args));
|
||||
add_def(args[0], r);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool mk_diff(expr* t, expr_ref& r) override {
|
||||
sort* s = t->get_sort();
|
||||
SASSERT(a.is_array(s));
|
||||
if (m.is_uninterp(get_array_range(s)))
|
||||
return false;
|
||||
unsigned arity = get_array_arity(s);
|
||||
for (unsigned i = 0; i < arity; i++)
|
||||
if (m.is_uninterp(get_array_domain(s, i)))
|
||||
return false;
|
||||
// building
|
||||
// r = (store t i1 ... in d)
|
||||
// where i1 ... in are arbitrary values
|
||||
// and d is a term different from (select t i1 ... in)
|
||||
expr_ref_vector new_args(m);
|
||||
new_args.push_back(t);
|
||||
for (unsigned i = 0; i < arity; i++)
|
||||
new_args.push_back(m.get_some_value(get_array_domain(s, i)));
|
||||
expr_ref sel(m);
|
||||
sel = a.mk_select(new_args);
|
||||
expr_ref diff_sel(m);
|
||||
if (!inv.mk_diff(sel, diff_sel))
|
||||
return false;
|
||||
new_args.push_back(diff_sel);
|
||||
r = a.mk_store(new_args);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class dt_expr_inverter : public iexpr_inverter {
|
||||
datatype_util dt;
|
||||
public:
|
||||
|
||||
dt_expr_inverter(ast_manager& m) : iexpr_inverter(m), dt(m) {}
|
||||
|
||||
family_id get_fid() const override { return dt.get_family_id(); }
|
||||
/**
|
||||
* head(x) -> fresh
|
||||
* x := cons(fresh, arb)
|
||||
*/
|
||||
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
|
||||
if (dt.is_accessor(f)) {
|
||||
SASSERT(num == 1);
|
||||
if (uncnstr(args[0])) {
|
||||
if (!m_mc) {
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
return true;
|
||||
}
|
||||
func_decl* c = dt.get_accessor_constructor(f);
|
||||
for (unsigned i = 0; i < c->get_arity(); i++)
|
||||
if (!m.is_fully_interp(c->get_domain(i)))
|
||||
return false;
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
ptr_vector<func_decl> const& accs = *dt.get_constructor_accessors(c);
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned i = 0; i < accs.size(); i++) {
|
||||
if (accs[i] == f)
|
||||
new_args.push_back(r);
|
||||
else
|
||||
new_args.push_back(m.get_some_value(c->get_domain(i)));
|
||||
}
|
||||
add_def(args[0], m.mk_app(c, new_args));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mk_diff(expr* t, expr_ref& r) override {
|
||||
// In the current implementation, I only handle the case where
|
||||
// the datatype has a recursive constructor.
|
||||
sort* s = t->get_sort();
|
||||
ptr_vector<func_decl> const& constructors = *dt.get_datatype_constructors(s);
|
||||
for (func_decl* constructor : constructors) {
|
||||
unsigned num = constructor->get_arity();
|
||||
unsigned target = UINT_MAX;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
sort* s_arg = constructor->get_domain(i);
|
||||
if (s == s_arg) {
|
||||
target = i;
|
||||
continue;
|
||||
}
|
||||
if (m.is_uninterp(s_arg))
|
||||
break;
|
||||
}
|
||||
if (target == UINT_MAX)
|
||||
continue;
|
||||
// use the constructor the distinct term constructor(...,t,...)
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
if (i == target)
|
||||
new_args.push_back(t);
|
||||
else
|
||||
new_args.push_back(m.get_some_value(constructor->get_domain(i)));
|
||||
}
|
||||
r = m.mk_app(constructor, new_args);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
expr_inverter::~expr_inverter() {
|
||||
for (auto* v : m_inverters)
|
||||
dealloc(v);
|
||||
}
|
||||
|
||||
|
||||
bool iexpr_inverter::uncnstr(unsigned num, expr * const * args) const {
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
if (!m_is_var(args[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Create a fresh variable for abstracting (f args[0] ... args[num-1])
|
||||
Return true if it a new variable was created, and false if the variable already existed for this
|
||||
application. Store the variable in v
|
||||
*/
|
||||
void iexpr_inverter::mk_fresh_uncnstr_var_for(sort * s, expr_ref & v) {
|
||||
v = m.mk_fresh_const(nullptr, s);
|
||||
if (m_mc)
|
||||
m_mc->hide(v);
|
||||
}
|
||||
|
||||
void iexpr_inverter::add_def(expr * v, expr * def) {
|
||||
expr_ref _v(v, m);
|
||||
expr_ref _d(def, m);
|
||||
if (!m_mc)
|
||||
return;
|
||||
SASSERT(uncnstr(v));
|
||||
SASSERT(to_app(v)->get_num_args() == 0);
|
||||
m_mc->add(v, def);
|
||||
}
|
||||
|
||||
void iexpr_inverter::add_defs(unsigned num, expr* const* args, expr* u, expr* identity) {
|
||||
expr_ref _id(identity, m);
|
||||
if (!m_mc)
|
||||
return;
|
||||
add_def(args[0], u);
|
||||
for (unsigned i = 1; i < num; i++)
|
||||
add_def(args[i], identity);
|
||||
}
|
||||
|
||||
|
||||
expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) {
|
||||
auto add = [&](iexpr_inverter* i) {
|
||||
m_inverters.setx(i->get_fid(), i, nullptr);
|
||||
};
|
||||
add(alloc(arith_expr_inverter, m));
|
||||
add(alloc(bv_expr_inverter, m));
|
||||
add(alloc(array_expr_inverter, m, *this));
|
||||
add(alloc(dt_expr_inverter, m));
|
||||
add(alloc(basic_expr_inverter, m, *this));
|
||||
}
|
||||
|
||||
|
||||
bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& new_expr) {
|
||||
if (num == 0)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
if (!is_ground(args[i]))
|
||||
return false;
|
||||
|
||||
family_id fid = f->get_family_id();
|
||||
if (fid == null_family_id)
|
||||
return false;
|
||||
|
||||
auto* p = m_inverters.get(fid, nullptr);
|
||||
return p && (*p)(f, num, args, new_expr);
|
||||
}
|
||||
|
||||
bool expr_inverter::mk_diff(expr* t, expr_ref& r) {
|
||||
sort * s = t->get_sort();
|
||||
if (!m.is_fully_interp(s))
|
||||
return false;
|
||||
|
||||
// If the interpreted sort has only one element,
|
||||
// then it is unsound to eliminate the unconstrained variable in the equality
|
||||
sort_size sz = s->get_num_elements();
|
||||
if (sz.is_finite() && sz.size() <= 1)
|
||||
return false;
|
||||
|
||||
if (!m_mc) {
|
||||
// easy case, model generation is disabled.
|
||||
mk_fresh_uncnstr_var_for(s, r);
|
||||
return true;
|
||||
}
|
||||
|
||||
family_id fid = s->get_family_id();
|
||||
auto* p = m_inverters.get(fid, nullptr);
|
||||
return p && p->mk_diff(t, r);
|
||||
}
|
||||
|
||||
void expr_inverter::set_is_var(std::function<bool(expr*)>& is_var) {
|
||||
for (auto* p : m_inverters)
|
||||
if (p)
|
||||
p->set_is_var(is_var);
|
||||
}
|
||||
|
||||
void expr_inverter::set_model_converter(generic_model_converter* mc) {
|
||||
m_mc = mc;
|
||||
for (auto* p : m_inverters)
|
||||
if (p)
|
||||
p->set_model_converter(mc);
|
||||
}
|
||||
|
||||
void expr_inverter::set_produce_proofs(bool pr) {
|
||||
m_produce_proofs = pr;
|
||||
for (auto* p : m_inverters)
|
||||
if (p)
|
||||
p->set_produce_proofs(pr);
|
||||
}
|
60
src/ast/converters/expr_inverter.h
Normal file
60
src/ast/converters/expr_inverter.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
expr_inverter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
inverter interface and instance
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-11
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
|
||||
class iexpr_inverter {
|
||||
protected:
|
||||
ast_manager& m;
|
||||
std::function<bool(expr*)> m_is_var;
|
||||
generic_model_converter_ref m_mc;
|
||||
bool m_produce_proofs = false;
|
||||
|
||||
bool uncnstr(expr* e) const { return m_is_var(e); }
|
||||
bool uncnstr(unsigned num, expr * const * args) const;
|
||||
void mk_fresh_uncnstr_var_for(sort* s, expr_ref& v);
|
||||
void mk_fresh_uncnstr_var_for(func_decl* f, expr_ref& v) { mk_fresh_uncnstr_var_for(f->get_range(), v); }
|
||||
void add_def(expr * v, expr * def);
|
||||
void add_defs(unsigned num, expr * const * args, expr * u, expr * identity);
|
||||
|
||||
public:
|
||||
iexpr_inverter(ast_manager& m): m(m) {}
|
||||
virtual ~iexpr_inverter() {}
|
||||
virtual void set_is_var(std::function<bool(expr*)>& is_var) { m_is_var = is_var; }
|
||||
virtual void set_model_converter(generic_model_converter* mc) { m_mc = mc; }
|
||||
virtual void set_produce_proofs(bool p) { m_produce_proofs = true; }
|
||||
|
||||
virtual bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr) = 0;
|
||||
virtual bool mk_diff(expr* t, expr_ref& r) = 0;
|
||||
virtual family_id get_fid() const = 0;
|
||||
};
|
||||
|
||||
class expr_inverter : public iexpr_inverter {
|
||||
ptr_vector<iexpr_inverter> m_inverters;
|
||||
|
||||
public:
|
||||
expr_inverter(ast_manager& m);
|
||||
~expr_inverter() override;
|
||||
bool operator()(func_decl* f, unsigned n, expr* const* args, expr_ref& new_expr) override;
|
||||
bool mk_diff(expr* t, expr_ref& r) override;
|
||||
void set_is_var(std::function<bool(expr*)>& is_var) override;
|
||||
void set_model_converter(generic_model_converter* mc) override;
|
||||
void set_produce_proofs(bool p) override;
|
||||
family_id get_fid() const override { return null_family_id; }
|
||||
};
|
|
@ -24,19 +24,13 @@ Notes:
|
|||
#include "ast/occurs.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "model/model_v2_pp.h"
|
||||
#include "model/model_evaluator.h"
|
||||
|
||||
|
||||
generic_model_converter::~generic_model_converter() {
|
||||
}
|
||||
|
||||
|
||||
void generic_model_converter::add(func_decl * d, expr* e) {
|
||||
VERIFY(e);
|
||||
VERIFY(d->get_range() == e->get_sort());
|
||||
m_first_idx.insert_if_not_there(d, m_entries.size());
|
||||
m_entries.push_back(entry(d, e, m, ADD));
|
||||
}
|
||||
|
||||
|
@ -44,7 +38,7 @@ void generic_model_converter::operator()(model_ref & md) {
|
|||
TRACE("model_converter", tout << "before generic_model_converter\n"; model_v2_pp(tout, *md); display(tout););
|
||||
|
||||
model_evaluator ev(*(md.get()));
|
||||
ev.set_model_completion(true);
|
||||
ev.set_model_completion(m_completion);
|
||||
ev.set_expand_array_equalities(false);
|
||||
expr_ref val(m);
|
||||
unsigned arity;
|
||||
|
@ -84,7 +78,7 @@ void generic_model_converter::operator()(model_ref & md) {
|
|||
}
|
||||
if (reset_ev) {
|
||||
ev.reset();
|
||||
ev.set_model_completion(true);
|
||||
ev.set_model_completion(m_completion);
|
||||
ev.set_expand_array_equalities(false);
|
||||
}
|
||||
break;
|
|
@ -19,9 +19,10 @@ Notes:
|
|||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "tactic/model_converter.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
|
||||
class generic_model_converter : public model_converter {
|
||||
public:
|
||||
enum instruction { HIDE, ADD };
|
||||
struct entry {
|
||||
func_decl_ref m_f;
|
||||
|
@ -30,18 +31,16 @@ class generic_model_converter : public model_converter {
|
|||
entry(func_decl* f, expr* d, ast_manager& m, instruction i):
|
||||
m_f(f, m), m_def(d, m), m_instruction(i) {}
|
||||
};
|
||||
private:
|
||||
ast_manager& m;
|
||||
std::string m_orig;
|
||||
vector<entry> m_entries;
|
||||
obj_map<func_decl, unsigned> m_first_idx;
|
||||
|
||||
expr_ref simplify_def(entry const& e);
|
||||
|
||||
public:
|
||||
generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {}
|
||||
|
||||
~generic_model_converter() override;
|
||||
|
||||
void hide(expr* e) { SASSERT(is_app(e) && to_app(e)->get_num_args() == 0); hide(to_app(e)->get_decl()); }
|
||||
|
||||
void hide(func_decl * f) { m_entries.push_back(entry(f, nullptr, m, HIDE)); }
|
||||
|
@ -67,6 +66,8 @@ public:
|
|||
void set_env(ast_pp_util* visitor) override;
|
||||
|
||||
void get_units(obj_map<expr, bool>& units) override;
|
||||
|
||||
vector<entry> const& entries() const { return m_entries; }
|
||||
};
|
||||
|
||||
typedef ref<generic_model_converter> generic_model_converter_ref;
|
|
@ -18,14 +18,14 @@ Revision History:
|
|||
--*/
|
||||
|
||||
|
||||
#include "tactic/horn_subsume_model_converter.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/well_sorted.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
#include "ast/converters/horn_subsume_model_converter.h"
|
||||
|
||||
void horn_subsume_model_converter::insert(app* head, expr* body) {
|
||||
m_delay_head.push_back(head);
|
|
@ -34,7 +34,7 @@ Subsumption transformation (remove Horn clause):
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "tactic/model_converter.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
|
||||
class horn_subsume_model_converter : public model_converter {
|
|
@ -16,7 +16,7 @@ Author:
|
|||
Notes:
|
||||
|
||||
--*/
|
||||
#include "tactic/model_converter.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
#include "model/model_v2_pp.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
||||
|
@ -26,7 +26,7 @@ Notes:
|
|||
void model_converter::display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e) {
|
||||
VERIFY(e);
|
||||
VERIFY(f->get_range() == e->get_sort());
|
||||
ast_smt2_pp(out, f, e, env, params_ref(), 0, "model-add") << "\n";
|
||||
ast_smt2_pp_rev(out, f, e, env, params_ref(), 0, "model-add") << "\n";
|
||||
}
|
||||
|
||||
void model_converter::display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const {
|
|
@ -57,21 +57,24 @@ Notes:
|
|||
#include "util/ref.h"
|
||||
#include "ast/ast_pp_util.h"
|
||||
#include "model/model.h"
|
||||
#include "tactic/converter.h"
|
||||
#include "ast/converters/converter.h"
|
||||
|
||||
class labels_vec : public svector<symbol> {};
|
||||
class smt2_pp_environment;
|
||||
|
||||
class model_converter : public converter {
|
||||
protected:
|
||||
smt2_pp_environment* m_env;
|
||||
smt2_pp_environment* m_env = nullptr;
|
||||
bool m_completion = true;
|
||||
static void display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e);
|
||||
void display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const;
|
||||
void display_del(std::ostream& out, func_decl* f) const;
|
||||
void display_add(std::ostream& out, ast_manager& m);
|
||||
public:
|
||||
|
||||
model_converter(): m_env(nullptr) {}
|
||||
model_converter() {}
|
||||
|
||||
void set_completion(bool f) { m_completion = f; }
|
||||
|
||||
virtual void operator()(model_ref & m) = 0;
|
||||
|
||||
|
@ -101,6 +104,10 @@ typedef sref_buffer<model_converter> model_converter_ref_buffer;
|
|||
|
||||
model_converter * concat(model_converter * mc1, model_converter * mc2);
|
||||
|
||||
inline model_converter * concat(model_converter * mc1, model_converter * mc2, model_converter* mc3) {
|
||||
return concat(mc1, concat(mc2, mc3));
|
||||
}
|
||||
|
||||
model_converter * model2model_converter(model * m);
|
||||
|
||||
model_converter * model_and_labels2model_converter(model * m, labels_vec const &r);
|
|
@ -16,8 +16,7 @@ Author:
|
|||
Notes:
|
||||
|
||||
--*/
|
||||
#include "tactic/proof_converter.h"
|
||||
#include "tactic/goal.h"
|
||||
#include "ast/converters/proof_converter.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
||||
class concat_proof_converter : public concat_converter<proof_converter> {
|
||||
|
@ -46,41 +45,6 @@ proof_converter * concat(proof_converter * pc1, proof_converter * pc2) {
|
|||
return alloc(concat_proof_converter, pc1, pc2);
|
||||
}
|
||||
|
||||
class subgoal_proof_converter : public proof_converter {
|
||||
proof_converter_ref m_pc;
|
||||
goal_ref_buffer m_goals;
|
||||
public:
|
||||
subgoal_proof_converter(proof_converter* pc, unsigned n, goal * const* goals):
|
||||
m_pc(pc)
|
||||
{
|
||||
for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]);
|
||||
}
|
||||
|
||||
proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override {
|
||||
// ignore the proofs from the arguments, instead obtain the proofs fromt he subgoals.
|
||||
SASSERT(num_source == 0);
|
||||
proof_converter_ref_buffer pc_buffer;
|
||||
for (goal_ref g : m_goals) {
|
||||
pc_buffer.push_back(g->pc());
|
||||
|
||||
}
|
||||
return apply(m, m_pc, pc_buffer);
|
||||
}
|
||||
|
||||
proof_converter* translate(ast_translation& tr) override {
|
||||
proof_converter_ref pc1 = m_pc->translate(tr);
|
||||
goal_ref_buffer goals;
|
||||
for (goal_ref g : m_goals) goals.push_back(g->translate(tr));
|
||||
return alloc(subgoal_proof_converter, pc1.get(), goals.size(), goals.data());
|
||||
}
|
||||
|
||||
void display(std::ostream& out) override {}
|
||||
|
||||
};
|
||||
|
||||
proof_converter * concat(proof_converter *pc, unsigned n, goal* const* goals) {
|
||||
return alloc(subgoal_proof_converter, pc, n, goals);
|
||||
}
|
||||
|
||||
class proof2pc : public proof_converter {
|
||||
proof_ref m_pr;
|
|
@ -20,8 +20,7 @@ Notes:
|
|||
|
||||
#include "ast/ast.h"
|
||||
#include "util/ref.h"
|
||||
#include "tactic/converter.h"
|
||||
class goal;
|
||||
#include "ast/converters/converter.h"
|
||||
|
||||
class proof_converter : public converter {
|
||||
public:
|
||||
|
@ -36,12 +35,6 @@ typedef sref_buffer<proof_converter> proof_converter_ref_buffer;
|
|||
|
||||
proof_converter * concat(proof_converter * pc1, proof_converter * pc2);
|
||||
|
||||
/**
|
||||
\brief create a proof converter that takes a set of subgoals and converts their proofs to a proof of
|
||||
the goal they were derived from.
|
||||
*/
|
||||
proof_converter * concat(proof_converter *pc1, unsigned n, goal* const* goals);
|
||||
|
||||
proof_converter * proof2proof_converter(ast_manager & m, proof * pr);
|
||||
|
||||
void apply(ast_manager & m, proof_converter * pc, proof_ref & pr);
|
|
@ -17,7 +17,7 @@ Revision History:
|
|||
|
||||
--*/
|
||||
|
||||
#include "tactic/replace_proof_converter.h"
|
||||
#include "ast/converters/replace_proof_converter.h"
|
||||
#include "ast/expr_functors.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/for_each_expr.h"
|
|
@ -22,7 +22,7 @@ Revision History:
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "tactic/proof_converter.h"
|
||||
#include "ast/converters/proof_converter.h"
|
||||
|
||||
class replace_proof_converter : public proof_converter {
|
||||
ast_manager& m;
|
|
@ -832,6 +832,10 @@ namespace datatype {
|
|||
bool util::is_declared(sort* s) const {
|
||||
return plugin().is_declared(s);
|
||||
}
|
||||
|
||||
bool util::is_declared(symbol const& n) const {
|
||||
return plugin().is_declared(n);
|
||||
}
|
||||
|
||||
void util::compute_datatype_size_functions(svector<symbol> const& names) {
|
||||
map<symbol, status, symbol_hash_proc, symbol_eq_proc> already_found;
|
||||
|
@ -1087,11 +1091,9 @@ namespace datatype {
|
|||
sort * datatype = con->get_range();
|
||||
def const& dd = get_def(datatype);
|
||||
symbol r;
|
||||
for (constructor const* c : dd) {
|
||||
if (c->name() == con->get_name()) {
|
||||
r = c->recognizer();
|
||||
}
|
||||
}
|
||||
for (constructor const* c : dd)
|
||||
if (c->name() == con->get_name())
|
||||
r = c->recognizer();
|
||||
parameter ps[2] = { parameter(con), parameter(r) };
|
||||
d = m.mk_func_decl(fid(), OP_DT_RECOGNISER, 2, ps, 1, &datatype);
|
||||
SASSERT(d);
|
||||
|
@ -1142,17 +1144,15 @@ namespace datatype {
|
|||
}
|
||||
|
||||
bool util::is_enum_sort(sort* s) {
|
||||
if (!is_datatype(s)) {
|
||||
return false;
|
||||
}
|
||||
if (!is_datatype(s))
|
||||
return false;
|
||||
bool r = false;
|
||||
if (m_is_enum.find(s, r))
|
||||
return r;
|
||||
ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
|
||||
r = true;
|
||||
for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
|
||||
r = cnstrs[i]->get_arity() == 0;
|
||||
}
|
||||
for (unsigned i = 0; r && i < cnstrs.size(); ++i)
|
||||
r = cnstrs[i]->get_arity() == 0;
|
||||
m_is_enum.insert(s, r);
|
||||
m_asts.push_back(s);
|
||||
return r;
|
||||
|
@ -1284,11 +1284,14 @@ namespace datatype {
|
|||
unsigned idx = 0;
|
||||
def const& d = get_def(f->get_range());
|
||||
for (constructor* c : d) {
|
||||
if (c->name() == f->get_name()) {
|
||||
return idx;
|
||||
}
|
||||
if (c->name() == f->get_name())
|
||||
return idx;
|
||||
++idx;
|
||||
}
|
||||
IF_VERBOSE(0, verbose_stream() << f->get_name() << "\n");
|
||||
for (constructor* c : d)
|
||||
IF_VERBOSE(0, verbose_stream() << "!= " << c->name() << "\n");
|
||||
SASSERT(false);
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -253,6 +253,7 @@ namespace datatype {
|
|||
ptr_vector<constructor> get_constructors(symbol const& s) const;
|
||||
ptr_vector<accessor> get_accessors(symbol const& s) const;
|
||||
bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); }
|
||||
bool is_declared(symbol const& n) const { return m_defs.contains(n); }
|
||||
unsigned get_axiom_base_id(symbol const& s) { return m_axiom_bases[s]; }
|
||||
util & u() const;
|
||||
|
||||
|
@ -375,6 +376,7 @@ namespace datatype {
|
|||
bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f);
|
||||
void reset();
|
||||
bool is_declared(sort* s) const;
|
||||
bool is_declared(symbol const& n) const;
|
||||
void display_datatype(sort *s, std::ostream& strm);
|
||||
bool is_fully_interp(sort * s) const;
|
||||
sort_ref_vector datatype_params(sort * s) const;
|
||||
|
|
|
@ -32,9 +32,8 @@ void decl_collector::visit_sort(sort * n) {
|
|||
m_todo.push_back(cnstr);
|
||||
ptr_vector<func_decl> const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr);
|
||||
unsigned num_cas = cnstr_acc.size();
|
||||
for (unsigned j = 0; j < num_cas; j++) {
|
||||
m_todo.push_back(cnstr_acc.get(j));
|
||||
}
|
||||
for (unsigned j = 0; j < num_cas; j++)
|
||||
m_todo.push_back(cnstr_acc.get(j));
|
||||
}
|
||||
}
|
||||
for (unsigned i = n->get_num_parameters(); i-- > 0; ) {
|
||||
|
@ -49,14 +48,19 @@ bool decl_collector::is_bool(sort * s) {
|
|||
|
||||
void decl_collector::visit_func(func_decl * n) {
|
||||
func_decl* g;
|
||||
|
||||
if (!m_visited.is_marked(n)) {
|
||||
family_id fid = n->get_family_id();
|
||||
if (fid == null_family_id)
|
||||
m_decls.push_back(n);
|
||||
else if (fid == m_rec_fid) {
|
||||
m_rec_decls.push_back(n);
|
||||
recfun::util u(m());
|
||||
m_todo.push_back(u.get_def(n).get_rhs());
|
||||
if (u.has_def(n)) {
|
||||
m_rec_decls.push_back(n);
|
||||
m_todo.push_back(u.get_def(n).get_rhs());
|
||||
}
|
||||
else
|
||||
m_decls.push_back(n);
|
||||
}
|
||||
else if (m_ar_util.is_as_array(n, g))
|
||||
m_todo.push_back(g);
|
||||
|
@ -97,13 +101,11 @@ void decl_collector::visit(ast* n) {
|
|||
case AST_QUANTIFIER: {
|
||||
quantifier * q = to_quantifier(n);
|
||||
unsigned num_decls = q->get_num_decls();
|
||||
for (unsigned i = 0; i < num_decls; ++i) {
|
||||
m_todo.push_back(q->get_decl_sort(i));
|
||||
}
|
||||
for (unsigned i = 0; i < num_decls; ++i)
|
||||
m_todo.push_back(q->get_decl_sort(i));
|
||||
m_todo.push_back(q->get_expr());
|
||||
for (unsigned i = 0; i < q->get_num_patterns(); ++i) {
|
||||
m_todo.push_back(q->get_pattern(i));
|
||||
}
|
||||
for (unsigned i = 0; i < q->get_num_patterns(); ++i)
|
||||
m_todo.push_back(q->get_pattern(i));
|
||||
break;
|
||||
}
|
||||
case AST_SORT:
|
||||
|
@ -111,9 +113,8 @@ void decl_collector::visit(ast* n) {
|
|||
break;
|
||||
case AST_FUNC_DECL: {
|
||||
func_decl * d = to_func_decl(n);
|
||||
for (sort* srt : *d) {
|
||||
m_todo.push_back(srt);
|
||||
}
|
||||
for (sort* srt : *d)
|
||||
m_todo.push_back(srt);
|
||||
m_todo.push_back(d->get_range());
|
||||
visit_func(d);
|
||||
break;
|
||||
|
|
|
@ -36,8 +36,8 @@ namespace euf {
|
|||
}
|
||||
m_expr2enode.setx(f->get_id(), n, nullptr);
|
||||
push_node(n);
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
set_merge_enabled(args[i], true);
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
set_cgc_enabled(args[i], true);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -78,9 +78,8 @@ namespace euf {
|
|||
|
||||
void egraph::reinsert_equality(enode* p) {
|
||||
SASSERT(p->is_equality());
|
||||
if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) {
|
||||
add_literal(p, true);
|
||||
}
|
||||
if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root())
|
||||
add_literal(p, nullptr);
|
||||
}
|
||||
|
||||
void egraph::force_push() {
|
||||
|
@ -92,9 +91,7 @@ namespace euf {
|
|||
m_scopes.push_back(m_updates.size());
|
||||
m_region.push_scope();
|
||||
m_updates.push_back(update_record(m_new_th_eqs_qhead, update_record::new_th_eq_qhead()));
|
||||
m_updates.push_back(update_record(m_new_lits_qhead, update_record::new_lits_qhead()));
|
||||
}
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
|
||||
}
|
||||
|
||||
|
@ -116,18 +113,16 @@ namespace euf {
|
|||
m_on_make(n);
|
||||
if (num_args == 0)
|
||||
return n;
|
||||
if (m.is_eq(f)) {
|
||||
if (m.is_eq(f) && !m.is_iff(f)) {
|
||||
n->set_is_equality();
|
||||
update_children(n);
|
||||
reinsert_equality(n);
|
||||
}
|
||||
else {
|
||||
auto [n2, comm] = insert_table(n);
|
||||
if (n2 == n)
|
||||
update_children(n);
|
||||
else
|
||||
merge(n, n2, justification::congruence(comm));
|
||||
}
|
||||
auto [n2, comm] = insert_table(n);
|
||||
if (n2 == n)
|
||||
update_children(n);
|
||||
else
|
||||
merge(n, n2, justification::congruence(comm, m_congruence_timestamp++));
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -158,11 +153,28 @@ namespace euf {
|
|||
++m_stats.m_num_th_diseqs;
|
||||
}
|
||||
|
||||
void egraph::add_literal(enode* n, bool is_eq) {
|
||||
TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";);
|
||||
m_new_lits.push_back(enode_bool_pair(n, is_eq));
|
||||
m_updates.push_back(update_record(update_record::new_lit()));
|
||||
if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
|
||||
void egraph::add_literal(enode* n, enode* ante) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void egraph::new_diseq(enode* n) {
|
||||
|
@ -173,7 +185,7 @@ namespace euf {
|
|||
enode* r2 = arg2->get_root();
|
||||
TRACE("euf", tout << "new-diseq: " << bpp(r1) << " " << bpp(r2) << ": " << r1->has_th_vars() << " " << r2->has_th_vars() << "\n";);
|
||||
if (r1 == r2) {
|
||||
add_literal(n, true);
|
||||
add_literal(n, nullptr);
|
||||
return;
|
||||
}
|
||||
if (!r1->has_th_vars())
|
||||
|
@ -264,10 +276,19 @@ namespace euf {
|
|||
root->del_th_var(tid);
|
||||
}
|
||||
|
||||
void egraph::set_merge_enabled(enode* n, bool enable_merge) {
|
||||
if (enable_merge != n->merge_enabled()) {
|
||||
toggle_merge_enabled(n, false);
|
||||
m_updates.push_back(update_record(n, update_record::toggle_merge()));
|
||||
void egraph::set_merge_tf_enabled(enode* n, bool enable_merge_tf) {
|
||||
if (!m.is_bool(n->get_sort()))
|
||||
return;
|
||||
if (enable_merge_tf != n->merge_tf()) {
|
||||
n->set_merge_tf(enable_merge_tf);
|
||||
m_updates.push_back(update_record(n, update_record::toggle_merge_tf()));
|
||||
}
|
||||
}
|
||||
|
||||
void egraph::set_cgc_enabled(enode* n, bool enable_merge) {
|
||||
if (enable_merge != n->cgc_enabled()) {
|
||||
toggle_cgc_enabled(n, false);
|
||||
m_updates.push_back(update_record(n, update_record::toggle_cgc()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,9 +299,9 @@ namespace euf {
|
|||
m_updates.push_back(update_record(n, update_record::set_relevant()));
|
||||
}
|
||||
|
||||
void egraph::toggle_merge_enabled(enode* n, bool backtracking) {
|
||||
bool enable_merge = !n->merge_enabled();
|
||||
n->set_merge_enabled(enable_merge);
|
||||
void egraph::toggle_cgc_enabled(enode* n, bool backtracking) {
|
||||
bool enable_merge = !n->cgc_enabled();
|
||||
n->set_cgc_enabled(enable_merge);
|
||||
if (n->num_args() > 0) {
|
||||
if (enable_merge) {
|
||||
auto [n2, comm] = insert_table(n);
|
||||
|
@ -290,7 +311,7 @@ namespace euf {
|
|||
else if (n->is_cgr())
|
||||
erase_from_table(n);
|
||||
}
|
||||
VERIFY(n->num_args() == 0 || !n->merge_enabled() || m_table.contains(n));
|
||||
VERIFY(n->num_args() == 0 || !n->cgc_enabled() || m_table.contains(n));
|
||||
}
|
||||
|
||||
void egraph::set_value(enode* n, lbool value, justification j) {
|
||||
|
@ -300,6 +321,8 @@ namespace euf {
|
|||
n->set_value(value);
|
||||
n->m_lit_justification = j;
|
||||
m_updates.push_back(update_record(n, update_record::value_assignment()));
|
||||
if (n->is_equality() && n->value() == l_false)
|
||||
new_diseq(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,7 +352,6 @@ namespace euf {
|
|||
num_scopes -= m_num_scopes;
|
||||
m_num_scopes = 0;
|
||||
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
unsigned old_lim = m_scopes.size() - num_scopes;
|
||||
unsigned num_updates = m_scopes[old_lim];
|
||||
auto undo_node = [&]() {
|
||||
|
@ -352,8 +374,11 @@ namespace euf {
|
|||
case update_record::tag_t::is_add_node:
|
||||
undo_node();
|
||||
break;
|
||||
case update_record::tag_t::is_toggle_merge:
|
||||
toggle_merge_enabled(p.r1, true);
|
||||
case update_record::tag_t::is_toggle_cgc:
|
||||
toggle_cgc_enabled(p.r1, true);
|
||||
break;
|
||||
case update_record::tag_t::is_toggle_merge_tf:
|
||||
p.r1->set_merge_tf(!p.r1->merge_tf());
|
||||
break;
|
||||
case update_record::tag_t::is_set_parent:
|
||||
undo_eq(p.r1, p.n1, p.r2_num_parents);
|
||||
|
@ -365,18 +390,12 @@ namespace euf {
|
|||
SASSERT(p.r1->get_th_var(p.m_th_id) != null_theory_var);
|
||||
p.r1->replace_th_var(p.m_old_th_var, p.m_th_id);
|
||||
break;
|
||||
case update_record::tag_t::is_new_lit:
|
||||
m_new_lits.pop_back();
|
||||
break;
|
||||
case update_record::tag_t::is_new_th_eq:
|
||||
m_new_th_eqs.pop_back();
|
||||
break;
|
||||
case update_record::tag_t::is_new_th_eq_qhead:
|
||||
m_new_th_eqs_qhead = p.qhead;
|
||||
break;
|
||||
case update_record::tag_t::is_new_lits_qhead:
|
||||
m_new_lits_qhead = p.qhead;
|
||||
break;
|
||||
case update_record::tag_t::is_inconsistent:
|
||||
m_inconsistent = p.m_inconsistent;
|
||||
break;
|
||||
|
@ -411,7 +430,6 @@ namespace euf {
|
|||
m_region.pop_scope(num_scopes);
|
||||
m_to_merge.reset();
|
||||
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
|
||||
|
||||
// DEBUG_CODE(invariant(););
|
||||
|
@ -419,7 +437,7 @@ namespace euf {
|
|||
|
||||
void egraph::merge(enode* n1, enode* n2, justification j) {
|
||||
|
||||
if (!n1->merge_enabled() && !n2->merge_enabled())
|
||||
if (!n1->cgc_enabled() && !n2->cgc_enabled())
|
||||
return;
|
||||
SASSERT(n1->get_sort() == n2->get_sort());
|
||||
enode* r1 = n1->get_root();
|
||||
|
@ -436,6 +454,7 @@ namespace euf {
|
|||
set_conflict(n1, n2, j);
|
||||
return;
|
||||
}
|
||||
|
||||
if (r1->value() != r2->value() && r1->value() != l_undef && r2->value() != l_undef) {
|
||||
SASSERT(m.is_bool(r1->get_expr()));
|
||||
set_conflict(n1, n2, j);
|
||||
|
@ -447,11 +466,7 @@ namespace euf {
|
|||
std::swap(n1, n2);
|
||||
}
|
||||
|
||||
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr())))
|
||||
add_literal(n1, false);
|
||||
if (n1->is_equality() && n1->value() == l_false)
|
||||
new_diseq(n1);
|
||||
remove_parents(r1, r2);
|
||||
remove_parents(r1);
|
||||
push_eq(r1, n1, r2->num_parents());
|
||||
merge_justification(n1, n2, j);
|
||||
for (enode* c : enode_class(n1))
|
||||
|
@ -460,15 +475,22 @@ namespace euf {
|
|||
r2->inc_class_size(r1->class_size());
|
||||
merge_th_eq(r1, r2);
|
||||
reinsert_parents(r1, r2);
|
||||
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr())))
|
||||
add_literal(n1, r2);
|
||||
else if (n2->value() != l_undef && n1->value() != n2->value())
|
||||
add_literal(n1, n2);
|
||||
else if (n1->value() != l_undef && n2->value() != n1->value())
|
||||
add_literal(n2, n1);
|
||||
|
||||
for (auto& cb : m_on_merge)
|
||||
cb(r2, r1);
|
||||
}
|
||||
|
||||
void egraph::remove_parents(enode* r1, enode* r2) {
|
||||
for (enode* p : enode_parents(r1)) {
|
||||
void egraph::remove_parents(enode* r) {
|
||||
for (enode* p : enode_parents(r)) {
|
||||
if (p->is_marked1())
|
||||
continue;
|
||||
if (p->merge_enabled()) {
|
||||
if (p->cgc_enabled()) {
|
||||
if (!p->is_cgr())
|
||||
continue;
|
||||
SASSERT(m_table.contains_ptr(p));
|
||||
|
@ -486,8 +508,8 @@ namespace euf {
|
|||
if (!p->is_marked1())
|
||||
continue;
|
||||
p->unmark1();
|
||||
TRACE("euf", tout << "reinsert " << bpp(r1) << " " << bpp(r2) << " " << bpp(p) << " " << p->merge_enabled() << "\n";);
|
||||
if (p->merge_enabled()) {
|
||||
TRACE("euf", tout << "reinsert " << bpp(r1) << " " << bpp(r2) << " " << bpp(p) << " " << p->cgc_enabled() << "\n";);
|
||||
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";);
|
||||
|
@ -531,9 +553,9 @@ namespace euf {
|
|||
for (auto it = begin; it != end; ++it) {
|
||||
enode* p = *it;
|
||||
TRACE("euf", tout << "erase " << bpp(p) << "\n";);
|
||||
SASSERT(!p->merge_enabled() || m_table.contains_ptr(p));
|
||||
SASSERT(!p->merge_enabled() || p->is_cgr());
|
||||
if (p->merge_enabled())
|
||||
SASSERT(!p->cgc_enabled() || m_table.contains_ptr(p));
|
||||
SASSERT(!p->cgc_enabled() || p->is_cgr());
|
||||
if (p->cgc_enabled())
|
||||
erase_from_table(p);
|
||||
}
|
||||
|
||||
|
@ -541,7 +563,7 @@ namespace euf {
|
|||
c->m_root = r1;
|
||||
|
||||
for (enode* p : enode_parents(r1))
|
||||
if (p->merge_enabled() && (p->is_cgr() || !p->congruent(p->m_cg)))
|
||||
if (p->cgc_enabled() && (p->is_cgr() || !p->congruent(p->m_cg)))
|
||||
insert_table(p);
|
||||
r2->m_parents.shrink(r2_num_parents);
|
||||
unmerge_justification(n1);
|
||||
|
@ -549,16 +571,14 @@ namespace euf {
|
|||
|
||||
|
||||
bool egraph::propagate() {
|
||||
SASSERT(m_new_lits_qhead <= m_new_lits.size());
|
||||
SASSERT(m_num_scopes == 0 || m_to_merge.empty());
|
||||
force_push();
|
||||
for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) {
|
||||
auto const& w = m_to_merge[i];
|
||||
merge(w.a, w.b, justification::congruence(w.commutativity));
|
||||
merge(w.a, w.b, justification::congruence(w.commutativity, m_congruence_timestamp++));
|
||||
}
|
||||
m_to_merge.reset();
|
||||
return
|
||||
(m_new_lits_qhead < m_new_lits.size()) ||
|
||||
(m_new_th_eqs_qhead < m_new_th_eqs.size()) ||
|
||||
inconsistent();
|
||||
}
|
||||
|
@ -571,6 +591,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");
|
||||
m_justification = j;
|
||||
}
|
||||
|
||||
|
@ -649,7 +670,7 @@ namespace euf {
|
|||
SASSERT(n1->get_decl() == n2->get_decl());
|
||||
m_uses_congruence = true;
|
||||
if (m_used_cc && !comm) {
|
||||
m_used_cc(to_app(n1->get_expr()), to_app(n2->get_expr()));
|
||||
m_used_cc(n1->get_app(), n2->get_app());
|
||||
}
|
||||
if (comm &&
|
||||
n1->get_arg(0)->get_root() == n2->get_arg(1)->get_root() &&
|
||||
|
@ -707,25 +728,28 @@ namespace euf {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void egraph::explain(ptr_vector<T>& justifications) {
|
||||
void egraph::explain(ptr_vector<T>& justifications, cc_justification* cc) {
|
||||
SASSERT(m_inconsistent);
|
||||
push_todo(m_n1);
|
||||
push_todo(m_n2);
|
||||
explain_eq(justifications, m_n1, m_n2, m_justification);
|
||||
explain_todo(justifications);
|
||||
explain_eq(justifications, cc, m_n1, m_n2, m_justification);
|
||||
explain_todo(justifications, cc);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, justification const& j) {
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b, justification const& j) {
|
||||
TRACE("euf_verbose", tout << "explain-eq: " << bpp(a) << " == " << bpp(b) << " jst: " << j << "\n";);
|
||||
if (j.is_external())
|
||||
justifications.push_back(j.ext<T>());
|
||||
else if (j.is_congruence())
|
||||
push_congruence(a, b, j.is_commutative());
|
||||
if (cc && j.is_congruence())
|
||||
cc->push_back(std::tuple(a->get_app(), b->get_app(), j.timestamp(), j.is_commutative()));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b) {
|
||||
void egraph::explain_eq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b) {
|
||||
SASSERT(a->get_root() == b->get_root());
|
||||
|
||||
enode* lca = find_lca(a, b);
|
||||
|
@ -734,27 +758,27 @@ namespace euf {
|
|||
push_to_lca(b, lca);
|
||||
if (m_used_eq)
|
||||
m_used_eq(a->get_expr(), b->get_expr(), lca->get_expr());
|
||||
explain_todo(justifications);
|
||||
explain_todo(justifications, cc);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unsigned egraph::explain_diseq(ptr_vector<T>& justifications, enode* a, enode* b) {
|
||||
unsigned egraph::explain_diseq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b) {
|
||||
enode* ra = a->get_root(), * rb = b->get_root();
|
||||
SASSERT(ra != rb);
|
||||
if (ra->interpreted() && rb->interpreted()) {
|
||||
explain_eq(justifications, a, ra);
|
||||
explain_eq(justifications, b, rb);
|
||||
explain_eq(justifications, cc, a, ra);
|
||||
explain_eq(justifications, cc, b, rb);
|
||||
return sat::null_bool_var;
|
||||
}
|
||||
enode* r = tmp_eq(ra, rb);
|
||||
SASSERT(r && r->get_root()->value() == l_false);
|
||||
explain_eq(justifications, r, r->get_root());
|
||||
explain_eq(justifications, cc, r, r->get_root());
|
||||
return r->get_root()->bool_var();
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void egraph::explain_todo(ptr_vector<T>& justifications) {
|
||||
void egraph::explain_todo(ptr_vector<T>& justifications, cc_justification* cc) {
|
||||
for (unsigned i = 0; i < m_todo.size(); ++i) {
|
||||
enode* n = m_todo[i];
|
||||
if (n->is_marked1())
|
||||
|
@ -762,7 +786,7 @@ namespace euf {
|
|||
if (n->m_target) {
|
||||
n->mark1();
|
||||
CTRACE("euf_verbose", m_display_justification, n->m_justification.display(tout << n->get_expr_id() << " = " << n->m_target->get_expr_id() << " ", m_display_justification) << "\n";);
|
||||
explain_eq(justifications, n, n->m_target, n->m_justification);
|
||||
explain_eq(justifications, cc, n, n->m_target, n->m_justification);
|
||||
}
|
||||
else if (!n->is_marked1() && n->value() != l_undef) {
|
||||
n->mark1();
|
||||
|
@ -779,7 +803,7 @@ namespace euf {
|
|||
for (enode* n : m_nodes)
|
||||
n->invariant(*this);
|
||||
for (enode* n : m_nodes)
|
||||
if (n->merge_enabled() && n->num_args() > 0 && (!m_table.find(n) || n->get_root() != m_table.find(n)->get_root())) {
|
||||
if (n->cgc_enabled() && n->num_args() > 0 && (!m_table.find(n) || n->get_root() != m_table.find(n)->get_root())) {
|
||||
CTRACE("euf", !m_table.find(n), tout << "node is not in table\n";);
|
||||
CTRACE("euf", m_table.find(n), tout << "root " << bpp(n->get_root()) << " table root " << bpp(m_table.find(n)->get_root()) << "\n";);
|
||||
TRACE("euf", display(tout << bpp(n) << " is not closed under congruence\n"););
|
||||
|
@ -814,7 +838,7 @@ namespace euf {
|
|||
}
|
||||
};
|
||||
if (n->bool_var() != sat::null_bool_var)
|
||||
out << "[b" << n->bool_var() << " := " << value_of() << (n->merge_tf() ? "" : " no merge") << "] ";
|
||||
out << "[b" << n->bool_var() << " := " << value_of() << (n->cgc_enabled() ? "" : " no-cgc") << (n->merge_tf()? " merge-tf" : "") << "] ";
|
||||
if (n->has_th_vars()) {
|
||||
out << "[t";
|
||||
for (auto const& v : enode_th_vars(n))
|
||||
|
@ -831,7 +855,6 @@ namespace euf {
|
|||
|
||||
std::ostream& egraph::display(std::ostream& out) const {
|
||||
out << "updates " << m_updates.size() << "\n";
|
||||
out << "newlits " << m_new_lits.size() << " qhead: " << m_new_lits_qhead << "\n";
|
||||
out << "neweqs " << m_new_th_eqs.size() << " qhead: " << m_new_th_eqs_qhead << "\n";
|
||||
m_table.display(out);
|
||||
unsigned max_args = 0;
|
||||
|
@ -869,7 +892,8 @@ namespace euf {
|
|||
n2->set_value(n1->value());
|
||||
n2->m_bool_var = n1->m_bool_var;
|
||||
n2->m_commutative = n1->m_commutative;
|
||||
n2->m_merge_enabled = n1->m_merge_enabled;
|
||||
n2->m_cgc_enabled = n1->m_cgc_enabled;
|
||||
n2->m_merge_tf_enabled = n1->m_merge_tf_enabled;
|
||||
n2->m_is_equality = n1->m_is_equality;
|
||||
}
|
||||
for (unsigned i = 0; i < src.m_nodes.size(); ++i) {
|
||||
|
@ -890,16 +914,20 @@ namespace euf {
|
|||
}
|
||||
}
|
||||
|
||||
template void euf::egraph::explain(ptr_vector<int>& justifications);
|
||||
template void euf::egraph::explain_todo(ptr_vector<int>& justifications);
|
||||
template void euf::egraph::explain_eq(ptr_vector<int>& justifications, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<int>& justifications, enode* a, enode* b);
|
||||
template void euf::egraph::explain(ptr_vector<int>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_todo(ptr_vector<int>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_eq(ptr_vector<int>& justifications, cc_justification*, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<int>& justifications, cc_justification*, enode* a, enode* b);
|
||||
|
||||
template void euf::egraph::explain(ptr_vector<size_t>& justifications);
|
||||
template void euf::egraph::explain_todo(ptr_vector<size_t>& justifications);
|
||||
template void euf::egraph::explain_eq(ptr_vector<size_t>& justifications, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<size_t>& justifications, enode* a, enode* b);
|
||||
template void euf::egraph::explain(ptr_vector<size_t>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_todo(ptr_vector<size_t>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_eq(ptr_vector<size_t>& justifications, cc_justification*, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<size_t>& justifications, cc_justification*, enode* a, enode* b);
|
||||
|
||||
template void euf::egraph::explain(ptr_vector<expr_dependency>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_todo(ptr_vector<expr_dependency>& justifications, cc_justification*);
|
||||
template void euf::egraph::explain_eq(ptr_vector<expr_dependency>& justifications, cc_justification*, enode* a, enode* b);
|
||||
template unsigned euf::egraph::explain_diseq(ptr_vector<expr_dependency>& justifications, cc_justification*, enode* a, enode* b);
|
||||
|
||||
|
||||
#if 0
|
||||
|
|
|
@ -72,6 +72,13 @@ namespace euf {
|
|||
th_eq(theory_id id, theory_var v1, theory_var v2, expr* eq) :
|
||||
m_id(id), m_v1(v1), m_v2(v2), m_eq(eq), m_root(nullptr) {}
|
||||
};
|
||||
|
||||
// cc_justification contains the uses of congruence closure
|
||||
// It is the only information collected from justifications in order to
|
||||
// reconstruct EUF proofs. Transitivity, Symmetry of equality are not
|
||||
// tracked.
|
||||
typedef std::tuple<app*,app*,uint64_t, bool> cc_justification_record;
|
||||
typedef svector<cc_justification_record> cc_justification;
|
||||
|
||||
class egraph {
|
||||
|
||||
|
@ -94,22 +101,21 @@ namespace euf {
|
|||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
struct update_record {
|
||||
struct toggle_merge {};
|
||||
struct toggle_cgc {};
|
||||
struct toggle_merge_tf {};
|
||||
struct add_th_var {};
|
||||
struct replace_th_var {};
|
||||
struct new_lit {};
|
||||
struct new_th_eq {};
|
||||
struct new_th_eq_qhead {};
|
||||
struct new_lits_qhead {};
|
||||
struct inconsistent {};
|
||||
struct value_assignment {};
|
||||
struct lbl_hash {};
|
||||
struct lbl_set {};
|
||||
struct update_children {};
|
||||
struct set_relevant {};
|
||||
enum class tag_t { is_set_parent, is_add_node, is_toggle_merge, is_update_children,
|
||||
is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq,
|
||||
is_lbl_hash, is_new_th_eq_qhead, is_new_lits_qhead,
|
||||
enum class tag_t { is_set_parent, is_add_node, is_toggle_cgc, is_toggle_merge_tf, is_update_children,
|
||||
is_add_th_var, is_replace_th_var, is_new_th_eq,
|
||||
is_lbl_hash, is_new_th_eq_qhead,
|
||||
is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant };
|
||||
tag_t tag;
|
||||
enode* r1;
|
||||
|
@ -129,20 +135,18 @@ namespace euf {
|
|||
tag(tag_t::is_set_parent), r1(r1), n1(n1), r2_num_parents(r2_num_parents) {}
|
||||
update_record(enode* n) :
|
||||
tag(tag_t::is_add_node), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
|
||||
update_record(enode* n, toggle_merge) :
|
||||
tag(tag_t::is_toggle_merge), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
|
||||
update_record(enode* n, toggle_cgc) :
|
||||
tag(tag_t::is_toggle_cgc), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
|
||||
update_record(enode* n, toggle_merge_tf) :
|
||||
tag(tag_t::is_toggle_merge_tf), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
|
||||
update_record(enode* n, unsigned id, add_th_var) :
|
||||
tag(tag_t::is_add_th_var), r1(n), n1(nullptr), r2_num_parents(id) {}
|
||||
update_record(enode* n, theory_id id, theory_var v, replace_th_var) :
|
||||
tag(tag_t::is_replace_th_var), r1(n), n1(nullptr), m_th_id(id), m_old_th_var(v) {}
|
||||
update_record(new_lit) :
|
||||
tag(tag_t::is_new_lit), r1(nullptr), n1(nullptr), r2_num_parents(0) {}
|
||||
update_record(new_th_eq) :
|
||||
tag(tag_t::is_new_th_eq), r1(nullptr), n1(nullptr), r2_num_parents(0) {}
|
||||
update_record(unsigned qh, new_th_eq_qhead):
|
||||
tag(tag_t::is_new_th_eq_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
|
||||
update_record(unsigned qh, new_lits_qhead):
|
||||
tag(tag_t::is_new_lits_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
|
||||
update_record(bool inc, inconsistent) :
|
||||
tag(tag_t::is_inconsistent), r1(nullptr), n1(nullptr), m_inconsistent(inc) {}
|
||||
update_record(enode* n, value_assignment) :
|
||||
|
@ -177,16 +181,17 @@ namespace euf {
|
|||
enode *m_n1 = nullptr;
|
||||
enode *m_n2 = nullptr;
|
||||
justification m_justification;
|
||||
unsigned m_new_lits_qhead = 0;
|
||||
unsigned m_new_th_eqs_qhead = 0;
|
||||
svector<enode_bool_pair> m_new_lits;
|
||||
svector<th_eq> m_new_th_eqs;
|
||||
bool_vector m_th_propagates_diseqs;
|
||||
enode_vector m_todo;
|
||||
stats m_stats;
|
||||
bool m_uses_congruence = false;
|
||||
bool m_default_relevant = true;
|
||||
std::vector<std::function<void(enode*,enode*)>> m_on_merge;
|
||||
uint64_t m_congruence_timestamp = 0;
|
||||
|
||||
std::vector<std::function<void(enode*,enode*)>> m_on_merge;
|
||||
std::function<void(enode*, enode*)> m_on_propagate_literal;
|
||||
std::function<void(enode*)> m_on_make;
|
||||
std::function<void(expr*,expr*,expr*)> m_used_eq;
|
||||
std::function<void(app*,app*)> m_used_cc;
|
||||
|
@ -201,7 +206,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, bool is_eq);
|
||||
void add_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);
|
||||
|
@ -211,7 +216,7 @@ namespace euf {
|
|||
void merge_th_eq(enode* n, enode* root);
|
||||
void merge_justification(enode* n1, enode* n2, justification j);
|
||||
void reinsert_parents(enode* r1, enode* r2);
|
||||
void remove_parents(enode* r1, enode* r2);
|
||||
void remove_parents(enode* r);
|
||||
void unmerge_justification(enode* n1);
|
||||
void reinsert_equality(enode* p);
|
||||
void update_children(enode* n);
|
||||
|
@ -220,16 +225,16 @@ namespace euf {
|
|||
void push_to_lca(enode* a, enode* lca);
|
||||
void push_congruence(enode* n1, enode* n2, bool commutative);
|
||||
void push_todo(enode* n);
|
||||
void toggle_merge_enabled(enode* n, bool backtracking);
|
||||
void toggle_cgc_enabled(enode* n, bool backtracking);
|
||||
|
||||
enode_bool_pair insert_table(enode* p);
|
||||
void erase_from_table(enode* p);
|
||||
|
||||
template <typename T>
|
||||
void explain_eq(ptr_vector<T>& justifications, enode* a, enode* b, justification const& j);
|
||||
void explain_eq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b, justification const& j);
|
||||
|
||||
template <typename T>
|
||||
void explain_todo(ptr_vector<T>& justifications);
|
||||
void explain_todo(ptr_vector<T>& justifications, cc_justification* cc);
|
||||
|
||||
std::ostream& display(std::ostream& out, unsigned max_args, enode* n) const;
|
||||
|
||||
|
@ -278,11 +283,8 @@ namespace euf {
|
|||
is an equality consequence.
|
||||
*/
|
||||
void add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq);
|
||||
bool has_literal() const { return m_new_lits_qhead < m_new_lits.size(); }
|
||||
bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); }
|
||||
enode_bool_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; }
|
||||
th_eq get_th_eq() const { return m_new_th_eqs[m_new_th_eqs_qhead]; }
|
||||
void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; }
|
||||
void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; }
|
||||
|
||||
void set_lbl_hash(enode* n);
|
||||
|
@ -290,13 +292,16 @@ namespace euf {
|
|||
|
||||
void add_th_var(enode* n, theory_var v, theory_id id);
|
||||
void set_th_propagates_diseqs(theory_id id);
|
||||
void set_merge_enabled(enode* n, bool enable_merge);
|
||||
void set_cgc_enabled(enode* n, bool enable_cgc);
|
||||
void set_merge_tf_enabled(enode* n, bool enable_merge_tf);
|
||||
|
||||
void set_value(enode* n, lbool value, justification j);
|
||||
void set_bool_var(enode* n, unsigned v) { n->set_bool_var(v); }
|
||||
void set_relevant(enode* n);
|
||||
void set_default_relevant(bool b) { m_default_relevant = b; }
|
||||
|
||||
void set_on_merge(std::function<void(enode* root,enode* other)>& on_merge) { m_on_merge.push_back(on_merge); }
|
||||
void set_on_propagate(std::function<void(enode* lit,enode* ante)>& on_propagate) { m_on_propagate_literal = on_propagate; }
|
||||
void set_on_make(std::function<void(enode* n)>& on_make) { m_on_make = on_make; }
|
||||
void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; }
|
||||
void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; }
|
||||
|
@ -306,11 +311,11 @@ namespace euf {
|
|||
void end_explain();
|
||||
bool uses_congruence() const { return m_uses_congruence; }
|
||||
template <typename T>
|
||||
void explain(ptr_vector<T>& justifications);
|
||||
void explain(ptr_vector<T>& justifications, cc_justification* cc);
|
||||
template <typename T>
|
||||
void explain_eq(ptr_vector<T>& justifications, enode* a, enode* b);
|
||||
void explain_eq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b);
|
||||
template <typename T>
|
||||
unsigned explain_diseq(ptr_vector<T>& justifications, enode* a, enode* b);
|
||||
unsigned explain_diseq(ptr_vector<T>& justifications, cc_justification* cc, enode* a, enode* b);
|
||||
enode_vector const& nodes() const { return m_nodes; }
|
||||
|
||||
ast_manager& get_manager() { return m; }
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace euf {
|
|||
if (is_root()) {
|
||||
VERIFY(!m_target);
|
||||
for (enode* p : enode_parents(this)) {
|
||||
if (!p->merge_enabled())
|
||||
if (!p->cgc_enabled())
|
||||
continue;
|
||||
bool found = false;
|
||||
for (enode* arg : enode_args(p)) {
|
||||
|
@ -49,7 +49,7 @@ namespace euf {
|
|||
if (c == this)
|
||||
continue;
|
||||
for (enode* p : enode_parents(c)) {
|
||||
if (!p->merge_enabled())
|
||||
if (!p->cgc_enabled())
|
||||
continue;
|
||||
bool found = false;
|
||||
for (enode* q : enode_parents(this)) {
|
||||
|
|
|
@ -48,7 +48,8 @@ namespace euf {
|
|||
bool m_mark3 = false;
|
||||
bool m_commutative = false;
|
||||
bool m_interpreted = false;
|
||||
bool m_merge_enabled = true;
|
||||
bool m_cgc_enabled = true;
|
||||
bool m_merge_tf_enabled = false;
|
||||
bool m_is_equality = false; // Does the expression represent an equality
|
||||
bool m_is_relevant = false;
|
||||
lbool m_value = l_undef; // Assignment by SAT solver for Boolean node
|
||||
|
@ -91,7 +92,7 @@ namespace euf {
|
|||
n->m_generation = generation,
|
||||
n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative();
|
||||
n->m_num_args = num_args;
|
||||
n->m_merge_enabled = true;
|
||||
n->m_cgc_enabled = true;
|
||||
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];
|
||||
|
@ -107,7 +108,7 @@ namespace euf {
|
|||
n->m_root = n;
|
||||
n->m_commutative = true;
|
||||
n->m_num_args = 2;
|
||||
n->m_merge_enabled = true;
|
||||
n->m_cgc_enabled = true;
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
n->m_args[i] = nullptr;
|
||||
return n;
|
||||
|
@ -121,7 +122,7 @@ namespace euf {
|
|||
n->m_root = n;
|
||||
n->m_commutative = true;
|
||||
n->m_num_args = 2;
|
||||
n->m_merge_enabled = true;
|
||||
n->m_cgc_enabled = true;
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
n->m_args[i] = nullptr;
|
||||
return n;
|
||||
|
@ -132,7 +133,8 @@ namespace euf {
|
|||
void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); }
|
||||
void replace_th_var(theory_var v, theory_id id) { m_th_vars.replace(v, id); }
|
||||
void del_th_var(theory_id id) { m_th_vars.del_var(id); }
|
||||
void set_merge_enabled(bool m) { m_merge_enabled = m; }
|
||||
void set_cgc_enabled(bool m) { m_cgc_enabled = m; }
|
||||
void set_merge_tf(bool m) { m_merge_tf_enabled = m; }
|
||||
void set_value(lbool v) { m_value = v; }
|
||||
void set_justification(justification j) { m_justification = j; }
|
||||
void set_is_equality() { m_is_equality = true; }
|
||||
|
@ -152,14 +154,13 @@ namespace euf {
|
|||
bool is_relevant() const { return m_is_relevant; }
|
||||
void set_relevant(bool b) { m_is_relevant = b; }
|
||||
lbool value() const { return m_value; }
|
||||
bool value_conflict() const { return value() != l_undef && get_root()->value() != l_undef && value() != get_root()->value(); }
|
||||
sat::bool_var bool_var() const { return m_bool_var; }
|
||||
bool is_cgr() const { return this == m_cg; }
|
||||
enode* get_cg() const { return m_cg; }
|
||||
bool commutative() const { return m_commutative; }
|
||||
void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; }
|
||||
bool merge_enabled() const { return m_merge_enabled; }
|
||||
bool merge_tf() const { return merge_enabled() && (class_size() > 1 || num_parents() > 0 || num_args() > 0); }
|
||||
bool cgc_enabled() const { return m_cgc_enabled; }
|
||||
bool merge_tf() const { return m_merge_tf_enabled && (class_size() > 1 || num_parents() > 0 || num_args() > 0); }
|
||||
|
||||
enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }
|
||||
unsigned hash() const { return m_expr->get_id(); }
|
||||
|
|
|
@ -201,8 +201,6 @@ namespace euf {
|
|||
enode_bool_pair etable::insert(enode * n) {
|
||||
// it doesn't make sense to insert a constant.
|
||||
SASSERT(n->num_args() > 0);
|
||||
SASSERT(!m_manager.is_and(n->get_expr()));
|
||||
SASSERT(!m_manager.is_or(n->get_expr()));
|
||||
enode * n_prime;
|
||||
void * t = get_table(n);
|
||||
switch (static_cast<table_kind>(GET_TAG(t))) {
|
||||
|
|
|
@ -13,6 +13,11 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2020-08-23
|
||||
|
||||
Notes:
|
||||
|
||||
- congruence closure justifications are given a timestamp so it is easy to sort them.
|
||||
See the longer descriptoin in euf_proof_checker.cpp
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
@ -27,11 +32,15 @@ namespace euf {
|
|||
};
|
||||
kind_t m_kind;
|
||||
bool m_comm;
|
||||
void* m_external;
|
||||
justification(bool comm):
|
||||
union {
|
||||
void* m_external;
|
||||
uint64_t m_timestamp;
|
||||
};
|
||||
|
||||
justification(bool comm, uint64_t ts):
|
||||
m_kind(kind_t::congruence_t),
|
||||
m_comm(comm),
|
||||
m_external(nullptr)
|
||||
m_timestamp(ts)
|
||||
{}
|
||||
|
||||
justification(void* ext):
|
||||
|
@ -48,12 +57,13 @@ namespace euf {
|
|||
{}
|
||||
|
||||
static justification axiom() { return justification(); }
|
||||
static justification congruence(bool c) { return justification(c); }
|
||||
static justification congruence(bool c, uint64_t ts) { return justification(c, ts); }
|
||||
static justification external(void* ext) { return justification(ext); }
|
||||
|
||||
bool is_external() const { return m_kind == kind_t::external_t; }
|
||||
bool is_congruence() const { return m_kind == kind_t::congruence_t; }
|
||||
bool is_commutative() const { return m_comm; }
|
||||
uint64_t timestamp() const { SASSERT(is_congruence()); return m_timestamp; }
|
||||
template <typename T>
|
||||
T* ext() const { SASSERT(is_external()); return static_cast<T*>(m_external); }
|
||||
|
||||
|
@ -64,7 +74,7 @@ namespace euf {
|
|||
case kind_t::axiom_t:
|
||||
return axiom();
|
||||
case kind_t::congruence_t:
|
||||
return congruence(m_comm);
|
||||
return congruence(m_comm, m_timestamp);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return axiom();
|
||||
|
@ -90,4 +100,8 @@ namespace euf {
|
|||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, justification const& j) {
|
||||
return j.display(out, nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,10 @@ public:
|
|||
bool empty() const { return m_subst.empty(); }
|
||||
unsigned size() const { return m_subst.size(); }
|
||||
void insert(expr * s, expr * def, proof * def_pr = nullptr, expr_dependency * def_dep = nullptr);
|
||||
void insert(expr* s, expr* def, expr_dependency* def_dep) { insert(s, def, nullptr, def_dep); }
|
||||
void erase(expr * s);
|
||||
expr* find(expr* s) { return m_subst[s]; }
|
||||
expr_dependency* dep(expr* s) { return (*m_subst_dep)[s]; }
|
||||
bool find(expr * s, expr * & def, proof * & def_pr);
|
||||
bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep);
|
||||
bool contains(expr * s);
|
||||
|
@ -56,6 +59,10 @@ public:
|
|||
std::ostream& display(std::ostream& out);
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, expr_substitution& s) {
|
||||
return s.display(out);
|
||||
}
|
||||
|
||||
class scoped_expr_substitution {
|
||||
expr_substitution& m_subst;
|
||||
expr_ref_vector m_trail;
|
||||
|
|
|
@ -64,15 +64,22 @@ bool has_skolem_functions(expr * n) {
|
|||
return false;
|
||||
}
|
||||
|
||||
subterms::subterms(expr_ref_vector const& es, bool include_bound): m_include_bound(include_bound), m_es(es) {}
|
||||
subterms::subterms(expr_ref const& e, bool include_bound) : m_include_bound(include_bound), m_es(e.m()) {if (e) m_es.push_back(e); }
|
||||
subterms::iterator subterms::begin() { return iterator(*this, true); }
|
||||
subterms::iterator subterms::end() { return iterator(*this, false); }
|
||||
subterms::iterator::iterator(subterms& f, bool start): m_include_bound(f.m_include_bound), m_es(f.m_es) {
|
||||
if (!start) m_es.reset();
|
||||
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) {
|
||||
if (!esp)
|
||||
m_esp = &m_es;
|
||||
else
|
||||
m_esp->reset();
|
||||
if (!m_visitedp)
|
||||
m_visitedp = &m_visited;
|
||||
if (start)
|
||||
m_esp->append(f.m_es.size(), f.m_es.data());
|
||||
}
|
||||
expr* subterms::iterator::operator*() {
|
||||
return m_es.back();
|
||||
return m_esp->back();
|
||||
}
|
||||
subterms::iterator subterms::iterator::operator++(int) {
|
||||
iterator tmp = *this;
|
||||
|
@ -80,27 +87,29 @@ subterms::iterator subterms::iterator::operator++(int) {
|
|||
return tmp;
|
||||
}
|
||||
subterms::iterator& subterms::iterator::operator++() {
|
||||
expr* e = m_es.back();
|
||||
m_visited.mark(e, true);
|
||||
expr* e = m_esp->back();
|
||||
// IF_VERBOSE(0, verbose_stream() << e->get_ref_count() << "\n");
|
||||
SASSERT(e->get_ref_count() > 0);
|
||||
m_visitedp->mark(e, true);
|
||||
if (is_app(e))
|
||||
for (expr* arg : *to_app(e))
|
||||
m_es.push_back(arg);
|
||||
m_esp->push_back(arg);
|
||||
else if (is_quantifier(e) && m_include_bound)
|
||||
m_es.push_back(to_quantifier(e)->get_expr());
|
||||
m_esp->push_back(to_quantifier(e)->get_expr());
|
||||
|
||||
while (!m_es.empty() && m_visited.is_marked(m_es.back()))
|
||||
m_es.pop_back();
|
||||
while (!m_esp->empty() && m_visitedp->is_marked(m_esp->back()))
|
||||
m_esp->pop_back();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool subterms::iterator::operator==(iterator const& other) const {
|
||||
// ignore state of visited
|
||||
if (other.m_es.size() != m_es.size()) {
|
||||
if (other.m_esp->size() != m_esp->size()) {
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = m_es.size(); i-- > 0; ) {
|
||||
if (m_es.get(i) != other.m_es.get(i))
|
||||
for (unsigned i = m_esp->size(); i-- > 0; ) {
|
||||
if (m_esp->get(i) != other.m_esp->get(i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -111,11 +120,11 @@ bool subterms::iterator::operator!=(iterator const& other) const {
|
|||
}
|
||||
|
||||
|
||||
subterms_postorder::subterms_postorder(expr_ref_vector const& es): m_es(es) {}
|
||||
subterms_postorder::subterms_postorder(expr_ref const& e) : m_es(e.m()) { if (e) m_es.push_back(e); }
|
||||
subterms_postorder::subterms_postorder(expr_ref_vector const& es, bool include_bound): m_include_bound(include_bound), m_es(es) {}
|
||||
subterms_postorder::subterms_postorder(expr_ref const& e, bool include_bound) : m_include_bound(include_bound), m_es(e.m()) { if (e) m_es.push_back(e); }
|
||||
subterms_postorder::iterator subterms_postorder::begin() { return iterator(*this, true); }
|
||||
subterms_postorder::iterator subterms_postorder::end() { return iterator(*this, false); }
|
||||
subterms_postorder::iterator::iterator(subterms_postorder& f, bool start): m_es(f.m_es) {
|
||||
subterms_postorder::iterator::iterator(subterms_postorder& f, bool start): m_include_bound(f.m_include_bound), m_es(f.m_es) {
|
||||
if (!start) m_es.reset();
|
||||
next();
|
||||
}
|
||||
|
@ -144,6 +153,13 @@ void subterms_postorder::iterator::next() {
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (is_quantifier(e) && m_include_bound) {
|
||||
expr* body = to_quantifier(e)->get_expr();
|
||||
if (!m_visited.is_marked(body)) {
|
||||
m_es.push_back(body);
|
||||
all_visited = false;
|
||||
}
|
||||
}
|
||||
if (all_visited) {
|
||||
m_visited.mark(e, true);
|
||||
break;
|
||||
|
|
|
@ -170,15 +170,20 @@ bool has_skolem_functions(expr * n);
|
|||
class subterms {
|
||||
bool m_include_bound = false;
|
||||
expr_ref_vector m_es;
|
||||
subterms(expr_ref const& e, bool include_bound);
|
||||
subterms(expr_ref_vector const& es, bool include_bound);
|
||||
ptr_vector<expr>* m_esp = nullptr;
|
||||
expr_mark* m_vp = nullptr;
|
||||
subterms(expr_ref const& e, bool include_bound, ptr_vector<expr>* esp, expr_mark* vp);
|
||||
subterms(expr_ref_vector const& es, bool include_bound, ptr_vector<expr>* esp, expr_mark* vp);
|
||||
public:
|
||||
~subterms() { if (m_vp) m_vp->reset(); }
|
||||
class iterator {
|
||||
bool m_include_bound = false;
|
||||
expr_ref_vector m_es;
|
||||
expr_mark m_visited;
|
||||
bool m_include_bound = false;
|
||||
ptr_vector<expr> m_es;
|
||||
ptr_vector<expr>* m_esp = nullptr;
|
||||
expr_mark m_visited;
|
||||
expr_mark* m_visitedp = nullptr;
|
||||
public:
|
||||
iterator(subterms& f, bool start);
|
||||
iterator(subterms& f, ptr_vector<expr>* esp, expr_mark* vp, bool start);
|
||||
expr* operator*();
|
||||
iterator operator++(int);
|
||||
iterator& operator++();
|
||||
|
@ -186,19 +191,24 @@ public:
|
|||
bool operator!=(iterator const& other) const;
|
||||
};
|
||||
|
||||
|
||||
static subterms all(expr_ref const& e) { return subterms(e, true); }
|
||||
static subterms ground(expr_ref const& e) { return subterms(e, false); }
|
||||
static subterms all(expr_ref_vector const& e) { return subterms(e, true); }
|
||||
static subterms ground(expr_ref_vector const& e) { return subterms(e, false); }
|
||||
static subterms all(expr_ref const& e, ptr_vector<expr>* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, true, esp, vp); }
|
||||
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();
|
||||
};
|
||||
|
||||
class subterms_postorder {
|
||||
bool m_include_bound;
|
||||
expr_ref_vector m_es;
|
||||
subterms_postorder(expr_ref_vector const& es, bool include_bound);
|
||||
subterms_postorder(expr_ref const& e, bool include_bound);
|
||||
|
||||
|
||||
public:
|
||||
class iterator {
|
||||
bool m_include_bound = false;
|
||||
expr_ref_vector m_es;
|
||||
expr_mark m_visited, m_seen;
|
||||
void next();
|
||||
|
@ -210,8 +220,10 @@ public:
|
|||
bool operator==(iterator const& other) const;
|
||||
bool operator!=(iterator const& other) const;
|
||||
};
|
||||
subterms_postorder(expr_ref_vector const& es);
|
||||
subterms_postorder(expr_ref const& e);
|
||||
static subterms_postorder all(expr_ref_vector const& es) { return subterms_postorder(es, true); }
|
||||
static subterms_postorder ground(expr_ref_vector const& es) { return subterms_postorder(es, false); }
|
||||
static subterms_postorder all(expr_ref const& e) { return subterms_postorder(e, true); }
|
||||
static subterms_postorder ground(expr_ref const& e) { return subterms_postorder(e, false); }
|
||||
iterator begin();
|
||||
iterator end();
|
||||
};
|
||||
|
|
|
@ -147,17 +147,13 @@ namespace format_ns {
|
|||
parameter p(s);
|
||||
return fm(m).mk_app(fid(m), OP_STRING, 1, &p, 0, nullptr);
|
||||
}
|
||||
|
||||
|
||||
format * mk_int(ast_manager & m, int i) {
|
||||
char buffer[128];
|
||||
SPRINTF_D(buffer, i);
|
||||
return mk_string(m, buffer);
|
||||
return mk_string(m, std::to_string(i));
|
||||
}
|
||||
|
||||
format * mk_unsigned(ast_manager & m, unsigned u) {
|
||||
char buffer[128];
|
||||
SPRINTF_U(buffer, u);
|
||||
return mk_string(m, buffer);
|
||||
return mk_string(m, std::to_string(u));
|
||||
}
|
||||
|
||||
format * mk_indent(ast_manager & m, unsigned i, format * f) {
|
||||
|
|
|
@ -324,8 +324,8 @@ 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)) {
|
||||
expr_ref_vector dom(m);
|
||||
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);
|
||||
result->set_else(else_value);
|
||||
|
|
|
@ -2809,6 +2809,8 @@ 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()))));
|
||||
}
|
||||
|
||||
SASSERT(is_well_sorted(m, result));
|
||||
|
@ -3288,7 +3290,7 @@ void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, ex
|
|||
void fpa2bv_converter::mk_to_ieee_bv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result)
|
||||
{
|
||||
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_IEEE_BV, 0, nullptr, num, args), m);
|
||||
mk_to_bv(f, num, args, true, result);
|
||||
mk_to_ieee_bv(fu, num, args, result);
|
||||
}
|
||||
|
||||
void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) {
|
||||
|
@ -3475,12 +3477,12 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg
|
|||
|
||||
void fpa2bv_converter::mk_to_ubv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
|
||||
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_UBV, 0, nullptr, num, args), m);
|
||||
mk_to_bv(f, num, args, false, result);
|
||||
mk_to_bv(fu, num, args, false, result);
|
||||
}
|
||||
|
||||
void fpa2bv_converter::mk_to_sbv_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
|
||||
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_SBV, 0, nullptr, num, args), m);
|
||||
mk_to_bv(f, num, args, true, result);
|
||||
mk_to_bv(fu, num, args, true, result);
|
||||
}
|
||||
|
||||
expr_ref fpa2bv_converter::nan_wrap(expr * n) {
|
||||
|
@ -3529,7 +3531,7 @@ void fpa2bv_converter::mk_to_real_unspecified(func_decl * f, unsigned num, expr
|
|||
|
||||
void fpa2bv_converter::mk_to_real_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
|
||||
func_decl_ref fu(m.mk_func_decl(f->get_family_id(), OP_FPA_TO_REAL, 0, nullptr, num, args), m);
|
||||
mk_to_real(f, num, args, result);
|
||||
mk_to_real(fu, num, args, result);
|
||||
}
|
||||
|
||||
void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
|
||||
|
|
|
@ -33,8 +33,7 @@ public:
|
|||
justified_expr(justified_expr const& other):
|
||||
m(other.m),
|
||||
m_fml(other.m_fml),
|
||||
m_proof(other.m_proof)
|
||||
{
|
||||
m_proof(other.m_proof) {
|
||||
m.inc_ref(m_fml);
|
||||
m.inc_ref(m_proof);
|
||||
}
|
||||
|
@ -42,8 +41,7 @@ public:
|
|||
justified_expr(justified_expr && other) noexcept :
|
||||
m(other.m),
|
||||
m_fml(nullptr),
|
||||
m_proof(nullptr)
|
||||
{
|
||||
m_proof(nullptr) {
|
||||
std::swap(m_fml, other.m_fml);
|
||||
std::swap(m_proof, other.m_proof);
|
||||
}
|
||||
|
@ -51,10 +49,11 @@ public:
|
|||
~justified_expr() {
|
||||
m.dec_ref(m_fml);
|
||||
m.dec_ref(m_proof);
|
||||
m_fml = nullptr;
|
||||
m_proof = nullptr;
|
||||
m_fml = nullptr;
|
||||
m_proof = nullptr;
|
||||
}
|
||||
|
||||
expr* get_fml() const { return m_fml; }
|
||||
|
||||
proof* get_proof() const { return m_proof; }
|
||||
};
|
||||
|
|
|
@ -175,12 +175,6 @@ namespace macro_manager_ns {
|
|||
/**
|
||||
\brief Mark all func_decls used in exprs as forbidden.
|
||||
*/
|
||||
void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) {
|
||||
expr_mark visited;
|
||||
macro_manager_ns::proc p(m_forbidden_set, m_forbidden);
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
for_each_expr(p, visited, exprs[i]);
|
||||
}
|
||||
|
||||
void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) {
|
||||
expr_mark visited;
|
||||
|
|
|
@ -73,9 +73,7 @@ public:
|
|||
void push_scope();
|
||||
void pop_scope(unsigned num_scopes);
|
||||
void reset();
|
||||
void mark_forbidden(unsigned n, expr * const * exprs);
|
||||
void mark_forbidden(unsigned n, justified_expr const * exprs);
|
||||
void mark_forbidden(expr * e) { mark_forbidden(1, &e); }
|
||||
bool is_forbidden(func_decl * d) const { return m_forbidden_set.contains(d); }
|
||||
obj_hashtable<func_decl> const & get_forbidden_set() const { return m_forbidden_set; }
|
||||
void display(std::ostream & out);
|
||||
|
|
|
@ -28,7 +28,7 @@ Revision History:
|
|||
#include "ast/rewriter/bool_rewriter.h"
|
||||
|
||||
macro_util::macro_util(ast_manager & m):
|
||||
m_manager(m),
|
||||
m(m),
|
||||
m_bv(m),
|
||||
m_arith(m),
|
||||
m_arith_rw(m),
|
||||
|
@ -176,7 +176,7 @@ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const {
|
|||
*/
|
||||
bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const {
|
||||
expr * lhs = nullptr, * rhs = nullptr;
|
||||
if (m_manager.is_eq(n, lhs, rhs) &&
|
||||
if (m.is_eq(n, lhs, rhs) &&
|
||||
is_macro_head(lhs, num_decls) &&
|
||||
!is_forbidden(to_app(lhs)->get_decl()) &&
|
||||
!occurs(to_app(lhs)->get_decl(), rhs)) {
|
||||
|
@ -184,13 +184,13 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & he
|
|||
def = rhs;
|
||||
return true;
|
||||
}
|
||||
if (m_manager.is_not(n, lhs) && m_manager.is_eq(lhs, lhs, rhs) &&
|
||||
m_manager.is_bool(lhs) &&
|
||||
if (m.is_not(n, lhs) && m.is_eq(lhs, lhs, rhs) &&
|
||||
m.is_bool(lhs) &&
|
||||
is_macro_head(lhs, num_decls) &&
|
||||
!is_forbidden(to_app(lhs)->get_decl()) &&
|
||||
!occurs(to_app(lhs)->get_decl(), rhs)) {
|
||||
head = to_app(lhs);
|
||||
def = m_manager.mk_not(rhs);
|
||||
def = m.mk_not(rhs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -216,7 +216,7 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & he
|
|||
*/
|
||||
bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const {
|
||||
expr * lhs = nullptr, * rhs = nullptr;
|
||||
if (m_manager.is_eq(n, lhs, rhs) &&
|
||||
if (m.is_eq(n, lhs, rhs) &&
|
||||
is_macro_head(rhs, num_decls) &&
|
||||
!is_forbidden(to_app(rhs)->get_decl()) &&
|
||||
!occurs(to_app(rhs)->get_decl(), lhs)) {
|
||||
|
@ -224,13 +224,13 @@ bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & h
|
|||
def = lhs;
|
||||
return true;
|
||||
}
|
||||
if (m_manager.is_not(n, n) && m_manager.is_eq(n, lhs, rhs) &&
|
||||
m_manager.is_bool(lhs) &&
|
||||
if (m.is_not(n, n) && m.is_eq(n, lhs, rhs) &&
|
||||
m.is_bool(lhs) &&
|
||||
is_macro_head(rhs, num_decls) &&
|
||||
!is_forbidden(to_app(rhs)->get_decl()) &&
|
||||
!occurs(to_app(rhs)->get_decl(), lhs)) {
|
||||
head = to_app(rhs);
|
||||
def = m_manager.mk_not(lhs);
|
||||
def = m.mk_not(lhs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -262,7 +262,7 @@ bool macro_util::poly_contains_head(expr * n, func_decl * f, expr * exception) c
|
|||
|
||||
bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const {
|
||||
// TODO: obsolete... we should move to collect_arith_macro_candidates
|
||||
if (!m_manager.is_eq(n) && !m_arith.is_le(n) && !m_arith.is_ge(n))
|
||||
if (!m.is_eq(n) && !m_arith.is_le(n) && !m_arith.is_ge(n))
|
||||
return false;
|
||||
expr * lhs = to_app(n)->get_arg(0);
|
||||
expr * rhs = to_app(n)->get_arg(1);
|
||||
|
@ -306,7 +306,7 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex
|
|||
if (h == nullptr)
|
||||
return false;
|
||||
head = to_app(h);
|
||||
expr_ref tmp(m_manager);
|
||||
expr_ref tmp(m);
|
||||
tmp = m_arith.mk_add(args.size(), args.data());
|
||||
if (inv)
|
||||
mk_sub(tmp, rhs, def);
|
||||
|
@ -321,12 +321,12 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex
|
|||
*/
|
||||
bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app_ref & head, app_ref & t) {
|
||||
expr* lhs = nullptr, *rhs = nullptr;
|
||||
if (!m_manager.is_eq(n, lhs, rhs))
|
||||
if (!m.is_eq(n, lhs, rhs))
|
||||
return false;
|
||||
if (!is_ground(lhs) && !is_ground(rhs))
|
||||
return false;
|
||||
sort * s = lhs->get_sort();
|
||||
if (m_manager.is_uninterp(s))
|
||||
if (m.is_uninterp(s))
|
||||
return false;
|
||||
sort_size sz = s->get_num_elements();
|
||||
if (sz.is_finite() && sz.size() == 1)
|
||||
|
@ -351,11 +351,11 @@ bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app_ref & head, ap
|
|||
bool macro_util::is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t, expr_ref & def) {
|
||||
if (!is_forall(n))
|
||||
return false;
|
||||
TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";);
|
||||
TRACE("macro_util", tout << "processing: " << mk_pp(n, m) << "\n";);
|
||||
expr * body = to_quantifier(n)->get_expr();
|
||||
unsigned num_decls = to_quantifier(n)->get_num_decls();
|
||||
expr * lhs, *rhs;
|
||||
if (!m_manager.is_iff(body, lhs, rhs))
|
||||
if (!m.is_iff(body, lhs, rhs))
|
||||
return false;
|
||||
if (is_pseudo_head(lhs, num_decls, head, t) &&
|
||||
!is_forbidden(head->get_decl()) &&
|
||||
|
@ -417,13 +417,11 @@ bool macro_util::is_quasi_macro_ok(expr * n, unsigned num_decls, expr * def) con
|
|||
if (is_app(n) &&
|
||||
to_app(n)->get_family_id() == null_family_id &&
|
||||
to_app(n)->get_num_args() >= num_decls) {
|
||||
unsigned num_args = to_app(n)->get_num_args();
|
||||
sbuffer<bool> found_vars;
|
||||
found_vars.resize(num_decls, false);
|
||||
unsigned num_found_vars = 0;
|
||||
expr_free_vars fv;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = to_app(n)->get_arg(i);
|
||||
for (expr* arg : *to_app(n)) {
|
||||
if (occurs(to_app(n)->get_decl(), arg))
|
||||
return false;
|
||||
else
|
||||
|
@ -466,14 +464,14 @@ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned & num_decl
|
|||
continue;
|
||||
}
|
||||
}
|
||||
var * new_var = m_manager.mk_var(next_var_idx, arg->get_sort());
|
||||
var * new_var = m.mk_var(next_var_idx, arg->get_sort());
|
||||
next_var_idx++;
|
||||
expr * new_cond = m_manager.mk_eq(new_var, arg);
|
||||
expr * new_cond = m.mk_eq(new_var, arg);
|
||||
new_args.push_back(new_var);
|
||||
new_conds.push_back(new_cond);
|
||||
}
|
||||
bool_rewriter(m_manager).mk_and(new_conds.size(), new_conds.data(), cond);
|
||||
head = m_manager.mk_app(qhead->get_decl(), new_args.size(), new_args.data());
|
||||
bool_rewriter(m).mk_and(new_conds.size(), new_conds.data(), cond);
|
||||
head = m.mk_app(qhead->get_decl(), new_args.size(), new_args.data());
|
||||
num_decls = next_var_idx;
|
||||
}
|
||||
|
||||
|
@ -485,7 +483,7 @@ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned & num_decl
|
|||
See normalize_expr
|
||||
*/
|
||||
void macro_util::mk_macro_interpretation(app * head, unsigned num_decls, expr * def, expr_ref & interp) const {
|
||||
TRACE("macro_util", tout << mk_pp(head, m_manager) << "\n" << mk_pp(def, m_manager) << "\n";);
|
||||
TRACE("macro_util", tout << mk_pp(head, m) << "\n" << mk_pp(def, m) << "\n";);
|
||||
SASSERT(is_macro_head(head, head->get_num_args()) ||
|
||||
is_quasi_macro_ok(head, head->get_num_args(), def));
|
||||
SASSERT(!occurs(head->get_decl(), def));
|
||||
|
@ -503,20 +501,20 @@ void macro_util::mk_macro_interpretation(app * head, unsigned num_decls, expr *
|
|||
f(x_3, x_2) --> f(x_0, x_1)
|
||||
*/
|
||||
void macro_util::normalize_expr(app * head, unsigned num_decls, expr * t, expr_ref & norm_t) const {
|
||||
expr_ref_buffer var_mapping(m_manager);
|
||||
expr_ref_buffer var_mapping(m);
|
||||
var_mapping.resize(num_decls);
|
||||
bool changed = false;
|
||||
unsigned num_args = head->get_num_args();
|
||||
TRACE("macro_util",
|
||||
tout << "head: " << mk_pp(head, m_manager) << "\n";
|
||||
tout << "applying substitution to:\n" << mk_bounded_pp(t, m_manager) << "\n";);
|
||||
tout << "head: " << mk_pp(head, m) << "\n";
|
||||
tout << "applying substitution to:\n" << mk_bounded_pp(t, m) << "\n";);
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
var * v = to_var(head->get_arg(i));
|
||||
unsigned vi = v->get_idx();
|
||||
SASSERT(vi < num_decls);
|
||||
if (vi != i) {
|
||||
changed = true;
|
||||
var_ref new_var(m_manager.mk_var(i, v->get_sort()), m_manager);
|
||||
var_ref new_var(m.mk_var(i, v->get_sort()), m);
|
||||
var_mapping.setx(num_decls - vi - 1, new_var);
|
||||
}
|
||||
else
|
||||
|
@ -525,13 +523,13 @@ void macro_util::normalize_expr(app * head, unsigned num_decls, expr * t, expr_r
|
|||
|
||||
if (changed) {
|
||||
// REMARK: t may have nested quantifiers... So, I must use the std order for variable substitution.
|
||||
var_subst subst(m_manager, true);
|
||||
var_subst subst(m, true);
|
||||
TRACE("macro_util",
|
||||
tout << "head: " << mk_pp(head, m_manager) << "\n";
|
||||
tout << "applying substitution to:\n" << mk_ll_pp(t, m_manager) << "\nsubstitution:\n";
|
||||
tout << "head: " << mk_pp(head, m) << "\n";
|
||||
tout << "applying substitution to:\n" << mk_ll_pp(t, m) << "\nsubstitution:\n";
|
||||
for (unsigned i = 0; i < var_mapping.size(); i++) {
|
||||
if (var_mapping[i] != 0)
|
||||
tout << "#" << i << " -> " << mk_ll_pp(var_mapping[i], m_manager);
|
||||
tout << "#" << i << " -> " << mk_ll_pp(var_mapping[i], m);
|
||||
});
|
||||
norm_t = subst(t, var_mapping.size(), var_mapping.data());
|
||||
}
|
||||
|
@ -553,12 +551,9 @@ bool is_hint_head(expr * n, ptr_buffer<var> & vars) {
|
|||
return false;
|
||||
if (to_app(n)->get_decl()->is_associative() || to_app(n)->get_family_id() != null_family_id)
|
||||
return false;
|
||||
unsigned num_args = to_app(n)->get_num_args();
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = to_app(n)->get_arg(i);
|
||||
for (expr * arg : *to_app(n))
|
||||
if (is_var(arg))
|
||||
vars.push_back(to_var(arg));
|
||||
}
|
||||
return !vars.empty();
|
||||
}
|
||||
|
||||
|
@ -579,9 +574,7 @@ bool vars_of_is_subset(expr * n, ptr_buffer<var> const & vars) {
|
|||
return false;
|
||||
}
|
||||
else if (is_app(curr)) {
|
||||
unsigned num_args = to_app(curr)->get_num_args();
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = to_app(curr)->get_arg(i);
|
||||
for (expr * arg : *to_app(curr)) {
|
||||
if (is_ground(arg))
|
||||
continue;
|
||||
if (visited.contains(arg))
|
||||
|
@ -611,13 +604,11 @@ bool is_hint_atom(expr * lhs, expr * rhs) {
|
|||
}
|
||||
|
||||
void hint_to_macro_head(ast_manager & m, app * head, unsigned & num_decls, app_ref & new_head) {
|
||||
unsigned num_args = head->get_num_args();
|
||||
ptr_buffer<expr> new_args;
|
||||
sbuffer<bool> found_vars;
|
||||
found_vars.resize(num_decls, false);
|
||||
unsigned next_var_idx = num_decls;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = head->get_arg(i);
|
||||
for (expr * arg : *head) {
|
||||
if (is_var(arg)) {
|
||||
unsigned idx = to_var(arg)->get_idx();
|
||||
SASSERT(idx < num_decls);
|
||||
|
@ -642,8 +633,8 @@ void hint_to_macro_head(ast_manager & m, app * head, unsigned & num_decls, app_r
|
|||
is_hint_head(head, vars) must also return true
|
||||
*/
|
||||
bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) {
|
||||
TRACE("macro_util", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n";
|
||||
if (exception) tout << mk_pp(exception, m_manager); else tout << "<null>";
|
||||
TRACE("macro_util", tout << "is_poly_hint n:\n" << mk_pp(n, m) << "\nhead:\n" << mk_pp(head, m) << "\nexception:\n";
|
||||
if (exception) tout << mk_pp(exception, m); else tout << "<null>";
|
||||
tout << "\n";);
|
||||
ptr_buffer<var> vars;
|
||||
if (!is_hint_head(head, vars)) {
|
||||
|
@ -664,7 +655,7 @@ bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) {
|
|||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (arg != exception && (occurs(f, arg) || !vars_of_is_subset(arg, vars))) {
|
||||
TRACE("macro_util", tout << "failed because of:\n" << mk_pp(arg, m_manager) << "\n";);
|
||||
TRACE("macro_util", tout << "failed because of:\n" << mk_pp(arg, m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -710,36 +701,36 @@ void macro_util::macro_candidates::insert(func_decl * f, expr * def, expr * cond
|
|||
// -----------------------------
|
||||
|
||||
void macro_util::insert_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) {
|
||||
expr_ref norm_def(m_manager);
|
||||
expr_ref norm_cond(m_manager);
|
||||
expr_ref norm_def(m);
|
||||
expr_ref norm_cond(m);
|
||||
normalize_expr(head, num_decls, def, norm_def);
|
||||
if (cond != nullptr)
|
||||
normalize_expr(head, num_decls, cond, norm_cond);
|
||||
else if (!hint)
|
||||
norm_cond = m_manager.mk_true();
|
||||
norm_cond = m.mk_true();
|
||||
SASSERT(!hint || norm_cond.get() == 0);
|
||||
r.insert(head->get_decl(), norm_def.get(), norm_cond.get(), ineq, satisfy_atom, hint);
|
||||
}
|
||||
|
||||
void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom,
|
||||
bool hint, macro_candidates & r) {
|
||||
TRACE("macro_util", tout << expr_ref(head, m_manager) << "\n";);
|
||||
TRACE("macro_util", tout << expr_ref(head, m) << "\n";);
|
||||
if (!is_macro_head(head, head->get_num_args())) {
|
||||
app_ref new_head(m_manager);
|
||||
expr_ref extra_cond(m_manager);
|
||||
expr_ref new_cond(m_manager);
|
||||
app_ref new_head(m);
|
||||
expr_ref extra_cond(m);
|
||||
expr_ref new_cond(m);
|
||||
if (!hint) {
|
||||
quasi_macro_head_to_macro_head(head, num_decls, new_head, extra_cond);
|
||||
if (cond == nullptr)
|
||||
new_cond = extra_cond;
|
||||
else
|
||||
bool_rewriter(m_manager).mk_and(cond, extra_cond, new_cond);
|
||||
bool_rewriter(m).mk_and(cond, extra_cond, new_cond);
|
||||
}
|
||||
else {
|
||||
hint_to_macro_head(m_manager, head, num_decls, new_head);
|
||||
hint_to_macro_head(m, head, num_decls, new_head);
|
||||
TRACE("macro_util",
|
||||
tout << "hint macro head: " << mk_ismt2_pp(new_head, m_manager) << std::endl;
|
||||
tout << "hint macro def: " << mk_ismt2_pp(def, m_manager) << std::endl; );
|
||||
tout << "hint macro head: " << mk_ismt2_pp(new_head, m) << std::endl;
|
||||
tout << "hint macro def: " << mk_ismt2_pp(def, m) << std::endl; );
|
||||
}
|
||||
insert_macro(new_head, num_decls, def, new_cond, ineq, satisfy_atom, hint, r);
|
||||
}
|
||||
|
@ -751,10 +742,10 @@ void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def,
|
|||
bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) {
|
||||
if (m_curr_clause == nullptr)
|
||||
return false;
|
||||
SASSERT(is_clause(m_manager, m_curr_clause));
|
||||
unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause);
|
||||
SASSERT(is_clause(m, m_curr_clause));
|
||||
unsigned num_lits = get_clause_num_literals(m, m_curr_clause);
|
||||
for (unsigned i = 0; i < num_lits; i++) {
|
||||
expr * l = get_clause_literal(m_manager, m_curr_clause, i);
|
||||
expr * l = get_clause_literal(m, m_curr_clause, i);
|
||||
if (l != except_lit && occurs(f, l))
|
||||
return true;
|
||||
}
|
||||
|
@ -764,20 +755,20 @@ bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) {
|
|||
void macro_util::get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond) {
|
||||
if (m_curr_clause == nullptr)
|
||||
return;
|
||||
SASSERT(is_clause(m_manager, m_curr_clause));
|
||||
expr_ref_buffer neg_other_lits(m_manager);
|
||||
unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause);
|
||||
SASSERT(is_clause(m, m_curr_clause));
|
||||
expr_ref_buffer neg_other_lits(m);
|
||||
unsigned num_lits = get_clause_num_literals(m, m_curr_clause);
|
||||
for (unsigned i = 0; i < num_lits; i++) {
|
||||
expr * l = get_clause_literal(m_manager, m_curr_clause, i);
|
||||
expr * l = get_clause_literal(m, m_curr_clause, i);
|
||||
if (l != except_lit) {
|
||||
expr_ref neg_l(m_manager);
|
||||
bool_rewriter(m_manager).mk_not(l, neg_l);
|
||||
expr_ref neg_l(m);
|
||||
bool_rewriter(m).mk_not(l, neg_l);
|
||||
neg_other_lits.push_back(neg_l);
|
||||
}
|
||||
}
|
||||
if (neg_other_lits.empty())
|
||||
return;
|
||||
bool_rewriter(m_manager).mk_and(neg_other_lits.size(), neg_other_lits.data(), extra_cond);
|
||||
bool_rewriter(m).mk_and(neg_other_lits.size(), neg_other_lits.data(), extra_cond);
|
||||
}
|
||||
|
||||
void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer<expr> & args) {
|
||||
|
@ -800,14 +791,14 @@ void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer<expr>
|
|||
}
|
||||
|
||||
void macro_util::add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r) {
|
||||
expr_ref cond(m_manager);
|
||||
expr_ref cond(m);
|
||||
if (!hint)
|
||||
get_rest_clause_as_cond(atom, cond);
|
||||
insert_quasi_macro(head, num_decls, def, cond, ineq, true, hint, r);
|
||||
}
|
||||
|
||||
void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool is_ineq, macro_candidates & r) {
|
||||
if (!is_add(lhs) && m_manager.is_eq(atom)) // this case is a simple macro.
|
||||
if (!is_add(lhs) && m.is_eq(atom)) // this case is a simple macro.
|
||||
return;
|
||||
ptr_buffer<expr> args;
|
||||
unsigned lhs_num_args;
|
||||
|
@ -837,9 +828,9 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a
|
|||
|
||||
if (_is_arith_macro || _is_poly_hint) {
|
||||
collect_poly_args(lhs, arg, args);
|
||||
expr_ref rest(m_manager);
|
||||
expr_ref rest(m);
|
||||
mk_add(args.size(), args.data(), arg->get_sort(), rest);
|
||||
expr_ref def(m_manager);
|
||||
expr_ref def(m);
|
||||
mk_sub(rhs, rest, def);
|
||||
// If is_poly_hint, rhs may contain variables that do not occur in to_app(arg).
|
||||
// So, we should re-check.
|
||||
|
@ -858,9 +849,9 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a
|
|||
|
||||
if (_is_arith_macro || _is_poly_hint) {
|
||||
collect_poly_args(lhs, arg, args);
|
||||
expr_ref rest(m_manager);
|
||||
expr_ref rest(m);
|
||||
mk_add(args.size(), args.data(), arg->get_sort(), rest);
|
||||
expr_ref def(m_manager);
|
||||
expr_ref def(m);
|
||||
mk_sub(rest, rhs, def);
|
||||
// If is_poly_hint, rhs may contain variables that do not occur in to_app(neg_arg).
|
||||
// So, we should re-check.
|
||||
|
@ -872,12 +863,12 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a
|
|||
}
|
||||
|
||||
void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) {
|
||||
TRACE("macro_util", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m_manager) << "\n";);
|
||||
if (!m_manager.is_eq(atom) && !is_le_ge(atom))
|
||||
TRACE("macro_util", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m) << "\n";);
|
||||
if (!m.is_eq(atom) && !is_le_ge(atom))
|
||||
return;
|
||||
expr * lhs = to_app(atom)->get_arg(0);
|
||||
expr * rhs = to_app(atom)->get_arg(1);
|
||||
bool is_ineq = !m_manager.is_eq(atom);
|
||||
bool is_ineq = !m.is_eq(atom);
|
||||
collect_arith_macro_candidates(lhs, rhs, atom, num_decls, is_ineq, r);
|
||||
collect_arith_macro_candidates(rhs, lhs, atom, num_decls, is_ineq, r);
|
||||
}
|
||||
|
@ -921,32 +912,40 @@ void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls,
|
|||
void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r) {
|
||||
expr* lhs, *rhs;
|
||||
|
||||
TRACE("macro_util", tout << "Candidate check for: " << mk_ismt2_pp(atom, m_manager) << std::endl;);
|
||||
TRACE("macro_util", tout << "Candidate check for: " << mk_ismt2_pp(atom, m) << std::endl;);
|
||||
|
||||
if (m_manager.is_eq(atom, lhs, rhs)) {
|
||||
auto insert_quasi = [&](expr* lhs, expr* rhs) {
|
||||
if (is_quasi_macro_head(lhs, num_decls) &&
|
||||
!is_forbidden(to_app(lhs)->get_decl()) &&
|
||||
!occurs(to_app(lhs)->get_decl(), rhs) &&
|
||||
!rest_contains_decl(to_app(lhs)->get_decl(), atom)) {
|
||||
expr_ref cond(m_manager);
|
||||
expr_ref cond(m);
|
||||
get_rest_clause_as_cond(atom, cond);
|
||||
insert_quasi_macro(to_app(lhs), num_decls, rhs, cond, false, true, false, r);
|
||||
return true;
|
||||
}
|
||||
else if (is_hint_atom(lhs, rhs)) {
|
||||
insert_quasi_macro(to_app(lhs), num_decls, rhs, nullptr, false, true, true, r);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (is_quasi_macro_head(rhs, num_decls) &&
|
||||
!is_forbidden(to_app(rhs)->get_decl()) &&
|
||||
!occurs(to_app(rhs)->get_decl(), lhs) &&
|
||||
!rest_contains_decl(to_app(rhs)->get_decl(), atom)) {
|
||||
expr_ref cond(m_manager);
|
||||
get_rest_clause_as_cond(atom, cond);
|
||||
insert_quasi_macro(to_app(rhs), num_decls, lhs, cond, false, true, false, r);
|
||||
}
|
||||
else if (is_hint_atom(rhs, lhs)) {
|
||||
insert_quasi_macro(to_app(rhs), num_decls, lhs, nullptr, false, true, true, r);
|
||||
}
|
||||
auto insert_hint = [&](expr* lhs, expr* rhs) {
|
||||
if (is_hint_atom(lhs, rhs))
|
||||
insert_quasi_macro(to_app(lhs), num_decls, rhs, nullptr, false, true, true, r);
|
||||
};
|
||||
|
||||
if (m.is_eq(atom, lhs, rhs)) {
|
||||
if (!insert_quasi(lhs, rhs))
|
||||
insert_hint(lhs, rhs);
|
||||
if (!insert_quasi(rhs, lhs))
|
||||
insert_hint(rhs, lhs);
|
||||
}
|
||||
expr* atom2;
|
||||
if (m.is_not(atom, atom2) && m.is_eq(atom2, lhs, rhs) && m.is_bool(lhs)) {
|
||||
expr_ref nlhs(m.mk_not(lhs), m);
|
||||
expr_ref nrhs(m.mk_not(rhs), m);
|
||||
if (!insert_quasi(lhs, nrhs))
|
||||
insert_hint(lhs, nrhs);
|
||||
if (!insert_quasi(rhs, nlhs))
|
||||
insert_hint(rhs, nlhs);
|
||||
}
|
||||
|
||||
collect_arith_macro_candidates(atom, num_decls, r);
|
||||
|
@ -965,11 +964,11 @@ void macro_util::collect_macro_candidates(quantifier * q, macro_candidates & r)
|
|||
return;
|
||||
unsigned num_decls = q->get_num_decls();
|
||||
SASSERT(m_curr_clause == 0);
|
||||
if (is_clause(m_manager, n)) {
|
||||
if (is_clause(m, n)) {
|
||||
m_curr_clause = n;
|
||||
unsigned num_lits = get_clause_num_literals(m_manager, n);
|
||||
unsigned num_lits = get_clause_num_literals(m, n);
|
||||
for (unsigned i = 0; i < num_lits; i++)
|
||||
collect_macro_candidates_core(get_clause_literal(m_manager, n, i), num_decls, r);
|
||||
collect_macro_candidates_core(get_clause_literal(m, n, i), num_decls, r);
|
||||
m_curr_clause = nullptr;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
ast_manager & m_manager;
|
||||
ast_manager & m;
|
||||
bv_util m_bv;
|
||||
arith_util m_arith;
|
||||
mutable arith_rewriter m_arith_rw;
|
||||
|
|
|
@ -114,10 +114,9 @@ bool quasi_macros::fully_depends_on(app * a, quantifier * q) const {
|
|||
if (is_var(arg))
|
||||
bitset.set(to_var(arg)->get_idx(), true);
|
||||
|
||||
for (unsigned i = 0; i < bitset.size() ; i++) {
|
||||
for (unsigned i = 0; i < bitset.size() ; i++)
|
||||
if (!bitset.get(i))
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -167,18 +166,18 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
|
|||
quantifier * q = to_quantifier(e);
|
||||
expr * qe = q->get_expr(), *lhs = nullptr, *rhs = nullptr;
|
||||
if (m.is_eq(qe, lhs, rhs)) {
|
||||
if (is_quasi_def(q, lhs, rhs)) {
|
||||
if (is_quasi_def(q, lhs, rhs)) {
|
||||
a = to_app(lhs);
|
||||
t = rhs;
|
||||
return true;
|
||||
} else if (is_quasi_def(q, rhs, lhs)) {
|
||||
} else if (is_quasi_def(q, rhs, lhs)) {
|
||||
a = to_app(rhs);
|
||||
t = lhs;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (m.is_not(qe, lhs) && is_non_ground_uninterp(lhs) &&
|
||||
is_unique(to_app(lhs)->get_decl())) { // this is like f(...) = false
|
||||
is_unique(to_app(lhs)->get_decl())) { // this is like f(...) = false
|
||||
a = to_app(lhs);
|
||||
t = m.mk_false();
|
||||
return true;
|
||||
|
@ -189,8 +188,8 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
|
|||
return true;
|
||||
}
|
||||
else if (m.is_not(qe, lhs) && m.is_eq(lhs, lhs, rhs) && m.is_bool(lhs)) {
|
||||
if (is_quasi_def(q, lhs, rhs)) {
|
||||
a = to_app(lhs);
|
||||
if (is_quasi_def(q, lhs, rhs)) {
|
||||
a = to_app(lhs);
|
||||
t = m.mk_not(rhs);
|
||||
return true;
|
||||
} else if (is_quasi_def(q, rhs, lhs)) {
|
||||
|
|
|
@ -22,6 +22,7 @@ Notes:
|
|||
#include "ast/normal_forms/nnf.h"
|
||||
#include "ast/normal_forms/nnf_params.hpp"
|
||||
#include "ast/used_vars.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/well_sorted.h"
|
||||
#include "ast/act_cache.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
|
@ -137,7 +138,7 @@ class skolemizer {
|
|||
if (is_sk_hack(p)) {
|
||||
expr * sk_hack = to_app(p)->get_arg(0);
|
||||
if (q->get_kind() == forall_k) // check whether is in negative/positive context.
|
||||
tmp = m.mk_or(body, m.mk_not(sk_hack)); // negative context
|
||||
tmp = m.mk_or(body, mk_not(m, sk_hack)); // negative context
|
||||
else
|
||||
tmp = m.mk_and(body, sk_hack); // positive context
|
||||
body = tmp;
|
||||
|
@ -148,7 +149,7 @@ class skolemizer {
|
|||
p = nullptr;
|
||||
if (m_proofs_enabled) {
|
||||
if (q->get_kind() == forall_k)
|
||||
p = m.mk_skolemization(m.mk_not(q), m.mk_not(r));
|
||||
p = m.mk_skolemization(mk_not(m, q), mk_not(m, r));
|
||||
else
|
||||
p = m.mk_skolemization(q, r);
|
||||
}
|
||||
|
@ -388,7 +389,7 @@ struct nnf::imp {
|
|||
}
|
||||
|
||||
void skip(expr * t, bool pol) {
|
||||
expr * r = pol ? t : m.mk_not(t);
|
||||
expr * r = pol ? t : mk_not(m, t);
|
||||
m_result_stack.push_back(r);
|
||||
if (proofs_enabled()) {
|
||||
m_result_pr_stack.push_back(m.mk_oeq_reflexivity(r));
|
||||
|
@ -639,7 +640,7 @@ struct nnf::imp {
|
|||
m_name_quant->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2);
|
||||
|
||||
if (!fr.m_pol)
|
||||
n2 = m.mk_not(n2);
|
||||
n2 = mk_not(m, n2);
|
||||
m_result_stack.push_back(n2);
|
||||
if (proofs_enabled()) {
|
||||
if (!fr.m_pol) {
|
||||
|
|
|
@ -74,3 +74,46 @@ bool occurs(func_decl * d, expr * n) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void mark_occurs(ptr_vector<expr>& to_check, expr* v, expr_mark& occ) {
|
||||
expr_fast_mark2 visited;
|
||||
occ.mark(v, true);
|
||||
visited.mark(v, true);
|
||||
while (!to_check.empty()) {
|
||||
expr* e = to_check.back();
|
||||
if (visited.is_marked(e)) {
|
||||
to_check.pop_back();
|
||||
continue;
|
||||
}
|
||||
if (is_app(e)) {
|
||||
bool does_occur = false;
|
||||
bool all_visited = true;
|
||||
for (expr* arg : *to_app(e)) {
|
||||
if (!visited.is_marked(arg)) {
|
||||
to_check.push_back(arg);
|
||||
all_visited = false;
|
||||
}
|
||||
else
|
||||
does_occur |= occ.is_marked(arg);
|
||||
}
|
||||
if (all_visited) {
|
||||
occ.mark(e, does_occur);
|
||||
visited.mark(e, true);
|
||||
to_check.pop_back();
|
||||
}
|
||||
}
|
||||
else if (is_quantifier(e)) {
|
||||
expr* body = to_quantifier(e)->get_expr();
|
||||
if (visited.is_marked(body)) {
|
||||
visited.mark(e, true);
|
||||
occ.mark(e, occ.is_marked(body));
|
||||
to_check.pop_back();
|
||||
}
|
||||
else
|
||||
to_check.push_back(body);
|
||||
}
|
||||
else {
|
||||
visited.mark(e, true);
|
||||
to_check.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,8 +18,8 @@ Revision History:
|
|||
--*/
|
||||
#pragma once
|
||||
|
||||
class expr;
|
||||
class func_decl;
|
||||
#include "util/vector.h"
|
||||
#include "ast/ast.h"
|
||||
|
||||
/**
|
||||
\brief Return true if n1 occurs in n2
|
||||
|
@ -31,4 +31,9 @@ bool occurs(expr * n1, expr * n2);
|
|||
*/
|
||||
bool occurs(func_decl * d, expr * n);
|
||||
|
||||
/**
|
||||
* \brief Mark sub-expressions of to_check by whether v occurs in these.
|
||||
*/
|
||||
void mark_occurs(ptr_vector<expr>& to_check, expr* v, expr_mark& occurs);
|
||||
|
||||
|
||||
|
|
|
@ -1245,12 +1245,8 @@ void proof_checker::dump_proof(proof const* pr) {
|
|||
}
|
||||
|
||||
void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent) {
|
||||
char buffer[128];
|
||||
#ifdef _WINDOWS
|
||||
sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id);
|
||||
#else
|
||||
sprintf(buffer, "proof_lemma_%d.smt2", m_proof_lemma_id);
|
||||
#endif
|
||||
std::string buffer;
|
||||
buffer = "proof_lemma_" + std::to_string(m_proof_lemma_id) + ".smt2";
|
||||
std::ofstream out(buffer);
|
||||
ast_smt_pp pp(m);
|
||||
pp.set_benchmark_name("lemma");
|
||||
|
|
|
@ -106,7 +106,7 @@ public:
|
|||
{
|
||||
ast_manager &m = args.get_manager();
|
||||
bool_rewriter brwr(m);
|
||||
brwr.set_flat(false);
|
||||
brwr.set_flat_and_or(false);
|
||||
|
||||
if (m.is_or(decl))
|
||||
{ mk_or_core(args, res); }
|
||||
|
|
|
@ -36,7 +36,6 @@ namespace recfun {
|
|||
ast_manager &m,
|
||||
family_id fid,
|
||||
def * d,
|
||||
std::string & name,
|
||||
unsigned case_index,
|
||||
sort_ref_vector const & arg_sorts,
|
||||
expr_ref_vector const& guards,
|
||||
|
@ -44,10 +43,10 @@ namespace recfun {
|
|||
: m_pred(m),
|
||||
m_guards(guards),
|
||||
m_rhs(expr_ref(rhs,m)),
|
||||
m_def(d) {
|
||||
parameter p(case_index);
|
||||
func_decl_info info(fid, OP_FUN_CASE_PRED, 1, &p);
|
||||
m_pred = m.mk_func_decl(symbol(name.c_str()), arg_sorts.size(), arg_sorts.data(), m.mk_bool_sort(), info);
|
||||
m_def(d) {
|
||||
parameter ps[2] = { parameter(case_index), parameter(d->get_decl()) };
|
||||
func_decl_info info(fid, OP_FUN_CASE_PRED, 2, ps);
|
||||
m_pred = m.mk_func_decl(symbol("case-def"), arg_sorts.size(), arg_sorts.data(), m.mk_bool_sort(), info);
|
||||
}
|
||||
|
||||
def::def(ast_manager &m, family_id fid, symbol const & s,
|
||||
|
@ -220,11 +219,10 @@ namespace recfun {
|
|||
}
|
||||
|
||||
|
||||
void def::add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) {
|
||||
case_def c(m, m_fid, this, name, case_index, get_domain(), conditions, rhs);
|
||||
void def::add_case(unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) {
|
||||
case_def c(m, m_fid, this, case_index, get_domain(), conditions, rhs);
|
||||
c.set_is_immediate(is_imm);
|
||||
TRACEFN("add_case " << name
|
||||
<< "\n" << mk_pp(rhs, m)
|
||||
TRACEFN("add_case " << case_index << " " << mk_pp(rhs, m)
|
||||
<< "\n:is_imm " << is_imm
|
||||
<< "\n:guards " << conditions);
|
||||
m_cases.push_back(c);
|
||||
|
@ -261,7 +259,7 @@ namespace recfun {
|
|||
// is the function a macro (unconditional body)?
|
||||
if (is_macro || n_vars == 0 || !contains_ite(u, rhs)) {
|
||||
// constant function or trivial control flow, only one (dummy) case
|
||||
add_case(name, 0, conditions, rhs);
|
||||
add_case(0, conditions, rhs);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -347,7 +345,7 @@ namespace recfun {
|
|||
|
||||
// yield new case
|
||||
bool is_imm = is_i(case_rhs);
|
||||
add_case(name, case_idx++, conditions, case_rhs, is_imm);
|
||||
add_case(case_idx++, conditions, case_rhs, is_imm);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -408,6 +406,7 @@ namespace recfun {
|
|||
void promise_def::set_definition(replace& r, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs) {
|
||||
SASSERT(n_vars == d->get_arity());
|
||||
|
||||
d->m_is_macro = is_macro;
|
||||
is_imm_pred is_i(*u);
|
||||
d->compute_cases(*u, r, is_i, is_macro, n_vars, vars, rhs);
|
||||
}
|
||||
|
@ -435,6 +434,12 @@ namespace recfun {
|
|||
return *(m_util.get());
|
||||
}
|
||||
|
||||
void plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
|
||||
op_names.push_back(builtin_name("case-def", OP_FUN_CASE_PRED));
|
||||
op_names.push_back(builtin_name("recfun-num-rounds", OP_NUM_ROUNDS));
|
||||
}
|
||||
|
||||
|
||||
promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
|
||||
def* d = u().decl_fun(name, n, params, range, is_generated);
|
||||
SASSERT(!m_defs.contains(d->get_decl()));
|
||||
|
@ -442,17 +447,18 @@ namespace recfun {
|
|||
return promise_def(&u(), d);
|
||||
}
|
||||
|
||||
void plugin::inherit(decl_plugin* other, ast_translation& tr) {
|
||||
for (auto [k, v] : static_cast<plugin*>(other)->m_defs) {
|
||||
void plugin::inherit(decl_plugin* _other, ast_translation& tr) {
|
||||
plugin* other = static_cast<plugin*>(_other);
|
||||
for (auto [k, v] : other->m_defs) {
|
||||
func_decl_ref f(tr(k), tr.to());
|
||||
if (m_defs.contains(f))
|
||||
continue;
|
||||
def* d = v->copy(u(), tr);
|
||||
m_defs.insert(f, d);
|
||||
for (case_def & c : d->get_cases())
|
||||
m_case_defs.insert(c.get_decl(), &c);
|
||||
|
||||
m_case_defs.insert(c.get_decl(), &c);
|
||||
}
|
||||
m_has_rec_defs = other->m_has_rec_defs;
|
||||
}
|
||||
|
||||
promise_def plugin::ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
|
||||
|
@ -473,6 +479,7 @@ namespace recfun {
|
|||
}
|
||||
|
||||
void plugin::set_definition(replace& r, promise_def & d, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs) {
|
||||
m_has_rec_defs |= !is_macro;
|
||||
u().set_definition(r, d, is_macro, n_vars, vars, rhs);
|
||||
for (case_def & c : d.get_def()->get_cases())
|
||||
m_case_defs.insert(c.get_decl(), &c);
|
||||
|
@ -495,6 +502,18 @@ namespace recfun {
|
|||
func_decl * plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range)
|
||||
{
|
||||
func_decl_info info(get_family_id(), k, num_parameters, parameters);
|
||||
switch (k) {
|
||||
case OP_FUN_CASE_PRED:
|
||||
SASSERT(num_parameters == 2);
|
||||
return m().mk_func_decl(symbol("case-def"), arity, domain, m().mk_bool_sort(), info);
|
||||
case OP_NUM_ROUNDS:
|
||||
SASSERT(num_parameters == 1);
|
||||
SASSERT(arity == 0);
|
||||
return m().mk_const_decl(symbol("recfun-num-rounds"), m().mk_bool_sort(), info);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace recfun {
|
|||
expr_ref_vector m_guards; //<! conjunction that is equivalent to this case
|
||||
expr_ref m_rhs; //<! if guard is true, `f(t1...tn) = rhs` holds
|
||||
def * m_def; //<! definition this is a part of
|
||||
bool m_immediate; //<! does `rhs` contain no defined_fun/case_pred?
|
||||
bool m_immediate = false; //<! does `rhs` contain no defined_fun/case_pred?
|
||||
|
||||
case_def(ast_manager& m):
|
||||
m_pred(m),
|
||||
|
@ -72,7 +72,6 @@ namespace recfun {
|
|||
case_def(ast_manager & m,
|
||||
family_id fid,
|
||||
def * d,
|
||||
std::string & name,
|
||||
unsigned case_index,
|
||||
sort_ref_vector const & arg_sorts,
|
||||
expr_ref_vector const& guards,
|
||||
|
@ -117,13 +116,14 @@ namespace recfun {
|
|||
func_decl_ref m_decl; //!< generic declaration
|
||||
expr_ref m_rhs; //!< definition
|
||||
family_id m_fid;
|
||||
bool m_is_macro;
|
||||
|
||||
def(ast_manager &m, family_id fid, symbol const & s, unsigned arity, sort *const * domain, sort* range, bool is_generated);
|
||||
|
||||
// compute cases for a function, given its RHS (possibly containing `ite`).
|
||||
void compute_cases(util& u, replace& subst, is_immediate_pred &,
|
||||
bool is_macro, unsigned n_vars, var *const * vars, expr* rhs);
|
||||
void add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr* rhs, bool is_imm = false);
|
||||
void add_case(unsigned case_index, expr_ref_vector const& conditions, expr* rhs, bool is_imm = false);
|
||||
bool contains_ite(util& u, expr* e); // expression contains a test over a def?
|
||||
bool contains_def(util& u, expr* e); // expression contains a def
|
||||
public:
|
||||
|
@ -138,6 +138,7 @@ namespace recfun {
|
|||
|
||||
bool is_fun_macro() const { return m_cases.size() == 1; }
|
||||
bool is_fun_defined() const { return !is_fun_macro(); }
|
||||
bool is_macro() const { return m_is_macro; }
|
||||
|
||||
def* copy(util& dst, ast_translation& tr);
|
||||
|
||||
|
@ -165,6 +166,7 @@ namespace recfun {
|
|||
mutable scoped_ptr<util> m_util;
|
||||
def_map m_defs; // function->def
|
||||
case_def_map m_case_defs; // case_pred->def
|
||||
bool m_has_rec_defs = false;
|
||||
|
||||
ast_manager & m() { return *m_manager; }
|
||||
|
||||
|
@ -187,6 +189,8 @@ namespace recfun {
|
|||
|
||||
func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range) override;
|
||||
|
||||
void get_op_names(svector<builtin_name> & op_names, symbol const & logic) override;
|
||||
|
||||
promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false);
|
||||
|
||||
|
@ -200,11 +204,13 @@ namespace recfun {
|
|||
|
||||
bool has_def(func_decl* f) const { return m_defs.contains(f); }
|
||||
bool has_defs() const;
|
||||
bool has_rec_defs() const { return m_has_rec_defs; }
|
||||
def const& get_def(func_decl* f) const { return *(m_defs[f]); }
|
||||
promise_def get_promise_def(func_decl* f) const { return promise_def(&u(), m_defs[f]); }
|
||||
def& get_def(func_decl* f) { return *(m_defs[f]); }
|
||||
bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); }
|
||||
bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); }
|
||||
case_def& get_case_def(func_decl* f) { SASSERT(has_case_def(f)); return *(m_case_defs[f]); }
|
||||
bool is_defined(func_decl* f) {return has_case_def(f) && !get_def(f).get_cases().empty(); }
|
||||
|
||||
func_decl_ref_vector get_rec_funs() {
|
||||
func_decl_ref_vector result(m());
|
||||
|
@ -248,6 +254,8 @@ namespace recfun {
|
|||
//<! don't use native theory if recursive function declarations are not populated with defs
|
||||
bool has_defs() const { return m_plugin->has_defs(); }
|
||||
|
||||
bool has_rec_defs() const { return m_plugin->has_rec_defs(); }
|
||||
|
||||
//<! add a function declaration
|
||||
def * decl_fun(symbol const & s, unsigned n_args, sort *const * args, sort * range, bool is_generated);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ z3_add_component(rewriter
|
|||
der.cpp
|
||||
distribute_forall.cpp
|
||||
dl_rewriter.cpp
|
||||
dom_simplifier.cpp
|
||||
elim_bounds.cpp
|
||||
enum2bv_rewriter.cpp
|
||||
expr_replacer.cpp
|
||||
|
@ -25,6 +26,7 @@ z3_add_component(rewriter
|
|||
hoist_rewriter.cpp
|
||||
inj_axiom.cpp
|
||||
label_rewriter.cpp
|
||||
macro_replacer.cpp
|
||||
maximize_ac_sharing.cpp
|
||||
mk_simplified_app.cpp
|
||||
pb_rewriter.cpp
|
||||
|
@ -45,5 +47,6 @@ z3_add_component(rewriter
|
|||
ast
|
||||
params
|
||||
automata
|
||||
interval
|
||||
polynomial
|
||||
)
|
||||
|
|
|
@ -23,9 +23,8 @@ Notes:
|
|||
#include "ast/ast_pp.h"
|
||||
|
||||
seq_util& arith_rewriter_core::seq() {
|
||||
if (!m_seq) {
|
||||
m_seq = alloc(seq_util, m());
|
||||
}
|
||||
if (!m_seq)
|
||||
m_seq = alloc(seq_util, m);
|
||||
return *m_seq;
|
||||
}
|
||||
|
||||
|
@ -93,9 +92,9 @@ br_status arith_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c
|
|||
case OP_TANH: SASSERT(num_args == 1); st = mk_tanh_core(args[0], result); break;
|
||||
default: st = BR_FAILED; break;
|
||||
}
|
||||
CTRACE("arith_rewriter", st != BR_FAILED, tout << st << ": " << mk_pp(f, m());
|
||||
for (unsigned i = 0; i < num_args; ++i) tout << mk_pp(args[i], m()) << " ";
|
||||
tout << "\n==>\n" << mk_pp(result.get(), m()) << "\n";
|
||||
CTRACE("arith_rewriter", st != BR_FAILED, tout << st << ": " << mk_pp(f, m);
|
||||
for (unsigned i = 0; i < num_args; ++i) tout << mk_pp(args[i], m) << " ";
|
||||
tout << "\n==>\n" << mk_pp(result.get(), m) << "\n";
|
||||
if (is_app(result)) tout << "args: " << to_app(result)->get_num_args() << "\n";
|
||||
);
|
||||
return st;
|
||||
|
@ -133,7 +132,7 @@ bool arith_rewriter::div_polynomial(expr * t, numeral const & g, const_treatment
|
|||
SASSERT(!g.is_one());
|
||||
unsigned sz;
|
||||
expr * const * ms = get_monomials(t, sz);
|
||||
expr_ref_buffer new_args(m());
|
||||
expr_ref_buffer new_args(m);
|
||||
numeral a;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * arg = ms[i];
|
||||
|
@ -196,10 +195,10 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref &
|
|||
switch (kind) {
|
||||
case LE: c = floor(c); break;
|
||||
case GE: c = ceil(c); break;
|
||||
case EQ: result = m().mk_false(); return true;
|
||||
case EQ: result = m.mk_false(); return true;
|
||||
}
|
||||
}
|
||||
expr_ref k(m_util.mk_numeral(c, is_int), m());
|
||||
expr_ref k(m_util.mk_numeral(c, is_int), m);
|
||||
switch (kind) {
|
||||
case LE: result = m_util.mk_le(pp, k); return true;
|
||||
case GE: result = m_util.mk_ge(pp, k); return true;
|
||||
|
@ -223,24 +222,24 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref &
|
|||
if (c.is_neg()) {
|
||||
switch (kind) {
|
||||
case EQ:
|
||||
case LE: result = m().mk_false(); return true;
|
||||
case GE: result = m().mk_true(); return true;
|
||||
case LE: result = m.mk_false(); return true;
|
||||
case GE: result = m.mk_true(); return true;
|
||||
}
|
||||
}
|
||||
if (c.is_zero() && kind == GE) {
|
||||
result = m().mk_true();
|
||||
result = m.mk_true();
|
||||
return true;
|
||||
}
|
||||
if (c.is_pos() && c >= abs(b)) {
|
||||
switch (kind) {
|
||||
case LE: result = m().mk_true(); return true;
|
||||
case LE: result = m.mk_true(); return true;
|
||||
case EQ:
|
||||
case GE: result = m().mk_false(); return true;
|
||||
case GE: result = m.mk_false(); return true;
|
||||
}
|
||||
}
|
||||
// mod x b <= b - 1
|
||||
if (c + rational::one() == abs(b) && kind == LE) {
|
||||
result = m().mk_true();
|
||||
result = m.mk_true();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -304,7 +303,7 @@ br_status arith_rewriter::is_separated(expr* arg1, expr* arg2, op_kind kind, exp
|
|||
if (kind != LE && kind != GE)
|
||||
return BR_FAILED;
|
||||
rational bound(0), r1, r2;
|
||||
expr_ref narg(m());
|
||||
expr_ref narg(m);
|
||||
bool has_bound = true;
|
||||
if (!m_util.is_numeral(arg2, r2))
|
||||
return BR_FAILED;
|
||||
|
@ -335,47 +334,47 @@ br_status arith_rewriter::is_separated(expr* arg1, expr* arg2, op_kind kind, exp
|
|||
if (kind == GE && r1 > r2)
|
||||
return BR_FAILED;
|
||||
if (kind == LE && r1 > r2) {
|
||||
result = m().mk_false();
|
||||
result = m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (kind == GE && r1 < r2) {
|
||||
result = m().mk_false();
|
||||
result = m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
SASSERT(r1 == r2);
|
||||
expr_ref zero(m_util.mk_numeral(rational(0), arg1->get_sort()), m());
|
||||
expr_ref zero(m_util.mk_numeral(rational(0), arg1->get_sort()), m);
|
||||
|
||||
if (r1.is_zero() && m_util.is_mul(arg1)) {
|
||||
expr_ref_buffer eqs(m());
|
||||
expr_ref_buffer eqs(m);
|
||||
ptr_buffer<expr> args;
|
||||
flat_mul(arg1, args);
|
||||
for (expr* arg : args) {
|
||||
if (m_util.is_numeral(arg))
|
||||
continue;
|
||||
eqs.push_back(m().mk_eq(arg, zero));
|
||||
eqs.push_back(m.mk_eq(arg, zero));
|
||||
}
|
||||
result = m().mk_or(eqs);
|
||||
result = m.mk_or(eqs);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
if (kind == LE && m_util.is_add(arg1)) {
|
||||
expr_ref_buffer leqs(m());
|
||||
expr_ref_buffer leqs(m);
|
||||
for (expr* arg : *to_app(arg1)) {
|
||||
if (!m_util.is_numeral(arg))
|
||||
leqs.push_back(m_util.mk_le(arg, zero));
|
||||
}
|
||||
result = m().mk_and(leqs);
|
||||
result = m.mk_and(leqs);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
if (kind == GE && m_util.is_add(arg1)) {
|
||||
expr_ref_buffer geqs(m());
|
||||
expr_ref_buffer geqs(m);
|
||||
for (expr* arg : *to_app(arg1)) {
|
||||
if (!m_util.is_numeral(arg))
|
||||
geqs.push_back(m_util.mk_ge(arg, zero));
|
||||
}
|
||||
result = m().mk_and(geqs);
|
||||
result = m.mk_and(geqs);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
@ -399,8 +398,8 @@ bool arith_rewriter::elim_to_real_var(expr * var, expr_ref & new_var) {
|
|||
|
||||
bool arith_rewriter::elim_to_real_mon(expr * monomial, expr_ref & new_monomial) {
|
||||
if (m_util.is_mul(monomial)) {
|
||||
expr_ref_buffer new_vars(m());
|
||||
expr_ref new_var(m());
|
||||
expr_ref_buffer new_vars(m);
|
||||
expr_ref new_var(m);
|
||||
unsigned num = to_app(monomial)->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
if (!elim_to_real_var(to_app(monomial)->get_arg(i), new_var))
|
||||
|
@ -417,8 +416,8 @@ bool arith_rewriter::elim_to_real_mon(expr * monomial, expr_ref & new_monomial)
|
|||
|
||||
bool arith_rewriter::elim_to_real_pol(expr * p, expr_ref & new_p) {
|
||||
if (m_util.is_add(p)) {
|
||||
expr_ref_buffer new_monomials(m());
|
||||
expr_ref new_monomial(m());
|
||||
expr_ref_buffer new_monomials(m);
|
||||
expr_ref new_monomial(m);
|
||||
for (expr* arg : *to_app(p)) {
|
||||
if (!elim_to_real_mon(arg, new_monomial))
|
||||
return false;
|
||||
|
@ -507,14 +506,14 @@ br_status arith_rewriter::reduce_power(expr * arg1, expr * arg2, op_kind kind, e
|
|||
switch (kind) {
|
||||
case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_REWRITE1;
|
||||
case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_REWRITE1;
|
||||
default: result = m().mk_eq(new_arg1, new_arg2); return BR_REWRITE1;
|
||||
default: result = m.mk_eq(new_arg1, new_arg2); return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
||||
br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) {
|
||||
expr *orig_arg1 = arg1, *orig_arg2 = arg2;
|
||||
expr_ref new_arg1(m());
|
||||
expr_ref new_arg2(m());
|
||||
expr_ref new_arg1(m);
|
||||
expr_ref new_arg2(m);
|
||||
if ((is_zero(arg1) && is_reduce_power_target(arg2, kind == EQ)) ||
|
||||
(is_zero(arg2) && is_reduce_power_target(arg1, kind == EQ)))
|
||||
return reduce_power(arg1, arg2, kind, result);
|
||||
|
@ -524,29 +523,29 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
|
|||
arg1 = new_arg1;
|
||||
arg2 = new_arg2;
|
||||
}
|
||||
expr_ref new_new_arg1(m());
|
||||
expr_ref new_new_arg2(m());
|
||||
expr_ref new_new_arg1(m);
|
||||
expr_ref new_new_arg2(m);
|
||||
if (m_elim_to_real && elim_to_real(arg1, arg2, new_new_arg1, new_new_arg2)) {
|
||||
arg1 = new_new_arg1;
|
||||
arg2 = new_new_arg2;
|
||||
CTRACE("elim_to_real", m_elim_to_real, tout << "after_elim_to_real\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";);
|
||||
CTRACE("elim_to_real", m_elim_to_real, tout << "after_elim_to_real\n" << mk_ismt2_pp(arg1, m) << "\n" << mk_ismt2_pp(arg2, m) << "\n";);
|
||||
if (st == BR_FAILED)
|
||||
st = BR_DONE;
|
||||
}
|
||||
numeral a1, a2;
|
||||
if (is_numeral(arg1, a1) && is_numeral(arg2, a2)) {
|
||||
switch (kind) {
|
||||
case LE: result = a1 <= a2 ? m().mk_true() : m().mk_false(); return BR_DONE;
|
||||
case GE: result = a1 >= a2 ? m().mk_true() : m().mk_false(); return BR_DONE;
|
||||
default: result = a1 == a2 ? m().mk_true() : m().mk_false(); return BR_DONE;
|
||||
case LE: result = a1 <= a2 ? m.mk_true() : m.mk_false(); return BR_DONE;
|
||||
case GE: result = a1 >= a2 ? m.mk_true() : m.mk_false(); return BR_DONE;
|
||||
default: result = a1 == a2 ? m.mk_true() : m.mk_false(); return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
#define ANUM_LE_GE_EQ() { \
|
||||
switch (kind) { \
|
||||
case LE: result = am.le(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \
|
||||
case GE: result = am.ge(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \
|
||||
default: result = am.eq(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \
|
||||
case LE: result = am.le(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \
|
||||
case GE: result = am.ge(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \
|
||||
default: result = am.eq(v1, v2) ? m.mk_true() : m.mk_false(); return BR_DONE; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
@ -593,12 +592,12 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
|
|||
if (!first && !g.is_one() && num_consts <= 1) {
|
||||
bool is_sat = div_polynomial(arg1, g, (kind == LE ? CT_CEIL : (kind == GE ? CT_FLOOR : CT_FALSE)), new_arg1);
|
||||
if (!is_sat) {
|
||||
result = m().mk_false();
|
||||
result = m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
is_sat = div_polynomial(arg2, g, (kind == LE ? CT_FLOOR : (kind == GE ? CT_CEIL : CT_FALSE)), new_arg2);
|
||||
if (!is_sat) {
|
||||
result = m().mk_false();
|
||||
result = m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
arg1 = new_arg1.get();
|
||||
|
@ -607,25 +606,25 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
|
|||
}
|
||||
}
|
||||
expr* c = nullptr, *t = nullptr, *e = nullptr;
|
||||
if (m().is_ite(arg1, c, t, e) && is_numeral(t, a1) && is_numeral(arg2, a2)) {
|
||||
if (m.is_ite(arg1, c, t, e) && is_numeral(t, a1) && is_numeral(arg2, a2)) {
|
||||
switch (kind) {
|
||||
case LE: result = a1 <= a2 ? m().mk_or(c, m_util.mk_le(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_le(e, arg2)); return BR_REWRITE2;
|
||||
case GE: result = a1 >= a2 ? m().mk_or(c, m_util.mk_ge(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_ge(e, arg2)); return BR_REWRITE2;
|
||||
case EQ: result = a1 == a2 ? m().mk_or(c, m().mk_eq(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_eq(e, arg2)); return BR_REWRITE2;
|
||||
case LE: result = a1 <= a2 ? m.mk_or(c, m_util.mk_le(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_le(e, arg2)); return BR_REWRITE2;
|
||||
case GE: result = a1 >= a2 ? m.mk_or(c, m_util.mk_ge(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_ge(e, arg2)); return BR_REWRITE2;
|
||||
case EQ: result = a1 == a2 ? m.mk_or(c, m.mk_eq(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_eq(e, arg2)); return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
if (m().is_ite(arg1, c, t, e) && is_numeral(e, a1) && is_numeral(arg2, a2)) {
|
||||
if (m.is_ite(arg1, c, t, e) && is_numeral(e, a1) && is_numeral(arg2, a2)) {
|
||||
switch (kind) {
|
||||
case LE: result = a1 <= a2 ? m().mk_or(m().mk_not(c), m_util.mk_le(t, arg2)) : m().mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2;
|
||||
case GE: result = a1 >= a2 ? m().mk_or(m().mk_not(c), m_util.mk_ge(t, arg2)) : m().mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2;
|
||||
case EQ: result = a1 == a2 ? m().mk_or(m().mk_not(c), m().mk_eq(t, arg2)) : m().mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2;
|
||||
case LE: result = a1 <= a2 ? m.mk_or(m.mk_not(c), m_util.mk_le(t, arg2)) : m.mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2;
|
||||
case GE: result = a1 >= a2 ? m.mk_or(m.mk_not(c), m_util.mk_ge(t, arg2)) : m.mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2;
|
||||
case EQ: result = a1 == a2 ? m.mk_or(m.mk_not(c), m.mk_eq(t, arg2)) : m.mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
if (m().is_ite(arg1, c, t, e) && arg1->get_ref_count() == 1) {
|
||||
if (m.is_ite(arg1, c, t, e) && arg1->get_ref_count() == 1) {
|
||||
switch (kind) {
|
||||
case LE: result = m().mk_ite(c, m_util.mk_le(t, arg2), m_util.mk_le(e, arg2)); return BR_REWRITE2;
|
||||
case GE: result = m().mk_ite(c, m_util.mk_ge(t, arg2), m_util.mk_ge(e, arg2)); return BR_REWRITE2;
|
||||
case EQ: result = m().mk_ite(c, m().mk_eq(t, arg2), m().mk_eq(e, arg2)); return BR_REWRITE2;
|
||||
case LE: result = m.mk_ite(c, m_util.mk_le(t, arg2), m_util.mk_le(e, arg2)); return BR_REWRITE2;
|
||||
case GE: result = m.mk_ite(c, m_util.mk_ge(t, arg2), m_util.mk_ge(e, arg2)); return BR_REWRITE2;
|
||||
case EQ: result = m.mk_ite(c, m.mk_eq(t, arg2), m.mk_eq(e, arg2)); return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
if (m_util.is_to_int(arg2) && is_numeral(arg1)) {
|
||||
|
@ -642,7 +641,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
|
|||
return BR_REWRITE1;
|
||||
case EQ:
|
||||
result = m_util.mk_ge(t, m_util.mk_numeral(a2, false));
|
||||
result = m().mk_and(m_util.mk_lt(t, m_util.mk_numeral(a2+1, false)), result);
|
||||
result = m.mk_and(m_util.mk_lt(t, m_util.mk_numeral(a2+1, false)), result);
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
}
|
||||
|
@ -663,7 +662,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
|
|||
switch (kind) {
|
||||
case LE: result = m_util.mk_le(arg1, arg2); return BR_DONE;
|
||||
case GE: result = m_util.mk_ge(arg1, arg2); return BR_DONE;
|
||||
default: result = m().mk_eq(arg1, arg2); return BR_DONE;
|
||||
default: result = m.mk_eq(arg1, arg2); return BR_DONE;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
|
@ -674,7 +673,7 @@ br_status arith_rewriter::mk_le_core(expr * arg1, expr * arg2, expr_ref & result
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_lt_core(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_util.mk_le(arg2, arg1));
|
||||
result = m.mk_not(m_util.mk_le(arg2, arg1));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
@ -683,7 +682,7 @@ br_status arith_rewriter::mk_ge_core(expr * arg1, expr * arg2, expr_ref & result
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_gt_core(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
result = m().mk_not(m_util.mk_le(arg1, arg2));
|
||||
result = m.mk_not(m_util.mk_le(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
@ -694,7 +693,7 @@ bool arith_rewriter::is_arith_term(expr * n) const {
|
|||
br_status arith_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
br_status st = BR_FAILED;
|
||||
if (m_eq2ineq) {
|
||||
result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2));
|
||||
result = m.mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2));
|
||||
st = BR_REWRITE2;
|
||||
}
|
||||
else if (m_arith_lhs || is_arith_term(arg1) || is_arith_term(arg2)) {
|
||||
|
@ -724,7 +723,7 @@ br_status arith_rewriter::mk_and_core(unsigned n, expr* const* args, expr_ref& r
|
|||
}
|
||||
if (rest.size() < n - 1) {
|
||||
rest.push_back(arg0);
|
||||
result = m().mk_and(rest);
|
||||
result = m.mk_and(rest);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
@ -742,8 +741,8 @@ bool arith_rewriter::mk_eq_mod(expr* arg1, expr* arg2, expr_ref& result) {
|
|||
rational a, b;
|
||||
rational g = gcd(p, k, a, b);
|
||||
if (g == 1) {
|
||||
expr_ref nb(m_util.mk_numeral(b, true), m());
|
||||
result = m().mk_eq(m_util.mk_mod(u, y),
|
||||
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));
|
||||
return true;
|
||||
}
|
||||
|
@ -752,7 +751,7 @@ bool arith_rewriter::mk_eq_mod(expr* arg1, expr* arg2, expr_ref& result) {
|
|||
}
|
||||
|
||||
expr_ref arith_rewriter::neg_monomial(expr* e) const {
|
||||
expr_ref_vector args(m());
|
||||
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()));
|
||||
|
@ -773,10 +772,10 @@ expr_ref arith_rewriter::neg_monomial(expr* e) const {
|
|||
args.push_back(e);
|
||||
}
|
||||
if (args.size() == 1) {
|
||||
return expr_ref(args.back(), m());
|
||||
return expr_ref(args.back(), m);
|
||||
}
|
||||
else {
|
||||
return expr_ref(m_util.mk_mul(args.size(), args.data()), m());
|
||||
return expr_ref(m_util.mk_mul(args.size(), args.data()), m);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -793,7 +792,7 @@ bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) const {
|
|||
expr * t2 = to_app(t)->get_arg(0);
|
||||
|
||||
if (m_util.is_mul(t2) && is_numeral(to_app(t2)->get_arg(0), r) && r.is_neg()) {
|
||||
expr_ref_vector args1(m());
|
||||
expr_ref_vector args1(m);
|
||||
for (expr* e1 : *to_app(t)) {
|
||||
args1.push_back(neg_monomial(e1));
|
||||
}
|
||||
|
@ -826,7 +825,7 @@ bool arith_rewriter::is_anum_simp_target(unsigned num_args, expr * const * args)
|
|||
|
||||
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());
|
||||
expr_ref_buffer new_args(m);
|
||||
anum_manager & am = m_util.am();
|
||||
scoped_anum r(am);
|
||||
scoped_anum arg(am);
|
||||
|
@ -864,7 +863,7 @@ br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, ex
|
|||
new_args.push_back(m_util.mk_numeral(am, r, false));
|
||||
br_status st = poly_rewriter<arith_rewriter_core>::mk_add_core(new_args.size(), new_args.data(), result);
|
||||
if (st == BR_FAILED) {
|
||||
result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data());
|
||||
result = m.mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data());
|
||||
return BR_DONE;
|
||||
}
|
||||
return st;
|
||||
|
@ -876,7 +875,7 @@ br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, ex
|
|||
|
||||
br_status arith_rewriter::mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (is_anum_simp_target(num_args, args)) {
|
||||
expr_ref_buffer new_args(m());
|
||||
expr_ref_buffer new_args(m);
|
||||
anum_manager & am = m_util.am();
|
||||
scoped_anum r(am);
|
||||
scoped_anum arg(am);
|
||||
|
@ -913,7 +912,7 @@ br_status arith_rewriter::mk_mul_core(unsigned num_args, expr * const * args, ex
|
|||
|
||||
br_status st = poly_rewriter<arith_rewriter_core>::mk_mul_core(new_args.size(), new_args.data(), result);
|
||||
if (st == BR_FAILED) {
|
||||
result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data());
|
||||
result = m.mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data());
|
||||
return BR_DONE;
|
||||
}
|
||||
return st;
|
||||
|
@ -998,7 +997,7 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
else {
|
||||
numeral k(1);
|
||||
k /= v2;
|
||||
result = m().mk_app(get_fid(), OP_MUL,
|
||||
result = m.mk_app(get_fid(), OP_MUL,
|
||||
m_util.mk_numeral(k, false),
|
||||
arg1);
|
||||
return BR_REWRITE1;
|
||||
|
@ -1028,8 +1027,8 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
v1 /= v2;
|
||||
result = m_util.mk_mul(m_util.mk_numeral(v1, false),
|
||||
m_util.mk_div(b, d));
|
||||
expr_ref z(m_util.mk_real(0), m());
|
||||
result = m().mk_ite(m().mk_eq(d, z), m_util.mk_div(arg1, z), result);
|
||||
expr_ref z(m_util.mk_real(0), m);
|
||||
result = m.mk_ite(m.mk_eq(d, z), m_util.mk_div(arg1, z), result);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
|
@ -1039,7 +1038,7 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_idivides(unsigned k, expr * arg, expr_ref & result) {
|
||||
result = m().mk_eq(m_util.mk_mod(arg, m_util.mk_int(k)), m_util.mk_int(0));
|
||||
result = m.mk_eq(m_util.mk_mod(arg, m_util.mk_int(k)), m_util.mk_int(0));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
@ -1063,12 +1062,12 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu
|
|||
return BR_FAILED;
|
||||
}
|
||||
if (arg1 == arg2) {
|
||||
expr_ref zero(m_util.mk_int(0), m());
|
||||
result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1));
|
||||
expr_ref zero(m_util.mk_int(0), m);
|
||||
result = m.mk_ite(m.mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1));
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
if (m_util.is_numeral(arg2, v2, is_int) && v2.is_pos() && m_util.is_add(arg1)) {
|
||||
expr_ref_buffer args(m());
|
||||
expr_ref_buffer args(m);
|
||||
bool change = false;
|
||||
rational add(0);
|
||||
for (expr* arg : *to_app(arg1)) {
|
||||
|
@ -1083,15 +1082,15 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu
|
|||
}
|
||||
}
|
||||
if (change) {
|
||||
result = m_util.mk_idiv(m().mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2);
|
||||
result = m_util.mk_idiv(m.mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2);
|
||||
result = m_util.mk_add(m_util.mk_numeral(add, true), result);
|
||||
TRACE("div_bug", tout << "mk_div result: " << result << "\n";);
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
}
|
||||
if (divides(arg1, arg2, result)) {
|
||||
expr_ref zero(m_util.mk_int(0), m());
|
||||
result = m().mk_ite(m().mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result);
|
||||
expr_ref zero(m_util.mk_int(0), m);
|
||||
result = m.mk_ite(m.mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
return BR_FAILED;
|
||||
|
@ -1150,17 +1149,17 @@ expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) {
|
|||
flat_mul(den, args2);
|
||||
remove_divisor(arg, args1);
|
||||
remove_divisor(arg, args2);
|
||||
expr_ref zero(m_util.mk_int(0), m());
|
||||
expr_ref zero(m_util.mk_int(0), m);
|
||||
num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.data());
|
||||
den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.data());
|
||||
expr_ref d(m_util.mk_idiv(num, den), m());
|
||||
expr_ref nd(m_util.mk_idiv(m_util.mk_uminus(num), m_util.mk_uminus(den)), m());
|
||||
return expr_ref(m().mk_ite(m().mk_eq(zero, arg),
|
||||
expr_ref d(m_util.mk_idiv(num, den), m);
|
||||
expr_ref nd(m_util.mk_idiv(m_util.mk_uminus(num), m_util.mk_uminus(den)), m);
|
||||
return expr_ref(m.mk_ite(m.mk_eq(zero, arg),
|
||||
m_util.mk_idiv(zero, zero),
|
||||
m().mk_ite(m_util.mk_ge(arg, zero),
|
||||
m.mk_ite(m_util.mk_ge(arg, zero),
|
||||
d,
|
||||
nd)),
|
||||
m());
|
||||
m);
|
||||
}
|
||||
|
||||
void arith_rewriter::flat_mul(expr* e, ptr_buffer<expr>& args) {
|
||||
|
@ -1208,8 +1207,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
}
|
||||
|
||||
if (arg1 == arg2 && !m_util.is_numeral(arg2)) {
|
||||
expr_ref zero(m_util.mk_int(0), m());
|
||||
result = m().mk_ite(m().mk_eq(arg2, zero), m_util.mk_mod(zero, zero), zero);
|
||||
expr_ref zero(m_util.mk_int(0), m);
|
||||
result = m.mk_ite(m.mk_eq(arg2, zero), m_util.mk_mod(zero, zero), zero);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
|
@ -1222,8 +1221,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
|
||||
// propagate mod inside only if there is something to reduce.
|
||||
if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) {
|
||||
TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";);
|
||||
expr_ref_buffer args(m());
|
||||
TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m) << "\n" << mk_ismt2_pp(arg2, m) << "\n";);
|
||||
expr_ref_buffer args(m);
|
||||
bool change = false;
|
||||
for (expr* arg : *to_app(arg1)) {
|
||||
rational arg_v;
|
||||
|
@ -1246,8 +1245,8 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
if (!change) {
|
||||
return BR_FAILED; // did not find any target for applying simplification
|
||||
}
|
||||
result = m_util.mk_mod(m().mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2);
|
||||
TRACE("mod_bug", tout << "mk_mod result: " << mk_ismt2_pp(result, m()) << "\n";);
|
||||
result = m_util.mk_mod(m.mk_app(to_app(arg1)->get_decl(), args.size(), args.data()), arg2);
|
||||
TRACE("mod_bug", tout << "mk_mod result: " << mk_ismt2_pp(result, m) << "\n";);
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
|
||||
|
@ -1290,10 +1289,10 @@ br_status arith_rewriter::mk_rem_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
}
|
||||
else if (m_elim_rem) {
|
||||
expr * mod = m_util.mk_mod(arg1, arg2);
|
||||
result = m().mk_ite(m_util.mk_ge(arg2, m_util.mk_numeral(rational(0), true)),
|
||||
result = m.mk_ite(m_util.mk_ge(arg2, m_util.mk_numeral(rational(0), true)),
|
||||
mod,
|
||||
m_util.mk_uminus(mod));
|
||||
TRACE("elim_rem", tout << "result: " << mk_ismt2_pp(result, m()) << "\n";);
|
||||
TRACE("elim_rem", tout << "result: " << mk_ismt2_pp(result, m) << "\n";);
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
return BR_FAILED;
|
||||
|
@ -1322,7 +1321,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res
|
|||
bool is_num_y = m_util.is_numeral(arg2, y);
|
||||
auto ensure_real = [&](expr* e) { return m_util.is_int(e) ? m_util.mk_to_real(e) : e; };
|
||||
|
||||
TRACE("arith", tout << mk_pp(arg1, m()) << " " << mk_pp(arg2, m()) << "\n";);
|
||||
TRACE("arith", tout << mk_bounded_pp(arg1, m) << " " << mk_bounded_pp(arg2, m) << "\n";);
|
||||
if (is_num_x && x.is_one()) {
|
||||
result = m_util.mk_numeral(x, false);
|
||||
return BR_DONE;
|
||||
|
@ -1377,7 +1376,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res
|
|||
|
||||
if (is_num_y && y.is_minus_one()) {
|
||||
result = m_util.mk_div(m_util.mk_real(1), ensure_real(arg1));
|
||||
result = m().mk_ite(m().mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))),
|
||||
result = m.mk_ite(m.mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))),
|
||||
m_util.mk_real(0),
|
||||
result);
|
||||
return BR_REWRITE2;
|
||||
|
@ -1387,7 +1386,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res
|
|||
// (^ t -k) --> (^ (/ 1 t) k)
|
||||
result = m_util.mk_power(m_util.mk_div(m_util.mk_numeral(rational(1), false), arg1),
|
||||
m_util.mk_numeral(-y, false));
|
||||
result = m().mk_ite(m().mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))),
|
||||
result = m.mk_ite(m.mk_eq(arg1, m_util.mk_numeral(rational(0), m_util.is_int(arg1))),
|
||||
m_util.mk_real(0),
|
||||
result);
|
||||
return BR_REWRITE3;
|
||||
|
@ -1504,7 +1503,7 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) {
|
|||
// Try to apply simplifications such as:
|
||||
// (to_int (+ 1.0 (to_real x)) y) --> (+ 1 x (to_int y))
|
||||
|
||||
expr_ref_buffer int_args(m()), real_args(m());
|
||||
expr_ref_buffer int_args(m), real_args(m);
|
||||
for (expr* c : *to_app(arg)) {
|
||||
if (m_util.is_numeral(c, a) && a.is_int()) {
|
||||
int_args.push_back(m_util.mk_numeral(a, true));
|
||||
|
@ -1520,17 +1519,17 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) {
|
|||
return BR_FAILED;
|
||||
|
||||
if (real_args.empty()) {
|
||||
result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), int_args.size(), int_args.data());
|
||||
result = m.mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), int_args.size(), int_args.data());
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
if (!int_args.empty() && m_util.is_add(arg)) {
|
||||
decl_kind k = to_app(arg)->get_decl()->get_decl_kind();
|
||||
expr_ref t1(m().mk_app(get_fid(), k, int_args.size(), int_args.data()), m());
|
||||
expr_ref t2(m().mk_app(get_fid(), k, real_args.size(), real_args.data()), m());
|
||||
expr_ref t1(m.mk_app(get_fid(), k, int_args.size(), int_args.data()), m);
|
||||
expr_ref t2(m.mk_app(get_fid(), k, real_args.size(), real_args.data()), m);
|
||||
int_args.reset();
|
||||
int_args.push_back(t1);
|
||||
int_args.push_back(m_util.mk_to_int(t2));
|
||||
result = m().mk_app(get_fid(), k, int_args.size(), int_args.data());
|
||||
result = m.mk_app(get_fid(), k, int_args.size(), int_args.data());
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
}
|
||||
|
@ -1550,9 +1549,9 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) {
|
|||
for (expr* e : *to_app(arg))
|
||||
new_args.push_back(m_util.mk_to_real(e));
|
||||
if (m_util.is_add(arg))
|
||||
result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data());
|
||||
result = m.mk_app(get_fid(), OP_ADD, new_args.size(), new_args.data());
|
||||
else
|
||||
result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data());
|
||||
result = m.mk_app(get_fid(), OP_MUL, new_args.size(), new_args.data());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
|
@ -1562,23 +1561,23 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) {
|
|||
br_status arith_rewriter::mk_is_int(expr * arg, expr_ref & result) {
|
||||
numeral a;
|
||||
if (m_util.is_numeral(arg, a)) {
|
||||
result = a.is_int() ? m().mk_true() : m().mk_false();
|
||||
result = a.is_int() ? m.mk_true() : m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
else if (m_util.is_to_real(arg)) {
|
||||
result = m().mk_true();
|
||||
result = m.mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
result = m().mk_eq(m().mk_app(get_fid(), OP_TO_REAL,
|
||||
m().mk_app(get_fid(), OP_TO_INT, arg)),
|
||||
result = m.mk_eq(m.mk_app(get_fid(), OP_TO_REAL,
|
||||
m.mk_app(get_fid(), OP_TO_INT, arg)),
|
||||
arg);
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
}
|
||||
|
||||
br_status arith_rewriter::mk_abs_core(expr * arg, expr_ref & result) {
|
||||
result = m().mk_ite(m_util.mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg))), arg, m_util.mk_uminus(arg));
|
||||
result = m.mk_ite(m_util.mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg))), arg, m_util.mk_uminus(arg));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
@ -1647,9 +1646,9 @@ bool arith_rewriter::is_pi_integer(expr * t) {
|
|||
a = c;
|
||||
b = d;
|
||||
}
|
||||
TRACE("tan", tout << "is_pi_integer " << mk_ismt2_pp(t, m()) << "\n";
|
||||
tout << "a: " << mk_ismt2_pp(a, m()) << "\n";
|
||||
tout << "b: " << mk_ismt2_pp(b, m()) << "\n";);
|
||||
TRACE("tan", tout << "is_pi_integer " << mk_ismt2_pp(t, m) << "\n";
|
||||
tout << "a: " << mk_ismt2_pp(a, m) << "\n";
|
||||
tout << "b: " << mk_ismt2_pp(b, m) << "\n";);
|
||||
return
|
||||
(m_util.is_pi(a) && m_util.is_to_real(b)) ||
|
||||
(m_util.is_to_real(a) && m_util.is_pi(b));
|
||||
|
@ -1861,7 +1860,7 @@ br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) {
|
|||
}
|
||||
|
||||
if (is_pi_multiple(arg, k)) {
|
||||
expr_ref n(m()), d(m());
|
||||
expr_ref n(m), d(m);
|
||||
n = mk_sin_value(k);
|
||||
if (n.get() == nullptr)
|
||||
goto end;
|
||||
|
|
|
@ -25,13 +25,13 @@ Notes:
|
|||
class arith_rewriter_core {
|
||||
protected:
|
||||
typedef rational numeral;
|
||||
ast_manager& m;
|
||||
arith_util m_util;
|
||||
scoped_ptr<seq_util> m_seq;
|
||||
bool m_expand_power{ false };
|
||||
bool m_mul2power{ false };
|
||||
bool m_expand_tan{ false };
|
||||
bool m_expand_power = false;
|
||||
bool m_mul2power = false;
|
||||
bool m_expand_tan = false;
|
||||
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
family_id get_fid() const { return m_util.get_family_id(); }
|
||||
seq_util& seq();
|
||||
|
||||
|
@ -47,7 +47,7 @@ protected:
|
|||
app* mk_power(expr* x, rational const& r, sort* s);
|
||||
expr* coerce(expr* x, sort* s);
|
||||
public:
|
||||
arith_rewriter_core(ast_manager & m):m_util(m) {}
|
||||
arith_rewriter_core(ast_manager & m):m(m), m_util(m) {}
|
||||
bool is_zero(expr * n) const { return m_util.is_zero(n); }
|
||||
};
|
||||
|
||||
|
@ -120,7 +120,7 @@ public:
|
|||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (mk_app_core(f, num_args, args, result) == BR_FAILED)
|
||||
result = m().mk_app(f, num_args, args);
|
||||
result = m.mk_app(f, num_args, args);
|
||||
}
|
||||
|
||||
br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result);
|
||||
|
@ -159,30 +159,30 @@ public:
|
|||
br_status mk_power_core(expr* arg1, expr* arg2, expr_ref & result);
|
||||
void mk_div(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_div_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_DIV, arg1, arg2);
|
||||
result = m.mk_app(get_fid(), OP_DIV, arg1, arg2);
|
||||
}
|
||||
void mk_idiv(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_idiv_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_IDIV, arg1, arg2);
|
||||
result = m.mk_app(get_fid(), OP_IDIV, arg1, arg2);
|
||||
}
|
||||
void mk_mod(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_mod_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_MOD, arg1, arg2);
|
||||
result = m.mk_app(get_fid(), OP_MOD, arg1, arg2);
|
||||
}
|
||||
void mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
|
||||
if (mk_rem_core(arg1, arg2, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_REM, arg1, arg2);
|
||||
result = m.mk_app(get_fid(), OP_REM, arg1, arg2);
|
||||
}
|
||||
|
||||
br_status mk_to_int_core(expr * arg, expr_ref & result);
|
||||
br_status mk_to_real_core(expr * arg, expr_ref & result);
|
||||
void mk_to_int(expr * arg, expr_ref & result) {
|
||||
if (mk_to_int_core(arg, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_TO_INT, 1, &arg);
|
||||
result = m.mk_app(get_fid(), OP_TO_INT, 1, &arg);
|
||||
}
|
||||
void mk_to_real(expr * arg, expr_ref & result) {
|
||||
if (mk_to_real_core(arg, result) == BR_FAILED)
|
||||
result = m().mk_app(get_fid(), OP_TO_REAL, 1, &arg);
|
||||
result = m.mk_app(get_fid(), OP_TO_REAL, 1, &arg);
|
||||
}
|
||||
br_status mk_is_int(expr * arg, expr_ref & result);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue