3
0
Fork 0
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:
Jakob Rath 2023-02-01 16:28:57 +01:00
commit 20b5455d08
669 changed files with 26145 additions and 20652 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -68,4 +68,5 @@ z3_add_component(api
opt
portfolio
realclosure
extra_cmds
)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -86,6 +86,7 @@ class ll_printer {
default:
display_child_ref(n);
}
}
template<typename T>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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
)

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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