3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 09:05:31 +00:00

added API to monitor clause inferences

See RELEASE_NOTES for more information
examples pending.
This commit is contained in:
Nikolaj Bjorner 2022-10-19 08:34:55 -07:00
parent 77cbd89420
commit 07dd1065db
34 changed files with 505 additions and 122 deletions

View file

@ -907,6 +907,33 @@ extern "C" {
~api_context_obj() override { dealloc(c); }
};
struct scoped_ast_vector {
Z3_ast_vector_ref* v;
scoped_ast_vector(Z3_ast_vector_ref* v): v(v) { v->inc_ref(); }
~scoped_ast_vector() { v->dec_ref(); }
};
void Z3_API Z3_solver_register_on_clause(
Z3_context c,
Z3_solver s,
void* user_context,
Z3_on_clause_eh on_clause_eh) {
Z3_TRY;
RESET_ERROR_CODE();
init_solver(c, s);
user_propagator::on_clause_eh_t _on_clause = [=](void* user_ctx, expr* proof, unsigned n, expr* const* _literals) {
Z3_ast_vector_ref * literals = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(literals);
expr_ref pr(proof, mk_c(c)->m());
scoped_ast_vector _sc(literals);
for (unsigned i = 0; i < n; ++i)
literals->m_ast_vector.push_back(_literals[i]);
on_clause_eh(user_ctx, of_expr(pr.get()), of_ast_vector(literals));
};
to_solver_ref(s)->register_on_clause(user_context, _on_clause);
Z3_CATCH;
}
void Z3_API Z3_solver_propagate_init(
Z3_context c,
Z3_solver s,

View file

@ -158,7 +158,7 @@ namespace z3 {
class context {
private:
friend class user_propagator_base;
bool m_enable_exceptions;
bool m_enable_exceptions = true;
rounding_mode m_rounding_mode;
Z3_context m_ctx = nullptr;
void init(config & c) {
@ -2670,10 +2670,10 @@ namespace z3 {
public:
struct simple {};
struct translate {};
solver(context & c):object(c) { init(Z3_mk_solver(c)); }
solver(context & c, simple):object(c) { init(Z3_mk_simple_solver(c)); }
solver(context & c):object(c) { init(Z3_mk_solver(c)); check_error(); }
solver(context & c, simple):object(c) { init(Z3_mk_simple_solver(c)); check_error(); }
solver(context & c, Z3_solver s):object(c) { init(s); }
solver(context & c, char const * logic):object(c) { init(Z3_mk_solver_for_logic(c, c.str_symbol(logic))); }
solver(context & c, char const * logic):object(c) { init(Z3_mk_solver_for_logic(c, c.str_symbol(logic))); check_error(); }
solver(context & c, solver const& src, translate): object(c) { Z3_solver s = Z3_solver_translate(src.ctx(), src, c); check_error(); init(s); }
solver(solver const & s):object(s) { init(s.m_solver); }
~solver() { Z3_solver_dec_ref(ctx(), m_solver); }
@ -4059,6 +4059,25 @@ namespace z3 {
return expr(ctx(), r);
}
typedef std::function<void(expr const& proof, expr_vector const& clause)> on_clause_eh_t;
class on_clause {
context& c;
on_clause_eh_t m_on_clause;
static void _on_clause_eh(void* _ctx, Z3_ast _proof, Z3_ast_vector _literals) {
on_clause* ctx = static_cast<on_clause*>(_ctx);
expr_vector lits(ctx->c, _literals);
expr proof(ctx->c, _proof);
ctx->m_on_clause(proof, lits);
}
public:
on_clause(solver& s, on_clause_eh_t& on_clause_eh): c(s.ctx()) {
m_on_clause = on_clause_eh;
Z3_solver_register_on_clause(c, s, this, _on_clause_eh);
c.check_error();
}
};
class user_propagator_base {

View file

@ -87,6 +87,7 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE
NativeFuncInterp.cs
NativeModel.cs
NativeSolver.cs
OnClause.cs
Optimize.cs
ParamDescrs.cs
Params.cs

102
src/api/dotnet/OnClause.cs Normal file
View file

@ -0,0 +1,102 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
OnClause.cs
Abstract:
Callback on clause inferences
Author:
Nikolaj Bjorner (nbjorner) 2022-10-19
Notes:
--*/
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Microsoft.Z3
{
using Z3_context = System.IntPtr;
using Z3_solver = System.IntPtr;
using voidp = System.IntPtr;
using Z3_ast = System.IntPtr;
using Z3_ast_vector = System.IntPtr;
/// <summary>
/// OnClause - clause inference callback
/// </summary>
public class OnClause : IDisposable
{
/// <summary>
/// Delegate type for when clauses are inferred.
/// An inference is a pair comprising of
/// - a proof (hint). A partial (or comprehensive) derivation justifying the inference.
/// - a clause (vector of literals)
/// The life-time of the proof hint and clause vector is limited to the scope of the callback.
/// should the callback want to store hints or clauses it will need to call Dup on the hints
/// and/or extract literals from the clause, respectively.
/// </summary>
public delegate void OnClauseEh(Expr proof_hint, ASTVector clause);
// access managed objects through a static array.
// thread safety is ignored for now.
GCHandle gch;
Solver solver;
Context ctx;
OnClauseEh on_clause;
Native.Z3_on_clause_eh on_clause_eh;
static void _on_clause(voidp ctx, Z3_ast _proof_hint, Z3_ast_vector _clause)
{
var onc = (OnClause)GCHandle.FromIntPtr(ctx).Target;
using var proof_hint = Expr.Create(onc.ctx, _proof_hint);
using var clause = new ASTVector(onc.ctx, _clause);
onc.on_clause(proof_hint, clause);
}
/// <summary>
/// OnClause constructor
/// </summary>
public OnClause(Solver s, OnClauseEh onc)
{
gch = GCHandle.Alloc(this);
solver = s;
ctx = solver.Context;
on_clause = onc;
on_clause_eh = _on_clause;
Native.Z3_solver_register_on_clause(ctx.nCtx, solver.NativeObject, GCHandle.ToIntPtr(gch), on_clause_eh);
}
/// <summary>
/// Release private memory.
/// </summary>
~OnClause()
{
Dispose();
}
/// <summary>
/// Must be called. The object will not be garbage collected automatically even if the context is disposed
/// </summary>
public virtual void Dispose()
{
if (!gch.IsAllocated)
return;
gch.Free();
}
}
}

View file

@ -11301,6 +11301,45 @@ def TransitiveClosure(f):
"""
return FuncDeclRef(Z3_mk_transitive_closure(f.ctx_ref(), f.ast), f.ctx)
def to_Ast(ptr,):
ast = Ast(ptr)
super(ctypes.c_void_p, ast).__init__(ptr)
return ast
def to_ContextObj(ptr,):
ctx = ContextObj(ptr)
super(ctypes.c_void_p, ctx).__init__(ptr)
return ctx
def to_AstVectorObj(ptr,):
v = AstVectorObj(ptr)
super(ctypes.c_void_p, v).__init__(ptr)
return v
# NB. my-hacky-class only works for a single instance of OnClause
# it should be replaced with a proper correlation between OnClause
# and object references that can be passed over the FFI.
# for UserPropagator we use a global dictionary, which isn't great code.
_my_hacky_class = None
def on_clause_eh(ctx, p, clause):
onc = _my_hacky_class
p = _to_expr_ref(to_Ast(p), onc.ctx)
clause = AstVector(to_AstVectorObj(clause), onc.ctx)
onc.on_clause(p, clause)
_on_clause_eh = Z3_on_clause_eh(on_clause_eh)
class OnClause:
def __init__(self, s, on_clause):
self.s = s
self.ctx = s.ctx
self.on_clause = on_clause
self.idx = 22
global _my_hacky_class
_my_hacky_class = self
Z3_solver_register_on_clause(self.ctx.ref(), self.s.solver, self.idx, _on_clause_eh)
class PropClosures:
def __init__(self):
@ -11358,11 +11397,6 @@ def user_prop_pop(ctx, cb, num_scopes):
prop.cb = cb
prop.pop(num_scopes)
def to_ContextObj(ptr,):
ctx = ContextObj(ptr)
super(ctypes.c_void_p, ctx).__init__(ptr)
return ctx
def user_prop_fresh(ctx, _new_ctx):
_prop_closures.set_threaded()
@ -11377,10 +11411,6 @@ def user_prop_fresh(ctx, _new_ctx):
_prop_closures.set(new_prop.id, new_prop)
return new_prop.id
def to_Ast(ptr,):
ast = Ast(ptr)
super(ctypes.c_void_p, ast).__init__(ptr)
return ast
def user_prop_fixed(ctx, cb, id, value):
prop = _prop_closures.get(ctx)
@ -11442,6 +11472,7 @@ _user_prop_eq = Z3_eq_eh(user_prop_eq)
_user_prop_diseq = Z3_eq_eh(user_prop_diseq)
_user_prop_decide = Z3_decide_eh(user_prop_decide)
def PropagateFunction(name, *sig):
"""Create a function that gets tracked by user propagator.
Every term headed by this function symbol is tracked.
@ -11462,7 +11493,8 @@ def PropagateFunction(name, *sig):
dom[i] = sig[i].ast
ctx = rng.ctx
return FuncDeclRef(Z3_solver_propagate_declare(ctx.ref(), to_symbol(name, ctx), arity, dom, rng.ast), ctx)
class UserPropagateBase:

View file

@ -1433,6 +1433,7 @@ Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, Z3_as
Z3_DECLARE_CLOSURE(Z3_final_eh, void, (void* ctx, Z3_solver_callback cb));
Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t));
Z3_DECLARE_CLOSURE(Z3_decide_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast* t, unsigned* idx, Z3_lbool* phase));
Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, Z3_ast_vector literals));
/**
@ -6877,6 +6878,24 @@ extern "C" {
*/
void Z3_API Z3_solver_get_levels(Z3_context c, Z3_solver s, Z3_ast_vector literals, unsigned sz, unsigned levels[]);
/**
\brief register a callback to that retrieves assumed, inferred and deleted clauses during search.
\param c - context.
\param s - solver object.
\param user_context - a context used to maintain state for callbacks.
\param on_clause_eh - a callback that is invoked by when a clause is
- asserted to the CDCL engine (corresponding to an input clause after pre-processing)
- inferred by CDCL(T) using either a SAT or theory conflict/propagation
- deleted by the CDCL(T) engine
def_API('Z3_solver_register_on_clause', VOID, (_in(CONTEXT), _in(SOLVER), _in(VOID_PTR), _fnptr(Z3_on_clause_eh)))
*/
void Z3_API Z3_solver_register_on_clause(
Z3_context c,
Z3_solver s,
void* user_context,
Z3_on_clause_eh on_clause_eh);
/**
\brief register a user-properator with the solver.