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

prepare polysat

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2022-01-26 06:19:24 +01:00
commit 3f5df04dc4
252 changed files with 5792 additions and 2553 deletions

View file

@ -67,11 +67,12 @@ add_subdirectory(solver)
add_subdirectory(cmd_context)
add_subdirectory(cmd_context/extra_cmds)
add_subdirectory(parsers/smt2)
add_subdirectory(qe/mbp)
add_subdirectory(qe/lite)
add_subdirectory(solver/assertions)
add_subdirectory(ast/pattern)
add_subdirectory(ast/rewriter/bit_blaster)
add_subdirectory(math/lp)
add_subdirectory(qe/mbp)
add_subdirectory(sat/smt)
add_subdirectory(sat/tactic)
add_subdirectory(nlsat/tactic)

View file

@ -1187,6 +1187,9 @@ extern "C" {
case OP_SEQ_CONTAINS: return Z3_OP_SEQ_CONTAINS;
case OP_SEQ_EXTRACT: return Z3_OP_SEQ_EXTRACT;
case OP_SEQ_REPLACE: return Z3_OP_SEQ_REPLACE;
case OP_SEQ_REPLACE_RE: return Z3_OP_SEQ_REPLACE_RE;
case OP_SEQ_REPLACE_RE_ALL: return Z3_OP_SEQ_REPLACE_RE_ALL;
case OP_SEQ_REPLACE_ALL: return Z3_OP_SEQ_REPLACE_ALL;
case OP_SEQ_AT: return Z3_OP_SEQ_AT;
case OP_SEQ_NTH: return Z3_OP_SEQ_NTH;
case OP_SEQ_LENGTH: return Z3_OP_SEQ_LENGTH;
@ -1210,6 +1213,9 @@ extern "C" {
case OP_STRING_STOI: return Z3_OP_STR_TO_INT;
case OP_STRING_ITOS: return Z3_OP_INT_TO_STR;
case OP_STRING_TO_CODE: return Z3_OP_STR_TO_CODE;
case OP_STRING_FROM_CODE: return Z3_OP_STR_FROM_CODE;
case OP_STRING_UBVTOS: return Z3_OP_UBV_TO_STR;
case OP_STRING_SBVTOS: return Z3_OP_SBV_TO_STR;
case OP_STRING_LT: return Z3_OP_STRING_LT;
@ -1220,9 +1226,11 @@ extern "C" {
case OP_RE_OPTION: return Z3_OP_RE_OPTION;
case OP_RE_CONCAT: return Z3_OP_RE_CONCAT;
case OP_RE_UNION: return Z3_OP_RE_UNION;
case OP_RE_DIFF: return Z3_OP_RE_DIFF;
case OP_RE_POWER: return Z3_OP_RE_POWER;
case OP_RE_INTERSECT: return Z3_OP_RE_INTERSECT;
case OP_RE_LOOP: return Z3_OP_RE_LOOP;
case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET;
case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET;
//case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_SET;
case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET;
default:

View file

@ -47,7 +47,7 @@ extern "C" {
env_params::updt_params();
}
Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value) {
Z3_bool Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value) {
memory::initialize(UINT_MAX);
LOG_Z3_global_param_get(param_id, param_value);
*param_value = nullptr;

View file

@ -585,7 +585,7 @@ extern "C" {
to_fixedpoint_ref(d)->collect_param_descrs(descrs);
to_params(p)->m_params.validate(descrs);
to_fixedpoint_ref(d)->updt_params(to_param_ref(p));
to_fixedpoint(d)->m_params = to_param_ref(p);
to_fixedpoint(d)->m_params.append(to_param_ref(p));
Z3_CATCH;
}

View file

@ -26,7 +26,7 @@ Notes:
#include "api/api_model.h"
#include "api/api_ast_map.h"
#include "api/api_ast_vector.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
#include "muz/spacer/spacer_util.h"
extern "C"

View file

@ -55,7 +55,6 @@ extern "C" {
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned sz, Z3_string str) {
Z3_TRY;
LOG_Z3_mk_lstring(c, sz, str);
@ -80,6 +79,16 @@ extern "C" {
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_char(Z3_context c, unsigned ch) {
Z3_TRY;
LOG_Z3_mk_char(c, ch);
RESET_ERROR_CODE();
app* a = mk_c(c)->sutil().str.mk_char(ch);
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) {
Z3_TRY;
LOG_Z3_mk_string_sort(c);
@ -277,6 +286,8 @@ extern "C" {
MK_BINARY(Z3_mk_seq_contains, mk_c(c)->get_seq_fid(), OP_SEQ_CONTAINS, SKIP);
MK_BINARY(Z3_mk_str_lt, mk_c(c)->get_seq_fid(), OP_STRING_LT, SKIP);
MK_BINARY(Z3_mk_str_le, mk_c(c)->get_seq_fid(), OP_STRING_LE, SKIP);
MK_UNARY(Z3_mk_string_to_code, mk_c(c)->get_seq_fid(), OP_STRING_TO_CODE, SKIP);
MK_UNARY(Z3_mk_string_from_code, mk_c(c)->get_seq_fid(), OP_STRING_FROM_CODE, SKIP);
MK_TERNARY(Z3_mk_seq_extract, mk_c(c)->get_seq_fid(), OP_SEQ_EXTRACT, SKIP);
MK_TERNARY(Z3_mk_seq_replace, mk_c(c)->get_seq_fid(), OP_SEQ_REPLACE, SKIP);
@ -308,6 +319,7 @@ extern "C" {
MK_UNARY(Z3_mk_re_star, mk_c(c)->get_seq_fid(), OP_RE_STAR, SKIP);
MK_UNARY(Z3_mk_re_option, mk_c(c)->get_seq_fid(), OP_RE_OPTION, SKIP);
MK_UNARY(Z3_mk_re_complement, mk_c(c)->get_seq_fid(), OP_RE_COMPLEMENT, SKIP);
MK_BINARY(Z3_mk_re_diff, mk_c(c)->get_seq_fid(), OP_RE_DIFF, SKIP);
MK_NARY(Z3_mk_re_union, mk_c(c)->get_seq_fid(), OP_RE_UNION, SKIP);
MK_NARY(Z3_mk_re_intersect, mk_c(c)->get_seq_fid(), OP_RE_INTERSECT, SKIP);
MK_NARY(Z3_mk_re_concat, mk_c(c)->get_seq_fid(), OP_RE_CONCAT, SKIP);

View file

@ -944,7 +944,7 @@ extern "C" {
Z3_TRY;
LOG_Z3_solver_propagate_register(c, s, e);
RESET_ERROR_CODE();
return to_solver_ref(s)->user_propagate_register(to_expr(e));
return to_solver_ref(s)->user_propagate_register_expr(to_expr(e));
Z3_CATCH_RETURN(0);
}
@ -964,4 +964,28 @@ extern "C" {
Z3_CATCH;
}
void Z3_API Z3_solver_propagate_created(Z3_context c, Z3_solver s, Z3_created_eh created_eh) {
Z3_TRY;
RESET_ERROR_CODE();
user_propagator::created_eh_t c = (void(*)(void*, user_propagator::callback*, expr*, unsigned))created_eh;
to_solver_ref(s)->user_propagate_register_created(c);
Z3_CATCH;
}
Z3_func_decl Z3_API Z3_solver_propagate_declare(Z3_context c, Z3_symbol name, unsigned n, Z3_sort* domain, Z3_sort range) {
Z3_TRY;
LOG_Z3_solver_propagate_declare(c, name, n, domain, range);
RESET_ERROR_CODE();
ast_manager& m = mk_c(c)->m();
family_id fid = m.mk_family_id(user_propagator::plugin::name());
if (!m.has_plugin(fid))
m.register_plugin(fid, alloc(user_propagator::plugin));
func_decl_info info(fid, user_propagator::plugin::kind_t::OP_USER_PROPAGATE);
func_decl* f = m.mk_func_decl(to_symbol(name), n, to_sorts(domain), to_sort(range), info);
mk_c(c)->save_ast_trail(f);
RETURN_Z3(of_func_decl(f));
Z3_CATCH_RETURN(nullptr);
}
};

View file

@ -334,6 +334,7 @@ namespace z3 {
func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range);
void recdef(func_decl, expr_vector const& args, expr const& body);
func_decl user_propagate_function(symbol const& name, sort_vector const& domain, sort const& range);
expr constant(symbol const & name, sort const & s);
expr constant(char const * name, sort const & s);
@ -700,6 +701,9 @@ namespace z3 {
sort array_range() const { assert(is_array()); Z3_sort s = Z3_get_array_sort_range(ctx(), *this); check_error(); return sort(ctx(), s); }
friend std::ostream & operator<<(std::ostream & out, sort const & s) { return out << Z3_sort_to_string(s.ctx(), Z3_sort(s.m_ast)); }
func_decl_vector constructors();
func_decl_vector recognizers();
};
/**
@ -740,6 +744,9 @@ namespace z3 {
expr operator()(expr const & a1, expr const & a2, expr const & a3) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const;
func_decl_vector accessors();
};
/**
@ -834,7 +841,7 @@ namespace z3 {
double as_double() const { double d = 0; is_numeral(d); return d; }
uint64_t as_uint64() const { uint64_t r = 0; is_numeral_u64(r); return r; }
uint64_t as_int64() const { int64_t r = 0; is_numeral_i64(r); return r; }
int64_t as_int64() const { int64_t r = 0; is_numeral_i64(r); return r; }
/**
@ -1333,9 +1340,9 @@ namespace z3 {
friend expr bvmul_no_overflow(expr const& a, expr const& b, bool is_signed);
friend expr bvmul_no_underflow(expr const& a, expr const& b);
expr rotate_left(unsigned i) { Z3_ast r = Z3_mk_rotate_left(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); }
expr rotate_right(unsigned i) { Z3_ast r = Z3_mk_rotate_right(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); }
expr repeat(unsigned i) { Z3_ast r = Z3_mk_repeat(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); }
expr rotate_left(unsigned i) const { Z3_ast r = Z3_mk_rotate_left(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); }
expr rotate_right(unsigned i) const { Z3_ast r = Z3_mk_rotate_right(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); }
expr repeat(unsigned i) const { Z3_ast r = Z3_mk_repeat(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); }
friend expr bvredor(expr const & a);
friend expr bvredand(expr const & a);
@ -3424,6 +3431,14 @@ namespace z3 {
Z3_add_rec_def(f.ctx(), f, vars.size(), vars.ptr(), body);
}
inline func_decl context::user_propagate_function(symbol const& name, sort_vector const& domain, sort const& range) {
check_context(domain, range);
array<Z3_sort> domain1(domain);
Z3_func_decl f = Z3_solver_propagate_declare(range.ctx(), name, domain1.size(), domain1.ptr(), range);
check_error();
return func_decl(*this, f);
}
inline expr context::constant(symbol const & name, sort const & s) {
Z3_ast r = Z3_mk_const(m_ctx, name, s);
check_error();
@ -3788,6 +3803,13 @@ namespace z3 {
ctx.check_error();
return expr(ctx, r);
}
inline expr re_diff(expr const& a, expr const& b) {
check_context(a, b);
context& ctx = a.ctx();
Z3_ast r = Z3_mk_re_diff(ctx, a, b);
ctx.check_error();
return expr(ctx, r);
}
inline expr re_complement(expr const& a) {
MK_EXPR1(Z3_mk_re_complement, a);
}
@ -3847,6 +3869,42 @@ namespace z3 {
return expr_vector(*this, r);
}
inline func_decl_vector sort::constructors() {
assert(is_datatype());
func_decl_vector cs(ctx());
unsigned n = Z3_get_datatype_sort_num_constructors(ctx(), *this);
for (unsigned i = 0; i < n; ++i)
cs.push_back(func_decl(ctx(), Z3_get_datatype_sort_constructor(ctx(), *this, i)));
return cs;
}
inline func_decl_vector sort::recognizers() {
assert(is_datatype());
func_decl_vector rs(ctx());
unsigned n = Z3_get_datatype_sort_num_constructors(ctx(), *this);
for (unsigned i = 0; i < n; ++i)
rs.push_back(func_decl(ctx(), Z3_get_datatype_sort_recognizer(ctx(), *this, i)));
return rs;
}
inline func_decl_vector func_decl::accessors() {
sort s = range();
assert(s.is_datatype());
unsigned n = Z3_get_datatype_sort_num_constructors(ctx(), s);
unsigned idx = 0;
for (; idx < n; ++idx) {
func_decl f(ctx(), Z3_get_datatype_sort_constructor(ctx(), s, idx));
if (id() == f.id())
break;
}
assert(idx < n);
n = arity();
func_decl_vector as(ctx());
for (unsigned i = 0; i < n; ++i)
as.push_back(func_decl(ctx(), Z3_get_datatype_sort_constructor_accessor(ctx(), s, idx, i)));
return as;
}
inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) {
assert(src.size() == dst.size());
@ -3877,10 +3935,12 @@ namespace z3 {
typedef std::function<void(unsigned, expr const&)> fixed_eh_t;
typedef std::function<void(void)> final_eh_t;
typedef std::function<void(unsigned, unsigned)> eq_eh_t;
typedef std::function<void(unsigned, expr const&)> created_eh_t;
final_eh_t m_final_eh;
eq_eh_t m_eq_eh;
fixed_eh_t m_fixed_eh;
created_eh_t m_created_eh;
solver* s;
Z3_context c;
Z3_solver_callback cb { nullptr };
@ -3929,6 +3989,14 @@ namespace z3 {
static_cast<user_propagator_base*>(p)->m_final_eh();
}
static void created_eh(void* _p, Z3_solver_callback cb, Z3_ast _e, unsigned id) {
user_propagator_base* p = static_cast<user_propagator_base*>(_p);
scoped_cb _cb(p, cb);
scoped_context ctx(p->ctx());
expr e(ctx(), _e);
static_cast<user_propagator_base*>(p)->m_created_eh(id, e);
}
public:
user_propagator_base(Z3_context c) : s(nullptr), c(c) {}
@ -4008,6 +4076,18 @@ namespace z3 {
Z3_solver_propagate_final(ctx(), *s, final_eh);
}
void register_created(created_eh_t& c) {
assert(s);
m_created_eh = c;
Z3_solver_propagate_created(ctx(), *s, created_eh);
}
void register_created() {
m_created_eh = [this](unsigned id, expr const& e) {
created(id, e);
};
Z3_solver_propagate_created(ctx(), *s, created_eh);
}
virtual void fixed(unsigned /*id*/, expr const& /*e*/) { }
@ -4015,6 +4095,8 @@ namespace z3 {
virtual void final() { }
virtual void created(unsigned /*id*/, expr const& /*e*/) {}
/**
\brief tracks \c e by a unique identifier that is returned by the call.

View file

@ -18,8 +18,6 @@ add_custom_command(OUTPUT "${Z3_DOTNET_NATIVE_FILE}"
${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN}
"${PROJECT_SOURCE_DIR}/scripts/update_api.py"
${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES}
# FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency
"${PROJECT_SOURCE_DIR}/scripts/mk_util.py"
COMMENT "Generating ${Z3_DOTNET_NATIVE_FILE}"
${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG}
)

View file

@ -980,7 +980,8 @@ namespace Microsoft.Z3
Debug.Assert(t != null);
Debug.Assert(t.All(a => a != null));
CheckContextMatch<BoolExpr>(t);
return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Count(), AST.EnumToNative(t)));
var ands = t.ToArray();
return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Count(), AST.ArrayToNative(ands)));
}
/// <summary>
@ -1005,7 +1006,8 @@ namespace Microsoft.Z3
Debug.Assert(t.All(a => a != null));
CheckContextMatch(t);
return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)t.Count(), AST.EnumToNative(t)));
var ts = t.ToArray();
return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)ts.Length, AST.ArrayToNative(ts)));
}
#endregion
@ -1032,7 +1034,8 @@ namespace Microsoft.Z3
Debug.Assert(t.All(a => a != null));
CheckContextMatch(t);
return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Count(), AST.EnumToNative(t)));
var ts = t.ToArray();
return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)ts.Length, AST.ArrayToNative(ts)));
}
/// <summary>
@ -1044,7 +1047,8 @@ namespace Microsoft.Z3
Debug.Assert(t.All(a => a != null));
CheckContextMatch<ArithExpr>(t);
return (ArithExpr)Expr.Create(this, Native.Z3_mk_mul(nCtx, (uint)t.Length, AST.ArrayToNative(t)));
var ts = t.ToArray();
return (ArithExpr)Expr.Create(this, Native.Z3_mk_mul(nCtx, (uint)ts.Length, AST.ArrayToNative(ts)));
}
/// <summary>
@ -1056,7 +1060,8 @@ namespace Microsoft.Z3
Debug.Assert(t.All(a => a != null));
CheckContextMatch<ArithExpr>(t);
return (ArithExpr)Expr.Create(this, Native.Z3_mk_mul(nCtx, (uint)t.Count(), AST.EnumToNative(t)));
var ts = t.ToArray();
return (ArithExpr)Expr.Create(this, Native.Z3_mk_mul(nCtx, (uint)ts.Length, AST.ArrayToNative(ts)));
}
/// <summary>
@ -2662,6 +2667,17 @@ namespace Microsoft.Z3
return new ReExpr(this, Native.Z3_mk_re_intersect(nCtx, (uint)t.Length, AST.ArrayToNative(t)));
}
/// <summary>
/// Create a difference regular expression.
/// </summary>
public ReExpr MkDiff(ReExpr a, ReExpr b)
{
Debug.Assert(a != null);
Debug.Assert(b != null);
CheckContextMatch(a, b);
return new ReExpr(this, Native.Z3_mk_re_diff(nCtx, a.NativeObject, b.NativeObject));
}
/// <summary>
/// Create the empty regular expression.
/// The sort s should be a regular expression.
@ -2749,10 +2765,11 @@ namespace Microsoft.Z3
/// </summary>
public BoolExpr MkAtMost(IEnumerable<BoolExpr> args, uint k)
{
Debug.Assert(args != null);
CheckContextMatch<BoolExpr>(args);
return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Count(),
AST.EnumToNative(args), k));
Debug.Assert(args != null);
CheckContextMatch<BoolExpr>(args);
var ts = args.ToArray();
return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) ts.Length,
AST.ArrayToNative(ts), k));
}
/// <summary>
@ -2760,10 +2777,11 @@ namespace Microsoft.Z3
/// </summary>
public BoolExpr MkAtLeast(IEnumerable<BoolExpr> args, uint k)
{
Debug.Assert(args != null);
CheckContextMatch<BoolExpr>(args);
return new BoolExpr(this, Native.Z3_mk_atleast(nCtx, (uint) args.Count(),
AST.EnumToNative(args), k));
Debug.Assert(args != null);
CheckContextMatch<BoolExpr>(args);
var ts = args.ToArray();
return new BoolExpr(this, Native.Z3_mk_atleast(nCtx, (uint) ts.Length,
AST.ArrayToNative(ts), k));
}
/// <summary>

View file

@ -131,24 +131,10 @@ namespace Microsoft.Z3
return an;
}
internal static IntPtr[] EnumToNative<T>(IEnumerable<T> a) where T : Z3Object
{
if (a == null) return null;
IntPtr[] an = new IntPtr[a.Count()];
int i = 0;
foreach (var ai in a)
{
if (ai != null) an[i] = ai.NativeObject;
++i;
}
return an;
}
internal static uint ArrayLength(Z3Object[] a)
{
return (a == null)?0:(uint)a.Length;
}
#endregion
#endregion
}
}

View file

@ -2016,7 +2016,15 @@ public class Context implements AutoCloseable {
*/
public SeqExpr<CharSort> mkString(String s)
{
return (SeqExpr<CharSort>) Expr.create(this, Native.mkString(nCtx(), s));
StringBuilder buf = new StringBuilder();
for (int i = 0; i < s.length(); ++i) {
int code = s.codePointAt(i);
if (code <= 32 || 127 < code)
buf.append(String.format("\\u{%x}", code));
else
buf.append(s.charAt(i));
}
return (SeqExpr<CharSort>) Expr.create(this, Native.mkString(nCtx(), buf.toString()));
}
/**
@ -2246,8 +2254,19 @@ public class Context implements AutoCloseable {
return (ReExpr<R>) Expr.create(this, Native.mkReIntersect(nCtx(), t.length, AST.arrayToNative(t)));
}
/**
* Create a difference regular expression.
*/
public <R extends Sort> ReExpr<R> mkDiff(Expr<ReSort<R>> a, Expr<ReSort<R>> b)
{
checkContextMatch(a, b);
return (ReExpr<R>) Expr.create(this, Native.mkReDiff(nCtx(), a.getNativeObject(), b.getNativeObject()));
}
/**
* Create the empty regular expression.
* Coresponds to re.none
*/
public <R extends Sort> ReExpr<R> mkEmptyRe(R s)
{
@ -2256,12 +2275,22 @@ public class Context implements AutoCloseable {
/**
* Create the full regular expression.
* Corresponds to re.all
*/
public <R extends Sort> ReExpr<R> mkFullRe(R s)
{
return (ReExpr<R>) Expr.create(this, Native.mkReFull(nCtx(), s.getNativeObject()));
}
/**
* Create regular expression that accepts all characters
* Corresponds to re.allchar
*/
public <R extends Sort> ReExpr<R> mkAllcharRe(R s)
{
return (ReExpr<R>) Expr.create(this, Native.mkReAllchar(nCtx(), s.getNativeObject()));
}
/**
* Create a range expression.
*/

View file

@ -0,0 +1,124 @@
# Z3 TypeScript Bindings
This project provides low-level TypeScript bindings for the [Z3 theorem prover](https://github.com/Z3Prover/z3). It is available on npm as [z3-solver](https://www.npmjs.com/package/z3-solver).
Z3 itself is distributed as a wasm artifact as part of this package. You can find the documentation for the Z3 API [here](https://z3prover.github.io/api/html/z3__api_8h.html), though note the differences below.
## Using
This requires threads, which means you'll need to be running in an environment which supports `SharedArrayBuffer`. In browsers, in addition to ensuring the browser has implemented `SharedArrayBuffer`, you'll need to serve your page with [special headers](https://web.dev/coop-coep/). There's a [neat trick](https://github.com/gzuidhof/coi-serviceworker) for doing that client-side on e.g. Github Pages, though you shouldn't use that trick in more complex applications.
Other than the differences below, the bindings can be used exactly as you'd use the C library. Because this is a wrapper around a C library, most of the values you'll use are just numbers representing pointers. For this reason you are strongly encouraged to make use of the TypeScript types to differentiate among the different kinds of value.
The module exports an `init` function, is an async function which initializes the library and returns `{ em, Z3 }` - `em` contains the underlying emscripten module, which you can use to e.g. kill stray threads, and `Z3` contains the actual bindings. The other module exports are the enums defined in the Z3 API.
[`test-ts-api.ts`](./test-ts-api.ts) contains a couple real cases translated very mechanically from [this file](https://github.com/Z3Prover/z3/blob/90fd3d82fce20d45ed2eececdf65545bab769503/examples/c/test_capi.c).
## Differences from the C API
### Integers
JavaScript numbers are IEEE double-precisions floats. These can be used wherever the C API expects an `int`, `unsigned int`, `float`, or `double`.
`int64_t` and `uint64_t` cannot be precisely represented by JS numbers, so in the TS bindings they are represented by [BigInts](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#bigint_type).
### Out parameters
In C, to return multiple values a function will take an address to write to, conventionally referred to as an "out parameter". Sometimes the function returns a boolean to indicate success; other times it may return nothing or some other value.
In JS the convention when returning multiple values is to return records containing the values of interest.
The wrapper translates between the two conventions. For example, the C declaration
```c
void Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d);
```
is represented in the TS bindings as
```ts
function rcf_get_numerator_denominator(c: Z3_context, a: Z3_rcf_num): { n: Z3_rcf_num; d: Z3_rcf_num } {
// ...
}
```
When there is only a single out parameter, and the return value is not otherwise of interest, the parameter is not wrapped. For example, the C declaration
```c
Z3_bool Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, bool model_completion, Z3_ast * v);
```
is represented in the TS bindings as
```ts
function model_eval(c: Z3_context, m: Z3_model, t: Z3_ast, model_completion: boolean): Z3_ast | null {
// ...
}
```
Note that the boolean return type of the C function is translated into a nullable return type for the TS binding.
When the return value is of interest it is included in the returned record under the key `rv`.
### Arrays
The when the C API takes an array as an argument it will also require a parameter which specifies the length of the array (since arrays do not carry their own lengths in C). In the TS bindings this is automatically inferred.
For example, the C declaration
```js
unsigned Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]);
```
is represented in the TS bindings as
```ts
function rcf_mk_roots(c: Z3_context, a: Z3_rcf_num[]): { rv: number; roots: Z3_rcf_num[] } {
// ...
}
```
When there are multiple arrays which the C API expects to be of the same length, the TS bindings will enforce that this is the case.
### Null pointers
Some of the C APIs accept or return null pointers (represented by types whose name end in `_opt`). These are represented in the TS bindings as `| null`. For example, the C declaration
```js
Z3_ast_opt Z3_model_get_const_interp(Z3_context c, Z3_model m, Z3_func_decl a);
```
is represented in the TS bindings as
```ts
function model_get_const_interp(c: Z3_context, m: Z3_model, a: Z3_func_decl): Z3_ast | null {
// ...
}
```
### Async functions
Certain long-running APIs are not appropriate to call on the main thread. In the TS bindings those APIs are marked as `async` and are automatically called on a different thread. This includes the following APIs:
- `Z3_simplify`
- `Z3_simplify_ex`
- `Z3_solver_check`
- `Z3_solver_check_assumptions`
- `Z3_solver_cube`
- `Z3_solver_get_consequences`
- `Z3_tactic_apply`
- `Z3_tactic_apply_ex`
- `Z3_optimize_check`
- `Z3_algebraic_roots`
- `Z3_algebraic_eval`
- `Z3_fixedpoint_query`
- `Z3_fixedpoint_query_relations`
- `Z3_fixedpoint_query_from_lvl`
- `Z3_polynomial_subresultants`
Note that these are not thread-safe, and so only one call can be running at a time. Trying to call a second async API before the first completes will throw.

17
src/api/js/README.md Normal file
View file

@ -0,0 +1,17 @@
# TypeScript Bindings
This directory contains JavaScript code to automatically derive TypeScript bindings for the C API, which are published on npm as [z3-solver](https://www.npmjs.com/package/z3-solver).
The readme for the bindings themselves is located in [`PUBLISHED_README.md`](./PUBLISHED_README.md).
## Building
You'll need to have emscripten set up, along with all of its dependencies. The easiest way to do that is with [emsdk](https://github.com/emscripten-core/emsdk).
Then run `npm i` to install dependencies, `npm run build-ts` to build the TypeScript wrapper, and `npm run build-wasm` to build the wasm artifact.
## Tests
Current tests are very minimal: [`test-ts-api.ts`](./test-ts-api.ts) contains a couple real cases translated very mechanically from [this file](https://github.com/Z3Prover/z3/blob/90fd3d82fce20d45ed2eececdf65545bab769503/examples/c/test_capi.c).

23
src/api/js/build-wasm.sh Executable file
View file

@ -0,0 +1,23 @@
#!/bin/bash
set -euxo pipefail
export ROOT=$PWD
cd ../../..
export CXXFLAGS="-pthread -s USE_PTHREADS=1 -s DISABLE_EXCEPTION_CATCHING=0"
export LDFLAGS="-s WASM_BIGINT -s -pthread -s USE_PTHREADS=1"
if [ ! -f "build/Makefile" ]; then
emconfigure python scripts/mk_make.py --staticlib --single-threaded
fi
cd build
emmake make -j$(nproc) libz3.a
cd $ROOT
export EM_CACHE=$HOME/.emscripten/
export FNS=$(node scripts/list-exports.js)
export METHODS='["ccall","FS","allocate","UTF8ToString","intArrayFromString","ALLOC_NORMAL"]'
emcc build/async-fns.cc ../../../build/libz3.a --std=c++20 --pre-js src/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=$METHODS -s EXPORTED_FUNCTIONS=$FNS -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s DEMANGLE_SUPPORT=1 -s TOTAL_MEMORY=1GB -I z3/src/api/ -o build/z3-built.js

57
src/api/js/example-raw.ts Normal file
View file

@ -0,0 +1,57 @@
import { init } from './build/wrapper';
// demonstrates use of the raw API
(async () => {
let { em, Z3 } = await init();
Z3.global_param_set('verbose', '10');
console.log('verbosity:', Z3.global_param_get('verbose'));
let config = Z3.mk_config();
let ctx = Z3.mk_context_rc(config);
Z3.del_config(config);
let unicodeStr = [...'hello™'].map(x => x.codePointAt(0)!);
let strAst = Z3.mk_u32string(ctx, unicodeStr);
Z3.inc_ref(ctx, strAst);
console.log(Z3.is_string(ctx, strAst));
console.log(Z3.get_string(ctx, strAst));
console.log(Z3.get_string_contents(ctx, strAst, unicodeStr.length));
let bv = Z3.mk_bv_numeral(ctx, [true, true, false]);
let bs = Z3.mk_ubv_to_str(ctx, bv);
console.log(Z3.ast_to_string(ctx, bs));
let intSort = Z3.mk_int_sort(ctx);
let big = Z3.mk_int64(ctx, 42n, intSort);
console.log(Z3.get_numeral_string(ctx, big));
console.log(Z3.get_numeral_int64(ctx, big));
console.log(Z3.get_version());
let head_tail = [Z3.mk_string_symbol(ctx, 'car'), Z3.mk_string_symbol(ctx, 'cdr')];
let nil_con = Z3.mk_constructor(ctx, Z3.mk_string_symbol(ctx, 'nil'), Z3.mk_string_symbol(ctx, 'is_nil'), [], [], []);
let cons_con = Z3.mk_constructor(
ctx,
Z3.mk_string_symbol(ctx, 'cons'),
Z3.mk_string_symbol(ctx, 'is_cons'),
head_tail,
[null, null],
[0, 0],
);
let cell = Z3.mk_datatype(ctx, Z3.mk_string_symbol(ctx, 'cell'), [nil_con, cons_con]);
console.log(Z3.query_constructor(ctx, nil_con, 0));
console.log(Z3.query_constructor(ctx, cons_con, 2));
Z3.dec_ref(ctx, strAst);
Z3.del_context(ctx);
em.PThread.terminateAllThreads();
})().catch(e => {
console.error('error', e);
process.exit(1);
});

30
src/api/js/package-lock.json generated Normal file
View file

@ -0,0 +1,30 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@types/node": {
"version": "17.0.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz",
"integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==",
"dev": true
},
"prettier": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true
},
"sprintf-js": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
"dev": true
},
"typescript": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==",
"dev": true
}
}
}

27
src/api/js/package.json Normal file
View file

@ -0,0 +1,27 @@
{
"name": "z3-solver",
"keywords": ["Z3", "theorem", "prover", "solver", "satisfiability", "smt", "satisfiability modulo theories"],
"homepage": "https://github.com/Z3Prover/z3/tree/master/src/api/js",
"repository": "github:Z3Prover/z3",
"engines": {
"node": ">=16"
},
"main": "build/wrapper.js",
"types": "build/wrapper.d.ts",
"files": [
"build/*.{js,d.ts,wasm}"
],
"scripts": {
"build-ts": "mkdir -p build && node scripts/make-ts-wrapper.js > build/wrapper.ts && tsc",
"build-wasm": "mkdir -p build && node scripts/make-cc-wrapper.js > build/async-fns.cc && ./build-wasm.sh",
"format": "prettier --write --single-quote --arrow-parens avoid --print-width 120 --trailing-comma all '{,src/,scripts/}*.{js,ts}'",
"test": "node test-ts-api.js"
},
"devDependencies": {
"@types/node": "^17.0.8",
"prettier": "^2.5.1",
"sprintf-js": "^1.1.2",
"typescript": "^4.5.4"
},
"license": "MIT"
}

View file

@ -0,0 +1,22 @@
'use strict';
// things which you probably want to do off-thread
// from https://github.com/Z3Prover/z3/issues/5746#issuecomment-1006289146
module.exports = [
'Z3_eval_smtlib2_string',
'Z3_simplify',
'Z3_simplify_ex',
'Z3_solver_check',
'Z3_solver_check_assumptions',
'Z3_solver_cube',
'Z3_solver_get_consequences',
'Z3_tactic_apply',
'Z3_tactic_apply_ex',
'Z3_optimize_check',
'Z3_algebraic_roots',
'Z3_algebraic_eval',
'Z3_fixedpoint_query',
'Z3_fixedpoint_query_relations',
'Z3_fixedpoint_query_from_lvl',
'Z3_polynomial_subresultants',
];

View file

@ -0,0 +1,11 @@
'use strict';
// this is called by build.sh to generate the names of the bindings to export
let { functions } = require('./parse-api.js');
let asyncFns = require('./async-fns.js');
let extras = asyncFns.map(f => '_async_' + f);
let fns = functions.filter(f => !asyncFns.includes(f.name));
console.log(JSON.stringify([...extras, ...functions.map(f => '_' + f.name)]));

View file

@ -0,0 +1,82 @@
'use strict';
// generates c wrappers with off-thread versions of specified functions
let path = require('path');
let { functions } = require('./parse-api.js');
let asyncFns = require('./async-fns.js');
let wrappers = [];
for (let fnName of asyncFns) {
let fn = functions.find(f => f.name === fnName);
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
.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(),
);
}
console.log(`// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND
#include <thread>
#include <emscripten.h>
#include "../../z3.h"
template<typename Fn, Fn fn, typename... Args>
void wrapper(Args&&... args) {
std::thread t([...args = std::forward<Args>(args)] {
try {
auto result = fn(args...);
MAIN_THREAD_ASYNC_EM_ASM({
resolve_async($0);
}, result);
} catch (...) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async('failed with unknown exception');
});
throw;
}
});
t.detach();
}
template<typename Fn, Fn fn, typename... Args>
void wrapper_str(Args&&... args) {
std::thread t([...args = std::forward<Args>(args)] {
try {
auto result = fn(args...);
MAIN_THREAD_ASYNC_EM_ASM({
resolve_async(UTF8ToString($0));
}, result);
} catch (...) {
MAIN_THREAD_ASYNC_EM_ASM({
reject_async('failed with unknown exception');
});
throw;
}
});
t.detach();
}
${wrappers.join('\n\n')}`);

View file

@ -0,0 +1,422 @@
'use strict';
let path = require('path');
let prettier = require('prettier');
let { primitiveTypes, types, enums, functions } = require('./parse-api.js');
let asyncFns = require('./async-fns.js');
let subtypes = {
__proto__: null,
Z3_sort: 'Z3_ast',
Z3_func_decl: 'Z3_ast',
};
let makePointerType = t =>
`export type ${t} = ` + (t in subtypes ? `Subpointer<'${t}', '${subtypes[t]}'>;` : `Pointer<'${t}'>;`);
// this supports a up to 6 out intergers/pointers
// or up to 3 out int64s
const BYTES_TO_ALLOCATE_FOR_OUT_PARAMS = 24;
function toEmType(type) {
if (type in primitiveTypes) {
type = primitiveTypes[type];
}
if (['boolean', 'number', 'string', 'bigint', 'void'].includes(type)) {
return type;
}
if (type.startsWith('Z3_')) {
return 'number';
}
throw new Error(`unknown parameter type ${type}`);
}
function isZ3PointerType(type) {
return type.startsWith('Z3_');
}
function toEm(p) {
if (typeof p === 'string') {
// we've already set this, e.g. by replacing it with an expression
return p;
}
let { type } = p;
if (p.kind === 'out') {
throw new Error(`unknown out parameter type ${JSON.stringify(p)}`);
}
if (p.isArray) {
if (isZ3PointerType(type) || type === 'unsigned' || type === 'int') {
// this works for nullables also because null coerces to 0
return `intArrayToByteArr(${p.name} as unknown as number[])`;
} else if (type === 'boolean') {
return `boolArrayToByteArr(${p.name})`;
} else {
throw new Error(`only know how to deal with arrays of int/bool (got ${type})`);
}
}
if (type in primitiveTypes) {
type = primitiveTypes[type];
}
if (['boolean', 'number', 'bigint', 'string'].includes(type)) {
return p.name;
}
if (type.startsWith('Z3_')) {
return p.name;
}
throw new Error(`unknown parameter type ${JSON.stringify(p)}`);
}
let isInParam = p => ['in', 'in_array'].includes(p.kind);
function wrapFunction(fn) {
let inParams = fn.params.filter(isInParam);
let outParams = fn.params.map((p, idx) => ({ ...p, idx })).filter(p => !isInParam(p));
// we'll figure out how to deal with these cases later
let unknownInParam = inParams.find(
p =>
p.isPtr ||
p.type === 'Z3_char_ptr' ||
(p.isArray && !(isZ3PointerType(p.type) || p.type === 'unsigned' || p.type === 'int' || p.type === 'boolean')),
);
if (unknownInParam) {
console.error(`skipping ${fn.name} - unknown in parameter ${JSON.stringify(unknownInParam)}`);
return null;
}
if (fn.ret === 'Z3_char_ptr') {
console.error(`skipping ${fn.name} - returns a string or char pointer`);
return null;
}
// console.error(fn.name);
let isAsync = asyncFns.includes(fn.name);
let trivial =
!['string', 'boolean'].includes(fn.ret) &&
!fn.nullableRet &&
outParams.length === 0 &&
!inParams.some(p => p.type === 'string' || p.isArray || p.nullable);
let name = fn.name.startsWith('Z3_') ? fn.name.substring(3) : fn.name;
let params = inParams.map(p => {
let type = p.type;
if (p.isArray && p.nullable) {
type = `(${type} | null)[]`;
} else if (p.isArray) {
type = `${type}[]`;
} else if (p.nullable) {
type = `${type} | null`;
}
return `${p.name}: ${type}`;
});
if (trivial && isAsync) {
// i.e. and async
return `${name}: function (${params.join(', ')}): Promise<${fn.ret}> {
return Mod.async_call(Mod._async_${fn.name}, ${fn.params.map(toEm).join(', ')});
}`;
}
if (trivial) {
return `${name}: Mod._${fn.name} as ((${params.join(', ')}) => ${fn.ret})`;
}
// otherwise fall back to ccall
let ctypes = fn.params.map(p =>
p.kind === 'in_array' ? 'array' : p.kind === 'out_array' ? 'number' : p.isPtr ? 'number' : toEmType(p.type),
);
let prefix = '';
let infix = '';
let rv = 'ret';
let suffix = '';
let args = fn.params;
let arrayLengthParams = new Map();
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
console.error(`skipping ${fn.name} - nullable input parameter`);
return null;
}
if (!p.isArray) {
continue;
}
let { sizeIndex } = p;
if (arrayLengthParams.has(sizeIndex)) {
let otherParam = arrayLengthParams.get(sizeIndex);
prefix += `
if (${otherParam}.length !== ${p.name}.length) {
throw new TypeError(\`${otherParam} and ${p.name} must be the same length (got \${${otherParam}.length} and \{${p.name}.length})\`);
}
`.trim();
continue;
}
arrayLengthParams.set(sizeIndex, p.name);
let sizeParam = fn.params[sizeIndex];
if (!(sizeParam.kind === 'in' && sizeParam.type === 'unsigned' && !sizeParam.isPtr && !sizeParam.isArray)) {
throw new Error(
`size index is not unsigned int (for fn ${fn.name} parameter ${sizeIndex} got ${sizeParam.type})`,
);
}
args[sizeIndex] = `${p.name}.length`;
params[sizeIndex] = null;
}
let returnType = fn.ret;
let cReturnType = toEmType(fn.ret);
if (outParams.length > 0) {
let mapped = [];
let memIdx = 0; // offset from `outAddress` where the data should get written, in units of 4 bytes
for (let outParam of outParams) {
if (outParam.isArray) {
if (isZ3PointerType(outParam.type) || outParam.type === 'unsigned') {
let { sizeIndex } = outParam;
let count;
if (arrayLengthParams.has(sizeIndex)) {
// i.e. this is also the length of an input array
count = args[sizeIndex];
} else {
let sizeParam = fn.params[sizeIndex];
if (!(sizeParam.kind === 'in' && sizeParam.type === 'unsigned' && !sizeParam.isPtr && !sizeParam.isArray)) {
throw new Error(
`size index is not unsigned int (for fn ${fn.name} parameter ${sizeIndex} got ${sizeParam.type})`,
);
}
count = sizeParam.name;
}
let outArrayAddress = `outArray_${outParam.name}`;
prefix += `
let ${outArrayAddress} = Mod._malloc(4 * ${count});
try {
`.trim();
suffix =
`
} finally {
Mod._free(${outArrayAddress});
}
`.trim() + suffix;
args[outParam.idx] = outArrayAddress;
mapped.push({
name: outParam.name,
read:
`readUintArray(${outArrayAddress}, ${count})` +
(outParam.type === 'unsigned' ? '' : `as unknown as ${outParam.type}[]`),
type: `${outParam.type}[]`,
});
} else {
console.error(`skipping ${fn.name} - out array of ${outParam.type}`);
return null;
}
} else if (outParam.isPtr) {
function setArg() {
args[outParam.idx] = memIdx === 0 ? 'outAddress' : `outAddress + ${memIdx * 4}`;
}
let read, type;
if (outParam.type === 'string') {
read = `Mod.UTF8ToString(getOutUint(${memIdx}))`;
setArg();
++memIdx;
} else if (isZ3PointerType(outParam.type)) {
read = `getOutUint(${memIdx}) as unknown as ${outParam.type}`;
setArg();
++memIdx;
} else if (outParam.type === 'unsigned') {
read = `getOutUint(${memIdx})`;
setArg();
++memIdx;
} else if (outParam.type === 'int') {
read = `getOutInt(${memIdx})`;
setArg();
++memIdx;
} else if (outParam.type === 'uint64_t') {
if (memIdx % 2 === 1) {
++memIdx;
}
read = `getOutUint64(${memIdx / 2})`;
setArg();
memIdx += 2;
} else if (outParam.type === 'int64_t') {
if (memIdx % 2 === 1) {
++memIdx;
}
read = `getOutInt64(${memIdx / 2})`;
setArg();
memIdx += 2;
} else {
console.error(`skipping ${fn.name} - unknown out parameter type ${outParam.type}`);
return null;
}
if (memIdx > Math.floor(BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 4)) {
// prettier-ignore
console.error(`skipping ${fn.name} - out parameter sizes sum to ${memIdx * 4}, which is > ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS}`);
return null;
}
mapped.push({
name: outParam.name,
read,
type: outParam.type,
});
} else {
console.error(`skipping ${fn.name} - out param is neither pointer nor array`);
return null;
}
}
let ignoreReturn = fn.ret === 'boolean' || fn.ret === 'void';
if (outParams.length === 1) {
let outParam = mapped[0];
if (ignoreReturn) {
returnType = outParam.type;
rv = outParam.read;
} else {
returnType = `{ rv: ${fn.ret}, ${outParam.name} : ${outParam.type} }`;
rv = `{ rv: ret, ${outParam.name} : ${outParam.read} }`;
}
} else {
if (ignoreReturn) {
returnType = `{ ${mapped.map(p => `${p.name} : ${p.type}`).join(', ')} }`;
rv = `{ ${mapped.map(p => `${p.name}: ${p.read}`).join(', ')} }`;
} else {
returnType = `{ rv: ${fn.ret}, ${mapped.map(p => `${p.name} : ${p.type}`).join(', ')} }`;
rv = `{ rv: ret, ${mapped.map(p => `${p.name}: ${p.read}`).join(', ')} }`;
}
}
if (fn.ret === 'boolean') {
// assume the boolean indicates success
infix += `
if (!ret) {
return null;
}
`.trim();
cReturnType = 'boolean';
returnType += ' | null';
} else if (fn.ret === 'void') {
cReturnType = 'void';
} else if (isZ3PointerType(fn.ret) || fn.ret === 'unsigned') {
cReturnType = 'number';
} else {
console.error(`skipping ${fn.name} - out parameter for function which returns non-boolean`);
return null;
}
}
if (fn.nullableRet) {
returnType += ' | null';
infix += `
if (ret === 0) {
return null;
}
`.trim();
}
if (isAsync) {
}
// prettier-ignore
let invocation = `Mod.ccall('${isAsync ? 'async_' : ''}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(', ')}])`;
if (isAsync) {
invocation = `await Mod.async_call(() => ${invocation})`;
returnType = `Promise<${returnType}>`;
}
let out = `${name}: ${isAsync ? 'async' : ''} function(${params.filter(p => p != null).join(', ')}): ${returnType} {
${prefix}`;
if (infix === '' && suffix === '' && rv === 'ret') {
out += `return ${invocation};`;
} else {
out += `
let ret = ${invocation};
${infix}return ${rv};${suffix}
`.trim();
}
out += '}';
return out;
}
function wrapEnum(name, values) {
let enumEntries = Object.entries(values);
return `export enum ${name} {
${enumEntries.map(([k, v], i) => k + (v === (enumEntries[i - 1]?.[1] ?? -1) + 1 ? '' : ` = ${v}`) + ',').join('\n')}
};`;
}
function getValidOutArrayIndexes(size) {
return Array.from({ length: Math.floor(BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / size) }, (_, i) => i).join(' | ');
}
let out = `
// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND
// @ts-ignore no-implicit-any
import initModule = require('./z3-built.js');
interface Pointer<T extends string> extends Number {
readonly __typeName: T;
}
interface Subpointer<T extends string, S extends string> extends Pointer<S> {
readonly __typeName2: T;
}
${Object.entries(primitiveTypes)
.filter(e => e[0] !== 'void')
.map(e => `type ${e[0]} = ${e[1]};`)
.join('\n')}
${Object.keys(types)
.filter(k => k.startsWith('Z3'))
.map(makePointerType)
.join('\n')}
${Object.entries(enums)
.map(e => wrapEnum(e[0], e[1]))
.join('\n\n')}
export async function init() {
let Mod = await initModule();
// this works for both signed and unsigned, because JS will wrap for you when constructing the Uint32Array
function intArrayToByteArr(ints: number[]) {
return new Uint8Array((new Uint32Array(ints)).buffer);
}
function boolArrayToByteArr(bools: boolean[]) {
return bools.map(b => b ? 1 : 0);
}
function readUintArray(address: number, count: number) {
return Array.from(new Uint32Array(Mod.HEAPU32.buffer, address, count));
}
let outAddress = Mod._malloc(${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS});
let outUintArray = (new Uint32Array(Mod.HEAPU32.buffer, outAddress, 4));
let getOutUint = (i: ${getValidOutArrayIndexes(4)}) => outUintArray[i];
let outIntArray = (new Int32Array(Mod.HEAPU32.buffer, outAddress, 4));
let getOutInt = (i: ${getValidOutArrayIndexes(4)}) => outIntArray[i];
let outUint64Array = (new BigUint64Array(Mod.HEAPU32.buffer, outAddress, 2));
let getOutUint64 = (i: ${getValidOutArrayIndexes(8)}) => outUint64Array[i];
let outInt64Array = (new BigInt64Array(Mod.HEAPU32.buffer, outAddress, 2));
let getOutInt64 = (i: ${getValidOutArrayIndexes(8)}) => outInt64Array[i];
return {
em: Mod,
Z3: {
${functions
.map(wrapFunction)
.filter(f => f != null)
.join(',\n')}
}
};
}
`;
console.log(prettier.format(out, { singleQuote: true, parser: 'typescript' }));

View file

@ -0,0 +1,362 @@
'use strict';
let fs = require('fs');
let path = require('path');
let files = [
'z3_api.h',
'z3_algebraic.h',
'z3_ast_containers.h',
'z3_fixedpoint.h',
'z3_fpa.h',
'z3_optimization.h',
'z3_polynomial.h',
'z3_rcf.h',
'z3_spacer.h',
];
let aliases = {
__proto__: null,
Z3_bool: 'boolean',
Z3_string: 'string',
bool: 'boolean',
signed: 'int',
};
let primitiveTypes = {
__proto__: null,
Z3_char_ptr: 'string',
unsigned: 'number',
int: 'number',
uint64_t: 'bigint',
int64_t: 'bigint',
double: 'number',
float: 'number',
};
let optTypes = {
__proto__: null,
Z3_sort_opt: 'Z3_sort',
Z3_ast_opt: 'Z3_ast',
Z3_func_interp_opt: 'Z3_func_interp',
};
// parse type declarations
let types = {
__proto__: null,
// these are function types I can't be bothered to parse
Z3_error_handler: 'Z3_error_handler',
Z3_push_eh: 'Z3_push_eh',
Z3_pop_eh: 'Z3_pop_eh',
Z3_fresh_eh: 'Z3_fresh_eh',
Z3_fixed_eh: 'Z3_fixed_eh',
Z3_eq_eh: 'Z3_eq_eh',
Z3_final_eh: 'Z3_final_eh',
Z3_created_eh: 'Z3_created_eh',
};
let defApis = Object.create(null);
let functions = [];
let enums = Object.create(null);
for (let file of files) {
let contents = fs.readFileSync(path.join(__dirname, '..', '..', file), 'utf8');
// we _could_ use an actual C++ parser, which accounted for macros and everything
// but that's super painful
// and the files are regular enough that we can get away without it
// we could also do this by modifying the `update_api.py` script
// which we should probably do eventually
// but this is easier while this remains not upstreamed
// we need to parse the `def_API` stuff so we know which things are out parameters
// unfortunately we also need to parse the actual declarations so we know the parameter names also
let pytypes = Object.create(null);
let typeMatches = contents.matchAll(
/def_Type\(\s*'(?<name>[A-Za-z0-9_]+)',\s*'(?<cname>[A-Za-z0-9_]+)',\s*'(?<pname>[A-Za-z0-9_]+)'\)/g,
);
for (let { groups } of typeMatches) {
pytypes[groups.name] = groups.cname;
}
// we filter first to ensure our regex isn't too strict
let apiLines = contents.split('\n').filter(l => /def_API|extra_API/.test(l));
for (let line of apiLines) {
let match = line.match(
/^\s*(?<def>def_API|extra_API) *\(\s*'(?<name>[A-Za-z0-9_]+)'\s*,\s*(?<ret>[A-Za-z0-9_]+)\s*,\s*\((?<params>((_in|_out|_in_array|_out_array|_inout_array)\([^)]+\)\s*,?\s*)*)\)\s*\)\s*$/,
);
if (match == null) {
throw new Error(`failed to match def_API call ${JSON.stringify(line)}`);
}
let { name, ret, def } = match.groups;
let params = match.groups.params.trim();
let text = params;
let parsedParams = [];
while (true) {
text = eatWs(text);
({ text, match } = eat(text, /^_(?<kind>in|out|in_array|out_array|inout_array)\(/));
if (match == null) {
break;
}
let kind = match.groups.kind;
if (kind === 'inout_array') kind = 'in_array'; // https://github.com/Z3Prover/z3/discussions/5761
if (kind === 'in' || kind === 'out') {
({ text, match } = expect(text, /^[A-Za-z0-9_]+/));
parsedParams.push({ kind, type: match[0] });
} else {
({ text, match } = expect(text, /^(\d+),/));
let sizeIndex = Number(match[1]);
text = eatWs(text);
({ text, match } = expect(text, /^[A-Za-z0-9_]+/));
parsedParams.push({ kind, sizeIndex, type: match[0] });
}
({ text, match } = expect(text, /^\)/));
text = eatWs(text);
({ text, match } = eat(text, /^,/));
}
if (text !== '') {
throw new Error(`extra text in parameter list ${JSON.stringify(text)}`);
}
if (name in defApis) {
throw new Error(`multiple defApi calls for ${name}`);
}
defApis[name] = { params: parsedParams, ret, extra: def === 'extra_API' };
}
for (let match of contents.matchAll(/DEFINE_TYPE\((?<type>[A-Za-z0-9_]+)\)/g)) {
types[match.groups.type] = match.groups.type;
}
// we don't have to pre-populate the types map with closure types
// use the Z3_DECLARE_CLOSURE to identify closure types
// for (let match of contents.matchAll(/Z3_DECLARE_CLOSURE\((?<type>[A-Za-z0-9_]+),/g)) {
// types[match.groups.type] = match.groups.type
// }
// parse enum declarations
for (let idx = 0; idx < contents.length; ) {
let nextIdx = contents.indexOf('typedef enum', idx);
if (nextIdx === -1) {
break;
}
let lineStart = contents.lastIndexOf('\n', nextIdx);
let lineEnd = contents.indexOf(';', nextIdx);
if (lineStart === -1 || lineEnd === -1) {
throw new Error(`could not parse enum at index ${nextIdx}`);
}
idx = lineEnd;
let slice = contents.substring(lineStart, lineEnd);
let { match, text } = eat(slice, /^\s*typedef enum\s*\{/);
if (match === null) {
throw new Error(`could not parse enum ${JSON.stringify(slice)}`);
}
let vals = Object.create(null);
let next = 0;
while (true) {
let blank = true;
while (blank) {
({ match, text } = eat(text, /^\s*(\/\/[^\n]*\n)?/));
blank = match[0].length > 0;
}
({ match, text } = eat(text, /^[A-Za-z0-9_]+/));
if (match === null) {
throw new Error(`could not parse enum value in ${slice}`);
}
let name = match[0];
text = eatWs(text);
({ match, text } = eat(text, /^= *(?<val>[^\n,\s]+)/));
if (match !== null) {
let parsedVal = Number(match.groups.val);
if (Object.is(parsedVal, NaN)) {
throw new Error('unknown value ' + match.groups.val);
}
vals[name] = parsedVal;
next = parsedVal;
} else {
vals[name] = next;
}
text = eatWs(text);
({ match, text } = eat(text, /^,?\s*}/));
if (match !== null) {
break;
}
({ match, text } = expect(text, /^,/));
++next;
}
text = eatWs(text);
({ match, text } = expect(text, /^[A-Za-z0-9_]+/));
if (match[0] in enums) {
throw new Error(`duplicate enum definition ${match[0]}`);
}
enums[match[0]] = vals;
text = eatWs(text);
if (text !== '') {
throw new Error('expected end of definition, got ' + text);
}
}
// parse function declarations
for (let idx = 0; idx < contents.length; ) {
let nextIdx = contents.indexOf(' Z3_API ', idx);
if (nextIdx === -1) {
break;
}
let lineStart = contents.lastIndexOf('\n', nextIdx);
let lineEnd = contents.indexOf(';', nextIdx);
if (lineStart === -1 || lineEnd === -1) {
throw new Error(`could not parse definition at index ${nextIdx}`);
}
idx = lineEnd;
let slice = contents.substring(lineStart, lineEnd);
let match = slice.match(/^\s*(?<ret>[A-Za-z0-9_]+) +Z3_API +(?<name>[A-Za-z0-9_]+)\s*\((?<params>[^)]*)\)/);
if (match == null) {
throw new Error(`failed to match c definition: ${JSON.stringify(slice)}`);
}
let { ret, name, params } = match.groups;
let parsedParams = [];
if (params.trim() !== 'void') {
for (let param of params.split(',')) {
let paramType, paramName, isConst, isPtr, isArray;
let { match, text } = eat(param, /^\s*/);
({ match, text } = eat(text, /^[A-Za-z0-9_]+/));
if (match === null) {
throw new Error(`failed to parse param type in ${JSON.stringify(slice)} for param ${JSON.stringify(param)}`);
}
paramType = match[0];
text = eatWs(text);
({ match, text } = eat(text, /^const(?![A-Za-z0-9_])/));
isConst = match !== null;
({ match, text } = eat(text, /^\s*\*/));
isPtr = match !== null;
text = eatWs(text);
if (text === '') {
paramName = 'UNNAMED';
isArray = false;
} else {
({ match, text } = eat(text, /^[A-Za-z0-9_]+/));
if (match === null) {
throw new Error(
`failed to parse param name in ${JSON.stringify(slice)} for param ${JSON.stringify(param)}`,
);
}
paramName = match[0];
text = eatWs(text);
({ match, text } = eat(text, /^\[\]/));
isArray = match !== null;
text = eatWs(text);
if (text !== '') {
throw new Error(`excess text in param in ${JSON.stringify(slice)} for param ${JSON.stringify(param)}`);
}
}
if (paramType === 'Z3_string_ptr' && !isPtr) {
paramType = 'Z3_string';
isPtr = true;
}
let nullable = false;
if (paramType in optTypes) {
nullable = true;
paramType = optTypes[paramType];
}
let cType = paramType;
paramType = aliases[paramType] ?? paramType;
parsedParams.push({ type: paramType, cType, name: paramName, isConst, isPtr, isArray, nullable });
}
}
let nullableRet = false;
if (ret in optTypes) {
nullableRet = true;
ret = optTypes[ret];
}
let cRet = ret;
ret = aliases[ret] ?? ret;
if (name in defApis) {
functions.push({ ret, cRet, name, params: parsedParams, nullableRet });
}
// only a few things are missing `def_API`; we'll skip those
}
}
function isKnownType(t) {
return t in enums || t in types || t in primitiveTypes || ['string', 'boolean', 'void'].includes(t);
}
for (let fn of functions) {
if (!isKnownType(fn.ret)) {
throw new Error(`unknown type ${fn.ret}`);
}
let defParams = defApis[fn.name].params;
if (fn.params.length !== defParams.length) {
throw new Error(`parameter length mismatch for ${fn.name}`);
}
let idx = 0;
for (let param of fn.params) {
if (!isKnownType(param.type)) {
throw new Error(`unknown type ${param.type}`);
}
param.kind = defParams[idx].kind;
if (param.kind === 'in_array' || param.kind === 'out_array') {
if (defParams[idx].sizeIndex == null) {
throw new Error(`function ${fn.name} parameter ${idx} is marked as ${param.kind} but has no index`);
}
param.sizeIndex = defParams[idx].sizeIndex;
if (!param.isArray && param.isPtr) {
// not clear why some things are written as `int * x` and others `int x[]`
// but we can jsut cast
param.isArray = true;
param.isPtr = false;
}
if (!param.isArray) {
throw new Error(`function ${fn.name} parameter ${idx} is marked as ${param.kind} but not typed as array`);
}
}
++idx;
}
}
function eat(str, regex) {
const match = str.match(regex);
if (match == null) {
return { match, text: str };
}
return { match, text: str.substring(match[0].length) };
}
function eatWs(text) {
return eat(text, /^\s*/).text;
}
function expect(str, regex) {
let { text, match } = eat(str, regex);
if (match === null) {
throw new Error(`expected ${regex}, got ${JSON.stringify(text)}`);
}
return { text, match };
}
module.exports = { primitiveTypes, types, enums, functions };

View file

@ -0,0 +1,38 @@
// this wrapper works with async-fns to provide promise-based off-thread versions of some functions
let capability = null;
function resolve_async(val) {
// setTimeout is a workaround for https://github.com/emscripten-core/emscripten/issues/15900
if (capability == null) {
return;
}
let cap = capability;
capability = null;
setTimeout(() => {
cap.resolve(val);
}, 0);
}
function reject_async(val) {
if (capability == null) {
return;
}
let cap = capability;
capability = null;
setTimeout(() => {
cap.reject(val);
}, 0);
}
Module.async_call = function (f, ...args) {
if (capability !== null) {
throw new Error(`you can't execute multiple async functions at the same time; let the previous one finish first`);
}
let promise = new Promise((resolve, reject) => {
capability = { resolve, reject };
});
f(...args);
return promise;
};

392
src/api/js/test-ts-api.ts Normal file
View file

@ -0,0 +1,392 @@
// Some of the examples from test_capi.c.
// Note that none of the type annotations on variable declarations are necessary:
// TypeScript can infer all of them.
// They're just here so readers can see what types things are.
import type {
Z3_config,
Z3_context,
Z3_solver,
Z3_sort,
Z3_ast,
Z3_app,
Z3_model,
Z3_symbol,
Z3_ast_vector,
Z3_func_decl,
Z3_func_interp,
Z3_func_entry,
} from './build/wrapper';
import { init, Z3_lbool, Z3_ast_kind, Z3_sort_kind, Z3_symbol_kind } from './build/wrapper';
// @ts-ignore we're not going to bother with types for this
import { sprintf } from 'sprintf-js';
let printf = (str: string, ...args: unknown[]) => console.log(sprintf(str.replace(/\n$/, ''), ...args));
(async () => {
let { em, Z3 } = await init();
function mk_context(): Z3_context {
let cfg: Z3_config = Z3.mk_config();
Z3.set_param_value(cfg, 'model', 'true');
let ctx: Z3_context = Z3.mk_context(cfg);
Z3.del_config(cfg);
return ctx;
}
function mk_proof_context(): Z3_context {
let cfg: Z3_config = Z3.mk_config();
let ctx: Z3_context;
Z3.set_param_value(cfg, 'proof', 'true');
ctx = Z3.mk_context(cfg);
Z3.del_config(cfg);
return ctx;
}
function mk_solver(ctx: Z3_context): Z3_solver {
let s: Z3_solver = Z3.mk_solver(ctx);
Z3.solver_inc_ref(ctx, s);
return s;
}
function del_solver(ctx: Z3_context, s: Z3_solver) {
Z3.solver_dec_ref(ctx, s);
}
function mk_var(ctx: Z3_context, name: string, ty: Z3_sort): Z3_ast {
let s: Z3_symbol = Z3.mk_string_symbol(ctx, name);
return Z3.mk_const(ctx, s, ty);
}
function mk_bool_var(ctx: Z3_context, name: string): Z3_ast {
let ty: Z3_sort = Z3.mk_bool_sort(ctx);
return mk_var(ctx, name, ty);
}
function exitf(m: string) {
console.error(`BUG: ${m}`);
process.exit(1);
}
function display_symbol(c: Z3_context, s: Z3_symbol) {
switch (Z3.get_symbol_kind(c, s)) {
case Z3_symbol_kind.Z3_INT_SYMBOL:
process.stdout.write(sprintf('#%d', Z3.get_symbol_int(c, s)));
break;
case Z3_symbol_kind.Z3_STRING_SYMBOL:
process.stdout.write(sprintf('%s', Z3.get_symbol_string(c, s)));
break;
default:
throw new Error('unreachable');
}
}
function display_sort(c: Z3_context, ty: Z3_sort) {
switch (Z3.get_sort_kind(c, ty)) {
case Z3_sort_kind.Z3_UNINTERPRETED_SORT:
display_symbol(c, Z3.get_sort_name(c, ty));
break;
case Z3_sort_kind.Z3_BOOL_SORT:
process.stdout.write('bool');
break;
case Z3_sort_kind.Z3_INT_SORT:
process.stdout.write('int');
break;
case Z3_sort_kind.Z3_REAL_SORT:
process.stdout.write('real');
break;
case Z3_sort_kind.Z3_BV_SORT:
process.stdout.write(sprintf('bv%d', Z3.get_bv_sort_size(c, ty)));
break;
case Z3_sort_kind.Z3_ARRAY_SORT:
process.stdout.write('[');
display_sort(c, Z3.get_array_sort_domain(c, ty));
process.stdout.write('->');
display_sort(c, Z3.get_array_sort_range(c, ty));
process.stdout.write(']');
break;
case Z3_sort_kind.Z3_DATATYPE_SORT:
if (Z3.get_datatype_sort_num_constructors(c, ty) !== 1) {
process.stdout.write(sprintf('%s', Z3.sort_to_string(c, ty)));
break;
}
{
let num_fields: number = Z3.get_tuple_sort_num_fields(c, ty);
let i: number;
process.stdout.write('(');
for (i = 0; i < num_fields; i++) {
let field: Z3_func_decl = Z3.get_tuple_sort_field_decl(c, ty, i);
if (i > 0) {
process.stdout.write(', ');
}
display_sort(c, Z3.get_range(c, field));
}
process.stdout.write(')');
break;
}
default:
process.stdout.write('unknown[');
display_symbol(c, Z3.get_sort_name(c, ty));
process.stdout.write(']');
break;
}
}
function display_ast(c: Z3_context, v: Z3_ast) {
switch (Z3.get_ast_kind(c, v)) {
case Z3_ast_kind.Z3_NUMERAL_AST: {
let t: Z3_sort;
process.stdout.write(sprintf('%s', Z3.get_numeral_string(c, v)));
t = Z3.get_sort(c, v);
process.stdout.write(':');
display_sort(c, t);
break;
}
case Z3_ast_kind.Z3_APP_AST: {
let i: number;
let app: Z3_app = Z3.to_app(c, v);
let num_fields: number = Z3.get_app_num_args(c, app);
let d: Z3_func_decl = Z3.get_app_decl(c, app);
process.stdout.write(sprintf('%s', Z3.func_decl_to_string(c, d)));
if (num_fields > 0) {
process.stdout.write('[');
for (i = 0; i < num_fields; i++) {
if (i > 0) {
process.stdout.write(', ');
}
display_ast(c, Z3.get_app_arg(c, app, i));
}
process.stdout.write(']');
}
break;
}
case Z3_ast_kind.Z3_QUANTIFIER_AST: {
process.stdout.write('quantifier');
}
default:
process.stdout.write('#unknown');
}
}
function display_function_interpretations(c: Z3_context, m: Z3_model) {
let num_functions: number;
let i: number;
process.stdout.write('function interpretations:\n');
num_functions = Z3.model_get_num_funcs(c, m);
for (i = 0; i < num_functions; i++) {
let fdecl: Z3_func_decl;
let name: Z3_symbol;
let func_else: Z3_ast;
let num_entries = 0;
let j: number;
let finterp: Z3_func_interp | null;
fdecl = Z3.model_get_func_decl(c, m, i);
finterp = Z3.model_get_func_interp(c, m, fdecl);
if (finterp) Z3.func_interp_inc_ref(c, finterp);
name = Z3.get_decl_name(c, fdecl);
display_symbol(c, name);
process.stdout.write(' = {');
if (finterp) num_entries = Z3.func_interp_get_num_entries(c, finterp);
for (j = 0; j < num_entries; j++) {
let num_args: number;
let k: number;
let fentry: Z3_func_entry = Z3.func_interp_get_entry(c, finterp!, j);
Z3.func_entry_inc_ref(c, fentry);
if (j > 0) {
process.stdout.write(', ');
}
num_args = Z3.func_entry_get_num_args(c, fentry);
process.stdout.write('(');
for (k = 0; k < num_args; k++) {
if (k > 0) {
process.stdout.write(', ');
}
display_ast(c, Z3.func_entry_get_arg(c, fentry, k));
}
process.stdout.write('|->');
display_ast(c, Z3.func_entry_get_value(c, fentry));
process.stdout.write(')');
Z3.func_entry_dec_ref(c, fentry);
}
if (num_entries > 0) {
process.stdout.write(', ');
}
if (finterp) {
process.stdout.write('(else|->');
func_else = Z3.func_interp_get_else(c, finterp);
display_ast(c, func_else);
Z3.func_interp_dec_ref(c, finterp);
}
process.stdout.write(')}\n');
}
}
function display_model(c: Z3_context, m: Z3_model | null) {
let num_constants: number;
let i: number;
if (!m) return;
num_constants = Z3.model_get_num_consts(c, m);
for (i = 0; i < num_constants; i++) {
let name: Z3_symbol;
let cnst: Z3_func_decl = Z3.model_get_const_decl(c, m, i);
let a: Z3_ast;
let v: Z3_ast;
let ok: boolean;
name = Z3.get_decl_name(c, cnst);
display_symbol(c, name);
process.stdout.write(' = ');
a = Z3.mk_app(c, cnst, []);
v = a;
let result = Z3.model_eval(c, m, a, true);
ok = result != null;
if (result != null) {
v = result;
}
display_ast(c, v);
process.stdout.write('\n');
}
display_function_interpretations(c, m);
}
async function check(ctx: Z3_context, s: Z3_solver, expected_result: Z3_lbool) {
let m: Z3_model | null = null;
let result: Z3_lbool = await Z3.solver_check(ctx, s);
switch (result) {
case Z3_lbool.Z3_L_FALSE:
printf('unsat\n');
break;
case Z3_lbool.Z3_L_UNDEF:
printf('unknown\n');
m = Z3.solver_get_model(ctx, s);
if (m) Z3.model_inc_ref(ctx, m);
printf('potential model:\n%s\n', Z3.model_to_string(ctx, m));
break;
case Z3_lbool.Z3_L_TRUE:
m = Z3.solver_get_model(ctx, s);
if (m) Z3.model_inc_ref(ctx, m);
printf('sat\n%s\n', Z3.model_to_string(ctx, m));
break;
}
if (result !== expected_result) {
exitf('unexpected result');
}
if (m) Z3.model_dec_ref(ctx, m);
}
// https://github.com/Z3Prover/z3/blob/174889ad5ea8b1e1127aeec8a4121a5687ac9a2b/examples/c/test_capi.c#L1440
async function bitvector_example2() {
let ctx: Z3_context = mk_context();
let s: Z3_solver = mk_solver(ctx);
/* construct x ^ y - 103 == x * y */
let bv_sort: Z3_sort = Z3.mk_bv_sort(ctx, 32);
let x: Z3_ast = mk_var(ctx, 'x', bv_sort);
let y: Z3_ast = mk_var(ctx, 'y', bv_sort);
let x_xor_y: Z3_ast = Z3.mk_bvxor(ctx, x, y);
let c103: Z3_ast = Z3.mk_numeral(ctx, '103', bv_sort);
let lhs: Z3_ast = Z3.mk_bvsub(ctx, x_xor_y, c103);
let rhs: Z3_ast = Z3.mk_bvmul(ctx, x, y);
let ctr: Z3_ast = Z3.mk_eq(ctx, lhs, rhs);
printf('\nbitvector_example2\n');
// LOG_MSG("bitvector_example2");
printf('find values of x and y, such that x ^ y - 103 == x * y\n');
// /* add the constraint <tt>x ^ y - 103 == x * y<\tt> to the logical context */
Z3.solver_assert(ctx, s, ctr);
// /* find a model (i.e., values for x an y that satisfy the constraint */
await check(ctx, s, Z3_lbool.Z3_L_TRUE);
del_solver(ctx, s);
Z3.del_context(ctx);
}
// https://github.com/Z3Prover/z3/blob/174889ad5ea8b1e1127aeec8a4121a5687ac9a2b/examples/c/test_capi.c#L2230
async function unsat_core_and_proof_example() {
let ctx: Z3_context = mk_proof_context();
let s: Z3_solver = mk_solver(ctx);
let pa: Z3_ast = mk_bool_var(ctx, 'PredA');
let pb: Z3_ast = mk_bool_var(ctx, 'PredB');
let pc: Z3_ast = mk_bool_var(ctx, 'PredC');
let pd: Z3_ast = mk_bool_var(ctx, 'PredD');
let p1: Z3_ast = mk_bool_var(ctx, 'P1');
let p2: Z3_ast = mk_bool_var(ctx, 'P2');
let p3: Z3_ast = mk_bool_var(ctx, 'P3');
let p4: Z3_ast = mk_bool_var(ctx, 'P4');
let assumptions: Z3_ast[] = [Z3.mk_not(ctx, p1), Z3.mk_not(ctx, p2), Z3.mk_not(ctx, p3), Z3.mk_not(ctx, p4)];
let args1: Z3_ast[] = [pa, pb, pc];
let f1 = Z3.mk_and(ctx, args1);
let args2: Z3_ast[] = [pa, Z3.mk_not(ctx, pb), pc];
let f2 = Z3.mk_and(ctx, args2);
let args3: Z3_ast[] = [Z3.mk_not(ctx, pa), Z3.mk_not(ctx, pc)];
let f3 = Z3.mk_or(ctx, args3);
let f4 = pd;
let g1: Z3_ast[] = [f1, p1];
let g2: Z3_ast[] = [f2, p2];
let g3: Z3_ast[] = [f3, p3];
let g4: Z3_ast[] = [f4, p4];
let result: Z3_lbool;
let proof: Z3_ast;
let m: Z3_model | null = null;
let i: number;
let core: Z3_ast_vector;
printf('\nunsat_core_and_proof_example\n');
// LOG_MSG("unsat_core_and_proof_example");
Z3.solver_assert(ctx, s, Z3.mk_or(ctx, g1));
Z3.solver_assert(ctx, s, Z3.mk_or(ctx, g2));
Z3.solver_assert(ctx, s, Z3.mk_or(ctx, g3));
Z3.solver_assert(ctx, s, Z3.mk_or(ctx, g4));
result = await Z3.solver_check_assumptions(ctx, s, assumptions);
switch (result) {
case Z3_lbool.Z3_L_FALSE:
core = Z3.solver_get_unsat_core(ctx, s);
proof = Z3.solver_get_proof(ctx, s);
printf('unsat\n');
printf('proof: %s\n', Z3.ast_to_string(ctx, proof));
printf('\ncore:\n');
for (i = 0; i < Z3.ast_vector_size(ctx, core); ++i) {
printf('%s\n', Z3.ast_to_string(ctx, Z3.ast_vector_get(ctx, core, i)));
}
printf('\n');
break;
case Z3_lbool.Z3_L_UNDEF:
printf('unknown\n');
printf('potential model:\n');
m = Z3.solver_get_model(ctx, s);
if (m) Z3.model_inc_ref(ctx, m);
display_model(ctx, m);
throw new Error('result was undef, should have been unsat');
case Z3_lbool.Z3_L_TRUE:
printf('sat\n');
m = Z3.solver_get_model(ctx, s);
if (m) Z3.model_inc_ref(ctx, m);
display_model(ctx, m);
throw new Error('result was sat, should have been unsat');
}
/* delete logical context */
if (m) Z3.model_dec_ref(ctx, m);
del_solver(ctx, s);
Z3.del_context(ctx);
}
await bitvector_example2();
await unsat_core_and_proof_example();
// shut down
em.PThread.terminateAllThreads();
})().catch(e => {
console.error('error', e);
process.exit(1);
});

14
src/api/js/tsconfig.json Normal file
View file

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es2021",
"module": "commonjs",
"esModuleInterop": true,
"declaration": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"exclude": [
"src"
]
}

View file

@ -203,7 +203,7 @@ end = struct
let equal = (=)
(* The standard comparison uses the custom operations of the C layer *)
let compare = Pervasives.compare
let compare = Stdlib.compare
let translate (x:ast) (to_ctx:context) =
if gc x = to_ctx then

View file

@ -99,7 +99,7 @@ if (Z3_INSTALL_PYTHON_BINDINGS)
message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR not set. Trying to guess")
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" "-c"
"import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())"
"import sysconfig; print(sysconfig.get_path('purelib'))"
RESULT_VARIABLE exit_code
OUTPUT_VARIABLE CMAKE_INSTALL_PYTHON_PKG_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE

View file

@ -1150,6 +1150,8 @@ def _to_expr_ref(a, ctx):
return FPRMRef(a, ctx)
if sk == Z3_SEQ_SORT:
return SeqRef(a, ctx)
if sk == Z3_CHAR_SORT:
return CharRef(a, ctx)
if sk == Z3_RE_SORT:
return ReRef(a, ctx)
return ExprRef(a, ctx)
@ -4809,7 +4811,7 @@ def Ext(a, b):
"""
ctx = a.ctx
if z3_debug():
_z3_assert(is_array_sort(a) and is_array(b), "arguments must be arrays")
_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)
@ -8789,6 +8791,10 @@ def Product(*args):
_args, sz = _to_ast_array(args)
return ArithRef(Z3_mk_mul(ctx.ref(), sz, _args), ctx)
def Abs(arg):
"""Create the absolute value of an arithmetic expression"""
return If(arg > 0, arg, -arg)
def AtMost(*args):
"""Create an at-most Pseudo-Boolean k constraint.
@ -10580,7 +10586,6 @@ class CharSortRef(SortRef):
"""Character sort."""
def StringSort(ctx=None):
"""Create a string sort
>>> s = StringSort()
@ -10646,18 +10651,68 @@ class SeqRef(ExprRef):
return Z3_ast_to_string(self.ctx_ref(), self.as_ast())
def __le__(self, other):
return SeqRef(Z3_mk_str_le(self.ctx_ref(), self.as_ast(), other.as_ast()), self.ctx)
return _to_expr_ref(Z3_mk_str_le(self.ctx_ref(), self.as_ast(), other.as_ast()), self.ctx)
def __lt__(self, other):
return SeqRef(Z3_mk_str_lt(self.ctx_ref(), self.as_ast(), other.as_ast()), self.ctx)
return _to_expr_ref(Z3_mk_str_lt(self.ctx_ref(), self.as_ast(), other.as_ast()), self.ctx)
def __ge__(self, other):
return SeqRef(Z3_mk_str_le(self.ctx_ref(), other.as_ast(), self.as_ast()), self.ctx)
return _to_expr_ref(Z3_mk_str_le(self.ctx_ref(), other.as_ast(), self.as_ast()), self.ctx)
def __gt__(self, other):
return SeqRef(Z3_mk_str_lt(self.ctx_ref(), other.as_ast(), self.as_ast()), self.ctx)
return _to_expr_ref(Z3_mk_str_lt(self.ctx_ref(), other.as_ast(), self.as_ast()), self.ctx)
def _coerce_char(ch, ctx=None):
if isinstance(ch, str):
ctx = _get_ctx(ctx)
ch = CharVal(ch, ctx)
if not is_expr(ch):
raise Z3Exception("Character expression expected")
return ch
class CharRef(ExprRef):
"""Character expression."""
def __le__(self, other):
other = _coerce_char(other, self.ctx)
return _to_expr_ref(Z3_mk_char_le(self.ctx_ref(), self.as_ast(), other.as_ast()), self.ctx)
def to_int(self):
return _to_expr_ref(Z3_mk_char_to_int(self.ctx_ref(), self.as_ast()), self.ctx)
def to_bv(self):
return _to_expr_ref(Z3_mk_char_to_bv(self.ctx_ref(), self.as_ast()), self.ctx)
def is_digit(self):
return _to_expr_ref(Z3_mk_char_is_digit(self.ctx_ref(), self.as_ast()), self.ctx)
def CharVal(ch, ctx=None):
ctx = _get_ctx(ctx)
if isinstance(ch, str):
ch = ord(ch)
if not isinstance(ch, int):
raise Z3Exception("character value should be an ordinal")
return _to_expr_ref(Z3_mk_char(ctx.ref(), ch), ctx)
def CharFromBv(ch, ctx=None):
if not is_expr(ch):
raise Z3Expression("Bit-vector expression needed")
return _to_expr_ref(Z3_mk_char_from_bv(ch.ctx_ref(), ch.as_ast()), ch.ctx)
def CharToBv(ch, ctx=None):
ch = _coerce_char(ch, ctx)
return ch.to_bv()
def CharToInt(ch, ctx=None):
ch = _coerce_char(ch, ctx)
return ch.to_int()
def CharIsDigit(ch, ctx=None):
ch = _coerce_char(ch, ctx)
return ch.is_digit()
def _coerce_seq(s, ctx=None):
if isinstance(s, str):
ctx = _get_ctx(ctx)
@ -10774,6 +10829,7 @@ def Full(s):
raise Z3Exception("Non-sequence, non-regular expression sort passed to Full")
def Unit(a):
"""Create a singleton sequence"""
return SeqRef(Z3_mk_seq_unit(a.ctx_ref(), a.as_ast()), a.ctx)
@ -10905,6 +10961,18 @@ def IntToStr(s):
return SeqRef(Z3_mk_int_to_str(s.ctx_ref(), s.as_ast()), s.ctx)
def StrToCode(s):
"""Convert a unit length string to integer code"""
if not is_expr(s):
s = _py2expr(s)
return ArithRef(Z3_mk_string_to_code(s.ctx_ref(), s.as_ast()), s.ctx)
def StrFromCode(c):
"""Convert code to a string"""
if not is_expr(c):
c = _py2expr(c)
return SeqRef(Z3_mk_string_from_code(c.ctx_ref(), c.as_ast()), c.ctx)
def Re(s, ctx=None):
"""The regular expression that accepts sequence 's'
>>> s1 = Re("ab")
@ -11065,6 +11133,11 @@ def Range(lo, hi, ctx=None):
hi = _coerce_seq(hi, ctx)
return ReRef(Z3_mk_re_range(lo.ctx_ref(), lo.ast, hi.ast), lo.ctx)
def Diff(a, b, ctx=None):
"""Create the difference regular epression
"""
return ReRef(Z3_mk_re_diff(a.ctx_ref(), a.ast, b.ast), a.ctx)
def AllChar(regex_sort, ctx=None):
"""Create a regular expression that accepts all single character strings
"""

View file

@ -1192,6 +1192,9 @@ typedef enum {
Z3_OP_SEQ_CONTAINS,
Z3_OP_SEQ_EXTRACT,
Z3_OP_SEQ_REPLACE,
Z3_OP_SEQ_REPLACE_RE,
Z3_OP_SEQ_REPLACE_RE_ALL,
Z3_OP_SEQ_REPLACE_ALL,
Z3_OP_SEQ_AT,
Z3_OP_SEQ_NTH,
Z3_OP_SEQ_LENGTH,
@ -1205,6 +1208,8 @@ typedef enum {
Z3_OP_INT_TO_STR,
Z3_OP_UBV_TO_STR,
Z3_OP_SBV_TO_STR,
Z3_OP_STR_TO_CODE,
Z3_OP_STR_FROM_CODE,
Z3_OP_STRING_LT,
Z3_OP_STRING_LE,
@ -1216,7 +1221,9 @@ typedef enum {
Z3_OP_RE_UNION,
Z3_OP_RE_RANGE,
Z3_OP_RE_LOOP,
Z3_OP_RE_POWER,
Z3_OP_RE_INTERSECT,
Z3_OP_RE_DIFF,
Z3_OP_RE_EMPTY_SET,
Z3_OP_RE_FULL_SET,
Z3_OP_RE_COMPLEMENT,
@ -1418,18 +1425,18 @@ typedef enum
/**
\brief Z3 custom error handler (See #Z3_set_error_handler).
*/
typedef void Z3_error_handler(Z3_context c, Z3_error_code e);
Z3_DECLARE_CLOSURE(Z3_error_handler, void, (Z3_context c, Z3_error_code e));
/**
\brief callback functions for user propagator.
*/
typedef void Z3_push_eh(void* ctx);
typedef void Z3_pop_eh(void* ctx, unsigned num_scopes);
typedef void* Z3_fresh_eh(void* ctx, Z3_context new_context);
typedef void Z3_fixed_eh(void* ctx, Z3_solver_callback cb, unsigned id, Z3_ast value);
typedef void Z3_eq_eh(void* ctx, Z3_solver_callback cb, unsigned x, unsigned y);
typedef void Z3_final_eh(void* ctx, Z3_solver_callback cb);
Z3_DECLARE_CLOSURE(Z3_push_eh, void, (void* ctx));
Z3_DECLARE_CLOSURE(Z3_pop_eh, void, (void* ctx, unsigned num_scopes));
Z3_DECLARE_CLOSURE(Z3_fresh_eh, void*, (void* ctx, Z3_context new_context));
Z3_DECLARE_CLOSURE(Z3_fixed_eh, void, (void* ctx, Z3_solver_callback cb, unsigned id, Z3_ast value));
Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, unsigned x, unsigned y));
Z3_DECLARE_CLOSURE(Z3_final_eh, void, (void* ctx, Z3_solver_callback cb));
Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast e, unsigned id));
/**
@ -1510,7 +1517,7 @@ extern "C" {
def_API('Z3_global_param_get', BOOL, (_in(STRING), _out(STRING)))
*/
Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value);
Z3_bool Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value);
/**@}*/
@ -3702,6 +3709,21 @@ extern "C" {
*/
Z3_ast Z3_API Z3_mk_int_to_str(Z3_context c, Z3_ast s);
/**
\brief String to code conversion.
def_API('Z3_mk_string_to_code', AST, (_in(CONTEXT), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_string_to_code(Z3_context c, Z3_ast a);
/**
\brief Code to string conversion.
def_API('Z3_mk_string_from_code', AST, (_in(CONTEXT), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_string_from_code(Z3_context c, Z3_ast a);
/**
\brief Unsigned bit-vector to string conversion.
@ -3811,6 +3833,13 @@ extern "C" {
*/
Z3_ast Z3_API Z3_mk_re_complement(Z3_context c, Z3_ast re);
/**
\brief Create the difference of regular expressions.
def_API('Z3_mk_re_diff', AST ,(_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_re_diff(Z3_context c, Z3_ast re1, Z3_ast re2);
/**
\brief Create an empty regular expression of sort \c re.
@ -3830,6 +3859,13 @@ extern "C" {
*/
Z3_ast Z3_API Z3_mk_re_full(Z3_context c, Z3_sort re);
/**
\brief Create a character literal
def_API('Z3_mk_char', AST, (_in(CONTEXT), _in(UINT)))
*/
Z3_ast Z3_API Z3_mk_char(Z3_context c, unsigned ch);
/**
\brief Create less than or equal to between two characters.
@ -4312,7 +4348,7 @@ extern "C" {
def_API('Z3_get_finite_domain_sort_size', BOOL, (_in(CONTEXT), _in(SORT), _out(UINT64)))
*/
Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, uint64_t* r);
Z3_bool Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, uint64_t* r);
/**
\brief Return the domain of the given array sort.
@ -5251,7 +5287,7 @@ extern "C" {
def_API('Z3_model_eval', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _in(BOOL), _out(AST)))
*/
Z3_bool_opt Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, bool model_completion, Z3_ast * v);
Z3_bool Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, bool model_completion, Z3_ast * v);
/**
\brief Return the interpretation (i.e., assignment) of constant \c a in the model \c m.
@ -6676,6 +6712,24 @@ extern "C" {
*/
void Z3_API Z3_solver_propagate_diseq(Z3_context c, Z3_solver s, Z3_eq_eh eq_eh);
/**
* \brief register a callback when a new expression with a registered function is used by the solver
* The registered function appears at the top level and is created using \ref Z3_propagate_solver_declare.
*/
void Z3_API Z3_solver_propagate_created(Z3_context c, Z3_solver s, Z3_created_eh created_eh);
/**
Create uninterpreted function declaration for the user propagator.
When expressions using the function are created by the solver invoke a callback
to \ref \Z3_solver_progate_created with arguments
1. context and callback solve
2. declared_expr: expression using function that was used as the top-level symbol
3. declared_id: a unique identifier (unique within the current scope) to track the expression.
def_API('Z3_solver_propagate_declare', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT)))
*/
Z3_func_decl Z3_API Z3_solver_propagate_declare(Z3_context c, Z3_symbol name, unsigned n, Z3_sort* domain, Z3_sort range);
/**
\brief register an expression to propagate on with the solver.
Only expressions of type Bool and type Bit-Vector can be registered for propagation.

View file

@ -4,9 +4,6 @@ Copyright (c) 2015 Microsoft Corporation
--*/
#ifndef Z3_bool_opt
#define Z3_bool_opt Z3_bool
#endif
#ifndef Z3_API
# ifdef __GNUC__
@ -19,3 +16,7 @@ Copyright (c) 2015 Microsoft Corporation
#ifndef DEFINE_TYPE
#define DEFINE_TYPE(T) typedef struct _ ## T *T
#endif
#ifndef Z3_DECLARE_CLOSURE
#define Z3_DECLARE_CLOSURE(_NAME_,_RETURN_,_ARGS_) typedef _RETURN_ _NAME_ _ARGS_
#endif

