/*++ Copyright (c) 2012 Microsoft Corporation Module Name: NativeSolver.cs Abstract: Z3 Managed API: Native Solver Author: Christoph Wintersteiger (cwinter) 2012-03-22 Nikolaj Bjorner (nbjorner) 2022-03-01 Notes: --*/ using System; using System.Diagnostics; using System.Collections.Generic; using System.Linq; namespace Microsoft.Z3 { using Z3_ast = System.IntPtr; using Z3_context = System.IntPtr; using Z3_func_decl = System.IntPtr; using Z3_params = System.IntPtr; using Z3_solver = System.IntPtr; using Z3_sort = System.IntPtr; using Z3_symbol = System.IntPtr; /// /// Solvers. /// public class NativeSolver : IDisposable { /// /// A string that describes all available solver parameters. /// public string Help => Native.Z3_solver_get_help(nCtx, z3solver); private void SetParam(Action setter) { Z3_params p = Native.Z3_mk_params(nCtx); Native.Z3_params_inc_ref(nCtx, p); setter(p); Native.Z3_solver_set_params(nCtx, z3solver, p); Native.Z3_params_dec_ref(nCtx, p); } /// /// Sets parameter on the solver /// public void Set(string name, bool value) { SetParam((Z3_params p) => Native.Z3_params_set_bool(nCtx, p, Native.Z3_mk_string_symbol(nCtx, name), (byte)(value ? 1 : 0))); } /// /// Sets parameter on the solver /// public void Set(string name, uint value) { SetParam((Z3_params p) => Native.Z3_params_set_uint(nCtx, p, Native.Z3_mk_string_symbol(nCtx, name), value)); } /// /// Sets parameter on the solver /// public void Set(string name, double value) { SetParam((Z3_params p) => Native.Z3_params_set_double(nCtx, p, Native.Z3_mk_string_symbol(nCtx, name), value)); } /// /// Sets parameter on the solver /// public void Set(string name, string value) { var value_sym = Native.Z3_mk_string_symbol(nCtx, value); SetParam((Z3_params p) => Native.Z3_params_set_symbol(nCtx, p, Native.Z3_mk_string_symbol(nCtx, name), value_sym)); } #if false /// /// Sets parameter on the solver /// public void Set(string name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, bool value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, uint value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, double value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, string value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } /// /// Retrieves parameter descriptions for solver. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_solver_get_param_descrs(nCtx, NativeObject)); } } #endif /// /// The current number of backtracking points (scopes). /// /// /// public uint NumScopes => Native.Z3_solver_get_num_scopes(nCtx, z3solver); /// /// Creates a backtracking point. /// /// public void Push() => Native.Z3_solver_push(nCtx, z3solver); /// /// Backtracks backtracking points. /// /// Note that an exception is thrown if is not smaller than NumScopes /// public void Pop(uint n = 1) => Native.Z3_solver_pop(nCtx, z3solver, n); /// /// Resets the Solver. /// /// This removes all assertions from the solver. public void Reset() => Native.Z3_solver_reset(nCtx, z3solver); /// /// Assert a constraint (or multiple) into the solver. /// public void Assert(params Z3_ast[] constraints) { Debug.Assert(constraints != null); Debug.Assert(constraints.All(c => c != IntPtr.Zero)); foreach (Z3_ast a in constraints) { Native.Z3_solver_assert(nCtx, z3solver, a); } } /// /// Alias for Assert. /// public void Add(params Z3_ast[] constraints) => Assert(constraints); /// /// Alias for Assert. /// public void Add(IEnumerable constraints) => Assert(constraints.ToArray()); /// /// Add constraints to ensure the function f can only be injective. /// Example: /// for function f : D1 x D2 -> R /// assert axioms /// forall (x1 : D1, x2 : D2) x1 = inv1(f(x1,x2)) /// forall (x1 : D1, x2 : D2) x2 = inv2(f(x1,x2)) /// /// public void AssertInjective(Z3_func_decl f) { uint arity = Native.Z3_get_arity(nCtx, f); Z3_sort range = Native.Z3_get_range(nCtx, f); Z3_ast[] vars = new Z3_ast[arity]; Z3_sort[] sorts = new Z3_sort[arity]; Z3_symbol[] names = new Z3_symbol[arity]; for (uint i = 0; i < arity; ++i) { Z3_sort domain = Native.Z3_get_domain(nCtx, f, i); vars[i] = ntvContext.MkBound(arity - i - 1, domain); sorts[i] = domain; names[i] = Native.Z3_mk_int_symbol(nCtx, (int)i); } Z3_ast app_f = IntPtr.Zero; // Context.MkApp(f, vars); for (uint i = 0; i < arity; ++i) { Z3_sort domain = Native.Z3_get_domain(nCtx, f, i); Z3_func_decl proj = ntvContext.MkFreshFuncDecl("inv", new Z3_sort[] { range }, domain); Z3_ast body = ntvContext.MkEq(vars[i], ntvContext.MkApp(proj, app_f)); Z3_ast q = ntvContext.MkForall(names, sorts, body); Assert(q); } } /// /// Assert multiple constraints into the solver, and track them (in the unsat) core /// using the Boolean constants in ps. /// /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination /// of the Boolean variables provided using /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(Z3_ast[] constraints, Z3_ast[] ps) { Debug.Assert(constraints != null); Debug.Assert(constraints.All(c => c != IntPtr.Zero)); Debug.Assert(ps.All(c => c != IntPtr.Zero)); if (constraints.Length != ps.Length) throw new Z3Exception("Argument size mismatch"); for (int i = 0; i < constraints.Length; i++) Native.Z3_solver_assert_and_track(nCtx, z3solver, constraints[i], ps[i]); } /// /// Assert a constraint into the solver, and track it (in the unsat) core /// using the Boolean constant p. /// /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination /// of the Boolean variables provided using /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(Z3_ast constraint, Z3_ast p) { Debug.Assert(constraint != null); Debug.Assert(p != null); Native.Z3_solver_assert_and_track(nCtx, z3solver, constraint, p); } /// /// Load solver assertions from a file. /// public void FromFile(string file) => Native.Z3_solver_from_file(nCtx, z3solver, file); /// /// Load solver assertions from a string. /// public void FromString(string str) => Native.Z3_solver_from_string(nCtx, z3solver, str); /// /// The number of assertions in the solver. /// public uint NumAssertions => (uint)ntvContext.ToArray(Native.Z3_solver_get_assertions(nCtx, z3solver)).Length; /// /// The set of asserted formulas. /// public Z3_ast[] Assertions => ntvContext.ToArray(Native.Z3_solver_get_assertions(nCtx, z3solver)); /// /// Currently inferred units. /// public Z3_ast[] Units => ntvContext.ToArray(Native.Z3_solver_get_units(nCtx, z3solver)); /// /// Checks whether the assertions in the solver are consistent or not. /// /// /// /// /// /// public Status Check(params Z3_ast[] assumptions) { Z3_lbool r; if (assumptions == null || assumptions.Length == 0) r = (Z3_lbool)Native.Z3_solver_check(nCtx, z3solver); else r = (Z3_lbool)Native.Z3_solver_check_assumptions(nCtx, z3solver, (uint)assumptions.Length, assumptions); return lboolToStatus(r); } /// /// Checks whether the assertions in the solver are consistent or not. /// /// /// /// /// /// public Status Check(IEnumerable assumptions) { Z3_lbool r; Z3_ast[] asms = assumptions.ToArray(); if (asms.Length == 0) r = (Z3_lbool)Native.Z3_solver_check(nCtx, z3solver); else r = (Z3_lbool)Native.Z3_solver_check_assumptions(nCtx, z3solver, (uint)asms.Length, asms); return lboolToStatus(r); } /// /// The model of the last Check(params Expr[] assumptions). /// /// /// The result is null if Check(params Expr[] assumptions) was not invoked before, /// if its results was not SATISFIABLE, or if model production is not enabled. /// public NativeModel Model { get { IntPtr x = Native.Z3_solver_get_model(nCtx, z3solver); return x == IntPtr.Zero ? null : new NativeModel(ntvContext, x); } } /// /// The proof of the last Check(params Expr[] assumptions). /// /// /// The result is null if Check(params Expr[] assumptions) was not invoked before, /// if its results was not UNSATISFIABLE, or if proof production is disabled. /// public Z3_ast Proof => Native.Z3_solver_get_proof(nCtx, z3solver); /// /// The unsat core of the last Check. /// /// /// The unsat core is a subset of Assertions /// The result is empty if Check was not invoked before, /// if its results was not UNSATISFIABLE, or if core production is disabled. /// public Z3_ast[] UnsatCore => ntvContext.ToArray(Native.Z3_solver_get_unsat_core(nCtx, z3solver)); /// /// A brief justification of why the last call to Check returned UNKNOWN. /// public string ReasonUnknown => Native.Z3_solver_get_reason_unknown(nCtx, z3solver); /// /// Create a clone of the current solver with respect to ctx. /// public NativeSolver Translate(NativeContext ctx) { Debug.Assert(ctx != null); return new NativeSolver(ctx, Native.Z3_solver_translate(nCtx, z3solver, ctx.nCtx)); } /// /// Import model converter from other solver. /// public void ImportModelConverter(NativeSolver src) { Debug.Assert(src != null); Native.Z3_solver_import_model_converter(nCtx, src.z3solver, z3solver); } /// /// Solver statistics. /// public Statistics.Entry[] Statistics { get { var stats = Native.Z3_solver_get_statistics(nCtx, z3solver); return ntvContext.GetStatistics(stats); } } /// /// A string representation of the solver. /// public override string ToString() { return Native.Z3_solver_to_string(nCtx, z3solver); } #region Internal readonly NativeContext ntvContext; Z3_solver z3solver; Z3_context nCtx => ntvContext.nCtx; internal NativeSolver(NativeContext nativeCtx, Z3_solver z3solver) { Debug.Assert(nativeCtx != null); Debug.Assert(z3solver != IntPtr.Zero); this.ntvContext = nativeCtx; this.z3solver = z3solver; Native.Z3_solver_inc_ref(nCtx, z3solver); } /// /// Finalizer. /// ~NativeSolver() { Dispose(); } /// /// Disposes of the underlying native Z3 object. /// public void Dispose() { if (z3solver != IntPtr.Zero) { Native.Z3_solver_dec_ref(nCtx, z3solver); z3solver = IntPtr.Zero; } GC.SuppressFinalize(this); } private Status lboolToStatus(Z3_lbool r) { switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } #endregion } }