3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-04-26 13:53:33 +00:00

Merge branch 'master' into nl2lin

This commit is contained in:
Valentin Promies 2026-02-20 12:55:51 +01:00
commit 8f9c527444
964 changed files with 54547 additions and 36359 deletions

View file

@ -134,6 +134,10 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# Enable RPATH support
MACOSX_RPATH TRUE
)
# Add header padding to allow install_name_tool to modify the dylib
# This fixes issues where install_name_tool fails with "larger updated load commands do not fit"
# See: https://github.com/Z3Prover/z3/issues/7623
target_link_options(libz3 PRIVATE "-Wl,-headerpad_max_install_names")
endif()
if (NOT MSVC)
@ -151,6 +155,11 @@ endif()
# so that if those are also shared libraries they are referenced by `libz3.so`.
target_link_libraries(libz3 PRIVATE ${Z3_DEPENDENT_LIBS})
# On macOS, add headerpad for install_name_tool compatibility
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND Z3_BUILD_LIBZ3_SHARED)
target_link_options(libz3 PRIVATE "-Wl,-headerpad_max_install_names")
endif()
################################################################################
# Create include directory with headers for easier developer integration
################################################################################
@ -346,4 +355,16 @@ if (Z3_BUILD_JULIA_BINDINGS)
add_subdirectory(api/julia)
endif()
################################################################################
# Go bindings
################################################################################
option(Z3_BUILD_GO_BINDINGS "Build Go bindings for Z3" OFF)
if (Z3_BUILD_GO_BINDINGS)
if (NOT Z3_BUILD_LIBZ3_SHARED)
message(FATAL_ERROR "The Go bindings will not work with a static libz3. "
"You either need to disable Z3_BUILD_GO_BINDINGS or enable Z3_BUILD_LIBZ3_SHARED")
endif()
add_subdirectory(api/go)
endif()
# TODO: Implement support for other bindigns

View file

@ -38,8 +38,7 @@ public:
TRACE(goal, g->display(tout << "in\n"););
ptr_vector<expr> flas;
const unsigned sz = g->size();
for (unsigned i = 0; i < sz; i++) flas.push_back(g->form(i));
for (auto [f, dep, pr] : *g) flas.push_back(f);
lackr lackr(m, m_p, m_st, flas, nullptr);
// mk result

View file

@ -62,10 +62,9 @@ class ackr_bound_probe : public probe {
public:
result operator()(goal const & g) override {
proc p(g.m());
unsigned sz = g.size();
expr_fast_mark1 visited;
for (unsigned i = 0; i < sz; i++) {
for_each_expr_core<proc, expr_fast_mark1, true, true>(p, visited, g.form(i));
for (auto [curr, dep, pr] : g) {
for_each_expr_core<proc, expr_fast_mark1, true, true>(p, visited, curr);
}
p.prune_non_select();
double total = ackr_helper::calculate_lemma_bound(p.m_fun2terms, p.m_sel2terms);

View file

@ -103,7 +103,7 @@ void ackr_model_converter::convert_constants(model * source, model * destination
evaluator.set_model_completion(true);
array_util autil(m);
for (unsigned i = 0; i < source->get_num_constants(); i++) {
for (unsigned i = 0, n = source->get_num_constants(); i < n; ++i) {
func_decl * const c = source->get_constant(i);
app * const term = info->find_term(c);
expr * value = source->get_const_interp(c);

View file

@ -47,7 +47,7 @@ public:
//
bool check() {
bool retv = true;
for (unsigned i = 0; i < m_abstr_model->get_num_constants(); i++) {
for (unsigned i = 0; i < m_abstr_model->get_num_constants(); ++i) {
func_decl * const c = m_abstr_model->get_constant(i);
app * const _term = m_info->find_term(c);
expr * const term = _term ? _term : m.mk_const(c);
@ -58,13 +58,13 @@ public:
void make_model(model_ref& destination) {
for (unsigned i = 0; i < m_abstr_model->get_num_uninterpreted_sorts(); i++) {
for (unsigned i = 0; i < m_abstr_model->get_num_uninterpreted_sorts(); ++i) {
sort * const s = m_abstr_model->get_uninterpreted_sort(i);
ptr_vector<expr> u = m_abstr_model->get_universe(s);
destination->register_usort(s, u.size(), u.data());
}
for (unsigned i = 0; i < m_abstr_model->get_num_functions(); i++) {
for (unsigned i = 0; i < m_abstr_model->get_num_functions(); ++i) {
func_decl * const fd = m_abstr_model->get_function(i);
func_interp * const fi = m_abstr_model->get_func_interp(fd);
destination->register_decl(fd, fi);
@ -247,7 +247,7 @@ private:
);
m_app2val.insert(a, result.get()); // memoize
m_pinned.push_back(a);
m_pinned.push_back(result);
m_pinned.push_back(std::move(result));
return true;
}

View file

@ -44,6 +44,7 @@ z3_add_component(api
api_context.cpp
api_datalog.cpp
api_datatype.cpp
api_finite_set.cpp
api_fpa.cpp
api_goal.cpp
api_log.cpp

View file

@ -325,7 +325,7 @@ extern "C" {
static bool to_anum_vector(Z3_context c, unsigned n, Z3_ast a[], scoped_anum_vector & as) {
algebraic_numbers::manager & _am = am(c);
scoped_anum tmp(_am);
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
if (is_rational(c, a[i])) {
_am.set(tmp, get_rational(c, a[i]).to_mpq());
as.push_back(tmp);
@ -378,7 +378,7 @@ extern "C" {
}
Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(result);
for (unsigned i = 0; i < roots.size(); i++) {
for (unsigned i = 0; i < roots.size(); ++i) {
result->m_ast_vector.push_back(au(c).mk_numeral(_am, roots.get(i), false));
}
RETURN_Z3(of_ast_vector(result));

View file

@ -901,7 +901,7 @@ extern "C" {
expr * const * from = to_exprs(num_exprs, _from);
expr * const * to = to_exprs(num_exprs, _to);
expr * r = nullptr;
for (unsigned i = 0; i < num_exprs; i++) {
for (unsigned i = 0; i < num_exprs; ++i) {
if (from[i]->get_sort() != to[i]->get_sort()) {
SET_ERROR_CODE(Z3_SORT_ERROR, nullptr);
RETURN_Z3(of_expr(nullptr));
@ -910,7 +910,7 @@ extern "C" {
SASSERT(to[i]->get_ref_count() > 0);
}
expr_safe_replace subst(m);
for (unsigned i = 0; i < num_exprs; i++) {
for (unsigned i = 0; i < num_exprs; ++i) {
subst.insert(from[i], to[i]);
}
expr_ref new_a(m);
@ -940,7 +940,7 @@ extern "C" {
obj_map<func_decl, expr*> rep;
obj_map<expr, expr*> cache;
for (unsigned i = 0; i < num_funs; i++) {
for (unsigned i = 0; i < num_funs; ++i) {
if (from[i]->get_range() != to[i]->get_sort()) {
SET_ERROR_CODE(Z3_SORT_ERROR, nullptr);
RETURN_Z3(of_expr(nullptr));
@ -1465,6 +1465,25 @@ extern "C" {
}
}
if (mk_c(c)->fsutil().get_family_id() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_FINITE_SET_EMPTY: return Z3_OP_FINITE_SET_EMPTY;
case OP_FINITE_SET_SINGLETON: return Z3_OP_FINITE_SET_SINGLETON;
case OP_FINITE_SET_UNION: return Z3_OP_FINITE_SET_UNION;
case OP_FINITE_SET_INTERSECT: return Z3_OP_FINITE_SET_INTERSECT;
case OP_FINITE_SET_DIFFERENCE: return Z3_OP_FINITE_SET_DIFFERENCE;
case OP_FINITE_SET_IN: return Z3_OP_FINITE_SET_IN;
case OP_FINITE_SET_SIZE: return Z3_OP_FINITE_SET_SIZE;
case OP_FINITE_SET_SUBSET: return Z3_OP_FINITE_SET_SUBSET;
case OP_FINITE_SET_MAP: return Z3_OP_FINITE_SET_MAP;
case OP_FINITE_SET_FILTER: return Z3_OP_FINITE_SET_FILTER;
case OP_FINITE_SET_RANGE: return Z3_OP_FINITE_SET_RANGE;
case OP_FINITE_SET_EXT: return Z3_OP_FINITE_SET_EXT;
case OP_FINITE_SET_MAP_INVERSE: return Z3_OP_FINITE_SET_MAP_INVERSE;
default: return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->recfun().get_family_id() == _d->get_family_id())
return Z3_OP_RECURSIVE;

View file

@ -138,10 +138,8 @@ extern "C" {
RESET_ERROR_CODE();
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), to_ast_map(m)->m);
mk_c(c)->save_object(v);
obj_map<ast, ast*>::iterator it = to_ast_map_ref(m).begin();
obj_map<ast, ast*>::iterator end = to_ast_map_ref(m).end();
for (; it != end; ++it) {
v->m_ast_vector.push_back(it->m_key);
for (auto const& kv : to_ast_map_ref(m)) {
v->m_ast_vector.push_back(kv.m_key);
}
Z3_ast_vector r = of_ast_vector(v);
RETURN_Z3(r);
@ -155,10 +153,8 @@ extern "C" {
std::ostringstream buffer;
ast_manager & mng = to_ast_map(m)->m;
buffer << "(ast-map";
obj_map<ast, ast*>::iterator it = to_ast_map_ref(m).begin();
obj_map<ast, ast*>::iterator end = to_ast_map_ref(m).end();
for (; it != end; ++it) {
buffer << "\n (" << mk_ismt2_pp(it->m_key, mng, 3) << "\n " << mk_ismt2_pp(it->m_value, mng, 3) << ")";
for (auto const& kv : to_ast_map_ref(m)) {
buffer << "\n (" << mk_ismt2_pp(kv.m_key, mng, 3) << "\n " << mk_ismt2_pp(kv.m_value, mng, 3) << ")";
}
buffer << ')';
return mk_c(c)->mk_external_string(std::move(buffer).str());

View file

@ -112,7 +112,7 @@ extern "C" {
Z3_ast_vector_ref * new_v = alloc(Z3_ast_vector_ref, *mk_c(t), mk_c(t)->m());
mk_c(t)->save_object(new_v);
unsigned sz = to_ast_vector_ref(v).size();
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
ast * new_ast = translator(to_ast_vector_ref(v).get(i));
new_v->m_ast_vector.push_back(new_ast);
}
@ -127,10 +127,10 @@ extern "C" {
std::ostringstream buffer;
buffer << "(ast-vector";
unsigned sz = to_ast_vector_ref(v).size();
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
buffer << "\n " << mk_ismt2_pp(to_ast_vector_ref(v).get(i), mk_c(c)->m(), 2);
}
buffer << ")";
buffer << ')';
return mk_c(c)->mk_external_string(buffer.str());
Z3_CATCH_RETURN(nullptr);
}

View file

@ -133,6 +133,7 @@ namespace api {
m_fpa_util(m()),
m_sutil(m()),
m_recfun(m()),
m_finite_set_util(m()),
m_ast_trail(m()),
m_pmanager(m_limit) {
@ -225,9 +226,8 @@ namespace api {
e = m_bv_util.mk_numeral(n, s);
}
else if (fid == get_datalog_fid() && n.is_uint64()) {
uint64_t sz;
if (m_datalog_util.try_get_size(s, sz) &&
sz <= n.get_uint64()) {
if (auto size_opt = m_datalog_util.try_get_size(s);
size_opt.has_value() && *size_opt <= n.get_uint64()) {
invoke_error_handler(Z3_INVALID_ARG);
}
e = m_datalog_util.mk_numeral(n.get_uint64(), s);
@ -252,7 +252,7 @@ namespace api {
save_ast_trail(exprs[0]);
return exprs[0];
default: {
expr * a = m().mk_and(num_exprs, exprs);
expr * a = m().mk_and(std::span<expr* const>(exprs, num_exprs));
save_ast_trail(a);
return a;
} }

View file

@ -31,6 +31,7 @@ Revision History:
#include "ast/fpa_decl_plugin.h"
#include "ast/recfun_decl_plugin.h"
#include "ast/special_relations_decl_plugin.h"
#include "ast/finite_set_decl_plugin.h"
#include "ast/rewriter/seq_rewriter.h"
#include "params/smt_params.h"
#include "smt/smt_kernel.h"
@ -77,6 +78,7 @@ namespace api {
fpa_util m_fpa_util;
seq_util m_sutil;
recfun::util m_recfun;
finite_set_util m_finite_set_util;
// Support for old solver API
smt_params m_fparams;
@ -146,6 +148,7 @@ namespace api {
datatype_util& dtutil() { return m_dt_plugin->u(); }
seq_util& sutil() { return m_sutil; }
recfun::util& recfun() { return m_recfun; }
finite_set_util& fsutil() { return m_finite_set_util; }
family_id get_basic_fid() const { return basic_family_id; }
family_id get_array_fid() const { return m_array_fid; }
family_id get_arith_fid() const { return arith_family_id; }

View file

@ -213,8 +213,11 @@ extern "C" {
// must start logging here, since function uses Z3_get_sort_kind above
LOG_Z3_get_finite_domain_sort_size(c, s, out);
RESET_ERROR_CODE();
VERIFY(mk_c(c)->datalog_util().try_get_size(to_sort(s), *out));
return true;
if (auto size = mk_c(c)->datalog_util().try_get_size(to_sort(s)); size) {
*out = *size;
return true;
}
return false;
Z3_CATCH_RETURN(false);
}

View file

@ -77,7 +77,7 @@ extern "C" {
// Create projections
ptr_vector<func_decl> const & _accs = *dt_util.get_constructor_accessors(decl);
SASSERT(_accs.size() == num_fields);
for (unsigned i = 0; i < _accs.size(); i++) {
for (unsigned i = 0; i < _accs.size(); ++i) {
mk_c(c)->save_multiple_ast_trail(_accs[i]);
proj_decls[i] = of_func_decl(_accs[i]);
}

187
src/api/api_finite_set.cpp Normal file
View file

@ -0,0 +1,187 @@
/*++
Copyright (c) 2025 Microsoft Corporation
Module Name:
api_finite_set.cpp
Abstract:
API for finite sets.
Author:
Copilot 2025-01-21
Revision History:
--*/
#include "api/z3.h"
#include "api/api_log_macros.h"
#include "api/api_context.h"
#include "api/api_util.h"
#include "ast/ast_pp.h"
extern "C" {
Z3_sort Z3_API Z3_mk_finite_set_sort(Z3_context c, Z3_sort elem_sort) {
Z3_TRY;
LOG_Z3_mk_finite_set_sort(c, elem_sort);
RESET_ERROR_CODE();
parameter param(to_sort(elem_sort));
sort* ty = mk_c(c)->m().mk_sort(mk_c(c)->fsutil().get_family_id(), FINITE_SET_SORT, 1, &param);
mk_c(c)->save_ast_trail(ty);
RETURN_Z3(of_sort(ty));
Z3_CATCH_RETURN(nullptr);
}
bool Z3_API Z3_is_finite_set_sort(Z3_context c, Z3_sort s) {
Z3_TRY;
LOG_Z3_is_finite_set_sort(c, s);
RESET_ERROR_CODE();
return mk_c(c)->fsutil().is_finite_set(to_sort(s));
Z3_CATCH_RETURN(false);
}
Z3_sort Z3_API Z3_get_finite_set_sort_basis(Z3_context c, Z3_sort s) {
Z3_TRY;
LOG_Z3_get_finite_set_sort_basis(c, s);
RESET_ERROR_CODE();
sort* elem_sort = nullptr;
if (!mk_c(c)->fsutil().is_finite_set(to_sort(s), elem_sort)) {
SET_ERROR_CODE(Z3_INVALID_ARG, "expected finite set sort");
RETURN_Z3(nullptr);
}
RETURN_Z3(of_sort(elem_sort));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_empty(Z3_context c, Z3_sort set_sort) {
Z3_TRY;
LOG_Z3_mk_finite_set_empty(c, set_sort);
RESET_ERROR_CODE();
app* a = mk_c(c)->fsutil().mk_empty(to_sort(set_sort));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_singleton(Z3_context c, Z3_ast elem) {
Z3_TRY;
LOG_Z3_mk_finite_set_singleton(c, elem);
RESET_ERROR_CODE();
CHECK_IS_EXPR(elem, nullptr);
app* a = mk_c(c)->fsutil().mk_singleton(to_expr(elem));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_union(Z3_context c, Z3_ast s1, Z3_ast s2) {
Z3_TRY;
LOG_Z3_mk_finite_set_union(c, s1, s2);
RESET_ERROR_CODE();
CHECK_IS_EXPR(s1, nullptr);
CHECK_IS_EXPR(s2, nullptr);
app* a = mk_c(c)->fsutil().mk_union(to_expr(s1), to_expr(s2));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_intersect(Z3_context c, Z3_ast s1, Z3_ast s2) {
Z3_TRY;
LOG_Z3_mk_finite_set_intersect(c, s1, s2);
RESET_ERROR_CODE();
CHECK_IS_EXPR(s1, nullptr);
CHECK_IS_EXPR(s2, nullptr);
app* a = mk_c(c)->fsutil().mk_intersect(to_expr(s1), to_expr(s2));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_difference(Z3_context c, Z3_ast s1, Z3_ast s2) {
Z3_TRY;
LOG_Z3_mk_finite_set_difference(c, s1, s2);
RESET_ERROR_CODE();
CHECK_IS_EXPR(s1, nullptr);
CHECK_IS_EXPR(s2, nullptr);
app* a = mk_c(c)->fsutil().mk_difference(to_expr(s1), to_expr(s2));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_member(Z3_context c, Z3_ast elem, Z3_ast set) {
Z3_TRY;
LOG_Z3_mk_finite_set_member(c, elem, set);
RESET_ERROR_CODE();
CHECK_IS_EXPR(elem, nullptr);
CHECK_IS_EXPR(set, nullptr);
app* a = mk_c(c)->fsutil().mk_in(to_expr(elem), to_expr(set));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_size(Z3_context c, Z3_ast set) {
Z3_TRY;
LOG_Z3_mk_finite_set_size(c, set);
RESET_ERROR_CODE();
CHECK_IS_EXPR(set, nullptr);
app* a = mk_c(c)->fsutil().mk_size(to_expr(set));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_subset(Z3_context c, Z3_ast s1, Z3_ast s2) {
Z3_TRY;
LOG_Z3_mk_finite_set_subset(c, s1, s2);
RESET_ERROR_CODE();
CHECK_IS_EXPR(s1, nullptr);
CHECK_IS_EXPR(s2, nullptr);
app* a = mk_c(c)->fsutil().mk_subset(to_expr(s1), to_expr(s2));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_map(Z3_context c, Z3_ast f, Z3_ast set) {
Z3_TRY;
LOG_Z3_mk_finite_set_map(c, f, set);
RESET_ERROR_CODE();
CHECK_IS_EXPR(f, nullptr);
CHECK_IS_EXPR(set, nullptr);
app* a = mk_c(c)->fsutil().mk_map(to_expr(f), to_expr(set));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_filter(Z3_context c, Z3_ast f, Z3_ast set) {
Z3_TRY;
LOG_Z3_mk_finite_set_filter(c, f, set);
RESET_ERROR_CODE();
CHECK_IS_EXPR(f, nullptr);
CHECK_IS_EXPR(set, nullptr);
app* a = mk_c(c)->fsutil().mk_filter(to_expr(f), to_expr(set));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_finite_set_range(Z3_context c, Z3_ast low, Z3_ast high) {
Z3_TRY;
LOG_Z3_mk_finite_set_range(c, low, high);
RESET_ERROR_CODE();
CHECK_IS_EXPR(low, nullptr);
CHECK_IS_EXPR(high, nullptr);
app* a = mk_c(c)->fsutil().mk_range(to_expr(low), to_expr(high));
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
};

View file

@ -141,7 +141,7 @@ extern "C" {
Z3_TRY;
LOG_Z3_optimize_check(c, o, num_assumptions, assumptions);
RESET_ERROR_CODE();
for (unsigned i = 0; i < num_assumptions; i++) {
for (unsigned i = 0; i < num_assumptions; ++i) {
if (!is_expr(to_ast(assumptions[i]))) {
SET_ERROR_CODE(Z3_INVALID_ARG, "assumption is not an expression");
return Z3_L_UNDEF;
@ -432,7 +432,7 @@ extern "C" {
unsigned n = to_optimize_ptr(o)->num_objectives();
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(v);
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
v->m_ast_vector.push_back(to_optimize_ptr(o)->get_objective(i));
}
RETURN_Z3(of_ast_vector(v));

View file

@ -200,14 +200,14 @@ extern "C" {
LOG_Z3_param_descrs_to_string(c, p);
RESET_ERROR_CODE();
std::ostringstream buffer;
buffer << "(";
buffer << '(';
unsigned sz = to_param_descrs_ptr(p)->size();
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
if (i > 0)
buffer << ", ";
buffer << to_param_descrs_ptr(p)->get_param_name(i);
}
buffer << ")";
buffer << ')';
return mk_c(c)->mk_external_string(std::move(buffer).str());
Z3_CATCH_RETURN("");
}

View file

@ -33,34 +33,48 @@ extern "C" {
LOG_Z3_polynomial_subresultants(c, p, q, x);
RESET_ERROR_CODE();
polynomial::manager & pm = mk_c(c)->pm();
polynomial_ref _p(pm), _q(pm);
polynomial::scoped_numeral d(pm.m());
default_expr2polynomial converter(mk_c(c)->m(), pm);
if (!converter.to_polynomial(to_expr(p), _p, d) ||
!converter.to_polynomial(to_expr(q), _q, d)) {
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return nullptr;
// Compute all polynomial results BEFORE allocating the result vector.
// This avoids a memory corruption issue where allocating API objects
// can interfere with the converter's internal state.
expr_ref_vector results(mk_c(c)->m());
{
polynomial_ref _p(pm), _q(pm);
polynomial::scoped_numeral d(pm.m());
default_expr2polynomial converter(mk_c(c)->m(), pm);
if (!converter.to_polynomial(to_expr(p), _p, d) ||
!converter.to_polynomial(to_expr(q), _q, d)) {
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return nullptr;
}
if (converter.is_var(to_expr(x))) {
expr2var const & mapping = converter.get_mapping();
unsigned v_x = mapping.to_var(to_expr(x));
if (v_x != UINT_MAX) {
polynomial_ref_vector rs(pm);
polynomial_ref r(pm);
expr_ref _r(mk_c(c)->m());
{
cancel_eh<reslimit> eh(mk_c(c)->poly_limit());
api::context::set_interruptable si(*(mk_c(c)), eh);
scoped_timer timer(mk_c(c)->params().m_timeout, &eh);
pm.psc_chain(_p, _q, v_x, rs);
}
for (unsigned i = 0; i < rs.size(); ++i) {
r = rs.get(i);
converter.to_expr(r, true, _r);
results.push_back(_r);
}
}
}
}
// Converter is now out of scope - safe to allocate result vector
Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(result);
if (converter.is_var(to_expr(x))) {
expr2var const & mapping = converter.get_mapping();
unsigned v_x = mapping.to_var(to_expr(x));
polynomial_ref_vector rs(pm);
polynomial_ref r(pm);
expr_ref _r(mk_c(c)->m());
{
cancel_eh<reslimit> eh(mk_c(c)->poly_limit());
api::context::set_interruptable si(*(mk_c(c)), eh);
scoped_timer timer(mk_c(c)->params().m_timeout, &eh);
pm.psc_chain(_p, _q, v_x, rs);
}
for (unsigned i = 0; i < rs.size(); i++) {
r = rs.get(i);
converter.to_expr(r, true, _r);
result->m_ast_vector.push_back(_r);
}
for (expr* e : results) {
result->m_ast_vector.push_back(e);
}
RETURN_Z3(of_ast_vector(result));
Z3_CATCH_RETURN(nullptr);

View file

@ -73,7 +73,7 @@ extern "C" {
expr * const* no_ps = reinterpret_cast<expr * const*>(no_patterns);
symbol qid = to_symbol(quantifier_id);
pattern_validator v(mk_c(c)->m());
for (unsigned i = 0; i < num_patterns; i++) {
for (unsigned i = 0; i < num_patterns; ++i) {
if (!v(num_decls, ps[i], 0, 0)) {
SET_ERROR_CODE(Z3_INVALID_PATTERN, nullptr);
return nullptr;

View file

@ -115,7 +115,7 @@ extern "C" {
reset_rcf_cancel(c);
rcnumeral_vector av;
unsigned rz = 0;
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
if (!rcfm(c).is_zero(to_rcnumeral(a[i])))
rz = i + 1;
av.push_back(to_rcnumeral(a[i]));
@ -129,7 +129,7 @@ extern "C" {
rcnumeral_vector rs;
rcfm(c).isolate_roots(av.size(), av.data(), rs);
unsigned num_roots = rs.size();
for (unsigned i = 0; i < num_roots; i++) {
for (unsigned i = 0; i < num_roots; ++i) {
roots[i] = from_rcnumeral(rs[i]);
}
RETURN_Z3_rcf_mk_roots num_roots;

View file

@ -558,7 +558,7 @@ extern "C" {
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(v);
unsigned sz = to_solver_ref(s)->get_num_assertions();
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
v->m_ast_vector.push_back(to_solver_ref(s)->get_assertion(i));
}
RETURN_Z3(of_ast_vector(v));
@ -638,7 +638,7 @@ extern "C" {
#define TOSTRING(x) STRINGIFY(x)
static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) {
for (unsigned i = 0; i < num_assumptions; i++) {
for (unsigned i = 0; i < num_assumptions; ++i) {
if (!is_expr(to_ast(assumptions[i]))) {
SET_ERROR_CODE(Z3_INVALID_ARG, "assumption is not an expression");
return Z3_L_UNDEF;

View file

@ -140,7 +140,7 @@ extern "C" {
LOG_Z3_tactic_par_or(c, num, ts);
RESET_ERROR_CODE();
ptr_buffer<tactic> _ts;
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
_ts.push_back(to_tactic_ref(ts[i]));
}
tactic * new_t = par(num, _ts.data());
@ -496,7 +496,7 @@ extern "C" {
std::ostringstream buffer;
buffer << "(goals\n";
unsigned sz = to_apply_result(r)->m_subgoals.size();
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
to_apply_result(r)->m_subgoals[i]->display(buffer);
}
buffer << ')';

View file

@ -26,6 +26,7 @@ Notes:
#include<memory>
#include<vector>
#include<z3.h>
#include<z3_rcf.h>
#include<limits.h>
#include<functional>
@ -82,6 +83,36 @@ namespace z3 {
inline void set_param(char const * param, int value) { auto str = std::to_string(value); Z3_global_param_set(param, str.c_str()); }
inline void reset_params() { Z3_global_param_reset_all(); }
/**
\brief Return Z3 version number information.
*/
inline void get_version(unsigned& major, unsigned& minor, unsigned& build_number, unsigned& revision_number) {
Z3_get_version(&major, &minor, &build_number, &revision_number);
}
/**
\brief Return a string that fully describes the version of Z3 in use.
*/
inline std::string get_full_version() {
return std::string(Z3_get_full_version());
}
/**
\brief Enable tracing messages tagged as \c tag when Z3 is compiled in debug mode.
It is a NOOP otherwise.
*/
inline void enable_trace(char const * tag) {
Z3_enable_trace(tag);
}
/**
\brief Disable tracing messages tagged as \c tag when Z3 is compiled in debug mode.
It is a NOOP otherwise.
*/
inline void disable_trace(char const * tag) {
Z3_disable_trace(tag);
}
/**
\brief Exception used to sign API usage errors.
*/
@ -183,6 +214,24 @@ namespace z3 {
public:
context() { config c; init(c); }
context(config & c) { init(c); }
context(context && other) noexcept
: m_enable_exceptions(other.m_enable_exceptions),
m_rounding_mode(other.m_rounding_mode),
m_ctx(other.m_ctx) {
other.m_ctx = nullptr;
}
context & operator=(context && other) noexcept {
if (this != &other) {
if (m_ctx) Z3_del_context(m_ctx);
m_enable_exceptions = other.m_enable_exceptions;
m_rounding_mode = other.m_rounding_mode;
m_ctx = other.m_ctx;
other.m_ctx = nullptr;
}
return *this;
}
~context() { if (m_ctx) Z3_del_context(m_ctx); }
operator Z3_context() const { return m_ctx; }
@ -274,6 +323,10 @@ namespace z3 {
\brief Return a regular expression sort over sequences \c seq_sort.
*/
sort re_sort(sort& seq_sort);
/**
\brief Return a finite set sort over element sort \c s.
*/
sort finite_set_sort(sort& s);
/**
\brief Return an array sort for arrays from \c d to \c r.
@ -1232,11 +1285,32 @@ namespace z3 {
expr_vector args() const {
expr_vector vec(ctx());
unsigned argCnt = num_args();
for (unsigned i = 0; i < argCnt; i++)
for (unsigned i = 0; i < argCnt; ++i)
vec.push_back(arg(i));
return vec;
}
/**
\brief Update the arguments of this application.
Return a new expression with the same function declaration and updated arguments.
The number of new arguments must match the current number of arguments.
\pre is_app()
\pre args.size() == num_args()
*/
expr update(expr_vector const& args) const;
/**
\brief Update a datatype field.
Return a new datatype expression with the specified field updated to the new value.
The remaining fields are unchanged.
\pre is_datatype()
\param field_access The accessor function declaration for the field to update
\param new_value The new value for the field
*/
expr update_field(func_decl const& field_access, expr const& new_value) const;
/**
\brief Return the 'body' of this quantifier.
@ -2305,6 +2379,19 @@ namespace z3 {
return to_func_decl(a.ctx(), Z3_mk_tree_order(a.ctx(), a, index));
}
/**
\brief Return the nonzero subresultants of p and q with respect to the "variable" x.
\pre p, q and x are Z3 expressions where p and q are arithmetic terms.
Note that, any subterm that cannot be viewed as a polynomial is assumed to be a variable.
*/
inline expr_vector polynomial_subresultants(expr const& p, expr const& q, expr const& x) {
check_context(p, q); check_context(p, x);
Z3_ast_vector r = Z3_polynomial_subresultants(p.ctx(), p, q, x);
p.check_error();
return expr_vector(p.ctx(), r);
}
template<> class cast_ast<ast> {
public:
ast operator()(context & c, Z3_ast a) { return ast(c, a); }
@ -2340,7 +2427,7 @@ namespace z3 {
template<typename T>
template<typename T2>
array<T>::array(ast_vector_tpl<T2> const & v):m_array(new T[v.size()]), m_size(v.size()) {
for (unsigned i = 0; i < m_size; i++) {
for (unsigned i = 0; i < m_size; ++i) {
m_array[i] = v[i];
}
}
@ -2702,6 +2789,29 @@ namespace z3 {
check_error();
}
unsigned num_sorts() const {
unsigned r = Z3_model_get_num_sorts(ctx(), m_model);
check_error();
return r;
}
/**
\brief Return the uninterpreted sort at position \c i.
\pre i < num_sorts()
*/
sort get_sort(unsigned i) const {
Z3_sort s = Z3_model_get_sort(ctx(), m_model, i);
check_error();
return sort(ctx(), s);
}
expr_vector sort_universe(sort const& s) const {
check_context(*this, s);
Z3_ast_vector r = Z3_model_get_sort_universe(ctx(), m_model, s);
check_error();
return expr_vector(ctx(), r);
}
friend std::ostream & operator<<(std::ostream & out, model const & m);
std::string to_string() const { return m_model ? std::string(Z3_model_to_string(ctx(), m_model)) : "null"; }
@ -2848,7 +2958,7 @@ namespace z3 {
check_result check() { Z3_lbool r = Z3_solver_check(ctx(), m_solver); check_error(); return to_check_result(r); }
check_result check(unsigned n, expr * const assumptions) {
array<Z3_ast> _assumptions(n);
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
check_context(*this, assumptions[i]);
_assumptions[i] = assumptions[i];
}
@ -2859,7 +2969,7 @@ namespace z3 {
check_result check(expr_vector const& assumptions) {
unsigned n = assumptions.size();
array<Z3_ast> _assumptions(n);
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
check_context(*this, assumptions[i]);
_assumptions[i] = assumptions[i];
}
@ -2890,6 +3000,25 @@ namespace z3 {
check_error();
return result;
}
expr congruence_root(expr const& t) const {
check_context(*this, t);
Z3_ast r = Z3_solver_congruence_root(ctx(), m_solver, t);
check_error();
return expr(ctx(), r);
}
expr congruence_next(expr const& t) const {
check_context(*this, t);
Z3_ast r = Z3_solver_congruence_next(ctx(), m_solver, t);
check_error();
return expr(ctx(), r);
}
expr congruence_explain(expr const& a, expr const& b) const {
check_context(*this, a);
check_context(*this, b);
Z3_ast r = Z3_solver_congruence_explain(ctx(), m_solver, a, b);
check_error();
return expr(ctx(), r);
}
void set_initial_value(expr const& var, expr const& value) {
Z3_solver_set_initial_value(ctx(), m_solver, var, value);
check_error();
@ -2901,6 +3030,26 @@ namespace z3 {
set_initial_value(var, ctx().bool_val(b));
}
void solve_for(expr_vector const& vars, expr_vector& terms, expr_vector& guards) {
// Create a copy of vars since the C API modifies the variables vector
expr_vector variables(ctx());
for (unsigned i = 0; i < vars.size(); ++i) {
check_context(*this, vars[i]);
variables.push_back(vars[i]);
}
// Clear output vectors before calling C API
terms = expr_vector(ctx());
guards = expr_vector(ctx());
Z3_solver_solve_for(ctx(), m_solver, variables, terms, guards);
check_error();
}
void import_model_converter(solver const& src) {
check_context(*this, src);
Z3_solver_import_model_converter(ctx(), src.m_solver, m_solver);
check_error();
}
expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); }
friend std::ostream & operator<<(std::ostream & out, solver const & s);
@ -3071,7 +3220,7 @@ namespace z3 {
return operator[](0u);
else {
array<Z3_ast> args(n);
for (unsigned i = 0; i < n; i++)
for (unsigned i = 0; i < n; ++i)
args[i] = operator[](i);
return expr(ctx(), Z3_mk_and(ctx(), n, args.ptr()));
}
@ -3400,7 +3549,7 @@ namespace z3 {
check_result check(expr_vector const& asms) {
unsigned n = asms.size();
array<Z3_ast> _asms(n);
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
check_context(*this, asms[i]);
_asms[i] = asms[i];
}
@ -3518,6 +3667,7 @@ namespace z3 {
inline sort context::char_sort() { Z3_sort s = Z3_mk_char_sort(m_ctx); check_error(); return sort(*this, s); }
inline sort context::seq_sort(sort& s) { Z3_sort r = Z3_mk_seq_sort(m_ctx, s); check_error(); return sort(*this, r); }
inline sort context::re_sort(sort& s) { Z3_sort r = Z3_mk_re_sort(m_ctx, s); check_error(); return sort(*this, r); }
inline sort context::finite_set_sort(sort& s) { Z3_sort r = Z3_mk_finite_set_sort(m_ctx, s); check_error(); return sort(*this, r); }
inline sort context::fpa_sort(unsigned ebits, unsigned sbits) { Z3_sort s = Z3_mk_fpa_sort(m_ctx, ebits, sbits); check_error(); return sort(*this, s); }
template<>
@ -3541,25 +3691,25 @@ namespace z3 {
}
inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) {
array<Z3_symbol> _enum_names(n);
for (unsigned i = 0; i < n; i++) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); }
for (unsigned i = 0; i < n; ++i) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); }
array<Z3_func_decl> _cs(n);
array<Z3_func_decl> _ts(n);
Z3_symbol _name = Z3_mk_string_symbol(*this, name);
sort s = to_sort(*this, Z3_mk_enumeration_sort(*this, _name, n, _enum_names.ptr(), _cs.ptr(), _ts.ptr()));
check_error();
for (unsigned i = 0; i < n; i++) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); }
for (unsigned i = 0; i < n; ++i) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); }
return s;
}
inline func_decl context::tuple_sort(char const * name, unsigned n, char const * const * names, sort const* sorts, func_decl_vector & projs) {
array<Z3_symbol> _names(n);
array<Z3_sort> _sorts(n);
for (unsigned i = 0; i < n; i++) { _names[i] = Z3_mk_string_symbol(*this, names[i]); _sorts[i] = sorts[i]; }
for (unsigned i = 0; i < n; ++i) { _names[i] = Z3_mk_string_symbol(*this, names[i]); _sorts[i] = sorts[i]; }
array<Z3_func_decl> _projs(n);
Z3_symbol _name = Z3_mk_string_symbol(*this, name);
Z3_func_decl tuple;
sort _ignore_s = to_sort(*this, Z3_mk_tuple_sort(*this, _name, n, _names.ptr(), _sorts.ptr(), &tuple, _projs.ptr()));
check_error();
for (unsigned i = 0; i < n; i++) { projs.push_back(func_decl(*this, _projs[i])); }
for (unsigned i = 0; i < n; ++i) { projs.push_back(func_decl(*this, _projs[i])); }
return func_decl(*this, tuple);
}
@ -3682,7 +3832,7 @@ namespace z3 {
inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) {
array<Z3_sort> args(arity);
for (unsigned i = 0; i < arity; i++) {
for (unsigned i = 0; i < arity; ++i) {
check_context(domain[i], range);
args[i] = domain[i];
}
@ -3697,7 +3847,7 @@ namespace z3 {
inline func_decl context::function(symbol const& name, sort_vector const& domain, sort const& range) {
array<Z3_sort> args(domain.size());
for (unsigned i = 0; i < domain.size(); i++) {
for (unsigned i = 0; i < domain.size(); ++i) {
check_context(domain[i], range);
args[i] = domain[i];
}
@ -3753,7 +3903,7 @@ namespace z3 {
inline func_decl context::recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range) {
array<Z3_sort> args(arity);
for (unsigned i = 0; i < arity; i++) {
for (unsigned i = 0; i < arity; ++i) {
check_context(domain[i], range);
args[i] = domain[i];
}
@ -3877,7 +4027,7 @@ namespace z3 {
inline expr func_decl::operator()(unsigned n, expr const * args) const {
array<Z3_ast> _args(n);
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
check_context(*this, args[i]);
_args[i] = args[i];
}
@ -3888,7 +4038,7 @@ namespace z3 {
}
inline expr func_decl::operator()(expr_vector const& args) const {
array<Z3_ast> _args(args.size());
for (unsigned i = 0; i < args.size(); i++) {
for (unsigned i = 0; i < args.size(); ++i) {
check_context(*this, args[i]);
_args[i] = args[i];
}
@ -4043,6 +4193,19 @@ namespace z3 {
return expr(f.ctx(), r);
}
inline expr array_default(expr const & a) {
Z3_ast r = Z3_mk_array_default(a.ctx(), a);
a.check_error();
return expr(a.ctx(), r);
}
inline expr array_ext(expr const & a, expr const & b) {
check_context(a, b);
Z3_ast r = Z3_mk_array_ext(a.ctx(), a, b);
a.check_error();
return expr(a.ctx(), r);
}
#define MK_EXPR1(_fn, _arg) \
Z3_ast r = _fn(_arg.ctx(), _arg); \
_arg.check_error(); \
@ -4106,6 +4269,54 @@ namespace z3 {
MK_EXPR2(Z3_mk_set_subset, a, b);
}
// finite set operations
inline expr finite_set_empty(sort const& s) {
Z3_ast r = Z3_mk_finite_set_empty(s.ctx(), s);
s.check_error();
return expr(s.ctx(), r);
}
inline expr finite_set_singleton(expr const& e) {
MK_EXPR1(Z3_mk_finite_set_singleton, e);
}
inline expr finite_set_union(expr const& a, expr const& b) {
MK_EXPR2(Z3_mk_finite_set_union, a, b);
}
inline expr finite_set_intersect(expr const& a, expr const& b) {
MK_EXPR2(Z3_mk_finite_set_intersect, a, b);
}
inline expr finite_set_difference(expr const& a, expr const& b) {
MK_EXPR2(Z3_mk_finite_set_difference, a, b);
}
inline expr finite_set_member(expr const& e, expr const& s) {
MK_EXPR2(Z3_mk_finite_set_member, e, s);
}
inline expr finite_set_size(expr const& s) {
MK_EXPR1(Z3_mk_finite_set_size, s);
}
inline expr finite_set_subset(expr const& a, expr const& b) {
MK_EXPR2(Z3_mk_finite_set_subset, a, b);
}
inline expr finite_set_map(expr const& f, expr const& s) {
MK_EXPR2(Z3_mk_finite_set_map, f, s);
}
inline expr finite_set_filter(expr const& f, expr const& s) {
MK_EXPR2(Z3_mk_finite_set_filter, f, s);
}
inline expr finite_set_range(expr const& low, expr const& high) {
MK_EXPR2(Z3_mk_finite_set_range, low, high);
}
// sequence and regular expression operations.
// union is +
// concat is overloaded to handle sequences and regular expressions
@ -4314,6 +4525,23 @@ namespace z3 {
return expr(ctx(), r);
}
inline expr expr::update(expr_vector const& args) const {
array<Z3_ast> _args(args.size());
for (unsigned i = 0; i < args.size(); ++i) {
_args[i] = args[i];
}
Z3_ast r = Z3_update_term(ctx(), m_ast, args.size(), _args.ptr());
check_error();
return expr(ctx(), r);
}
inline expr expr::update_field(func_decl const& field_access, expr const& new_value) const {
assert(is_datatype());
Z3_ast r = Z3_datatype_update_field(ctx(), field_access, m_ast, new_value);
check_error();
return expr(ctx(), r);
}
typedef std::function<void(expr const& proof, std::vector<unsigned> const& deps, expr_vector const& clause)> on_clause_eh_t;
class on_clause {
@ -4642,6 +4870,223 @@ namespace z3 {
}
};
/**
\brief Wrapper for Z3 Real Closed Field (RCF) numerals.
RCF numerals can represent:
- Rational numbers
- Algebraic numbers (roots of polynomials)
- Transcendental extensions (e.g., pi, e)
- Infinitesimal extensions
*/
class rcf_num {
Z3_context m_ctx;
Z3_rcf_num m_num;
void check_context(rcf_num const& other) const {
if (m_ctx != other.m_ctx) {
throw exception("rcf_num objects from different contexts");
}
}
public:
rcf_num(context& c, Z3_rcf_num n): m_ctx(c), m_num(n) {}
rcf_num(context& c, int val): m_ctx(c) {
m_num = Z3_rcf_mk_small_int(c, val);
}
rcf_num(context& c, char const* val): m_ctx(c) {
m_num = Z3_rcf_mk_rational(c, val);
}
rcf_num(rcf_num const& other): m_ctx(other.m_ctx) {
// Create a copy by converting to string and back
std::string str = Z3_rcf_num_to_string(m_ctx, other.m_num, false, false);
m_num = Z3_rcf_mk_rational(m_ctx, str.c_str());
}
rcf_num& operator=(rcf_num const& other) {
if (this != &other) {
Z3_rcf_del(m_ctx, m_num);
m_ctx = other.m_ctx;
std::string str = Z3_rcf_num_to_string(m_ctx, other.m_num, false, false);
m_num = Z3_rcf_mk_rational(m_ctx, str.c_str());
}
return *this;
}
~rcf_num() {
Z3_rcf_del(m_ctx, m_num);
}
operator Z3_rcf_num() const { return m_num; }
Z3_context ctx() const { return m_ctx; }
/**
\brief Return string representation of the RCF numeral.
*/
std::string to_string(bool compact = false) const {
return std::string(Z3_rcf_num_to_string(m_ctx, m_num, compact, false));
}
/**
\brief Return decimal string representation with given precision.
*/
std::string to_decimal(unsigned precision = 10) const {
return std::string(Z3_rcf_num_to_decimal_string(m_ctx, m_num, precision));
}
// Arithmetic operations
rcf_num operator+(rcf_num const& other) const {
check_context(other);
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
Z3_rcf_add(m_ctx, m_num, other.m_num));
}
rcf_num operator-(rcf_num const& other) const {
check_context(other);
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
Z3_rcf_sub(m_ctx, m_num, other.m_num));
}
rcf_num operator*(rcf_num const& other) const {
check_context(other);
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
Z3_rcf_mul(m_ctx, m_num, other.m_num));
}
rcf_num operator/(rcf_num const& other) const {
check_context(other);
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
Z3_rcf_div(m_ctx, m_num, other.m_num));
}
rcf_num operator-() const {
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
Z3_rcf_neg(m_ctx, m_num));
}
/**
\brief Return the power of this number raised to k.
*/
rcf_num power(unsigned k) const {
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
Z3_rcf_power(m_ctx, m_num, k));
}
/**
\brief Return the multiplicative inverse (1/this).
*/
rcf_num inv() const {
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
Z3_rcf_inv(m_ctx, m_num));
}
// Comparison operations
bool operator<(rcf_num const& other) const {
check_context(other);
return Z3_rcf_lt(m_ctx, m_num, other.m_num);
}
bool operator>(rcf_num const& other) const {
check_context(other);
return Z3_rcf_gt(m_ctx, m_num, other.m_num);
}
bool operator<=(rcf_num const& other) const {
check_context(other);
return Z3_rcf_le(m_ctx, m_num, other.m_num);
}
bool operator>=(rcf_num const& other) const {
check_context(other);
return Z3_rcf_ge(m_ctx, m_num, other.m_num);
}
bool operator==(rcf_num const& other) const {
check_context(other);
return Z3_rcf_eq(m_ctx, m_num, other.m_num);
}
bool operator!=(rcf_num const& other) const {
check_context(other);
return Z3_rcf_neq(m_ctx, m_num, other.m_num);
}
// Type queries
bool is_rational() const {
return Z3_rcf_is_rational(m_ctx, m_num);
}
bool is_algebraic() const {
return Z3_rcf_is_algebraic(m_ctx, m_num);
}
bool is_infinitesimal() const {
return Z3_rcf_is_infinitesimal(m_ctx, m_num);
}
bool is_transcendental() const {
return Z3_rcf_is_transcendental(m_ctx, m_num);
}
friend std::ostream& operator<<(std::ostream& out, rcf_num const& n) {
return out << n.to_string();
}
};
/**
\brief Create an RCF numeral representing pi.
*/
inline rcf_num rcf_pi(context& c) {
return rcf_num(c, Z3_rcf_mk_pi(c));
}
/**
\brief Create an RCF numeral representing e (Euler's constant).
*/
inline rcf_num rcf_e(context& c) {
return rcf_num(c, Z3_rcf_mk_e(c));
}
/**
\brief Create an RCF numeral representing an infinitesimal.
*/
inline rcf_num rcf_infinitesimal(context& c) {
return rcf_num(c, Z3_rcf_mk_infinitesimal(c));
}
/**
\brief Find roots of a polynomial with given coefficients.
The polynomial is a[n-1]*x^(n-1) + ... + a[1]*x + a[0].
Returns a vector of RCF numerals representing the roots.
*/
inline std::vector<rcf_num> rcf_roots(context& c, std::vector<rcf_num> const& coeffs) {
if (coeffs.empty()) {
throw exception("polynomial coefficients cannot be empty");
}
unsigned n = static_cast<unsigned>(coeffs.size());
std::vector<Z3_rcf_num> a(n);
std::vector<Z3_rcf_num> roots(n);
for (unsigned i = 0; i < n; ++i) {
a[i] = coeffs[i];
}
unsigned num_roots = Z3_rcf_mk_roots(c, n, a.data(), roots.data());
std::vector<rcf_num> result;
result.reserve(num_roots);
for (unsigned i = 0; i < num_roots; ++i) {
result.push_back(rcf_num(c, roots[i]));
}
return result;
}
}
/**@}*/

View file

@ -64,6 +64,7 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE
FiniteDomainExpr.cs
FiniteDomainNum.cs
FiniteDomainSort.cs
FiniteSetSort.cs
Fixedpoint.cs
FPExpr.cs
FPNum.cs

View file

@ -2442,6 +2442,180 @@ namespace Microsoft.Z3
#endregion
#region Finite Sets
/// <summary>
/// Create a finite set sort over the given element sort.
/// </summary>
public FiniteSetSort MkFiniteSetSort(Sort elemSort)
{
Debug.Assert(elemSort != null);
CheckContextMatch(elemSort);
return new FiniteSetSort(this, elemSort);
}
/// <summary>
/// Check if a sort is a finite set sort.
/// </summary>
public bool IsFiniteSetSort(Sort s)
{
Debug.Assert(s != null);
CheckContextMatch(s);
return Native.Z3_is_finite_set_sort(nCtx, s.NativeObject) != 0;
}
/// <summary>
/// Get the element sort (basis) of a finite set sort.
/// </summary>
public Sort GetFiniteSetSortBasis(Sort s)
{
Debug.Assert(s != null);
CheckContextMatch(s);
return Sort.Create(this, Native.Z3_get_finite_set_sort_basis(nCtx, s.NativeObject));
}
/// <summary>
/// Create an empty finite set.
/// </summary>
public Expr MkFiniteSetEmpty(Sort setSort)
{
Debug.Assert(setSort != null);
CheckContextMatch(setSort);
return Expr.Create(this, Native.Z3_mk_finite_set_empty(nCtx, setSort.NativeObject));
}
/// <summary>
/// Create a singleton finite set.
/// </summary>
public Expr MkFiniteSetSingleton(Expr elem)
{
Debug.Assert(elem != null);
CheckContextMatch(elem);
return Expr.Create(this, Native.Z3_mk_finite_set_singleton(nCtx, elem.NativeObject));
}
/// <summary>
/// Create the union of two finite sets.
/// </summary>
public Expr MkFiniteSetUnion(Expr s1, Expr s2)
{
Debug.Assert(s1 != null);
Debug.Assert(s2 != null);
CheckContextMatch(s1);
CheckContextMatch(s2);
return Expr.Create(this, Native.Z3_mk_finite_set_union(nCtx, s1.NativeObject, s2.NativeObject));
}
/// <summary>
/// Create the intersection of two finite sets.
/// </summary>
public Expr MkFiniteSetIntersect(Expr s1, Expr s2)
{
Debug.Assert(s1 != null);
Debug.Assert(s2 != null);
CheckContextMatch(s1);
CheckContextMatch(s2);
return Expr.Create(this, Native.Z3_mk_finite_set_intersect(nCtx, s1.NativeObject, s2.NativeObject));
}
/// <summary>
/// Create the difference of two finite sets.
/// </summary>
public Expr MkFiniteSetDifference(Expr s1, Expr s2)
{
Debug.Assert(s1 != null);
Debug.Assert(s2 != null);
CheckContextMatch(s1);
CheckContextMatch(s2);
return Expr.Create(this, Native.Z3_mk_finite_set_difference(nCtx, s1.NativeObject, s2.NativeObject));
}
/// <summary>
/// Check for membership in a finite set.
/// </summary>
public BoolExpr MkFiniteSetMember(Expr elem, Expr set)
{
Debug.Assert(elem != null);
Debug.Assert(set != null);
CheckContextMatch(elem);
CheckContextMatch(set);
return (BoolExpr)Expr.Create(this, Native.Z3_mk_finite_set_member(nCtx, elem.NativeObject, set.NativeObject));
}
/// <summary>
/// Get the cardinality of a finite set.
/// </summary>
public Expr MkFiniteSetSize(Expr set)
{
Debug.Assert(set != null);
CheckContextMatch(set);
return Expr.Create(this, Native.Z3_mk_finite_set_size(nCtx, set.NativeObject));
}
/// <summary>
/// Check if one finite set is a subset of another.
/// </summary>
public BoolExpr MkFiniteSetSubset(Expr s1, Expr s2)
{
Debug.Assert(s1 != null);
Debug.Assert(s2 != null);
CheckContextMatch(s1);
CheckContextMatch(s2);
return (BoolExpr)Expr.Create(this, Native.Z3_mk_finite_set_subset(nCtx, s1.NativeObject, s2.NativeObject));
}
/// <summary>
/// Map a function over all elements in a finite set.
/// </summary>
public Expr MkFiniteSetMap(Expr f, Expr set)
{
Debug.Assert(f != null);
Debug.Assert(set != null);
CheckContextMatch(f);
CheckContextMatch(set);
return Expr.Create(this, Native.Z3_mk_finite_set_map(nCtx, f.NativeObject, set.NativeObject));
}
/// <summary>
/// Filter a finite set with a predicate.
/// </summary>
public Expr MkFiniteSetFilter(Expr f, Expr set)
{
Debug.Assert(f != null);
Debug.Assert(set != null);
CheckContextMatch(f);
CheckContextMatch(set);
return Expr.Create(this, Native.Z3_mk_finite_set_filter(nCtx, f.NativeObject, set.NativeObject));
}
/// <summary>
/// Create a finite set containing integers in the range [low, high].
/// </summary>
public Expr MkFiniteSetRange(Expr low, Expr high)
{
Debug.Assert(low != null);
Debug.Assert(high != null);
CheckContextMatch(low);
CheckContextMatch(high);
return Expr.Create(this, Native.Z3_mk_finite_set_range(nCtx, low.NativeObject, high.NativeObject));
}
#endregion
#region Sequence, string and regular expressions
/// <summary>
@ -2647,6 +2821,55 @@ namespace Microsoft.Z3
return new SeqExpr(this, Native.Z3_mk_seq_replace(nCtx, s.NativeObject, src.NativeObject, dst.NativeObject));
}
/// <summary>
/// Map function f over the sequence s.
/// </summary>
public Expr MkSeqMap(Expr f, SeqExpr s)
{
Debug.Assert(f != null);
Debug.Assert(s != null);
CheckContextMatch(f, s);
return Expr.Create(this, Native.Z3_mk_seq_map(nCtx, f.NativeObject, s.NativeObject));
}
/// <summary>
/// Map function f over the sequence s at index i.
/// </summary>
public Expr MkSeqMapi(Expr f, Expr i, SeqExpr s)
{
Debug.Assert(f != null);
Debug.Assert(i != null);
Debug.Assert(s != null);
CheckContextMatch(f, i, s);
return Expr.Create(this, Native.Z3_mk_seq_mapi(nCtx, f.NativeObject, i.NativeObject, s.NativeObject));
}
/// <summary>
/// Fold left the function f over the sequence s with initial value a.
/// </summary>
public Expr MkSeqFoldLeft(Expr f, Expr a, SeqExpr s)
{
Debug.Assert(f != null);
Debug.Assert(a != null);
Debug.Assert(s != null);
CheckContextMatch(f, a, s);
return Expr.Create(this, Native.Z3_mk_seq_foldl(nCtx, f.NativeObject, a.NativeObject, s.NativeObject));
}
/// <summary>
/// Fold left with index the function f over the sequence s with initial value a starting at index i.
/// </summary>
public Expr MkSeqFoldLeftI(Expr f, Expr i, Expr a, SeqExpr s)
{
Debug.Assert(f != null);
Debug.Assert(i != null);
Debug.Assert(a != null);
Debug.Assert(s != null);
CheckContextMatch(f, i, a);
CheckContextMatch(s, a);
return Expr.Create(this, Native.Z3_mk_seq_foldli(nCtx, f.NativeObject, i.NativeObject, a.NativeObject, s.NativeObject));
}
/// <summary>
/// Convert a regular expression that accepts sequence s.
/// </summary>
@ -3395,7 +3618,12 @@ namespace Microsoft.Z3
/// <seealso cref="Sort.ToString()"/>
public Z3_ast_print_mode PrintMode
{
set { Native.Z3_set_ast_print_mode(nCtx, (uint)value); }
get { return m_print_mode; }
set
{
Native.Z3_set_ast_print_mode(nCtx, (uint)value);
m_print_mode = value;
}
}
#endregion
@ -3438,6 +3666,32 @@ namespace Microsoft.Z3
AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls)));
return assertions.ToBoolExprArray();
}
/// <summary>
/// Convert a benchmark into SMT-LIB2 formatted string.
/// </summary>
/// <param name="name">Name of the benchmark. May be null.</param>
/// <param name="logic">The benchmark logic. May be null.</param>
/// <param name="status">Status string, such as "sat", "unsat", or "unknown".</param>
/// <param name="attributes">Other attributes, such as source, difficulty or category. May be null.</param>
/// <param name="assumptions">Auxiliary assumptions.</param>
/// <param name="formula">Formula to be checked for consistency in conjunction with assumptions.</param>
/// <returns>A string representation of the benchmark in SMT-LIB2 format.</returns>
public string BenchmarkToSmtlibString(string name, string logic, string status, string attributes, BoolExpr[] assumptions, BoolExpr formula)
{
Debug.Assert(assumptions != null);
Debug.Assert(formula != null);
return Native.Z3_benchmark_to_smtlib_string(
nCtx,
name,
logic,
status,
attributes,
(uint)(assumptions?.Length ?? 0),
AST.ArrayToNative(assumptions),
formula.NativeObject);
}
#endregion
#region Goals
@ -4849,6 +5103,44 @@ namespace Microsoft.Z3
return a.NativeObject;
}
/// <summary>
/// Create a partial order relation over a sort.
/// </summary>
/// <param name="a">The sort of the relation.</param>
/// <param name="index">The index of the relation.</param>
public FuncDecl MkPartialOrder(Sort a, uint index)
{
return new FuncDecl(this, Native.Z3_mk_partial_order(this.nCtx, a.NativeObject, index));
}
/// <summary>
/// Create the transitive closure of a binary relation.
/// </summary>
/// <remarks>The resulting relation is recursive.</remarks>
/// <param name="f">A binary relation represented as a function declaration.</param>
public FuncDecl MkTransitiveClosure(FuncDecl f)
{
return new FuncDecl(this, Native.Z3_mk_transitive_closure(this.nCtx, f.NativeObject));
}
/// <summary>
/// Return the nonzero subresultants of p and q with respect to the "variable" x.
/// </summary>
/// <remarks>
/// p, q and x are Z3 expressions where p and q are arithmetic terms.
/// Note that any subterm that cannot be viewed as a polynomial is assumed to be a variable.
/// </remarks>
/// <param name="p">First arithmetic term.</param>
/// <param name="q">Second arithmetic term.</param>
/// <param name="x">The variable with respect to which subresultants are computed.</param>
public ASTVector PolynomialSubresultants(Expr p, Expr q, Expr x)
{
CheckContextMatch(p);
CheckContextMatch(q);
CheckContextMatch(x);
return new ASTVector(this, Native.Z3_polynomial_subresultants(this.nCtx, p.NativeObject, q.NativeObject, x.NativeObject));
}
/// <summary>
/// Return a string describing all available parameters to <c>Expr.Simplify</c>.
/// </summary>
@ -4905,6 +5197,7 @@ namespace Microsoft.Z3
internal Native.Z3_error_handler m_n_err_handler = null;
internal static Object creation_lock = new Object();
internal IntPtr nCtx { get { return m_ctx; } }
private Z3_ast_print_mode m_print_mode = Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT;
internal void NativeErrorHandler(IntPtr ctx, Z3_error_code errorCode)
{

View file

@ -159,6 +159,28 @@ namespace Microsoft.Z3
return Expr.Create(Context, Native.Z3_substitute_vars(Context.nCtx, NativeObject, (uint)to.Length, Expr.ArrayToNative(to)));
}
/// <summary>
/// Substitute functions in <paramref name="from"/> with the expressions in <paramref name="to"/>.
/// </summary>
/// <remarks>
/// The expressions in <paramref name="to"/> can have free variables. The free variable in <c>to[i]</c> at de-Bruijn index 0
/// refers to the first argument of <c>from[i]</c>, the free variable at index 1 corresponds to the second argument, and so on.
/// The arrays <paramref name="from"/> and <paramref name="to"/> must have the same size.
/// </remarks>
public Expr SubstituteFuns(FuncDecl[] from, Expr[] to)
{
Debug.Assert(from != null);
Debug.Assert(to != null);
Debug.Assert(from.All(f => f != null));
Debug.Assert(to.All(t => t != null));
Context.CheckContextMatch<FuncDecl>(from);
Context.CheckContextMatch<Expr>(to);
if (from.Length != to.Length)
throw new Z3Exception("Arrays 'from' and 'to' must have the same length");
return Expr.Create(Context, Native.Z3_substitute_funs(Context.nCtx, NativeObject, (uint)from.Length, FuncDecl.ArrayToNative(from), Expr.ArrayToNative(to)));
}
/// <summary>
/// Translates (copies) the term to the Context <paramref name="ctx"/>.
/// </summary>

View file

@ -0,0 +1,53 @@
/*++
Copyright (c) 2024 Microsoft Corporation
Module Name:
FiniteSetSort.cs
Abstract:
Z3 Managed API: Finite Set Sorts
Author:
GitHub Copilot
Notes:
--*/
using System.Diagnostics;
using System;
namespace Microsoft.Z3
{
/// <summary>
/// Finite set sorts.
/// </summary>
public class FiniteSetSort : Sort
{
#region Internal
internal FiniteSetSort(Context ctx, IntPtr obj)
: base(ctx, obj)
{
Debug.Assert(ctx != null);
}
internal FiniteSetSort(Context ctx, Sort elemSort)
: base(ctx, Native.Z3_mk_finite_set_sort(ctx.nCtx, elemSort.NativeObject))
{
Debug.Assert(ctx != null);
Debug.Assert(elemSort != null);
}
#endregion
/// <summary>
/// Get the element sort (basis) of this finite set sort.
/// </summary>
public Sort Basis
{
get { return Sort.Create(Context, Native.Z3_get_finite_set_sort_basis(Context.nCtx, NativeObject)); }
}
}
}

View file

@ -277,6 +277,50 @@ namespace Microsoft.Z3
public Z3_ast[] Units
=> ntvContext.ToArray(Native.Z3_solver_get_units(nCtx, z3solver));
/// <summary>
/// Non-unit literals in the solver state.
/// </summary>
public Z3_ast[] NonUnits
=> ntvContext.ToArray(Native.Z3_solver_get_non_units(nCtx, z3solver));
/// <summary>
/// Trail of the solver state after a check() call.
/// </summary>
public Z3_ast[] Trail
=> ntvContext.ToArray(Native.Z3_solver_get_trail(nCtx, z3solver));
/// <summary>
/// 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.
/// </summary>
public Z3_ast CongruenceRoot(Z3_ast t)
{
Debug.Assert(t != IntPtr.Zero);
return Native.Z3_solver_congruence_root(nCtx, z3solver, t);
}
/// <summary>
/// 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.
/// </summary>
public Z3_ast CongruenceNext(Z3_ast t)
{
Debug.Assert(t != IntPtr.Zero);
return Native.Z3_solver_congruence_next(nCtx, z3solver, t);
}
/// <summary>
/// Explain congruence of a and b relative to the current search state.
/// </summary>
public Z3_ast CongruenceExplain(Z3_ast a, Z3_ast b)
{
Debug.Assert(a != IntPtr.Zero);
Debug.Assert(b != IntPtr.Zero);
return Native.Z3_solver_congruence_explain(nCtx, z3solver, a, b);
}
/// <summary>
/// Checks whether the assertions in the solver are consistent or not.
/// </summary>

View file

@ -156,7 +156,7 @@ namespace Microsoft.Z3
/// <remarks>
/// This API is an alternative to <see cref="Check(Expr[])"/> with assumptions for extracting unsat cores.
/// Both APIs can be used in the same solver. The unsat core will contain a combination
/// of the Boolean variables provided using <see cref="AssertAndTrack(BoolExpr[],BoolExpr[])"/>
/// of the Boolean variables provided using <see cref="AssertAndTrack(BoolExpr,BoolExpr)"/>
/// and the Boolean literals
/// provided using <see cref="Check(Expr[])"/> with assumptions.
/// </remarks>

459
src/api/dotnet/RCFNum.cs Normal file
View file

@ -0,0 +1,459 @@
/*++
Copyright (c) 2024 Microsoft Corporation
Module Name:
RCFNum.cs
Abstract:
Z3 Managed API: Real Closed Field (RCF) Numerals
Author:
GitHub Copilot 2024-01-12
Notes:
--*/
using System.Diagnostics;
using System;
namespace Microsoft.Z3
{
/// <summary>
/// Real Closed Field (RCF) numerals.
///
/// RCF numerals can represent:
/// - Rational numbers
/// - Algebraic numbers (roots of polynomials)
/// - Transcendental extensions (e.g., pi, e)
/// - Infinitesimal extensions
/// </summary>
public class RCFNum : Z3Object
{
/// <summary>
/// Create an RCF numeral from a rational string.
/// </summary>
/// <param name="ctx">Z3 context</param>
/// <param name="value">String representation of a rational number (e.g., "3/2", "0.5", "42")</param>
public RCFNum(Context ctx, string value)
: base(ctx, Native.Z3_rcf_mk_rational(ctx.nCtx, value))
{
Debug.Assert(ctx != null);
}
/// <summary>
/// Create an RCF numeral from a small integer.
/// </summary>
/// <param name="ctx">Z3 context</param>
/// <param name="value">Integer value</param>
public RCFNum(Context ctx, int value)
: base(ctx, Native.Z3_rcf_mk_small_int(ctx.nCtx, value))
{
Debug.Assert(ctx != null);
}
/// <summary>
/// Internal constructor for wrapping native RCF numeral pointers.
/// </summary>
internal RCFNum(Context ctx, IntPtr obj)
: base(ctx, obj)
{
Debug.Assert(ctx != null);
}
/// <summary>
/// Create an RCF numeral representing pi.
/// </summary>
/// <param name="ctx">Z3 context</param>
/// <returns>RCF numeral for pi</returns>
public static RCFNum MkPi(Context ctx)
{
return new RCFNum(ctx, Native.Z3_rcf_mk_pi(ctx.nCtx));
}
/// <summary>
/// Create an RCF numeral representing e (Euler's constant).
/// </summary>
/// <param name="ctx">Z3 context</param>
/// <returns>RCF numeral for e</returns>
public static RCFNum MkE(Context ctx)
{
return new RCFNum(ctx, Native.Z3_rcf_mk_e(ctx.nCtx));
}
/// <summary>
/// Create an RCF numeral representing an infinitesimal.
/// </summary>
/// <param name="ctx">Z3 context</param>
/// <returns>RCF numeral for an infinitesimal</returns>
public static RCFNum MkInfinitesimal(Context ctx)
{
return new RCFNum(ctx, Native.Z3_rcf_mk_infinitesimal(ctx.nCtx));
}
/// <summary>
/// Find roots of a polynomial.
///
/// The polynomial is a[n-1]*x^(n-1) + ... + a[1]*x + a[0].
/// </summary>
/// <param name="ctx">Z3 context</param>
/// <param name="coefficients">Polynomial coefficients (constant term first)</param>
/// <returns>Array of RCF numerals representing the roots</returns>
public static RCFNum[] MkRoots(Context ctx, RCFNum[] coefficients)
{
if (coefficients == null || coefficients.Length == 0)
{
throw new Z3Exception("Polynomial coefficients cannot be empty");
}
uint n = (uint)coefficients.Length;
IntPtr[] a = new IntPtr[n];
IntPtr[] roots = new IntPtr[n];
for (uint i = 0; i < n; i++)
{
a[i] = coefficients[i].NativeObject;
}
uint numRoots = Native.Z3_rcf_mk_roots(ctx.nCtx, n, a, roots);
RCFNum[] result = new RCFNum[numRoots];
for (uint i = 0; i < numRoots; i++)
{
result[i] = new RCFNum(ctx, roots[i]);
}
return result;
}
/// <summary>
/// Add two RCF numerals.
/// </summary>
/// <param name="other">The RCF numeral to add</param>
/// <returns>this + other</returns>
public RCFNum Add(RCFNum other)
{
CheckContext(other);
return new RCFNum(Context, Native.Z3_rcf_add(Context.nCtx, NativeObject, other.NativeObject));
}
/// <summary>
/// Subtract two RCF numerals.
/// </summary>
/// <param name="other">The RCF numeral to subtract</param>
/// <returns>this - other</returns>
public RCFNum Sub(RCFNum other)
{
CheckContext(other);
return new RCFNum(Context, Native.Z3_rcf_sub(Context.nCtx, NativeObject, other.NativeObject));
}
/// <summary>
/// Multiply two RCF numerals.
/// </summary>
/// <param name="other">The RCF numeral to multiply</param>
/// <returns>this * other</returns>
public RCFNum Mul(RCFNum other)
{
CheckContext(other);
return new RCFNum(Context, Native.Z3_rcf_mul(Context.nCtx, NativeObject, other.NativeObject));
}
/// <summary>
/// Divide two RCF numerals.
/// </summary>
/// <param name="other">The RCF numeral to divide by</param>
/// <returns>this / other</returns>
public RCFNum Div(RCFNum other)
{
CheckContext(other);
return new RCFNum(Context, Native.Z3_rcf_div(Context.nCtx, NativeObject, other.NativeObject));
}
/// <summary>
/// Negate this RCF numeral.
/// </summary>
/// <returns>-this</returns>
public RCFNum Neg()
{
return new RCFNum(Context, Native.Z3_rcf_neg(Context.nCtx, NativeObject));
}
/// <summary>
/// Compute the multiplicative inverse.
/// </summary>
/// <returns>1/this</returns>
public RCFNum Inv()
{
return new RCFNum(Context, Native.Z3_rcf_inv(Context.nCtx, NativeObject));
}
/// <summary>
/// Raise this RCF numeral to a power.
/// </summary>
/// <param name="k">The exponent</param>
/// <returns>this^k</returns>
public RCFNum Power(uint k)
{
return new RCFNum(Context, Native.Z3_rcf_power(Context.nCtx, NativeObject, k));
}
/// <summary>
/// Operator overload for addition.
/// </summary>
public static RCFNum operator +(RCFNum a, RCFNum b)
{
return a.Add(b);
}
/// <summary>
/// Operator overload for subtraction.
/// </summary>
public static RCFNum operator -(RCFNum a, RCFNum b)
{
return a.Sub(b);
}
/// <summary>
/// Operator overload for multiplication.
/// </summary>
public static RCFNum operator *(RCFNum a, RCFNum b)
{
return a.Mul(b);
}
/// <summary>
/// Operator overload for division.
/// </summary>
public static RCFNum operator /(RCFNum a, RCFNum b)
{
return a.Div(b);
}
/// <summary>
/// Operator overload for negation.
/// </summary>
public static RCFNum operator -(RCFNum a)
{
return a.Neg();
}
/// <summary>
/// Check if this RCF numeral is less than another.
/// </summary>
/// <param name="other">The RCF numeral to compare with</param>
/// <returns>true if this &lt; other</returns>
public bool Lt(RCFNum other)
{
CheckContext(other);
return 0 != Native.Z3_rcf_lt(Context.nCtx, NativeObject, other.NativeObject);
}
/// <summary>
/// Check if this RCF numeral is greater than another.
/// </summary>
/// <param name="other">The RCF numeral to compare with</param>
/// <returns>true if this &gt; other</returns>
public bool Gt(RCFNum other)
{
CheckContext(other);
return 0 != Native.Z3_rcf_gt(Context.nCtx, NativeObject, other.NativeObject);
}
/// <summary>
/// Check if this RCF numeral is less than or equal to another.
/// </summary>
/// <param name="other">The RCF numeral to compare with</param>
/// <returns>true if this &lt;= other</returns>
public bool Le(RCFNum other)
{
CheckContext(other);
return 0 != Native.Z3_rcf_le(Context.nCtx, NativeObject, other.NativeObject);
}
/// <summary>
/// Check if this RCF numeral is greater than or equal to another.
/// </summary>
/// <param name="other">The RCF numeral to compare with</param>
/// <returns>true if this &gt;= other</returns>
public bool Ge(RCFNum other)
{
CheckContext(other);
return 0 != Native.Z3_rcf_ge(Context.nCtx, NativeObject, other.NativeObject);
}
/// <summary>
/// Check if this RCF numeral is equal to another.
/// </summary>
/// <param name="other">The RCF numeral to compare with</param>
/// <returns>true if this == other</returns>
public bool Eq(RCFNum other)
{
CheckContext(other);
return 0 != Native.Z3_rcf_eq(Context.nCtx, NativeObject, other.NativeObject);
}
/// <summary>
/// Check if this RCF numeral is not equal to another.
/// </summary>
/// <param name="other">The RCF numeral to compare with</param>
/// <returns>true if this != other</returns>
public bool Neq(RCFNum other)
{
CheckContext(other);
return 0 != Native.Z3_rcf_neq(Context.nCtx, NativeObject, other.NativeObject);
}
/// <summary>
/// Operator overload for less than.
/// </summary>
public static bool operator <(RCFNum a, RCFNum b)
{
return a.Lt(b);
}
/// <summary>
/// Operator overload for greater than.
/// </summary>
public static bool operator >(RCFNum a, RCFNum b)
{
return a.Gt(b);
}
/// <summary>
/// Operator overload for less than or equal.
/// </summary>
public static bool operator <=(RCFNum a, RCFNum b)
{
return a.Le(b);
}
/// <summary>
/// Operator overload for greater than or equal.
/// </summary>
public static bool operator >=(RCFNum a, RCFNum b)
{
return a.Ge(b);
}
/// <summary>
/// Operator overload for equality.
/// </summary>
public static bool operator ==(RCFNum a, RCFNum b)
{
if (ReferenceEquals(a, b)) return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;
return a.Eq(b);
}
/// <summary>
/// Operator overload for inequality.
/// </summary>
public static bool operator !=(RCFNum a, RCFNum b)
{
return !(a == b);
}
/// <summary>
/// Check if this RCF numeral is a rational number.
/// </summary>
/// <returns>true if this is rational</returns>
public bool IsRational()
{
return 0 != Native.Z3_rcf_is_rational(Context.nCtx, NativeObject);
}
/// <summary>
/// Check if this RCF numeral is an algebraic number.
/// </summary>
/// <returns>true if this is algebraic</returns>
public bool IsAlgebraic()
{
return 0 != Native.Z3_rcf_is_algebraic(Context.nCtx, NativeObject);
}
/// <summary>
/// Check if this RCF numeral is an infinitesimal.
/// </summary>
/// <returns>true if this is infinitesimal</returns>
public bool IsInfinitesimal()
{
return 0 != Native.Z3_rcf_is_infinitesimal(Context.nCtx, NativeObject);
}
/// <summary>
/// Check if this RCF numeral is a transcendental number.
/// </summary>
/// <returns>true if this is transcendental</returns>
public bool IsTranscendental()
{
return 0 != Native.Z3_rcf_is_transcendental(Context.nCtx, NativeObject);
}
/// <summary>
/// Convert this RCF numeral to a string.
/// </summary>
/// <param name="compact">If true, use compact representation</param>
/// <returns>String representation</returns>
public string ToString(bool compact)
{
return Native.Z3_rcf_num_to_string(Context.nCtx, NativeObject, compact ? (byte)1 : (byte)0, 0);
}
/// <summary>
/// Convert this RCF numeral to a string (non-compact).
/// </summary>
/// <returns>String representation</returns>
public override string ToString()
{
return ToString(false);
}
/// <summary>
/// Convert this RCF numeral to a decimal string.
/// </summary>
/// <param name="precision">Number of decimal places</param>
/// <returns>Decimal string representation</returns>
public string ToDecimal(uint precision)
{
return Native.Z3_rcf_num_to_decimal_string(Context.nCtx, NativeObject, precision);
}
/// <summary>
/// Override Equals for proper equality semantics.
/// </summary>
public override bool Equals(object obj)
{
if (obj is RCFNum other)
{
return this == other;
}
return false;
}
/// <summary>
/// Override GetHashCode for proper equality semantics.
/// </summary>
public override int GetHashCode()
{
return NativeObject.GetHashCode();
}
#region Internal
internal override void DecRef(IntPtr o)
{
Native.Z3_rcf_del(Context.nCtx, o);
}
private void CheckContext(RCFNum other)
{
if (Context != other.Context)
{
throw new Z3Exception("RCF numerals from different contexts");
}
}
#endregion
}
}

View file

@ -325,6 +325,66 @@ namespace Microsoft.Z3
}
}
/// <summary>
/// Non-unit literals in the solver state.
/// </summary>
public BoolExpr[] NonUnits
{
get
{
using ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_non_units(Context.nCtx, NativeObject));
return assertions.ToBoolExprArray();
}
}
/// <summary>
/// Trail of the solver state after a check() call.
/// </summary>
public BoolExpr[] Trail
{
get
{
using ASTVector trail = new ASTVector(Context, Native.Z3_solver_get_trail(Context.nCtx, NativeObject));
return trail.ToBoolExprArray();
}
}
/// <summary>
/// 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.
/// </summary>
public Expr CongruenceRoot(Expr t)
{
Debug.Assert(t != null);
Context.CheckContextMatch(t);
return Expr.Create(Context, Native.Z3_solver_congruence_root(Context.nCtx, NativeObject, t.NativeObject));
}
/// <summary>
/// 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.
/// </summary>
public Expr CongruenceNext(Expr t)
{
Debug.Assert(t != null);
Context.CheckContextMatch(t);
return Expr.Create(Context, Native.Z3_solver_congruence_next(Context.nCtx, NativeObject, t.NativeObject));
}
/// <summary>
/// Explain congruence of a and b relative to the current search state.
/// </summary>
public Expr CongruenceExplain(Expr a, Expr b)
{
Debug.Assert(a != null);
Debug.Assert(b != null);
Context.CheckContextMatch(a);
Context.CheckContextMatch(b);
return Expr.Create(Context, Native.Z3_solver_congruence_explain(Context.nCtx, NativeObject, a.NativeObject, b.NativeObject));
}
/// <summary>
/// Checks whether the assertions in the solver are consistent or not.
/// </summary>
@ -493,6 +553,38 @@ namespace Microsoft.Z3
}
}
/// <summary>
/// Retrieve the trail and their associated decision levels after a <c>Check</c> call.
/// The trail contains Boolean literals (decisions and propagations), and the levels
/// array contains the corresponding decision levels at which each literal was assigned.
/// </summary>
public uint[] TrailLevels
{
get
{
using ASTVector trail = new ASTVector(Context, Native.Z3_solver_get_trail(Context.nCtx, NativeObject));
uint[] levels = new uint[trail.Size];
Native.Z3_solver_get_levels(Context.nCtx, NativeObject, trail.NativeObject, (uint)trail.Size, levels);
return levels;
}
}
/// <summary>
/// Set an initial value for a variable to guide the solver's search heuristics.
/// This can improve performance when good initial values are known for the problem domain.
/// </summary>
/// <param name="var">The variable to set an initial value for</param>
/// <param name="value">The initial value for the variable</param>
public void SetInitialValue(Expr var, Expr value)
{
Debug.Assert(var != null);
Debug.Assert(value != null);
Context.CheckContextMatch(var);
Context.CheckContextMatch(value);
Native.Z3_solver_set_initial_value(Context.nCtx, NativeObject, var.NativeObject, value.NativeObject);
}
/// <summary>
/// Create a clone of the current solver with respect to <c>ctx</c>.
/// </summary>
@ -530,6 +622,34 @@ namespace Microsoft.Z3
return Native.Z3_solver_to_string(Context.nCtx, NativeObject);
}
/// <summary>
/// Convert the solver assertions to SMT-LIB2 format as a benchmark.
/// </summary>
/// <param name="status">Status string, such as "sat", "unsat", or "unknown". Default is "unknown".</param>
/// <returns>A string representation of the solver's assertions in SMT-LIB2 format.</returns>
public string ToSmt2(string status = "unknown")
{
BoolExpr[] assertions = Assertions;
BoolExpr formula;
BoolExpr[] assumptions;
if (assertions.Length > 0)
{
// Use last assertion as formula and rest as assumptions
assumptions = new BoolExpr[assertions.Length - 1];
Array.Copy(assertions, assumptions, assertions.Length - 1);
formula = assertions[assertions.Length - 1];
}
else
{
// No assertions, use true
assumptions = new BoolExpr[0];
formula = Context.MkTrue();
}
return Context.BenchmarkToSmtlibString("", "", status, "", assumptions, formula);
}
#region Internal
internal Solver(Context ctx, IntPtr obj)
: base(ctx, obj)

58
src/api/go/CMakeLists.txt Normal file
View file

@ -0,0 +1,58 @@
# Z3 Go API
# Note: This CMakeLists.txt is a placeholder for Go binding integration.
# Go bindings use CGO and are typically built using the Go toolchain directly.
# However, we can set up installation targets here.
if(Z3_BUILD_GO_BINDINGS)
message(STATUS "Z3 Go bindings will be installed")
# Install Go source files
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/z3.go
${CMAKE_CURRENT_SOURCE_DIR}/solver.go
${CMAKE_CURRENT_SOURCE_DIR}/go.mod
${CMAKE_CURRENT_SOURCE_DIR}/README.md
DESTINATION "${CMAKE_INSTALL_LIBDIR}/go/src/github.com/Z3Prover/z3/go"
)
# On Windows, we need to ensure the DLL is accessible
if(WIN32)
message(STATUS "Go bindings on Windows require libz3.dll in PATH")
endif()
# Add a custom target to test Go bindings if Go is available
find_program(GO_EXECUTABLE go)
if(GO_EXECUTABLE)
message(STATUS "Found Go: ${GO_EXECUTABLE}")
# Custom target to build Go bindings
set(CGO_LDFLAGS_VALUE "-L${CMAKE_BINARY_DIR} -lz3")
add_custom_target(go-bindings
COMMAND ${CMAKE_COMMAND} -E env
CGO_CFLAGS=-I${CMAKE_SOURCE_DIR}/src/api
CGO_LDFLAGS=${CGO_LDFLAGS_VALUE}
${GO_EXECUTABLE} build -v
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Building Go bindings"
DEPENDS libz3
VERBATIM
)
# Custom target to test Go examples
add_custom_target(test-go-examples
COMMAND ${CMAKE_COMMAND} -E env
CGO_CFLAGS=-I${CMAKE_SOURCE_DIR}/src/api
CGO_LDFLAGS=${CGO_LDFLAGS_VALUE}
LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH}
PATH=${CMAKE_BINARY_DIR}$<SEMICOLON>$ENV{PATH}
${GO_EXECUTABLE} run basic_example.go
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/examples/go
COMMENT "Running Go examples"
DEPENDS libz3
VERBATIM
)
else()
message(STATUS "Go not found - Go bindings can be built manually")
endif()
endif()

331
src/api/go/README.md Normal file
View file

@ -0,0 +1,331 @@
# Z3 Go Bindings
This directory contains Go language bindings for the Z3 theorem prover.
## Overview
The Go bindings provide a comprehensive interface to Z3's C API using CGO. The bindings support:
- **Core Z3 Types**: Context, Config, Symbol, AST, Sort, Expr, FuncDecl
- **Solver Operations**: Creating solvers, asserting constraints, checking satisfiability
- **Model Manipulation**: Extracting and evaluating models
- **Boolean Logic**: And, Or, Not, Implies, Iff, Xor
- **Arithmetic**: Add, Sub, Mul, Div, Mod, comparison operators
- **Bit-vectors**: Full bit-vector arithmetic, bitwise operations, shifts, comparisons
- **Floating Point**: IEEE 754 floating-point arithmetic with rounding modes
- **Arrays**: Select, Store, constant arrays
- **Sequences/Strings**: String operations, concatenation, contains, indexing
- **Regular Expressions**: Pattern matching, Kleene star/plus, regex operations
- **Quantifiers**: Forall, Exists
- **Functions**: Function declarations and applications
- **Tactics & Goals**: Goal-based solving and tactic combinators
- **Probes**: Goal property checking
- **Datatypes**: Algebraic datatypes, tuples, enumerations, lists
- **Parameters**: Solver and tactic configuration
- **Optimize**: Optimization problems with maximize/minimize objectives
## Building
### Prerequisites
- Go 1.20 or later
- Z3 library built and installed
- CGO enabled
### With CMake
```bash
mkdir build && cd build
cmake -DBUILD_GO_BINDINGS=ON ..
make
```
### With Python Build System
```bash
python scripts/mk_make.py --go
cd build
make
```
## Usage
### Basic Example
```go
package main
import (
"fmt"
"github.com/Z3Prover/z3/src/api/go"
)
func main() {
// Create a context
ctx := z3.NewContext()
// Create variables
x := ctx.MkIntConst("x")
y := ctx.MkIntConst("y")
// Create constraints: x + y == 10 && x > y
ten := ctx.MkInt(10, ctx.MkIntSort())
eq := ctx.MkEq(ctx.MkAdd(x, y), ten)
gt := ctx.MkGt(x, y)
// Create solver and check
solver := ctx.NewSolver()
solver.Assert(eq)
solver.Assert(gt)
if solver.Check() == z3.Satisfiable {
model := solver.Model()
if xVal, ok := model.Eval(x, true); ok {
fmt.Println("x =", xVal.String())
}
if yVal, ok := model.Eval(y, true); ok {
fmt.Println("y =", yVal.String())
}
}
}
```
### Running Examples
```bash
cd examples/go
# Set library path (Linux/Mac)
export LD_LIBRARY_PATH=../../build:$LD_LIBRARY_PATH
export CGO_CFLAGS="-I../../src/api"
export CGO_LDFLAGS="-L../../build -lz3"
# Set library path (Windows)
set PATH=..\..\build;%PATH%
set CGO_CFLAGS=-I../../src/api
set CGO_LDFLAGS=-L../../build -lz3
# Run example
go run basic_example.go
```
## API Reference
### Context
- `NewContext()` - Create a new Z3 context
- `NewContextWithConfig(cfg *Config)` - Create context with configuration
- `SetParam(key, value string)` - Set context parameters
### Creating Expressions
- `MkBoolConst(name string)` - Create Boolean variable
- `MkIntConst(name string)` - Create integer variable
- `MkRealConst(name string)` - Create real variable
- `MkInt(value int, sort *Sort)` - Create integer constant
- `MkReal(num, den int)` - Create rational constant
### Boolean Operations
- `MkAnd(exprs ...*Expr)` - Conjunction
- `MkOr(exprs ...*Expr)` - Disjunction
- `MkNot(expr *Expr)` - Negation
- `MkImplies(lhs, rhs *Expr)` - Implication
- `MkIff(lhs, rhs *Expr)` - If-and-only-if
- `MkXor(lhs, rhs *Expr)` - Exclusive or
### Arithmetic Operations
- `MkAdd(exprs ...*Expr)` - Addition
- `MkSub(exprs ...*Expr)` - Subtraction
- `MkMul(exprs ...*Expr)` - Multiplication
- `MkDiv(lhs, rhs *Expr)` - Division
- `MkMod(lhs, rhs *Expr)` - Modulo
- `MkRem(lhs, rhs *Expr)` - Remainder
### Comparison Operations
- `MkEq(lhs, rhs *Expr)` - Equality
- `MkDistinct(exprs ...*Expr)` - Distinct
- `MkLt(lhs, rhs *Expr)` - Less than
- `MkLe(lhs, rhs *Expr)` - Less than or equal
- `MkGt(lhs, rhs *Expr)` - Greater than
- `MkGe(lhs, rhs *Expr)` - Greater than or equal
### Solver Operations
- `NewSolver()` - Create a new solver
- `Assert(constraint *Expr)` - Add constraint
- `Check()` - Check satisfiability (returns Satisfiable, Unsatisfiable, or Unknown)
- `Model()` - Get model (if satisfiable)
- `Push()` - Create backtracking point
- `Pop(n uint)` - Remove backtracking points
- `Reset()` - Remove all assertions
### Model Operations
- `Eval(expr *Expr, modelCompletion bool)` - Evaluate expression in model
- `NumConsts()` - Number of constants in model
- `NumFuncs()` - Number of functions in model
- `String()` - Get string representation
### Bit-vector Operations
- `MkBvSort(sz uint)` - Create bit-vector sort
- `MkBVConst(name string, size uint)` - Create bit-vector variable
- `MkBVAdd/Sub/Mul/UDiv/SDiv(lhs, rhs *Expr)` - Arithmetic operations
- `MkBVAnd/Or/Xor/Not(...)` - Bitwise operations
- `MkBVShl/LShr/AShr(lhs, rhs *Expr)` - Shift operations
- `MkBVULT/SLT/ULE/SLE/UGE/SGE/UGT/SGT(...)` - Comparisons
- `MkConcat(lhs, rhs *Expr)` - Bit-vector concatenation
- `MkExtract(high, low uint, expr *Expr)` - Extract bits
- `MkSignExt/ZeroExt(i uint, expr *Expr)` - Extend bit-vectors
### Floating-Point Operations
- `MkFPSort(ebits, sbits uint)` - Create floating-point sort
- `MkFPSort16/32/64/128()` - Standard IEEE 754 sorts
- `MkFPInf/NaN/Zero(sort *Sort, ...)` - Special values
- `MkFPAdd/Sub/Mul/Div(rm, lhs, rhs *Expr)` - Arithmetic with rounding
- `MkFPNeg/Abs/Sqrt(...)` - Unary operations
- `MkFPLT/GT/LE/GE/Eq(lhs, rhs *Expr)` - Comparisons
- `MkFPIsNaN/IsInf/IsZero(expr *Expr)` - Predicates
### Sequence/String Operations
- `MkStringSort()` - Create string sort
- `MkSeqSort(elemSort *Sort)` - Create sequence sort
- `MkString(value string)` - Create string constant
- `MkSeqConcat(exprs ...*Expr)` - Concatenation
- `MkSeqLength(seq *Expr)` - Length
- `MkSeqPrefix/Suffix/Contains(...)` - Predicates
- `MkSeqAt(seq, index *Expr)` - Element access
- `MkSeqExtract(seq, offset, length *Expr)` - Substring
- `MkStrToInt/IntToStr(...)` - Conversions
### Regular Expression Operations
- `MkReSort(seqSort *Sort)` - Create regex sort
- `MkToRe(seq *Expr)` - Convert string to regex
- `MkInRe(seq, re *Expr)` - String matches regex predicate
- `MkReStar(re *Expr)` - Kleene star (zero or more)
- `MkRePlus(re *Expr)` - Kleene plus (one or more)
- `MkReOption(re *Expr)` - Optional (zero or one)
- `MkRePower(re *Expr, n uint)` - Exactly n repetitions
- `MkReLoop(re *Expr, lo, hi uint)` - Bounded repetition
- `MkReConcat(regexes ...*Expr)` - Concatenation
- `MkReUnion(regexes ...*Expr)` - Alternation (OR)
- `MkReIntersect(regexes ...*Expr)` - Intersection
- `MkReComplement(re *Expr)` - Complement
- `MkReDiff(a, b *Expr)` - Difference
- `MkReEmpty/Full/Allchar(sort *Sort)` - Special regexes
- `MkReRange(lo, hi *Expr)` - Character range
- `MkSeqReplaceRe/ReAll(seq, re, replacement *Expr)` - Regex replace
### Datatype Operations
- `MkConstructor(name, recognizer string, ...)` - Create constructor
- `MkDatatypeSort(name string, constructors []*Constructor)` - Create datatype
- `MkDatatypeSorts(names []string, ...)` - Mutually recursive datatypes
- `MkTupleSort(name string, fieldNames []string, fieldSorts []*Sort)` - Tuples
- `MkEnumSort(name string, enumNames []string)` - Enumerations
- `MkListSort(name string, elemSort *Sort)` - Lists
### Tactic Operations
- `MkTactic(name string)` - Create tactic by name
- `MkGoal(models, unsatCores, proofs bool)` - Create goal
- `Apply(g *Goal)` - Apply tactic to goal
- `AndThen(t2 *Tactic)` - Sequential composition
- `OrElse(t2 *Tactic)` - Try first, fallback to second
- `Repeat(max uint)` - Repeat tactic
- `TacticWhen/Cond(...)` - Conditional tactics
### Probe Operations
- `MkProbe(name string)` - Create probe by name
- `Apply(g *Goal)` - Evaluate probe on goal
- `Lt/Gt/Le/Ge/Eq(p2 *Probe)` - Probe comparisons
- `And/Or/Not(...)` - Probe combinators
### Parameter Operations
- `MkParams()` - Create parameter set
- `SetBool/Uint/Double/Symbol(key string, value ...)` - Set parameters
### Optimize Operations
- `NewOptimize()` - Create optimization context
- `Assert(constraint *Expr)` - Add constraint
- `AssertSoft(constraint *Expr, weight, group string)` - Add soft constraint
- `Maximize(expr *Expr)` - Add maximization objective
- `Minimize(expr *Expr)` - Add minimization objective
- `Check(assumptions ...*Expr)` - Check and optimize
- `Model()` - Get optimal model
- `GetLower/Upper(index uint)` - Get objective bounds
- `Push/Pop()` - Backtracking
- `Assertions/Objectives()` - Get assertions and objectives
- `UnsatCore()` - Get unsat core
### Fixedpoint Operations (Datalog/CHC)
- `NewFixedpoint()` - Create fixedpoint solver
- `RegisterRelation(funcDecl *FuncDecl)` - Register predicate
- `AddRule(rule *Expr, name *Symbol)` - Add Horn clause
- `AddFact(pred *FuncDecl, args []int)` - Add table fact
- `Query(query *Expr)` - Query constraints
- `QueryRelations(relations []*FuncDecl)` - Query relations
- `GetAnswer()` - Get satisfying instance or proof
- `Push/Pop()` - Backtracking
### Quantifier Operations
- `MkQuantifier(isForall bool, weight int, sorts, names, body, patterns)` - Create quantifier
- `MkQuantifierConst(isForall bool, weight int, bound, body, patterns)` - Create with bound vars
- `IsUniversal/IsExistential()` - Check quantifier type
- `GetNumBound()` - Number of bound variables
- `GetBoundName/Sort(idx int)` - Get bound variable info
- `GetBody()` - Get quantifier body
- `GetNumPatterns()` - Number of patterns
- `GetPattern(idx int)` - Get pattern
### Lambda Operations
- `MkLambda(sorts, names, body)` - Create lambda expression
- `MkLambdaConst(bound, body)` - Create lambda with bound variables
- `GetNumBound()` - Number of bound variables
- `GetBoundName/Sort(idx int)` - Get bound variable info
- `GetBody()` - Get lambda body
### Type Variables
- `MkTypeVariable(name *Symbol)` - Create polymorphic type variable sort
### Logging
- `OpenLog(filename string)` - Open interaction log
- `CloseLog()` - Close log
- `AppendLog(s string)` - Append to log
- `IsLogOpen()` - Check if log is open
## Memory Management
The Go bindings use `runtime.SetFinalizer` to automatically manage Z3 reference counts. You don't need to manually call inc_ref/dec_ref. However, be aware that finalizers run during garbage collection, so resources may not be freed immediately.
## Thread Safety
Z3 contexts are not thread-safe. Each goroutine should use its own context, or use appropriate synchronization when sharing a context.
## License
Z3 is licensed under the MIT License. See LICENSE.txt in the repository root.
## Contributing
Bug reports and contributions are welcome! Please submit issues and pull requests to the main Z3 repository.
## References
- [Z3 GitHub Repository](https://github.com/Z3Prover/z3)
- [Z3 API Documentation](https://z3prover.github.io/api/html/index.html)
- [Z3 Guide](https://microsoft.github.io/z3guide/)

89
src/api/go/add_godoc.py Normal file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""
Add godoc comments to Z3 Go bindings systematically.
This script adds proper godoc documentation to all exported types and functions.
"""
import os
import re
# Godoc comment templates for common patterns
TYPE_COMMENTS = {
'Config': '// Config represents a Z3 configuration object used to customize solver behavior.\n// Create with NewConfig and configure using SetParamValue before creating a Context.',
'Context': '// Context represents a Z3 logical context.\n// All Z3 objects (sorts, expressions, solvers) are tied to the context that created them.\n// Contexts are not thread-safe - use separate contexts for concurrent operations.',
'Symbol': '// Symbol represents a Z3 symbol, which can be either a string or integer identifier.\n// Symbols are used to name sorts, constants, and functions.',
'AST': '// AST represents an Abstract Syntax Tree node in Z3.\n// This is the base type for all Z3 expressions, sorts, and function declarations.',
'Sort': '// Sort represents a type in Z3\'s type system.\n// Common sorts include Bool, Int, Real, BitVec, Array, and user-defined datatypes.',
'Expr': '// Expr represents a Z3 expression (term).\n// Expressions are typed AST nodes that can be evaluated, simplified, or used in constraints.',
'FuncDecl': '// FuncDecl represents a function declaration in Z3.\n// Function declarations define the signature (domain and range sorts) of functions.',
'Pattern': '// Pattern represents a pattern for quantifier instantiation.\n// Patterns guide Z3\'s E-matching algorithm for quantifier instantiation.',
'Quantifier': '// Quantifier represents a quantified formula (forall or exists).\n// Quantifiers bind variables and include optional patterns for instantiation.',
'Lambda': '// Lambda represents a lambda expression (anonymous function).\n// Lambda expressions can be used as array values or in higher-order reasoning.',
'Statistics': '// Statistics holds performance and diagnostic information from Z3 solvers.\n// Use GetKey, GetUintValue, and GetDoubleValue to access individual statistics.',
}
FUNCTION_COMMENTS = {
'NewConfig': '// NewConfig creates a new Z3 configuration object.\n// Use SetParamValue to configure parameters before creating a context.',
'NewContext': '// NewContext creates a new Z3 context with default configuration.\n// The context manages memory for all Z3 objects and must outlive any objects it creates.',
'NewContextWithConfig': '// NewContextWithConfig creates a new Z3 context with the given configuration.\n// The configuration is consumed and should not be reused.',
}
def add_godoc_comment(content, pattern, comment):
"""Add godoc comment before a type or function declaration."""
# Check if comment already exists
lines = content.split('\n')
result = []
i = 0
while i < len(lines):
line = lines[i]
# Check if this line matches our pattern
if re.match(pattern, line):
# Check if previous line is already a comment
if i > 0 and (result[-1].strip().startswith('//') or result[-1].strip().startswith('/*')):
# Comment exists, skip
result.append(line)
else:
# Add comment
result.append(comment)
result.append(line)
else:
result.append(line)
i += 1
return '\n'.join(result)
def process_file(filepath, type_comments, func_comments):
"""Process a single Go file and add godoc comments."""
print(f"Processing {filepath}...")
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# Add type comments
for type_name, comment in type_comments.items():
pattern = f'^type {type_name} struct'
content = add_godoc_comment(content, pattern, comment)
# Add function comments
for func_name, comment in func_comments.items():
pattern = f'^func (\\([^)]+\\) )?{func_name}\\('
content = add_godoc_comment(content, pattern, comment)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
print(f"Updated {filepath}")
if __name__ == '__main__':
go_api_dir = os.path.dirname(os.path.abspath(__file__))
# Process z3.go with core types
z3_go = os.path.join(go_api_dir, 'z3.go')
if os.path.exists(z3_go):
process_file(z3_go, TYPE_COMMENTS, FUNCTION_COMMENTS)
print("\nGodoc comments added successfully!")
print("Run 'go doc' to verify documentation.")

126
src/api/go/arith.go Normal file
View file

@ -0,0 +1,126 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
// Arithmetic operations and sorts
// MkIntSort creates the integer sort.
func (c *Context) MkIntSort() *Sort {
return newSort(c, C.Z3_mk_int_sort(c.ptr))
}
// MkRealSort creates the real number sort.
func (c *Context) MkRealSort() *Sort {
return newSort(c, C.Z3_mk_real_sort(c.ptr))
}
// MkInt creates an integer constant from an int.
func (c *Context) MkInt(value int, sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_int(c.ptr, C.int(value), sort.ptr))
}
// MkInt64 creates an integer constant from an int64.
func (c *Context) MkInt64(value int64, sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_int64(c.ptr, C.int64_t(value), sort.ptr))
}
// MkReal creates a real constant from numerator and denominator.
func (c *Context) MkReal(num, den int) *Expr {
return newExpr(c, C.Z3_mk_real(c.ptr, C.int(num), C.int(den)))
}
// MkIntConst creates an integer constant (variable) with the given name.
func (c *Context) MkIntConst(name string) *Expr {
sym := c.MkStringSymbol(name)
return c.MkConst(sym, c.MkIntSort())
}
// MkRealConst creates a real constant (variable) with the given name.
func (c *Context) MkRealConst(name string) *Expr {
sym := c.MkStringSymbol(name)
return c.MkConst(sym, c.MkRealSort())
}
// MkAdd creates an addition.
func (c *Context) MkAdd(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkInt(0, c.MkIntSort())
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_add(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkSub creates a subtraction.
func (c *Context) MkSub(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkInt(0, c.MkIntSort())
}
if len(exprs) == 1 {
return newExpr(c, C.Z3_mk_unary_minus(c.ptr, exprs[0].ptr))
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_sub(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkMul creates a multiplication.
func (c *Context) MkMul(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkInt(1, c.MkIntSort())
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_mul(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkDiv creates a division.
func (c *Context) MkDiv(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_div(c.ptr, lhs.ptr, rhs.ptr))
}
// MkMod creates a modulo operation.
func (c *Context) MkMod(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_mod(c.ptr, lhs.ptr, rhs.ptr))
}
// MkRem creates a remainder operation.
func (c *Context) MkRem(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_rem(c.ptr, lhs.ptr, rhs.ptr))
}
// MkLt creates a less-than constraint.
func (c *Context) MkLt(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_lt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkLe creates a less-than-or-equal constraint.
func (c *Context) MkLe(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_le(c.ptr, lhs.ptr, rhs.ptr))
}
// MkGt creates a greater-than constraint.
func (c *Context) MkGt(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_gt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkGe creates a greater-than-or-equal constraint.
func (c *Context) MkGe(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_ge(c.ptr, lhs.ptr, rhs.ptr))
}

29
src/api/go/array.go Normal file
View file

@ -0,0 +1,29 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
// Array operations and sorts
// MkArraySort creates an array sort.
func (c *Context) MkArraySort(domain, range_ *Sort) *Sort {
return newSort(c, C.Z3_mk_array_sort(c.ptr, domain.ptr, range_.ptr))
}
// MkSelect creates an array read (select) operation.
func (c *Context) MkSelect(array, index *Expr) *Expr {
return newExpr(c, C.Z3_mk_select(c.ptr, array.ptr, index.ptr))
}
// MkStore creates an array write (store) operation.
func (c *Context) MkStore(array, index, value *Expr) *Expr {
return newExpr(c, C.Z3_mk_store(c.ptr, array.ptr, index.ptr, value.ptr))
}
// MkConstArray creates a constant array.
func (c *Context) MkConstArray(sort *Sort, value *Expr) *Expr {
return newExpr(c, C.Z3_mk_const_array(c.ptr, sort.ptr, value.ptr))
}

160
src/api/go/bitvec.go Normal file
View file

@ -0,0 +1,160 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
// Bit-vector operations
// MkBVConst creates a bit-vector constant with the given name and size.
func (c *Context) MkBVConst(name string, size uint) *Expr {
sym := c.MkStringSymbol(name)
return c.MkConst(sym, c.MkBvSort(size))
}
// MkBV creates a bit-vector numeral from an integer.
func (c *Context) MkBV(value int, size uint) *Expr {
return newExpr(c, C.Z3_mk_int(c.ptr, C.int(value), c.MkBvSort(size).ptr))
}
// MkBVFromInt64 creates a bit-vector from an int64.
func (c *Context) MkBVFromInt64(value int64, size uint) *Expr {
return newExpr(c, C.Z3_mk_int64(c.ptr, C.int64_t(value), c.MkBvSort(size).ptr))
}
// MkBVAdd creates a bit-vector addition.
func (c *Context) MkBVAdd(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvadd(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSub creates a bit-vector subtraction.
func (c *Context) MkBVSub(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsub(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVMul creates a bit-vector multiplication.
func (c *Context) MkBVMul(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvmul(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVUDiv creates an unsigned bit-vector division.
func (c *Context) MkBVUDiv(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvudiv(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSDiv creates a signed bit-vector division.
func (c *Context) MkBVSDiv(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsdiv(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVURem creates an unsigned bit-vector remainder.
func (c *Context) MkBVURem(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvurem(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSRem creates a signed bit-vector remainder.
func (c *Context) MkBVSRem(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsrem(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVNeg creates a bit-vector negation.
func (c *Context) MkBVNeg(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvneg(c.ptr, expr.ptr))
}
// MkBVAnd creates a bit-vector bitwise AND.
func (c *Context) MkBVAnd(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvand(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVOr creates a bit-vector bitwise OR.
func (c *Context) MkBVOr(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvor(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVXor creates a bit-vector bitwise XOR.
func (c *Context) MkBVXor(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvxor(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVNot creates a bit-vector bitwise NOT.
func (c *Context) MkBVNot(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvnot(c.ptr, expr.ptr))
}
// MkBVShl creates a bit-vector shift left.
func (c *Context) MkBVShl(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvshl(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVLShr creates a bit-vector logical shift right.
func (c *Context) MkBVLShr(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvlshr(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVAShr creates a bit-vector arithmetic shift right.
func (c *Context) MkBVAShr(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvashr(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVULT creates an unsigned bit-vector less-than.
func (c *Context) MkBVULT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvult(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSLT creates a signed bit-vector less-than.
func (c *Context) MkBVSLT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvslt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVULE creates an unsigned bit-vector less-than-or-equal.
func (c *Context) MkBVULE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvule(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSLE creates a signed bit-vector less-than-or-equal.
func (c *Context) MkBVSLE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsle(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVUGE creates an unsigned bit-vector greater-than-or-equal.
func (c *Context) MkBVUGE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvuge(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSGE creates a signed bit-vector greater-than-or-equal.
func (c *Context) MkBVSGE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsge(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVUGT creates an unsigned bit-vector greater-than.
func (c *Context) MkBVUGT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvugt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkBVSGT creates a signed bit-vector greater-than.
func (c *Context) MkBVSGT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_bvsgt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkConcat creates a bit-vector concatenation.
func (c *Context) MkConcat(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_concat(c.ptr, lhs.ptr, rhs.ptr))
}
// MkExtract creates a bit-vector extraction.
func (c *Context) MkExtract(high, low uint, expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_extract(c.ptr, C.uint(high), C.uint(low), expr.ptr))
}
// MkSignExt creates a bit-vector sign extension.
func (c *Context) MkSignExt(i uint, expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_sign_ext(c.ptr, C.uint(i), expr.ptr))
}
// MkZeroExt creates a bit-vector zero extension.
func (c *Context) MkZeroExt(i uint, expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_zero_ext(c.ptr, C.uint(i), expr.ptr))
}

295
src/api/go/datatype.go Normal file
View file

@ -0,0 +1,295 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Constructor represents a datatype constructor.
type Constructor struct {
ctx *Context
ptr C.Z3_constructor
}
// newConstructor creates a new Constructor and manages its reference count.
func newConstructor(ctx *Context, ptr C.Z3_constructor) *Constructor {
c := &Constructor{ctx: ctx, ptr: ptr}
// Note: Z3_constructor doesn't use inc_ref/dec_ref pattern
// It uses Z3_del_constructor for cleanup
runtime.SetFinalizer(c, func(cons *Constructor) {
C.Z3_del_constructor(cons.ctx.ptr, cons.ptr)
})
return c
}
// MkConstructor creates a constructor for a datatype.
// name is the constructor name, recognizer is the recognizer name,
// fieldNames are the names of the fields, and fieldSorts are the sorts of the fields.
// fieldSortRefs can be 0 for non-recursive fields or the datatype index for recursive fields.
func (c *Context) MkConstructor(name, recognizer string, fieldNames []string, fieldSorts []*Sort, fieldSortRefs []uint) *Constructor {
cName := C.CString(name)
cRecognizer := C.CString(recognizer)
defer C.free(unsafe.Pointer(cName))
defer C.free(unsafe.Pointer(cRecognizer))
numFields := uint(len(fieldNames))
if numFields != uint(len(fieldSorts)) || numFields != uint(len(fieldSortRefs)) {
panic("fieldNames, fieldSorts, and fieldSortRefs must have the same length")
}
var cFieldNames *C.Z3_symbol
var cSorts *C.Z3_sort
var cSortRefs *C.uint
if numFields > 0 {
fieldSyms := make([]C.Z3_symbol, numFields)
for i, fname := range fieldNames {
fieldSyms[i] = c.MkStringSymbol(fname).ptr
}
cFieldNames = &fieldSyms[0]
sorts := make([]C.Z3_sort, numFields)
for i, s := range fieldSorts {
if s != nil {
sorts[i] = s.ptr
}
}
cSorts = &sorts[0]
refs := make([]C.uint, numFields)
for i, r := range fieldSortRefs {
refs[i] = C.uint(r)
}
cSortRefs = &refs[0]
}
sym := c.MkStringSymbol(name)
recSym := c.MkStringSymbol(recognizer)
return newConstructor(c, C.Z3_mk_constructor(
c.ptr,
sym.ptr,
recSym.ptr,
C.uint(numFields),
cFieldNames,
cSorts,
cSortRefs,
))
}
// ConstructorList represents a list of datatype constructors.
type ConstructorList struct {
ctx *Context
ptr C.Z3_constructor_list
}
// newConstructorList creates a new ConstructorList and manages its reference count.
func newConstructorList(ctx *Context, ptr C.Z3_constructor_list) *ConstructorList {
cl := &ConstructorList{ctx: ctx, ptr: ptr}
// Note: Z3_constructor_list doesn't use inc_ref/dec_ref pattern
// It uses Z3_del_constructor_list for cleanup
runtime.SetFinalizer(cl, func(list *ConstructorList) {
C.Z3_del_constructor_list(list.ctx.ptr, list.ptr)
})
return cl
}
// MkConstructorList creates a list of constructors for a datatype.
func (c *Context) MkConstructorList(constructors []*Constructor) *ConstructorList {
numCons := uint(len(constructors))
if numCons == 0 {
return nil
}
cons := make([]C.Z3_constructor, numCons)
for i, constr := range constructors {
cons[i] = constr.ptr
}
return newConstructorList(c, C.Z3_mk_constructor_list(c.ptr, C.uint(numCons), &cons[0]))
}
// MkDatatypeSort creates a datatype sort from a constructor list.
func (c *Context) MkDatatypeSort(name string, constructors []*Constructor) *Sort {
sym := c.MkStringSymbol(name)
numCons := uint(len(constructors))
cons := make([]C.Z3_constructor, numCons)
for i, constr := range constructors {
cons[i] = constr.ptr
}
return newSort(c, C.Z3_mk_datatype(c.ptr, sym.ptr, C.uint(numCons), &cons[0]))
}
// MkDatatypeSorts creates multiple mutually recursive datatype sorts.
func (c *Context) MkDatatypeSorts(names []string, constructorLists [][]*Constructor) []*Sort {
numTypes := uint(len(names))
if numTypes != uint(len(constructorLists)) {
panic("names and constructorLists must have the same length")
}
syms := make([]C.Z3_symbol, numTypes)
for i, name := range names {
syms[i] = c.MkStringSymbol(name).ptr
}
cLists := make([]C.Z3_constructor_list, numTypes)
for i, constrs := range constructorLists {
cons := make([]C.Z3_constructor, len(constrs))
for j, constr := range constrs {
cons[j] = constr.ptr
}
cLists[i] = C.Z3_mk_constructor_list(c.ptr, C.uint(len(constrs)), &cons[0])
}
resultSorts := make([]C.Z3_sort, numTypes)
C.Z3_mk_datatypes(c.ptr, C.uint(numTypes), &syms[0], &resultSorts[0], &cLists[0])
// Clean up constructor lists
for i := range cLists {
C.Z3_del_constructor_list(c.ptr, cLists[i])
}
sorts := make([]*Sort, numTypes)
for i := range resultSorts {
sorts[i] = newSort(c, resultSorts[i])
}
return sorts
}
// GetDatatypeSortConstructor returns the i-th constructor of a datatype sort.
func (c *Context) GetDatatypeSortConstructor(sort *Sort, i uint) *FuncDecl {
return newFuncDecl(c, C.Z3_get_datatype_sort_constructor(c.ptr, sort.ptr, C.uint(i)))
}
// GetDatatypeSortRecognizer returns the i-th recognizer of a datatype sort.
func (c *Context) GetDatatypeSortRecognizer(sort *Sort, i uint) *FuncDecl {
return newFuncDecl(c, C.Z3_get_datatype_sort_recognizer(c.ptr, sort.ptr, C.uint(i)))
}
// GetDatatypeSortConstructorAccessor returns the accessor for the i-th field of the j-th constructor.
func (c *Context) GetDatatypeSortConstructorAccessor(sort *Sort, constructorIdx, accessorIdx uint) *FuncDecl {
return newFuncDecl(c, C.Z3_get_datatype_sort_constructor_accessor(
c.ptr, sort.ptr, C.uint(constructorIdx), C.uint(accessorIdx)))
}
// GetDatatypeSortNumConstructors returns the number of constructors in a datatype sort.
func (c *Context) GetDatatypeSortNumConstructors(sort *Sort) uint {
return uint(C.Z3_get_datatype_sort_num_constructors(c.ptr, sort.ptr))
}
// Tuple sorts (special case of datatypes)
// MkTupleSort creates a tuple sort with the given field sorts.
func (c *Context) MkTupleSort(name string, fieldNames []string, fieldSorts []*Sort) (*Sort, *FuncDecl, []*FuncDecl) {
sym := c.MkStringSymbol(name)
numFields := uint(len(fieldNames))
if numFields != uint(len(fieldSorts)) {
panic("fieldNames and fieldSorts must have the same length")
}
fieldSyms := make([]C.Z3_symbol, numFields)
for i, fname := range fieldNames {
fieldSyms[i] = c.MkStringSymbol(fname).ptr
}
sorts := make([]C.Z3_sort, numFields)
for i, s := range fieldSorts {
sorts[i] = s.ptr
}
var mkTupleDecl C.Z3_func_decl
projDecls := make([]C.Z3_func_decl, numFields)
tupleSort := C.Z3_mk_tuple_sort(
c.ptr,
sym.ptr,
C.uint(numFields),
&fieldSyms[0],
&sorts[0],
&mkTupleDecl,
&projDecls[0],
)
projections := make([]*FuncDecl, numFields)
for i := range projDecls {
projections[i] = newFuncDecl(c, projDecls[i])
}
return newSort(c, tupleSort), newFuncDecl(c, mkTupleDecl), projections
}
// Enumeration sorts (special case of datatypes)
// MkEnumSort creates an enumeration sort with the given constants.
func (c *Context) MkEnumSort(name string, enumNames []string) (*Sort, []*FuncDecl, []*FuncDecl) {
sym := c.MkStringSymbol(name)
numEnums := uint(len(enumNames))
enumSyms := make([]C.Z3_symbol, numEnums)
for i, ename := range enumNames {
enumSyms[i] = c.MkStringSymbol(ename).ptr
}
enumConsts := make([]C.Z3_func_decl, numEnums)
enumTesters := make([]C.Z3_func_decl, numEnums)
enumSort := C.Z3_mk_enumeration_sort(
c.ptr,
sym.ptr,
C.uint(numEnums),
&enumSyms[0],
&enumConsts[0],
&enumTesters[0],
)
consts := make([]*FuncDecl, numEnums)
for i := range enumConsts {
consts[i] = newFuncDecl(c, enumConsts[i])
}
testers := make([]*FuncDecl, numEnums)
for i := range enumTesters {
testers[i] = newFuncDecl(c, enumTesters[i])
}
return newSort(c, enumSort), consts, testers
}
// List sorts (special case of datatypes)
// MkListSort creates a list sort with the given element sort.
func (c *Context) MkListSort(name string, elemSort *Sort) (*Sort, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl) {
sym := c.MkStringSymbol(name)
var nilDecl, consDecl, isNilDecl, isConsDecl, headDecl, tailDecl C.Z3_func_decl
listSort := C.Z3_mk_list_sort(
c.ptr,
sym.ptr,
elemSort.ptr,
&nilDecl,
&isNilDecl,
&consDecl,
&isConsDecl,
&headDecl,
&tailDecl,
)
return newSort(c, listSort),
newFuncDecl(c, nilDecl),
newFuncDecl(c, consDecl),
newFuncDecl(c, isNilDecl),
newFuncDecl(c, isConsDecl),
newFuncDecl(c, headDecl),
newFuncDecl(c, tailDecl)
}

272
src/api/go/fixedpoint.go Normal file
View file

@ -0,0 +1,272 @@
// Copyright (c) Microsoft Corporation 2025
// Z3 Go API: Fixedpoint solver for Datalog and CHC (Constrained Horn Clauses)
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Fixedpoint represents a fixedpoint solver for Datalog/CHC queries
type Fixedpoint struct {
ctx *Context
ptr C.Z3_fixedpoint
}
// newFixedpoint creates a new Fixedpoint solver with proper memory management
func newFixedpoint(ctx *Context, ptr C.Z3_fixedpoint) *Fixedpoint {
fp := &Fixedpoint{ctx: ctx, ptr: ptr}
C.Z3_fixedpoint_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(fp, func(f *Fixedpoint) {
C.Z3_fixedpoint_dec_ref(f.ctx.ptr, f.ptr)
})
return fp
}
// NewFixedpoint creates a new fixedpoint solver
func (ctx *Context) NewFixedpoint() *Fixedpoint {
ptr := C.Z3_mk_fixedpoint(ctx.ptr)
return newFixedpoint(ctx, ptr)
}
// GetHelp returns a string describing all available fixedpoint solver parameters
func (f *Fixedpoint) GetHelp() string {
cstr := C.Z3_fixedpoint_get_help(f.ctx.ptr, f.ptr)
return C.GoString(cstr)
}
// SetParams sets the fixedpoint solver parameters
func (f *Fixedpoint) SetParams(params *Params) {
C.Z3_fixedpoint_set_params(f.ctx.ptr, f.ptr, params.ptr)
}
// GetParamDescrs retrieves parameter descriptions for the fixedpoint solver
func (f *Fixedpoint) GetParamDescrs() *ParamDescrs {
ptr := C.Z3_fixedpoint_get_param_descrs(f.ctx.ptr, f.ptr)
return newParamDescrs(f.ctx, ptr)
}
// Assert adds a constraint into the fixedpoint solver
func (f *Fixedpoint) Assert(constraint *Expr) {
C.Z3_fixedpoint_assert(f.ctx.ptr, f.ptr, constraint.ptr)
}
// RegisterRelation registers a predicate as a recursive relation
func (f *Fixedpoint) RegisterRelation(funcDecl *FuncDecl) {
C.Z3_fixedpoint_register_relation(f.ctx.ptr, f.ptr, funcDecl.ptr)
}
// AddRule adds a rule (Horn clause) to the fixedpoint solver
// The rule should be an implication of the form body => head
// where head is a relation and body is a conjunction of relations
func (f *Fixedpoint) AddRule(rule *Expr, name *Symbol) {
var namePtr C.Z3_symbol
if name != nil {
namePtr = name.ptr
} else {
namePtr = nil
}
C.Z3_fixedpoint_add_rule(f.ctx.ptr, f.ptr, rule.ptr, namePtr)
}
// AddFact adds a table fact to the fixedpoint solver
func (f *Fixedpoint) AddFact(pred *FuncDecl, args []int) {
if len(args) == 0 {
C.Z3_fixedpoint_add_fact(f.ctx.ptr, f.ptr, pred.ptr, 0, nil)
return
}
cArgs := make([]C.uint, len(args))
for i, arg := range args {
cArgs[i] = C.uint(arg)
}
C.Z3_fixedpoint_add_fact(f.ctx.ptr, f.ptr, pred.ptr, C.uint(len(args)), &cArgs[0])
}
// Query queries the fixedpoint solver with a constraint
// Returns Satisfiable if there is a derivation, Unsatisfiable if not
func (f *Fixedpoint) Query(query *Expr) Status {
result := C.Z3_fixedpoint_query(f.ctx.ptr, f.ptr, query.ptr)
switch result {
case C.Z3_L_TRUE:
return Satisfiable
case C.Z3_L_FALSE:
return Unsatisfiable
default:
return Unknown
}
}
// QueryRelations queries the fixedpoint solver with an array of relations
// Returns Satisfiable if any relation is non-empty, Unsatisfiable otherwise
func (f *Fixedpoint) QueryRelations(relations []*FuncDecl) Status {
if len(relations) == 0 {
return Unknown
}
cRelations := make([]C.Z3_func_decl, len(relations))
for i, rel := range relations {
cRelations[i] = rel.ptr
}
result := C.Z3_fixedpoint_query_relations(f.ctx.ptr, f.ptr, C.uint(len(relations)), &cRelations[0])
switch result {
case C.Z3_L_TRUE:
return Satisfiable
case C.Z3_L_FALSE:
return Unsatisfiable
default:
return Unknown
}
}
// UpdateRule updates a named rule in the fixedpoint solver
func (f *Fixedpoint) UpdateRule(rule *Expr, name *Symbol) {
var namePtr C.Z3_symbol
if name != nil {
namePtr = name.ptr
} else {
namePtr = nil
}
C.Z3_fixedpoint_update_rule(f.ctx.ptr, f.ptr, rule.ptr, namePtr)
}
// GetAnswer retrieves the satisfying instance or instances of solver,
// or definitions for the recursive predicates that show unsatisfiability
func (f *Fixedpoint) GetAnswer() *Expr {
ptr := C.Z3_fixedpoint_get_answer(f.ctx.ptr, f.ptr)
if ptr == nil {
return nil
}
return newExpr(f.ctx, ptr)
}
// GetReasonUnknown retrieves explanation why fixedpoint engine returned status Unknown
func (f *Fixedpoint) GetReasonUnknown() string {
cstr := C.Z3_fixedpoint_get_reason_unknown(f.ctx.ptr, f.ptr)
return C.GoString(cstr)
}
// GetNumLevels retrieves the number of levels explored for a given predicate
func (f *Fixedpoint) GetNumLevels(predicate *FuncDecl) int {
return int(C.Z3_fixedpoint_get_num_levels(f.ctx.ptr, f.ptr, predicate.ptr))
}
// GetCoverDelta retrieves the cover delta for a given predicate and level
func (f *Fixedpoint) GetCoverDelta(level int, predicate *FuncDecl) *Expr {
ptr := C.Z3_fixedpoint_get_cover_delta(f.ctx.ptr, f.ptr, C.int(level), predicate.ptr)
if ptr == nil {
return nil
}
return newExpr(f.ctx, ptr)
}
// AddCover adds a cover constraint to a predicate at a given level
func (f *Fixedpoint) AddCover(level int, predicate *FuncDecl, property *Expr) {
C.Z3_fixedpoint_add_cover(f.ctx.ptr, f.ptr, C.int(level), predicate.ptr, property.ptr)
}
// String returns the string representation of the fixedpoint solver
func (f *Fixedpoint) String() string {
cstr := C.Z3_fixedpoint_to_string(f.ctx.ptr, f.ptr, 0, nil)
return C.GoString(cstr)
}
// GetStatistics retrieves statistics for the fixedpoint solver
func (f *Fixedpoint) GetStatistics() *Statistics {
ptr := C.Z3_fixedpoint_get_statistics(f.ctx.ptr, f.ptr)
return newStatistics(f.ctx, ptr)
}
// GetRules retrieves the current rules as a string
func (f *Fixedpoint) GetRules() string {
return f.String()
}
// GetAssertions retrieves the fixedpoint assertions as an AST vector
func (f *Fixedpoint) GetAssertions() *ASTVector {
ptr := C.Z3_fixedpoint_get_assertions(f.ctx.ptr, f.ptr)
return newASTVector(f.ctx, ptr)
}
// SetPredicateRepresentation sets the predicate representation for a given relation
func (f *Fixedpoint) SetPredicateRepresentation(funcDecl *FuncDecl, kinds []C.Z3_symbol) {
if len(kinds) == 0 {
C.Z3_fixedpoint_set_predicate_representation(f.ctx.ptr, f.ptr, funcDecl.ptr, 0, nil)
return
}
C.Z3_fixedpoint_set_predicate_representation(f.ctx.ptr, f.ptr, funcDecl.ptr, C.uint(len(kinds)), &kinds[0])
}
// FromString parses a Datalog program from a string
func (f *Fixedpoint) FromString(s string) {
cstr := C.CString(s)
defer C.free(unsafe.Pointer(cstr))
C.Z3_fixedpoint_from_string(f.ctx.ptr, f.ptr, cstr)
}
// FromFile parses a Datalog program from a file
func (f *Fixedpoint) FromFile(filename string) {
cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr))
C.Z3_fixedpoint_from_file(f.ctx.ptr, f.ptr, cstr)
}
// Statistics represents statistics for Z3 solvers
type Statistics struct {
ctx *Context
ptr C.Z3_stats
}
// newStatistics creates a new Statistics object with proper memory management
func newStatistics(ctx *Context, ptr C.Z3_stats) *Statistics {
stats := &Statistics{ctx: ctx, ptr: ptr}
C.Z3_stats_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(stats, func(s *Statistics) {
C.Z3_stats_dec_ref(s.ctx.ptr, s.ptr)
})
return stats
}
// String returns the string representation of statistics
func (s *Statistics) String() string {
cstr := C.Z3_stats_to_string(s.ctx.ptr, s.ptr)
return C.GoString(cstr)
}
// Size returns the number of statistical data entries
func (s *Statistics) Size() int {
return int(C.Z3_stats_size(s.ctx.ptr, s.ptr))
}
// GetKey returns the key (name) of a statistical data entry at the given index
func (s *Statistics) GetKey(idx int) string {
cstr := C.Z3_stats_get_key(s.ctx.ptr, s.ptr, C.uint(idx))
return C.GoString(cstr)
}
// IsUint returns true if the statistical data at the given index is unsigned integer
func (s *Statistics) IsUint(idx int) bool {
return bool(C.Z3_stats_is_uint(s.ctx.ptr, s.ptr, C.uint(idx)))
}
// IsDouble returns true if the statistical data at the given index is double
func (s *Statistics) IsDouble(idx int) bool {
return bool(C.Z3_stats_is_double(s.ctx.ptr, s.ptr, C.uint(idx)))
}
// GetUintValue returns the unsigned integer value at the given index
func (s *Statistics) GetUintValue(idx int) uint64 {
return uint64(C.Z3_stats_get_uint_value(s.ctx.ptr, s.ptr, C.uint(idx)))
}
// GetDoubleValue returns the double value at the given index
func (s *Statistics) GetDoubleValue(idx int) float64 {
return float64(C.Z3_stats_get_double_value(s.ctx.ptr, s.ptr, C.uint(idx)))
}

139
src/api/go/fp.go Normal file
View file

@ -0,0 +1,139 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
// Floating-point operations
// MkFPSort creates a floating-point sort.
func (c *Context) MkFPSort(ebits, sbits uint) *Sort {
return newSort(c, C.Z3_mk_fpa_sort(c.ptr, C.uint(ebits), C.uint(sbits)))
}
// MkFPSort16 creates a 16-bit floating-point sort.
func (c *Context) MkFPSort16() *Sort {
return newSort(c, C.Z3_mk_fpa_sort_16(c.ptr))
}
// MkFPSort32 creates a 32-bit floating-point sort (single precision).
func (c *Context) MkFPSort32() *Sort {
return newSort(c, C.Z3_mk_fpa_sort_32(c.ptr))
}
// MkFPSort64 creates a 64-bit floating-point sort (double precision).
func (c *Context) MkFPSort64() *Sort {
return newSort(c, C.Z3_mk_fpa_sort_64(c.ptr))
}
// MkFPSort128 creates a 128-bit floating-point sort (quadruple precision).
func (c *Context) MkFPSort128() *Sort {
return newSort(c, C.Z3_mk_fpa_sort_128(c.ptr))
}
// MkFPRoundingModeSort creates the rounding mode sort.
func (c *Context) MkFPRoundingModeSort() *Sort {
return newSort(c, C.Z3_mk_fpa_rounding_mode_sort(c.ptr))
}
// MkFPNumeral creates a floating-point numeral from a string.
func (c *Context) MkFPNumeral(value string, sort *Sort) *Expr {
cStr := C.CString(value)
defer C.free(unsafe.Pointer(cStr))
return newExpr(c, C.Z3_mk_numeral(c.ptr, cStr, sort.ptr))
}
// MkFPInf creates a floating-point infinity.
func (c *Context) MkFPInf(sort *Sort, negative bool) *Expr {
return newExpr(c, C.Z3_mk_fpa_inf(c.ptr, sort.ptr, C.bool(negative)))
}
// MkFPNaN creates a floating-point NaN.
func (c *Context) MkFPNaN(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_fpa_nan(c.ptr, sort.ptr))
}
// MkFPZero creates a floating-point zero.
func (c *Context) MkFPZero(sort *Sort, negative bool) *Expr {
return newExpr(c, C.Z3_mk_fpa_zero(c.ptr, sort.ptr, C.bool(negative)))
}
// MkFPAdd creates a floating-point addition.
func (c *Context) MkFPAdd(rm, lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_add(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
}
// MkFPSub creates a floating-point subtraction.
func (c *Context) MkFPSub(rm, lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_sub(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
}
// MkFPMul creates a floating-point multiplication.
func (c *Context) MkFPMul(rm, lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_mul(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
}
// MkFPDiv creates a floating-point division.
func (c *Context) MkFPDiv(rm, lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_div(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
}
// MkFPNeg creates a floating-point negation.
func (c *Context) MkFPNeg(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_neg(c.ptr, expr.ptr))
}
// MkFPAbs creates a floating-point absolute value.
func (c *Context) MkFPAbs(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_abs(c.ptr, expr.ptr))
}
// MkFPSqrt creates a floating-point square root.
func (c *Context) MkFPSqrt(rm, expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_sqrt(c.ptr, rm.ptr, expr.ptr))
}
// MkFPLT creates a floating-point less-than.
func (c *Context) MkFPLT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_lt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPGT creates a floating-point greater-than.
func (c *Context) MkFPGT(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_gt(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPLE creates a floating-point less-than-or-equal.
func (c *Context) MkFPLE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_leq(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPGE creates a floating-point greater-than-or-equal.
func (c *Context) MkFPGE(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_geq(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPEq creates a floating-point equality.
func (c *Context) MkFPEq(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_eq(c.ptr, lhs.ptr, rhs.ptr))
}
// MkFPIsNaN creates a predicate checking if a floating-point number is NaN.
func (c *Context) MkFPIsNaN(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_is_nan(c.ptr, expr.ptr))
}
// MkFPIsInf creates a predicate checking if a floating-point number is infinite.
func (c *Context) MkFPIsInf(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_is_infinite(c.ptr, expr.ptr))
}
// MkFPIsZero creates a predicate checking if a floating-point number is zero.
func (c *Context) MkFPIsZero(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_fpa_is_zero(c.ptr, expr.ptr))
}

6
src/api/go/go.mod Normal file
View file

@ -0,0 +1,6 @@
module github.com/Z3Prover/z3/src/api/go
go 1.20
// This package provides Go bindings for the Z3 theorem prover.
// It uses CGO to wrap the Z3 C API.

67
src/api/go/log.go Normal file
View file

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation 2025
// Z3 Go API: Logging functionality
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"sync"
"unsafe"
)
var (
logMutex sync.Mutex
isLogOpen bool
)
// OpenLog opens an interaction log file
// Returns true if successful, false otherwise
func OpenLog(filename string) bool {
logMutex.Lock()
defer logMutex.Unlock()
cFilename := C.CString(filename)
defer C.free(unsafe.Pointer(cFilename))
result := C.Z3_open_log(cFilename)
if bool(result) {
isLogOpen = true
return true
}
return false
}
// CloseLog closes the interaction log
func CloseLog() {
logMutex.Lock()
defer logMutex.Unlock()
C.Z3_close_log()
isLogOpen = false
}
// AppendLog appends a user-provided string to the interaction log
// Panics if the log is not open
func AppendLog(s string) {
logMutex.Lock()
defer logMutex.Unlock()
if !isLogOpen {
panic("Log is not open")
}
cStr := C.CString(s)
defer C.free(unsafe.Pointer(cStr))
C.Z3_append_log(cStr)
}
// IsLogOpen returns true if the interaction log is open
func IsLogOpen() bool {
logMutex.Lock()
defer logMutex.Unlock()
return isLogOpen
}

195
src/api/go/optimize.go Normal file
View file

@ -0,0 +1,195 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Optimize represents a Z3 optimization context for solving optimization problems.
// Unlike Solver which only checks satisfiability, Optimize can find optimal solutions
// with respect to objective functions.
type Optimize struct {
ctx *Context
ptr C.Z3_optimize
}
// NewOptimize creates a new optimization context.
func (c *Context) NewOptimize() *Optimize {
opt := &Optimize{
ctx: c,
ptr: C.Z3_mk_optimize(c.ptr),
}
C.Z3_optimize_inc_ref(c.ptr, opt.ptr)
runtime.SetFinalizer(opt, func(o *Optimize) {
C.Z3_optimize_dec_ref(o.ctx.ptr, o.ptr)
})
return opt
}
// String returns the string representation of the optimize context.
func (o *Optimize) String() string {
return C.GoString(C.Z3_optimize_to_string(o.ctx.ptr, o.ptr))
}
// Assert adds a constraint to the optimizer.
func (o *Optimize) Assert(constraint *Expr) {
C.Z3_optimize_assert(o.ctx.ptr, o.ptr, constraint.ptr)
}
// AssertAndTrack adds a constraint with a tracking literal for unsat core extraction.
func (o *Optimize) AssertAndTrack(constraint, track *Expr) {
C.Z3_optimize_assert_and_track(o.ctx.ptr, o.ptr, constraint.ptr, track.ptr)
}
// AssertSoft adds a soft constraint with a weight.
// Soft constraints are used for MaxSMT problems.
// Returns a handle to the objective.
func (o *Optimize) AssertSoft(constraint *Expr, weight string, group string) uint {
cWeight := C.CString(weight)
cGroup := C.CString(group)
defer C.free(unsafe.Pointer(cWeight))
defer C.free(unsafe.Pointer(cGroup))
sym := o.ctx.MkStringSymbol(group)
return uint(C.Z3_optimize_assert_soft(o.ctx.ptr, o.ptr, constraint.ptr, cWeight, sym.ptr))
}
// Maximize adds a maximization objective.
// Returns a handle to the objective that can be used to retrieve bounds.
func (o *Optimize) Maximize(expr *Expr) uint {
return uint(C.Z3_optimize_maximize(o.ctx.ptr, o.ptr, expr.ptr))
}
// Minimize adds a minimization objective.
// Returns a handle to the objective that can be used to retrieve bounds.
func (o *Optimize) Minimize(expr *Expr) uint {
return uint(C.Z3_optimize_minimize(o.ctx.ptr, o.ptr, expr.ptr))
}
// Check checks the satisfiability of the constraints and optimizes objectives.
func (o *Optimize) Check(assumptions ...*Expr) Status {
var result C.Z3_lbool
if len(assumptions) == 0 {
result = C.Z3_optimize_check(o.ctx.ptr, o.ptr, 0, nil)
} else {
cAssumptions := make([]C.Z3_ast, len(assumptions))
for i, a := range assumptions {
cAssumptions[i] = a.ptr
}
result = C.Z3_optimize_check(o.ctx.ptr, o.ptr, C.uint(len(assumptions)), &cAssumptions[0])
}
return Status(result)
}
// Model returns the model if the constraints are satisfiable.
func (o *Optimize) Model() *Model {
modelPtr := C.Z3_optimize_get_model(o.ctx.ptr, o.ptr)
if modelPtr == nil {
return nil
}
return newModel(o.ctx, modelPtr)
}
// Push creates a backtracking point.
func (o *Optimize) Push() {
C.Z3_optimize_push(o.ctx.ptr, o.ptr)
}
// Pop removes a backtracking point.
func (o *Optimize) Pop() {
C.Z3_optimize_pop(o.ctx.ptr, o.ptr)
}
// GetLower retrieves a lower bound for the objective at the given index.
func (o *Optimize) GetLower(index uint) *Expr {
result := C.Z3_optimize_get_lower(o.ctx.ptr, o.ptr, C.uint(index))
if result == nil {
return nil
}
return newExpr(o.ctx, result)
}
// GetUpper retrieves an upper bound for the objective at the given index.
func (o *Optimize) GetUpper(index uint) *Expr {
result := C.Z3_optimize_get_upper(o.ctx.ptr, o.ptr, C.uint(index))
if result == nil {
return nil
}
return newExpr(o.ctx, result)
}
// GetLowerAsVector retrieves a lower bound as a vector (inf, value, eps).
// The objective value is unbounded if inf is non-zero,
// otherwise it's represented as value + eps * EPSILON.
func (o *Optimize) GetLowerAsVector(index uint) []*Expr {
vec := C.Z3_optimize_get_lower_as_vector(o.ctx.ptr, o.ptr, C.uint(index))
result := astVectorToExprs(o.ctx, vec)
if len(result) != 3 {
return nil
}
return result
}
// GetUpperAsVector retrieves an upper bound as a vector (inf, value, eps).
// The objective value is unbounded if inf is non-zero,
// otherwise it's represented as value + eps * EPSILON.
func (o *Optimize) GetUpperAsVector(index uint) []*Expr {
vec := C.Z3_optimize_get_upper_as_vector(o.ctx.ptr, o.ptr, C.uint(index))
result := astVectorToExprs(o.ctx, vec)
if len(result) != 3 {
return nil
}
return result
}
// ReasonUnknown returns the reason why the result is unknown.
func (o *Optimize) ReasonUnknown() string {
return C.GoString(C.Z3_optimize_get_reason_unknown(o.ctx.ptr, o.ptr))
}
// GetHelp returns help information for the optimizer.
func (o *Optimize) GetHelp() string {
return C.GoString(C.Z3_optimize_get_help(o.ctx.ptr, o.ptr))
}
// SetParams sets parameters for the optimizer.
func (o *Optimize) SetParams(params *Params) {
C.Z3_optimize_set_params(o.ctx.ptr, o.ptr, params.ptr)
}
// Assertions returns the assertions in the optimizer.
func (o *Optimize) Assertions() []*Expr {
vec := C.Z3_optimize_get_assertions(o.ctx.ptr, o.ptr)
return astVectorToExprs(o.ctx, vec)
}
// Objectives returns the objectives in the optimizer.
func (o *Optimize) Objectives() []*Expr {
vec := C.Z3_optimize_get_objectives(o.ctx.ptr, o.ptr)
return astVectorToExprs(o.ctx, vec)
}
// UnsatCore returns the unsat core if the constraints are unsatisfiable.
func (o *Optimize) UnsatCore() []*Expr {
vec := C.Z3_optimize_get_unsat_core(o.ctx.ptr, o.ptr)
return astVectorToExprs(o.ctx, vec)
}
// FromFile parses an SMT-LIB2 file with optimization objectives and constraints.
func (o *Optimize) FromFile(filename string) {
cFilename := C.CString(filename)
defer C.free(unsafe.Pointer(cFilename))
C.Z3_optimize_from_file(o.ctx.ptr, o.ptr, cFilename)
}
// FromString parses an SMT-LIB2 string with optimization objectives and constraints.
func (o *Optimize) FromString(s string) {
cStr := C.CString(s)
defer C.free(unsafe.Pointer(cStr))
C.Z3_optimize_from_string(o.ctx.ptr, o.ptr, cStr)
}

232
src/api/go/seq.go Normal file
View file

@ -0,0 +1,232 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
// Sequence and string operations
// MkSeqSort creates a sequence sort.
func (c *Context) MkSeqSort(elemSort *Sort) *Sort {
return newSort(c, C.Z3_mk_seq_sort(c.ptr, elemSort.ptr))
}
// MkStringSort creates a string sort (sequence of characters).
func (c *Context) MkStringSort() *Sort {
return newSort(c, C.Z3_mk_string_sort(c.ptr))
}
// MkString creates a string constant.
func (c *Context) MkString(value string) *Expr {
cStr := C.CString(value)
defer C.free(unsafe.Pointer(cStr))
return newExpr(c, C.Z3_mk_string(c.ptr, cStr))
}
// MkEmptySeq creates an empty sequence.
func (c *Context) MkEmptySeq(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_seq_empty(c.ptr, sort.ptr))
}
// MkSeqUnit creates a unit sequence containing a single element.
func (c *Context) MkSeqUnit(elem *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_unit(c.ptr, elem.ptr))
}
// MkSeqConcat creates a sequence concatenation.
func (c *Context) MkSeqConcat(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return nil
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_seq_concat(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkSeqLength creates a sequence length operation.
func (c *Context) MkSeqLength(seq *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_length(c.ptr, seq.ptr))
}
// MkSeqPrefix creates a sequence prefix predicate.
func (c *Context) MkSeqPrefix(prefix, seq *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_prefix(c.ptr, prefix.ptr, seq.ptr))
}
// MkSeqSuffix creates a sequence suffix predicate.
func (c *Context) MkSeqSuffix(suffix, seq *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_suffix(c.ptr, suffix.ptr, seq.ptr))
}
// MkSeqContains creates a sequence contains predicate.
func (c *Context) MkSeqContains(seq, substr *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_contains(c.ptr, seq.ptr, substr.ptr))
}
// MkSeqAt creates a sequence element access operation.
func (c *Context) MkSeqAt(seq, index *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_at(c.ptr, seq.ptr, index.ptr))
}
// MkSeqExtract creates a sequence extract (substring) operation.
func (c *Context) MkSeqExtract(seq, offset, length *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_extract(c.ptr, seq.ptr, offset.ptr, length.ptr))
}
// MkSeqReplace creates a sequence replace operation.
func (c *Context) MkSeqReplace(seq, src, dst *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_replace(c.ptr, seq.ptr, src.ptr, dst.ptr))
}
// MkSeqIndexOf creates a sequence index-of operation.
func (c *Context) MkSeqIndexOf(seq, substr, offset *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_index(c.ptr, seq.ptr, substr.ptr, offset.ptr))
}
// MkStrToInt creates a string-to-integer conversion.
func (c *Context) MkStrToInt(str *Expr) *Expr {
return newExpr(c, C.Z3_mk_str_to_int(c.ptr, str.ptr))
}
// MkIntToStr creates an integer-to-string conversion.
func (c *Context) MkIntToStr(num *Expr) *Expr {
return newExpr(c, C.Z3_mk_int_to_str(c.ptr, num.ptr))
}
// Regular expression operations
// MkReSort creates a regular expression sort.
func (c *Context) MkReSort(seqSort *Sort) *Sort {
return newSort(c, C.Z3_mk_re_sort(c.ptr, seqSort.ptr))
}
// MkToRe converts a sequence to a regular expression that accepts exactly that sequence.
func (c *Context) MkToRe(seq *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_to_re(c.ptr, seq.ptr))
}
// MkInRe creates a membership predicate for a sequence in a regular expression.
func (c *Context) MkInRe(seq, re *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_in_re(c.ptr, seq.ptr, re.ptr))
}
// MkReStar creates a Kleene star (zero or more repetitions) of a regular expression.
func (c *Context) MkReStar(re *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_star(c.ptr, re.ptr))
}
// MkRePlus creates a Kleene plus (one or more repetitions) of a regular expression.
func (c *Context) MkRePlus(re *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_plus(c.ptr, re.ptr))
}
// MkReOption creates an optional regular expression (zero or one repetition).
func (c *Context) MkReOption(re *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_option(c.ptr, re.ptr))
}
// MkRePower creates a regular expression that matches exactly n repetitions.
func (c *Context) MkRePower(re *Expr, n uint) *Expr {
return newExpr(c, C.Z3_mk_re_power(c.ptr, re.ptr, C.uint(n)))
}
// MkReLoop creates a regular expression with bounded repetition (between lo and hi times).
// If hi is 0, it means unbounded (at least lo times).
func (c *Context) MkReLoop(re *Expr, lo, hi uint) *Expr {
return newExpr(c, C.Z3_mk_re_loop(c.ptr, re.ptr, C.uint(lo), C.uint(hi)))
}
// MkReConcat creates a concatenation of regular expressions.
func (c *Context) MkReConcat(regexes ...*Expr) *Expr {
if len(regexes) == 0 {
return nil
}
if len(regexes) == 1 {
return regexes[0]
}
cRegexes := make([]C.Z3_ast, len(regexes))
for i, re := range regexes {
cRegexes[i] = re.ptr
}
return newExpr(c, C.Z3_mk_re_concat(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
}
// MkReUnion creates a union (alternation) of regular expressions.
func (c *Context) MkReUnion(regexes ...*Expr) *Expr {
if len(regexes) == 0 {
return nil
}
if len(regexes) == 1 {
return regexes[0]
}
cRegexes := make([]C.Z3_ast, len(regexes))
for i, re := range regexes {
cRegexes[i] = re.ptr
}
return newExpr(c, C.Z3_mk_re_union(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
}
// MkReIntersect creates an intersection of regular expressions.
func (c *Context) MkReIntersect(regexes ...*Expr) *Expr {
if len(regexes) == 0 {
return nil
}
if len(regexes) == 1 {
return regexes[0]
}
cRegexes := make([]C.Z3_ast, len(regexes))
for i, re := range regexes {
cRegexes[i] = re.ptr
}
return newExpr(c, C.Z3_mk_re_intersect(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
}
// MkReComplement creates the complement of a regular expression.
func (c *Context) MkReComplement(re *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_complement(c.ptr, re.ptr))
}
// MkReDiff creates the difference of two regular expressions (a - b).
func (c *Context) MkReDiff(a, b *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_diff(c.ptr, a.ptr, b.ptr))
}
// MkReEmpty creates an empty regular expression (accepts no strings).
func (c *Context) MkReEmpty(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_re_empty(c.ptr, sort.ptr))
}
// MkReFull creates a full regular expression (accepts all strings).
func (c *Context) MkReFull(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_re_full(c.ptr, sort.ptr))
}
// MkReAllchar creates a regular expression that accepts all single characters.
func (c *Context) MkReAllchar(sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_re_allchar(c.ptr, sort.ptr))
}
// MkReRange creates a regular expression for a character range [lo, hi].
func (c *Context) MkReRange(lo, hi *Expr) *Expr {
return newExpr(c, C.Z3_mk_re_range(c.ptr, lo.ptr, hi.ptr))
}
// MkSeqReplaceRe replaces the first occurrence matching a regular expression.
func (c *Context) MkSeqReplaceRe(seq, re, replacement *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_replace_re(c.ptr, seq.ptr, re.ptr, replacement.ptr))
}
// MkSeqReplaceReAll replaces all occurrences matching a regular expression.
func (c *Context) MkSeqReplaceReAll(seq, re, replacement *Expr) *Expr {
return newExpr(c, C.Z3_mk_seq_replace_re_all(c.ptr, seq.ptr, re.ptr, replacement.ptr))
}

403
src/api/go/solver.go Normal file
View file

@ -0,0 +1,403 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Status represents the result of a satisfiability check.
type Status int
const (
// Unsatisfiable means the constraints are unsatisfiable.
Unsatisfiable Status = -1
// Unknown means Z3 could not determine satisfiability.
Unknown Status = 0
// Satisfiable means the constraints are satisfiable.
Satisfiable Status = 1
)
// String returns the string representation of the status.
func (s Status) String() string {
switch s {
case Unsatisfiable:
return "unsat"
case Unknown:
return "unknown"
case Satisfiable:
return "sat"
default:
return "unknown"
}
}
// Solver represents a Z3 solver.
type Solver struct {
ctx *Context
ptr C.Z3_solver
}
// NewSolver creates a new solver for the given context.
func (c *Context) NewSolver() *Solver {
s := &Solver{
ctx: c,
ptr: C.Z3_mk_solver(c.ptr),
}
C.Z3_solver_inc_ref(c.ptr, s.ptr)
runtime.SetFinalizer(s, func(solver *Solver) {
C.Z3_solver_dec_ref(solver.ctx.ptr, solver.ptr)
})
return s
}
// NewSolverForLogic creates a solver for a specific logic.
func (c *Context) NewSolverForLogic(logic string) *Solver {
sym := c.MkStringSymbol(logic)
s := &Solver{
ctx: c,
ptr: C.Z3_mk_solver_for_logic(c.ptr, sym.ptr),
}
C.Z3_solver_inc_ref(c.ptr, s.ptr)
runtime.SetFinalizer(s, func(solver *Solver) {
C.Z3_solver_dec_ref(solver.ctx.ptr, solver.ptr)
})
return s
}
// String returns the string representation of the solver.
func (s *Solver) String() string {
return C.GoString(C.Z3_solver_to_string(s.ctx.ptr, s.ptr))
}
// Assert adds a constraint to the solver.
func (s *Solver) Assert(constraint *Expr) {
C.Z3_solver_assert(s.ctx.ptr, s.ptr, constraint.ptr)
}
// AssertAndTrack adds a constraint with a tracking literal.
func (s *Solver) AssertAndTrack(constraint, track *Expr) {
C.Z3_solver_assert_and_track(s.ctx.ptr, s.ptr, constraint.ptr, track.ptr)
}
// Check checks the satisfiability of the constraints.
func (s *Solver) Check() Status {
result := C.Z3_solver_check(s.ctx.ptr, s.ptr)
return Status(result)
}
// CheckAssumptions checks satisfiability under assumptions.
func (s *Solver) CheckAssumptions(assumptions ...*Expr) Status {
if len(assumptions) == 0 {
return s.Check()
}
cAssumptions := make([]C.Z3_ast, len(assumptions))
for i, a := range assumptions {
cAssumptions[i] = a.ptr
}
result := C.Z3_solver_check_assumptions(s.ctx.ptr, s.ptr, C.uint(len(assumptions)), &cAssumptions[0])
return Status(result)
}
// Model returns the model if the constraints are satisfiable.
func (s *Solver) Model() *Model {
modelPtr := C.Z3_solver_get_model(s.ctx.ptr, s.ptr)
if modelPtr == nil {
return nil
}
return newModel(s.ctx, modelPtr)
}
// Push creates a backtracking point.
func (s *Solver) Push() {
C.Z3_solver_push(s.ctx.ptr, s.ptr)
}
// Pop removes backtracking points.
func (s *Solver) Pop(n uint) {
C.Z3_solver_pop(s.ctx.ptr, s.ptr, C.uint(n))
}
// Reset removes all assertions from the solver.
func (s *Solver) Reset() {
C.Z3_solver_reset(s.ctx.ptr, s.ptr)
}
// NumScopes returns the number of backtracking points.
func (s *Solver) NumScopes() uint {
return uint(C.Z3_solver_get_num_scopes(s.ctx.ptr, s.ptr))
}
// Assertions returns the assertions in the solver.
func (s *Solver) Assertions() []*Expr {
vec := C.Z3_solver_get_assertions(s.ctx.ptr, s.ptr)
return astVectorToExprs(s.ctx, vec)
}
// UnsatCore returns the unsat core if the constraints are unsatisfiable.
func (s *Solver) UnsatCore() []*Expr {
vec := C.Z3_solver_get_unsat_core(s.ctx.ptr, s.ptr)
return astVectorToExprs(s.ctx, vec)
}
// ReasonUnknown returns the reason why the result is unknown.
func (s *Solver) ReasonUnknown() string {
return C.GoString(C.Z3_solver_get_reason_unknown(s.ctx.ptr, s.ptr))
}
// GetStatistics returns the statistics for the solver.
// Statistics include performance metrics, memory usage, and decision statistics.
func (s *Solver) GetStatistics() *Statistics {
ptr := C.Z3_solver_get_statistics(s.ctx.ptr, s.ptr)
return newStatistics(s.ctx, ptr)
}
// FromFile parses and asserts SMT-LIB2 formulas from a file.
// The solver will contain the assertions from the file after this call.
func (s *Solver) FromFile(filename string) {
cFilename := C.CString(filename)
defer C.free(unsafe.Pointer(cFilename))
C.Z3_solver_from_file(s.ctx.ptr, s.ptr, cFilename)
}
// FromString parses and asserts SMT-LIB2 formulas from a string.
// The solver will contain the assertions from the string after this call.
func (s *Solver) FromString(str string) {
cStr := C.CString(str)
defer C.free(unsafe.Pointer(cStr))
C.Z3_solver_from_string(s.ctx.ptr, s.ptr, cStr)
}
// GetHelp returns a string describing all available solver parameters.
func (s *Solver) GetHelp() string {
return C.GoString(C.Z3_solver_get_help(s.ctx.ptr, s.ptr))
}
// SetParams sets solver parameters.
// Parameters control solver behavior such as timeout, proof generation, etc.
func (s *Solver) SetParams(params *Params) {
C.Z3_solver_set_params(s.ctx.ptr, s.ptr, params.ptr)
}
// GetParamDescrs returns parameter descriptions for the solver.
func (s *Solver) GetParamDescrs() *ParamDescrs {
ptr := C.Z3_solver_get_param_descrs(s.ctx.ptr, s.ptr)
return newParamDescrs(s.ctx, ptr)
}
// Interrupt interrupts the solver execution.
// This is useful for stopping long-running solver operations gracefully.
func (s *Solver) Interrupt() {
C.Z3_solver_interrupt(s.ctx.ptr, s.ptr)
}
// Units returns the unit clauses (literals) learned by the solver.
// Unit clauses are assertions that have been simplified to single literals.
// This is useful for debugging and understanding solver behavior.
func (s *Solver) Units() []*Expr {
vec := C.Z3_solver_get_units(s.ctx.ptr, s.ptr)
return astVectorToExprs(s.ctx, vec)
}
// NonUnits returns the non-unit clauses in the solver's current state.
// These are clauses that have not been reduced to unit clauses.
// This is useful for debugging and understanding solver behavior.
func (s *Solver) NonUnits() []*Expr {
vec := C.Z3_solver_get_non_units(s.ctx.ptr, s.ptr)
return astVectorToExprs(s.ctx, vec)
}
// Trail returns the decision trail of the solver.
// The trail contains the sequence of literals assigned during search.
// This is useful for understanding the solver's decision history.
// Note: This function works primarily with SimpleSolver. For solvers created
// using tactics (e.g., NewSolver()), it may return an error.
func (s *Solver) Trail() []*Expr {
vec := C.Z3_solver_get_trail(s.ctx.ptr, s.ptr)
return astVectorToExprs(s.ctx, vec)
}
// TrailLevels returns the decision levels for each literal in the trail.
// The returned slice has the same length as the trail, where each element
// indicates the decision level at which the corresponding trail literal was assigned.
// This is useful for understanding the structure of the search tree.
// Note: This function works primarily with SimpleSolver. For solvers created
// using tactics (e.g., NewSolver()), it may return an error.
func (s *Solver) TrailLevels() []uint {
// Get the trail vector directly from the C API
trailVec := C.Z3_solver_get_trail(s.ctx.ptr, s.ptr)
C.Z3_ast_vector_inc_ref(s.ctx.ptr, trailVec)
defer C.Z3_ast_vector_dec_ref(s.ctx.ptr, trailVec)
n := uint(C.Z3_ast_vector_size(s.ctx.ptr, trailVec))
if n == 0 {
return []uint{}
}
// Allocate the levels array
levels := make([]C.uint, n)
// Get the levels using the trail vector directly
// Safe to pass &levels[0] because we checked n > 0 above
C.Z3_solver_get_levels(s.ctx.ptr, s.ptr, trailVec, C.uint(n), &levels[0])
// Convert to Go slice
result := make([]uint, n)
for i := uint(0); i < n; i++ {
result[i] = uint(levels[i])
}
return result
}
// CongruenceRoot returns the congruence class representative of the given expression.
// This returns the root element in the congruence closure for the term.
// Note: This function works primarily with SimpleSolver. Terms and variables that
// are eliminated during pre-processing are not visible to the congruence closure.
func (s *Solver) CongruenceRoot(expr *Expr) *Expr {
ast := C.Z3_solver_congruence_root(s.ctx.ptr, s.ptr, expr.ptr)
return newExpr(s.ctx, ast)
}
// CongruenceNext returns the next element in the congruence class of the given expression.
// This allows iteration through all elements in a congruence class.
// Note: This function works primarily with SimpleSolver. Terms and variables that
// are eliminated during pre-processing are not visible to the congruence closure.
func (s *Solver) CongruenceNext(expr *Expr) *Expr {
ast := C.Z3_solver_congruence_next(s.ctx.ptr, s.ptr, expr.ptr)
return newExpr(s.ctx, ast)
}
// CongruenceExplain returns an explanation for why two expressions are congruent.
// The result is an expression that justifies the congruence between a and b.
// Note: This function works primarily with SimpleSolver. Terms and variables that
// are eliminated during pre-processing are not visible to the congruence closure.
func (s *Solver) CongruenceExplain(a, b *Expr) *Expr {
ast := C.Z3_solver_congruence_explain(s.ctx.ptr, s.ptr, a.ptr, b.ptr)
return newExpr(s.ctx, ast)
}
// SetInitialValue provides an initial value hint for a variable to the solver.
// This can help guide the solver to find solutions more efficiently.
// The variable must be a constant or function application, and the value must be
// compatible with the variable's sort.
func (s *Solver) SetInitialValue(variable, value *Expr) {
C.Z3_solver_set_initial_value(s.ctx.ptr, s.ptr, variable.ptr, value.ptr)
}
// Model represents a Z3 model (satisfying assignment).
type Model struct {
ctx *Context
ptr C.Z3_model
}
// newModel creates a new Model and manages its reference count.
func newModel(ctx *Context, ptr C.Z3_model) *Model {
m := &Model{ctx: ctx, ptr: ptr}
C.Z3_model_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(m, func(model *Model) {
C.Z3_model_dec_ref(model.ctx.ptr, model.ptr)
})
return m
}
// String returns the string representation of the model.
func (m *Model) String() string {
return C.GoString(C.Z3_model_to_string(m.ctx.ptr, m.ptr))
}
// NumConsts returns the number of constants in the model.
func (m *Model) NumConsts() uint {
return uint(C.Z3_model_get_num_consts(m.ctx.ptr, m.ptr))
}
// NumFuncs returns the number of function interpretations in the model.
func (m *Model) NumFuncs() uint {
return uint(C.Z3_model_get_num_funcs(m.ctx.ptr, m.ptr))
}
// GetConstDecl returns the i-th constant declaration in the model.
func (m *Model) GetConstDecl(i uint) *FuncDecl {
return newFuncDecl(m.ctx, C.Z3_model_get_const_decl(m.ctx.ptr, m.ptr, C.uint(i)))
}
// GetFuncDecl returns the i-th function declaration in the model.
func (m *Model) GetFuncDecl(i uint) *FuncDecl {
return newFuncDecl(m.ctx, C.Z3_model_get_func_decl(m.ctx.ptr, m.ptr, C.uint(i)))
}
// Eval evaluates an expression in the model.
// If modelCompletion is true, Z3 will assign an interpretation for uninterpreted constants.
func (m *Model) Eval(expr *Expr, modelCompletion bool) (*Expr, bool) {
var result C.Z3_ast
var completion C.bool
if modelCompletion {
completion = C.bool(true)
} else {
completion = C.bool(false)
}
success := C.Z3_model_eval(m.ctx.ptr, m.ptr, expr.ptr, completion, &result)
if success == C.bool(false) {
return nil, false
}
return newExpr(m.ctx, result), true
}
// GetConstInterp returns the interpretation of a constant.
func (m *Model) GetConstInterp(decl *FuncDecl) *Expr {
result := C.Z3_model_get_const_interp(m.ctx.ptr, m.ptr, decl.ptr)
if result == nil {
return nil
}
return newExpr(m.ctx, result)
}
// FuncInterp represents a function interpretation in a model.
type FuncInterp struct {
ctx *Context
ptr C.Z3_func_interp
}
// GetFuncInterp returns the interpretation of a function.
func (m *Model) GetFuncInterp(decl *FuncDecl) *FuncInterp {
result := C.Z3_model_get_func_interp(m.ctx.ptr, m.ptr, decl.ptr)
if result == nil {
return nil
}
fi := &FuncInterp{ctx: m.ctx, ptr: result}
C.Z3_func_interp_inc_ref(m.ctx.ptr, result)
runtime.SetFinalizer(fi, func(f *FuncInterp) {
C.Z3_func_interp_dec_ref(f.ctx.ptr, f.ptr)
})
return fi
}
// NumEntries returns the number of entries in the function interpretation.
func (fi *FuncInterp) NumEntries() uint {
return uint(C.Z3_func_interp_get_num_entries(fi.ctx.ptr, fi.ptr))
}
// GetElse returns the else value of the function interpretation.
func (fi *FuncInterp) GetElse() *Expr {
result := C.Z3_func_interp_get_else(fi.ctx.ptr, fi.ptr)
return newExpr(fi.ctx, result)
}
// GetArity returns the arity of the function interpretation.
func (fi *FuncInterp) GetArity() uint {
return uint(C.Z3_func_interp_get_arity(fi.ctx.ptr, fi.ptr))
}
// SortUniverse returns the universe of values for an uninterpreted sort in the model.
// The universe is represented as a list of distinct expressions.
// Returns nil if the sort is not an uninterpreted sort in this model.
func (m *Model) SortUniverse(sort *Sort) []*Expr {
vec := C.Z3_model_get_sort_universe(m.ctx.ptr, m.ptr, sort.ptr)
if vec == nil {
return nil
}
return astVectorToExprs(m.ctx, vec)
}

294
src/api/go/tactic.go Normal file
View file

@ -0,0 +1,294 @@
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Tactic represents a Z3 tactic for transforming goals.
type Tactic struct {
ctx *Context
ptr C.Z3_tactic
}
// newTactic creates a new Tactic and manages its reference count.
func newTactic(ctx *Context, ptr C.Z3_tactic) *Tactic {
t := &Tactic{ctx: ctx, ptr: ptr}
C.Z3_tactic_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(t, func(tactic *Tactic) {
C.Z3_tactic_dec_ref(tactic.ctx.ptr, tactic.ptr)
})
return t
}
// MkTactic creates a tactic with the given name.
func (c *Context) MkTactic(name string) *Tactic {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
return newTactic(c, C.Z3_mk_tactic(c.ptr, cName))
}
// Apply applies the tactic to a goal.
func (t *Tactic) Apply(g *Goal) *ApplyResult {
return newApplyResult(t.ctx, C.Z3_tactic_apply(t.ctx.ptr, t.ptr, g.ptr))
}
// GetHelp returns help information for the tactic.
func (t *Tactic) GetHelp() string {
return C.GoString(C.Z3_tactic_get_help(t.ctx.ptr, t.ptr))
}
// AndThen creates a tactic that applies t1 and then t2.
func (t *Tactic) AndThen(t2 *Tactic) *Tactic {
return newTactic(t.ctx, C.Z3_tactic_and_then(t.ctx.ptr, t.ptr, t2.ptr))
}
// OrElse creates a tactic that applies t1, and if it fails, applies t2.
func (t *Tactic) OrElse(t2 *Tactic) *Tactic {
return newTactic(t.ctx, C.Z3_tactic_or_else(t.ctx.ptr, t.ptr, t2.ptr))
}
// Repeat creates a tactic that applies t repeatedly (at most max times).
func (t *Tactic) Repeat(max uint) *Tactic {
return newTactic(t.ctx, C.Z3_tactic_repeat(t.ctx.ptr, t.ptr, C.uint(max)))
}
// When creates a conditional tactic that applies t only if probe p evaluates to true.
func (c *Context) TacticWhen(p *Probe, t *Tactic) *Tactic {
return newTactic(c, C.Z3_tactic_when(c.ptr, p.ptr, t.ptr))
}
// TacticCond creates a conditional tactic: if p then t1 else t2.
func (c *Context) TacticCond(p *Probe, t1, t2 *Tactic) *Tactic {
return newTactic(c, C.Z3_tactic_cond(c.ptr, p.ptr, t1.ptr, t2.ptr))
}
// TacticFail creates a tactic that always fails.
func (c *Context) TacticFail() *Tactic {
return newTactic(c, C.Z3_tactic_fail(c.ptr))
}
// TacticSkip creates a tactic that always succeeds.
func (c *Context) TacticSkip() *Tactic {
return newTactic(c, C.Z3_tactic_skip(c.ptr))
}
// Goal represents a set of formulas that can be solved or transformed.
type Goal struct {
ctx *Context
ptr C.Z3_goal
}
// newGoal creates a new Goal and manages its reference count.
func newGoal(ctx *Context, ptr C.Z3_goal) *Goal {
g := &Goal{ctx: ctx, ptr: ptr}
C.Z3_goal_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(g, func(goal *Goal) {
C.Z3_goal_dec_ref(goal.ctx.ptr, goal.ptr)
})
return g
}
// MkGoal creates a new goal.
func (c *Context) MkGoal(models, unsatCores, proofs bool) *Goal {
return newGoal(c, C.Z3_mk_goal(c.ptr, C.bool(models), C.bool(unsatCores), C.bool(proofs)))
}
// Assert adds a constraint to the goal.
func (g *Goal) Assert(constraint *Expr) {
C.Z3_goal_assert(g.ctx.ptr, g.ptr, constraint.ptr)
}
// Size returns the number of formulas in the goal.
func (g *Goal) Size() uint {
return uint(C.Z3_goal_size(g.ctx.ptr, g.ptr))
}
// Formula returns the i-th formula in the goal.
func (g *Goal) Formula(i uint) *Expr {
return newExpr(g.ctx, C.Z3_goal_formula(g.ctx.ptr, g.ptr, C.uint(i)))
}
// NumExprs returns the number of expressions in the goal.
func (g *Goal) NumExprs() uint {
return uint(C.Z3_goal_num_exprs(g.ctx.ptr, g.ptr))
}
// IsDecidedSat returns true if the goal is decided to be satisfiable.
func (g *Goal) IsDecidedSat() bool {
return bool(C.Z3_goal_is_decided_sat(g.ctx.ptr, g.ptr))
}
// IsDecidedUnsat returns true if the goal is decided to be unsatisfiable.
func (g *Goal) IsDecidedUnsat() bool {
return bool(C.Z3_goal_is_decided_unsat(g.ctx.ptr, g.ptr))
}
// Reset removes all formulas from the goal.
func (g *Goal) Reset() {
C.Z3_goal_reset(g.ctx.ptr, g.ptr)
}
// String returns the string representation of the goal.
func (g *Goal) String() string {
return C.GoString(C.Z3_goal_to_string(g.ctx.ptr, g.ptr))
}
// ApplyResult represents the result of applying a tactic to a goal.
type ApplyResult struct {
ctx *Context
ptr C.Z3_apply_result
}
// newApplyResult creates a new ApplyResult and manages its reference count.
func newApplyResult(ctx *Context, ptr C.Z3_apply_result) *ApplyResult {
ar := &ApplyResult{ctx: ctx, ptr: ptr}
C.Z3_apply_result_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(ar, func(result *ApplyResult) {
C.Z3_apply_result_dec_ref(result.ctx.ptr, result.ptr)
})
return ar
}
// NumSubgoals returns the number of subgoals in the result.
func (ar *ApplyResult) NumSubgoals() uint {
return uint(C.Z3_apply_result_get_num_subgoals(ar.ctx.ptr, ar.ptr))
}
// Subgoal returns the i-th subgoal.
func (ar *ApplyResult) Subgoal(i uint) *Goal {
return newGoal(ar.ctx, C.Z3_apply_result_get_subgoal(ar.ctx.ptr, ar.ptr, C.uint(i)))
}
// String returns the string representation of the apply result.
func (ar *ApplyResult) String() string {
return C.GoString(C.Z3_apply_result_to_string(ar.ctx.ptr, ar.ptr))
}
// Probe represents a probe for checking properties of goals.
type Probe struct {
ctx *Context
ptr C.Z3_probe
}
// newProbe creates a new Probe and manages its reference count.
func newProbe(ctx *Context, ptr C.Z3_probe) *Probe {
p := &Probe{ctx: ctx, ptr: ptr}
C.Z3_probe_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(p, func(probe *Probe) {
C.Z3_probe_dec_ref(probe.ctx.ptr, probe.ptr)
})
return p
}
// MkProbe creates a probe with the given name.
func (c *Context) MkProbe(name string) *Probe {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
return newProbe(c, C.Z3_mk_probe(c.ptr, cName))
}
// Apply evaluates the probe on a goal.
func (p *Probe) Apply(g *Goal) float64 {
return float64(C.Z3_probe_apply(p.ctx.ptr, p.ptr, g.ptr))
}
// ProbeConst creates a probe that always evaluates to the given value.
func (c *Context) ProbeConst(val float64) *Probe {
return newProbe(c, C.Z3_probe_const(c.ptr, C.double(val)))
}
// ProbeLt creates a probe that evaluates to true if p1 < p2.
func (p *Probe) Lt(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_lt(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeGt creates a probe that evaluates to true if p1 > p2.
func (p *Probe) Gt(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_gt(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeLe creates a probe that evaluates to true if p1 <= p2.
func (p *Probe) Le(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_le(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeGe creates a probe that evaluates to true if p1 >= p2.
func (p *Probe) Ge(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_ge(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeEq creates a probe that evaluates to true if p1 == p2.
func (p *Probe) Eq(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_eq(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeAnd creates a probe that is the conjunction of p1 and p2.
func (p *Probe) And(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_and(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeOr creates a probe that is the disjunction of p1 and p2.
func (p *Probe) Or(p2 *Probe) *Probe {
return newProbe(p.ctx, C.Z3_probe_or(p.ctx.ptr, p.ptr, p2.ptr))
}
// ProbeNot creates a probe that is the negation of p.
func (p *Probe) Not() *Probe {
return newProbe(p.ctx, C.Z3_probe_not(p.ctx.ptr, p.ptr))
}
// Params represents a parameter set.
type Params struct {
ctx *Context
ptr C.Z3_params
}
// newParams creates a new Params and manages its reference count.
func newParams(ctx *Context, ptr C.Z3_params) *Params {
params := &Params{ctx: ctx, ptr: ptr}
C.Z3_params_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(params, func(p *Params) {
C.Z3_params_dec_ref(p.ctx.ptr, p.ptr)
})
return params
}
// MkParams creates a new parameter set.
func (c *Context) MkParams() *Params {
return newParams(c, C.Z3_mk_params(c.ptr))
}
// SetBool sets a Boolean parameter.
func (p *Params) SetBool(key string, value bool) {
sym := p.ctx.MkStringSymbol(key)
C.Z3_params_set_bool(p.ctx.ptr, p.ptr, sym.ptr, C.bool(value))
}
// SetUint sets an unsigned integer parameter.
func (p *Params) SetUint(key string, value uint) {
sym := p.ctx.MkStringSymbol(key)
C.Z3_params_set_uint(p.ctx.ptr, p.ptr, sym.ptr, C.uint(value))
}
// SetDouble sets a double parameter.
func (p *Params) SetDouble(key string, value float64) {
sym := p.ctx.MkStringSymbol(key)
C.Z3_params_set_double(p.ctx.ptr, p.ptr, sym.ptr, C.double(value))
}
// SetSymbol sets a symbol parameter.
func (p *Params) SetSymbol(key string, value *Symbol) {
sym := p.ctx.MkStringSymbol(key)
C.Z3_params_set_symbol(p.ctx.ptr, p.ptr, sym.ptr, value.ptr)
}
// String returns the string representation of the parameters.
func (p *Params) String() string {
return C.GoString(C.Z3_params_to_string(p.ctx.ptr, p.ptr))
}

806
src/api/go/z3.go Normal file
View file

@ -0,0 +1,806 @@
// Package z3 provides Go bindings for the Z3 theorem prover.
//
// Z3 is a high-performance SMT (Satisfiability Modulo Theories) solver
// developed at Microsoft Research. These bindings wrap the Z3 C API using
// CGO and provide idiomatic Go interfaces with automatic memory management.
//
// # Basic Usage
//
// Create a context and solver:
//
// ctx := z3.NewContext()
// solver := ctx.NewSolver()
//
// Create variables and constraints:
//
// x := ctx.MkIntConst("x")
// y := ctx.MkIntConst("y")
// solver.Assert(ctx.MkEq(ctx.MkAdd(x, y), ctx.MkInt(10, ctx.MkIntSort())))
// solver.Assert(ctx.MkGt(x, y))
//
// Check satisfiability and get model:
//
// if solver.Check() == z3.Satisfiable {
// model := solver.Model()
// xVal, _ := model.Eval(x, true)
// fmt.Println("x =", xVal.String())
// }
//
// # Memory Management
//
// All Z3 objects are automatically managed using Go finalizers. Reference
// counting is handled transparently - you don't need to manually free objects.
//
// # Supported Features
//
// - Boolean logic, integer and real arithmetic
// - Bit-vectors and floating-point arithmetic
// - Arrays, sequences, and strings
// - Regular expressions
// - Algebraic datatypes
// - Quantifiers and lambda expressions
// - Tactics and goal-based solving
// - Optimization (MaxSMT)
// - Fixedpoint solver (Datalog/CHC)
//
// For more examples, see the examples/go directory in the Z3 repository.
package z3
/*
#cgo CFLAGS: -I${SRCDIR}/..
#cgo LDFLAGS: -lz3
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
// Config represents a Z3 configuration object.
type Config struct {
ptr C.Z3_config
}
// NewConfig creates a new Z3 configuration.
func NewConfig() *Config {
cfg := &Config{ptr: C.Z3_mk_config()}
runtime.SetFinalizer(cfg, func(c *Config) {
C.Z3_del_config(c.ptr)
})
return cfg
}
// SetParamValue sets a configuration parameter.
func (c *Config) SetParamValue(paramID, paramValue string) {
cParamID := C.CString(paramID)
cParamValue := C.CString(paramValue)
defer C.free(unsafe.Pointer(cParamID))
defer C.free(unsafe.Pointer(cParamValue))
C.Z3_set_param_value(c.ptr, cParamID, cParamValue)
}
// Context represents a Z3 logical context.
type Context struct {
ptr C.Z3_context
}
// NewContext creates a new Z3 context with default configuration.
func NewContext() *Context {
ctx := &Context{ptr: C.Z3_mk_context_rc(C.Z3_mk_config())}
runtime.SetFinalizer(ctx, func(c *Context) {
C.Z3_del_context(c.ptr)
})
return ctx
}
// NewContextWithConfig creates a new Z3 context with the given configuration.
func NewContextWithConfig(cfg *Config) *Context {
ctx := &Context{ptr: C.Z3_mk_context_rc(cfg.ptr)}
runtime.SetFinalizer(ctx, func(c *Context) {
C.Z3_del_context(c.ptr)
})
return ctx
}
// SetParam sets a global or context parameter.
func (c *Context) SetParam(key, value string) {
cKey := C.CString(key)
cValue := C.CString(value)
defer C.free(unsafe.Pointer(cKey))
defer C.free(unsafe.Pointer(cValue))
C.Z3_update_param_value(c.ptr, cKey, cValue)
}
// Symbol represents a Z3 symbol.
type Symbol struct {
ctx *Context
ptr C.Z3_symbol
}
// newSymbol creates a new Symbol.
func newSymbol(ctx *Context, ptr C.Z3_symbol) *Symbol {
return &Symbol{ctx: ctx, ptr: ptr}
}
// MkIntSymbol creates an integer symbol.
func (c *Context) MkIntSymbol(i int) *Symbol {
return &Symbol{
ctx: c,
ptr: C.Z3_mk_int_symbol(c.ptr, C.int(i)),
}
}
// MkStringSymbol creates a string symbol.
func (c *Context) MkStringSymbol(s string) *Symbol {
cStr := C.CString(s)
defer C.free(unsafe.Pointer(cStr))
return &Symbol{
ctx: c,
ptr: C.Z3_mk_string_symbol(c.ptr, cStr),
}
}
// String returns the string representation of the symbol.
func (s *Symbol) String() string {
kind := C.Z3_get_symbol_kind(s.ctx.ptr, s.ptr)
if kind == C.Z3_INT_SYMBOL {
return string(rune(C.Z3_get_symbol_int(s.ctx.ptr, s.ptr)))
}
return C.GoString(C.Z3_get_symbol_string(s.ctx.ptr, s.ptr))
}
// AST represents a Z3 abstract syntax tree node.
type AST struct {
ctx *Context
ptr C.Z3_ast
}
// incRef increments the reference count of the AST.
func (a *AST) incRef() {
C.Z3_inc_ref(a.ctx.ptr, a.ptr)
}
// decRef decrements the reference count of the AST.
func (a *AST) decRef() {
C.Z3_dec_ref(a.ctx.ptr, a.ptr)
}
// String returns the string representation of the AST.
func (a *AST) String() string {
return C.GoString(C.Z3_ast_to_string(a.ctx.ptr, a.ptr))
}
// Hash returns the hash code of the AST.
func (a *AST) Hash() uint32 {
return uint32(C.Z3_get_ast_hash(a.ctx.ptr, a.ptr))
}
// Equal checks if two ASTs are equal.
func (a *AST) Equal(other *AST) bool {
if a.ctx != other.ctx {
return false
}
return bool(C.Z3_is_eq_ast(a.ctx.ptr, a.ptr, other.ptr))
}
// Sort represents a Z3 sort (type).
type Sort struct {
ctx *Context
ptr C.Z3_sort
}
// newSort creates a new Sort and manages its reference count.
func newSort(ctx *Context, ptr C.Z3_sort) *Sort {
sort := &Sort{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, C.Z3_sort_to_ast(ctx.ptr, ptr))
runtime.SetFinalizer(sort, func(s *Sort) {
C.Z3_dec_ref(s.ctx.ptr, C.Z3_sort_to_ast(s.ctx.ptr, s.ptr))
})
return sort
}
// String returns the string representation of the sort.
func (s *Sort) String() string {
return C.GoString(C.Z3_sort_to_string(s.ctx.ptr, s.ptr))
}
// Equal checks if two sorts are equal.
func (s *Sort) Equal(other *Sort) bool {
if s.ctx != other.ctx {
return false
}
return bool(C.Z3_is_eq_sort(s.ctx.ptr, s.ptr, other.ptr))
}
// MkBoolSort creates the Boolean sort.
func (c *Context) MkBoolSort() *Sort {
return newSort(c, C.Z3_mk_bool_sort(c.ptr))
}
// MkBvSort creates a bit-vector sort of the given size.
func (c *Context) MkBvSort(sz uint) *Sort {
return newSort(c, C.Z3_mk_bv_sort(c.ptr, C.uint(sz)))
}
// Expr represents a Z3 expression.
type Expr struct {
ctx *Context
ptr C.Z3_ast
}
// newExpr creates a new Expr and manages its reference count.
func newExpr(ctx *Context, ptr C.Z3_ast) *Expr {
expr := &Expr{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(expr, func(e *Expr) {
C.Z3_dec_ref(e.ctx.ptr, e.ptr)
})
return expr
}
// String returns the string representation of the expression.
func (e *Expr) String() string {
return C.GoString(C.Z3_ast_to_string(e.ctx.ptr, e.ptr))
}
// Equal checks if two expressions are equal.
func (e *Expr) Equal(other *Expr) bool {
if e.ctx != other.ctx {
return false
}
return bool(C.Z3_is_eq_ast(e.ctx.ptr, e.ptr, other.ptr))
}
// GetSort returns the sort of the expression.
func (e *Expr) GetSort() *Sort {
return newSort(e.ctx, C.Z3_get_sort(e.ctx.ptr, e.ptr))
}
// Pattern represents a Z3 pattern for quantifier instantiation.
type Pattern struct {
ctx *Context
ptr C.Z3_pattern
}
// newPattern creates a new Pattern and manages its reference count.
func newPattern(ctx *Context, ptr C.Z3_pattern) *Pattern {
p := &Pattern{ctx: ctx, ptr: ptr}
// Patterns are ASTs in the C API
C.Z3_inc_ref(ctx.ptr, (C.Z3_ast)(unsafe.Pointer(ptr)))
runtime.SetFinalizer(p, func(pat *Pattern) {
C.Z3_dec_ref(pat.ctx.ptr, (C.Z3_ast)(unsafe.Pointer(pat.ptr)))
})
return p
}
// ASTVector represents a vector of Z3 ASTs.
type ASTVector struct {
ctx *Context
ptr C.Z3_ast_vector
}
// newASTVector creates a new ASTVector and manages its reference count.
func newASTVector(ctx *Context, ptr C.Z3_ast_vector) *ASTVector {
v := &ASTVector{ctx: ctx, ptr: ptr}
C.Z3_ast_vector_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(v, func(vec *ASTVector) {
C.Z3_ast_vector_dec_ref(vec.ctx.ptr, vec.ptr)
})
return v
}
// ParamDescrs represents parameter descriptions for Z3 objects.
type ParamDescrs struct {
ctx *Context
ptr C.Z3_param_descrs
}
// newParamDescrs creates a new ParamDescrs and manages its reference count.
func newParamDescrs(ctx *Context, ptr C.Z3_param_descrs) *ParamDescrs {
pd := &ParamDescrs{ctx: ctx, ptr: ptr}
C.Z3_param_descrs_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(pd, func(descrs *ParamDescrs) {
C.Z3_param_descrs_dec_ref(descrs.ctx.ptr, descrs.ptr)
})
return pd
}
// MkTrue creates the Boolean constant true.
func (c *Context) MkTrue() *Expr {
return newExpr(c, C.Z3_mk_true(c.ptr))
}
// MkFalse creates the Boolean constant false.
func (c *Context) MkFalse() *Expr {
return newExpr(c, C.Z3_mk_false(c.ptr))
}
// MkBool creates a Boolean constant.
func (c *Context) MkBool(value bool) *Expr {
if value {
return c.MkTrue()
}
return c.MkFalse()
}
// MkNumeral creates a numeral from a string.
func (c *Context) MkNumeral(numeral string, sort *Sort) *Expr {
cStr := C.CString(numeral)
defer C.free(unsafe.Pointer(cStr))
return newExpr(c, C.Z3_mk_numeral(c.ptr, cStr, sort.ptr))
}
// MkConst creates a constant (variable) with the given name and sort.
func (c *Context) MkConst(name *Symbol, sort *Sort) *Expr {
return newExpr(c, C.Z3_mk_const(c.ptr, name.ptr, sort.ptr))
}
// MkBoolConst creates a Boolean constant (variable) with the given name.
func (c *Context) MkBoolConst(name string) *Expr {
sym := c.MkStringSymbol(name)
return c.MkConst(sym, c.MkBoolSort())
}
// Boolean operations
// MkAnd creates a conjunction.
func (c *Context) MkAnd(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkTrue()
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_and(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkOr creates a disjunction.
func (c *Context) MkOr(exprs ...*Expr) *Expr {
if len(exprs) == 0 {
return c.MkFalse()
}
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_or(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// MkNot creates a negation.
func (c *Context) MkNot(expr *Expr) *Expr {
return newExpr(c, C.Z3_mk_not(c.ptr, expr.ptr))
}
// MkImplies creates an implication.
func (c *Context) MkImplies(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_implies(c.ptr, lhs.ptr, rhs.ptr))
}
// MkIff creates a bi-implication (if and only if).
func (c *Context) MkIff(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_iff(c.ptr, lhs.ptr, rhs.ptr))
}
// MkXor creates exclusive or.
func (c *Context) MkXor(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_xor(c.ptr, lhs.ptr, rhs.ptr))
}
// Comparison operations
// MkEq creates an equality.
func (c *Context) MkEq(lhs, rhs *Expr) *Expr {
return newExpr(c, C.Z3_mk_eq(c.ptr, lhs.ptr, rhs.ptr))
}
// MkDistinct creates a distinct constraint.
func (c *Context) MkDistinct(exprs ...*Expr) *Expr {
if len(exprs) <= 1 {
return c.MkTrue()
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_distinct(c.ptr, C.uint(len(exprs)), &cExprs[0]))
}
// FuncDecl represents a function declaration.
type FuncDecl struct {
ctx *Context
ptr C.Z3_func_decl
}
// newFuncDecl creates a new FuncDecl and manages its reference count.
func newFuncDecl(ctx *Context, ptr C.Z3_func_decl) *FuncDecl {
fd := &FuncDecl{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, C.Z3_func_decl_to_ast(ctx.ptr, ptr))
runtime.SetFinalizer(fd, func(f *FuncDecl) {
C.Z3_dec_ref(f.ctx.ptr, C.Z3_func_decl_to_ast(f.ctx.ptr, f.ptr))
})
return fd
}
// String returns the string representation of the function declaration.
func (f *FuncDecl) String() string {
return C.GoString(C.Z3_func_decl_to_string(f.ctx.ptr, f.ptr))
}
// GetName returns the name of the function declaration.
func (f *FuncDecl) GetName() *Symbol {
return &Symbol{
ctx: f.ctx,
ptr: C.Z3_get_decl_name(f.ctx.ptr, f.ptr),
}
}
// GetArity returns the arity (number of parameters) of the function.
func (f *FuncDecl) GetArity() int {
return int(C.Z3_get_arity(f.ctx.ptr, f.ptr))
}
// GetDomain returns the sort of the i-th parameter.
func (f *FuncDecl) GetDomain(i int) *Sort {
return newSort(f.ctx, C.Z3_get_domain(f.ctx.ptr, f.ptr, C.uint(i)))
}
// GetRange returns the sort of the return value.
func (f *FuncDecl) GetRange() *Sort {
return newSort(f.ctx, C.Z3_get_range(f.ctx.ptr, f.ptr))
}
// MkFuncDecl creates a function declaration.
func (c *Context) MkFuncDecl(name *Symbol, domain []*Sort, range_ *Sort) *FuncDecl {
cDomain := make([]C.Z3_sort, len(domain))
for i, s := range domain {
cDomain[i] = s.ptr
}
var domainPtr *C.Z3_sort
if len(domain) > 0 {
domainPtr = &cDomain[0]
}
return newFuncDecl(c, C.Z3_mk_func_decl(c.ptr, name.ptr, C.uint(len(domain)), domainPtr, range_.ptr))
}
// MkApp creates a function application.
func (c *Context) MkApp(decl *FuncDecl, args ...*Expr) *Expr {
cArgs := make([]C.Z3_ast, len(args))
for i, a := range args {
cArgs[i] = a.ptr
}
var argsPtr *C.Z3_ast
if len(args) > 0 {
argsPtr = &cArgs[0]
}
return newExpr(c, C.Z3_mk_app(c.ptr, decl.ptr, C.uint(len(args)), argsPtr))
}
// Quantifier operations
// MkForall creates a universal quantifier.
func (c *Context) MkForall(bound []*Expr, body *Expr) *Expr {
cBound := make([]C.Z3_app, len(bound))
for i, b := range bound {
// Z3_app is a subtype of Z3_ast; constants are apps
cBound[i] = (C.Z3_app)(unsafe.Pointer(b.ptr))
}
var boundPtr *C.Z3_app
if len(bound) > 0 {
boundPtr = &cBound[0]
}
return newExpr(c, C.Z3_mk_forall_const(c.ptr, 0, C.uint(len(bound)), boundPtr, 0, nil, body.ptr))
}
// MkExists creates an existential quantifier.
func (c *Context) MkExists(bound []*Expr, body *Expr) *Expr {
cBound := make([]C.Z3_app, len(bound))
for i, b := range bound {
// Z3_app is a subtype of Z3_ast; constants are apps
cBound[i] = (C.Z3_app)(unsafe.Pointer(b.ptr))
}
var boundPtr *C.Z3_app
if len(bound) > 0 {
boundPtr = &cBound[0]
}
return newExpr(c, C.Z3_mk_exists_const(c.ptr, 0, C.uint(len(bound)), boundPtr, 0, nil, body.ptr))
}
// Simplify simplifies an expression.
func (e *Expr) Simplify() *Expr {
return newExpr(e.ctx, C.Z3_simplify(e.ctx.ptr, e.ptr))
}
// MkTypeVariable creates a type variable sort for use in polymorphic functions and datatypes
func (c *Context) MkTypeVariable(name *Symbol) *Sort {
return newSort(c, C.Z3_mk_type_variable(c.ptr, name.ptr))
}
// Quantifier represents a quantified formula (forall or exists)
type Quantifier struct {
ctx *Context
ptr C.Z3_ast
}
// newQuantifier creates a new Quantifier with proper memory management
func newQuantifier(ctx *Context, ptr C.Z3_ast) *Quantifier {
q := &Quantifier{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(q, func(qf *Quantifier) {
C.Z3_dec_ref(qf.ctx.ptr, qf.ptr)
})
return q
}
// AsExpr converts a Quantifier to an Expr
func (q *Quantifier) AsExpr() *Expr {
return newExpr(q.ctx, q.ptr)
}
// IsUniversal returns true if this is a universal quantifier (forall)
func (q *Quantifier) IsUniversal() bool {
return bool(C.Z3_is_quantifier_forall(q.ctx.ptr, q.ptr))
}
// IsExistential returns true if this is an existential quantifier (exists)
func (q *Quantifier) IsExistential() bool {
return bool(C.Z3_is_quantifier_exists(q.ctx.ptr, q.ptr))
}
// GetWeight returns the weight of the quantifier
func (q *Quantifier) GetWeight() int {
return int(C.Z3_get_quantifier_weight(q.ctx.ptr, q.ptr))
}
// GetNumPatterns returns the number of patterns
func (q *Quantifier) GetNumPatterns() int {
return int(C.Z3_get_quantifier_num_patterns(q.ctx.ptr, q.ptr))
}
// GetPattern returns the pattern at the given index
func (q *Quantifier) GetPattern(idx int) *Pattern {
ptr := C.Z3_get_quantifier_pattern_ast(q.ctx.ptr, q.ptr, C.uint(idx))
return newPattern(q.ctx, ptr)
}
// GetNumNoPatterns returns the number of no-patterns
func (q *Quantifier) GetNumNoPatterns() int {
return int(C.Z3_get_quantifier_num_no_patterns(q.ctx.ptr, q.ptr))
}
// GetNoPattern returns the no-pattern at the given index
func (q *Quantifier) GetNoPattern(idx int) *Pattern {
ptr := C.Z3_get_quantifier_no_pattern_ast(q.ctx.ptr, q.ptr, C.uint(idx))
return newPattern(q.ctx, (C.Z3_pattern)(unsafe.Pointer(ptr)))
}
// GetNumBound returns the number of bound variables
func (q *Quantifier) GetNumBound() int {
return int(C.Z3_get_quantifier_num_bound(q.ctx.ptr, q.ptr))
}
// GetBoundName returns the name of the bound variable at the given index
func (q *Quantifier) GetBoundName(idx int) *Symbol {
ptr := C.Z3_get_quantifier_bound_name(q.ctx.ptr, q.ptr, C.uint(idx))
return newSymbol(q.ctx, ptr)
}
// GetBoundSort returns the sort of the bound variable at the given index
func (q *Quantifier) GetBoundSort(idx int) *Sort {
ptr := C.Z3_get_quantifier_bound_sort(q.ctx.ptr, q.ptr, C.uint(idx))
return newSort(q.ctx, ptr)
}
// GetBody returns the body of the quantifier
func (q *Quantifier) GetBody() *Expr {
ptr := C.Z3_get_quantifier_body(q.ctx.ptr, q.ptr)
return newExpr(q.ctx, ptr)
}
// String returns the string representation of the quantifier
func (q *Quantifier) String() string {
return q.AsExpr().String()
}
// MkQuantifier creates a quantifier with patterns
func (c *Context) MkQuantifier(isForall bool, weight int, sorts []*Sort, names []*Symbol, body *Expr, patterns []*Pattern) *Quantifier {
var forallInt C.bool
if isForall {
forallInt = true
} else {
forallInt = false
}
numBound := len(sorts)
if numBound != len(names) {
panic("Number of sorts must match number of names")
}
var cSorts []C.Z3_sort
var cNames []C.Z3_symbol
if numBound > 0 {
cSorts = make([]C.Z3_sort, numBound)
cNames = make([]C.Z3_symbol, numBound)
for i := 0; i < numBound; i++ {
cSorts[i] = sorts[i].ptr
cNames[i] = names[i].ptr
}
}
var cPatterns []C.Z3_pattern
var patternsPtr *C.Z3_pattern
numPatterns := len(patterns)
if numPatterns > 0 {
cPatterns = make([]C.Z3_pattern, numPatterns)
for i := 0; i < numPatterns; i++ {
cPatterns[i] = patterns[i].ptr
}
patternsPtr = &cPatterns[0]
}
var sortsPtr *C.Z3_sort
var namesPtr *C.Z3_symbol
if numBound > 0 {
sortsPtr = &cSorts[0]
namesPtr = &cNames[0]
}
ptr := C.Z3_mk_quantifier(c.ptr, forallInt, C.uint(weight), C.uint(numPatterns), patternsPtr,
C.uint(numBound), sortsPtr, namesPtr, body.ptr)
return newQuantifier(c, ptr)
}
// MkQuantifierConst creates a quantifier using constant bound variables
func (c *Context) MkQuantifierConst(isForall bool, weight int, bound []*Expr, body *Expr, patterns []*Pattern) *Quantifier {
var forallInt C.bool
if isForall {
forallInt = true
} else {
forallInt = false
}
numBound := len(bound)
var cBound []C.Z3_app
var boundPtr *C.Z3_app
if numBound > 0 {
cBound = make([]C.Z3_app, numBound)
for i := 0; i < numBound; i++ {
cBound[i] = (C.Z3_app)(unsafe.Pointer(bound[i].ptr))
}
boundPtr = &cBound[0]
}
var cPatterns []C.Z3_pattern
var patternsPtr *C.Z3_pattern
numPatterns := len(patterns)
if numPatterns > 0 {
cPatterns = make([]C.Z3_pattern, numPatterns)
for i := 0; i < numPatterns; i++ {
cPatterns[i] = patterns[i].ptr
}
patternsPtr = &cPatterns[0]
}
ptr := C.Z3_mk_quantifier_const(c.ptr, forallInt, C.uint(weight), C.uint(numBound), boundPtr,
C.uint(numPatterns), patternsPtr, body.ptr)
return newQuantifier(c, ptr)
}
// Lambda represents a lambda expression
type Lambda struct {
ctx *Context
ptr C.Z3_ast
}
// newLambda creates a new Lambda with proper memory management
func newLambda(ctx *Context, ptr C.Z3_ast) *Lambda {
l := &Lambda{ctx: ctx, ptr: ptr}
C.Z3_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(l, func(lam *Lambda) {
C.Z3_dec_ref(lam.ctx.ptr, lam.ptr)
})
return l
}
// AsExpr converts a Lambda to an Expr
func (l *Lambda) AsExpr() *Expr {
return newExpr(l.ctx, l.ptr)
}
// GetNumBound returns the number of bound variables
func (l *Lambda) GetNumBound() int {
return int(C.Z3_get_quantifier_num_bound(l.ctx.ptr, l.ptr))
}
// GetBoundName returns the name of the bound variable at the given index
func (l *Lambda) GetBoundName(idx int) *Symbol {
ptr := C.Z3_get_quantifier_bound_name(l.ctx.ptr, l.ptr, C.uint(idx))
return newSymbol(l.ctx, ptr)
}
// GetBoundSort returns the sort of the bound variable at the given index
func (l *Lambda) GetBoundSort(idx int) *Sort {
ptr := C.Z3_get_quantifier_bound_sort(l.ctx.ptr, l.ptr, C.uint(idx))
return newSort(l.ctx, ptr)
}
// GetBody returns the body of the lambda expression
func (l *Lambda) GetBody() *Expr {
ptr := C.Z3_get_quantifier_body(l.ctx.ptr, l.ptr)
return newExpr(l.ctx, ptr)
}
// String returns the string representation of the lambda
func (l *Lambda) String() string {
return l.AsExpr().String()
}
// MkLambda creates a lambda expression with sorts and names
func (c *Context) MkLambda(sorts []*Sort, names []*Symbol, body *Expr) *Lambda {
numBound := len(sorts)
if numBound != len(names) {
panic("Number of sorts must match number of names")
}
var cSorts []C.Z3_sort
var cNames []C.Z3_symbol
var sortsPtr *C.Z3_sort
var namesPtr *C.Z3_symbol
if numBound > 0 {
cSorts = make([]C.Z3_sort, numBound)
cNames = make([]C.Z3_symbol, numBound)
for i := 0; i < numBound; i++ {
cSorts[i] = sorts[i].ptr
cNames[i] = names[i].ptr
}
sortsPtr = &cSorts[0]
namesPtr = &cNames[0]
}
ptr := C.Z3_mk_lambda(c.ptr, C.uint(numBound), sortsPtr, namesPtr, body.ptr)
return newLambda(c, ptr)
}
// MkLambdaConst creates a lambda expression using constant bound variables
func (c *Context) MkLambdaConst(bound []*Expr, body *Expr) *Lambda {
numBound := len(bound)
var cBound []C.Z3_app
var boundPtr *C.Z3_app
if numBound > 0 {
cBound = make([]C.Z3_app, numBound)
for i := 0; i < numBound; i++ {
cBound[i] = (C.Z3_app)(unsafe.Pointer(bound[i].ptr))
}
boundPtr = &cBound[0]
}
ptr := C.Z3_mk_lambda_const(c.ptr, C.uint(numBound), boundPtr, body.ptr)
return newLambda(c, ptr)
}
// astVectorToExprs converts a Z3_ast_vector to a slice of Expr.
// This function properly manages the reference count of the vector by
// incrementing it on entry and decrementing it on exit.
// The individual AST elements are already reference counted by newExpr.
func astVectorToExprs(ctx *Context, vec C.Z3_ast_vector) []*Expr {
// Increment reference count for the vector since we're using it
C.Z3_ast_vector_inc_ref(ctx.ptr, vec)
defer C.Z3_ast_vector_dec_ref(ctx.ptr, vec)
size := uint(C.Z3_ast_vector_size(ctx.ptr, vec))
result := make([]*Expr, size)
for i := uint(0); i < size; i++ {
result[i] = newExpr(ctx, C.Z3_ast_vector_get(ctx.ptr, vec, C.uint(i)))
}
return result
}

View file

@ -48,8 +48,17 @@ target_include_directories(z3java PRIVATE
"${PROJECT_BINARY_DIR}/src/api"
${JNI_INCLUDE_DIRS}
)
# Add header padding for macOS to allow install_name_tool to modify the dylib
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_options(z3java PRIVATE "-Wl,-headerpad_max_install_names")
endif()
# FIXME: Should this library have SONAME and VERSION set?
# On macOS, add headerpad for install_name_tool compatibility
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_options(z3java PRIVATE "-Wl,-headerpad_max_install_names")
endif()
# This prevents CMake from automatically defining ``z3java_EXPORTS``
set_property(TARGET z3java PROPERTY DEFINE_SYMBOL "")
@ -115,6 +124,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES
FiniteDomainExpr.java
FiniteDomainNum.java
FiniteDomainSort.java
FiniteSetSort.java
Fixedpoint.java
FPExpr.java
FPNum.java
@ -158,6 +168,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES
Symbol.java
Tactic.java
TupleSort.java
TypeVarSort.java
UninterpretedSort.java
UserPropagatorBase.java
Version.java
@ -187,6 +198,8 @@ add_custom_target(build_z3_java_bindings
# TODO: Should we set ``CMAKE_JNI_TARGET`` to ``TRUE``?
# REMARK: removed VERSION to fix issue with using this to create installations.
set(CMAKE_JAVA_COMPILE_FLAGS -source 1.8 -target 1.8)
add_jar(z3JavaJar
SOURCES ${Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH}
OUTPUT_NAME ${Z3_JAVA_PACKAGE_NAME}

View file

@ -473,6 +473,88 @@ public class Context implements AutoCloseable {
return mkDatatypeSorts(mkSymbols(names), c);
}
/**
* Create a type variable for use in polymorphic functions and datatypes.
* Type variables can be used as sort parameters in polymorphic datatypes.
* @param name name of the type variable
* @return a new type variable sort
**/
public TypeVarSort mkTypeVariable(Symbol name)
{
checkContextMatch(name);
return new TypeVarSort(this, name);
}
/**
* Create a type variable for use in polymorphic functions and datatypes.
* Type variables can be used as sort parameters in polymorphic datatypes.
* @param name name of the type variable
* @return a new type variable sort
**/
public TypeVarSort mkTypeVariable(String name)
{
return mkTypeVariable(mkSymbol(name));
}
/**
* Create a polymorphic (parametric) datatype sort.
* A polymorphic datatype is parameterized by type variables, allowing it to
* work with different types. This is similar to generic types in programming languages.
*
* @param name name of the datatype sort
* @param parameters array of type variable sorts to parameterize the datatype
* @param constructors array of constructor specifications
* @return a new polymorphic datatype sort
*
* Example:
* <pre>
* // Create a polymorphic List datatype: List[T]
* TypeVarSort T = ctx.mkTypeVariable("T");
* Constructor&lt;Object&gt; nil = ctx.mkConstructor("nil", "is_nil", null, null, null);
* Constructor&lt;Object&gt; cons = ctx.mkConstructor("cons", "is_cons",
* new String[]{"head", "tail"},
* new Sort[]{T, null}, // head has type T, tail is recursive reference
* new int[]{0, 0}); // sortRef 0 refers back to List[T]
* DatatypeSort&lt;Object&gt; listSort = ctx.mkPolymorphicDatatypeSort("List",
* new Sort[]{T}, new Constructor[]{nil, cons});
* </pre>
**/
public <R> DatatypeSort<R> mkPolymorphicDatatypeSort(Symbol name, Sort[] parameters, Constructor<R>[] constructors)
{
checkContextMatch(name);
checkContextMatch(parameters);
checkContextMatch(constructors);
int numParams = parameters.length;
long[] paramsNative = AST.arrayToNative(parameters);
int numConstructors = constructors.length;
long[] constructorsNative = new long[numConstructors];
for (int i = 0; i < numConstructors; i++) {
constructorsNative[i] = constructors[i].getNativeObject();
}
long nativeSort = Native.mkPolymorphicDatatype(nCtx(), name.getNativeObject(),
numParams, paramsNative, numConstructors, constructorsNative);
return new DatatypeSort<>(this, nativeSort);
}
/**
* Create a polymorphic (parametric) datatype sort.
* A polymorphic datatype is parameterized by type variables, allowing it to
* work with different types. This is similar to generic types in programming languages.
*
* @param name name of the datatype sort
* @param parameters array of type variable sorts to parameterize the datatype
* @param constructors array of constructor specifications
* @return a new polymorphic datatype sort
**/
public <R> DatatypeSort<R> mkPolymorphicDatatypeSort(String name, Sort[] parameters, Constructor<R>[] constructors)
{
return mkPolymorphicDatatypeSort(mkSymbol(name), parameters, constructors);
}
/**
* Update a datatype field at expression t with value v.
* The function performs a record update at t. The field
@ -2052,6 +2134,145 @@ public class Context implements AutoCloseable {
}
/**
* Finite Sets
*/
/**
* Create a finite set sort over the given element sort.
**/
public final FiniteSetSort mkFiniteSetSort(Sort elemSort)
{
checkContextMatch(elemSort);
return new FiniteSetSort(this, elemSort);
}
/**
* Check if a sort is a finite set sort.
**/
public final boolean isFiniteSetSort(Sort s)
{
checkContextMatch(s);
return Native.isFiniteSetSort(nCtx(), s.getNativeObject());
}
/**
* Get the element sort (basis) of a finite set sort.
**/
public final Sort getFiniteSetSortBasis(Sort s)
{
checkContextMatch(s);
return Sort.create(this, Native.getFiniteSetSortBasis(nCtx(), s.getNativeObject()));
}
/**
* Create an empty finite set.
**/
public final Expr mkFiniteSetEmpty(Sort setSort)
{
checkContextMatch(setSort);
return Expr.create(this, Native.mkFiniteSetEmpty(nCtx(), setSort.getNativeObject()));
}
/**
* Create a singleton finite set.
**/
public final Expr mkFiniteSetSingleton(Expr elem)
{
checkContextMatch(elem);
return Expr.create(this, Native.mkFiniteSetSingleton(nCtx(), elem.getNativeObject()));
}
/**
* Create the union of two finite sets.
**/
public final Expr mkFiniteSetUnion(Expr s1, Expr s2)
{
checkContextMatch(s1);
checkContextMatch(s2);
return Expr.create(this, Native.mkFiniteSetUnion(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
}
/**
* Create the intersection of two finite sets.
**/
public final Expr mkFiniteSetIntersect(Expr s1, Expr s2)
{
checkContextMatch(s1);
checkContextMatch(s2);
return Expr.create(this, Native.mkFiniteSetIntersect(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
}
/**
* Create the difference of two finite sets.
**/
public final Expr mkFiniteSetDifference(Expr s1, Expr s2)
{
checkContextMatch(s1);
checkContextMatch(s2);
return Expr.create(this, Native.mkFiniteSetDifference(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
}
/**
* Check for membership in a finite set.
**/
public final BoolExpr mkFiniteSetMember(Expr elem, Expr set)
{
checkContextMatch(elem);
checkContextMatch(set);
return (BoolExpr) Expr.create(this, Native.mkFiniteSetMember(nCtx(), elem.getNativeObject(), set.getNativeObject()));
}
/**
* Get the cardinality of a finite set.
**/
public final Expr mkFiniteSetSize(Expr set)
{
checkContextMatch(set);
return Expr.create(this, Native.mkFiniteSetSize(nCtx(), set.getNativeObject()));
}
/**
* Check if one finite set is a subset of another.
**/
public final BoolExpr mkFiniteSetSubset(Expr s1, Expr s2)
{
checkContextMatch(s1);
checkContextMatch(s2);
return (BoolExpr) Expr.create(this, Native.mkFiniteSetSubset(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
}
/**
* Map a function over all elements in a finite set.
**/
public final Expr mkFiniteSetMap(Expr f, Expr set)
{
checkContextMatch(f);
checkContextMatch(set);
return Expr.create(this, Native.mkFiniteSetMap(nCtx(), f.getNativeObject(), set.getNativeObject()));
}
/**
* Filter a finite set with a predicate.
**/
public final Expr mkFiniteSetFilter(Expr f, Expr set)
{
checkContextMatch(f);
checkContextMatch(set);
return Expr.create(this, Native.mkFiniteSetFilter(nCtx(), f.getNativeObject(), set.getNativeObject()));
}
/**
* Create a finite set containing integers in the range [low, high].
**/
public final Expr mkFiniteSetRange(Expr low, Expr high)
{
checkContextMatch(low);
checkContextMatch(high);
return Expr.create(this, Native.mkFiniteSetRange(nCtx(), low.getNativeObject(), high.getNativeObject()));
}
/**
* Sequences, Strings and regular expressions.
*/
@ -2235,6 +2456,46 @@ public class Context implements AutoCloseable {
return (IntExpr)Expr.create(this, Native.mkSeqLastIndex(nCtx(), s.getNativeObject(), substr.getNativeObject()));
}
/**
* Map function f over sequence s.
* Returns a new sequence where f is applied to each element of s.
*/
public final <R extends Sort> SeqExpr<R> mkSeqMap(Expr<?> f, Expr<SeqSort<R>> s)
{
checkContextMatch(f, s);
return (SeqExpr<R>) Expr.create(this, Native.mkSeqMap(nCtx(), f.getNativeObject(), s.getNativeObject()));
}
/**
* Map function f over sequence s starting at index i.
* Returns a new sequence where f is applied to each element of s along with its index starting from i.
*/
public final <R extends Sort> SeqExpr<R> mkSeqMapi(Expr<?> f, Expr<IntSort> i, Expr<SeqSort<R>> s)
{
checkContextMatch(f, i, s);
return (SeqExpr<R>) Expr.create(this, Native.mkSeqMapi(nCtx(), f.getNativeObject(), i.getNativeObject(), s.getNativeObject()));
}
/**
* Left fold of function f over sequence s with accumulator a.
* Applies f to accumulate values from left to right over the sequence.
*/
public final <R extends Sort, A extends Sort> Expr<A> mkSeqFoldl(Expr<?> f, Expr<A> a, Expr<SeqSort<R>> s)
{
checkContextMatch(f, a, s);
return (Expr<A>) Expr.create(this, Native.mkSeqFoldl(nCtx(), f.getNativeObject(), a.getNativeObject(), s.getNativeObject()));
}
/**
* Left fold of function f over sequence s with accumulator a starting at index i.
* Applies f to accumulate values from left to right over the sequence, tracking the index starting from i.
*/
public final <R extends Sort, A extends Sort> Expr<A> mkSeqFoldli(Expr<?> f, Expr<IntSort> i, Expr<A> a, Expr<SeqSort<R>> s)
{
checkContextMatch(f, i, a, s);
return (Expr<A>) Expr.create(this, Native.mkSeqFoldli(nCtx(), f.getNativeObject(), i.getNativeObject(), a.getNativeObject(), s.getNativeObject()));
}
/**
* Replace the first occurrence of src by dst in s.
*/
@ -4291,7 +4552,7 @@ public class Context implements AutoCloseable {
}
/**
* Creates or a partial order.
* Creates a partial order.
* @param index The index of the order.
* @param sort The sort of the order.
*/
@ -4306,6 +4567,40 @@ public class Context implements AutoCloseable {
);
}
/**
* Create the transitive closure of a binary relation.
* The resulting relation is recursive.
* @param f function declaration of a binary relation
*/
public final <R extends Sort> FuncDecl<BoolSort> mkTransitiveClosure(FuncDecl<BoolSort> f) {
return (FuncDecl<BoolSort>) FuncDecl.create(
this,
Native.mkTransitiveClosure(
nCtx(),
f.getNativeObject()
)
);
}
/**
* Return the nonzero subresultants of p and q with respect to the "variable" x.
* Note that any subterm that cannot be viewed as a polynomial is assumed to be a variable.
* @param p arithmetic term
* @param q arithmetic term
* @param x variable
*/
public final <R extends Sort> ASTVector polynomialSubresultants(Expr<R> p, Expr<R> q, Expr<R> x) {
return new ASTVector(
this,
Native.polynomialSubresultants(
nCtx(),
p.getNativeObject(),
q.getNativeObject(),
x.getNativeObject()
)
);
}
/**
* Wraps an AST.
* Remarks: This function is used for transitions between
@ -4396,6 +4691,14 @@ public class Context implements AutoCloseable {
checkContextMatch(other3);
}
void checkContextMatch(Z3Object other1, Z3Object other2, Z3Object other3, Z3Object other4)
{
checkContextMatch(other1);
checkContextMatch(other2);
checkContextMatch(other3);
checkContextMatch(other4);
}
void checkContextMatch(Z3Object[] arr)
{
if (arr != null)

View file

@ -188,6 +188,30 @@ public class Expr<R extends Sort> extends AST
getNativeObject(), to.length, Expr.arrayToNative(to)));
}
/**
* Substitute functions in {@code from} with the expressions in {@code to}.
* The expressions in {@code to} can have free variables. The free variable
* in {@code to[i]} at de-Bruijn index 0 refers to the first argument of
* {@code from[i]}, the free variable at index 1 corresponds to the second
* argument, and so on.
* Remarks: The arrays {@code from} and {@code to} must have the same size.
* @param from Array of function declarations to be substituted
* @param to Array of expressions to substitute with
* @throws Z3Exception on error
* @return an Expr
**/
public Expr<R> substituteFuns(FuncDecl<?>[] from, Expr<?>[] to)
{
getContext().checkContextMatch(from);
getContext().checkContextMatch(to);
if (from.length != to.length) {
throw new Z3Exception("Arrays 'from' and 'to' must have the same length");
}
return (Expr<R>) Expr.create(getContext(), Native.substituteFuns(getContext().nCtx(),
getNativeObject(), from.length, AST.arrayToNative(from),
Expr.arrayToNative(to)));
}
/**
* Translates (copies) the term to the Context {@code ctx}.
*

View file

@ -0,0 +1,42 @@
/**
Copyright (c) 2024 Microsoft Corporation
Module Name:
FiniteSetSort.java
Abstract:
Author:
GitHub Copilot
Notes:
**/
package com.microsoft.z3;
/**
* Finite set sorts.
**/
public class FiniteSetSort extends Sort
{
FiniteSetSort(Context ctx, long obj)
{
super(ctx, obj);
}
FiniteSetSort(Context ctx, Sort elemSort)
{
super(ctx, Native.mkFiniteSetSort(ctx.nCtx(), elemSort.getNativeObject()));
}
/**
* Get the element sort (basis) of this finite set sort.
**/
public Sort getBasis()
{
return Sort.create(getContext(), Native.getFiniteSetSortBasis(getContext().nCtx(), getNativeObject()));
}
}

374
src/api/java/RCFNum.java Normal file
View file

@ -0,0 +1,374 @@
/**
Copyright (c) 2024 Microsoft Corporation
Module Name:
RCFNum.java
Abstract:
Real Closed Field (RCF) numerals
Author:
GitHub Copilot 2024-01-12
Notes:
**/
package com.microsoft.z3;
/**
* Real Closed Field (RCF) numerals.
*
* RCF numerals can represent:
* - Rational numbers
* - Algebraic numbers (roots of polynomials)
* - Transcendental extensions (e.g., pi, e)
* - Infinitesimal extensions
**/
public class RCFNum extends Z3Object {
/**
* Create an RCF numeral from a rational string.
* @param ctx Z3 context
* @param value String representation of a rational number (e.g., "3/2", "0.5", "42")
* @throws Z3Exception on error
**/
public RCFNum(Context ctx, String value) {
super(ctx, Native.rcfMkRational(ctx.nCtx(), value));
}
/**
* Create an RCF numeral from a small integer.
* @param ctx Z3 context
* @param value Integer value
* @throws Z3Exception on error
**/
public RCFNum(Context ctx, int value) {
super(ctx, Native.rcfMkSmallInt(ctx.nCtx(), value));
}
/**
* Internal constructor for wrapping native RCF numeral pointers.
**/
RCFNum(Context ctx, long obj) {
super(ctx, obj);
}
/**
* Create an RCF numeral representing pi.
* @param ctx Z3 context
* @return RCF numeral for pi
* @throws Z3Exception on error
**/
public static RCFNum mkPi(Context ctx) {
return new RCFNum(ctx, Native.rcfMkPi(ctx.nCtx()));
}
/**
* Create an RCF numeral representing e (Euler's constant).
* @param ctx Z3 context
* @return RCF numeral for e
* @throws Z3Exception on error
**/
public static RCFNum mkE(Context ctx) {
return new RCFNum(ctx, Native.rcfMkE(ctx.nCtx()));
}
/**
* Create an RCF numeral representing an infinitesimal.
* @param ctx Z3 context
* @return RCF numeral for an infinitesimal
* @throws Z3Exception on error
**/
public static RCFNum mkInfinitesimal(Context ctx) {
return new RCFNum(ctx, Native.rcfMkInfinitesimal(ctx.nCtx()));
}
/**
* Find roots of a polynomial.
*
* The polynomial is a[n-1]*x^(n-1) + ... + a[1]*x + a[0].
*
* @param ctx Z3 context
* @param coefficients Polynomial coefficients (constant term first)
* @return Array of RCF numerals representing the roots
* @throws Z3Exception on error
**/
public static RCFNum[] mkRoots(Context ctx, RCFNum[] coefficients) {
if (coefficients == null || coefficients.length == 0) {
throw new Z3Exception("Polynomial coefficients cannot be empty");
}
int n = coefficients.length;
long[] a = new long[n];
long[] roots = new long[n];
for (int i = 0; i < n; i++) {
a[i] = coefficients[i].getNativeObject();
}
int numRoots = Native.rcfMkRoots(ctx.nCtx(), n, a, roots);
RCFNum[] result = new RCFNum[numRoots];
for (int i = 0; i < numRoots; i++) {
result[i] = new RCFNum(ctx, roots[i]);
}
return result;
}
/**
* Add two RCF numerals.
* @param other The RCF numeral to add
* @return this + other
* @throws Z3Exception on error
**/
public RCFNum add(RCFNum other) {
checkContext(other);
return new RCFNum(getContext(), Native.rcfAdd(getContext().nCtx(),
getNativeObject(),
other.getNativeObject()));
}
/**
* Subtract two RCF numerals.
* @param other The RCF numeral to subtract
* @return this - other
* @throws Z3Exception on error
**/
public RCFNum sub(RCFNum other) {
checkContext(other);
return new RCFNum(getContext(), Native.rcfSub(getContext().nCtx(),
getNativeObject(),
other.getNativeObject()));
}
/**
* Multiply two RCF numerals.
* @param other The RCF numeral to multiply
* @return this * other
* @throws Z3Exception on error
**/
public RCFNum mul(RCFNum other) {
checkContext(other);
return new RCFNum(getContext(), Native.rcfMul(getContext().nCtx(),
getNativeObject(),
other.getNativeObject()));
}
/**
* Divide two RCF numerals.
* @param other The RCF numeral to divide by
* @return this / other
* @throws Z3Exception on error
**/
public RCFNum div(RCFNum other) {
checkContext(other);
return new RCFNum(getContext(), Native.rcfDiv(getContext().nCtx(),
getNativeObject(),
other.getNativeObject()));
}
/**
* Negate this RCF numeral.
* @return -this
* @throws Z3Exception on error
**/
public RCFNum neg() {
return new RCFNum(getContext(), Native.rcfNeg(getContext().nCtx(),
getNativeObject()));
}
/**
* Compute the multiplicative inverse.
* @return 1/this
* @throws Z3Exception on error
**/
public RCFNum inv() {
return new RCFNum(getContext(), Native.rcfInv(getContext().nCtx(),
getNativeObject()));
}
/**
* Raise this RCF numeral to a power.
* @param k The exponent
* @return this^k
* @throws Z3Exception on error
**/
public RCFNum power(int k) {
return new RCFNum(getContext(), Native.rcfPower(getContext().nCtx(),
getNativeObject(), k));
}
/**
* Check if this RCF numeral is less than another.
* @param other The RCF numeral to compare with
* @return true if this < other
* @throws Z3Exception on error
**/
public boolean lt(RCFNum other) {
checkContext(other);
return Native.rcfLt(getContext().nCtx(), getNativeObject(),
other.getNativeObject());
}
/**
* Check if this RCF numeral is greater than another.
* @param other The RCF numeral to compare with
* @return true if this > other
* @throws Z3Exception on error
**/
public boolean gt(RCFNum other) {
checkContext(other);
return Native.rcfGt(getContext().nCtx(), getNativeObject(),
other.getNativeObject());
}
/**
* Check if this RCF numeral is less than or equal to another.
* @param other The RCF numeral to compare with
* @return true if this <= other
* @throws Z3Exception on error
**/
public boolean le(RCFNum other) {
checkContext(other);
return Native.rcfLe(getContext().nCtx(), getNativeObject(),
other.getNativeObject());
}
/**
* Check if this RCF numeral is greater than or equal to another.
* @param other The RCF numeral to compare with
* @return true if this >= other
* @throws Z3Exception on error
**/
public boolean ge(RCFNum other) {
checkContext(other);
return Native.rcfGe(getContext().nCtx(), getNativeObject(),
other.getNativeObject());
}
/**
* Check if this RCF numeral is equal to another.
* @param other The RCF numeral to compare with
* @return true if this == other
* @throws Z3Exception on error
**/
public boolean eq(RCFNum other) {
checkContext(other);
return Native.rcfEq(getContext().nCtx(), getNativeObject(),
other.getNativeObject());
}
/**
* Check if this RCF numeral is not equal to another.
* @param other The RCF numeral to compare with
* @return true if this != other
* @throws Z3Exception on error
**/
public boolean neq(RCFNum other) {
checkContext(other);
return Native.rcfNeq(getContext().nCtx(), getNativeObject(),
other.getNativeObject());
}
/**
* Check if this RCF numeral is a rational number.
* @return true if this is rational
* @throws Z3Exception on error
**/
public boolean isRational() {
return Native.rcfIsRational(getContext().nCtx(), getNativeObject());
}
/**
* Check if this RCF numeral is an algebraic number.
* @return true if this is algebraic
* @throws Z3Exception on error
**/
public boolean isAlgebraic() {
return Native.rcfIsAlgebraic(getContext().nCtx(), getNativeObject());
}
/**
* Check if this RCF numeral is an infinitesimal.
* @return true if this is infinitesimal
* @throws Z3Exception on error
**/
public boolean isInfinitesimal() {
return Native.rcfIsInfinitesimal(getContext().nCtx(), getNativeObject());
}
/**
* Check if this RCF numeral is a transcendental number.
* @return true if this is transcendental
* @throws Z3Exception on error
**/
public boolean isTranscendental() {
return Native.rcfIsTranscendental(getContext().nCtx(), getNativeObject());
}
/**
* Convert this RCF numeral to a string.
* @param compact If true, use compact representation
* @return String representation
* @throws Z3Exception on error
**/
public String toString(boolean compact) {
return Native.rcfNumToString(getContext().nCtx(), getNativeObject(),
compact, false);
}
/**
* Convert this RCF numeral to a string (non-compact).
* @return String representation
* @throws Z3Exception on error
**/
@Override
public String toString() {
return toString(false);
}
/**
* Convert this RCF numeral to a decimal string.
* @param precision Number of decimal places
* @return Decimal string representation
* @throws Z3Exception on error
**/
public String toDecimal(int precision) {
return Native.rcfNumToDecimalString(getContext().nCtx(),
getNativeObject(), precision);
}
@Override
void incRef() {
// RCF numerals don't use standard reference counting
// They are managed through Z3_rcf_del
}
@Override
void addToReferenceQueue() {
getContext().getReferenceQueue().storeReference(this, RCFNumRef::new);
}
private static class RCFNumRef extends Z3ReferenceQueue.Reference<RCFNum> {
private RCFNumRef(RCFNum referent, java.lang.ref.ReferenceQueue<Z3Object> q) {
super(referent, q);
}
@Override
void decRef(Context ctx, long z3Obj) {
Native.rcfDel(ctx.nCtx(), z3Obj);
}
}
private void checkContext(RCFNum other) {
if (getContext() != other.getContext()) {
throw new Z3Exception("RCF numerals from different contexts");
}
}
}

View file

@ -4,3 +4,10 @@ Java bindings
The Java bindings will be included in the Z3 build if it is configured with
the option --java to python scripts/mk_make.py. This will produce the
com.microsoft.z3.jar package in the build directory.
## IDE Setup Guide
For comprehensive instructions on using Z3 Java bindings in IDEs (Eclipse,
IntelliJ IDEA, Visual Studio Code), including troubleshooting common issues
like ClassNotFoundException, see:
../../../doc/JAVA_IDE_SETUP.md

View file

@ -338,6 +338,48 @@ public class Solver extends Z3Object {
return core.ToBoolExprArray();
}
/**
* Retrieve currently inferred units.
* Remarks: This retrieves the set of literals that the solver has inferred
* at the current decision level after a {@code check} call.
*
* @return An array of Boolean expressions representing the inferred units
* @throws Z3Exception
**/
public BoolExpr[] getUnits()
{
ASTVector units = new ASTVector(getContext(), Native.solverGetUnits(getContext().nCtx(), getNativeObject()));
return units.ToBoolExprArray();
}
/**
* Retrieve non-unit atomic formulas in the solver state.
* Remarks: This retrieves atomic formulas that are not units after a {@code check} call.
*
* @return An array of Boolean expressions representing the non-unit formulas
* @throws Z3Exception
**/
public BoolExpr[] getNonUnits()
{
ASTVector nonUnits = new ASTVector(getContext(), Native.solverGetNonUnits(getContext().nCtx(), getNativeObject()));
return nonUnits.ToBoolExprArray();
}
/**
* Retrieve the solver decision trail.
* Remarks: This retrieves the trail of decisions made by the solver after a {@code check} call.
* The trail represents the sequence of Boolean literals (decisions and propagations) in the order
* they were assigned.
*
* @return An array of Boolean expressions representing the decision trail
* @throws Z3Exception
**/
public BoolExpr[] getTrail()
{
ASTVector trail = new ASTVector(getContext(), Native.solverGetTrail(getContext().nCtx(), getNativeObject()));
return trail.ToBoolExprArray();
}
/**
* A brief justification of why the last call to {@code Check} returned
* {@code UNKNOWN}.
@ -348,6 +390,96 @@ public class Solver extends Z3Object {
getNativeObject());
}
/**
* Retrieve the decision levels for each literal in the solver's trail after a {@code check} call.
* The trail contains Boolean literals (decisions and propagations) in the order they were assigned.
* The returned array has one entry per trail literal, indicating at which decision level it was assigned.
* Use {@link #getTrail()} to retrieve the trail literals themselves.
*
* @return An array where element i contains the decision level for the i-th trail literal
* @throws Z3Exception
**/
public int[] getTrailLevels()
{
ASTVector trailVector = new ASTVector(getContext(), Native.solverGetTrail(getContext().nCtx(), getNativeObject()));
int[] levels = new int[trailVector.size()];
Native.solverGetLevels(getContext().nCtx(), getNativeObject(), trailVector.getNativeObject(), trailVector.size(), levels);
return levels;
}
/**
* Return a sequence of cubes (conjunctions of literals) for partitioning the search space.
* Each cube represents a partial assignment that can be used as a starting point for parallel solving.
* This is primarily useful for cube-and-conquer parallel SAT solving strategies, where different
* cubes can be solved independently by different workers.
* The iterator yields cubes until the search space is exhausted.
*
* @param vars Optional array of variables to use as initial cube variables. If null, solver decides.
* @param cutoff Backtrack level cutoff for cube generation
* @return Iterator over arrays of Boolean expressions representing cubes
* @throws Z3Exception
**/
public java.util.Iterator<BoolExpr[]> cube(Expr<?>[] vars, int cutoff)
{
ASTVector cubeVars = new ASTVector(getContext());
if (vars != null) {
for (Expr<?> v : vars) {
cubeVars.push(v);
}
}
return new java.util.Iterator<BoolExpr[]>() {
private BoolExpr[] nextCube = computeNext();
private BoolExpr[] computeNext() {
ASTVector result = new ASTVector(getContext(),
Native.solverCube(getContext().nCtx(), getNativeObject(),
cubeVars.getNativeObject(), cutoff));
BoolExpr[] cube = result.ToBoolExprArray();
// Check for termination conditions
if (cube.length == 1 && cube[0].isFalse()) {
return null; // No more cubes
}
if (cube.length == 0) {
return null; // Search space exhausted
}
return cube;
}
@Override
public boolean hasNext() {
return nextCube != null;
}
@Override
public BoolExpr[] next() {
if (nextCube == null) {
throw new java.util.NoSuchElementException();
}
BoolExpr[] current = nextCube;
nextCube = computeNext();
return current;
}
};
}
/**
* Set an initial value for a variable to guide the solver's search heuristics.
* This can improve performance when good initial values are known for the problem domain.
*
* @param var The variable to set an initial value for
* @param value The initial value for the variable
* @throws Z3Exception
**/
public void setInitialValue(Expr<?> var, Expr<?> value)
{
getContext().checkContextMatch(var);
getContext().checkContextMatch(value);
Native.solverSetInitialValue(getContext().nCtx(), getNativeObject(),
var.getNativeObject(), value.getNativeObject());
}
/**
* Create a clone of the current solver with respect to{@code ctx}.
*/

View file

@ -0,0 +1,27 @@
/**
Copyright (c) 2024 Microsoft Corporation
Module Name:
TypeVarSort.java
Abstract:
Author:
GitHub Copilot 2024-01-30
Notes:
**/
package com.microsoft.z3;
/**
* A type variable sort for use in polymorphic functions and datatypes.
**/
public class TypeVarSort extends Sort
{
TypeVarSort(Context ctx, long obj) { super(ctx, obj); }
TypeVarSort(Context ctx, Symbol s) { super(ctx, Native.mkTypeVariable(ctx.nCtx(), s.getNativeObject())); }
}

View file

@ -0,0 +1,335 @@
# TypeScript API Enhancements - Simplifier, Params, and ParamDescrs
This document describes the new APIs added to the Z3 TypeScript bindings to bring them to feature parity with Python, Java, C++, and C# bindings.
## Overview
Three new high-level APIs have been added:
1. **Params** - Parameter configuration objects
2. **ParamDescrs** - Parameter introspection and documentation
3. **Simplifier** - Modern preprocessing for incremental solving (Z3 4.12+)
These APIs address the gaps identified in [GitHub Discussion #8145](https://github.com/Z3Prover/z3/discussions/8145).
## Params API
The `Params` class allows you to create reusable parameter configuration objects that can be passed to tactics, simplifiers, and solvers.
### Features
- Create parameter objects with typed values (boolean, number, string)
- Pass to tactics via `tactic.usingParams(params)`
- Pass to simplifiers via `simplifier.usingParams(params)`
- Validate against parameter descriptions
- Convert to string for debugging
### Example
```typescript
const { Params, Tactic } = Context('main');
// Create a parameter object
const params = new Params();
params.set('elim_and', true);
params.set('max_steps', 1000);
params.set('timeout', 5.0);
params.set('logic', 'QF_LIA');
// Use with a tactic
const tactic = new Tactic('simplify');
const configuredTactic = tactic.usingParams(params);
// Validate parameters
const paramDescrs = tactic.paramDescrs();
params.validate(paramDescrs); // Throws if invalid
// Debug output
console.log(params.toString());
```
### API Reference
```typescript
class Params {
/**
* Set a parameter with the given name and value.
* @param name - Parameter name
* @param value - Parameter value (boolean, number, or string)
*/
set(name: string, value: boolean | number | string): void;
/**
* Validate the parameter set against a parameter description set.
* @param descrs - Parameter descriptions to validate against
*/
validate(descrs: ParamDescrs): void;
/**
* Convert the parameter set to a string representation.
*/
toString(): string;
}
```
## ParamDescrs API
The `ParamDescrs` class provides runtime introspection of available parameters for tactics, simplifiers, and solvers.
### Features
- Query available parameters
- Get parameter types
- Access parameter documentation
- Validate parameter configurations
### Example
```typescript
const { Simplifier } = Context('main');
// Get parameter descriptions
const simplifier = new Simplifier('solve-eqs');
const paramDescrs = simplifier.paramDescrs();
// Introspect parameters
const size = paramDescrs.size();
console.log(`Number of parameters: ${size}`);
for (let i = 0; i < size; i++) {
const name = paramDescrs.getName(i);
const kind = paramDescrs.getKind(name);
const doc = paramDescrs.getDocumentation(name);
console.log(`${name}: ${doc}`);
}
// Get all as string
console.log(paramDescrs.toString());
```
### API Reference
```typescript
class ParamDescrs {
/**
* Return the number of parameters in the description set.
*/
size(): number;
/**
* Return the name of the parameter at the given index.
* @param i - Index of the parameter
*/
getName(i: number): string;
/**
* Return the kind (type) of the parameter with the given name.
* @param name - Parameter name
*/
getKind(name: string): number;
/**
* Return the documentation string for the parameter with the given name.
* @param name - Parameter name
*/
getDocumentation(name: string): string;
/**
* Convert the parameter description set to a string representation.
*/
toString(): string;
}
```
## Simplifier API
The `Simplifier` class provides modern preprocessing capabilities for incremental solving, introduced in Z3 4.12. Simplifiers are more efficient than tactics for incremental solving and can be attached directly to solvers.
### Features
- Create simplifiers by name
- Compose simplifiers with `andThen()`
- Configure with parameters using `usingParams()`
- Attach to solvers for incremental preprocessing
- Get help and parameter documentation
### Example
```typescript
const { Simplifier, Solver, Params, Int } = Context('main');
// Create a simplifier
const simplifier = new Simplifier('solve-eqs');
// Get help
console.log(simplifier.help());
// Configure with parameters
const params = new Params();
params.set('som', true);
const configured = simplifier.usingParams(params);
// Compose simplifiers
const s1 = new Simplifier('solve-eqs');
const s2 = new Simplifier('simplify');
const composed = s1.andThen(s2);
// Attach to solver
const solver = new Solver();
solver.addSimplifier(composed);
// Use the solver normally
const x = Int.const('x');
const y = Int.const('y');
solver.add(x.eq(y.add(1)));
solver.add(y.eq(5));
const result = await solver.check(); // 'sat'
if (result === 'sat') {
const model = solver.model();
console.log('x =', model.eval(x).toString()); // 6
}
```
### API Reference
```typescript
class Simplifier {
/**
* Create a simplifier by name.
* @param name - Built-in simplifier name (e.g., 'solve-eqs', 'simplify')
*/
constructor(name: string);
/**
* Return a string containing a description of parameters accepted by this simplifier.
*/
help(): string;
/**
* Return the parameter description set for this simplifier.
*/
paramDescrs(): ParamDescrs;
/**
* Return a simplifier that uses the given configuration parameters.
* @param params - Parameters to configure the simplifier
*/
usingParams(params: Params): Simplifier;
/**
* Return a simplifier that applies this simplifier and then another simplifier.
* @param other - The simplifier to apply after this one
*/
andThen(other: Simplifier): Simplifier;
}
```
### Solver Integration
The `Solver` class has been extended with a new method:
```typescript
class Solver {
/**
* Attach a simplifier to the solver for incremental pre-processing.
* The solver will use the simplifier for incremental pre-processing of assertions.
* @param simplifier - The simplifier to attach
*/
addSimplifier(simplifier: Simplifier): void;
}
```
## Tactic Enhancement
The `Tactic` class has been enhanced with parameter configuration:
```typescript
class Tactic {
/**
* Return a tactic that uses the given configuration parameters.
* @param params - Parameters to configure the tactic
*/
usingParams(params: Params): Tactic;
/**
* Get parameter descriptions for the tactic.
* Returns a ParamDescrs object for introspecting available parameters.
*/
paramDescrs(): ParamDescrs;
}
```
### Example
```typescript
const { Tactic, Params } = Context('main');
const tactic = new Tactic('simplify');
const params = new Params();
params.set('max_steps', 100);
const configured = tactic.usingParams(params);
```
## Available Simplifiers
Common built-in simplifiers include:
- `'solve-eqs'` - Solve for variables
- `'simplify'` - General simplification
- `'propagate-values'` - Propagate constant values
- `'elim-uncnstr'` - Eliminate unconstrained variables
- `'ctx-simplify'` - Context-dependent simplification
Use `simplifier.help()` to see documentation for each simplifier and its parameters.
## Migration Guide
### Before (using global setParam)
```typescript
// Global parameter setting
setParam('pp.decimal', true);
// No way to create reusable parameter configurations
// No simplifier support
```
### After (using Params and Simplifier)
```typescript
// Reusable parameter objects
const params = new Params();
params.set('pp.decimal', true);
params.set('max_steps', 1000);
// Configure tactics
const tactic = new Tactic('simplify').usingParams(params);
// Use simplifiers for better incremental solving
const simplifier = new Simplifier('solve-eqs').usingParams(params);
solver.addSimplifier(simplifier);
```
## Compatibility
These APIs match the functionality available in:
- ✅ Python (`ParamsRef`, `ParamDescrsRef`, `Simplifier`)
- ✅ Java (`Params`, `ParamDescrs`, `Simplifier`)
- ✅ C# (`Params`, `ParamDescrs`, `Simplifier`)
- ✅ C++ (`params`, `param_descrs`, `simplifier`)
The TypeScript API now has 100% coverage of the Params, ParamDescrs, and Simplifier C APIs.
## Examples
See [simplifier-example.ts](./examples/high-level/simplifier-example.ts) for a complete working example demonstrating all features.
## Further Reading
- [Z3 Guide - Parameters](https://z3prover.github.io/api/html/group__capi.html#ga3e04c0bc49ffc0e8c9c6c1e72e6e44b1)
- [Z3 Guide - Simplifiers](https://z3prover.github.io/api/html/group__capi.html#ga1a6e5b5a0c6c6f1c6e9e9e9f6e8e8e8e)
- [Z3 Guide - Tactics](https://z3prover.github.io/api/html/group__capi.html#ga9f7f1d1f1f1f1f1f1f1f1f1f1f1f1f1f)

View file

@ -0,0 +1,199 @@
/**
* Example demonstrating the RCF (Real Closed Field) API in TypeScript.
*
* This example shows how to use RCF numerals to work with:
* - Transcendental numbers (pi, e)
* - Algebraic numbers (roots of polynomials)
* - Infinitesimals
* - Exact real arithmetic
*
* Note: This example uses the high-level API for a cleaner interface.
*/
import { init } from 'z3-solver';
async function rcfBasicExample() {
console.log('RCF Basic Example (High-Level API)');
console.log('===================================');
const { Context } = await init();
const { RCFNum } = Context('main');
// Create pi and e
const pi = RCFNum.pi();
const e = RCFNum.e();
console.log('pi =', pi.toString());
console.log('e =', e.toString());
// Arithmetic operations
const sum = pi.add(e);
const prod = pi.mul(e);
console.log('pi + e =', sum.toString());
console.log('pi * e =', prod.toString());
// Decimal approximations
console.log('pi (10 decimals) =', pi.toDecimal(10));
console.log('e (10 decimals) =', e.toDecimal(10));
// Comparisons
console.log('pi < e?', pi.lt(e) ? 'yes' : 'no');
console.log('pi > e?', pi.gt(e) ? 'yes' : 'no');
}
async function rcfRationalExample() {
console.log('\nRCF Rational Example (High-Level API)');
console.log('=====================================');
const { Context } = await init();
const { RCFNum } = Context('main');
// Create rational numbers
const half = RCFNum('1/2');
const third = RCFNum('1/3');
console.log('1/2 =', half.toString());
console.log('1/3 =', third.toString());
// Arithmetic
const sum = half.add(third);
console.log('1/2 + 1/3 =', sum.toString());
console.log('1/2 + 1/3 (decimal) =', sum.toDecimal(10));
// Type queries
console.log('Is 1/2 rational?', half.isRational() ? 'yes' : 'no');
console.log('Is 1/2 algebraic?', half.isAlgebraic() ? 'yes' : 'no');
}
async function rcfRootsExample() {
console.log('\nRCF Roots Example (High-Level API)');
console.log('===================================');
const { Context } = await init();
const { RCFNum } = Context('main');
// Find roots of x^2 - 2 = 0
// Polynomial: -2 + 0*x + 1*x^2
const coeffs = [
RCFNum(-2), // constant term
RCFNum(0), // x coefficient
RCFNum(1), // x^2 coefficient
];
const roots = RCFNum.roots(coeffs);
console.log('Roots of x^2 - 2 = 0:');
for (let i = 0; i < roots.length; i++) {
console.log(` root[${i}] =`, roots[i].toString());
console.log(` decimal =`, roots[i].toDecimal(15));
console.log(` is_algebraic =`, roots[i].isAlgebraic() ? 'yes' : 'no');
}
}
async function rcfInfinitesimalExample() {
console.log('\nRCF Infinitesimal Example (High-Level API)');
console.log('===========================================');
const { Context } = await init();
const { RCFNum } = Context('main');
// Create an infinitesimal
const eps = RCFNum.infinitesimal();
console.log('eps =', eps.toString());
console.log('Is eps infinitesimal?', eps.isInfinitesimal() ? 'yes' : 'no');
// Infinitesimals are smaller than any positive real number
const tiny = RCFNum('1/1000000000');
console.log('eps < 1/1000000000?', eps.lt(tiny) ? 'yes' : 'no');
}
async function rcfArithmeticExample() {
console.log('\nRCF Arithmetic Operations Example');
console.log('==================================');
const { Context } = await init();
const { RCFNum } = Context('main');
const a = RCFNum(5);
const b = RCFNum(3);
console.log('a =', a.toString());
console.log('b =', b.toString());
console.log('a + b =', a.add(b).toString());
console.log('a - b =', a.sub(b).toString());
console.log('a * b =', a.mul(b).toString());
console.log('a / b =', a.div(b).toString(), '=', a.div(b).toDecimal(5));
console.log('-a =', a.neg().toString());
console.log('1/a =', a.inv().toString(), '=', a.inv().toDecimal(5));
console.log('a^3 =', a.power(3).toString());
// Comparisons
console.log('\nComparisons:');
console.log('a < b?', a.lt(b));
console.log('a > b?', a.gt(b));
console.log('a <= b?', a.le(b));
console.log('a >= b?', a.ge(b));
console.log('a == b?', a.eq(b));
console.log('a != b?', a.neq(b));
}
async function rcfSymbolicMathExample() {
console.log('\nRCF Symbolic Mathematics Example');
console.log('=================================');
const { Context } = await init();
const { RCFNum } = Context('main');
// Work with exact symbolic values
const pi = RCFNum.pi();
const e = RCFNum.e();
const sqrt2Coeffs = [RCFNum(-2), RCFNum(0), RCFNum(1)];
const sqrt2Roots = RCFNum.roots(sqrt2Coeffs);
const sqrt2 = sqrt2Roots.find(r => r.gt(RCFNum(0)))!;
console.log('π =', pi.toDecimal(15));
console.log('e =', e.toDecimal(15));
console.log('√2 =', sqrt2.toDecimal(15));
// Combine them
const expr1 = pi.add(e);
const expr2 = pi.mul(sqrt2);
const expr3 = e.power(2);
console.log('\nExpressions:');
console.log('π + e =', expr1.toDecimal(10));
console.log(× √2 =', expr2.toDecimal(10));
console.log('e² =', expr3.toDecimal(10));
// Check properties
console.log('\nProperties:');
console.log('π is transcendental:', pi.isTranscendental());
console.log('e is transcendental:', e.isTranscendental());
console.log('√2 is algebraic:', sqrt2.isAlgebraic());
console.log('√2 is rational:', sqrt2.isRational());
}
async function main() {
try {
await rcfBasicExample();
await rcfRationalExample();
await rcfRootsExample();
await rcfInfinitesimalExample();
await rcfArithmeticExample();
await rcfSymbolicMathExample();
console.log('\n✓ All RCF examples completed successfully!');
console.log('\nThe RCF API in TypeScript now provides:');
console.log(' • 38 functions for exact real arithmetic');
console.log(' • Support for π, e, algebraic numbers, and infinitesimals');
console.log(' • Full arithmetic and comparison operations');
console.log(' • Polynomial root finding');
console.log(' • Type predicates and conversions');
} catch (error) {
console.error('Error:', error);
throw error;
}
}
main();

View file

@ -0,0 +1,88 @@
/**
* Example demonstrating the new Simplifier, Params, and ParamDescrs APIs
*
* This example shows how to:
* 1. Create and configure parameter objects
* 2. Use simplifiers for preprocessing
* 3. Compose simplifiers
* 4. Introspect parameter descriptions
*/
import { init } from '../../build/node';
(async () => {
const { Context } = await init();
const { Int, Solver, Simplifier, Params } = Context('main');
// Example 1: Using Params to configure tactics
console.log('Example 1: Creating and using Params');
const params = new Params();
params.set('elim_and', true);
params.set('max_steps', 1000);
params.set('timeout', 5.0);
console.log('Params:', params.toString());
// Example 2: Creating and using a Simplifier
console.log('\nExample 2: Using a Simplifier');
const simplifier = new Simplifier('solve-eqs');
console.log('Simplifier help:', simplifier.help());
// Example 3: Composing simplifiers
console.log('\nExample 3: Composing simplifiers');
const s1 = new Simplifier('solve-eqs');
const s2 = new Simplifier('simplify');
const composed = s1.andThen(s2);
console.log('Composed simplifier created');
// Example 4: Using simplifier with parameters
console.log('\nExample 4: Configuring simplifier with parameters');
const configParams = new Params();
configParams.set('ite_solver', false);
const configuredSimplifier = simplifier.usingParams(configParams);
console.log('Configured simplifier created');
// Example 4b: Configuring tactic with parameters
console.log('\nExample 4b: Configuring tactic with parameters');
const { Tactic } = Context('main');
const tactic = new Tactic('simplify');
const tacticParams = new Params();
tacticParams.set('max_steps', 1000);
const configuredTactic = tactic.usingParams(tacticParams);
console.log('Configured tactic created');
// Example 5: Adding simplifier to solver
console.log('\nExample 5: Using simplifier with solver');
const solver = new Solver();
solver.addSimplifier(s1);
const x = Int.const('x');
const y = Int.const('y');
solver.add(x.eq(y.add(1)));
solver.add(y.eq(5));
const result = await solver.check();
console.log('Result:', result);
if (result === 'sat') {
const model = solver.model();
console.log('x =', model.eval(x).toString());
console.log('y =', model.eval(y).toString());
}
// Example 6: Introspecting parameter descriptions
console.log('\nExample 6: Parameter introspection');
const paramDescrs = simplifier.paramDescrs();
console.log('Parameter descriptions:');
console.log(paramDescrs.toString());
const size = paramDescrs.size();
console.log(`Number of parameters: ${size}`);
if (size > 0) {
const firstParamName = paramDescrs.getName(0);
console.log(`First parameter: ${firstParamName}`);
console.log(`Documentation: ${paramDescrs.getDocumentation(firstParamName)}`);
}
console.log('\nAll examples completed successfully!');
})();

View file

@ -0,0 +1,165 @@
/**
* Example demonstrating the RCF (Real Closed Field) API in TypeScript.
*
* This example shows how to use RCF numerals to work with:
* - Transcendental numbers (pi, e)
* - Algebraic numbers (roots of polynomials)
* - Infinitesimals
* - Exact real arithmetic
*
* Note: The RCF API is exposed at the low-level API layer.
* Import from 'z3-solver' for low-level access.
*/
import { init } from 'z3-solver';
async function rcfBasicExample() {
console.log('RCF Basic Example');
console.log('=================');
const { Z3 } = await init();
const ctx = Z3.mk_context_rc(Z3.mk_config());
try {
// Create pi and e
const pi = Z3.rcf_mk_pi(ctx);
const e = Z3.rcf_mk_e(ctx);
console.log('pi =', Z3.rcf_num_to_string(ctx, pi, false, false));
console.log('e =', Z3.rcf_num_to_string(ctx, e, false, false));
// Arithmetic operations
const sum = Z3.rcf_add(ctx, pi, e);
const prod = Z3.rcf_mul(ctx, pi, e);
console.log('pi + e =', Z3.rcf_num_to_string(ctx, sum, false, false));
console.log('pi * e =', Z3.rcf_num_to_string(ctx, prod, false, false));
// Decimal approximations
console.log('pi (10 decimals) =', Z3.rcf_num_to_decimal_string(ctx, pi, 10));
console.log('e (10 decimals) =', Z3.rcf_num_to_decimal_string(ctx, e, 10));
// Comparisons
console.log('pi < e?', Z3.rcf_lt(ctx, pi, e) ? 'yes' : 'no');
console.log('pi > e?', Z3.rcf_gt(ctx, pi, e) ? 'yes' : 'no');
// Cleanup
Z3.rcf_del(ctx, pi);
Z3.rcf_del(ctx, e);
Z3.rcf_del(ctx, sum);
Z3.rcf_del(ctx, prod);
} finally {
Z3.del_context(ctx);
}
}
async function rcfRationalExample() {
console.log('\nRCF Rational Example');
console.log('====================');
const { Z3 } = await init();
const ctx = Z3.mk_context_rc(Z3.mk_config());
try {
// Create rational numbers
const half = Z3.rcf_mk_rational(ctx, '1/2');
const third = Z3.rcf_mk_rational(ctx, '1/3');
console.log('1/2 =', Z3.rcf_num_to_string(ctx, half, false, false));
console.log('1/3 =', Z3.rcf_num_to_string(ctx, third, false, false));
// Arithmetic
const sum = Z3.rcf_add(ctx, half, third);
console.log('1/2 + 1/3 =', Z3.rcf_num_to_string(ctx, sum, false, false));
// Type queries
console.log('Is 1/2 rational?', Z3.rcf_is_rational(ctx, half) ? 'yes' : 'no');
console.log('Is 1/2 algebraic?', Z3.rcf_is_algebraic(ctx, half) ? 'yes' : 'no');
// Cleanup
Z3.rcf_del(ctx, half);
Z3.rcf_del(ctx, third);
Z3.rcf_del(ctx, sum);
} finally {
Z3.del_context(ctx);
}
}
async function rcfRootsExample() {
console.log('\nRCF Roots Example');
console.log('=================');
const { Z3 } = await init();
const ctx = Z3.mk_context_rc(Z3.mk_config());
try {
// Find roots of x^2 - 2 = 0
// Polynomial: -2 + 0*x + 1*x^2
const coeffs = [
Z3.rcf_mk_small_int(ctx, -2), // constant term
Z3.rcf_mk_small_int(ctx, 0), // x coefficient
Z3.rcf_mk_small_int(ctx, 1), // x^2 coefficient
];
const roots = new Array(coeffs.length);
const numRoots = Z3.rcf_mk_roots(ctx, coeffs, roots);
console.log('Roots of x^2 - 2 = 0:');
for (let i = 0; i < numRoots; i++) {
console.log(` root[${i}] =`, Z3.rcf_num_to_string(ctx, roots[i], false, false));
console.log(` decimal =`, Z3.rcf_num_to_decimal_string(ctx, roots[i], 15));
console.log(` is_algebraic =`, Z3.rcf_is_algebraic(ctx, roots[i]) ? 'yes' : 'no');
}
// Cleanup
for (const coeff of coeffs) {
Z3.rcf_del(ctx, coeff);
}
for (let i = 0; i < numRoots; i++) {
Z3.rcf_del(ctx, roots[i]);
}
} finally {
Z3.del_context(ctx);
}
}
async function rcfInfinitesimalExample() {
console.log('\nRCF Infinitesimal Example');
console.log('=========================');
const { Z3 } = await init();
const ctx = Z3.mk_context_rc(Z3.mk_config());
try {
// Create an infinitesimal
const eps = Z3.rcf_mk_infinitesimal(ctx);
console.log('eps =', Z3.rcf_num_to_string(ctx, eps, false, false));
console.log('Is eps infinitesimal?', Z3.rcf_is_infinitesimal(ctx, eps) ? 'yes' : 'no');
// Infinitesimals are smaller than any positive real number
const tiny = Z3.rcf_mk_rational(ctx, '1/1000000000');
console.log('eps < 1/1000000000?', Z3.rcf_lt(ctx, eps, tiny) ? 'yes' : 'no');
// Cleanup
Z3.rcf_del(ctx, eps);
Z3.rcf_del(ctx, tiny);
} finally {
Z3.del_context(ctx);
}
}
async function main() {
try {
await rcfBasicExample();
await rcfRationalExample();
await rcfRootsExample();
await rcfInfinitesimalExample();
console.log('\nAll RCF examples completed successfully!');
} catch (error) {
console.error('Error:', error);
throw error;
}
}
main();

View file

@ -0,0 +1,268 @@
# Regular Expression Support in Z3 TypeScript API
This document demonstrates the new regular expression support added to the Z3 TypeScript API.
## Basic Usage
### Creating Regular Expressions
```typescript
const { Re, String: Str, InRe, Solver } = Context('main');
// Create a regex from a string
const hello = Re.toRe('hello');
// Check if a string matches
const solver = new Solver();
solver.add(InRe('hello', hello));
await solver.check(); // sat
```
### Star Operator (Zero or More)
```typescript
const { Re, InRe, Star } = Context('main');
const a = Re.toRe('a');
const aStar = Star(a);
// Empty string matches a*
InRe('', aStar); // true
// Multiple 'a's match
InRe('aaa', aStar); // true
```
### Plus Operator (One or More)
```typescript
const { Re, InRe, Plus } = Context('main');
const a = Re.toRe('a');
const aPlus = Plus(a);
// Empty string does NOT match a+
InRe('', aPlus); // false
// One or more 'a's match
InRe('aa', aPlus); // true
```
### Option Operator (Zero or One)
```typescript
const { Re, InRe, Option } = Context('main');
const a = Re.toRe('a');
const aOpt = Option(a);
// Both empty and 'a' match a?
InRe('', aOpt); // true
InRe('a', aOpt); // true
InRe('aa', aOpt); // false
```
### Union (Or)
```typescript
const { Re, InRe, Union } = Context('main');
const a = Re.toRe('a');
const b = Re.toRe('b');
const aOrB = Union(a, b);
// Either 'a' or 'b' match
InRe('a', aOrB); // true
InRe('b', aOrB); // true
InRe('c', aOrB); // false
```
### Intersect (And)
```typescript
const { Re, InRe, Intersect, Star } = Context('main');
const a = Re.toRe('a');
const b = Re.toRe('b');
const both = Intersect(Star(a), Star(b));
// Only empty string matches both a* and b*
InRe('', both); // true
InRe('a', both); // false
```
### Range
```typescript
const { Range, InRe } = Context('main');
const azRange = Range('a', 'z');
// Lowercase letters match
InRe('m', azRange); // true
// Others don't
InRe('1', azRange); // false
InRe('Z', azRange); // false
```
### Loop (Bounded Repetition)
```typescript
const { Re, InRe, Loop } = Context('main');
const a = Re.toRe('a');
// Between 2 and 3 repetitions
const a2to3 = Loop(a, 2, 3);
InRe('aa', a2to3); // true
InRe('aaa', a2to3); // true
InRe('a', a2to3); // false
InRe('aaaa', a2to3); // false
// At least 2 repetitions (hi=0 or omitted means unbounded)
const a2Plus = Loop(a, 2, 0); // or Loop(a, 2)
InRe('aa', a2Plus); // true
InRe('aaa', a2Plus); // true
InRe('aaaa', a2Plus); // true
InRe('a', a2Plus); // false
```
### Power (Exact Repetition)
```typescript
const { Re, InRe, Power } = Context('main');
const a = Re.toRe('a');
const a3 = Power(a, 3);
// Exactly 3 repetitions match
InRe('aaa', a3); // true
// Others don't
InRe('aa', a3); // false
InRe('aaaa', a3); // false
```
### Complement (Negation)
```typescript
const { Re, InRe, Complement } = Context('main');
const a = Re.toRe('a');
const notA = Complement(a);
// Everything except 'a' matches
InRe('a', notA); // false
InRe('b', notA); // true
InRe('', notA); // true
```
### Diff (Set Difference)
```typescript
const { Re, InRe, Diff, Star } = Context('main');
const a = Re.toRe('a');
const b = Re.toRe('b');
const diff = Diff(Star(a), b);
// a* except 'b'
InRe('aaa', diff); // true
InRe('b', diff); // false
```
### ReConcat (Concatenation)
```typescript
const { Re, InRe, ReConcat } = Context('main');
const hello = Re.toRe('hello');
const world = Re.toRe('world');
const helloworld = ReConcat(hello, world);
// Concatenated strings match
InRe('helloworld', helloworld); // true
InRe('hello', helloworld); // false
```
## Method Chaining
Regular expression objects support method chaining:
```typescript
const { Re, InRe } = Context('main');
const a = Re.toRe('a');
// Using methods
const aStar = a.star();
const aPlus = a.plus();
const aOpt = a.option();
const notA = a.complement();
// Chaining
const complex = a.plus().union(Re.toRe('b').star());
```
## Complex Example: Pattern Matching
```typescript
const { Re, String: Str, InRe, Union, Star, Solver } = Context('main');
const x = Str.const('x');
const a = Re.toRe('a');
const b = Re.toRe('b');
// Pattern: any combination of 'a' and 'b'
const pattern = Star(Union(a, b));
const solver = new Solver();
solver.add(InRe(x, pattern));
solver.add(x.length().eq(5));
if (await solver.check() === 'sat') {
const model = solver.model();
const result = model.eval(x);
// Result will be a 5-character string containing only 'a' and 'b'
console.log(result.asString()); // e.g., "aabba"
}
```
## API Reference
### Factory Methods
- `Re.sort(seqSort)` - Create a regex sort
- `Re.toRe(seq)` - Convert sequence/string to regex
### Operators
- `Star(re)` - Zero or more repetitions (*)
- `Plus(re)` - One or more repetitions (+)
- `Option(re)` - Zero or one repetition (?)
- `Union(...res)` - Or operation (|)
- `Intersect(...res)` - And operation (&)
- `ReConcat(...res)` - Concatenation
- `Complement(re)` - Negation (~)
- `Diff(a, b)` - Set difference (a \ b)
- `Range(lo, hi)` - Character range
- `Loop(re, lo, hi?)` - Bounded repetition {lo,hi} or at least lo if hi=0 or omitted
- `Power(re, n)` - Exact repetition {n}
### Special Patterns
- `AllChar(reSort)` - Match any single character
- `Empty(reSort)` - Empty language
- `Full(reSort)` - Match all strings
### Membership Test
- `InRe(seq, re)` - Test if sequence matches regex
## Notes
- All regex operations are symbolic - they create constraints rather than executing pattern matching
- The solver finds strings that satisfy the regex constraints
- Regular expressions can be combined with other Z3 constraints (length, containment, etc.)
- This implementation follows the SMT-LIB2 regular expression theory

View file

@ -12,30 +12,126 @@ export function makeCCWrapper() {
if (fn == null) {
throw new Error(`could not find definition for ${fnName}`);
}
let wrapper;
if (fn.cRet === 'Z3_string') {
wrapper = `wrapper_str`;
} else if (['int', 'unsigned', 'void'].includes(fn.cRet) || fn.cRet.startsWith('Z3_')) {
wrapper = `wrapper`;
} else {
throw new Error(`async function with unknown return type ${fn.cRet}`);
}
wrappers.push(
`
extern "C" void async_${fn.name}(${fn.params
// Check if function has array parameters
const arrayParams = fn.params.filter(p => p.isArray && p.kind === 'in_array');
const hasArrayParams = arrayParams.length > 0;
if (hasArrayParams) {
// Generate custom wrapper for functions with array parameters
const paramList = fn.params
.map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`)
.join(', ')}) {
.join(', ');
// Find the size parameter for each array and build copy/free code
const arrayCopies: string[] = [];
const arrayFrees: string[] = [];
const arrayCopyNames: string[] = [];
for (let p of arrayParams) {
const sizeParam = fn.params[p.sizeIndex!];
const ptrType = p.cType.endsWith('*') ? p.cType : `${p.cType}*`;
const copyName = `${p.name}_copy`;
arrayCopyNames.push(copyName);
// Allocate and copy with null check
arrayCopies.push(`${ptrType} ${copyName} = (${ptrType})malloc(sizeof(${p.cType}) * ${sizeParam.name});`);
arrayCopies.push(`if (!${copyName}) {`);
arrayCopies.push(` MAIN_THREAD_ASYNC_EM_ASM({ reject_async(new Error("Memory allocation failed")); });`);
arrayCopies.push(` return;`);
arrayCopies.push(`}`);
arrayCopies.push(`memcpy(${copyName}, ${p.name}, sizeof(${p.cType}) * ${sizeParam.name});`);
arrayFrees.push(`free(${copyName});`);
}
// Build lambda capture list
const nonArrayParams = fn.params.filter(p => !p.isArray || p.kind !== 'in_array');
const captureList = [...arrayCopyNames, ...nonArrayParams.map(p => p.name)].join(', ');
// Build argument list for the actual function call, using copied arrays
const callArgs = fn.params
.map(p => {
if (p.isArray && p.kind === 'in_array') {
return `${p.name}_copy`;
}
return p.name;
})
.join(', ');
const isString = fn.cRet === 'Z3_string';
const returnType = isString ? 'auto' : fn.cRet;
wrappers.push(
`
extern "C" void async_${fn.name}(${paramList}) {
${arrayCopies.join('\n ')}
std::thread t([${captureList}] {
try {
${returnType} result = ${fn.name}(${callArgs});
${
isString
? `
MAIN_THREAD_ASYNC_EM_ASM({
resolve_async(UTF8ToString($0));
}, result);
`
: `
MAIN_THREAD_ASYNC_EM_ASM({
resolve_async($0);
}, result);
`
}
} catch (std::exception& e) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async(new Error(UTF8ToString($0)));
}, e.what());
} catch (...) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async(new Error('failed with unknown exception'));
});
}
${arrayFrees.join('\n ')}
MAIN_THREAD_ASYNC_EM_ASM({
clearTimeout(threadTimeouts.shift());
});
});
t.detach();
EM_ASM({
threadTimeouts.push(setTimeout(() => {}, 600000));
});
}
`.trim(),
);
} else {
// Use template wrapper for functions without array parameters
let wrapper;
if (fn.cRet === 'Z3_string') {
wrapper = `wrapper_str`;
} else if (['int', 'unsigned', 'void'].includes(fn.cRet) || fn.cRet.startsWith('Z3_')) {
wrapper = `wrapper`;
} else {
throw new Error(`async function with unknown return type ${fn.cRet}`);
}
wrappers.push(
`
extern "C" void async_${fn.name}(${fn.params
.map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`)
.join(', ')}) {
${wrapper}<decltype(&${fn.name}), &${fn.name}>(${fn.params.map(p => `${p.name}`).join(', ')});
}
`.trim(),
);
);
}
}
return `// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND
#include <thread>
#include <cstdlib>
#include <cstring>
#include <emscripten.h>

View file

@ -137,7 +137,7 @@ async function makeTsWrapper() {
// otherwise fall back to ccall
const ctypes = fn.params.map(p =>
let ctypes = fn.params.map(p =>
p.kind === 'in_array' ? 'array' : p.kind === 'out_array' ? 'number' : p.isPtr ? 'number' : toEmType(p.type),
);
@ -149,6 +149,8 @@ async function makeTsWrapper() {
const args: (string | FuncParam)[] = fn.params;
let arrayLengthParams = new Map();
let allocatedArrays: string[] = []; // Track allocated arrays for cleanup
for (let p of inParams) {
if (p.nullable && !p.isArray) {
// this would be easy to implement - just map null to 0 - but nothing actually uses nullable non-array input parameters, so we can't ensure we've done it right
@ -179,6 +181,34 @@ async function makeTsWrapper() {
}
args[sizeIndex] = `${p.name}.length`;
params[sizeIndex] = null;
// For async functions, we need to manually manage array memory
// because ccall frees it before the async thread uses it
if (isAsync && p.kind === 'in_array') {
const paramIdx = fn.params.indexOf(p);
const ptrName = `${p.name}_ptr`;
allocatedArrays.push(ptrName);
// Allocate memory for array of pointers (4 bytes per pointer on wasm32)
prefix += `
const ${ptrName} = Mod._malloc(${p.name}.length * 4);
Mod.HEAPU32.set(${p.name} as unknown as number[], ${ptrName} / 4);
`.trim();
args[paramIdx] = ptrName;
ctypes[paramIdx] = 'number'; // Pass as pointer, not array
}
}
// Add try-finally for async functions with allocated arrays
if (isAsync && allocatedArrays.length > 0) {
prefix += `
try {
`.trim();
suffix =
`
} finally {
${allocatedArrays.map(arr => `Mod._free(${arr});`).join('\n ')}
}
`.trim() + suffix;
}
let returnType = fn.ret;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -34,6 +34,15 @@ target_include_directories(z3jl PRIVATE
"${PROJECT_BINARY_DIR}/src/api"
"${PROJECT_SOURCE_DIR}/src/api/c++"
)
# Add header padding for macOS to allow install_name_tool to modify the dylib
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_options(z3jl PRIVATE "-Wl,-headerpad_max_install_names")
endif()
# On macOS, add headerpad for install_name_tool compatibility
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_options(z3jl PRIVATE "-Wl,-headerpad_max_install_names")
endif()
option(Z3_INSTALL_JULIA_BINDINGS "Install Julia bindings when invoking install target" ON)
if(Z3_INSTALL_JULIA_BINDINGS)

View file

@ -309,6 +309,17 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
m.method("sqrt", static_cast<expr (*)(expr const &, expr const &)>(&sqrt));
m.method("fma", static_cast<expr (*)(expr const &, expr const &, expr const &, expr const &)>(&fma));
m.method("range", &range);
m.method("finite_set_empty", &finite_set_empty);
m.method("finite_set_singleton", &finite_set_singleton);
m.method("finite_set_union", &finite_set_union);
m.method("finite_set_intersect", &finite_set_intersect);
m.method("finite_set_difference", &finite_set_difference);
m.method("finite_set_member", &finite_set_member);
m.method("finite_set_size", &finite_set_size);
m.method("finite_set_subset", &finite_set_subset);
m.method("finite_set_map", &finite_set_map);
m.method("finite_set_filter", &finite_set_filter);
m.method("finite_set_range", &finite_set_range);
// -------------------------------------------------------------------------
@ -400,9 +411,9 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
.MM(solver, units)
.method("trail", static_cast<expr_vector (solver::*)() const>(&solver::trail))
.method("trail", [](solver &s, jlcxx::ArrayRef<unsigned> levels) {
int sz = levels.size();
int sz = static_cast<int>(levels.size());
z3::array<unsigned> _levels(sz);
for (int i = 0; i < sz; i++) {
for (int i = 0; i < sz; ++i) {
_levels[i] = levels[i];
}
return s.trail(_levels);
@ -618,6 +629,7 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
.MM(context, string_sort)
.MM(context, seq_sort)
.MM(context, re_sort)
.MM(context, finite_set_sort)
.method("array_sort", static_cast<sort (context::*)(sort, sort)>(&context::array_sort))
.method("array_sort", static_cast<sort (context::*)(sort_vector const&, sort)>(&context::array_sort))
.method("fpa_sort", static_cast<sort (context::*)(unsigned, unsigned)>(&context::fpa_sort))
@ -629,9 +641,9 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
.MM(context, set_rounding_mode)
.method("enumeration_sort",
[](context& c, char const * name, jlcxx::ArrayRef<jl_value_t*,1> names, func_decl_vector &cs, func_decl_vector &ts) {
int sz = names.size();
int sz = static_cast<int>(names.size());
std::vector<const char *> _names;
for (int i = 0; i < sz; i++) {
for (int i = 0; i < sz; ++i) {
const char *x = jl_string_data(names[i]);
_names.push_back(x);
}
@ -639,10 +651,10 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
})
.method("tuple_sort",
[](context& c, char const * name, jlcxx::ArrayRef<jl_value_t*,1> names, jlcxx::ArrayRef<jl_value_t*,1> sorts, func_decl_vector &projs) {
int sz = names.size();
int sz = static_cast<int>(names.size());
std::vector<sort> _sorts;
std::vector<const char *> _names;
for (int i = 0; i < sz; i++) {
for (int i = 0; i < sz; ++i) {
const sort &x = jlcxx::unbox<sort&>(sorts[i]);
const char *y = jl_string_data(names[i]);
_sorts.push_back(x);

View file

@ -1,3 +1,4 @@
find_package(OCaml REQUIRED)
set(exe_ext ${CMAKE_EXECUTABLE_SUFFIX})
@ -159,6 +160,8 @@ endif()
if( APPLE )
# set(ocaml_rpath "@executable_path/../libz3${so_ext}")
# Add headerpad for install_name_tool compatibility on macOS
list(APPEND c_lib_deps "-ldopt" "-Wl,-headerpad_max_install_names")
elseif( UNIX )
set(ocaml_rpath "\\$ORIGIN/../libz3${so_ext}")
list(APPEND c_lib_deps "-dllpath" ${ocaml_rpath})

View file

@ -216,6 +216,8 @@ sig
val to_string : sort -> string
val mk_uninterpreted : context -> Symbol.symbol -> sort
val mk_uninterpreted_s : context -> string -> sort
val mk_type_variable : context -> Symbol.symbol -> sort
val mk_type_variable_s : context -> string -> sort
end = struct
type sort = Z3native.sort
let gc a = Z3native.context_of_ast a
@ -228,6 +230,8 @@ end = struct
let to_string (x:sort) = Z3native.sort_to_string (gc x) x
let mk_uninterpreted ctx s = Z3native.mk_uninterpreted_sort ctx s
let mk_uninterpreted_s (ctx:context) (s:string) = mk_uninterpreted ctx (Symbol.mk_string ctx s)
let mk_type_variable ctx s = Z3native.mk_type_variable ctx s
let mk_type_variable_s (ctx:context) (s:string) = mk_type_variable ctx (Symbol.mk_string ctx s)
end
and FuncDecl :
@ -908,6 +912,13 @@ struct
let mk_sort_s (ctx:context) (name:string) (constructors:Constructor.constructor list) =
mk_sort ctx (Symbol.mk_string ctx name) constructors
let mk_polymorphic_sort (ctx:context) (name:Symbol.symbol) (type_params:Sort.sort list) (constructors:Constructor.constructor list) =
let (x,_) = Z3native.mk_polymorphic_datatype ctx name (List.length type_params) type_params (List.length constructors) constructors in
x
let mk_polymorphic_sort_s (ctx:context) (name:string) (type_params:Sort.sort list) (constructors:Constructor.constructor list) =
mk_polymorphic_sort ctx (Symbol.mk_string ctx name) type_params constructors
let mk_sort_ref (ctx: context) (name:Symbol.symbol) =
Z3native.mk_datatype_sort ctx name 0 []
@ -950,6 +961,9 @@ struct
let g j = Z3native.get_datatype_sort_constructor_accessor (Sort.gc x) x i j in
List.init ds g) in
List.init n f
let update_field (ctx:context) (field_access:FuncDecl.func_decl) (t:Expr.expr) (v:Expr.expr) =
Z3native.datatype_update_field ctx field_access t v
end
@ -1926,6 +1940,37 @@ struct
let interrupt (ctx:context) (s:solver) =
Z3native.solver_interrupt ctx s
let get_units x =
let av = Z3native.solver_get_units (gc x) x in
AST.ASTVector.to_expr_list av
let get_non_units x =
let av = Z3native.solver_get_non_units (gc x) x in
AST.ASTVector.to_expr_list av
let get_trail x =
let av = Z3native.solver_get_trail (gc x) x in
AST.ASTVector.to_expr_list av
let get_levels x literals =
let n = List.length literals in
let av = Z3native.mk_ast_vector (gc x) in
List.iter (fun e -> Z3native.ast_vector_push (gc x) av e) literals;
let level_list = Z3native.solver_get_levels (gc x) x av n in
Array.of_list level_list
let congruence_root x a = Z3native.solver_congruence_root (gc x) x a
let congruence_next x a = Z3native.solver_congruence_next (gc x) x a
let congruence_explain x a b = Z3native.solver_congruence_explain (gc x) x a b
let from_file x = Z3native.solver_from_file (gc x) x
let from_string x = Z3native.solver_from_string (gc x) x
let set_initial_value x var value = Z3native.solver_set_initial_value (gc x) x var value
end

View file

@ -290,6 +290,12 @@ sig
(** Create a new uninterpreted sort. *)
val mk_uninterpreted_s : context -> string -> sort
(** Create a type variable sort for use in polymorphic datatypes. *)
val mk_type_variable : context -> Symbol.symbol -> sort
(** Create a type variable sort for use in polymorphic datatypes. *)
val mk_type_variable_s : context -> string -> sort
end
(** Function declarations *)
@ -1099,6 +1105,14 @@ sig
(** Create a new datatype sort. *)
val mk_sort_s : context -> string -> Constructor.constructor list -> Sort.sort
(** Create a new polymorphic datatype sort with type parameters.
Type parameters should be created using Sort.mk_type_variable. *)
val mk_polymorphic_sort : context -> Symbol.symbol -> Sort.sort list -> Constructor.constructor list -> Sort.sort
(** Create a new polymorphic datatype sort with type parameters.
Type parameters should be created using Sort.mk_type_variable. *)
val mk_polymorphic_sort_s : context -> string -> Sort.sort list -> Constructor.constructor list -> Sort.sort
(** Create mutually recursive datatypes. *)
val mk_sorts : context -> Symbol.symbol list -> Constructor.constructor list list -> Sort.sort list
@ -1116,6 +1130,12 @@ sig
(** The constructor accessors. *)
val get_accessors : Sort.sort -> FuncDecl.func_decl list list
(** Update a datatype field at expression [t] with value [v].
The function performs a record update at [t]. The field
that is passed in as argument is updated with value [v],
the remaining fields of [t] are unchanged. *)
val update_field : context -> FuncDecl.func_decl -> Expr.expr -> Expr.expr -> Expr.expr
end
(** Functions to manipulate Enumeration expressions *)
@ -3369,6 +3389,46 @@ sig
it is more convenient to cancel a specific solver. Solvers
that are not selected for interrupts are left alone.*)
val interrupt: context -> solver -> unit
(** Retrieve the set of units from the solver.
Units are clauses of size 1 learned by the solver during solving. *)
val get_units : solver -> Expr.expr list
(** Retrieve the set of non-units from the solver.
Non-units are clauses of size greater than 1 learned by the solver. *)
val get_non_units : solver -> Expr.expr list
(** Retrieve the trail (sequence of assignments) from the solver.
The trail shows the sequence of literal assignments made by the solver. *)
val get_trail : solver -> Expr.expr list
(** Retrieve the decision levels of trail literals.
Given a list of literals from the trail, returns an array of their decision levels.
@param literals List of literals from the trail
@return Array of decision levels corresponding to the input literals *)
val get_levels : solver -> Expr.expr list -> int array
(** Retrieve the congruence closure root of an expression.
Returns the representative of the equivalence class containing the expression. *)
val congruence_root : solver -> Expr.expr -> Expr.expr
(** Retrieve the next element in the congruence closure equivalence class.
Congruence classes form a circular list; this returns the next element. *)
val congruence_next : solver -> Expr.expr -> Expr.expr
(** Retrieve an explanation for why two expressions are congruent.
Returns an expression that justifies the congruence between a and b. *)
val congruence_explain : solver -> Expr.expr -> Expr.expr -> Expr.expr
(** Parse SMT-LIB2 formulas from a file and assert them into the solver. *)
val from_file : solver -> string -> unit
(** Parse SMT-LIB2 formulas from a string and assert them into the solver. *)
val from_string : solver -> string -> unit
(** Provide an initial value hint for a variable to the solver.
This can help guide the solver to find solutions more efficiently. *)
val set_initial_value : solver -> Expr.expr -> Expr.expr -> unit
end
(** Fixedpoint solving *)

View file

@ -24,6 +24,7 @@ ROOT_DIR = os.path.abspath(os.path.dirname(__file__))
SRC_DIR_LOCAL = os.path.join(ROOT_DIR, 'core')
SRC_DIR_REPO = os.path.join(ROOT_DIR, '..', '..', '..')
SRC_DIR = SRC_DIR_LOCAL if os.path.exists(SRC_DIR_LOCAL) else SRC_DIR_REPO
BUILD_DIR = build_env.get('Z3BUILD', 'build')
IS_SINGLE_THREADED = False
ENABLE_LTO = True
@ -34,7 +35,7 @@ IS_PYODIDE = 'PYODIDE_ROOT' in os.environ and os.environ.get('_PYTHON_HOST_PLATF
# determine where binaries are
RELEASE_DIR = os.environ.get('PACKAGE_FROM_RELEASE', None)
if RELEASE_DIR is None:
BUILD_DIR = os.path.join(SRC_DIR, 'build') # implicit in configure script
BUILD_DIR = os.path.join(SRC_DIR, BUILD_DIR) # implicit in configure script
HEADER_DIRS = [os.path.join(SRC_DIR, 'src', 'api'), os.path.join(SRC_DIR, 'src', 'api', 'c++')]
RELEASE_METADATA = None
if IS_PYODIDE:
@ -315,6 +316,7 @@ class bdist_wheel(_bdist_wheel):
# windows arm64 is not supported by pypi yet
("win", "x64"): "win_amd64",
("win", "x86"): "win32",
("win", "arm64"): "win_arm64",
("osx", "x64"): f"macosx_{os_version_tag}_x86_64",
("osx", "arm64"): f"macosx_{os_version_tag}_arm64",
("darwin", "x86_64"): f"macosx_{os_version_tag}_x86_64",

View file

@ -240,6 +240,23 @@ class Context:
def param_descrs(self):
"""Return the global parameter description set."""
return ParamDescrsRef(Z3_get_global_param_descrs(self.ref()), self)
def set_ast_print_mode(self, mode):
"""Set the pretty printing mode for ASTs.
The following modes are available:
- Z3_PRINT_SMTLIB_FULL (0): Print AST nodes in SMTLIB verbose format.
- Z3_PRINT_LOW_LEVEL (1): Print AST nodes using a low-level format.
- Z3_PRINT_SMTLIB2_COMPLIANT (2): Print AST nodes in SMTLIB 2.x compliant format.
Example:
>>> c = Context()
>>> x = Int('x', c)
>>> c.set_ast_print_mode(Z3_PRINT_SMTLIB2_COMPLIANT)
>>> print(x)
x
"""
Z3_set_ast_print_mode(self.ref(), mode)
# Global Z3 context
@ -678,6 +695,8 @@ def is_sort(s : Any) -> bool:
def _to_sort_ref(s, ctx):
if z3_debug():
_z3_assert(isinstance(s, Sort), "Z3 Sort expected")
if Z3_is_finite_set_sort(ctx.ref(), s):
return FiniteSetSortRef(s, ctx)
k = _sort_kind(ctx, s)
if k == Z3_BOOL_SORT:
return BoolSortRef(s, ctx)
@ -841,7 +860,7 @@ class FuncDeclRef(AstRef):
elif k == Z3_PARAMETER_RATIONAL:
result[i] = Z3_get_decl_rational_parameter(self.ctx_ref(), self.ast, i)
elif k == Z3_PARAMETER_SYMBOL:
result[i] = Z3_get_decl_symbol_parameter(self.ctx_ref(), self.ast, i)
result[i] = _symbol2py(ctx, Z3_get_decl_symbol_parameter(self.ctx_ref(), self.ast, i))
elif k == Z3_PARAMETER_SORT:
result[i] = SortRef(Z3_get_decl_sort_parameter(self.ctx_ref(), self.ast, i), ctx)
elif k == Z3_PARAMETER_AST:
@ -853,7 +872,7 @@ class FuncDeclRef(AstRef):
elif k == Z3_PARAMETER_ZSTRING:
result[i] = "internal string"
else:
assert(False)
raise Z3Exception("Unexpected parameter kind")
return result
def __call__(self, *args):
@ -1154,6 +1173,30 @@ class ExprRef(AstRef):
else:
return []
def update(self, *args):
"""Update the arguments of the expression.
Return a new expression with the same function declaration and updated arguments.
The number of new arguments must match the current number of arguments.
>>> f = Function('f', IntSort(), IntSort(), IntSort())
>>> a = Int('a')
>>> b = Int('b')
>>> c = Int('c')
>>> t = f(a, b)
>>> t.update(c, c)
f(c, c)
"""
if z3_debug():
_z3_assert(is_app(self), "Z3 application expected")
_z3_assert(len(args) == self.num_args(), "Number of arguments does not match")
_z3_assert(all([is_expr(arg) for arg in args]), "Z3 expressions expected")
num = len(args)
_args = (Ast * num)()
for i in range(num):
_args[i] = args[i].as_ast()
return _to_expr_ref(Z3_update_term(self.ctx_ref(), self.as_ast(), num, _args), self.ctx)
def from_string(self, s):
pass
@ -1184,7 +1227,11 @@ def _to_expr_ref(a, ctx):
k = Z3_get_ast_kind(ctx_ref, a)
if k == Z3_QUANTIFIER_AST:
return QuantifierRef(a, ctx)
sk = Z3_get_sort_kind(ctx_ref, Z3_get_sort(ctx_ref, a))
# Check for finite set sort before checking sort kind
s = Z3_get_sort(ctx_ref, a)
if Z3_is_finite_set_sort(ctx_ref, s):
return FiniteSetRef(a, ctx)
sk = Z3_get_sort_kind(ctx_ref, s)
if sk == Z3_BOOL_SORT:
return BoolRef(a, ctx)
if sk == Z3_INT_SORT:
@ -2763,6 +2810,17 @@ class ArithRef(ExprRef):
a, b = _coerce_exprs(self, other)
return BoolRef(Z3_mk_ge(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx)
def __abs__(self):
"""Return an expression representing `abs(self)`.
>>> x = Int('x')
>>> abs(x)
If(x > 0, x, -x)
>>> eq(abs(x), Abs(x))
True
"""
return Abs(self)
def is_arith(a):
"""Return `True` if `a` is an arithmetical expression.
@ -3324,6 +3382,9 @@ def RatVal(a, b, ctx=None):
"""Return a Z3 rational a/b.
If `ctx=None`, then the global context is used.
Note: Division by zero (b == 0) is allowed in Z3 symbolic expressions.
Z3 can reason about such expressions symbolically.
>>> RatVal(3,5)
3/5
@ -3333,6 +3394,7 @@ def RatVal(a, b, ctx=None):
if z3_debug():
_z3_assert(_is_int(a) or isinstance(a, str), "First argument cannot be converted into an integer")
_z3_assert(_is_int(b) or isinstance(b, str), "Second argument cannot be converted into an integer")
# Division by 0 is intentionally allowed - Z3 handles it symbolically
return simplify(RealVal(a, ctx) / RealVal(b, ctx))
@ -5010,6 +5072,25 @@ def Ext(a, b):
_z3_assert(is_array_sort(a) and (is_array(b) or b.is_lambda()), "arguments must be arrays")
return _to_expr_ref(Z3_mk_array_ext(ctx.ref(), a.as_ast(), b.as_ast()), ctx)
def AsArray(f):
"""Return a Z3 as-array expression for the given function declaration.
>>> f = Function('f', IntSort(), IntSort())
>>> a = AsArray(f)
>>> a.sort()
Array(Int, Int)
>>> is_as_array(a)
True
>>> get_as_array_func(a) == f
True
"""
if z3_debug():
_z3_assert(isinstance(f, FuncDeclRef), "function declaration expected")
ctx = f.ctx
return ArrayRef(Z3_mk_as_array(ctx.ref(), f.ast), ctx)
def is_select(a):
"""Return `True` if `a` is a Z3 array select application.
@ -5052,6 +5133,8 @@ def EmptySet(s):
K(Int, False)
"""
ctx = s.ctx
if is_finite_set_sort(s):
return FiniteSetEmpty(s)
return ArrayRef(Z3_mk_empty_set(ctx.ref(), s.ast), ctx)
@ -5072,6 +5155,9 @@ def SetUnion(*args):
union(a, b)
"""
args = _get_args(args)
if len(args) > 0 and is_finite_set(args[0]):
from functools import reduce
return reduce(FiniteSetUnion, args)
ctx = _ctx_from_ast_arg_list(args)
_args, sz = _to_ast_array(args)
return ArrayRef(Z3_mk_set_union(ctx.ref(), sz, _args), ctx)
@ -5086,6 +5172,9 @@ def SetIntersect(*args):
"""
args = _get_args(args)
ctx = _ctx_from_ast_arg_list(args)
if len(args) > 0 and is_finite_set(args[0]):
from functools import reduce
return reduce(FiniteSetIntersect, args)
_args, sz = _to_ast_array(args)
return ArrayRef(Z3_mk_set_intersect(ctx.ref(), sz, _args), ctx)
@ -5096,8 +5185,10 @@ def SetAdd(s, e):
>>> SetAdd(a, 1)
Store(a, 1, True)
"""
ctx = _ctx_from_ast_arg_list([s, e])
ctx = _ctx_from_ast_arg_list([s, e])
e = _py2expr(e, ctx)
if is_finite_set(s):
return FiniteSetSingleton(e) | s
return ArrayRef(Z3_mk_set_add(ctx.ref(), s.as_ast(), e.as_ast()), ctx)
@ -5109,6 +5200,8 @@ def SetDel(s, e):
"""
ctx = _ctx_from_ast_arg_list([s, e])
e = _py2expr(e, ctx)
if is_finite_set(s):
return s - FiniteSetSingleton(e)
return ArrayRef(Z3_mk_set_del(ctx.ref(), s.as_ast(), e.as_ast()), ctx)
@ -5130,6 +5223,8 @@ def SetDifference(a, b):
setminus(a, b)
"""
ctx = _ctx_from_ast_arg_list([a, b])
if is_finite_set(a):
return FiniteSetDifference(a, b)
return ArrayRef(Z3_mk_set_difference(ctx.ref(), a.as_ast(), b.as_ast()), ctx)
@ -5141,6 +5236,8 @@ def IsMember(e, s):
"""
ctx = _ctx_from_ast_arg_list([s, e])
e = _py2expr(e, ctx)
if is_finite_set(s):
return FiniteSetIsMember(e, s)
return BoolRef(Z3_mk_set_member(ctx.ref(), e.as_ast(), s.as_ast()), ctx)
@ -5152,9 +5249,228 @@ def IsSubset(a, b):
subset(a, b)
"""
ctx = _ctx_from_ast_arg_list([a, b])
if is_finite_set(a):
return FiniteSetIsSubset(a, b)
return BoolRef(Z3_mk_set_subset(ctx.ref(), a.as_ast(), b.as_ast()), ctx)
#########################################
#
# Finite Sets
#
#########################################
class FiniteSetSortRef(SortRef):
"""Finite set sort."""
def element_sort(self):
"""Return the element sort of this finite set sort."""
return _to_sort_ref(Z3_get_finite_set_sort_basis(self.ctx_ref(), self.ast), self.ctx)
def cast(self, val):
"""Try to cast val as a finite set expression."""
if is_expr(val):
if self.eq(val.sort()):
return val
else:
_z3_assert(False, "Cannot cast to finite set sort")
if isinstance(val, set):
elem_sort = self.element_sort()
result = FiniteSetEmpty(self)
for e in val:
result = FiniteSetUnion(result, Singleton(_py2expr(e, self.ctx, elem_sort)))
return result
_z3_assert(False, "Cannot cast to finite set sort")
def subsort(self, other):
return False
def is_int(self):
return False
def is_bool(self):
return False
def is_datatype(self):
return False
def is_array(self):
return False
def is_bv(self):
return False
def is_finite_set(a):
"""Return True if a is a Z3 finite set expression.
>>> s = FiniteSetSort(IntSort())
>>> is_finite_set(FiniteSetEmpty(s))
True
>>> is_finite_set(IntVal(1))
False
"""
return isinstance(a, FiniteSetRef)
def is_finite_set_sort(s):
"""Return True if s is a Z3 finite set sort.
>>> is_finite_set_sort(FiniteSetSort(IntSort()))
True
>>> is_finite_set_sort(IntSort())
False
"""
return isinstance(s, FiniteSetSortRef)
class FiniteSetRef(ExprRef):
"""Finite set expression."""
def sort(self):
return FiniteSetSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx)
def __or__(self, other):
"""Return the union of self and other."""
return FiniteSetUnion(self, other)
def __and__(self, other):
"""Return the intersection of self and other."""
return FiniteSetIntersect(self, other)
def __sub__(self, other):
"""Return the set difference of self and other."""
return FiniteSetDifference(self, other)
def FiniteSetSort(elem_sort):
"""Create a finite set sort over element sort elem_sort.
>>> s = FiniteSetSort(IntSort())
>>> s
FiniteSet(Int)
"""
return FiniteSetSortRef(Z3_mk_finite_set_sort(elem_sort.ctx_ref(), elem_sort.ast), elem_sort.ctx)
def FiniteSetEmpty(set_sort):
"""Create an empty finite set of the given sort.
>>> s = FiniteSetSort(IntSort())
>>> FiniteSetEmpty(s)
set.empty
"""
ctx = set_sort.ctx
return FiniteSetRef(Z3_mk_finite_set_empty(ctx.ref(), set_sort.ast), ctx)
def Singleton(elem):
"""Create a singleton finite set containing elem.
>>> Singleton(IntVal(1))
set.singleton(1)
"""
ctx = elem.ctx
return FiniteSetRef(Z3_mk_finite_set_singleton(ctx.ref(), elem.as_ast()), ctx)
def FiniteSetUnion(s1, s2):
"""Create the union of two finite sets.
>>> a = Const('a', FiniteSetSort(IntSort()))
>>> b = Const('b', FiniteSetSort(IntSort()))
>>> FiniteSetUnion(a, b)
set.union(a, b)
"""
ctx = _ctx_from_ast_arg_list([s1, s2])
return FiniteSetRef(Z3_mk_finite_set_union(ctx.ref(), s1.as_ast(), s2.as_ast()), ctx)
def FiniteSetIntersect(s1, s2):
"""Create the intersection of two finite sets.
>>> a = Const('a', FiniteSetSort(IntSort()))
>>> b = Const('b', FiniteSetSort(IntSort()))
>>> FiniteSetIntersect(a, b)
set.intersect(a, b)
"""
ctx = _ctx_from_ast_arg_list([s1, s2])
return FiniteSetRef(Z3_mk_finite_set_intersect(ctx.ref(), s1.as_ast(), s2.as_ast()), ctx)
def FiniteSetDifference(s1, s2):
"""Create the set difference of two finite sets.
>>> a = Const('a', FiniteSetSort(IntSort()))
>>> b = Const('b', FiniteSetSort(IntSort()))
>>> FiniteSetDifference(a, b)
set.difference(a, b)
"""
ctx = _ctx_from_ast_arg_list([s1, s2])
return FiniteSetRef(Z3_mk_finite_set_difference(ctx.ref(), s1.as_ast(), s2.as_ast()), ctx)
def FiniteSetMember(elem, set):
"""Check if elem is a member of the finite set.
>>> a = Const('a', FiniteSetSort(IntSort()))
>>> FiniteSetMember(IntVal(1), a)
set.in(1, a)
"""
ctx = _ctx_from_ast_arg_list([elem, set])
return BoolRef(Z3_mk_finite_set_member(ctx.ref(), elem.as_ast(), set.as_ast()), ctx)
def In(elem, set):
return FiniteSetMember(elem, set)
def FiniteSetSize(set):
"""Get the size (cardinality) of a finite set.
>>> a = Const('a', FiniteSetSort(IntSort()))
>>> FiniteSetSize(a)
set.size(a)
"""
ctx = set.ctx
return ArithRef(Z3_mk_finite_set_size(ctx.ref(), set.as_ast()), ctx)
def FiniteSetSubset(s1, s2):
"""Check if s1 is a subset of s2.
>>> a = Const('a', FiniteSetSort(IntSort()))
>>> b = Const('b', FiniteSetSort(IntSort()))
>>> FiniteSetSubset(a, b)
set.subset(a, b)
"""
ctx = _ctx_from_ast_arg_list([s1, s2])
return BoolRef(Z3_mk_finite_set_subset(ctx.ref(), s1.as_ast(), s2.as_ast()), ctx)
def FiniteSetMap(f, set):
"""Apply function f to all elements of the finite set.
>>> f = Array('f', IntSort(), IntSort())
>>> a = Const('a', FiniteSetSort(IntSort()))
>>> FiniteSetMap(f, a)
set.map(f, a)
"""
if isinstance(f, FuncDeclRef):
f = AsArray(f)
ctx = _ctx_from_ast_arg_list([f, set])
return FiniteSetRef(Z3_mk_finite_set_map(ctx.ref(), f.as_ast(), set.as_ast()), ctx)
def FiniteSetFilter(f, set):
"""Filter a finite set using predicate f.
>>> f = Array('f', IntSort(), BoolSort())
>>> a = Const('a', FiniteSetSort(IntSort()))
>>> FiniteSetFilter(f, a)
set.filter(f, a)
"""
if isinstance(f, FuncDeclRef):
f = AsArray(f)
ctx = _ctx_from_ast_arg_list([f, set])
return FiniteSetRef(Z3_mk_finite_set_filter(ctx.ref(), f.as_ast(), set.as_ast()), ctx)
def FiniteSetRange(low, high):
"""Create a finite set of integers in the range [low, high).
>>> FiniteSetRange(IntVal(0), IntVal(5))
set.range(0, 5)
"""
ctx = _ctx_from_ast_arg_list([low, high])
return FiniteSetRef(Z3_mk_finite_set_range(ctx.ref(), low.as_ast(), high.as_ast()), ctx)
#########################################
#
# Datatypes
@ -5482,6 +5798,32 @@ class DatatypeRef(ExprRef):
"""Return the datatype sort of the datatype expression `self`."""
return DatatypeSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx)
def update_field(self, field_accessor, new_value):
"""Return a new datatype expression with the specified field updated.
Args:
field_accessor: The accessor function declaration for the field to update
new_value: The new value for the field
Returns:
A new datatype expression with the field updated, other fields unchanged
Example:
>>> Person = Datatype('Person')
>>> Person.declare('person', ('name', StringSort()), ('age', IntSort()))
>>> Person = Person.create()
>>> person_age = Person.accessor(0, 1) # age accessor
>>> p = Const('p', Person)
>>> p2 = p.update_field(person_age, IntVal(30))
"""
if z3_debug():
_z3_assert(is_func_decl(field_accessor), "Z3 function declaration expected")
_z3_assert(is_expr(new_value), "Z3 expression expected")
return _to_expr_ref(
Z3_datatype_update_field(self.ctx_ref(), field_accessor.ast, self.as_ast(), new_value.as_ast()),
self.ctx
)
def DatatypeSort(name, params=None, ctx=None):
"""Create a reference to a sort that was declared, or will be declared, as a recursive datatype.
@ -5829,7 +6171,9 @@ class Goal(Z3PPObject):
>>> g[1]
y > x
"""
if arg >= len(self):
if arg < 0:
arg += len(self)
if arg < 0 or arg >= len(self):
raise IndexError
return self.get(arg)
@ -6071,7 +6415,9 @@ class AstVector(Z3PPObject):
>>> A[0]
x
"""
if i >= self.__len__():
if i < 0:
i += self.__len__()
if i < 0 or i >= self.__len__():
raise IndexError
Z3_ast_vector_set(self.ctx.ref(), self.vector, i, v.as_ast())
@ -6760,7 +7106,9 @@ class ModelRef(Z3PPObject):
f -> [else -> 0]
"""
if _is_int(idx):
if idx >= len(self):
if idx < 0:
idx += len(self)
if idx < 0 or idx >= len(self):
raise IndexError
num_consts = Z3_model_get_num_consts(self.ctx.ref(), self.model)
if (idx < num_consts):
@ -6774,7 +7122,7 @@ class ModelRef(Z3PPObject):
if isinstance(idx, SortRef):
return self.get_universe(idx)
if z3_debug():
_z3_assert(False, "Integer, Z3 declaration, or Z3 constant expected")
_z3_assert(False, "Integer, Z3 declaration, or Z3 constant expected. Use model.eval instead for complicated expressions")
return None
def decls(self):
@ -7576,13 +7924,6 @@ class Solver(Z3PPObject):
def sexpr(self):
"""Return a formatted string (in Lisp-like format) with all added constraints.
We say the string is in s-expression format.
>>> x = Int('x')
>>> s = Solver()
>>> s.add(x > 0)
>>> s.add(x < 2)
>>> r = s.sexpr()
"""
return Z3_solver_to_string(self.ctx.ref(), self.solver)
@ -7608,6 +7949,39 @@ class Solver(Z3PPObject):
self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e,
)
def solutions(self, t):
"""Returns an iterator over solutions that satisfy the constraints.
The parameter `t` is an expression whose values should be returned.
>>> s = Solver()
>>> x, y, z = Ints("x y z")
>>> s.add(x * x == 4)
>>> print(list(s.solutions(x)))
[-2, 2]
>>> s.reset()
>>> s.add(x >= 0, x < 10)
>>> print(list(s.solutions(x)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> s.reset()
>>> s.add(x >= 0, y < 10, y == 2*x)
>>> print(list(s.solutions([x, y])))
[[0, 0], [1, 2], [2, 4], [3, 6], [4, 8]]
"""
s = Solver()
s.add(self.assertions())
t = _get_args(t)
if isinstance(t, (list, tuple)):
while s.check() == sat:
result = [s.model().eval(t_, model_completion=True) for t_ in t]
yield result
s.add(*(t_ != result_ for t_, result_ in zip(t, result)))
else:
while s.check() == sat:
result = s.model().eval(t, model_completion=True)
yield result
s.add(t != result)
def SolverFor(logic, ctx=None, logFile=None):
"""Create a solver customized for the given logic.
@ -8367,7 +8741,9 @@ class ApplyResult(Z3PPObject):
>>> r[1]
[a == 1, Or(b == 0, b == 1), a > b]
"""
if idx >= len(self):
if idx < 0:
idx += len(self)
if idx < 0 or idx >= len(self):
raise IndexError
return Goal(goal=Z3_apply_result_get_subgoal(self.ctx.ref(), self.result, idx), ctx=self.ctx)
@ -11537,6 +11913,14 @@ def Union(*args):
sz = len(args)
if z3_debug():
_z3_assert(sz > 0, "At least one argument expected.")
arg0 = args[0]
if is_finite_set(arg0):
for a in args[1:]:
if not is_finite_set(a):
raise Z3Exception("All arguments must be regular expressions or finite sets.")
arg0 = arg0 | a
return arg0
if z3_debug():
_z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.")
if sz == 1:
return args[0]
@ -11555,6 +11939,14 @@ def Intersect(*args):
sz = len(args)
if z3_debug():
_z3_assert(sz > 0, "At least one argument expected.")
arg0 = args[0]
if is_finite_set(arg0):
for a in args[1:]:
if not is_finite_set(a):
raise Z3Exception("All arguments must be regular expressions or finite sets.")
arg0 = arg0 & a
return arg0
if z3_debug():
_z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.")
if sz == 1:
return args[0]
@ -11941,50 +12333,64 @@ class UserPropagateBase:
return self.ctx().ref()
def add_fixed(self, fixed):
assert not self.fixed
assert not self._ctx
if self.fixed:
raise Z3Exception("fixed callback already registered")
if self._ctx:
raise Z3Exception("context already initialized")
if self.solver:
Z3_solver_propagate_fixed(self.ctx_ref(), self.solver.solver, _user_prop_fixed)
self.fixed = fixed
def add_created(self, created):
assert not self.created
assert not self._ctx
if self.created:
raise Z3Exception("created callback already registered")
if self._ctx:
raise Z3Exception("context already initialized")
if self.solver:
Z3_solver_propagate_created(self.ctx_ref(), self.solver.solver, _user_prop_created)
self.created = created
def add_final(self, final):
assert not self.final
assert not self._ctx
if self.final:
raise Z3Exception("final callback already registered")
if self._ctx:
raise Z3Exception("context already initialized")
if self.solver:
Z3_solver_propagate_final(self.ctx_ref(), self.solver.solver, _user_prop_final)
self.final = final
def add_eq(self, eq):
assert not self.eq
assert not self._ctx
if self.eq:
raise Z3Exception("eq callback already registered")
if self._ctx:
raise Z3Exception("context already initialized")
if self.solver:
Z3_solver_propagate_eq(self.ctx_ref(), self.solver.solver, _user_prop_eq)
self.eq = eq
def add_diseq(self, diseq):
assert not self.diseq
assert not self._ctx
if self.diseq:
raise Z3Exception("diseq callback already registered")
if self._ctx:
raise Z3Exception("context already initialized")
if self.solver:
Z3_solver_propagate_diseq(self.ctx_ref(), self.solver.solver, _user_prop_diseq)
self.diseq = diseq
def add_decide(self, decide):
assert not self.decide
assert not self._ctx
if self.decide:
raise Z3Exception("decide callback already registered")
if self._ctx:
raise Z3Exception("context already initialized")
if self.solver:
Z3_solver_propagate_decide(self.ctx_ref(), self.solver.solver, _user_prop_decide)
self.decide = decide
def add_on_binding(self, binding):
assert not self.binding
assert not self._ctx
if self.binding:
raise Z3Exception("binding callback already registered")
if self._ctx:
raise Z3Exception("context already initialized")
if self.solver:
Z3_solver_propagate_on_binding(self.ctx_ref(), self.solver.solver, _user_prop_binding)
self.binding = binding
@ -11999,7 +12405,8 @@ class UserPropagateBase:
raise Z3Exception("fresh needs to be overwritten")
def add(self, e):
assert not self._ctx
if self._ctx:
raise Z3Exception("context already initialized")
if self.solver:
Z3_solver_propagate_register(self.ctx_ref(), self.solver.solver, e.ast)
else:

View file

@ -142,7 +142,8 @@ class Numeral:
>>> Numeral("2/3").denominator()
3
"""
assert(self.is_rational())
if not self.is_rational():
raise Z3Exception("denominator() requires a rational numeral")
return Numeral(Z3_get_denominator(self.ctx_ref(), self.as_ast()), self.ctx)
def numerator(self):
@ -151,7 +152,8 @@ class Numeral:
>>> Numeral("2/3").numerator()
2
"""
assert(self.is_rational())
if not self.is_rational():
raise Z3Exception("numerator() requires a rational numeral")
return Numeral(Z3_get_numerator(self.ctx_ref(), self.as_ast()), self.ctx)
def is_irrational(self):
@ -170,7 +172,8 @@ class Numeral:
""" Return a numeral (that is an integer) as a Python long.
"""
assert(self.is_integer())
if not self.is_integer():
raise Z3Exception("as_long() requires an integer numeral")
if sys.version_info.major >= 3:
return int(Z3_get_numeral_string(self.ctx_ref(), self.as_ast()))
else:
@ -181,7 +184,8 @@ class Numeral:
>>> Numeral("1/5").as_fraction()
Fraction(1, 5)
"""
assert(self.is_rational())
if not self.is_rational():
raise Z3Exception("as_fraction() requires a rational numeral")
return Fraction(self.numerator().as_long(), self.denominator().as_long())
def approx(self, precision=10):

View file

@ -760,6 +760,8 @@ class Formatter:
return seq1("Seq", (self.pp_sort(s.basis()), ))
elif isinstance(s, z3.CharSortRef):
return to_format("Char")
elif isinstance(s, z3.FiniteSetSortRef):
return seq1("FiniteSet", (self.pp_sort(s.element_sort()), ))
else:
return to_format(s.name())

View file

@ -78,7 +78,8 @@ def ehash(v):
"""
if z3_debug():
assert is_expr(v)
if not is_expr(v):
raise ValueError(f"ehash expects a Z3 expression, got {type(v)}")
return "{}_{}_{}".format(str(v), v.hash(), v.sort_kind())

View file

@ -980,6 +980,32 @@ typedef enum
3 = 011 = Z3_OP_FPA_RM_TOWARD_NEGATIVE,
4 = 100 = Z3_OP_FPA_RM_TOWARD_ZERO.
- Z3_OP_FINITE_SET_EMPTY: Empty finite set.
- Z3_OP_FINITE_SET_SINGLETON: Finite set containing a single element.
- Z3_OP_FINITE_SET_UNION: Union of two finite sets.
- Z3_OP_FINITE_SET_INTERSECT: Intersection of two finite sets.
- Z3_OP_FINITE_SET_DIFFERENCE: Difference of two finite sets.
- Z3_OP_FINITE_SET_IN: Membership predicate for finite sets.
- Z3_OP_FINITE_SET_SIZE: Cardinality of a finite set.
- Z3_OP_FINITE_SET_SUBSET: Subset predicate for finite sets.
- Z3_OP_FINITE_SET_MAP: Map operation on finite sets.
- Z3_OP_FINITE_SET_FILTER: Filter operation on finite sets.
- Z3_OP_FINITE_SET_RANGE: Range operation for finite sets of integers.
- Z3_OP_FINITE_SET_EXT: Finite set extensionality. Returns a witness element that is in one set but not the other, demonstrating that two sets are different.
- Z3_OP_FINITE_SET_MAP_INVERSE: Inverse image under a finite set map operation. Related to reasoning about the pre-image of elements under set mappings.
- Z3_OP_INTERNAL: internal (often interpreted) symbol, but no additional
information is exposed. Tools may use the string representation of the
function declaration to obtain more information.
@ -1313,6 +1339,21 @@ typedef enum {
Z3_OP_FPA_BVWRAP,
Z3_OP_FPA_BV2RM,
// Finite Sets
Z3_OP_FINITE_SET_EMPTY = 0xc000,
Z3_OP_FINITE_SET_SINGLETON,
Z3_OP_FINITE_SET_UNION,
Z3_OP_FINITE_SET_INTERSECT,
Z3_OP_FINITE_SET_DIFFERENCE,
Z3_OP_FINITE_SET_IN,
Z3_OP_FINITE_SET_SIZE,
Z3_OP_FINITE_SET_SUBSET,
Z3_OP_FINITE_SET_MAP,
Z3_OP_FINITE_SET_FILTER,
Z3_OP_FINITE_SET_RANGE,
Z3_OP_FINITE_SET_EXT,
Z3_OP_FINITE_SET_MAP_INVERSE,
Z3_OP_INTERNAL,
Z3_OP_RECURSIVE,
@ -3413,6 +3454,107 @@ extern "C" {
Z3_ast Z3_API Z3_mk_array_ext(Z3_context c, Z3_ast arg1, Z3_ast arg2);
/**@}*/
/** @name Finite Sets */
/**@{*/
/**
\brief Create a finite set sort.
def_API('Z3_mk_finite_set_sort', SORT, (_in(CONTEXT), _in(SORT)))
*/
Z3_sort Z3_API Z3_mk_finite_set_sort(Z3_context c, Z3_sort elem_sort);
/**
\brief Check if a sort is a finite set sort.
def_API('Z3_is_finite_set_sort', BOOL, (_in(CONTEXT), _in(SORT)))
*/
bool Z3_API Z3_is_finite_set_sort(Z3_context c, Z3_sort s);
/**
\brief Get the element sort of a finite set sort.
def_API('Z3_get_finite_set_sort_basis', SORT, (_in(CONTEXT), _in(SORT)))
*/
Z3_sort Z3_API Z3_get_finite_set_sort_basis(Z3_context c, Z3_sort s);
/**
\brief Create an empty finite set of the given sort.
def_API('Z3_mk_finite_set_empty', AST, (_in(CONTEXT), _in(SORT)))
*/
Z3_ast Z3_API Z3_mk_finite_set_empty(Z3_context c, Z3_sort set_sort);
/**
\brief Create a singleton finite set.
def_API('Z3_mk_finite_set_singleton', AST, (_in(CONTEXT), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_singleton(Z3_context c, Z3_ast elem);
/**
\brief Create the union of two finite sets.
def_API('Z3_mk_finite_set_union', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_union(Z3_context c, Z3_ast s1, Z3_ast s2);
/**
\brief Create the intersection of two finite sets.
def_API('Z3_mk_finite_set_intersect', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_intersect(Z3_context c, Z3_ast s1, Z3_ast s2);
/**
\brief Create the set difference of two finite sets.
def_API('Z3_mk_finite_set_difference', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_difference(Z3_context c, Z3_ast s1, Z3_ast s2);
/**
\brief Check if an element is a member of a finite set.
def_API('Z3_mk_finite_set_member', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_member(Z3_context c, Z3_ast elem, Z3_ast set);
/**
\brief Get the size (cardinality) of a finite set.
def_API('Z3_mk_finite_set_size', AST, (_in(CONTEXT), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_size(Z3_context c, Z3_ast set);
/**
\brief Check if one finite set is a subset of another.
def_API('Z3_mk_finite_set_subset', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_subset(Z3_context c, Z3_ast s1, Z3_ast s2);
/**
\brief Apply a function to all elements of a finite set.
def_API('Z3_mk_finite_set_map', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_map(Z3_context c, Z3_ast f, Z3_ast set);
/**
\brief Filter a finite set using a predicate.
def_API('Z3_mk_finite_set_filter', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_filter(Z3_context c, Z3_ast f, Z3_ast set);
/**
\brief Create a finite set of integers in the range [low, high].
def_API('Z3_mk_finite_set_range', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_finite_set_range(Z3_context c, Z3_ast low, Z3_ast high);
/**@}*/
/** @name Numerals */
/**@{*/
/**

View file

@ -29,7 +29,7 @@ Notes:
void register_z3_replayer_cmds(z3_replayer & in);
void throw_invalid_reference() {
static void throw_invalid_reference() {
TRACE(z3_replayer, tout << "invalid argument reference\n";);
throw z3_replayer_exception("invalid argument reference");
}
@ -155,7 +155,7 @@ struct z3_replayer::imp {
}
void display_args(std::ostream & out) const {
for (unsigned i = 0; i < m_args.size(); i++) {
for (unsigned i = 0; i < m_args.size(); ++i) {
if (i > 0) out << " ";
display_arg(out, m_args[i]);
}
@ -348,7 +348,7 @@ struct z3_replayer::imp {
throw z3_replayer_exception("invalid array size");
uint64_t aidx;
value_kind nk;
for (unsigned i = asz - sz; i < asz; i++) {
for (unsigned i = asz - sz; i < asz; ++i) {
if (m_args[i].m_kind != k)
throw z3_replayer_exception("invalid array: mixed value types");
}
@ -357,7 +357,7 @@ struct z3_replayer::imp {
nk = UINT_ARRAY;
m_unsigned_arrays.push_back(unsigned_vector());
unsigned_vector & v = m_unsigned_arrays.back();
for (unsigned i = asz - sz; i < asz; i++) {
for (unsigned i = asz - sz; i < asz; ++i) {
v.push_back(static_cast<unsigned>(m_args[i].m_uint));
}
}
@ -366,7 +366,7 @@ struct z3_replayer::imp {
nk = INT_ARRAY;
m_int_arrays.push_back(svector<int>());
svector<int> & v = m_int_arrays.back();
for (unsigned i = asz - sz; i < asz; i++) {
for (unsigned i = asz - sz; i < asz; ++i) {
v.push_back(static_cast<int>(m_args[i].m_int));
}
}
@ -375,7 +375,7 @@ struct z3_replayer::imp {
nk = SYMBOL_ARRAY;
m_sym_arrays.push_back(svector<Z3_symbol>());
svector<Z3_symbol> & v = m_sym_arrays.back();
for (unsigned i = asz - sz; i < asz; i++) {
for (unsigned i = asz - sz; i < asz; ++i) {
v.push_back(reinterpret_cast<Z3_symbol>(const_cast<char*>(m_args[i].m_str)));
}
}
@ -383,14 +383,14 @@ struct z3_replayer::imp {
TRACE(z3_replayer_bug,
tout << "args: "; display_args(tout); tout << "\n";
tout << "push_back, sz: " << sz << ", m_obj_arrays.size(): " << m_obj_arrays.size() << "\n";
for (unsigned i = asz - sz; i < asz; i++) {
for (unsigned i = asz - sz; i < asz; ++i) {
tout << "pushing: " << m_args[i].m_obj << "\n";
});
aidx = m_obj_arrays.size();
nk = OBJECT_ARRAY;
m_obj_arrays.push_back(ptr_vector<void>());
ptr_vector<void> & v = m_obj_arrays.back();
for (unsigned i = asz - sz; i < asz; i++) {
for (unsigned i = asz - sz; i < asz; ++i) {
v.push_back(m_args[i].m_obj);
}
}
@ -427,13 +427,13 @@ struct z3_replayer::imp {
case 'R':
// reset
next();
TRACE(z3_replayer, tout << "[" << m_line << "] " << "R\n";);
TRACE(z3_replayer, tout << "[" << m_line << "] R\n";);
reset();
break;
case 'P': {
// push pointer
next(); skip_blank(); read_ptr();
TRACE(z3_replayer, tout << "[" << m_line << "] " << "P " << m_ptr << "\n";);
TRACE(z3_replayer, tout << "[" << m_line << "] P " << m_ptr << "\n";);
if (m_ptr == 0) {
m_args.push_back(nullptr);
}
@ -449,7 +449,7 @@ struct z3_replayer::imp {
case 'S': {
// push string
next(); skip_blank(); read_string();
TRACE(z3_replayer, tout << "[" << m_line << "] " << "S " << m_string.begin() << "\n";);
TRACE(z3_replayer, tout << "[" << m_line << "] S " << m_string.begin() << "\n";);
symbol sym(m_string.begin()); // save string
m_args.push_back(value(STRING, sym.bare_str()));
break;
@ -457,7 +457,7 @@ struct z3_replayer::imp {
case 'N':
// push null symbol
next();
TRACE(z3_replayer, tout << "[" << m_line << "] " << "N\n";);
TRACE(z3_replayer, tout << "[" << m_line << "] N\n";);
m_args.push_back(value(SYMBOL, symbol::null));
break;
case '$': {
@ -658,7 +658,7 @@ struct z3_replayer::imp {
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
ptr_vector<void> const & v = m_obj_arrays[idx];
TRACE(z3_replayer_bug, tout << "pos: " << pos << ", idx: " << idx << " size(): " << v.size() << "\n";
for (unsigned i = 0; i < v.size(); i++) tout << v[i] << " "; tout << "\n";);
for (unsigned i = 0; i < v.size(); ++i) tout << v[i] << " "; tout << "\n";);
return v.data();
}

View file

@ -28,6 +28,7 @@ z3_add_component(ast
expr_map.cpp
expr_stat.cpp
expr_substitution.cpp
finite_set_decl_plugin.cpp
for_each_ast.cpp
for_each_expr.cpp
format.cpp

View file

@ -58,7 +58,7 @@ void act_cache::compress_queue() {
SASSERT(m_qhead > 0);
unsigned sz = m_queue.size();
unsigned j = 0;
for (unsigned i = m_qhead; i < sz; i++, j++) {
for (unsigned i = m_qhead; i < sz; ++i, ++j) {
m_queue[j] = m_queue[i];
}
m_queue.shrink(j);

View file

@ -490,14 +490,14 @@ static bool use_coercion(decl_kind k) {
}
static bool has_real_arg(unsigned arity, sort * const * domain, sort * real_sort) {
for (unsigned i = 0; i < arity; i++)
for (unsigned i = 0; i < arity; ++i)
if (domain[i] == real_sort)
return true;
return false;
}
static bool has_real_arg(ast_manager * m, unsigned num_args, expr * const * args, sort * real_sort) {
for (unsigned i = 0; i < num_args; i++)
for (unsigned i = 0; i < num_args; ++i)
if (args[i]->get_sort() == real_sort)
return true;
return false;

View file

@ -503,9 +503,13 @@ public:
app * mk_power0(expr* arg1, expr* arg2) { return m_manager.mk_app(arith_family_id, OP_POWER0, arg1, arg2); }
app* mk_band(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_BAND, 1, &p, 2, args); }
app* mk_band(unsigned n, std::initializer_list<expr*> args) { parameter p(n); return m_manager.mk_app(arith_family_id, OP_ARITH_BAND, 1, &p, static_cast<unsigned>(args.size()), args.begin()); }
app* mk_shl(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_SHL, 1, &p, 2, args); }
app* mk_shl(unsigned n, std::initializer_list<expr*> args) { parameter p(n); return m_manager.mk_app(arith_family_id, OP_ARITH_SHL, 1, &p, static_cast<unsigned>(args.size()), args.begin()); }
app* mk_ashr(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_ASHR, 1, &p, 2, args); }
app* mk_ashr(unsigned n, std::initializer_list<expr*> args) { parameter p(n); return m_manager.mk_app(arith_family_id, OP_ARITH_ASHR, 1, &p, static_cast<unsigned>(args.size()), args.begin()); }
app* mk_lshr(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_LSHR, 1, &p, 2, args); }
app* mk_lshr(unsigned n, std::initializer_list<expr*> args) { parameter p(n); return m_manager.mk_app(arith_family_id, OP_ARITH_LSHR, 1, &p, static_cast<unsigned>(args.size()), args.begin()); }
app * mk_sin(expr * arg) { return m_manager.mk_app(arith_family_id, OP_SIN, arg); }
app * mk_cos(expr * arg) { return m_manager.mk_app(arith_family_id, OP_COS, arg); }

View file

@ -17,6 +17,7 @@ Revision History:
--*/
#include<sstream>
#include<format>
#include "ast/array_decl_plugin.h"
#include "util/warning.h"
#include "ast/ast_pp.h"
@ -56,7 +57,7 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete
return nullptr;
}
for (unsigned i = 0; i < num_parameters; i++) {
for (unsigned i = 0; i < num_parameters; ++i) {
if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) {
m_manager->raise_exception("invalid array sort definition, parameter is not a sort");
return nullptr;
@ -70,7 +71,7 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete
}
bool is_infinite = false;
bool is_very_big = false;
for (unsigned i = 0; i < num_parameters; i++) {
for (unsigned i = 0; i < num_parameters; ++i) {
sort * s = to_sort(parameters[i].get_ast());
if (s->is_infinite()) {
is_infinite = true;
@ -89,7 +90,7 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete
else {
rational domain_sz(1);
rational num_elements;
for (unsigned i = 0; i < num_parameters - 1; i++) {
for (unsigned i = 0; i < num_parameters - 1; ++i) {
domain_sz *= rational(to_sort(parameters[i].get_ast())->get_num_elements().size(),rational::ui64());
}
if (domain_sz <= rational(128)) {
@ -139,10 +140,8 @@ func_decl * array_decl_plugin::mk_const(sort * s, unsigned arity, sort * const *
func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* domain) {
if (arity != f->get_arity()) {
std::ostringstream buffer;
buffer << "map expects to take as many arguments as the function being mapped, "
<< "it was given " << arity << " but expects " << f->get_arity();
m_manager->raise_exception(buffer.str());
m_manager->raise_exception(std::format("map expects to take as many arguments as the function being mapped, it was given {} but expects {}",
arity, f->get_arity()));
return nullptr;
}
if (arity == 0) {
@ -157,32 +156,21 @@ func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const*
unsigned dom_arity = get_array_arity(domain[0]);
for (unsigned i = 0; i < arity; ++i) {
if (!is_array_sort(domain[i])) {
std::ostringstream buffer;
buffer << "map expects an array sort as argument at position " << i;
m_manager->raise_exception(buffer.str());
m_manager->raise_exception(std::format("map expects an array sort as argument at position {}", i));
return nullptr;
}
if (get_array_arity(domain[i]) != dom_arity) {
std::ostringstream buffer;
buffer << "map expects all arguments to have the same array domain, "
<< "this is not the case for argument " << i;
m_manager->raise_exception(buffer.str());
m_manager->raise_exception(std::format("map expects all arguments to have the same array domain, this is not the case for argument {}", i));
return nullptr;
}
for (unsigned j = 0; j < dom_arity; ++j) {
if (get_array_domain(domain[i],j) != get_array_domain(domain[0],j)) {
std::ostringstream buffer;
buffer << "map expects all arguments to have the same array domain, "
<< "this is not the case for argument " << i;
m_manager->raise_exception(buffer.str());
m_manager->raise_exception(std::format("map expects all arguments to have the same array domain, this is not the case for argument {}", i));
return nullptr;
}
}
if (get_array_range(domain[i]) != f->get_domain(i)) {
std::ostringstream buffer;
buffer << "map expects the argument at position " << i
<< " to have the array range the same as the function";
m_manager->raise_exception(buffer.str());
m_manager->raise_exception(std::format("map expects the argument at position {} to have the array range the same as the function", i));
return nullptr;
}
}
@ -243,9 +231,8 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) {
parameter const* parameters = s->get_parameters();
if (num_parameters != arity) {
std::stringstream strm;
strm << "select requires " << num_parameters << " arguments, but was provided with " << arity << " arguments";
m_manager->raise_exception(strm.str());
m_manager->raise_exception(std::format("select requires {} arguments, but was provided with {} arguments",
num_parameters, arity));
return nullptr;
}
ptr_buffer<sort> new_domain; // we need this because of coercions.
@ -254,10 +241,9 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) {
if (!parameters[i].is_ast() ||
!is_sort(parameters[i].get_ast()) ||
!m_manager->compatible_sorts(domain[i+1], to_sort(parameters[i].get_ast()))) {
std::stringstream strm;
strm << "domain sort " << sort_ref(domain[i+1], *m_manager) << " and parameter ";
strm << parameter_pp(parameters[i], *m_manager) << " do not match";
m_manager->raise_exception(strm.str());
m_manager->raise_exception(std::format("domain sort {} and parameter {} do not match",
to_string(sort_ref(domain[i+1], *m_manager)),
to_string(parameter_pp(parameters[i], *m_manager))));
return nullptr;
}
new_domain.push_back(to_sort(parameters[i].get_ast()));
@ -281,10 +267,8 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) {
return nullptr;
}
if (arity != num_parameters+1) {
std::ostringstream buffer;
buffer << "store expects the first argument to be an array taking " << num_parameters+1
<< ", instead it was passed " << (arity - 1) << "arguments";
m_manager->raise_exception(buffer.str());
m_manager->raise_exception(std::format("store expects the first argument to be an array taking {}, instead it was passed {} arguments",
num_parameters+1, arity - 1));
UNREACHABLE();
return nullptr;
}
@ -298,9 +282,9 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) {
sort* srt1 = to_sort(parameters[i].get_ast());
sort* srt2 = domain[i+1];
if (!m_manager->compatible_sorts(srt1, srt2)) {
std::stringstream strm;
strm << "domain sort " << sort_ref(srt2, *m_manager) << " and parameter sort " << sort_ref(srt1, *m_manager) << " do not match";
m_manager->raise_exception(strm.str());
m_manager->raise_exception(std::format("domain sort {} and parameter sort {} do not match",
to_string(sort_ref(srt2, *m_manager)),
to_string(sort_ref(srt1, *m_manager))));
UNREACHABLE();
return nullptr;
}
@ -333,15 +317,11 @@ func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domai
bool array_decl_plugin::check_set_arguments(unsigned arity, sort * const * domain) {
for (unsigned i = 0; i < arity; ++i) {
if (domain[i] != domain[0]) {
std::ostringstream buffer;
buffer << "arguments " << 1 << " and " << (i+1) << " have different sorts";
m_manager->raise_exception(buffer.str());
m_manager->raise_exception(std::format("arguments {} and {} have different sorts", 1, i+1));
return false;
}
if (domain[i]->get_family_id() != m_family_id) {
std::ostringstream buffer;
buffer << "argument " << (i+1) << " is not of array sort";
m_manager->raise_exception(buffer.str());
m_manager->raise_exception(std::format("argument {} is not of array sort", i+1));
return false;
}
}
@ -443,7 +423,7 @@ func_decl * array_decl_plugin::mk_set_subset(unsigned arity, sort * const * doma
func_decl * array_decl_plugin::mk_as_array(func_decl * f) {
vector<parameter> parameters;
for (unsigned i = 0; i < f->get_arity(); i++) {
for (unsigned i = 0; i < f->get_arity(); ++i) {
parameters.push_back(parameter(f->get_domain(i)));
}
parameters.push_back(parameter(f->get_range()));
@ -570,7 +550,7 @@ expr * array_decl_plugin::get_some_value(sort * s) {
bool array_decl_plugin::is_fully_interp(sort * s) const {
SASSERT(s->is_sort_of(m_family_id, ARRAY_SORT));
unsigned sz = get_array_arity(s);
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
if (!m_manager->is_fully_interp(get_array_domain(s, i)))
return false;
}

View file

@ -1,3 +1,4 @@
/*++
Copyright (c) 2006 Microsoft Corporation
@ -17,6 +18,7 @@ Revision History:
--*/
#include <sstream>
#include <format>
#include <cstring>
#include "ast/ast.h"
#include "ast/ast_pp.h"
@ -40,18 +42,12 @@ Revision History:
// -----------------------------------
parameter::~parameter() {
if (auto p = std::get_if<rational*>(&m_val)) {
dealloc(*p);
}
if (auto p = std::get_if<zstring*>(&m_val)) {
dealloc(*p);
}
}
parameter::parameter(parameter const& other) : m_val(other.m_val) {
if (auto p = std::get_if<rational*>(&m_val)) {
m_val = alloc(rational, **p);
}
if (auto p = std::get_if<zstring*>(&m_val)) {
m_val = alloc(zstring, **p);
}
@ -199,7 +195,7 @@ bool decl_info::operator==(decl_info const & info) const {
std::ostream & operator<<(std::ostream & out, decl_info const & info) {
out << ":fid " << info.get_family_id() << " :decl-kind " << info.get_decl_kind() << " :parameters (";
for (unsigned i = 0; i < info.get_num_parameters(); i++) {
for (unsigned i = 0; i < info.get_num_parameters(); ++i) {
if (i > 0) out << " ";
out << info.get_parameter(i);
}
@ -315,7 +311,7 @@ app::app(func_decl * decl, unsigned num_args, expr * const * args):
expr(AST_APP),
m_decl(decl),
m_num_args(num_args) {
for (unsigned i = 0; i < num_args; i++)
for (unsigned i = 0; i < num_args; ++i)
m_args[i] = args[i];
}
@ -634,7 +630,7 @@ bool decl_plugin::log_constant_meaning_prelude(app * a) {
func_decl * decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned num_args, expr * const * args, sort * range) {
ptr_buffer<sort> sorts;
for (unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; ++i) {
sorts.push_back(args[i]->get_sort());
}
return mk_func_decl(k, num_parameters, parameters, num_args, sorts.data(), range);
@ -652,7 +648,7 @@ bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort
if (arity == 0)
return false;
else {
for (unsigned i = 0; i < arity - 1; i++)
for (unsigned i = 0; i < arity - 1; ++i)
if (domain[i] != m_proof_sort)
return false;
#define is_array(_x_) true
@ -666,7 +662,7 @@ bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, exp
if (num_args == 0)
return false;
else {
for (unsigned i = 0; i < num_args - 1; i++)
for (unsigned i = 0; i < num_args - 1; ++i)
if (args[i]->get_sort() != m_proof_sort)
return false;
return
@ -679,7 +675,7 @@ bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, exp
func_decl * basic_decl_plugin::mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args, bool assoc, bool comm, bool idempotent,
bool flat_associative, bool chainable) {
ptr_buffer<sort> domain;
for (unsigned i = 0; i < num_args; i++)
for (unsigned i = 0; i < num_args; ++i)
domain.push_back(m_bool_sort);
func_decl_info info(m_family_id, k);
info.set_associative(assoc);
@ -705,7 +701,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(
char const * name, basic_op_kind k,
unsigned num_parameters, parameter const* params, unsigned num_parents) {
ptr_buffer<sort> domain;
for (unsigned i = 0; i < num_parents; i++)
for (unsigned i = 0; i < num_parents; ++i)
domain.push_back(m_proof_sort);
domain.push_back(m_bool_sort);
func_decl_info info(m_family_id, k, num_parameters, params);
@ -714,7 +710,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(
func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, bool inc_ref) {
ptr_buffer<sort> domain;
for (unsigned i = 0; i < num_parents; i++)
for (unsigned i = 0; i < num_parents; ++i)
domain.push_back(m_proof_sort);
domain.push_back(m_bool_sort);
func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents+1, domain.data(), m_proof_sort, func_decl_info(m_family_id, k));
@ -724,7 +720,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k,
func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) {
ptr_buffer<sort> domain;
for (unsigned i = 0; i < num_parents; i++)
for (unsigned i = 0; i < num_parents; ++i)
domain.push_back(m_proof_sort);
func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents, domain.data(), m_proof_sort, func_decl_info(m_family_id, k));
m_manager->inc_ref(d);
@ -1021,9 +1017,9 @@ sort* basic_decl_plugin::join(sort* s1, sort* s2) {
return s2;
if (s2 == m_bool_sort && s1->get_family_id() == arith_family_id)
return s1;
std::ostringstream buffer;
buffer << "Sorts " << mk_pp(s1, *m_manager) << " and " << mk_pp(s2, *m_manager) << " are incompatible";
throw ast_exception(buffer.str());
throw ast_exception(std::format("Sorts {} and {} are incompatible",
to_string(mk_pp(s1, *m_manager)),
to_string(mk_pp(s2, *m_manager))));
}
@ -1049,7 +1045,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
func_decl_info info(m_family_id, OP_DISTINCT);
info.set_pairwise();
ptr_buffer<sort> sorts;
for (unsigned i = 1; i < arity; i++) {
for (unsigned i = 1; i < arity; ++i) {
if (domain[i] != domain[0]) {
sort* srt = join(arity, domain);
for (unsigned j = 0; j < arity; ++j)
@ -1144,7 +1140,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
m_manager->raise_exception("invalid label declaration");
return nullptr;
}
for (unsigned i = 2; i < num_parameters; i++) {
for (unsigned i = 2; i < num_parameters; ++i) {
if (!parameters[i].is_symbol()) {
m_manager->raise_exception("invalid label declaration");
return nullptr;
@ -1159,7 +1155,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
m_manager->raise_exception("invalid label literal declaration");
return nullptr;
}
for (unsigned i = 0; i < num_parameters; i++) {
for (unsigned i = 0; i < num_parameters; ++i) {
if (!parameters[i].is_symbol()) {
m_manager->raise_exception("invalid label literal declaration");
return nullptr;
@ -1355,13 +1351,13 @@ void ast_manager::init() {
template<typename T>
static void mark_array_ref(ast_mark& mark, unsigned sz, T * const * a) {
for(unsigned i = 0; i < sz; i++) {
for(unsigned i = 0; i < sz; ++i) {
mark.mark(a[i], true);
}
}
static void mark_array_ref(ast_mark& mark, unsigned sz, parameter const * a) {
for(unsigned i = 0; i < sz; i++) {
for(unsigned i = 0; i < sz; ++i) {
if (a[i].is_ast()) {
mark.mark(a[i].get_ast(), true);
}
@ -1508,14 +1504,14 @@ std::ostream& ast_manager::display(std::ostream& out, parameter const& p) {
void ast_manager::copy_families_plugins(ast_manager const & from) {
TRACE(copy_families_plugins,
tout << "target:\n";
for (family_id fid = 0; m_family_manager.has_family(fid); fid++) {
for (family_id fid = 0; m_family_manager.has_family(fid); ++fid) {
tout << "fid: " << fid << " fidname: " << get_family_name(fid) << "\n";
});
ast_translation trans(const_cast<ast_manager&>(from), *this, false);
// Inheriting plugins can create new family ids. Since new family ids are
// assigned in the order that they are created, this can result in differing
// family ids. To avoid this, we first assign all family ids and only then inherit plugins.
for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) {
for (family_id fid = 0; from.m_family_manager.has_family(fid); ++fid) {
symbol fid_name = from.get_family_name(fid);
if (!m_family_manager.has_family(fid)) {
family_id new_fid = mk_family_id(fid_name);
@ -1523,7 +1519,7 @@ void ast_manager::copy_families_plugins(ast_manager const & from) {
TRACE(copy_families_plugins, tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";);
}
}
for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) {
for (family_id fid = 0; from.m_family_manager.has_family(fid); ++fid) {
SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid));
SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid));
symbol fid_name = from.get_family_name(fid);
@ -1674,7 +1670,7 @@ bool ast_manager::slow_not_contains(ast const * n) {
}
#endif
#if 0
#if 1
static unsigned s_count = 0;
static void track_id(ast_manager& m, ast* n, unsigned id) {
@ -1700,10 +1696,8 @@ ast * ast_manager::register_node_core(ast * n) {
SASSERT(contains);
SASSERT(m_ast_table.contains(n));
if (is_func_decl(r) && to_func_decl(r)->get_range() != to_func_decl(n)->get_range()) {
std::ostringstream buffer;
buffer << "Recycling of declaration for the same name '" << to_func_decl(r)->get_name().str()
<< "' and domain, but different range type is not permitted";
throw ast_exception(buffer.str());
throw ast_exception(std::format("Recycling of declaration for the same name '{}' and domain, but different range type is not permitted",
to_func_decl(r)->get_name().str()));
}
deallocate_node(n, ::get_node_size(n));
return r;
@ -1715,9 +1709,8 @@ ast * ast_manager::register_node_core(ast * n) {
n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk();
// track_id(*this, n, 9213);
// TRACE(ast, tout << (s_count++) << " Object " << n->m_id << " was created.\n";);
// TRACE(ast, tout << (s_count++) << " Object " << n->m_id << " was created.\n";);
TRACE(mk_var_bug, tout << "mk_ast: " << n->m_id << "\n";);
// increment reference counters
switch (n->get_kind()) {
@ -1747,7 +1740,7 @@ ast * ast_manager::register_node_core(ast * n) {
if (is_label(t))
f->m_has_labels = true;
unsigned depth = 0;
for (unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; ++i) {
expr * arg = t->get_arg(i);
inc_ref(arg);
unsigned arg_depth = 0;
@ -1914,6 +1907,10 @@ app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_args, expr *
return mk_app(fid, k, 0, nullptr, num_args, args);
}
app * ast_manager::mk_app(family_id fid, decl_kind k, std::span<expr* const> args) {
return mk_app(fid, k, 0, nullptr, static_cast<unsigned>(args.size()), args.data());
}
app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg) {
return mk_app(fid, k, 0, nullptr, 1, &arg);
}
@ -2019,14 +2016,14 @@ void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * c
if (decl->is_associative()) {
sort * expected = decl->get_domain(0);
for (unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; ++i) {
sort * given = args[i]->get_sort();
if (!compatible_sorts(expected, given)) {
std::ostringstream buff;
buff << "invalid function application for " << decl->get_name() << ", ";
buff << "sort mismatch on argument at position " << (i+1) << ", ";
buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m);
throw ast_exception(buff.str());
throw ast_exception(std::format("invalid function application for {}, sort mismatch on argument at position {}, expected {} but given {}",
to_string(decl->get_name()),
i + 1,
to_string(mk_pp(expected, m)),
to_string(mk_pp(given, m))));
}
}
}
@ -2034,15 +2031,15 @@ void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * c
if (decl->get_arity() != num_args) {
throw ast_exception("invalid function application, wrong number of arguments");
}
for (unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; ++i) {
sort * expected = decl->get_domain(i);
sort * given = args[i]->get_sort();
if (!compatible_sorts(expected, given)) {
std::ostringstream buff;
buff << "invalid function application for " << decl->get_name() << ", ";
buff << "sort mismatch on argument at position " << (i+1) << ", ";
buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m);
throw ast_exception(buff.str());
throw ast_exception(std::format("invalid function application for {}, sort mismatch on argument at position {}, expected {} but given {}",
to_string(decl->get_name()),
i + 1,
to_string(mk_pp(expected, m)),
to_string(mk_pp(given, m))));
}
}
}
@ -2099,7 +2096,7 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co
if (decl->is_associative()) {
sort * d = decl->get_domain(0);
if (d->get_family_id() == arith_family_id) {
for (unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; ++i) {
if (d != args[i]->get_sort())
return true;
}
@ -2111,7 +2108,7 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co
// So, there is no point in coercing the input arguments.
return false;
}
for (unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; ++i) {
sort * d = decl->get_domain(i);
if (d->get_family_id() == arith_family_id && d != args[i]->get_sort())
return true;
@ -2148,7 +2145,7 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const
try {
if (m_int_real_coercions && coercion_needed(decl, num_args, args)) {
expr_ref_buffer new_args(*this);
for (unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; ++i) {
sort * d = decl->is_associative() ? decl->get_domain(0) : decl->get_domain(i);
new_args.push_back(coerce_to(args[i], d));
}
@ -2178,7 +2175,7 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const
ast_ll_pp(*m_trace_stream, *this, r);
else {
*m_trace_stream << r->get_decl()->get_name();
for (unsigned i = 0; i < r->get_num_args(); i++)
for (unsigned i = 0; i < r->get_num_args(); ++i)
*m_trace_stream << " #" << r->get_arg(i)->get_id();
*m_trace_stream << "\n";
}
@ -2193,16 +2190,14 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const
}
void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) {
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
sort * actual_sort = es[i]->get_sort();
sort * expected_sort = f->is_associative() ? f->get_domain(0) : f->get_domain(i);
if (expected_sort != actual_sort) {
std::ostringstream buffer;
buffer << "Sort mismatch at argument #" << (i+1)
<< " for function " << mk_pp(f,*this)
<< " supplied sort is "
<< mk_pp(actual_sort, *this);
throw ast_exception(buffer.str());
throw ast_exception(std::format("Sort mismatch at argument #{} for function {} supplied sort is {}",
i + 1,
to_string(mk_pp(f, *this)),
to_string(mk_pp(actual_sort, *this))));
}
}
}
@ -2223,12 +2218,13 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar
decl->get_family_id() == basic_family_id && !decl->is_associative());
if (type_error) {
std::ostringstream buffer;
buffer << "Wrong number of arguments (" << num_args
<< ") passed to function " << mk_pp(decl, *this) << " ";
std::string arg_list;
for (unsigned i = 0; i < num_args; ++i)
buffer << "\narg: " << mk_pp(args[i], *this) << "\n";
throw ast_exception(std::move(buffer).str());
arg_list += std::format("\narg: {}\n", to_string(mk_pp(args[i], *this)));
throw ast_exception(std::format("Wrong number of arguments ({}) passed to function {} {}",
num_args,
to_string(mk_pp(decl, *this)),
arg_list));
}
app * r = nullptr;
if (num_args == 1 && decl->is_chainable() && decl->get_arity() == 2) {
@ -2246,14 +2242,14 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar
}
else if (decl->is_left_associative()) {
r = mk_app_core(decl, args[0], args[1]);
for (unsigned i = 2; i < num_args; i++) {
for (unsigned i = 2; i < num_args; ++i) {
r = mk_app_core(decl, r, args[i]);
}
}
else if (decl->is_chainable()) {
TRACE(chainable, tout << "chainable...\n";);
ptr_buffer<expr> new_args;
for (unsigned i = 1; i < num_args; i++) {
for (unsigned i = 1; i < num_args; ++i) {
new_args.push_back(mk_app_core(decl, args[i-1], args[i]));
}
r = mk_and(new_args.size(), new_args.data());
@ -2336,7 +2332,7 @@ app * ast_manager::mk_label(bool pos, unsigned num_names, symbol const * names,
SASSERT(n->get_sort() == m_bool_sort);
buffer<parameter> p;
p.push_back(parameter(static_cast<int>(pos)));
for (unsigned i = 0; i < num_names; i++)
for (unsigned i = 0; i < num_names; ++i)
p.push_back(parameter(names[i]));
return mk_app(label_family_id, OP_LABEL, p.size(), p.data(), 1, &n);
}
@ -2351,7 +2347,7 @@ bool ast_manager::is_label(expr const * n, bool & pos, buffer<symbol> & names) c
}
func_decl const * decl = to_app(n)->get_decl();
pos = decl->get_parameter(0).get_int() != 0;
for (unsigned i = 1; i < decl->get_num_parameters(); i++)
for (unsigned i = 1; i < decl->get_num_parameters(); ++i)
names.push_back(decl->get_parameter(i).get_symbol());
return true;
}
@ -2359,7 +2355,7 @@ bool ast_manager::is_label(expr const * n, bool & pos, buffer<symbol> & names) c
app * ast_manager::mk_label_lit(unsigned num_names, symbol const * names) {
SASSERT(num_names > 0);
buffer<parameter> p;
for (unsigned i = 0; i < num_names; i++)
for (unsigned i = 0; i < num_names; ++i)
p.push_back(parameter(names[i]));
return mk_app(label_family_id, OP_LABEL_LIT, p.size(), p.data(), 0, nullptr);
}
@ -2473,7 +2469,7 @@ quantifier * ast_manager::mk_lambda(unsigned num_decls, sort * const * decl_sort
static bool same_patterns(quantifier * q, unsigned num_patterns, expr * const * patterns) {
if (num_patterns != q->get_num_patterns())
return false;
for (unsigned i = 0; i < num_patterns; i++)
for (unsigned i = 0; i < num_patterns; ++i)
if (q->get_pattern(i) != patterns[i])
return false;
return true;
@ -2483,7 +2479,7 @@ static bool same_patterns(quantifier * q, unsigned num_patterns, expr * const *
static bool same_no_patterns(quantifier * q, unsigned num_no_patterns, expr * const * no_patterns) {
if (num_no_patterns != q->get_num_no_patterns())
return false;
for (unsigned i = 0; i < num_no_patterns; i++)
for (unsigned i = 0; i < num_no_patterns; ++i)
if (q->get_no_pattern(i) != no_patterns[i])
return false;
return true;
@ -2592,6 +2588,15 @@ quantifier * ast_manager::update_quantifier(quantifier * q, quantifier_kind k, u
num_patterns == 0 ? q->get_no_patterns() : nullptr);
}
quantifier * ast_manager::update_quantifier(quantifier * q, std::initializer_list<expr*> patterns, expr * body) {
return update_quantifier(q, static_cast<unsigned>(patterns.size()), patterns.begin(), body);
}
quantifier * ast_manager::update_quantifier(quantifier * q, std::initializer_list<expr*> patterns, std::initializer_list<expr*> no_patterns, expr * body) {
return update_quantifier(q, static_cast<unsigned>(patterns.size()), patterns.begin(),
static_cast<unsigned>(no_patterns.size()), no_patterns.begin(), body);
}
app * ast_manager::mk_distinct(unsigned num_args, expr * const * args) {
return mk_app(basic_family_id, OP_DISTINCT, num_args, args);
}
@ -2602,9 +2607,9 @@ app * ast_manager::mk_distinct_expanded(unsigned num_args, expr * const * args)
if (num_args == 2)
return mk_not(mk_eq(args[0], args[1]));
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < num_args - 1; i++) {
for (unsigned i = 0; i < num_args - 1; ++i) {
expr * a1 = args[i];
for (unsigned j = i + 1; j < num_args; j++) {
for (unsigned j = i + 1; j < num_args; ++j) {
expr * a2 = args[j];
new_args.push_back(mk_not(mk_eq(a1, a2)));
}
@ -2629,7 +2634,7 @@ expr_dependency * ast_manager::mk_leaf(expr * t) {
expr_dependency * ast_manager::mk_join(unsigned n, expr * const * ts) {
expr_dependency * d = nullptr;
for (unsigned i = 0; i < n; i++)
for (unsigned i = 0; i < n; ++i)
d = mk_join(d, mk_leaf(ts[i]));
return d;
}
@ -2897,7 +2902,7 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3, proof *
proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs) {
SASSERT(num_proofs > 0);
proof * r = proofs[0];
for (unsigned i = 1; i < num_proofs; i++)
for (unsigned i = 1; i < num_proofs; ++i)
r = mk_transitivity(r, proofs[i]);
return r;
}
@ -2908,7 +2913,7 @@ proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs
if (num_proofs == 1)
return proofs[0];
DEBUG_CODE({
for (unsigned i = 0; i < num_proofs; i++) {
for (unsigned i = 0; i < num_proofs; ++i) {
SASSERT(proofs[i]);
SASSERT(!is_reflexivity(proofs[i]));
}
@ -3050,7 +3055,7 @@ proof * ast_manager::mk_def_axiom(expr * ax) {
proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs) {
SASSERT(num_proofs >= 2);
DEBUG_CODE(for (unsigned i = 0; i < num_proofs; i++) SASSERT(has_fact(proofs[i])););
DEBUG_CODE(for (unsigned i = 0; i < num_proofs; ++i) SASSERT(has_fact(proofs[i])););
ptr_buffer<expr> args;
expr * fact;
expr * f1 = get_fact(proofs[0]);
@ -3091,10 +3096,10 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
bool_vector found;
#endif
ast_mark mark;
for (unsigned i = 0; i < num_args; i++) {
for (unsigned i = 0; i < num_args; ++i) {
bool found_complement = false;
expr * lit = cls->get_arg(i);
for (unsigned j = 1; j < num_proofs; j++) {
for (unsigned j = 1; j < num_proofs; ++j) {
expr const * _fact = get_fact(proofs[j]);
if (is_complement(lit, _fact)) {
found_complement = true;
@ -3110,9 +3115,9 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
}
}
DEBUG_CODE({
for (unsigned i = 1; proofs_enabled() && i < num_proofs; i++) {
for (unsigned i = 1; proofs_enabled() && i < num_proofs; ++i) {
CTRACE(mk_unit_resolution_bug, !found.get(i, false),
for (unsigned j = 0; j < num_proofs; j++) {
for (unsigned j = 0; j < num_proofs; ++j) {
if (j == i) tout << "Index " << i << " was not found:\n";
tout << mk_ll_pp(get_fact(proofs[j]), *this);
});
@ -3140,7 +3145,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact) {
TRACE(unit_bug,
for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
for (unsigned i = 0; i < num_proofs; ++i) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
tout << "===>\n";
tout << mk_pp(new_fact, *this) << "\n";);
@ -3158,7 +3163,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
app * cls = to_app(f1);
unsigned cls_sz = cls->get_num_args();
CTRACE(unit_bug, !(num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact))),
for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
for (unsigned i = 0; i < num_proofs; ++i) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
tout << "===>\n";
tout << mk_pp(new_fact, *this) << "\n";);
//
@ -3166,10 +3171,10 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
// but formula could have repeated literals that are merged in the clausal representation.
//
unsigned num_matches = 0, num_occ = 0;
for (unsigned i = 0; i < cls_sz; i++) {
for (unsigned i = 0; i < cls_sz; ++i) {
expr * lit = cls->get_arg(i);
unsigned j = 1;
for (; j < num_proofs; j++) {
for (; j < num_proofs; ++j) {
if (is_complement(lit, get_fact(proofs[j]))) {
num_matches++;
break;
@ -3231,7 +3236,7 @@ proof * ast_manager::mk_iff_oeq(proof * p) {
}
bool ast_manager::check_nnf_proof_parents(unsigned num_proofs, proof * const * proofs) const {
for (unsigned i = 0; i < num_proofs; i++) {
for (unsigned i = 0; i < num_proofs; ++i) {
if (!has_fact(proofs[i]))
return false;
if (!is_oeq(get_fact(proofs[i])))
@ -3338,15 +3343,14 @@ proof * ast_manager::mk_th_lemma(
if (proofs_disabled())
return nullptr;
ptr_buffer<expr> args;
vector<parameter> parameters;
parameters.push_back(parameter(get_family_name(tid)));
for (unsigned i = 0; i < num_params; ++i) {
parameters.push_back(params[i]);
}
for (unsigned i = 0; i < num_params; ++i)
parameters.push_back(params[i]);
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.push_back(fact);
return mk_app(basic_family_id, PR_TH_LEMMA, num_params+1, parameters.data(), args.size(), args.data());
return mk_app(basic_family_id, PR_TH_LEMMA, parameters.size(), parameters.data(), args.size(), args.data());
}
proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premises, expr* concl,

View file

@ -26,7 +26,7 @@ Revision History:
#include "util/symbol.h"
#include "util/rational.h"
#include "util/hash.h"
#include "util/optional.h"
#include <optional>
#include "util/trace.h"
#include "util/bit_vector.h"
#include "util/symbol_table.h"
@ -48,6 +48,8 @@ Revision History:
#include "util/dependency.h"
#include "util/rlimit.h"
#include <variant>
#include <span>
#include <initializer_list>
#define RECYCLE_FREE_AST_INDICES
@ -122,20 +124,20 @@ private:
ast*, // for PARAM_AST
symbol, // for PARAM_SYMBOL
zstring*, // for PARAM_ZSTRING
rational*, // for PARAM_RATIONAL
rational, // for PARAM_RATIONAL
double, // for PARAM_DOUBLE (remark: this is not used in float_decl_plugin)
unsigned // for PARAM_EXTERNAL
> m_val;
> m_val = 0;
public:
parameter() : m_val(0) {}
parameter() noexcept = default;
explicit parameter(int val): m_val(val) {}
explicit parameter(unsigned val): m_val((int)val) {}
explicit parameter(ast * p): m_val(p) {}
explicit parameter(symbol const & s): m_val(s) {}
explicit parameter(rational const & r): m_val(alloc(rational, r)) {}
explicit parameter(rational && r) : m_val(alloc(rational, std::move(r))) {}
explicit parameter(rational const & r): m_val(r) {}
explicit parameter(rational && r) : m_val(std::move(r)) {}
explicit parameter(zstring const& s): m_val(alloc(zstring, s)) {}
explicit parameter(zstring && s): m_val(alloc(zstring, std::move(s))) {}
explicit parameter(double d): m_val(d) {}
@ -187,7 +189,7 @@ public:
int get_int() const { SASSERT(is_int()); return std::get<int>(m_val); }
ast * get_ast() const { SASSERT(is_ast()); return std::get<ast*>(m_val); }
symbol get_symbol() const { SASSERT(is_symbol()); return std::get<symbol>(m_val); }
rational const & get_rational() const { SASSERT(is_rational()); return *std::get<rational*>(m_val); }
rational const & get_rational() const { SASSERT(is_rational()); return std::get<rational>(m_val); }
zstring const& get_zstring() const { SASSERT(is_zstring()); return *std::get<zstring*>(m_val); }
double get_double() const { SASSERT(is_double()); return std::get<double>(m_val); }
unsigned get_ext_id() const { SASSERT(is_external()); return std::get<unsigned>(m_val); }
@ -314,12 +316,12 @@ class sort_size {
// of elements is at least bigger than 2^64.
SS_FINITE_VERY_BIG,
SS_INFINITE
} m_kind;
uint64_t m_size; // It is only meaningful if m_kind == SS_FINITE
} m_kind = SS_INFINITE;
uint64_t m_size = 0; // It is only meaningful if m_kind == SS_FINITE
sort_size(kind_t k, uint64_t r):m_kind(k), m_size(r) {}
public:
sort_size():m_kind(SS_INFINITE), m_size(0) {}
sort_size(uint64_t const & sz):m_kind(SS_FINITE), m_size(sz) {}
sort_size() = default;
sort_size(uint64_t sz):m_kind(SS_FINITE), m_size(sz) {}
explicit sort_size(rational const& r) {
if (r.is_uint64()) {
m_kind = SS_FINITE;
@ -1667,17 +1669,29 @@ public:
}
template<typename T>
void inc_array_ref(unsigned sz, T * const * a) {
for(unsigned i = 0; i < sz; i++) {
inc_ref(a[i]);
void inc_array_ref(std::span<T * const> a) {
for(auto elem : a) {
inc_ref(elem);
}
}
// Backward compatibility overload
template<typename T>
void inc_array_ref(unsigned sz, T * const * a) {
inc_array_ref(std::span<T * const>(a, sz));
}
template<typename T>
void dec_array_ref(std::span<T * const> a) {
for(auto elem : a) {
dec_ref(elem);
}
}
// Backward compatibility overload
template<typename T>
void dec_array_ref(unsigned sz, T * const * a) {
for(unsigned i = 0; i < sz; i++) {
dec_ref(a[i]);
}
dec_array_ref(std::span<T * const>(a, sz));
}
static unsigned get_node_size(ast const * n);
@ -1719,13 +1733,13 @@ private:
sort * mk_sort(symbol const & name, sort_info * info);
public:
sort * mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters);
[[nodiscard]] sort * mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters);
sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, nullptr); }
[[nodiscard]] sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, nullptr); }
sort * mk_type_var(symbol const& name);
[[nodiscard]] sort * mk_type_var(symbol const& name);
sort * mk_sort(symbol const & name, sort_info const & info) {
[[nodiscard]] sort * mk_sort(symbol const & name, sort_info const & info) {
if (info.get_family_id() == null_family_id) {
return mk_uninterpreted_sort(name);
}
@ -1734,15 +1748,15 @@ public:
}
}
sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr);
[[nodiscard]] sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr);
sort * substitute(sort* s, unsigned n, sort * const * src, sort * const * dst);
[[nodiscard]] sort * substitute(sort* s, unsigned n, sort * const * src, sort * const * dst);
sort * mk_bool_sort() const { return m_bool_sort; }
[[nodiscard]] sort * mk_bool_sort() const { return m_bool_sort; }
sort * mk_proof_sort() const { return m_proof_sort; }
[[nodiscard]] sort * mk_proof_sort() const { return m_proof_sort; }
sort * mk_fresh_sort(char const * prefix = "");
[[nodiscard]] sort * mk_fresh_sort(char const * prefix = "");
bool is_uninterp(sort const * s) const { return s->get_family_id() == null_family_id || s->get_family_id() == user_sort_family_id; }
@ -1767,24 +1781,26 @@ public:
bool has_type_var(unsigned n, sort* const* domain, sort* range) const;
func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
[[nodiscard]] func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range = nullptr);
func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
[[nodiscard]] func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned num_args, expr * const * args, sort * range = nullptr);
app * mk_app(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr,
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr,
unsigned num_args = 0, expr * const * args = nullptr, sort * range = nullptr);
app * mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args);
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args);
app * mk_app(family_id fid, decl_kind k, expr * arg);
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, std::span<expr* const> args);
app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2);
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, expr * arg);
app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3);
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2);
app * mk_const(family_id fid, decl_kind k) { return mk_app(fid, k, 0, static_cast<expr * const *>(nullptr)); }
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3);
[[nodiscard]] app * mk_const(family_id fid, decl_kind k) { return mk_app(fid, k, 0, static_cast<expr * const *>(nullptr)); }
private:
func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
func_decl_info * info);
@ -1793,12 +1809,14 @@ private:
app * mk_app_core(func_decl * decl, unsigned num_args, expr * const * args);
app * mk_and(unsigned num_args, expr * const * args) { return mk_app(basic_family_id, OP_AND, num_args, args); }
public:
func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) {
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) {
return mk_func_decl(name, arity, domain, range, static_cast<func_decl_info *>(nullptr));
}
func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
func_decl_info const & info) {
if (info.is_null()) {
return mk_func_decl(name, arity, domain, range, static_cast<func_decl_info *>(nullptr));
@ -1808,55 +1826,55 @@ public:
}
}
func_decl * mk_func_decl(unsigned arity, sort * const * domain, func_decl_info const & info) {
[[nodiscard]] func_decl * mk_func_decl(unsigned arity, sort * const * domain, func_decl_info const & info) {
return mk_func_decl(info.get_family_id(), info.get_decl_kind(), info.get_num_parameters(), info.get_parameters(),
arity, domain);
}
func_decl * mk_skolem_const_decl(symbol const& name, sort* s) {
[[nodiscard]] func_decl * mk_skolem_const_decl(symbol const& name, sort* s) {
func_decl_info info;
info.set_skolem(true);
return mk_func_decl(name, static_cast<unsigned>(0), nullptr, s, info);
}
func_decl * mk_const_decl(const char* name, sort * s) {
[[nodiscard]] func_decl * mk_const_decl(const char* name, sort * s) {
return mk_func_decl(symbol(name), static_cast<unsigned>(0), nullptr, s);
}
func_decl * mk_const_decl(std::string const& name, sort * s) {
[[nodiscard]] func_decl * mk_const_decl(std::string const& name, sort * s) {
return mk_func_decl(symbol(name.c_str()), static_cast<unsigned>(0), nullptr, s);
}
func_decl * mk_const_decl(symbol const & name, sort * s) {
[[nodiscard]] func_decl * mk_const_decl(symbol const & name, sort * s) {
return mk_func_decl(name, static_cast<unsigned>(0), nullptr, s);
}
func_decl * mk_const_decl(symbol const & name, sort * s, func_decl_info const & info) {
[[nodiscard]] func_decl * mk_const_decl(symbol const & name, sort * s, func_decl_info const & info) {
return mk_func_decl(name, static_cast<unsigned>(0), nullptr, s, info);
}
func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range, func_decl_info const & info) {
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range, func_decl_info const & info) {
return mk_func_decl(name, 1, &domain, range, info);
}
func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range) {
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range) {
return mk_func_decl(name, 1, &domain, range);
}
func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, func_decl_info const & info) {
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, func_decl_info const & info) {
sort * d[2] = { domain1, domain2 };
return mk_func_decl(name, 2, d, range, info);
}
func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range) {
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range) {
sort * d[2] = { domain1, domain2 };
return mk_func_decl(name, 2, d, range);
}
func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
bool assoc, bool comm = false, bool inj = false);
func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, bool assoc, bool comm = false) {
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, bool assoc, bool comm = false) {
sort * d[2] = { domain1, domain2 };
return mk_func_decl(name, 2, d, range, assoc, comm, false);
}
@ -1869,113 +1887,113 @@ public:
return !p || p->is_considered_uninterpreted(f);
}
app * mk_app(func_decl * decl, unsigned num_args, expr * const * args);
[[nodiscard]] app * mk_app(func_decl * decl, unsigned num_args, expr * const * args);
app* mk_app(func_decl* decl, ref_vector<expr, ast_manager> const& args) {
[[nodiscard]] app* mk_app(func_decl* decl, ref_vector<expr, ast_manager> const& args) {
return mk_app(decl, args.size(), args.data());
}
app* mk_app(func_decl* decl, ref_buffer<expr, ast_manager> const& args) {
[[nodiscard]] app* mk_app(func_decl* decl, ref_buffer<expr, ast_manager> const& args) {
return mk_app(decl, args.size(), args.data());
}
app* mk_app(func_decl* decl, ref_vector<app, ast_manager> const& args) {
[[nodiscard]] app* mk_app(func_decl* decl, ref_vector<app, ast_manager> const& args) {
return mk_app(decl, args.size(), (expr*const*)args.data());
}
app * mk_app(func_decl * decl, ptr_vector<expr> const& args) {
[[nodiscard]] app * mk_app(func_decl * decl, ptr_vector<expr> const& args) {
return mk_app(decl, args.size(), args.data());
}
app * mk_app(func_decl * decl, ptr_buffer<expr> const& args) {
[[nodiscard]] app * mk_app(func_decl * decl, ptr_buffer<expr> const& args) {
return mk_app(decl, args.size(), args.data());
}
app * mk_app(func_decl * decl, ptr_vector<app> const& args) {
[[nodiscard]] app * mk_app(func_decl * decl, ptr_vector<app> const& args) {
return mk_app(decl, args.size(), (expr*const*)args.data());
}
app * mk_app(func_decl * decl, expr * const * args) {
[[nodiscard]] app * mk_app(func_decl * decl, expr * const * args) {
return mk_app(decl, decl->get_arity(), args);
}
app * mk_app(func_decl * decl, expr * arg) {
[[nodiscard]] app * mk_app(func_decl * decl, expr * arg) {
SASSERT(decl->get_arity() == 1);
return mk_app(decl, 1, &arg);
}
app * mk_app(func_decl * decl, expr * arg1, expr * arg2) {
[[nodiscard]] app * mk_app(func_decl * decl, expr * arg1, expr * arg2) {
SASSERT(decl->get_arity() == 2);
expr * args[2] = { arg1, arg2 };
return mk_app(decl, 2, args);
}
app * mk_app(func_decl * decl, expr * arg1, expr * arg2, expr * arg3) {
[[nodiscard]] app * mk_app(func_decl * decl, expr * arg1, expr * arg2, expr * arg3) {
SASSERT(decl->get_arity() == 3);
expr * args[3] = { arg1, arg2, arg3 };
return mk_app(decl, 3, args);
}
app * mk_app(symbol const& name, unsigned n, expr* const* args, sort* range);
[[nodiscard]] app * mk_app(symbol const& name, unsigned n, expr* const* args, sort* range);
app * mk_const(func_decl * decl) {
[[nodiscard]] app * mk_const(func_decl * decl) {
SASSERT(decl->get_arity() == 0);
return mk_app(decl, static_cast<unsigned>(0), static_cast<expr**>(nullptr));
}
app * mk_skolem_const(symbol const & name, sort * s) {
[[nodiscard]] app * mk_skolem_const(symbol const & name, sort * s) {
return mk_const(mk_skolem_const_decl(name, s));
}
app * mk_const(symbol const & name, sort * s) {
[[nodiscard]] app * mk_const(symbol const & name, sort * s) {
return mk_const(mk_const_decl(name, s));
}
app * mk_const(std::string const & name, sort * s) {
[[nodiscard]] app * mk_const(std::string const & name, sort * s) {
return mk_const(mk_const_decl(name, s));
}
app * mk_const(char const* name, sort * s) {
[[nodiscard]] app * mk_const(char const* name, sort * s) {
return mk_const(mk_const_decl(name, s));
}
func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity,
[[nodiscard]] func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity,
sort * const * domain, sort * range, bool skolem = true);
func_decl * mk_fresh_func_decl(unsigned arity, sort * const * domain, sort * range, bool skolem = true) {
[[nodiscard]] func_decl * mk_fresh_func_decl(unsigned arity, sort * const * domain, sort * range, bool skolem = true) {
return mk_fresh_func_decl(symbol::null, symbol::null, arity, domain, range, skolem);
}
func_decl * mk_fresh_func_decl(char const * prefix, char const * suffix, unsigned arity,
[[nodiscard]] func_decl * mk_fresh_func_decl(char const * prefix, char const * suffix, unsigned arity,
sort * const * domain, sort * range, bool skolem = true) {
return mk_fresh_func_decl(symbol(prefix), symbol(suffix), arity, domain, range, skolem);
}
func_decl * mk_fresh_func_decl(char const * prefix, unsigned arity, sort * const * domain, sort * range, bool skolem = true) {
[[nodiscard]] func_decl * mk_fresh_func_decl(char const * prefix, unsigned arity, sort * const * domain, sort * range, bool skolem = true) {
return mk_fresh_func_decl(symbol(prefix), symbol::null, arity, domain, range, skolem);
}
bool is_parametric_function(func_decl* f, func_decl *& g) const;
app * mk_fresh_const(char const * prefix, sort * s, bool skolem = true) {
[[nodiscard]] app * mk_fresh_const(char const * prefix, sort * s, bool skolem = true) {
return mk_const(mk_fresh_func_decl(prefix, 0, nullptr, s, skolem));
}
app * mk_fresh_const(std::string const& prefix, sort * s, bool skolem = true) {
[[nodiscard]] app * mk_fresh_const(std::string const& prefix, sort * s, bool skolem = true) {
return mk_fresh_const(prefix.c_str(), s, skolem);
}
app * mk_fresh_const(symbol const& prefix, sort * s, bool skolem = true) {
[[nodiscard]] app * mk_fresh_const(symbol const& prefix, sort * s, bool skolem = true) {
return mk_const(mk_fresh_func_decl(prefix, symbol::null, 0, nullptr, s, skolem));
}
symbol mk_fresh_var_name(char const * prefix = nullptr);
[[nodiscard]] symbol mk_fresh_var_name(char const * prefix = nullptr);
var * mk_var(unsigned idx, sort * ty);
[[nodiscard]] var * mk_var(unsigned idx, sort * ty);
app * mk_label(bool pos, unsigned num_names, symbol const * names, expr * n);
[[nodiscard]] app * mk_label(bool pos, unsigned num_names, symbol const * names, expr * n);
app * mk_label(bool pos, symbol const & name, expr * n);
[[nodiscard]] app * mk_label(bool pos, symbol const & name, expr * n);
bool is_label(expr const * n, bool & pos, buffer<symbol> & names) const;
@ -1999,9 +2017,9 @@ public:
}
}
app * mk_label_lit(unsigned num_names, symbol const * names);
[[nodiscard]] app * mk_label_lit(unsigned num_names, symbol const * names);
app * mk_label_lit(symbol const & name);
[[nodiscard]] app * mk_label_lit(symbol const & name);
bool is_label_lit(expr const * n, buffer<symbol> & names) const;
@ -2009,9 +2027,9 @@ public:
family_id get_label_family_id() const { return label_family_id; }
app * mk_pattern(unsigned num_exprs, app * const * exprs);
[[nodiscard]] app * mk_pattern(unsigned num_exprs, app * const * exprs);
app * mk_pattern(app * expr) { return mk_pattern(1, &expr); }
[[nodiscard]] app * mk_pattern(app * expr) { return mk_pattern(1, &expr); }
bool is_pattern(expr const * n) const;
@ -2019,12 +2037,12 @@ public:
public:
quantifier * mk_quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
[[nodiscard]] quantifier * mk_quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null,
unsigned num_patterns = 0, expr * const * patterns = nullptr,
unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr);
quantifier * mk_forall(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
[[nodiscard]] quantifier * mk_forall(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null,
unsigned num_patterns = 0, expr * const * patterns = nullptr,
unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) {
@ -2032,7 +2050,7 @@ public:
num_no_patterns, no_patterns);
}
quantifier * mk_exists(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
[[nodiscard]] quantifier * mk_exists(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null,
unsigned num_patterns = 0, expr * const * patterns = nullptr,
unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) {
@ -2040,13 +2058,13 @@ public:
num_no_patterns, no_patterns);
}
quantifier * mk_lambda(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body);
[[nodiscard]] quantifier * mk_lambda(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body);
quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body);
[[nodiscard]] quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body);
quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, unsigned new_num_no_patterns, expr * const * new_no_patterns, expr * new_body);
[[nodiscard]] quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, unsigned new_num_no_patterns, expr * const * new_no_patterns, expr * new_body);
quantifier * update_quantifier(quantifier * q, expr * new_body);
[[nodiscard]] quantifier * update_quantifier(quantifier * q, expr * new_body);
quantifier * update_quantifier_weight(quantifier * q, int new_weight);
@ -2054,6 +2072,12 @@ public:
quantifier * update_quantifier(quantifier * q, quantifier_kind new_kind, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body);
// Convenience overloads with std::initializer_list
[[nodiscard]] quantifier * update_quantifier(quantifier * q, std::initializer_list<expr*> new_patterns, expr * new_body);
[[nodiscard]] quantifier * update_quantifier(quantifier * q, std::initializer_list<expr*> new_patterns, std::initializer_list<expr*> new_no_patterns, expr * new_body);
// -----------------------------------
//
// expr_array
@ -2188,21 +2212,22 @@ public:
app * mk_xor(ptr_vector<expr> const& args) { return mk_xor(args.size(), args.data()); }
app * mk_xor(ref_buffer<expr, ast_manager> const& args) { return mk_xor(args.size(), args.data()); }
app * mk_or(unsigned num_args, expr * const * args) { return mk_app(basic_family_id, OP_OR, num_args, args); }
app * mk_and(unsigned num_args, expr * const * args) { return mk_app(basic_family_id, OP_AND, num_args, args); }
app * mk_and(std::span<expr* const> args) { return mk_app(basic_family_id, OP_AND, args); }
app * mk_or(std::span<expr* const> args) { return mk_app(basic_family_id, OP_OR, args); }
app * mk_or(expr * arg1, expr * arg2) { return mk_app(basic_family_id, OP_OR, arg1, arg2); }
app * mk_and(expr * arg1, expr * arg2) { return mk_app(basic_family_id, OP_AND, arg1, arg2); }
app * mk_or(expr * arg1, expr * arg2, expr * arg3) { return mk_app(basic_family_id, OP_OR, arg1, arg2, arg3); }
app * mk_or(expr* a, expr* b, expr* c, expr* d) { expr* args[4] = { a, b, c, d }; return mk_app(basic_family_id, OP_OR, 4, args); }
app * mk_and(expr * arg1, expr * arg2, expr * arg3) { return mk_app(basic_family_id, OP_AND, arg1, arg2, arg3); }
app * mk_and(ref_vector<expr, ast_manager> const& args) { return mk_and(args.size(), args.data()); }
app * mk_and(ptr_vector<expr> const& args) { return mk_and(args.size(), args.data()); }
app * mk_and(ref_buffer<expr, ast_manager> const& args) { return mk_and(args.size(), args.data()); }
app * mk_and(ptr_buffer<expr> const& args) { return mk_and(args.size(), args.data()); }
app * mk_or(ref_vector<expr, ast_manager> const& args) { return mk_or(args.size(), args.data()); }
app * mk_or(ptr_vector<expr> const& args) { return mk_or(args.size(), args.data()); }
app * mk_or(ref_buffer<expr, ast_manager> const& args) { return mk_or(args.size(), args.data()); }
app * mk_or(ptr_buffer<expr> const& args) { return mk_or(args.size(), args.data()); }
app * mk_and(ref_vector<expr, ast_manager> const& args) { return mk_and(std::span<expr* const>(args.data(), args.size())); }
app * mk_and(ptr_vector<expr> const& args) { return mk_and(std::span<expr* const>(args.data(), args.size())); }
app * mk_and(ref_buffer<expr, ast_manager> const& args) { return mk_and(std::span<expr* const>(args.data(), args.size())); }
app * mk_and(ptr_buffer<expr> const& args) { return mk_and(std::span<expr* const>(args.data(), args.size())); }
app * mk_or(ref_vector<expr, ast_manager> const& args) { return mk_or(std::span<expr* const>(args.data(), args.size())); }
app * mk_or(ptr_vector<expr> const& args) { return mk_or(std::span<expr* const>(args.data(), args.size())); }
app * mk_or(ref_buffer<expr, ast_manager> const& args) { return mk_or(std::span<expr* const>(args.data(), args.size())); }
app * mk_or(ptr_buffer<expr> const& args) { return mk_or(std::span<expr* const>(args.data(), args.size())); }
app * mk_implies(expr * arg1, expr * arg2) { return mk_app(basic_family_id, OP_IMPLIES, arg1, arg2); }
app * mk_not(expr * n) { return mk_app(basic_family_id, OP_NOT, n); }
app * mk_distinct(unsigned num_args, expr * const * args);
@ -2343,6 +2368,12 @@ public:
proof * mk_transitivity(proof * p1, proof * p2, proof * p3, proof * p4);
proof * mk_transitivity(unsigned num_proofs, proof * const * proofs);
proof * mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2);
proof * mk_transitivity(std::initializer_list<proof*> const& proofs) {
return mk_transitivity(static_cast<unsigned>(proofs.size()), proofs.begin());
}
proof * mk_transitivity(std::initializer_list<proof*> const& proofs, expr * n1, expr * n2) {
return mk_transitivity(static_cast<unsigned>(proofs.size()), proofs.begin(), n1, n2);
}
proof * mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs);
proof * mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs);
proof * mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs);
@ -2373,6 +2404,12 @@ public:
proof * mk_def_axiom(expr * ax);
proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs);
proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact);
proof * mk_unit_resolution(std::initializer_list<proof*> const& proofs) {
return mk_unit_resolution(static_cast<unsigned>(proofs.size()), proofs.begin());
}
proof * mk_unit_resolution(std::initializer_list<proof*> const& proofs, expr * new_fact) {
return mk_unit_resolution(static_cast<unsigned>(proofs.size()), proofs.begin(), new_fact);
}
proof * mk_hypothesis(expr * h);
proof * mk_lemma(proof * p, expr * lemma);
@ -2405,11 +2442,17 @@ private:
}
template<typename T>
void push_dec_array_ref(unsigned sz, T * const * a) {
for(unsigned i = 0; i < sz; i++) {
push_dec_ref(a[i]);
void push_dec_array_ref(std::span<T * const> a) {
for(auto elem : a) {
push_dec_ref(elem);
}
}
// Backward compatibility overload
template<typename T>
void push_dec_array_ref(unsigned sz, T * const * a) {
push_dec_array_ref(std::span<T * const>(a, sz));
}
};
typedef ast_manager::expr_array expr_array;

View file

@ -91,7 +91,7 @@ class ll_printer {
template<typename T>
void display_children(unsigned num_children, T * const * children) {
for (unsigned i = 0; i < num_children; i++) {
for (unsigned i = 0; i < num_children; ++i) {
if (i > 0) {
m_out << " ";
}
@ -213,7 +213,7 @@ public:
m_out << n->get_decl()->get_parameter(i);
}
unsigned num_parents = m_manager.get_num_parents(n);
for (unsigned i = 0; i < num_parents; i++) {
for (unsigned i = 0; i < num_parents; ++i) {
m_out << " ";
display_child(m_manager.get_parent(n, i));
}
@ -256,7 +256,7 @@ public:
m_out << "(" << (n->get_kind() == forall_k ? "forall" : (n->get_kind() == exists_k ? "exists" : "lambda")) << " ";
unsigned num_decls = n->get_num_decls();
m_out << "(vars ";
for (unsigned i = 0; i < num_decls; i++) {
for (unsigned i = 0; i < num_decls; ++i) {
if (i > 0) {
m_out << " ";
}
@ -307,7 +307,7 @@ public:
m_out << "(";
display_name(to_app(n)->get_decl());
display_params(to_app(n)->get_decl());
for (unsigned i = 0; i < num_args && i < 16; i++) {
for (unsigned i = 0; i < num_args && i < 16; ++i) {
m_out << " ";
display(to_app(n)->get_arg(i), depth-1);
}

View file

@ -67,7 +67,7 @@ bool lt(ast * n1, ast * n2) {
check_value(to_sort(n1)->get_num_parameters(), to_sort(n2)->get_num_parameters());
num = to_sort(n1)->get_num_parameters();
SASSERT(num > 0);
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
const parameter &p1 = to_sort(n1)->get_parameter(i);
const parameter &p2 = to_sort(n2)->get_parameter(i);
check_parameter(p1, p2);
@ -79,13 +79,13 @@ bool lt(ast * n1, ast * n2) {
check_value(to_func_decl(n1)->get_arity(), to_func_decl(n2)->get_arity());
check_value(to_func_decl(n1)->get_num_parameters(), to_func_decl(n2)->get_num_parameters());
num = to_func_decl(n1)->get_num_parameters();
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
const parameter &p1 = to_func_decl(n1)->get_parameter(i);
const parameter &p2 = to_func_decl(n2)->get_parameter(i);
check_parameter(p1, p2);
}
num = to_func_decl(n1)->get_arity();
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
ast * d1 = to_func_decl(n1)->get_domain(i);
ast * d2 = to_func_decl(n2)->get_domain(i);
check_ast(d1, d2);
@ -98,7 +98,7 @@ bool lt(ast * n1, ast * n2) {
check_value(to_app(n1)->get_depth(), to_app(n2)->get_depth());
check_ast(to_app(n1)->get_decl(), to_app(n2)->get_decl());
num = to_app(n1)->get_num_args();
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
expr * arg1 = to_app(n1)->get_arg(i);
expr * arg2 = to_app(n2)->get_arg(i);
check_ast(arg1, arg2);
@ -112,16 +112,16 @@ bool lt(ast * n1, ast * n2) {
check_value(to_quantifier(n1)->get_num_no_patterns(), to_quantifier(n2)->get_num_no_patterns());
check_value(to_quantifier(n1)->get_weight(), to_quantifier(n2)->get_weight());
num = to_quantifier(n1)->get_num_decls();
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
check_symbol(to_quantifier(n1)->get_decl_name(i), to_quantifier(n2)->get_decl_name(i));
check_ast(to_quantifier(n1)->get_decl_sort(i), to_quantifier(n2)->get_decl_sort(i));
}
num = to_quantifier(n1)->get_num_patterns();
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
check_ast(to_quantifier(n1)->get_pattern(i), to_quantifier(n2)->get_pattern(i));
}
num = to_quantifier(n1)->get_num_no_patterns();
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
check_ast(to_quantifier(n1)->get_no_pattern(i), to_quantifier(n2)->get_no_pattern(i));
}
n1 = to_quantifier(n1)->get_expr();
@ -139,7 +139,7 @@ bool lt(ast * n1, ast * n2) {
}
bool is_sorted(unsigned num, expr * const * ns) {
for (unsigned i = 1; i < num; i++) {
for (unsigned i = 1; i < num; ++i) {
ast * prev = ns[i-1];
ast * curr = ns[i];
if (lt(curr, prev))

View file

@ -71,3 +71,11 @@ inline std::string& operator+=(std::string& s, mk_pp const& pp) {
return s = s + pp;
}
// Helper function to convert streamable objects (like mk_pp) to strings for use with std::format
template<typename T>
inline std::string to_string(T const& obj) {
std::ostringstream strm;
strm << obj;
return std::move(strm).str();
}

View file

@ -79,7 +79,7 @@ bool smt2_pp_environment::is_indexed_fdecl(func_decl * f) const {
return false;
unsigned num = f->get_num_parameters();
unsigned i;
for (i = 0; i < num; i++) {
for (i = 0; i < num; ++i) {
if (f->get_parameter(i).is_int())
continue;
if (f->get_parameter(i).is_rational())
@ -111,7 +111,7 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) {
unsigned num = f->get_num_parameters();
ptr_buffer<format> fs;
fs.push_back(fname);
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
SASSERT(f->get_parameter(i).is_int() ||
f->get_parameter(i).is_rational() ||
(f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())));
@ -149,7 +149,7 @@ format * smt2_pp_environment::pp_signature(format * f_name, func_decl * f) {
f_name = pp_fdecl_params(f_name, f);
}
ptr_buffer<format> f_domain;
for (unsigned i = 0; i < f->get_arity(); i++)
for (unsigned i = 0; i < f->get_arity(); ++i)
f_domain.push_back(pp_sort(f->get_domain(i)));
ptr_buffer<format> args;
args.push_back(f_name);
@ -417,7 +417,7 @@ format_ns::format * smt2_pp_environment::pp_sort(sort * s) {
if (get_arutil().is_array(s)) {
ptr_buffer<format> fs;
unsigned sz = get_array_arity(s);
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
fs.push_back(pp_sort(get_array_domain(s, i)));
}
fs.push_back(pp_sort(get_array_range(s)));
@ -437,13 +437,18 @@ format_ns::format * smt2_pp_environment::pp_sort(sort * s) {
fs.push_back(pp_sort(to_sort(s->get_parameter(0).get_ast())));
return mk_seq1(m, fs.begin(), fs.end(), f2f(), get_sutil().is_seq(s)?"Seq":"RegEx");
}
if ((get_fsutil().is_finite_set(s))) {
ptr_buffer<format> fs;
fs.push_back(pp_sort(to_sort(s->get_parameter(0).get_ast())));
return mk_seq1(m, fs.begin(), fs.end(), f2f(), "FiniteSet");
}
std::string name = ensure_quote(s->get_name());
if (get_dtutil().is_datatype(s)) {
unsigned sz = get_dtutil().get_datatype_num_parameter_sorts(s);
if (sz > 0) {
ptr_buffer<format> fs;
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
fs.push_back(pp_sort(get_dtutil().get_datatype_parameter_sort(s, i)));
}
return mk_seq1(m, fs.begin(), fs.end(), f2f(), name);
@ -804,7 +809,7 @@ class smt2_printer {
if (old_sz == sz)
return f;
vector<ptr_vector<format> > decls;
for (unsigned i = old_sz; i < sz; i++) {
for (unsigned i = old_sz; i < sz; ++i) {
unsigned lvl = m_aliased_lvls_names[i].first;
symbol f_name = m_aliased_lvls_names[i].second;
format * f_def[1] = { m_aliased_pps.get(i) };
@ -828,7 +833,7 @@ class smt2_printer {
if (num_op == 0)
return f;
buf.push_back(mk_indent(m(), SMALL_INDENT, mk_compose(m(), mk_line_break(m()), f)));
for (unsigned i = 0; i < num_op; i++)
for (unsigned i = 0; i < num_op; ++i)
buf.push_back(mk_string(m(), ")"));
return mk_compose(m(), buf.size(), buf.data());
}
@ -869,7 +874,7 @@ class smt2_printer {
void register_var_names(quantifier * q) {
unsigned num_decls = q->get_num_decls();
for (unsigned i = 0; i < num_decls; i++) {
for (unsigned i = 0; i < num_decls; ++i) {
symbol name = ensure_quote_sym(q->get_decl_name(i));
if (name.is_numerical()) {
unsigned idx = 1;
@ -887,7 +892,7 @@ class smt2_printer {
void register_var_names(unsigned n) {
unsigned idx = 1;
for (unsigned i = 0; i < n; i++) {
for (unsigned i = 0; i < n; ++i) {
symbol name = next_name("x", idx);
SASSERT(!m_var_names_set.contains(name));
m_var_names.push_back(name);
@ -900,7 +905,7 @@ class smt2_printer {
}
void unregister_var_names(unsigned num_decls) {
for (unsigned i = 0; i < num_decls; i++) {
for (unsigned i = 0; i < num_decls; ++i) {
symbol s = m_var_names.back();
m_var_names.pop_back();
m_var_names_set.erase(s);
@ -911,7 +916,7 @@ class smt2_printer {
ptr_buffer<format> buf;
SASSERT(num_decls <= m_var_names.size());
symbol * it = m_var_names.end() - num_decls;
for (unsigned i = 0; i < num_decls; i++, it++) {
for (unsigned i = 0; i < num_decls; ++i, ++it) {
format * fs[1] = { m_env.pp_sort(srts[i]) };
std::string var_name;
if (is_smt2_quoted_symbol (*it)) {
@ -1110,7 +1115,7 @@ public:
var_prefix = "_a";
}
unsigned idx = 0;
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
symbol name = next_name(var_prefix, idx);
name = ensure_quote_sym(name);
var_names.push_back(name);
@ -1136,7 +1141,7 @@ public:
format * args[3];
args[0] = fname;
ptr_buffer<format> buf;
for (unsigned i = 0; i < arity; i++) {
for (unsigned i = 0; i < arity; ++i) {
buf.push_back(m_env.pp_sort(f->get_domain(i)));
}
args[1] = mk_seq5<format**, f2f>(m(), buf.begin(), buf.end(), f2f());

View file

@ -30,6 +30,7 @@ Revision History:
#include "ast/dl_decl_plugin.h"
#include "ast/seq_decl_plugin.h"
#include "ast/datatype_decl_plugin.h"
#include "ast/finite_set_decl_plugin.h"
#include "ast/ast_smt_pp.h"
#include "util/smt2_util.h"
@ -53,6 +54,7 @@ public:
virtual array_util & get_arutil() = 0;
virtual fpa_util & get_futil() = 0;
virtual seq_util & get_sutil() = 0;
virtual finite_set_util &get_fsutil() = 0;
virtual datalog::dl_decl_util& get_dlutil() = 0;
virtual datatype_util& get_dtutil() = 0;
virtual bool uses(symbol const & s) const = 0;
@ -80,9 +82,12 @@ class smt2_pp_environment_dbg : public smt2_pp_environment {
fpa_util m_futil;
seq_util m_sutil;
datatype_util m_dtutil;
finite_set_util m_fsutil;
datalog::dl_decl_util m_dlutil;
public:
smt2_pp_environment_dbg(ast_manager & m):m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_sutil(m), m_dtutil(m), m_dlutil(m) {}
smt2_pp_environment_dbg(ast_manager &m)
: m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_sutil(m), m_dtutil(m), m_fsutil(m),
m_dlutil(m) {}
ast_manager & get_manager() const override { return m_manager; }
arith_util & get_autil() override { return m_autil; }
bv_util & get_bvutil() override { return m_bvutil; }
@ -91,6 +96,7 @@ public:
fpa_util & get_futil() override { return m_futil; }
datalog::dl_decl_util& get_dlutil() override { return m_dlutil; }
datatype_util& get_dtutil() override { return m_dtutil; }
finite_set_util &get_fsutil() override { return m_fsutil; }
bool uses(symbol const & s) const override { return false; }
};

View file

@ -977,7 +977,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
strm << "(set-logic " << m_logic << ")\n";
}
if (!m_attributes.empty()) {
strm << "; " << m_attributes;
strm << "; " << m_attributes << "\n";
}
#if 0

View file

@ -57,7 +57,7 @@ void ast_translation::cache(ast * s, ast * t) {
void ast_translation::collect_decl_extra_children(decl * d) {
unsigned num_params = d->get_num_parameters();
for (unsigned i = 0; i < num_params; i++) {
for (unsigned i = 0; i < num_params; ++i) {
parameter const & p = d->get_parameter(i);
if (p.is_ast())
m_extra_children_stack.push_back(p.get_ast());
@ -102,7 +102,7 @@ bool ast_translation::visit(ast * n) {
void ast_translation::copy_params(decl * d, unsigned rpos, buffer<parameter> & ps) {
unsigned num = d->get_num_parameters();
unsigned j = rpos;
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
parameter const & p = d->get_parameter(i);
if (p.is_ast()) {
ps.push_back(parameter(m_result_stack[j]));
@ -365,7 +365,7 @@ expr_dependency * expr_dependency_translation::operator()(expr_dependency * d) {
m_translation.from().linearize(d, m_buffer);
unsigned sz = m_buffer.size();
SASSERT(sz >= 1);
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
m_buffer[i] = m_translation(m_buffer[i]);
}
return m_translation.to().mk_join(sz, m_buffer.data());

View file

@ -164,7 +164,7 @@ expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args) {
else if (num_args == 1)
return args[0];
else
return m.mk_and(num_args, args);
return m.mk_and(std::span<expr* const>(args, num_args));
}
app* mk_and(ast_manager & m, unsigned num_args, app * const * args) {
@ -232,8 +232,8 @@ expr_ref push_not(const expr_ref& e, unsigned limit) {
expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) {
expr_ref_buffer new_diseqs(m);
for (unsigned i = 0; i < num_args; i++) {
for (unsigned j = i + 1; j < num_args; j++)
for (unsigned i = 0; i < num_args; ++i) {
for (unsigned j = i + 1; j < num_args; ++j)
new_diseqs.push_back(m.mk_not(m.mk_eq(args[i], args[j])));
}
return mk_and(m, new_diseqs.size(), new_diseqs.data());

View file

@ -27,7 +27,7 @@ void remove_duplicates(C & v) {
if (!v.empty()) {
unsigned sz = v.size();
unsigned j = 0;
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
auto curr = v.get(i);
if (!visited.is_marked(curr)) {
visited.mark(curr);
@ -47,7 +47,7 @@ bool is_well_formed_vars(ptr_vector<sort>& bound, expr* n);
inline bool args_are_vars(app const * n) {
unsigned sz = n->get_num_args();
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
if (!is_var(n->get_arg(i)))
return false;
}
@ -56,7 +56,7 @@ inline bool args_are_vars(app const * n) {
inline bool depth_leq_one(app * n) {
unsigned sz = n->get_num_args();
for (unsigned i = 0; i < sz; i++) {
for (unsigned i = 0; i < sz; ++i) {
expr * arg = n->get_arg(i);
if (is_app(arg) && to_app(arg)->get_num_args() > 0)
return false;

View file

@ -17,6 +17,7 @@ Revision History:
--*/
#include<sstream>
#include<format>
#include "ast/bv_decl_plugin.h"
#include "ast/arith_decl_plugin.h"
#include "util/warning.h"
@ -44,7 +45,7 @@ bv_decl_plugin::bv_decl_plugin():
void bv_decl_plugin::set_manager(ast_manager * m, family_id id) {
decl_plugin::set_manager(m, id);
for (unsigned i = 1; i <= 64; i++)
for (unsigned i = 1; i <= 64; ++i)
mk_bv_sort(i);
m_bit0 = m->mk_const_decl(symbol("bit0"), get_bv_sort(1), func_decl_info(m_family_id, OP_BIT0));
@ -161,7 +162,7 @@ void bv_decl_plugin::mk_bv_sort(unsigned bv_size) {
sz = sort_size::mk_very_big();
}
else {
sz = sort_size(rational::power_of_two(bv_size));
sz = sort_size(1ULL << bv_size);
}
m_bv_sorts[bv_size] = m_manager->mk_sort(m_bv_sym, sort_info(m_family_id, BV_SORT, sz, 1, &p));
m_manager->inc_ref(m_bv_sorts[bv_size]);
@ -407,7 +408,7 @@ inline bool bv_decl_plugin::get_bv_size(expr * t, int & result) {
bool bv_decl_plugin::get_concat_size(unsigned arity, sort * const * domain, int & result) {
result = 0;
for (unsigned i = 0; i < arity; i++) {
for (unsigned i = 0; i < arity; ++i) {
int sz;
if (!get_bv_size(domain[i], sz)) {
return false;
@ -500,7 +501,7 @@ func_decl * bv_decl_plugin::mk_bit2bool(unsigned bv_size, unsigned num_parameter
}
func_decl * bv_decl_plugin::mk_mkbv(unsigned arity, sort * const * domain) {
for (unsigned i = 0; i < arity; i++) {
for (unsigned i = 0; i < arity; ++i) {
if (!m_manager->is_bool(domain[i])) {
m_manager->raise_exception("invalid mkbv operator");
return nullptr;
@ -672,9 +673,11 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
}
for (unsigned i = 0; i < num_args; ++i) {
if (args[i]->get_sort() != r->get_domain(i)) {
std::ostringstream buffer;
buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " has sort " << mk_pp(args[i]->get_sort(), m) << " it does not match declaration " << mk_pp(r, m);
m.raise_exception(buffer.str());
m.raise_exception(std::format("Argument {} at position {} has sort {} it does not match declaration {}",
to_string(mk_pp(args[i], m)),
i,
to_string(mk_pp(args[i]->get_sort(), m)),
to_string(mk_pp(r, m))));
return nullptr;
}
}
@ -833,9 +836,7 @@ rational bv_recognizers::norm(rational const & val, unsigned bv_size, bool is_si
bool bv_recognizers::has_sign_bit(rational const & n, unsigned bv_size) const {
SASSERT(bv_size > 0);
rational m = norm(n, bv_size, false);
rational p = rational::power_of_two(bv_size - 1);
return m >= p;
return numerator(n).get_bit(bv_size - 1) == 1;
}
bool bv_recognizers::is_bv_sort(sort const * s) const {

View file

@ -118,26 +118,6 @@ enum bv_op_kind {
LAST_BV_OP
};
// Assume k is a "div" operator. It returns the div0 uninterpreted function that
// models the value of "div" it is underspecified (i.e., when the denominator is zero).
inline bv_op_kind get_div0_op(bv_op_kind k) {
switch (k) {
case OP_BSDIV: return OP_BSDIV0;
case OP_BUDIV: return OP_BUDIV0;
case OP_BSREM: return OP_BSREM0;
case OP_BUREM: return OP_BUREM0;
case OP_BSMOD: return OP_BSMOD0;
default: UNREACHABLE(); return LAST_BV_OP;
}
}
// Assume decl is the declaration of a "div" operator. It returns the div0 declaration that
// models the value of "div" it is underspecified (i.e., when the denominator is zero).
inline func_decl * get_div0_decl(ast_manager & m, func_decl * decl) {
return m.mk_func_decl(decl->get_family_id(), get_div0_op(static_cast<bv_op_kind>(decl->get_decl_kind())),
0, nullptr, 1, decl->get_domain());
}
class bv_decl_plugin : public decl_plugin {
friend class bv_util;
protected:
@ -461,8 +441,7 @@ public:
MATCH_UNARY(is_int2bv);
bool is_bit2bool(expr* e, expr*& bv, unsigned& idx) const;
rational norm(rational const & val, unsigned bv_size, bool is_signed) const ;
rational norm(rational const & val, unsigned bv_size) const { return norm(val, bv_size, false); }
rational norm(rational const & val, unsigned bv_size, bool is_signed = false) const ;
bool has_sign_bit(rational const & n, unsigned bv_size) const;
};
@ -501,13 +480,17 @@ public:
return m_manager.mk_app(get_fid(), OP_EXTRACT, 2, params, 1, &n);
}
app * mk_concat(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_CONCAT, num, args); }
app * mk_concat(std::initializer_list<expr*> args) { return m_manager.mk_app(get_fid(), OP_CONCAT, static_cast<unsigned>(args.size()), args.begin()); }
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_or(std::initializer_list<expr*> args) { return m_manager.mk_app(get_fid(), OP_BOR, static_cast<unsigned>(args.size()), args.begin()); }
app * mk_bv_and(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BAND, num, args); }
app * mk_bv_and(std::initializer_list<expr*> args) { return m_manager.mk_app(get_fid(), OP_BAND, static_cast<unsigned>(args.size()), args.begin()); }
app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); }
app * mk_bv_xor(std::initializer_list<expr*> args) { return m_manager.mk_app(get_fid(), OP_BXOR, static_cast<unsigned>(args.size()), args.begin()); }
app * mk_concat(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_concat(2, args); }
app * mk_bv_and(expr* x, expr* y) { expr* args[2] = { x, y }; return mk_bv_and(2, args); }

View file

@ -23,9 +23,8 @@ Notes:
#include "ast/ast_translation.h"
class converter {
unsigned m_ref_count;
unsigned m_ref_count = 0;
public:
converter():m_ref_count(0) {}
virtual ~converter() = default;
void inc_ref() { ++m_ref_count; }
@ -90,7 +89,7 @@ protected:
public:
concat_star_converter(T * c1, unsigned num, T * const * c2s, unsigned * szs):
m_c1(c1) {
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
T * c2 = c2s[i];
if (c2)
c2->inc_ref();

View file

@ -44,6 +44,6 @@ public:
ast_manager& get_manager() { return m; }
void display(std::ostream & out) override {}
void display(std::ostream &) override { }
};

View file

@ -183,7 +183,7 @@ public:
return false;
unsigned i;
expr* v = nullptr;
for (i = 0; i < num; i++) {
for (i = 0; i < num; ++i) {
expr* arg = args[i];
if (uncnstr(arg)) {
v = arg;
@ -196,7 +196,7 @@ public:
if (!m_mc)
return true;
ptr_buffer<expr> new_args;
for (unsigned j = 0; j < num; j++)
for (unsigned j = 0; j < num; ++j)
if (j != i)
new_args.push_back(args[j]);
@ -270,7 +270,7 @@ class bv_expr_inverter : public iexpr_inverter {
return false;
unsigned i;
expr* v = nullptr;
for (i = 0; i < num; i++) {
for (i = 0; i < num; ++i) {
expr* arg = args[i];
if (uncnstr(arg)) {
v = arg;
@ -283,7 +283,7 @@ class bv_expr_inverter : public iexpr_inverter {
if (!m_mc)
return true;
ptr_buffer<expr> new_args;
for (unsigned j = 0; j < num; j++)
for (unsigned j = 0; j < num; ++j)
if (j != i)
new_args.push_back(args[j]);
@ -648,7 +648,7 @@ public:
if (m.is_uninterp(get_array_range(s)))
return false;
unsigned arity = get_array_arity(s);
for (unsigned i = 0; i < arity; i++)
for (unsigned i = 0; i < arity; ++i)
if (m.is_uninterp(get_array_domain(s, i)))
return false;
// building
@ -657,7 +657,7 @@ public:
// 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++)
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);
@ -692,13 +692,13 @@ public:
return true;
}
func_decl* c = dt.get_accessor_constructor(f);
for (unsigned i = 0; i < c->get_arity(); i++)
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++) {
for (unsigned i = 0; i < accs.size(); ++i) {
if (accs[i] == f)
new_args.push_back(r);
else
@ -719,7 +719,7 @@ public:
for (func_decl* constructor : constructors) {
unsigned num = constructor->get_arity();
unsigned target = UINT_MAX;
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
sort* s_arg = constructor->get_domain(i);
if (s == s_arg) {
target = i;
@ -732,7 +732,7 @@ public:
continue;
// use the constructor the distinct term constructor(...,t,...)
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; ++i) {
if (i == target)
new_args.push_back(t);
else
@ -823,6 +823,47 @@ public:
};
#endif
class finite_set_inverter : public iexpr_inverter {
finite_set_util fs;
public:
finite_set_inverter(ast_manager& m): iexpr_inverter(m), fs(m) {}
family_id get_fid() const override { return fs.get_family_id(); }
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
switch (f->get_decl_kind()) {
case OP_FINITE_SET_UNION:
// x union y -> x
// y := x
if (num == 2 && uncnstr(args[0]) && uncnstr(args[1])) {
r = args[0];
if (m_mc) {
add_def(args[1], r);
}
return true;
}
return false;
case OP_FINITE_SET_INTERSECT:
// x intersect y -> x
// y := x
if (num == 2 && uncnstr(args[0]) && uncnstr(args[1])) {
r = args[0];
if (m_mc) {
add_def(args[1], r);
}
return true;
}
return false;
default:
break;
}
return false;
}
bool mk_diff(expr* t, expr_ref& r) override {
return false;
}
};
class seq_expr_inverter : public iexpr_inverter {
seq_util seq;
@ -924,7 +965,7 @@ expr_inverter::~expr_inverter() {
bool iexpr_inverter::uncnstr(unsigned num, expr * const * args) const {
for (unsigned i = 0; i < num; i++)
for (unsigned i = 0; i < num; ++i)
if (!m_is_var(args[i]))
return false;
return true;
@ -956,7 +997,7 @@ void iexpr_inverter::add_defs(unsigned num, expr* const* args, expr* u, expr* id
if (!m_mc)
return;
add_def(args[0], u);
for (unsigned i = 1; i < num; i++)
for (unsigned i = 1; i < num; ++i)
add_def(args[i], identity);
}
@ -972,6 +1013,7 @@ expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) {
add(alloc(basic_expr_inverter, m, *this));
add(alloc(seq_expr_inverter, m));
//add(alloc(pb_expr_inverter, m));
add(alloc(finite_set_inverter, m));
}
@ -979,7 +1021,7 @@ bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, ex
if (num == 0)
return false;
for (unsigned i = 0; i < num; i++)
for (unsigned i = 0; i < num; ++i)
if (!is_ground(args[i]))
return false;

View file

@ -35,7 +35,7 @@ protected:
public:
iexpr_inverter(ast_manager& m): m(m) {}
virtual ~iexpr_inverter() {}
virtual ~iexpr_inverter() = default;
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; }

View file

@ -32,7 +32,7 @@ Notes:
void generic_model_converter::add(func_decl * d, expr* e) {
VERIFY(e);
VERIFY(d->get_range() == e->get_sort());
m_entries.push_back(entry(d, e, m, ADD));
m_entries.push_back(entry(d, e, m, instruction::ADD));
}
void generic_model_converter::operator()(model_ref & md) {
@ -138,9 +138,9 @@ void generic_model_converter::convert_initialize_value(vector<std::pair<expr_ref
auto& [var, value] = var2value[i];
for (auto const& e : m_entries) {
switch (e.m_instruction) {
case HIDE:
case instruction::HIDE:
break;
case ADD:
case instruction::ADD:
if (is_uninterp_const(var) && e.m_f == to_app(var)->get_decl())
convert_initialize_value(e.m_def, i, var2value);
break;
@ -203,14 +203,14 @@ void generic_model_converter::get_units(obj_map<expr, bool>& units) {
for (unsigned i = m_entries.size(); i-- > 0;) {
entry const& e = m_entries[i];
switch (e.m_instruction) {
case HIDE:
case instruction::HIDE:
tmp = m.mk_const(e.m_f);
if (units.contains(tmp)) {
m.dec_ref(tmp);
units.remove(tmp);
}
break;
case ADD:
case instruction::ADD:
if (e.m_f->get_arity() == 0 && m.is_bool(e.m_f->get_range())) {
tmp = m.mk_const(e.m_f);
if (units.contains(tmp)) {

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