View file

@ -273,6 +273,17 @@ public:
bool is_rem0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_REM0); }
bool is_mod0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_MOD0); }
bool is_power0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER0); }
bool is_power(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER); }
bool is_add(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_ADD); }
bool is_mul(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_MUL); }
bool is_sub(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_SUB); }
bool is_uminus(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_UMINUS); }
bool is_div(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_DIV); }
bool is_rem(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_REM); }
bool is_mod(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_MOD); }
bool is_to_real(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_TO_REAL); }
bool is_to_int(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_TO_INT); }
bool is_is_int(func_decl const* f) const { return is_decl_of(f, arith_family_id, OP_IS_INT); }
bool is_add(expr const * n) const { return is_app_of(n, arith_family_id, OP_ADD); }
bool is_sub(expr const * n) const { return is_app_of(n, arith_family_id, OP_SUB); }
@ -291,6 +302,7 @@ public:
bool is_is_int(expr const * n) const { return is_app_of(n, arith_family_id, OP_IS_INT); }
bool is_power(expr const * n) const { return is_app_of(n, arith_family_id, OP_POWER); }
bool is_power0(expr const * n) const { return is_app_of(n, arith_family_id, OP_POWER0); }
bool is_abs(expr const* n) const { return is_app_of(n, arith_family_id, OP_ABS); }
bool is_int(sort const * s) const { return is_sort_of(s, arith_family_id, INT_SORT); }
bool is_int(expr const * n) const { return is_int(n->get_sort()); }
@ -330,6 +342,7 @@ public:
MATCH_UNARY(is_to_real);
MATCH_UNARY(is_to_int);
MATCH_UNARY(is_is_int);
MATCH_UNARY(is_abs);
MATCH_BINARY(is_sub);
MATCH_BINARY(is_add);
MATCH_BINARY(is_mul);

View file

@ -149,7 +149,12 @@ public:
bool is_store(expr* n) const { return is_app_of(n, m_fid, OP_STORE); }
bool is_const(expr* n) const { return is_app_of(n, m_fid, OP_CONST_ARRAY); }
bool is_ext(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_EXT); }
bool is_ext(func_decl const* f) const { return is_decl_of(f, m_fid, OP_ARRAY_EXT); }
bool is_map(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAP); }
bool is_union(expr* n) const { return is_app_of(n, m_fid, OP_SET_UNION); }
bool is_intersect(expr* n) const { return is_app_of(n, m_fid, OP_SET_INTERSECT); }
bool is_difference(expr* n) const { return is_app_of(n, m_fid, OP_SET_DIFFERENCE); }
bool is_complement(expr* n) const { return is_app_of(n, m_fid, OP_SET_COMPLEMENT); }
bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); }
bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); }
bool is_set_has_size(expr* e) const { return is_app_of(e, m_fid, OP_SET_HAS_SIZE); }
@ -158,11 +163,14 @@ public:
bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); }
bool is_const(func_decl* f) const { return is_decl_of(f, m_fid, OP_CONST_ARRAY); }
bool is_map(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_MAP); }
bool is_union(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_UNION); }
bool is_intersect(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_INTERSECT); }
bool is_as_array(func_decl* f) const { return is_decl_of(f, m_fid, OP_AS_ARRAY); }
bool is_set_has_size(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_HAS_SIZE); }
bool is_set_card(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_CARD); }
bool is_default(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_DEFAULT); }
bool is_default(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_DEFAULT); }
bool is_subset(expr const* n) const { return is_app_of(n, m_fid, OP_SET_SUBSET); }
bool is_as_array(func_decl* f, func_decl*& g) const { return is_decl_of(f, m_fid, OP_AS_ARRAY) && (g = get_as_array_func_decl(f), true); }
func_decl * get_as_array_func_decl(expr * n) const;
func_decl * get_as_array_func_decl(func_decl* f) const;
@ -172,6 +180,8 @@ public:
bool is_const(expr* e, expr*& v) const;
bool is_store_ext(expr* e, expr_ref& a, expr_ref_vector& args, expr_ref& value);
MATCH_BINARY(is_subset);
};
class array_util : public array_recognizers {

View file

@ -845,7 +845,7 @@ class quantifier : public expr {
char m_patterns_decls[0];
static unsigned get_obj_size(unsigned num_decls, unsigned num_patterns, unsigned num_no_patterns) {
return sizeof(quantifier) + num_decls * (sizeof(sort *) + sizeof(symbol)) + (num_patterns + num_no_patterns) * sizeof(expr*);
return (unsigned)(sizeof(quantifier) + num_decls * (sizeof(sort *) + sizeof(symbol)) + (num_patterns + num_no_patterns) * sizeof(expr*));
}
quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* s,

View file

@ -260,39 +260,52 @@ expr_ref mk_distinct(expr_ref_vector const& args) {
void flatten_and(expr_ref_vector& result) {
ast_manager& m = result.get_manager();
expr* e1, *e2, *e3;
expr_ref_vector pin(m);
expr_fast_mark1 seen;
for (unsigned i = 0; i < result.size(); ++i) {
if (m.is_and(result.get(i))) {
app* a = to_app(result.get(i));
for (expr* arg : *a) result.push_back(arg);
expr* e = result.get(i);
if (seen.is_marked(e)) {
result[i] = result.back();
result.pop_back();
--i;
continue;
}
seen.mark(e);
pin.push_back(e);
if (m.is_and(e)) {
app* a = to_app(e);
for (expr* arg : *a)
result.push_back(arg);
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_not(result.get(i), e1) && m.is_not(e1, e2)) {
else if (m.is_not(e, e1) && m.is_not(e1, e2)) {
result[i] = e2;
--i;
}
else if (m.is_not(result.get(i), e1) && m.is_or(e1)) {
else if (m.is_not(e, e1) && m.is_or(e1)) {
app* a = to_app(e1);
for (expr* arg : *a) result.push_back(m.mk_not(arg));
for (expr* arg : *a)
result.push_back(mk_not(m, arg));
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_not(result.get(i), e1) && m.is_implies(e1,e2,e3)) {
else if (m.is_not(e, e1) && m.is_implies(e1, e2, e3)) {
result.push_back(e2);
result[i] = m.mk_not(e3);
result[i] = mk_not(m, e3);
--i;
}
else if (m.is_true(result.get(i)) ||
(m.is_not(result.get(i), e1) &&
else if (m.is_true(e) ||
(m.is_not(e, e1) &&
m.is_false(e1))) {
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_false(result.get(i)) ||
(m.is_not(result.get(i), e1) &&
else if (m.is_false(e) ||
(m.is_not(e, e1) &&
m.is_true(e1))) {
result.reset();
result.push_back(m.mk_false());
@ -317,39 +330,52 @@ void flatten_and(expr_ref& fml) {
void flatten_or(expr_ref_vector& result) {
ast_manager& m = result.get_manager();
expr* e1, *e2, *e3;
expr_ref_vector pin(m);
expr_fast_mark1 seen;
for (unsigned i = 0; i < result.size(); ++i) {
if (m.is_or(result.get(i))) {
app* a = to_app(result.get(i));
for (expr* arg : *a) result.push_back(arg);
expr* e = result.get(i);
if (seen.is_marked(e)) {
result[i] = result.back();
result.pop_back();
--i;
continue;
}
seen.mark(e);
pin.push_back(e);
if (m.is_or(e)) {
app* a = to_app(e);
for (expr* arg : *a)
result.push_back(arg);
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_not(result.get(i), e1) && m.is_not(e1, e2)) {
else if (m.is_not(e, e1) && m.is_not(e1, e2)) {
result[i] = e2;
--i;
}
else if (m.is_not(result.get(i), e1) && m.is_and(e1)) {
else if (m.is_not(e, e1) && m.is_and(e1)) {
app* a = to_app(e1);
for (expr* arg : *a) result.push_back(m.mk_not(arg));
for (expr* arg : *a)
result.push_back(mk_not(m, arg));
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_implies(result.get(i),e2,e3)) {
else if (m.is_implies(e,e2,e3)) {
result.push_back(e3);
result[i] = m.mk_not(e2);
result[i] = mk_not(m, e2);
--i;
}
else if (m.is_false(result.get(i)) ||
(m.is_not(result.get(i), e1) &&
else if (m.is_false(e) ||
(m.is_not(e, e1) &&
m.is_true(e1))) {
result[i] = result.back();
result.pop_back();
--i;
}
else if (m.is_true(result.get(i)) ||
(m.is_not(result.get(i), e1) &&
else if (m.is_true(e) ||
(m.is_not(e, e1) &&
m.is_false(e1))) {
result.reset();
result.push_back(m.mk_true());

View file

@ -580,7 +580,7 @@ namespace datatype {
m_defs.remove(s);
}
bool plugin::is_value_visit(expr * arg, ptr_buffer<app> & todo) const {
bool plugin::is_value_visit(bool unique, expr * arg, ptr_buffer<app> & todo) const {
if (!is_app(arg))
return false;
family_id fid = to_app(arg)->get_family_id();
@ -592,12 +592,13 @@ namespace datatype {
todo.push_back(to_app(arg));
return true;
}
else {
else if (unique)
return m_manager->is_unique_value(arg);
else
return m_manager->is_value(arg);
}
}
bool plugin::is_value(app * e) const {
bool plugin::is_value_aux(bool unique, app * e) const {
TRACE("dt_is_value", tout << "checking\n" << mk_ismt2_pp(e, *m_manager) << "\n";);
if (!u().is_constructor(e))
return false;
@ -608,7 +609,7 @@ namespace datatype {
ptr_buffer<app> todo;
// potentially expensive check for common sub-expressions.
for (expr* arg : *e) {
if (!is_value_visit(arg, todo)) {
if (!is_value_visit(unique, arg, todo)) {
TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";);
return false;
}
@ -618,7 +619,7 @@ namespace datatype {
SASSERT(u().is_constructor(curr));
todo.pop_back();
for (expr* arg : *curr) {
if (!is_value_visit(arg, todo)) {
if (!is_value_visit(unique, arg, todo)) {
TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";);
return false;
}

View file

@ -230,9 +230,9 @@ namespace datatype {
bool is_fully_interp(sort * s) const override;
bool is_value(app* e) const override;
bool is_value(app* e) const override { return is_value_aux(false, e); }
bool is_unique_value(app * e) const override { return is_value(e); }
bool is_unique_value(app * e) const override { return is_value_aux(true, e); }
void get_op_names(svector<builtin_name> & op_names, symbol const & logic) override;
@ -257,7 +257,8 @@ namespace datatype {
bool has_nested_arrays() const { return m_has_nested_arrays; }
private:
bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
bool is_value_visit(bool unique, expr * arg, ptr_buffer<app> & todo) const;
bool is_value_aux(bool unique, app * arg) const;
func_decl * mk_update_field(
unsigned num_parameters, parameter const * parameters,

View file

@ -25,6 +25,8 @@ namespace euf {
enode* egraph::mk_enode(expr* f, unsigned generation, unsigned num_args, enode * const* args) {
enode* n = enode::mk(m_region, f, generation, num_args, args);
if (m_default_relevant)
n->set_relevant(true);
m_nodes.push_back(n);
m_exprs.push_back(f);
if (is_app(f) && num_args > 0) {
@ -187,10 +189,10 @@ namespace euf {
add_th_diseq(id, v1, v2, n->get_expr());
return;
}
for (auto p : euf::enode_th_vars(r1)) {
for (auto const& p : euf::enode_th_vars(r1)) {
if (!th_propagates_diseqs(p.get_id()))
continue;
for (auto q : euf::enode_th_vars(r2))
for (auto const& q : euf::enode_th_vars(r2))
if (p.get_id() == q.get_id())
add_th_diseq(p.get_id(), p.get_var(), q.get_var(), n->get_expr());
}
@ -269,6 +271,13 @@ namespace euf {
}
}
void egraph::set_relevant(enode* n) {
if (n->is_relevant())
return;
n->set_relevant(true);
m_updates.push_back(update_record(n, update_record::set_relevant()));
}
void egraph::toggle_merge_enabled(enode* n, bool backtracking) {
bool enable_merge = !n->merge_enabled();
n->set_merge_enabled(enable_merge);
@ -380,6 +389,10 @@ namespace euf {
case update_record::tag_t::is_lbl_set:
p.r1->m_lbls.set(p.m_lbls);
break;
case update_record::tag_t::is_set_relevant:
SASSERT(p.r1->is_relevant());
p.r1->set_relevant(false);
break;
case update_record::tag_t::is_update_children:
for (unsigned i = 0; i < p.r1->num_args(); ++i) {
SASSERT(p.r1->m_args[i]->get_root()->m_parents.back() == p.r1);
@ -446,8 +459,8 @@ namespace euf {
r2->inc_class_size(r1->class_size());
merge_th_eq(r1, r2);
reinsert_parents(r1, r2);
if (m_on_merge)
m_on_merge(r2, r1);
for (auto& cb : m_on_merge)
cb(r2, r1);
}
void egraph::remove_parents(enode* r1, enode* r2) {
@ -493,7 +506,7 @@ namespace euf {
void egraph::merge_th_eq(enode* n, enode* root) {
SASSERT(n != root);
for (auto iv : enode_th_vars(n)) {
for (auto const& iv : enode_th_vars(n)) {
theory_id id = iv.get_id();
theory_var v = root->get_th_var(id);
if (v == null_theory_var) {
@ -754,6 +767,8 @@ namespace euf {
}
std::ostream& egraph::display(std::ostream& out, unsigned max_args, enode* n) const {
if (!n->is_relevant())
out << "n";
out << "#" << n->get_expr_id() << " := ";
expr* f = n->get_expr();
if (is_app(f))
@ -770,11 +785,18 @@ namespace euf {
out << " " << p->get_expr_id();
out << "] ";
}
if (n->value() != l_undef)
out << "[b" << n->bool_var() << " := " << (n->value() == l_true ? "T":"F") << (n->merge_tf()?"":" no merge") << "] ";
auto value_of = [&]() {
switch (n->value()) {
case l_true: return "T";
case l_false: return "F";
default: return "?";
}
};
if (n->bool_var() != sat::null_bool_var)
out << "[b" << n->bool_var() << " := " << value_of() << (n->merge_tf() ? "" : " no merge") << "] ";
if (n->has_th_vars()) {
out << "[t";
for (auto v : enode_th_vars(n))
for (auto const& v : enode_th_vars(n))
out << " " << v.get_id() << ":" << v.get_var();
out << "] ";
}

View file

@ -32,6 +32,7 @@ Notes:
#include "ast/euf/euf_enode.h"
#include "ast/euf/euf_etable.h"
#include "ast/ast_ll_pp.h"
#include <vector>
namespace euf {
@ -105,10 +106,11 @@ namespace euf {
struct lbl_hash {};
struct lbl_set {};
struct update_children {};
struct set_relevant {};
enum class tag_t { is_set_parent, is_add_node, is_toggle_merge, is_update_children,
is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq,
is_lbl_hash, is_new_th_eq_qhead, is_new_lits_qhead,
is_inconsistent, is_value_assignment, is_lbl_set };
is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant };
tag_t tag;
enode* r1;
enode* n1;
@ -151,6 +153,8 @@ namespace euf {
tag(tag_t::is_lbl_set), r1(n), n1(nullptr), m_lbls(n->m_lbls.get()) {}
update_record(enode* n, update_children) :
tag(tag_t::is_update_children), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
update_record(enode* n, set_relevant) :
tag(tag_t::is_set_relevant), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
};
ast_manager& m;
svector<to_merge> m_to_merge;
@ -181,7 +185,8 @@ namespace euf {
enode_vector m_todo;
stats m_stats;
bool m_uses_congruence = false;
std::function<void(enode*,enode*)> m_on_merge;
bool m_default_relevant = true;
std::vector<std::function<void(enode*,enode*)>> m_on_merge;
std::function<void(enode*)> m_on_make;
std::function<void(expr*,expr*,expr*)> m_used_eq;
std::function<void(app*,app*)> m_used_cc;
@ -292,8 +297,10 @@ namespace euf {
void set_merge_enabled(enode* n, bool enable_merge);
void set_value(enode* n, lbool value);
void set_bool_var(enode* n, unsigned v) { n->set_bool_var(v); }
void set_relevant(enode* n);
void set_default_relevant(bool b) { m_default_relevant = b; }
void set_on_merge(std::function<void(enode* root,enode* other)>& on_merge) { m_on_merge = on_merge; }
void set_on_merge(std::function<void(enode* root,enode* other)>& on_merge) { m_on_merge.push_back(on_merge); }
void set_on_make(std::function<void(enode* n)>& on_make) { m_on_make = on_make; }
void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; }
void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; }

View file

@ -45,10 +45,12 @@ namespace euf {
expr* m_expr = nullptr;
bool m_mark1 = false;
bool m_mark2 = false;
bool m_mark3 = false;
bool m_commutative = false;
bool m_interpreted = false;
bool m_merge_enabled = true;
bool m_is_equality = false; // Does the expression represent an equality
bool m_is_relevant = false;
lbool m_value = l_undef; // Assignment by SAT solver for Boolean node
sat::bool_var m_bool_var = sat::null_bool_var; // SAT solver variable associated with Boolean node
unsigned m_class_size = 1; // Size of the equivalence class if the enode is the root.
@ -145,6 +147,8 @@ namespace euf {
unsigned num_parents() const { return m_parents.size(); }
bool interpreted() const { return m_interpreted; }
bool is_equality() const { return m_is_equality; }
bool is_relevant() const { return m_is_relevant; }
void set_relevant(bool b) { m_is_relevant = b; }
lbool value() const { return m_value; }
bool value_conflict() const { return value() != l_undef && get_root()->value() != l_undef && value() != get_root()->value(); }
sat::bool_var bool_var() const { return m_bool_var; }
@ -170,6 +174,9 @@ namespace euf {
void mark2() { m_mark2 = true; }
void unmark2() { m_mark2 = false; }
bool is_marked2() { return m_mark2; }
void mark3() { m_mark3 = true; }
void unmark3() { m_mark3 = false; }
bool is_marked3() { return m_mark3; }
template<bool m> void mark1_targets() {
enode* n = this;

View file

@ -410,10 +410,7 @@ namespace recfun {
promise_def plugin::ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
def* d = u().decl_fun(name, n, params, range, is_generated);
def* d2 = nullptr;
if (m_defs.find(d->get_decl(), d2)) {
dealloc(d2);
}
erase_def(d->get_decl());
m_defs.insert(d->get_decl(), d);
return promise_def(&u(), d);
}

View file

@ -305,7 +305,7 @@ namespace recfun {
m_pred(pred), m_cdef(&d), m_args(args) {}
body_expansion(body_expansion const & from):
m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(from.m_args) {}
body_expansion(body_expansion && from) :
body_expansion(body_expansion && from) noexcept :
m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(std::move(from.m_args)) {}
std::ostream& display(std::ostream& out) const;

View file

@ -722,12 +722,12 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
return BR_DONE;
}
if (m().is_not(rhs))
if (m().is_not(rhs))
std::swap(lhs, rhs);
if (m().is_not(lhs, lhs)) {
result = m().mk_not(m().mk_eq(lhs, rhs));
return BR_REWRITE2;
if (m().is_not(lhs, lhs)) {
result = m().mk_not(m().mk_eq(lhs, rhs));
return BR_REWRITE2;
}
if (unfolded) {

View file

@ -2818,14 +2818,15 @@ br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args,
return BR_DONE;
}
if (is_num1 && is_num2) {
rational mr = a0_val * a1_val;
rational lim = rational::power_of_two(bv_sz-1);
result = m().mk_bool_val(mr < lim);
return BR_DONE;
}
return BR_FAILED;
if (!is_num1 || !is_num2)
return BR_FAILED;
rational lim = rational::power_of_two(bv_sz);
rational r = a0_val * a1_val;
bool sign1 = m_util.has_sign_bit(a0_val, bv_sz);
bool sign2 = m_util.has_sign_bit(a1_val, bv_sz);
result = m().mk_bool_val((sign1 != sign2) || r < lim);
return BR_DONE;
}
br_status bv_rewriter::mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result) {

View file

@ -46,7 +46,7 @@ class maximize_ac_sharing : public default_rewriter_cfg {
entry(func_decl * d = nullptr, expr * arg1 = nullptr, expr * arg2 = nullptr):m_decl(d), m_arg1(arg1), m_arg2(arg2) {
SASSERT((d == 0 && arg1 == 0 && arg2 == 0) || (d != 0 && arg1 != 0 && arg2 != 0));
if (arg1->get_id() > arg2->get_id())
if (arg1 && arg2 && arg1->get_id() > arg2->get_id())
std::swap(m_arg1, m_arg2);
}

View file

@ -21,8 +21,11 @@ Revision History:
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/recfun_decl_plugin.h"
#include "ast/rewriter/recfun_replace.h"
#include "ast/rewriter/seq_axioms.h"
namespace seq {
axioms::axioms(th_rewriter& r):
@ -1052,6 +1055,81 @@ namespace seq {
NOT_IMPLEMENTED_YET();
}
// A basic strategy for supporting replace_all and other
// similar functions is to use recursive relations.
// Then the features for recursive functions that allow expanding definitions
// using iterative deepening can be re-used.
//
// create recursive relation 'ra' with properties:
// ra(i, j, s, p, t, r) <- len(s) = i && len(r) = j
// ra(i, j, s, p, t, r) <- len(s) > i = 0 && p = "" && r = t + s
// ra(i, j, s, p, t, r) <- len(s) > i && p != "" && s = extract(s, 0, i) + p + extract(s, i + len(p), len(s)) && r = extract(r, 0, i) + t + extract(r, i + len(p), len(r)) && ra(i + len(p), j + len(t), s, p, t, r)
// ra(i, s, p, t, r) <- ~prefix(p, extract(s, i, len(s)) && at(s,i) = at(r,j) && ra(i + 1, j + 1, s, p, t, r)
// which amounts to:
//
//
// Then assert
// ra(s, p, t, replace_all(s, p, t))
//
void axioms::replace_all_axiom(expr* r) {
expr* s = nullptr, *p = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_all(r, s, p, t));
recfun::util rec(m);
recfun::decl::plugin& plugin = rec.get_plugin();
recfun_replace replace(m);
sort* srt = s->get_sort();
sort* domain[4] = { srt, srt, srt, srt };
auto d = plugin.ensure_def(symbol("ra"), 4, domain, m.mk_bool_sort(), true);
func_decl* ra = d.get_def()->get_decl();
(void)ra;
sort* isrt = a.mk_int();
var_ref vi(m.mk_var(5, isrt), m);
var_ref vj(m.mk_var(4, isrt), m);
var_ref vs(m.mk_var(3, srt), m);
var_ref vp(m.mk_var(2, srt), m);
var_ref vt(m.mk_var(1, srt), m);
var_ref vr(m.mk_var(0, srt), m);
var* vars[6] = { vi, vj, vs, vp, vt, vr };
(void)vars;
expr_ref len_s(seq.str.mk_length(vs), m);
expr_ref len_r(seq.str.mk_length(vr), m);
expr_ref test1(m.mk_eq(len_s, vi), m);
expr_ref branch1(m.mk_eq(len_r, vj), m);
expr_ref test2(m.mk_and(a.mk_gt(len_s, vi), m.mk_eq(vi, a.mk_int(0)), seq.str.mk_is_empty(vp)), m);
expr_ref branch2(m.mk_eq(vr, seq.str.mk_concat(vt, vs)), m);
NOT_IMPLEMENTED_YET();
#if 0
expr_ref test3(, m);
expr_ref s1(m_sk.mk_prefix_inv(vp, vs), m);
expr_ref r1(m_sk.mk_prefix_inv(vp, vr), m);
expr* args1[4] = { s1, vp, vt, r1 };
expr_ref branch3(m.mk_and(m.mk_eq(seq.str.mk_concat(vp, s1), vs),
m.mk_eq(seq.str.mk_concat(vr, r1), vr),
m.mk_app(ra, 4, args1)
), m);
expr_ref s0(m), r0(m);
m_sk.decompose(vs, s0, s1);
m_sk.decompose(vr, r0, r1);
expr* args2[4] = { s1, vp, vt, r1 };
expr_ref branch4(m.mk_and(m.mk_eq(vs, seq.str.mk_concat(s0, s1)),
m.mk_eq(vr, seq.str.mk_concat(s0, r1)),
m.mk_app(ra, 4, args2)), m);
// s = [s0] + s' && r = [s0] + r' && ra(s', p, t, r')
expr_ref body(m.mk_ite(test1, branch1, m.mk_ite(test2, branch2, m.mk_ite(test3, branch3, branch4))), m);
plugin.set_definition(replace, d, true, 4, vars, body);
expr* args3[4] = { s, p, t, r };
expr_ref lit(m.mk_app(ra, 4, args3), m);
add_clause(lit);
#endif
}
void axioms::replace_re_all_axiom(expr* e) {
expr* s = nullptr, *p = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_re_all(e, s, p, t));
NOT_IMPLEMENTED_YET();
}
/**

View file

@ -107,6 +107,8 @@ namespace seq {
void length_axiom(expr* n);
void unroll_not_contains(expr* e);
void replace_re_axiom(expr* e);
void replace_all_axiom(expr* e);
void replace_re_all_axiom(expr* e);
expr_ref length_limit(expr* s, unsigned k);
expr_ref is_digit(expr* ch);

File diff suppressed because it is too large Load diff

View file

@ -200,7 +200,7 @@ class seq_rewriter {
expr_ref mk_der_inter(expr* a, expr* b);
expr_ref mk_der_compl(expr* a);
expr_ref mk_der_cond(expr* cond, expr* ele, sort* seq_sort);
expr_ref mk_der_antimorov_union(expr* r1, expr* r2);
expr_ref mk_der_antimirov_union(expr* r1, expr* r2);
bool ite_bdds_compatabile(expr* a, expr* b);
/* if r has the form deriv(en..deriv(e1,to_re(s))..) returns 's = [e1..en]' else returns '() in r'*/
expr_ref is_nullable_symbolic_regex(expr* r, sort* seq_sort);
@ -214,14 +214,19 @@ class seq_rewriter {
expr_ref mk_in_antimirov_rec(expr* s, expr* d);
expr_ref mk_in_antimirov(expr* s, expr* d);
expr_ref mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path);
expr_ref mk_antimirov_deriv_intersection(expr* elem, expr* d1, expr* d2, expr* path);
expr_ref mk_antimirov_deriv_concat(expr* d, expr* r);
expr_ref mk_antimirov_deriv_negate(expr* d);
expr_ref mk_antimirov_deriv_negate(expr* elem, expr* d);
expr_ref mk_antimirov_deriv_union(expr* d1, expr* d2);
expr_ref mk_antimirov_deriv_restrict(expr* elem, expr* d1, expr* cond);
expr_ref mk_regex_reverse(expr* r);
expr_ref mk_regex_concat(expr* r1, expr* r2);
expr_ref simplify_path(expr* path);
expr_ref merge_regex_sets(expr* r1, expr* r2, expr* unit, std::function<bool(expr*, expr*&, expr*&)>& decompose, std::function<expr* (expr*, expr*)>& compose);
// elem is (:var 0) and path a condition that may have (:var 0) as a free variable
// simplify path, e.g., (:var 0) = 'a' & (:var 0) = 'b' is simplified to false
expr_ref simplify_path(expr* elem, expr* path);
bool lt_char(expr* ch1, expr* ch2);
bool eq_char(expr* ch1, expr* ch2);
@ -414,5 +419,10 @@ public:
// heuristic elimination of element from condition that comes form a derivative.
// special case optimization for conjunctions of equalities, disequalities and ranges.
void elim_condition(expr* elem, expr_ref& cond);
/* Apply simplifications to the union to keep the union normalized (r1 and r2 are not normalized)*/
expr_ref mk_regex_union_normalize(expr* r1, expr* r2);
/* Apply simplifications to the intersection to keep it normalized (r1 and r2 are not normalized)*/
expr_ref mk_regex_inter_normalize(expr* r1, expr* r2);
};

View file

@ -36,7 +36,8 @@ namespace seq {
symbol m_indexof_left, m_indexof_right; // inverse of indexof: (indexof_left s t) + s + (indexof_right s t) = t, for s in t.
symbol m_aut_step; // regex unfolding state
symbol m_accept; // regex
symbol m_is_empty, m_is_non_empty; // regex emptiness check
symbol m_is_empty; // regex emptiness check
symbol m_is_non_empty;
symbol m_pre, m_post; // inverse of at: (pre s i) + (at s i) + (post s i) = s if 0 <= i < (len s)
symbol m_postp;
symbol m_eq; // equality atom
@ -73,8 +74,8 @@ namespace seq {
}
expr_ref mk_accept(expr_ref_vector const& args) { return expr_ref(seq.mk_skolem(m_accept, args.size(), args.data(), m.mk_bool_sort()), m); }
expr_ref mk_accept(expr* s, expr* i, expr* r) { return mk(m_accept, s, i, r, nullptr, m.mk_bool_sort()); }
expr_ref mk_is_non_empty(expr* r, expr* u, expr* n) { return mk(m_is_non_empty, r, u, n, m.mk_bool_sort(), false); }
expr_ref mk_is_empty(expr* r, expr* u, expr* n) { return mk(m_is_empty, r, u, n, m.mk_bool_sort(), false); }
expr_ref mk_is_non_empty(expr* r, expr* u, expr* n) { return mk(m_is_non_empty, r, u, n, m.mk_bool_sort(), false); }
expr_ref mk_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_left, t, s, offset); }
expr_ref mk_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_right, t, s, offset); }
@ -153,12 +154,12 @@ namespace seq {
bool is_length_limit(expr* e) const { return is_skolem(m_length_limit, e); }
bool is_length_limit(expr* p, unsigned& lim, expr*& s) const;
bool is_is_empty(expr* e) const { return is_skolem(m_is_empty, e); }
bool is_is_non_empty(expr* e) const { return is_skolem(m_is_non_empty, e); }
bool is_is_empty(expr* e, expr*& r, expr*& u, expr*& n) const {
return is_skolem(m_is_empty, e) && (r = to_app(e)->get_arg(0), u = to_app(e)->get_arg(1), n = to_app(e)->get_arg(2), true);
}
bool is_is_non_empty(expr* e, expr*& r, expr*& u, expr*& n) const {
return is_skolem(m_is_non_empty, e) && (r = to_app(e)->get_arg(0), u = to_app(e)->get_arg(1), n = to_app(e)->get_arg(2), true);
bool is_is_non_empty(expr* e) const { return is_skolem(m_is_non_empty, e); }
bool is_is_non_empty(expr* e, expr*& r, expr*& u, expr*& n) const {
return is_skolem(m_is_non_empty, e) && (r = to_app(e)->get_arg(0), u = to_app(e)->get_arg(1), n = to_app(e)->get_arg(2), true);
}
void decompose(expr* e, expr_ref& head, expr_ref& tail);

View file

@ -627,7 +627,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
count_down_subterm_references(result, reference_map);
// Any term that was newly introduced by the rewrite step is only referenced within / reachable from the result term.
for (auto kv : reference_map) {
for (auto const& kv : reference_map) {
if (kv.m_value == 0) {
m().trace_stream() << "[attach-enode] #" << kv.m_key->get_id() << " 0\n";
}
@ -691,7 +691,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
return;
if (m_new_subst) {
m_rep.reset();
for (auto kv : m_subst->sub())
for (auto const& kv : m_subst->sub())
m_rep.insert(kv.m_key, kv.m_value);
m_new_subst = false;
}
@ -859,8 +859,8 @@ ast_manager & th_rewriter::m() const {
}
void th_rewriter::updt_params(params_ref const & p) {
m_params = p;
m_imp->cfg().updt_params(p);
m_params.append(p);
m_imp->cfg().updt_params(m_params);
}
void th_rewriter::get_param_descrs(param_descrs & r) {

View file

@ -48,6 +48,7 @@ public:
Otherwise, (VAR 0) is stored in the first position, (VAR 1) in the second, and so on.
*/
expr_ref operator()(expr * n, unsigned num_args, expr * const * args);
inline expr_ref operator()(expr* n, expr* arg) { return (*this)(n, 1, &arg); }
inline expr_ref operator()(expr * n, expr_ref_vector const& args) { return (*this)(n, args.size(), args.data()); }
inline expr_ref operator()(expr * n, var_ref_vector const& args) { return (*this)(n, args.size(), (expr*const*)args.data()); }
inline expr_ref operator()(expr * n, app_ref_vector const& args) { return (*this)(n, args.size(), (expr*const*)args.data()); }

View file

@ -243,7 +243,7 @@ void seq_decl_plugin::init() {
m_sigs[OP_RE_OF_PRED] = alloc(psig, m, "re.of.pred", 1, 1, &predA, reA);
m_sigs[OP_RE_REVERSE] = alloc(psig, m, "re.reverse", 1, 1, &reA, reA);
m_sigs[OP_RE_DERIVATIVE] = alloc(psig, m, "re.derivative", 1, 2, AreA, reA);
m_sigs[_OP_RE_ANTIMOROV_UNION] = alloc(psig, m, "re.union", 1, 2, reAreA, reA);
m_sigs[_OP_RE_ANTIMIROV_UNION] = alloc(psig, m, "re.union", 1, 2, reAreA, reA);
m_sigs[OP_SEQ_TO_RE] = alloc(psig, m, "seq.to.re", 1, 1, &seqA, reA);
m_sigs[OP_SEQ_IN_RE] = alloc(psig, m, "seq.in.re", 1, 2, seqAreA, boolT);
m_sigs[OP_SEQ_REPLACE_RE_ALL] = alloc(psig, m, "str.replace_re_all", 1, 3, seqAreAseqA, seqA);
@ -414,9 +414,9 @@ func_decl* seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
case OP_RE_COMPLEMENT:
case OP_RE_REVERSE:
case OP_RE_DERIVATIVE:
case _OP_RE_ANTIMOROV_UNION:
case _OP_RE_ANTIMIROV_UNION:
m_has_re = true;
// fall-through
Z3_fallthrough;
case OP_SEQ_UNIT:
case OP_STRING_ITOS:
case OP_STRING_STOI:
@ -516,6 +516,7 @@ func_decl* seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
case OP_SEQ_REPLACE_RE_ALL:
case OP_SEQ_REPLACE_RE:
m_has_re = true;
Z3_fallthrough;
case OP_SEQ_REPLACE_ALL:
return mk_str_fun(k, arity, domain, range, k);
@ -830,6 +831,39 @@ app* seq_util::mk_lt(expr* ch1, expr* ch2) const {
return m.mk_not(mk_le(ch2, ch1));
}
bool seq_util::is_char_const_range(expr const* x, expr* e, unsigned& l, unsigned& u, bool& negated) const {
expr* a, * b, * e0, * e1, * e2, * lb, * ub;
e1 = e;
negated = (m.is_not(e, e1)) ? true : false;
if (m.is_eq(e1, a, b) && (a == x && is_const_char(b, l))) {
u = l;
return true;
}
if (is_char_le(e1, a, b) && a == x && is_const_char(b, u)) {
// (x <= u)
l = 0;
return true;
}
if (is_char_le(e1, a, b) && b == x && is_const_char(a, l)) {
// (l <= x)
u = max_char();
return true;
}
if (m.is_and(e1, e0, e2) && is_char_le(e0, lb, a) && a == x && is_const_char(lb, l) &&
is_char_le(e2, b, ub) && b == x && is_const_char(ub, u))
// (l <= x) & (x <= u)
return true;
if (m.is_eq(e1, a, b) && b == x && is_const_char(a, l)) {
u = l;
return true;
}
if (m.is_and(e1, e0, e2) && is_char_le(e0, a, ub) && a == x && is_const_char(ub, u) &&
is_char_le(e2, lb, b) && b == x && is_const_char(lb, l))
// (x <= u) & (l <= x)
return true;
return false;
}
bool seq_util::str::is_string(func_decl const* f, zstring& s) const {
if (is_string(f)) {
s = f->get_parameter(0).get_zstring();
@ -1029,6 +1063,23 @@ app* seq_util::rex::mk_loop(expr* r, unsigned lo, unsigned hi) {
return m.mk_app(m_fid, OP_RE_LOOP, 2, params, 1, &r);
}
expr* seq_util::rex::mk_loop_proper(expr* r, unsigned lo, unsigned hi)
{
if (lo == 0 && hi == 0) {
sort* seq_sort = nullptr;
VERIFY(u.is_re(r, seq_sort));
// avoid creating a loop with both bounds 0
// such an expression is invalid as a loop
// it is BY DEFINITION = epsilon
return mk_epsilon(seq_sort);
}
if (lo == 1 && hi == 1)
// do not create a loop unless it actually is a loop
return r;
parameter params[2] = { parameter(lo), parameter(hi) };
return m.mk_app(m_fid, OP_RE_LOOP, 2, params, 1, &r);
}
app* seq_util::rex::mk_loop(expr* r, expr* lo) {
expr* rs[2] = { r, lo };
return m.mk_app(m_fid, OP_RE_LOOP, 0, nullptr, 2, rs);
@ -1283,7 +1334,7 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const {
print(out, r1);
print(out, r2);
}
else if (re.is_antimorov_union(e, r1, r2) || re.is_union(e, r1, r2)) {
else if (re.is_antimirov_union(e, r1, r2) || re.is_union(e, r1, r2)) {
out << "(";
print(out, r1);
out << (html_encode ? "&#x22C3;" : "|");
@ -1502,9 +1553,9 @@ seq_util::rex::info seq_util::rex::mk_info_rec(app* e) const {
if (e->get_family_id() == u.get_family_id()) {
switch (e->get_decl()->get_decl_kind()) {
case OP_RE_EMPTY_SET:
return info(true, true, true, true, true, true, false, l_false, UINT_MAX, 0);
return info(true, l_false, UINT_MAX);
case OP_RE_FULL_SEQ_SET:
return info(true, true, true, true, true, true, false, l_true, 0, 1);
return info(true, l_true, 0);
case OP_RE_STAR:
i1 = get_info_rec(e->get_arg(0));
return i1.star();
@ -1516,7 +1567,7 @@ seq_util::rex::info seq_util::rex::mk_info_rec(app* e) const {
case OP_RE_OF_PRED:
//TBD: check if the character predicate contains uninterpreted symbols or is nonground or is unsat
//TBD: check if the range is unsat
return info(true, true, true, true, true, true, true, l_false, 1, 0);
return info(true, l_false, 1);
case OP_RE_CONCAT:
i1 = get_info_rec(e->get_arg(0));
i2 = get_info_rec(e->get_arg(1));
@ -1533,7 +1584,7 @@ seq_util::rex::info seq_util::rex::mk_info_rec(app* e) const {
min_length = u.str.min_length(e->get_arg(0));
is_value = m.is_value(e->get_arg(0));
nullable = (is_value && min_length == 0 ? l_true : (min_length > 0 ? l_false : l_undef));
return info(true, true, is_value, true, true, true, (min_length == 1 && u.str.max_length(e->get_arg(0)) == 1), nullable, min_length, 0);
return info(is_value, nullable, min_length);
case OP_RE_REVERSE:
return get_info_rec(e->get_arg(0));
case OP_RE_PLUS:
@ -1569,14 +1620,7 @@ std::ostream& seq_util::rex::info::display(std::ostream& out) const {
if (is_known()) {
out << "info("
<< "nullable=" << (nullable == l_true ? "T" : (nullable == l_false ? "F" : "U")) << ", "
<< "classical=" << (classical ? "T" : "F") << ", "
<< "standard=" << (standard ? "T" : "F") << ", "
<< "nonbranching=" << (nonbranching ? "T" : "F") << ", "
<< "normalized=" << (normalized ? "T" : "F") << ", "
<< "monadic=" << (monadic ? "T" : "F") << ", "
<< "singleton=" << (singleton ? "T" : "F") << ", "
<< "min_length=" << min_length << ", "
<< "star_height=" << star_height << ")";
<< "min_length=" << min_length << ")";
}
else if (is_valid())
out << "UNKNOWN";
@ -1596,13 +1640,13 @@ std::string seq_util::rex::info::str() const {
seq_util::rex::info seq_util::rex::info::star() const {
//if is_known() is false then all mentioned properties will remain false
return seq_util::rex::info(classical, classical, interpreted, nonbranching, normalized, monadic, false, l_true, 0, star_height + 1);
return seq_util::rex::info(interpreted, l_true, 0);
}
seq_util::rex::info seq_util::rex::info::plus() const {
if (is_known()) {
//plus never occurs in a normalized regex
return info(classical, classical, interpreted, nonbranching, false, monadic, false, nullable, min_length, star_height + 1);
return info(interpreted, nullable, min_length);
}
else
return *this;
@ -1611,14 +1655,14 @@ seq_util::rex::info seq_util::rex::info::plus() const {
seq_util::rex::info seq_util::rex::info::opt() const {
// if is_known() is false then all mentioned properties will remain false
// optional construct never occurs in a normalized regex
return seq_util::rex::info(classical, classical, interpreted, nonbranching, false, monadic, false, l_true, 0, star_height);
return seq_util::rex::info(interpreted, l_true, 0);
}
seq_util::rex::info seq_util::rex::info::complement() const {
if (is_known()) {
lbool compl_nullable = (nullable == l_true ? l_false : (nullable == l_false ? l_true : l_undef));
unsigned compl_min_length = (compl_nullable == l_false ? 1 : 0);
return info(false, standard, interpreted, nonbranching, normalized, monadic, false, compl_nullable, compl_min_length, star_height);
return info(interpreted, compl_nullable, compl_min_length);
}
else
return *this;
@ -1630,16 +1674,9 @@ seq_util::rex::info seq_util::rex::info::concat(seq_util::rex::info const& rhs,
unsigned m = min_length + rhs.min_length;
if (m < min_length || m < rhs.min_length)
m = UINT_MAX;
return info(classical & rhs.classical,
classical && rhs.classical, //both args of concat must be classical for it to be standard
interpreted && rhs.interpreted,
nonbranching && rhs.nonbranching,
(normalized && !lhs_is_concat && rhs.normalized),
monadic && rhs.monadic,
false,
return info(interpreted && rhs.interpreted,
((nullable == l_false || rhs.nullable == l_false) ? l_false : ((nullable == l_true && rhs.nullable == l_true) ? l_true : l_undef)),
m,
std::max(star_height, rhs.star_height));
m);
}
else
return rhs;
@ -1651,16 +1688,9 @@ seq_util::rex::info seq_util::rex::info::concat(seq_util::rex::info const& rhs,
seq_util::rex::info seq_util::rex::info::disj(seq_util::rex::info const& rhs) const {
if (is_known() || rhs.is_known()) {
//works correctly if one of the arguments is unknown
return info(classical & rhs.classical,
standard && rhs.standard,
interpreted && rhs.interpreted,
nonbranching && rhs.nonbranching,
normalized && rhs.normalized,
monadic && rhs.monadic,
singleton && rhs.singleton,
return info(interpreted && rhs.interpreted,
((nullable == l_true || rhs.nullable == l_true) ? l_true : ((nullable == l_false && rhs.nullable == l_false) ? l_false : l_undef)),
std::min(min_length, rhs.min_length),
std::max(star_height, rhs.star_height));
std::min(min_length, rhs.min_length));
}
else
return rhs;
@ -1669,16 +1699,9 @@ seq_util::rex::info seq_util::rex::info::disj(seq_util::rex::info const& rhs) co
seq_util::rex::info seq_util::rex::info::conj(seq_util::rex::info const& rhs) const {
if (is_known()) {
if (rhs.is_known()) {
return info(false,
standard && rhs.standard,
interpreted && rhs.interpreted,
nonbranching && rhs.nonbranching,
normalized && rhs.normalized,
monadic && rhs.monadic,
singleton && rhs.singleton,
return info(interpreted && rhs.interpreted,
((nullable == l_true && rhs.nullable == l_true) ? l_true : ((nullable == l_false || rhs.nullable == l_false) ? l_false : l_undef)),
std::max(min_length, rhs.min_length),
std::max(star_height, rhs.star_height));
std::max(min_length, rhs.min_length));
}
else
return rhs;
@ -1690,16 +1713,9 @@ seq_util::rex::info seq_util::rex::info::conj(seq_util::rex::info const& rhs) co
seq_util::rex::info seq_util::rex::info::diff(seq_util::rex::info const& rhs) const {
if (is_known()) {
if (rhs.is_known()) {
return info(false,
standard & rhs.standard,
interpreted & rhs.interpreted,
nonbranching & rhs.nonbranching,
normalized & rhs.normalized,
monadic & rhs.monadic,
false,
return info(interpreted & rhs.interpreted,
((nullable == l_true && rhs.nullable == l_false) ? l_true : ((nullable == l_false || rhs.nullable == l_false) ? l_false : l_undef)),
std::max(min_length, rhs.min_length),
std::max(star_height, rhs.star_height));
std::max(min_length, rhs.min_length));
}
else
return rhs;
@ -1714,13 +1730,9 @@ seq_util::rex::info seq_util::rex::info::orelse(seq_util::rex::info const& i) co
// unsigned ite_min_length = std::min(min_length, i.min_length);
// lbool ite_nullable = (nullable == i.nullable ? nullable : l_undef);
// TBD: whether ite is interpreted or not depends on whether the condition is interpreted and both branches are interpreted
return info(false, false, false, false,
normalized && i.normalized,
monadic && i.monadic,
singleton && i.singleton,
return info(false,
((nullable == l_true && i.nullable == l_true) ? l_true : ((nullable == l_false && i.nullable == l_false) ? l_false : l_undef)),
std::min(min_length, i.min_length),
std::max(star_height, i.star_height));
std::min(min_length, i.min_length));
}
else
return i;
@ -1736,24 +1748,22 @@ seq_util::rex::info seq_util::rex::info::loop(unsigned lower, unsigned upper) co
if (m > 0 && (m < min_length || m < lower))
m = UINT_MAX;
lbool loop_nullable = (nullable == l_true || lower == 0 ? l_true : nullable);
if (upper == UINT_MAX) {
// this means the loop is r{lower,*} and is therefore not normalized
// normalized regex would be r{lower,lower}r* and would in particular not use r{0,} for r*
return info(classical, classical, interpreted, nonbranching, false, singleton, false, loop_nullable, m, star_height + 1);
}
else {
bool loop_normalized = normalized;
// r{lower,upper} is not normalized if r is nullable but lower > 0
// r{0,1} is not normalized: it should be ()|r
// r{1,1} is not normalized: it should be r
// r{lower,upper} is not normalized if lower > upper it should then be [] (empty)
if ((nullable == l_true && lower > 0) || upper == 1 || lower > upper)
loop_normalized = false;
return info(classical, classical, interpreted, nonbranching, loop_normalized, singleton, false, loop_nullable, m, star_height);
}
return info(interpreted, loop_nullable, m);
}
else
return *this;
}
seq_util::rex::info& seq_util::rex::info::operator=(info const& other) {
if (this == &other) {
return *this;
}
known = other.known;
interpreted = other.interpreted;
nullable = other.nullable;
min_length = other.min_length;
return *this;
}

View file

@ -103,7 +103,7 @@ enum seq_op_kind {
_OP_REGEXP_EMPTY,
_OP_REGEXP_FULL_CHAR,
_OP_RE_IS_NULLABLE,
_OP_RE_ANTIMOROV_UNION, // Lifted union for antimorov-style derivatives
_OP_RE_ANTIMIROV_UNION, // Lifted union for antimirov-style derivatives
_OP_SEQ_SKOLEM,
LAST_SEQ_OP
};
@ -252,6 +252,12 @@ public:
unsigned max_char() const { return seq.max_char(); }
unsigned num_bits() const { return seq.num_bits(); }
/*
e has a form that is equivalent to l <= x <= u (then negated = false)
or e is equivalent to !(l <= x <= u) (then negated = true)
*/
bool is_char_const_range(expr const* x, expr * e, unsigned& l, unsigned& u, bool& negated) const;
app* mk_skolem(symbol const& name, unsigned n, expr* const* args, sort* range);
bool is_skolem(expr const* e) const { return is_app_of(e, m_fid, _OP_SEQ_SKOLEM); }
@ -411,26 +417,11 @@ public:
struct info {
/* Value is either undefined (known=l_undef) or defined and known (l_true) or defined but unknown (l_false)*/
lbool known { l_undef };
/* No complement, no intersection, no difference, and no if-then-else is used. Reverse is allowed. */
bool classical { false };
/* Boolean-reverse combination of classical regexes (using reverse, union, complement, intersection or difference). */
bool standard { false };
/* There are no uninterpreted symbols. */
bool interpreted { false };
/* No if-then-else is used. */
bool nonbranching { false };
/* Concatenations are right associative and if a loop body is nullable then the lower bound is zero. */
bool normalized { false };
/* All bounded loops have a body that is a singleton. */
bool monadic { false };
/* Positive Boolean combination of ranges or predicates or singleton sequences. */
bool singleton { false };
/* If l_true then empty word is accepted, if l_false then empty word is not accepted. */
lbool nullable { l_undef };
/* Lower bound on the length of all accepted words. */
unsigned min_length { 0 };
/* Maximum nesting depth of Kleene stars. */
unsigned star_height { 0 };
/*
Default constructor of invalid info.
@ -445,19 +436,13 @@ public:
/*
General info constructor.
*/
info(bool is_classical,
bool is_standard,
bool is_interpreted,
bool is_nonbranching,
bool is_normalized,
bool is_monadic,
bool is_singleton,
info(bool is_interpreted,
lbool is_nullable,
unsigned min_l,
unsigned star_h) :
known(l_true), classical(is_classical), standard(is_standard), interpreted(is_interpreted), nonbranching(is_nonbranching),
normalized(is_normalized), monadic(is_monadic), singleton(is_singleton), nullable(is_nullable),
min_length(min_l), star_height(star_h) {}
unsigned min_l) :
known(l_true),
interpreted(is_interpreted),
nullable(is_nullable),
min_length(min_l) {}
/*
Appends a string representation of the info into the stream.
@ -483,6 +468,8 @@ public:
info diff(info const& rhs) const;
info orelse(info const& rhs) const;
info loop(unsigned lower, unsigned upper) const;
info& operator=(info const& other);
};
private:
seq_util& u;
@ -517,6 +504,7 @@ public:
app* mk_opt(expr* r) { return m.mk_app(m_fid, OP_RE_OPTION, r); }
app* mk_loop(expr* r, unsigned lo);
app* mk_loop(expr* r, unsigned lo, unsigned hi);
expr* mk_loop_proper(expr* r, unsigned lo, unsigned hi);
app* mk_loop(expr* r, expr* lo);
app* mk_loop(expr* r, expr* lo, expr* hi);
app* mk_full_char(sort* s);
@ -525,7 +513,7 @@ public:
app* mk_of_pred(expr* p);
app* mk_reverse(expr* r) { return m.mk_app(m_fid, OP_RE_REVERSE, r); }
app* mk_derivative(expr* ele, expr* r) { return m.mk_app(m_fid, OP_RE_DERIVATIVE, ele, r); }
app* mk_antimorov_union(expr* r1, expr* r2) { return m.mk_app(m_fid, _OP_RE_ANTIMOROV_UNION, r1, r2); }
app* mk_antimirov_union(expr* r1, expr* r2) { return m.mk_app(m_fid, _OP_RE_ANTIMIROV_UNION, r1, r2); }
bool is_to_re(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_TO_RE); }
bool is_concat(expr const* n) const { return is_app_of(n, m_fid, OP_RE_CONCAT); }
@ -557,7 +545,7 @@ public:
bool is_of_pred(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OF_PRED); }
bool is_reverse(expr const* n) const { return is_app_of(n, m_fid, OP_RE_REVERSE); }
bool is_derivative(expr const* n) const { return is_app_of(n, m_fid, OP_RE_DERIVATIVE); }
bool is_antimorov_union(expr const* n) const { return is_app_of(n, m_fid, _OP_RE_ANTIMOROV_UNION); }
bool is_antimirov_union(expr const* n) const { return is_app_of(n, m_fid, _OP_RE_ANTIMIROV_UNION); }
MATCH_UNARY(is_to_re);
MATCH_BINARY(is_concat);
MATCH_BINARY(is_union);
@ -571,7 +559,7 @@ public:
MATCH_UNARY(is_of_pred);
MATCH_UNARY(is_reverse);
MATCH_BINARY(is_derivative);
MATCH_BINARY(is_antimorov_union);
MATCH_BINARY(is_antimirov_union);
bool is_loop(expr const* n, expr*& body, unsigned& lo, unsigned& hi) const;
bool is_loop(expr const* n, expr*& body, unsigned& lo) const;
bool is_loop(expr const* n, expr*& body, expr*& lo, expr*& hi) const;

View file

@ -18,6 +18,7 @@ Revision History:
--*/
#include "ast/static_features.h"
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/for_each_expr.h"
static_features::static_features(ast_manager & m):
@ -39,7 +40,8 @@ static_features::static_features(ast_manager & m):
}
void static_features::reset() {
m_already_visited .reset();
m_pre_processed .reset();
m_post_processed.reset();
m_cnf = true;
m_num_exprs = 0;
m_num_roots = 0;
@ -343,8 +345,8 @@ void static_features::update_core(expr * e) {
}
func_decl * d = to_app(e)->get_decl();
inc_num_apps(d);
if (d->get_arity() > 0 && !is_marked(d)) {
mark(d);
if (d->get_arity() > 0 && !is_marked_pre(d)) {
mark_pre(d);
if (fid == null_family_id)
m_num_uninterpreted_functions++;
}
@ -396,73 +398,68 @@ void static_features::update_core(sort * s) {
check_array(s);
}
void static_features::process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth) {
TRACE("static_features", tout << "processing\n" << mk_pp(e, m) << "\n";);
if (is_var(e))
return;
if (is_marked(e)) {
m_num_sharing++;
return;
}
bool static_features::pre_process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx) {
if (is_marked_post(e))
return true;
if (stack_depth > m_max_stack_depth) {
ptr_vector<expr> todo;
todo.push_back(e);
for (unsigned i = 0; i < todo.size(); ++i) {
e = todo[i];
if (is_marked(e))
continue;
if (is_basic_expr(e)) {
mark(e);
todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
}
else {
process(e, form_ctx, or_and_ctx, ite_ctx, stack_depth - 10);
}
}
return;
if (is_marked_pre(e))
return true;
if (is_var(e)) {
mark_pre(e);
mark_post(e);
return true;
}
mark(e);
mark_pre(e);
update_core(e);
if (is_quantifier(e)) {
expr * body = to_quantifier(e)->get_expr();
process(body, false, false, false, stack_depth+1);
if (is_marked_post(body))
return true;
add_process(body, false, false, false);
return false;
}
auto [form_ctx_new, or_and_ctx_new, ite_ctx_new] = new_ctx(e);
bool all_processed = true;
for (expr* arg : *to_app(e)) {
m.is_not(arg, arg);
if (is_marked_post(arg))
++m_num_sharing;
else {
add_process(arg, form_ctx_new, or_and_ctx_new, ite_ctx_new);
all_processed = false;
}
}
return all_processed;
}
void static_features::post_process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx) {
if (is_marked_post(e))
return;
mark_post(e);
if (is_quantifier(e)) {
expr * body = to_quantifier(e)->get_expr();
set_depth(e, get_depth(body)+1);
return;
}
bool form_ctx_new = false;
bool or_and_ctx_new = false;
bool ite_ctx_new = false;
if (is_basic_expr(e)) {
switch (to_app(e)->get_decl_kind()) {
case OP_ITE:
form_ctx_new = m.is_bool(e);
ite_ctx_new = true;
break;
case OP_AND:
case OP_OR:
form_ctx_new = true;
or_and_ctx_new = true;
break;
case OP_EQ:
form_ctx_new = true;
break;
}
}
unsigned depth = 0;
unsigned form_depth = 0;
unsigned or_and_depth = 0;
unsigned ite_depth = 0;
auto [form_ctx_new, or_and_ctx_new, ite_ctx_new] = new_ctx(e);
for (expr* arg : *to_app(e)) {
m.is_not(arg, arg);
process(arg, form_ctx_new, or_and_ctx_new, ite_ctx_new, stack_depth+1);
SASSERT(is_marked_post(arg));
depth = std::max(depth, get_depth(arg));
if (form_ctx_new)
form_depth = std::max(form_depth, get_form_depth(arg));
@ -507,16 +504,58 @@ void static_features::process(expr * e, bool form_ctx, bool or_and_ctx, bool ite
}
set_ite_depth(e, ite_depth);
}
}
std::tuple<bool, bool, bool> static_features::new_ctx(expr* e) {
bool form_ctx_new = false;
bool or_and_ctx_new = false;
bool ite_ctx_new = false;
if (is_basic_expr(e)) {
switch (to_app(e)->get_decl_kind()) {
case OP_ITE:
form_ctx_new = m.is_bool(e);
ite_ctx_new = true;
break;
case OP_AND:
case OP_OR:
form_ctx_new = true;
or_and_ctx_new = true;
break;
case OP_EQ:
form_ctx_new = true;
break;
}
}
return std::tuple(form_ctx_new, or_and_ctx_new, ite_ctx_new);
}
void static_features::process_all() {
while (!m_to_process.empty()) {
auto const& [e, form, or_and, ite] = m_to_process.back();
if (is_marked_post(e)) {
m_to_process.pop_back();
++m_num_sharing;
continue;
}
if (!pre_process(e, form, or_and, ite))
continue;
post_process(e, form, or_and, ite);
m_to_process.pop_back();
}
}
void static_features::process_root(expr * e) {
if (is_marked(e)) {
if (is_marked_post(e)) {
m_num_sharing++;
return;
}
m_num_roots++;
if (m.is_or(e)) {
mark(e);
mark_post(e);
m_num_clauses++;
m_num_bool_exprs++;
unsigned num_args = to_app(e)->get_num_args();
@ -530,7 +569,8 @@ void static_features::process_root(expr * e) {
expr * arg = to_app(e)->get_arg(i);
if (m.is_not(arg))
arg = to_app(arg)->get_arg(0);
process(arg, true, true, false, 0);
add_process(arg, true, true, false);
process_all();
depth = std::max(depth, get_depth(arg));
form_depth = std::max(form_depth, get_form_depth(arg));
or_and_depth = std::max(or_and_depth, get_or_and_depth(arg));
@ -558,7 +598,8 @@ void static_features::process_root(expr * e) {
m_num_units++;
m_num_clauses++;
}
process(e, false, false, false, 0);
add_process(e, false, false, false);
process_all();
}
void static_features::collect(unsigned num_formulas, expr * const * formulas) {

View file

@ -28,6 +28,12 @@ Revision History:
#include "util/map.h"
struct static_features {
struct to_process {
expr* e;
bool form_ctx;
bool and_or_ctx;
bool ite_ctx;
};
ast_manager & m;
arith_util m_autil;
bv_util m_bvutil;
@ -39,7 +45,7 @@ struct static_features {
family_id m_lfid;
family_id m_arrfid;
family_id m_srfid;
ast_mark m_already_visited;
ast_mark m_pre_processed, m_post_processed;
bool m_cnf;
unsigned m_num_exprs; //
unsigned m_num_roots; //
@ -111,14 +117,17 @@ struct static_features {
u_map<unsigned> m_expr2formula_depth;
unsigned m_num_theories;
bool_vector m_theories; // mapping family_id -> bool
bool_vector m_theories; // mapping family_id -> bool
symbol m_label_sym;
symbol m_pattern_sym;
symbol m_expr_list_sym;
svector<to_process> m_to_process;
bool is_marked(ast * e) const { return m_already_visited.is_marked(e); }
void mark(ast * e) { m_already_visited.mark(e, true); }
bool is_marked_pre(ast * e) const { return m_pre_processed.is_marked(e); }
void mark_pre(ast * e) { m_pre_processed.mark(e, true); }
bool is_marked_post(ast * e) const { return m_post_processed.is_marked(e); }
void mark_post(ast * e) { m_post_processed.mark(e, true); }
bool is_bool(expr const * e) const { return m.is_bool(e); }
bool is_basic_expr(expr const * e) const { return is_app(e) && to_app(e)->get_family_id() == m_bfid; }
bool is_arith_expr(expr const * e) const { return is_app(e) && to_app(e)->get_family_id() == m_afid; }
@ -161,7 +170,12 @@ struct static_features {
void inc_num_aliens(family_id fid) { m_num_aliens_per_family.reserve(fid+1, 0); m_num_aliens_per_family[fid]++; }
void update_core(expr * e);
void update_core(sort * s);
void process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth);
// void process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth);
bool pre_process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx);
void post_process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx);
void add_process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx) { m_to_process.push_back({e, form_ctx, or_and_ctx, ite_ctx}); }
void process_all();
std::tuple<bool, bool, bool> new_ctx(expr* );
void process_root(expr * e);
unsigned get_depth(expr const * e) const { return m_expr2depth.get(e->get_id(), 1); }
void set_depth(expr const * e, unsigned d) { m_expr2depth.setx(e->get_id(), d, 1); }

View file

@ -301,10 +301,23 @@ UNARY_CMD(pp_cmd, "display", "<term>", "display the given term.", CPK_EXPR, expr
ctx.regular_stream() << std::endl;
});
UNARY_CMD(echo_cmd, "echo", "<string>", "display the given string", CPK_STRING, char const *,
bool smt2c = ctx.params().m_smtlib2_compliant;
ctx.regular_stream() << (smt2c ? "\"" : "") << arg << (smt2c ? "\"" : "") << std::endl;);
static std::string escape_string(char const* arg) {
std::string result;
while (*arg) {
auto ch = *arg++;
if (ch == '"')
result.push_back(ch);
result.push_back(ch);
}
return result;
}
UNARY_CMD(echo_cmd, "echo", "<string>", "display the given string", CPK_STRING, char const *,
bool smt2c = ctx.params().m_smtlib2_compliant;
if (smt2c)
ctx.regular_stream() << "\"" << escape_string(arg) << "\"" << std::endl;
else
ctx.regular_stream() << arg << std::endl;);
class set_get_option_cmd : public cmd {
protected:

View file

@ -2107,6 +2107,21 @@ void cmd_context::analyze_failure(expr_mark& seen, model_evaluator& ev, expr* a,
<< (expected_value?"true":"false") << "\n";);
IF_VERBOSE(11, display_detailed_analysis(verbose_stream(), ev, a));
if (m().is_iff(a)) {
ptr_vector<expr> todo;
todo.push_back(a);
for (unsigned i = 0; i < todo.size(); ++i) {
e = todo[i];
if (m().is_and(e) || m().is_or(e) || m().is_iff(e) || m().is_implies(e) || m().is_not(e))
for (expr* arg : *to_app(e))
todo.push_back(arg);
else
IF_VERBOSE(10, verbose_stream() << "#" << e->get_id() << " " << mk_bounded_pp(e, m()) << " " << (ev.is_true(e)?"true":"false") << "\n");
}
return;
}
}
void cmd_context::display_detailed_analysis(std::ostream& out, model_evaluator& ev, expr* e) {

View file

@ -199,12 +199,12 @@ void tst_params(cmd_context & ctx) {
params_ref p1;
params_ref p2;
p1.set_uint("val", 100);
p2 = p1;
p2.append(p1);
SASSERT(p2.get_uint("val", 0) == 100);
p2.set_uint("val", 200);
SASSERT(p2.get_uint("val", 0) == 200);
SASSERT(p1.get_uint("val", 0) == 100);
p2 = p1;
p2.append(p1);
SASSERT(p2.get_uint("val", 0) == 100);
SASSERT(p1.get_uint("val", 0) == 100);
ctx.regular_stream() << "worked" << std::endl;

View file

@ -416,9 +416,9 @@ void psort_builtin_decl::display(std::ostream & out) const {
void ptype::display(std::ostream & out, pdatatype_decl const * const * dts) const {
switch (kind()) {
case PTR_PSORT: get_psort()->display(out); break;
case PTR_REC_REF: out << dts[get_idx()]->get_name(); break;
case PTR_MISSING_REF: out << get_missing_ref(); break;
case ptype_kind::PTR_PSORT: get_psort()->display(out); break;
case ptype_kind::PTR_REC_REF: out << dts[get_idx()]->get_name(); break;
case ptype_kind::PTR_MISSING_REF: out << get_missing_ref(); break;
}
}
@ -426,19 +426,19 @@ paccessor_decl::paccessor_decl(unsigned id, unsigned num_params, pdecl_manager &
pdecl(id, num_params),
m_name(n),
m_type(r) {
if (m_type.kind() == PTR_PSORT) {
if (m_type.kind() == ptype_kind::PTR_PSORT) {
m.inc_ref(r.get_psort());
}
}
void paccessor_decl::finalize(pdecl_manager & m) {
if (m_type.kind() == PTR_PSORT) {
if (m_type.kind() == ptype_kind::PTR_PSORT) {
m.lazy_dec_ref(m_type.get_psort());
}
}
bool paccessor_decl::has_missing_refs(symbol & missing) const {
if (m_type.kind() == PTR_MISSING_REF) {
if (m_type.kind() == ptype_kind::PTR_MISSING_REF) {
missing = m_type.get_missing_ref();
return true;
}
@ -446,14 +446,14 @@ bool paccessor_decl::has_missing_refs(symbol & missing) const {
}
bool paccessor_decl::fix_missing_refs(dictionary<int> const & symbol2idx, symbol & missing) {
TRACE("fix_missing_refs", tout << "m_type.kind(): " << m_type.kind() << "\n";
if (m_type.kind() == PTR_MISSING_REF) tout << m_type.get_missing_ref() << "\n";);
if (m_type.kind() != PTR_MISSING_REF)
TRACE("fix_missing_refs", tout << "m_type.kind(): " << (int)m_type.kind() << "\n";
if (m_type.kind() == ptype_kind::PTR_MISSING_REF) tout << m_type.get_missing_ref() << "\n";);
if (m_type.kind() != ptype_kind::PTR_MISSING_REF)
return true;
int idx;
if (symbol2idx.find(m_type.get_missing_ref(), idx)) {
m_type = ptype(idx);
SASSERT(m_type.kind() == PTR_REC_REF);
SASSERT(m_type.kind() == ptype_kind::PTR_REC_REF);
return true;
}
missing = m_type.get_missing_ref();
@ -462,8 +462,8 @@ bool paccessor_decl::fix_missing_refs(dictionary<int> const & symbol2idx, symbol
accessor_decl * paccessor_decl::instantiate_decl(pdecl_manager & m, unsigned n, sort * const * s) {
switch (m_type.kind()) {
case PTR_REC_REF: return mk_accessor_decl(m.m(), m_name, type_ref(m_type.get_idx()));
case PTR_PSORT: return mk_accessor_decl(m.m(), m_name, type_ref(m_type.get_psort()->instantiate(m, n, s)));
case ptype_kind::PTR_REC_REF: return mk_accessor_decl(m.m(), m_name, type_ref(m_type.get_idx()));
case ptype_kind::PTR_PSORT: return mk_accessor_decl(m.m(), m_name, type_ref(m_type.get_psort()->instantiate(m, n, s)));
default:
// missing refs must have been eliminated.
UNREACHABLE();

View file

@ -157,7 +157,7 @@ class pdatatype_decl;
class pconstructor_decl;
class paccessor_decl;
enum ptype_kind {
enum class ptype_kind {
PTR_PSORT, // psort
PTR_REC_REF, // recursive reference
PTR_MISSING_REF // a symbol, it is useful for building parsers.
@ -171,14 +171,14 @@ class ptype {
};
symbol m_missing_ref;
public:
ptype():m_kind(PTR_PSORT), m_sort(nullptr) {}
ptype(int idx):m_kind(PTR_REC_REF), m_idx(idx) {}
ptype(psort * s):m_kind(PTR_PSORT), m_sort(s) {}
ptype(symbol const & s):m_kind(PTR_MISSING_REF), m_missing_ref(s) {}
ptype():m_kind(ptype_kind::PTR_PSORT), m_sort(nullptr) {}
ptype(int idx):m_kind(ptype_kind::PTR_REC_REF), m_idx(idx) {}
ptype(psort * s):m_kind(ptype_kind::PTR_PSORT), m_sort(s) {}
ptype(symbol const & s):m_kind(ptype_kind::PTR_MISSING_REF), m_sort(nullptr), m_missing_ref(s) {}
ptype_kind kind() const { return m_kind; }
psort * get_psort() const { SASSERT(kind() == PTR_PSORT); return m_sort; }
int get_idx() const { SASSERT(kind() == PTR_REC_REF); return m_idx; }
symbol const & get_missing_ref() const { SASSERT(kind() == PTR_MISSING_REF); return m_missing_ref; }
psort * get_psort() const { SASSERT(kind() == ptype_kind::PTR_PSORT); return m_sort; }
int get_idx() const { SASSERT(kind() == ptype_kind::PTR_REC_REF); return m_idx; }
symbol const & get_missing_ref() const { SASSERT(kind() == ptype_kind::PTR_MISSING_REF); return m_missing_ref; }
void display(std::ostream & out, pdatatype_decl const * const * dts) const;
};

View file

@ -730,9 +730,9 @@ namespace algebraic_numbers {
}
else {
algebraic_cell * c = a.to_algebraic();
if (!upm().normalize_interval_core(c->m_p_sz, c->m_p, sign_lower(c), bqm(), lower(c), upper(c)))
if (!upm().normalize_interval_core(c->m_p_sz, c->m_p, sign_lower(c), bqm(), lower(c), upper(c)))
reset(a);
SASSERT(acell_inv(*c));
SASSERT(is_zero(a) || acell_inv(*a.to_algebraic()));
}
}

View file

@ -227,8 +227,8 @@ public:
}
void updt_params(params_ref const & p) override {
m_params = p;
m_imp->updt_params(p);
m_params.append(p);
m_imp->updt_params(m_params);
}
void collect_param_descrs(param_descrs & r) override {

View file

@ -111,6 +111,7 @@ void model_core::unregister_decl(func_decl * d) {
m_interp[m_const_decls.back()].first = v.first;
m_const_decls.pop_back();
m_interp.remove(d);
m_decls.erase(d);
m.dec_ref(k);
m.dec_ref(v.second);
return;
@ -122,6 +123,7 @@ void model_core::unregister_decl(func_decl * d) {
auto v = ef->get_data().m_value;
m_finterp.remove(d);
m_func_decls.erase(d);
m_decls.erase(d);
m.dec_ref(k);
dealloc(v);
}

View file

@ -178,7 +178,7 @@ namespace datalog {
m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false);
}
for (unsigned i = 0; i < fmls.size(); ++i) {
mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name);
mk_horn_rule(fmls.get(i), prs.get(i), rules, name);
}
}
@ -190,12 +190,10 @@ namespace datalog {
hoist_compound_predicates(index, m_head, m_body);
TRACE("dl_rule",
tout << mk_pp(m_head, m) << " :- ";
for (unsigned i = 0; i < m_body.size(); ++i) {
tout << mk_pp(m_body[i].get(), m) << " ";
}
for (expr* b : m_body)
tout << mk_pp(b, m) << " ";
tout << "\n";);
mk_negations(m_body, m_neg);
check_valid_rule(m_head, m_body.size(), m_body.data());
@ -241,9 +239,8 @@ namespace datalog {
m_args.reset();
head = ensure_app(e2);
flatten_and(e1, m_args);
for (unsigned i = 0; i < m_args.size(); ++i) {
body.push_back(ensure_app(m_args[i].get()));
}
for (expr* a : m_args)
body.push_back(ensure_app(a));
}
else {
head = ensure_app(fml);
@ -255,7 +252,7 @@ namespace datalog {
unsigned sz = body.size();
hoist_compound(index, head, body);
for (unsigned i = 0; i < sz; ++i) {
app_ref b(body[i].get(), m);
app_ref b(body.get(i), m);
hoist_compound(index, b, body);
body[i] = b;
}
@ -781,7 +778,7 @@ namespace datalog {
tail_neg.push_back(false);
}
SASSERT(tail.size()==tail_neg.size());
SASSERT(tail.size() == tail_neg.size());
rule_ref old_r = r;
r = mk(head, tail.size(), tail.data(), tail_neg.data(), old_r->name());
r->set_accounting_parent_object(m_ctx, old_r);
@ -949,7 +946,8 @@ namespace datalog {
}
bool rule::has_negation() const {
for (unsigned i = 0; i < get_uninterpreted_tail_size(); ++i) {
unsigned sz = get_uninterpreted_tail_size();
for (unsigned i = 0; i < sz; ++i) {
if (is_neg_tail(i)) {
return true;
}

View file

@ -28,7 +28,7 @@ Revision History:
#include "ast/rewriter/ast_counter.h"
#include "ast/rewriter/rewriter.h"
#include "muz/base/hnf.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
#include "ast/rewriter/var_subst.h"
#include "ast/datatype_decl_plugin.h"
#include "ast/rewriter/label_rewriter.h"
@ -287,13 +287,12 @@ namespace datalog {
class rule : public accounted_object {
friend class rule_manager;
app* m_head{ nullptr };
proof* m_proof{ nullptr };
unsigned m_tail_size:20;
// unsigned m_reserve:12;
unsigned m_ref_cnt{ 0 };
unsigned m_positive_cnt{ 0 };
unsigned m_uninterp_cnt{ 0 };
app* m_head = nullptr;
proof* m_proof = nullptr;
unsigned m_tail_size = 0;
unsigned m_ref_cnt = 0;
unsigned m_positive_cnt = 0;
unsigned m_uninterp_cnt = 0;
symbol m_name;
/**
The following field is an array of tagged pointers.

View file

@ -204,6 +204,72 @@ void rule_properties::operator()(var* n) {
void rule_properties::operator()(quantifier* n) {
m_quantifiers.insert(n, m_rule);
}
bool rule_properties::check_accessor(app* n) {
sort* s = n->get_arg(0)->get_sort();
SASSERT(m_dt.is_datatype(s));
if (m_dt.get_datatype_constructors(s)->size() <= 1)
return true;
func_decl* f = n->get_decl();
func_decl * c = m_dt.get_accessor_constructor(f);
unsigned ut_size = m_rule->get_uninterpreted_tail_size();
unsigned t_size = m_rule->get_tail_size();
auto is_recognizer_base = [&](expr* t) {
return m_dt.is_recognizer(t) &&
to_app(t)->get_arg(0) == n->get_arg(0) &&
m_dt.get_recognizer_constructor(to_app(t)->get_decl()) == c;
};
auto is_recognizer = [&](expr* t) {
if (m.is_and(t))
for (expr* arg : *to_app(t))
if (is_recognizer_base(arg))
return true;
return is_recognizer_base(t);
};
for (unsigned i = ut_size; i < t_size; ++i)
if (is_recognizer(m_rule->get_tail(i)))
return true;
// create parent use list for every sub-expression in the rule
obj_map<expr, ptr_vector<expr>> use_list;
for (unsigned i = ut_size; i < t_size; ++i) {
app* t = m_rule->get_tail(i);
use_list.insert_if_not_there(t, ptr_vector<expr>()).push_back(nullptr); // add marker for top-level expression.
for (expr* sub : subterms::all(expr_ref(t, m)))
if (is_app(sub))
for (expr* arg : *to_app(sub))
use_list.insert_if_not_there(arg, ptr_vector<expr>()).push_back(sub);
}
// walk parents of n to check that each path is guarded by a recognizer.
ptr_vector<expr> todo;
todo.push_back(n);
for (unsigned i = 0; i < todo.size(); ++i) {
expr* e = todo[i];
if (!use_list.contains(e))
return false;
for (expr* parent : use_list[e]) {
if (!parent)
return false; // top-level expressions are not guarded
if (is_recognizer(parent))
continue;
if (m.is_ite(parent) && to_app(parent)->get_arg(1) == e && is_recognizer(to_app(parent)->get_arg(0)))
continue;
todo.push_back(parent);
}
}
return true;
}
void rule_properties::operator()(app* n) {
func_decl_ref f_out(m);
expr* n1 = nullptr, *n2 = nullptr;
@ -216,23 +282,9 @@ void rule_properties::operator()(app* n) {
m_uninterp_funs.insert(f, m_rule);
}
else if (m_dt.is_accessor(n)) {
sort* s = n->get_arg(0)->get_sort();
SASSERT(m_dt.is_datatype(s));
if (m_dt.get_datatype_constructors(s)->size() > 1) {
bool found = false;
func_decl * c = m_dt.get_accessor_constructor(f);
unsigned ut_size = m_rule->get_uninterpreted_tail_size();
unsigned t_size = m_rule->get_tail_size();
for (unsigned i = ut_size; !found && i < t_size; ++i) {
app* t = m_rule->get_tail(i);
if (m_dt.is_recognizer(t) && t->get_arg(0) == n->get_arg(0) && m_dt.get_recognizer_constructor(t->get_decl()) == c) {
found = true;
}
}
if (!found) {
m_uninterp_funs.insert(f, m_rule);
}
}
if (!check_accessor(n))
m_uninterp_funs.insert(f, m_rule);
}
else if (m_a.is_considered_uninterpreted(f, n->get_num_args(), n->get_args(), f_out)) {
m_uninterp_funs.insert(f, m_rule);

View file

@ -44,7 +44,7 @@ namespace datalog {
bool m_generate_proof;
rule* m_rule;
obj_map<quantifier, rule*> m_quantifiers;
obj_map<func_decl, rule*> m_uninterp_funs;
obj_map<func_decl, rule*> m_uninterp_funs;
ptr_vector<rule> m_interp_pred;
ptr_vector<rule> m_negative_rules;
ptr_vector<rule> m_inf_sort;
@ -55,6 +55,7 @@ namespace datalog {
void check_sort(sort* s);
void visit_rules(expr_sparse_mark& visited, rule_set const& rules);
bool evaluates_to_numeral(expr * n, rational& val);
bool check_accessor(app* n);
public:
rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& is_predicate);
~rule_properties();

View file

@ -395,8 +395,8 @@ public:
char const* name() const override { return "horn"; }
void updt_params(params_ref const & p) override {
m_params = p;
m_imp->updt_params(p);
m_params.append(p);
m_imp->updt_params(m_params);
}

View file

@ -35,7 +35,7 @@ Notes:
#include "ast/rewriter/expr_replacer.h"
#include "model/model_smt2_pp.h"
#include "ast/scoped_proof.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
#include "muz/spacer/spacer_qe_project.h"
#include "model/model_pp.h"
#include "ast/rewriter/expr_safe_replace.h"

View file

@ -23,7 +23,7 @@ Copyright (c) 2017 Arie Gurfinkel
#include "ast/rewriter/expr_replacer.h"
#include "model/model_smt2_pp.h"
#include "ast/scoped_proof.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
#include "muz/spacer/spacer_qe_project.h"
#include "model/model_pp.h"
#include "ast/rewriter/expr_safe_replace.h"

View file

@ -36,7 +36,7 @@ Revision History:
#include "model/model_pp.h"
#include "qe/qe.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
#include "muz/spacer/spacer_mev_array.h"
#include "muz/spacer/spacer_qe_project.h"

View file

@ -52,7 +52,7 @@ Notes:
#include "model/model_smt2_pp.h"
#include "model/model_pp.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
#include "qe/qe_mbp.h"
#include "qe/mbp/mbp_term_graph.h"
#include "qe/mbp/mbp_plugin.h"

View file

@ -23,7 +23,7 @@ Revision History:
#include "muz/base/dl_context.h"
#include "muz/transforms/dl_mk_rule_inliner.h"
#include "smt/smt_kernel.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
#include "ast/rewriter/bool_rewriter.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/datatype_decl_plugin.h"

View file

@ -55,8 +55,8 @@ class nlsat_tactic : public tactic {
}
void updt_params(params_ref const & p) {
m_params = p;
m_solver.updt_params(p);
m_params.append(p);
m_solver.updt_params(m_params);
}
bool contains_unsupported(expr_ref_vector & b2a, expr_ref_vector & x2t) {
@ -226,7 +226,7 @@ public:
char const* name() const override { return "nlsat"; }
void updt_params(params_ref const & p) override {
m_params = p;
m_params.append(p);
}
void collect_param_descrs(param_descrs & r) override {

View file

@ -259,6 +259,12 @@ namespace opt {
if (!m_models[i])
m_models.set(i, m_last_model.get());
if (val > m_objective_values[i])
m_objective_values[i] = val;
if (!m_last_model)
return true;
//
// retrieve value of objective from current model and update
// current optimal.
@ -267,7 +273,7 @@ namespace opt {
rational r;
expr_ref value = (*m_last_model)(m_objective_terms.get(i));
if (arith_util(m).is_numeral(value, r) && r > m_objective_values[i])
m_objective_values[i] = inf_eps(r);
m_objective_values[i] = inf_eps(r);
};
update_objective();
@ -342,6 +348,11 @@ namespace opt {
}
void opt_solver::get_model_core(model_ref & m) {
if (m_last_model.get()) {
m = m_last_model.get();
return;
}
for (unsigned i = m_models.size(); i-- > 0; ) {
auto* mdl = m_models[i];
if (mdl) {

View file

@ -49,14 +49,14 @@ namespace opt {
dst[i] = src[i];
m_models.set(i, m_s->get_model_idx(i));
m_s->get_labels(m_labels);
m_lower_fmls[i] = fmls[i].get();
m_lower_fmls[i] = fmls.get(i);
if (dst[i].is_pos() && !dst[i].is_finite()) { // review: likely done already.
m_lower_fmls[i] = m.mk_false();
fmls[i] = m.mk_false();
}
}
else if (src[i] < dst[i] && !m.is_true(m_lower_fmls[i].get())) {
fmls[i] = m_lower_fmls[i].get();
else if (src[i] < dst[i] && !m.is_true(m_lower_fmls.get(i))) {
fmls[i] = m_lower_fmls.get(i);
}
}
}
@ -202,6 +202,9 @@ namespace opt {
for (unsigned i = 0; i < obj_index; ++i)
commit_assignment(i);
// m_s->maximize_objective(obj_index, bound);
// m_s->assert_expr(bound);
unsigned steps = 0;
unsigned step_incs = 0;
rational delta_per_step(1);
@ -282,7 +285,7 @@ namespace opt {
bool optsmt::can_increment_delta(vector<inf_eps> const& lower, unsigned i) {
arith_util arith(m);
inf_eps max_delta;
if (m_lower[i] < m_upper[i] && arith.is_int(m_objs[i].get())) {
if (m_lower[i] < m_upper[i] && arith.is_int(m_objs.get(i))) {
inf_eps delta = m_lower[i] - lower[i];
if (m_lower[i].is_finite() && delta > max_delta) {
return true;
@ -435,7 +438,7 @@ namespace opt {
bool progress = false;
for (unsigned i = 0; i < m_lower.size() && m.inc(); ++i) {
if (m_lower[i] <= mid[i] && mid[i] <= m_upper[i] && m_lower[i] < m_upper[i]) {
th.enable_record_conflict(bounds[i].get());
th.enable_record_conflict(bounds.get(i));
lbool is_sat = m_s->check_sat(1, bounds.data() + i);
switch(is_sat) {
case l_true:
@ -484,10 +487,10 @@ namespace opt {
}
for (unsigned i = 0; i < m_objs.size(); ++i) {
smt::theory_var v = solver.add_objective(m_objs[i].get());
smt::theory_var v = solver.add_objective(m_objs.get(i));
if (v == smt::null_theory_var) {
std::ostringstream out;
out << "Objective function '" << mk_pp(m_objs[i].get(), m) << "' is not supported";
out << "Objective function '" << mk_pp(m_objs.get(i), m) << "' is not supported";
throw default_exception(out.str());
}
m_vars.push_back(v);
@ -547,7 +550,7 @@ namespace opt {
// force lower_bound(i) <= objective_value(i)
void optsmt::commit_assignment(unsigned i) {
inf_eps lo = m_lower[i];
TRACE("opt", tout << "set lower bound of " << mk_pp(m_objs[i].get(), m) << " to: " << lo << "\n";
TRACE("opt", tout << "set lower bound of " << mk_pp(m_objs.get(i), m) << " to: " << lo << "\n";
tout << get_lower(i) << ":" << get_upper(i) << "\n";);
// Only assert bounds for bounded objectives
if (lo.is_finite()) {

View file

@ -190,7 +190,8 @@ void context_params::get_solver_params(params_ref & p, bool & proofs_enabled, bo
proofs_enabled &= p.get_bool("proof", m_proof);
models_enabled &= p.get_bool("model", m_model);
unsat_core_enabled = m_unsat_core || p.get_bool("unsat_core", false);
p = merge_default_params(p);
if (!m_auto_config && !p.contains("auto_config"))
p.set_bool("auto_config", false);
}

View file

@ -2470,6 +2470,10 @@ namespace smt2 {
next();
}
/**
* (declare-fun f (sorts) sort)
* (declare-fun (alphas) (sorts) sort)
*/
void parse_declare_fun() {
SASSERT(curr_is_identifier());
SASSERT(curr_id() == m_declare_fun);

View file

@ -10,7 +10,6 @@ z3_add_component(qe
qe.cpp
qe_datatype_plugin.cpp
qe_dl_plugin.cpp
qe_lite.cpp
qe_mbi.cpp
qe_mbp.cpp
qe_tactic.cpp
@ -21,9 +20,9 @@ z3_add_component(qe
smt
tactic
mbp
qe_lite
TACTIC_HEADERS
nlqsat.h
qe_lite.h
qe_tactic.h
qsat.h
)

View file

@ -0,0 +1,9 @@
z3_add_component(qe_lite
SOURCES
qe_lite.cpp
COMPONENT_DEPENDENCIES
tactic
mbp
TACTIC_HEADERS
qe_lite.h
)

View file

@ -34,7 +34,7 @@ Revision History:
#include "ast/datatype_decl_plugin.h"
#include "tactic/tactical.h"
#include "qe/mbp/mbp_solve_plugin.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
namespace qel {
@ -2466,7 +2466,7 @@ public:
}
void updt_params(params_ref const & p) override {
m_params = p;
m_params.append(p);
// m_imp->updt_params(p);
}

View file

@ -60,6 +60,10 @@ public:
\brief full rewriting based light-weight quantifier elimination round.
*/
void operator()(expr_ref& fml, proof_ref& pr);
void operator()(expr* fml, expr_ref& result, proof_ref& pr) { result = fml; (*this)(result, pr); }
void operator()(expr_ref& fml) { proof_ref pr(fml.m()); (*this)(fml, pr); }
};
tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p = params_ref());

View file

@ -249,8 +249,8 @@ namespace mbp {
bool operator()(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) {
app_ref_vector vs(m);
vs.push_back(v);
project(model, vs, lits, false);
return vs.empty();
vector<def> defs;
return project(model, vs, lits, defs, false) && vs.empty();
}
typedef opt::model_based_opt::var var;
@ -265,12 +265,12 @@ namespace mbp {
return t;
}
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& fmls, bool compute_def) {
bool project(model& model, app_ref_vector& vars, expr_ref_vector& fmls, vector<def>& result, bool compute_def) {
bool has_arith = false;
for (expr* v : vars)
has_arith |= is_arith(v);
if (!has_arith)
return vector<def>();
return true;
model_evaluator eval(model);
TRACE("qe", tout << model;);
eval.set_model_completion(true);
@ -294,7 +294,7 @@ namespace mbp {
}
fmls.shrink(j);
TRACE("qe", tout << "formulas\n" << fmls << "\n";
for (auto [e, id] : tids)
for (auto const& [e, id] : tids)
tout << mk_pp(e, m) << " -> " << id << "\n";);
// fmls holds residue,
@ -312,7 +312,7 @@ namespace mbp {
rational r;
expr_ref val = eval(v);
if (!m.inc())
return vector<def>();
return false;
if (!a.is_numeral(val, r))
throw default_exception("evaluation did not produce a numeral");
TRACE("qe", tout << mk_pp(v, m) << " " << val << "\n";);
@ -326,15 +326,13 @@ namespace mbp {
if (is_arith(e) && !var_mark.is_marked(e))
mark_rec(fmls_mark, e);
}
if (m_check_purified) {
for (expr* fml : fmls)
mark_rec(fmls_mark, fml);
for (auto& kv : tids) {
expr* e = kv.m_key;
if (!var_mark.is_marked(e)) {
mark_rec(fmls_mark, e);
}
if (!var_mark.is_marked(e))
mark_rec(fmls_mark, e);
}
}
@ -364,16 +362,18 @@ namespace mbp {
for (auto const& d : defs) tout << "def: " << d << "\n";
tout << fmls << "\n";);
vector<def> result;
if (compute_def)
optdefs2mbpdef(defs, index2expr, real_vars, result);
if (m_apply_projection)
apply_projection(result, fmls);
if (m_apply_projection && !apply_projection(eval, result, fmls))
return false;
TRACE("qe",
for (auto [v, t] : result)
for (auto const& [v, t] : result)
tout << v << " := " << t << "\n";
for (auto* f : fmls)
tout << mk_pp(f, m) << " := " << eval(f) << "\n";
tout << "fmls:" << fmls << "\n";);
return result;
return true;
}
void optdefs2mbpdef(vector<opt::model_based_opt::def> const& defs, ptr_vector<expr> const& index2expr, unsigned_vector const& real_vars, vector<def>& result) {
@ -548,10 +548,11 @@ namespace mbp {
}
}
void apply_projection(vector<def>& defs, expr_ref_vector& fmls) {
bool apply_projection(model_evaluator& eval, vector<def> const& defs, expr_ref_vector& fmls) {
if (fmls.empty() || defs.empty())
return;
return true;
expr_safe_replace subst(m);
expr_ref_vector fmls_tmp(m);
expr_ref tmp(m);
for (unsigned i = defs.size(); i-- > 0; ) {
auto const& d = defs[i];
@ -561,8 +562,11 @@ namespace mbp {
unsigned j = 0;
for (expr* fml : fmls) {
subst(fml, tmp);
if (m.is_false(eval(tmp)))
return false;
fmls[j++] = tmp;
}
return true;
}
};
@ -579,12 +583,13 @@ namespace mbp {
return (*m_imp)(model, var, vars, lits);
}
void arith_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
m_imp->project(model, vars, lits, false);
bool arith_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
vector<def> defs;
return m_imp->project(model, vars, lits, defs, false);
}
vector<def> arith_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return m_imp->project(model, vars, lits, true);
bool arith_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) {
return m_imp->project(model, vars, lits, defs, true);
}
void arith_project_plugin::set_check_purified(bool check_purified) {

View file

@ -29,8 +29,8 @@ namespace mbp {
bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override;
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override { return false; }
family_id get_family_id() override;
void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override { UNREACHABLE(); }
opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt);

View file

@ -1624,8 +1624,8 @@ namespace mbp {
);
}
vector<def> array_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return vector<def>();
bool array_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) {
return true;
}
void array_project_plugin::saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) {

View file

@ -35,7 +35,7 @@ namespace mbp {
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
void operator()(model& model, app_ref_vector& vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects);
family_id get_family_id() override;
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override;
};

View file

@ -300,8 +300,8 @@ namespace mbp {
return m_imp->solve(model, vars, lits);
}
vector<def> datatype_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return vector<def>();
bool datatype_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) {
return true;
}
void datatype_project_plugin::saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) {

View file

@ -34,7 +34,7 @@ namespace mbp {
bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override;
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
family_id get_family_id() override;
vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override;
};

View file

@ -67,7 +67,7 @@ namespace mbp {
virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; }
virtual family_id get_family_id() { return null_family_id; }
virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { };
virtual bool operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; };
/**
\brief project vars modulo model, return set of definitions for eliminated variables.
@ -76,7 +76,7 @@ namespace mbp {
- returns set of definitions
(TBD: in triangular form, the last definition can be substituted into definitions that come before)
*/
virtual vector<def> project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return vector<def>(); }
virtual bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) { return true; }
/**
\brief model based saturation. Saturates theory axioms to equi-satisfiable literals over EUF,

View file

@ -266,7 +266,11 @@ namespace qe {
vector<mbp::def> uflia_mbi::arith_project(model_ref& mdl, app_ref_vector& avars, expr_ref_vector& lits) {
mbp::arith_project_plugin ap(m);
ap.set_check_purified(false);
return ap.project(*mdl.get(), avars, lits);
vector<mbp::def> defs;
bool ok = ap.project(*mdl.get(), avars, lits, defs);
(void)ok;
CTRACE("qe", !ok, tout << "projection failure ignored!!!!\n");
return defs;
}
mbi_result uflia_mbi::operator()(expr_ref_vector& lits, model_ref& mdl) {

View file

@ -30,7 +30,7 @@ Revision History:
#include "qe/mbp/mbp_arith.h"
#include "qe/mbp/mbp_arrays.h"
#include "qe/mbp/mbp_datatypes.h"
#include "qe/qe_lite.h"
#include "qe/lite/qe_lite.h"
#include "model/model_pp.h"
#include "model/model_evaluator.h"

View file

@ -103,8 +103,8 @@ public:
char const* name() const override { return "qe"; }
void updt_params(params_ref const & p) override {
m_params = p;
m_imp->updt_params(p);
m_params.append(p);
m_imp->updt_params(m_params);
}

View file

@ -229,11 +229,12 @@ namespace sat {
literal r = roots[v];
SASSERT(v != r.var());
if (m_solver.m_cut_simplifier) m_solver.m_cut_simplifier->set_root(v, r);
if (m_solver.m_cut_simplifier)
m_solver.m_cut_simplifier->set_root(v, r);
bool set_root = m_solver.set_root(l, r);
bool root_ok = !m_solver.is_external(v) || set_root;
TRACE("elim_eqs", tout << l << " " << r << "\n";);
if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !root_ok))) {
if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !set_root))) {
// cannot really eliminate v, since we have to notify extension of future assignments
if (m_solver.m_config.m_drat) {
m_solver.m_drat.add(~l, r, sat::status::redundant());

View file

@ -288,6 +288,7 @@ namespace sat {
m_free_vars.pop_back();
m_active_vars.push_back(v);
reset_var(v, ext, dvar);
SASSERT(v < m_justification.size());
return v;
}
m_active_vars.push_back(v);
@ -3008,7 +3009,7 @@ namespace sat {
svector<double> logits(vars.size(), 0.0);
double itau = m_config.m_reorder_itau;
double lse = 0;
double mid = m_rand.max_value()/2;
double mid = (double)(m_rand.max_value()/2);
double max = 0;
for (double& f : logits) {
f = itau * (m_rand() - mid)/mid;
@ -3508,7 +3509,6 @@ namespace sat {
unsigned old_num_vars = m_vars_lim.pop(num_scopes);
if (old_num_vars == m_active_vars.size())
return;
unsigned free_vars_head = m_free_vars.size();
unsigned sz = m_active_vars.size(), j = old_num_vars;
unsigned new_lvl = m_scopes.size() - num_scopes;
@ -3531,16 +3531,14 @@ namespace sat {
for (unsigned i = old_num_vars; i < sz; ++i) {
bool_var v = m_active_vars[i];
if (is_visited(v) || is_active(v)) {
if (is_external(v) || is_visited(v) || is_active(v)) {
m_vars_to_reinit.push_back(v);
m_active_vars[j++] = v;
m_var_scope[v] = new_lvl;
}
else {
set_eliminated(v, true);
if (!is_external(v) || true) {
m_free_vars.push_back(v);
}
m_vars_to_free.push_back(v);
}
}
m_active_vars.shrink(j);
@ -3550,8 +3548,7 @@ namespace sat {
IF_VERBOSE(0, verbose_stream() << "cleanup: " << lit << " " << w.is_binary_clause() << "\n");
}
};
for (unsigned i = m_free_vars.size(); i-- > free_vars_head; ) {
bool_var v = m_free_vars[i];
for (bool_var v : m_vars_to_free) {
cleanup_watch(literal(v, false));
cleanup_watch(literal(v, true));
@ -3560,7 +3557,7 @@ namespace sat {
tout << "clauses to reinit: " << (m_clauses_to_reinit.size() - old_sz) << "\n";
tout << "new level: " << new_lvl << "\n";
tout << "vars to reinit: " << m_vars_to_reinit << "\n";
tout << "free vars: " << bool_var_vector(m_free_vars.size() - free_vars_head, m_free_vars.data() + free_vars_head) << "\n";
tout << "free vars: " << bool_var_vector(m_vars_to_free) << "\n";
for (unsigned i = m_clauses_to_reinit.size(); i-- > old_sz; )
tout << "reinit: " << m_clauses_to_reinit[i] << "\n";
display(tout););
@ -3599,7 +3596,6 @@ namespace sat {
void solver::pop(unsigned num_scopes) {
if (num_scopes == 0)
return;
unsigned free_vars_head = m_free_vars.size();
if (m_ext) {
pop_vars(num_scopes);
m_ext->pop(num_scopes);
@ -3609,13 +3605,16 @@ namespace sat {
scope & s = m_scopes[new_lvl];
m_inconsistent = false; // TBD: use model seems to make this redundant: s.m_inconsistent;
unassign_vars(s.m_trail_lim, new_lvl);
for (unsigned i = m_free_vars.size(); i-- > free_vars_head; )
m_case_split_queue.del_var_eh(m_free_vars[i]);
for (bool_var v : m_vars_to_free)
m_case_split_queue.del_var_eh(v);
m_scope_lvl -= num_scopes;
reinit_clauses(s.m_clauses_to_reinit_lim);
m_scopes.shrink(new_lvl);
if (m_ext)
if (m_ext) {
m_ext->pop_reinit();
m_free_vars.append(m_vars_to_free);
m_vars_to_free.reset();
}
}
void solver::unassign_vars(unsigned old_sz, unsigned new_lvl) {
@ -3656,17 +3655,17 @@ namespace sat {
clause_wrapper cw = m_clauses_to_reinit[i];
bool reinit = false;
if (cw.is_binary()) {
if (propagate_bin_clause(cw[0], cw[1]) && !at_base_lvl())
if (propagate_bin_clause(cw[0], cw[1]) && !at_base_lvl())
m_clauses_to_reinit[j++] = cw;
else if (has_variables_to_reinit(cw[0], cw[1]))
else if (has_variables_to_reinit(cw[0], cw[1]) && !at_base_lvl())
m_clauses_to_reinit[j++] = cw;
}
else {
clause & c = *(cw.get_clause());
if (ENABLE_TERNARY && c.size() == 3) {
if (!at_base_lvl() && propagate_ter_clause(c))
if (propagate_ter_clause(c) && !at_base_lvl())
m_clauses_to_reinit[j++] = cw;
else if (has_variables_to_reinit(c))
else if (has_variables_to_reinit(c) && !at_base_lvl())
m_clauses_to_reinit[j++] = cw;
else
c.set_reinit_stack(false);
@ -3674,13 +3673,13 @@ namespace sat {
}
detach_clause(c);
attach_clause(c, reinit);
if (!at_base_lvl() && reinit)
if (reinit && !at_base_lvl())
// clause propagated literal, must keep it in the reinit stack.
m_clauses_to_reinit[j++] = cw;
else if (has_variables_to_reinit(c))
else if (has_variables_to_reinit(c) && !at_base_lvl())
m_clauses_to_reinit[j++] = cw;
else
c.set_reinit_stack(false);
c.set_reinit_stack(false);
}
}
m_clauses_to_reinit.shrink(j);
@ -3692,6 +3691,7 @@ namespace sat {
//
void solver::user_push() {
pop_to_base_level();
m_free_var_freeze.push_back(m_free_vars);
m_free_vars.reset(); // resetting free_vars forces new variables to be assigned above new_v

View file

@ -124,7 +124,7 @@ namespace sat {
clause_vector m_clauses;
clause_vector m_learned;
unsigned m_num_frozen;
unsigned_vector m_active_vars, m_free_vars, m_vars_to_reinit;
unsigned_vector m_active_vars, m_free_vars, m_vars_to_free, m_vars_to_reinit;
vector<watch_list> m_watches;
svector<lbool> m_assignment;
svector<justification> m_justification;
@ -266,6 +266,11 @@ namespace sat {
//
// -----------------------
void add_clause(unsigned num_lits, literal * lits, sat::status st) override { mk_clause(num_lits, lits, st); }
void add_clause(literal l1, literal l2, status st) {
literal lits[2] = { l1, l2 };
add_clause(2, lits, st);
}
void add_clause(literal lit, status st) { literal lits[1] = { lit }; add_clause(1, lits, st); }
bool_var add_var(bool ext) override { return mk_var(ext, true); }
bool_var mk_var(bool ext = false, bool dvar = true);
@ -444,9 +449,10 @@ namespace sat {
void flush_roots();
typedef std::pair<literal, literal> bin_clause;
struct bin_clause_hash { unsigned operator()(bin_clause const& b) const { return b.first.hash() + 2*b.second.hash(); } };
protected:
watch_list & get_wlist(literal l) { return m_watches[l.index()]; }
watch_list const & get_wlist(literal l) const { return m_watches[l.index()]; }
watch_list const& get_wlist(literal l) const { return m_watches[l.index()]; }
watch_list& get_wlist(literal l) { return m_watches[l.index()]; }
protected:
watch_list & get_wlist(unsigned l_idx) { return m_watches[l_idx]; }
bool is_marked(bool_var v) const { return m_mark[v]; }
void mark(bool_var v) { SASSERT(!is_marked(v)); m_mark[v] = true; }

View file

@ -280,8 +280,8 @@ public:
m_inserted_const2bits.reset();
m_map.pop(n);
SASSERT(n <= m_num_scopes);
m_solver.user_pop(n);
m_goal2sat.user_pop(n);
m_solver.user_pop(n);
m_num_scopes -= n;
// ? m_internalized_converted = false;
m_has_uninterpreted.pop(n);
@ -683,10 +683,15 @@ public:
ensure_euf()->user_propagate_register_diseq(diseq_eh);
}
unsigned user_propagate_register(expr* e) override {
return ensure_euf()->user_propagate_register(e);
unsigned user_propagate_register_expr(expr* e) override {
return ensure_euf()->user_propagate_register_expr(e);
}
void user_propagate_register_created(user_propagator::created_eh_t& r) {
ensure_euf()->user_propagate_register_created(r);
}
private:
lbool internalize_goal(goal_ref& g) {

View file

@ -35,10 +35,7 @@ namespace sat {
// add clauses
virtual void add_clause(unsigned n, literal* lits, status st) = 0;
void add_clause(literal l1, literal l2, status st) {
literal lits[2] = {l1, l2};
add_clause(2, lits, st);
}
void add_clause(literal l1, literal l2, literal l3, status st) {
literal lits[3] = {l1, l2, l3};
add_clause(3, lits, st);

View file

@ -38,7 +38,6 @@ z3_add_component(sat_smt
q_queue.cpp
q_solver.cpp
recfun_solver.cpp
sat_dual_solver.cpp
sat_th.cpp
user_solver.cpp
COMPONENT_DEPENDENCIES

View file

@ -49,6 +49,14 @@ namespace arith {
}
}
void solver::mk_abs_axiom(app* n) {
expr* x = nullptr;
VERIFY(a.is_abs(n, x));
literal is_nonneg = mk_literal(a.mk_ge(x, a.mk_numeral(rational::zero(), n->get_sort())));
add_clause(~is_nonneg, eq_internalize(n, x));
add_clause(is_nonneg, eq_internalize(n, a.mk_uminus(x)));
}
// t = n^0
void solver::mk_power0_axioms(app* t, app* n) {
expr_ref p0(a.mk_power0(n, t->get_arg(1)), m);

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