(** Module test_mlapi - ML test and demo program for Z3. Matches test_capi.ml - JakobL@2007-09-08 *) module Z3 = Z3.V3 (* @name Auxiliary Functions *) (** printf *) let printf = Printf.printf;; (** fprintf *) let fprintf = Printf.fprintf;; (** Exit gracefully in case of error. *) let exitf message = fprintf stderr "BUG: %s.\n" message; exit 1;; (** Create a logical context. Enable model construction. Also enable tracing to stderr. *) let mk_context ctx = let ctx = Z3.mk_context_x (Array.append [|("MODEL", "true")|] ctx) in (* You may comment out the following line to disable tracing: *) (* Z3.trace_to_stderr ctx; *) ctx;; (** Create a variable using the given name and type. *) let mk_var ctx name ty = Z3.mk_const ctx (Z3.mk_string_symbol ctx name) ty;; (** Create a boolean variable using the given name. *) let mk_bool_var ctx name = mk_var ctx name (Z3.mk_bool_sort ctx);; (** Create an integer variable using the given name. *) let mk_int_var ctx name = mk_var ctx name (Z3.mk_int_sort ctx);; (** Create a Z3 integer node using a C int. *) let mk_int ctx v = Z3.mk_int ctx v (Z3.mk_int_sort ctx);; (** Create a real variable using the given name. *) let mk_real_var ctx name = mk_var ctx name (Z3.mk_real_sort ctx);; (** Create the unary function application: {e (f x) }. *) let mk_unary_app ctx f x = Z3.mk_app ctx f [|x|];; (** Create the binary function application: {e (f x y) }. *) let mk_binary_app ctx f x y = Z3.mk_app ctx f [|x;y|];; (** Auxiliary function to check whether two Z3 types are equal or not. *) let equal_sorts ctx t1 t2 = Z3.is_eq_sort ctx t1 t2;; (** Check whether the logical context is satisfiable, and compare the result with the expected result. If the context is satisfiable, then display the model. *) let check ctx expected_result = begin let (result, m) = Z3.check_and_get_model ctx in (match result with | Z3.L_FALSE -> printf "unsat\n"; | Z3.L_UNDEF -> printf "unknown\n"; printf "potential model:\n%s\n" (Z3.model_to_string ctx m); (Z3.del_model ctx m); | Z3.L_TRUE -> printf "sat\n%s\n" (Z3.model_to_string ctx m); (Z3.del_model ctx m); ); if result != expected_result then exitf "unexpected result"; end;; (** Prove that the constraints already asserted into the logical context implies the given formula. The result of the proof is displayed. Z3 is a satisfiability checker. So, one can prove {e f } by showing that {e (not f) } is unsatisfiable. The context {e ctx } is not modified by this function. *) let prove ctx f is_valid = begin (* save the current state of the context *) Z3.push ctx; let not_f = Z3.mk_not ctx f in Z3.assert_cnstr ctx not_f; (match Z3.check_and_get_model ctx with | (Z3.L_FALSE,_) -> (* proved *) printf "valid\n"; if not is_valid then exitf "unexpected result"; | (Z3.L_UNDEF,m) -> (* Z3 failed to prove/disprove f. *) printf "unknown\n"; (* m should be viewed as a potential counterexample. *) printf "potential counterexample:\n%s\n" (Z3.model_to_string ctx m); if is_valid then exitf "unexpected result"; (Z3.del_model ctx m); | (Z3.L_TRUE,m) -> (* disproved *) printf "invalid\n"; (* the model returned by Z3 is a counterexample *) printf "counterexample:\n%s\n" (Z3.model_to_string ctx m); if is_valid then exitf "unexpected result"; (Z3.del_model ctx m); ); (* restore context *) Z3.pop ctx 1; end;; (** Assert the axiom: function f is injective in the i-th argument. The following axiom is asserted into the logical context: forall (x_1, ..., x_n) finv(f(x_1, ..., x_i, ..., x_n)) = x_i Where, {e finv } is a fresh function declaration. *) let assert_inj_axiom ctx f i = begin let sz = Z3.get_domain_size ctx f in if i >= sz then exitf "failed to create inj axiom"; (* declare the i-th inverse of f: finv *) let finv_domain = Z3.get_range ctx f in let finv_range = Z3.get_domain ctx f i in let finv = Z3.mk_fresh_func_decl ctx "inv" [|finv_domain|] finv_range in (* allocate temporary arrays *) (* fill types, names and xs *) let types = Z3.get_domains ctx f in let names = Array.init sz (Z3.mk_int_symbol ctx) in let xs = Array.init sz (fun j->Z3.mk_bound ctx j (types.(j))) in (* create f(x_0, ..., x_i, ..., x_{n-1}) *) let fxs = Z3.mk_app ctx f xs in (* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) *) let finv_fxs = mk_unary_app ctx finv fxs in (* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i *) let eq = Z3.mk_eq ctx finv_fxs (xs.(i)) in (* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier *) let p = Z3.mk_pattern ctx [|fxs|] in printf "pattern: %s\n" (Z3.pattern_to_string ctx p); printf "\n"; (* create & assert quantifier *) let q = Z3.mk_forall ctx 0 (* using default weight *) [|p|] (* the "array" of patterns *) types names eq in printf "assert axiom:\n%s\n" (Z3.ast_to_string ctx q); Z3.assert_cnstr ctx q; end;; (** Assert the axiom: function f is commutative. This example uses the SMT-LIB parser to simplify the axiom construction. *) let assert_comm_axiom ctx f = begin let t = Z3.get_range ctx f in if Z3.get_domain_size ctx f != 2 || not (equal_sorts ctx (Z3.get_domain ctx f 0) t) || not (equal_sorts ctx (Z3.get_domain ctx f 1) t) then exitf "function must be binary, and argument types must be equal to return type"; (* Inside the parser, function f will be referenced using the symbol 'f'. *) let f_name = Z3.mk_string_symbol ctx "f" in (* Inside the parser, type t will be referenced using the symbol 'T'. *) let t_name = Z3.mk_string_symbol ctx "T" in let str = "(benchmark comm :formula (forall (x T) (y T) (= (f x y) (f y x))))" in let q = Z3.parse_smtlib_string_formula ctx str [|t_name|] [|t|] [|f_name|] [|f|] in printf "assert axiom:\n%s\n" (Z3.ast_to_string ctx q); Z3.assert_cnstr ctx q; end;; (** Z3 does not support explicitly tuple updates. They can be easily implemented as macros. The argument {e t } must have tuple type. A tuple update is a new tuple where field {e i } has value {e new_val }, and all other fields have the value of the respective field of {e t }. {e update(t, i, new_val) } is equivalent to {e mk_tuple(proj_0(t), ..., new_val, ..., proj_n(t)) } *) let mk_tuple_update c t i new_val = begin let ty = Z3.get_sort c t in let (mk_tuple_decl,fields)=Z3.get_tuple_sort c ty in if i>=Array.length fields then exitf "invalid tuple update, index is too big"; let f j = if i = j then (* use new_val at position i: *) new_val else (* use field j of t: *) (mk_unary_app c (fields.(j)) t) in let new_fields = Array.init (Array.length fields) f in Z3.mk_app c (Z3.get_tuple_sort_mk_decl c ty) new_fields; end;; (** Display a symbol in the given output stream. *) let display_symbol c out s = match Z3.symbol_refine c s with | Z3.Symbol_int i -> fprintf out "#%d" i; | Z3.Symbol_string r ->fprintf out "%s" r; | Z3.Symbol_unknown -> ();; (** Display the given type. *) let rec display_sort c out ty = begin match Z3.sort_refine c ty with | Z3.Sort_uninterpreted s -> display_symbol c out s; | Z3.Sort_bool -> fprintf out "bool"; | Z3.Sort_int -> fprintf out "int"; | Z3.Sort_real -> fprintf out "real"; | Z3.Sort_relation -> fprintf out "relation"; | Z3.Sort_finite_domain -> fprintf out "finite-domain"; | Z3.Sort_bv sz -> fprintf out "bv%d" sz; | Z3.Sort_array (domain, range) -> fprintf out "["; display_sort c out domain; fprintf out "->"; display_sort c out range; fprintf out "]"; | Z3.Sort_datatype cons -> Array.iter (fun (dt_con : Z3.datatype_constructor_refined) -> let fields = dt_con.Z3.accessors in fprintf out "("; let f i v = if i>0 then fprintf out ", "; display_sort c out (Z3.get_range c v); in Array.iteri f fields; fprintf out ")") cons | Z3.Sort_unknown s -> fprintf out "unknown["; display_symbol c out s; fprintf out "unknown]"; end;; (** Custom ast pretty printer. This function demonstrates how to use the API to navigate terms. *) let rec display_numeral c out nm = match nm with | Z3.Numeral_small(n,1L) -> Printf.fprintf out "%Ld" n | Z3.Numeral_small(n,d) -> Printf.fprintf out "%Ld/%Ld" n d | Z3.Numeral_large s -> Printf.fprintf out "%s" s let rec display_ast c out v = begin match Z3.term_refine c v with | Z3.Term_app(k, f, args) -> let num_fields = Array.length args in let a = Z3.to_app c v in let d = Z3.get_app_decl c a in Printf.fprintf out "%s" (Z3.func_decl_to_string c d); if num_fields > 0 then begin Printf.fprintf out "["; for i = 0 to num_fields - 1 do if i > 0 then Printf.fprintf out ", "; display_ast c out (Z3.get_app_arg c a i) done; Printf.fprintf out "]" end | Z3.Term_numeral(nm, s) -> display_numeral c out nm; Printf.fprintf out ":"; display_sort c out s | Z3.Term_var(idx, s) -> printf "#unknown" | Z3.Term_quantifier(b, w, pats, bound, body) -> printf "quantifier" end;; (** Custom function for traversing a term and replacing the constant 'x' by the bound variable having index 'idx'. This function illustrates how to walk Z3 terms and reconstruct them. **) let rec abstract c x idx term = if Z3.is_eq_ast c term x then Z3.mk_bound c idx (Z3.get_sort c x) else match Z3.term_refine c term with | Z3.Term_app(k, f, args) -> Z3.mk_app c f (Array.map (abstract c x idx) args) | Z3.Term_numeral(nm, s) -> term | Z3.Term_var(idx, s) -> term | Z3.Term_quantifier(b, w, pats, bound, body) -> let idx = (idx + Array.length bound) in let body = abstract c x idx body in let is_forall = b = Z3.Forall in let mk_pattern terms = Z3.mk_pattern c (Array.map (abstract c x idx) terms) in let patterns = Array.map mk_pattern pats in Z3.mk_quantifier c is_forall w patterns (Array.map snd bound) (Array.map fst bound) body (** Example abstraction function. **) let abstract_example() = begin printf "\nabstract_example\n"; let ctx = mk_context [||] in let x = mk_int_var ctx "x" in let x_decl = Z3.get_app_decl ctx (Z3.to_app ctx x) in let y = mk_int_var ctx "y" in let y_decl = Z3.get_app_decl ctx (Z3.to_app ctx y) in let decls = [| x_decl; y_decl |] in let a = Z3.mk_string_symbol ctx "a" in let b = Z3.mk_string_symbol ctx "b" in let names = [| a; b |] in let str = "(benchmark tst :formula (> a b))" in let f = Z3.parse_smtlib_string_formula ctx str [||] [||] names decls in printf "formula: %s\n" (Z3.ast_to_string ctx f); let f2 = abstract ctx x 0 f in printf "abstracted formula: %s\n" (Z3.ast_to_string ctx f2); (* delete logical context *) Z3.del_context ctx; end;; (** Custom function interpretations pretty printer. *) let display_function_interpretations c out m = begin fprintf out "function interpretations:\n"; let display_function (name, entries, func_else) = begin display_symbol c out name; fprintf out " = {"; let display_entry j (args,valu) = if j > 0 then fprintf out ", "; fprintf out "("; let f k arg = if k > 0 then fprintf out ", "; display_ast c out arg in Array.iteri f args; fprintf out "|->"; display_ast c out valu; fprintf out ")"; in Array.iteri display_entry entries; if Array.length entries > 0 then fprintf out ", "; fprintf out "(else|->"; display_ast c out func_else; fprintf out ")}\n"; end; in Array.iter display_function (Z3.get_model_funcs c m); end;; (** Custom model pretty printer. *) let display_model c out m = begin let constants=Z3.get_model_constants c m in let f i e = let name = Z3.get_decl_name c e in let (ok, v) = Z3.eval_func_decl c m e in display_symbol c out name; fprintf out " = "; display_ast c out v; fprintf out "\n" in Array.iteri f constants; display_function_interpretations c out m; end;; (** Similar to #check, but uses #display_model instead of #Z3_model_to_string. *) let check2 ctx expected_result = begin let (result,m) = Z3.check_and_get_model ctx in (match result with | Z3.L_FALSE -> printf "unsat\n"; | Z3.L_UNDEF -> printf "unknown\n"; printf "potential model:\n"; display_model ctx stdout m; (Z3.del_model ctx m); | Z3.L_TRUE -> printf "sat\n"; display_model ctx stdout m; (Z3.del_model ctx m); ); if result != expected_result then exitf "unexpected result"; end;; (** Display Z3 version in the standard output. *) let display_version() = begin let (major, minor, build, revision)=Z3.get_version() in printf "Z3 %d.%d.%d.%d\n" major minor build revision; end;; (* @name Examples *) (** "Hello world" example: create a Z3 logical context, and delete it. *) let simple_example() = begin printf "\nsimple_example\n"; let ctx = mk_context [||] in (* do something with the context *) printf "CONTEXT:\n%sEND OF CONTEXT\n" (Z3.context_to_string ctx); (* delete logical context *) Z3.del_context ctx; end;; (** Demonstration of how Z3 can be used to prove validity of De Morgan's Duality Law: {e not(x and y) <-> (not x) or ( not y) } *) let demorgan() = begin printf "\nDeMorgan\n"; let ctx = mk_context [||] in let bool_sort = Z3.mk_bool_sort ctx in let symbol_x = Z3.mk_int_symbol ctx 0 in let symbol_y = Z3.mk_int_symbol ctx 1 in let x = Z3.mk_const ctx symbol_x bool_sort in let y = Z3.mk_const ctx symbol_y bool_sort in (* De Morgan - with a negation around: *) (* !(!(x && y) <-> (!x || !y)) *) let not_x = Z3.mk_not ctx x in let not_y = Z3.mk_not ctx y in let x_and_y = Z3.mk_and ctx [|x;y|] in let ls = Z3.mk_not ctx x_and_y in let rs = Z3.mk_or ctx [|not_x;not_y|] in let conjecture = Z3.mk_iff ctx ls rs in let negated_conjecture = Z3.mk_not ctx conjecture in Z3.assert_cnstr ctx negated_conjecture; (match Z3.check ctx with | Z3.L_FALSE -> (* The negated conjecture was unsatisfiable, hence the conjecture is valid *) printf "DeMorgan is valid\n" | Z3.L_UNDEF -> (* Check returned undef *) printf "Undef\n" | Z3.L_TRUE -> (* The negated conjecture was satisfiable, hence the conjecture is not valid *) Printf.printf "DeMorgan is not valid\n"); Z3.del_context ctx; end;; (** Find a model for {e x xor y }. *) let find_model_example1() = begin printf "\nfind_model_example1\n"; let ctx = mk_context [||] in let x = mk_bool_var ctx "x" in let y = mk_bool_var ctx "y" in let x_xor_y = Z3.mk_xor ctx x y in Z3.assert_cnstr ctx x_xor_y; printf "model for: x xor y\n"; check ctx Z3.L_TRUE; Z3.del_context ctx; end;; (** Find a model for {e x < y + 1, x > 2 }. Then, assert {e not(x = y) }, and find another model. *) let find_model_example2() = begin printf "\nfind_model_example2\n"; let ctx = mk_context [||] in let x = mk_int_var ctx "x" in let y = mk_int_var ctx "y" in let one = mk_int ctx 1 in let two = mk_int ctx 2 in let y_plus_one = Z3.mk_add ctx [|y;one|] in let c1 = Z3.mk_lt ctx x y_plus_one in let c2 = Z3.mk_gt ctx x two in Z3.assert_cnstr ctx c1; Z3.assert_cnstr ctx c2; printf "model for: x < y + 1, x > 2\n"; check ctx Z3.L_TRUE; (* assert not(x = y) *) let x_eq_y = Z3.mk_eq ctx x y in let c3 = Z3.mk_not ctx x_eq_y in Z3.assert_cnstr ctx c3; printf "model for: x < y + 1, x > 2, not(x = y)\n"; check ctx Z3.L_TRUE; Z3.del_context ctx; end;; (** Prove {e x = y implies g(x) = g(y) }, and disprove {e x = y implies g(g(x)) = g(y) }. This function demonstrates how to create uninterpreted types and functions. *) let prove_example1() = begin printf "\nprove_example1\n"; let ctx = mk_context [||] in (* create uninterpreted type. *) let u_name = Z3.mk_string_symbol ctx "U" in let u = Z3.mk_uninterpreted_sort ctx u_name in (* declare function g *) let g_name = Z3.mk_string_symbol ctx "g" in let g = Z3.mk_func_decl ctx g_name [|u|] u in (* create x and y *) let x_name = Z3.mk_string_symbol ctx "x" in let y_name = Z3.mk_string_symbol ctx "y" in let x = Z3.mk_const ctx x_name u in let y = Z3.mk_const ctx y_name u in (* create g(x), g(y) *) let gx = mk_unary_app ctx g x in let gy = mk_unary_app ctx g y in (* assert x = y *) let eq = Z3.mk_eq ctx x y in Z3.assert_cnstr ctx eq; (* prove g(x) = g(y) *) let f = Z3.mk_eq ctx gx gy in printf "prove: x = y implies g(x) = g(y)\n"; prove ctx f true; (* create g(g(x)) *) let ggx = mk_unary_app ctx g gx in (* disprove g(g(x)) = g(y) *) let f = Z3.mk_eq ctx ggx gy in printf "disprove: x = y implies g(g(x)) = g(y)\n"; prove ctx f false; Z3.del_context ctx; end;; (** Prove {e not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 }. Then, show that {e z < -1 } is not implied. This example demonstrates how to combine uninterpreted functions and arithmetic. *) let prove_example2() = begin printf "\nprove_example2\n"; let ctx = mk_context [||] in (* declare function g *) let int_sort = Z3.mk_int_sort ctx in let g_name = Z3.mk_string_symbol ctx "g" in let g = Z3.mk_func_decl ctx g_name [|int_sort|] int_sort in (* create x, y, and z *) let x = mk_int_var ctx "x" in let y = mk_int_var ctx "y" in let z = mk_int_var ctx "z" in (* create gx, gy, gz *) let gx = mk_unary_app ctx g x in let gy = mk_unary_app ctx g y in let gz = mk_unary_app ctx g z in (* create zero *) let zero = mk_int ctx 0 in (* assert not(g(g(x) - g(y)) = g(z)) *) let gx_gy = Z3.mk_sub ctx [|gx;gy|] in let ggx_gy = mk_unary_app ctx g gx_gy in let eq = Z3.mk_eq ctx ggx_gy gz in let c1 = Z3.mk_not ctx eq in Z3.assert_cnstr ctx c1; (* assert x + z <= y *) let x_plus_z = Z3.mk_add ctx [|x;z|] in let c2 = Z3.mk_le ctx x_plus_z y in Z3.assert_cnstr ctx c2; (* assert y <= x *) let c3 = Z3.mk_le ctx y x in Z3.assert_cnstr ctx c3; (* prove z < 0 *) let f = Z3.mk_lt ctx z zero in printf "prove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0\n"; prove ctx f true; (* disprove z < -1 *) let minus_one = mk_int ctx (-1) in let f = Z3.mk_lt ctx z minus_one in printf "disprove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < -1\n"; prove ctx f false; Z3.del_context ctx; end;; (** Show how push & pop can be used to create "backtracking" points. This example also demonstrates how big numbers can be created in Z3. *) let push_pop_example1() = begin printf "\npush_pop_example1\n"; let ctx = mk_context [||] in (* create a big number *) let int_sort = Z3.mk_int_sort ctx in let big_number = Z3.mk_numeral ctx "1000000000000000000000000000000000000000000000000000000" int_sort in (* create number 3 *) let three = Z3.mk_numeral ctx "3" int_sort in (* create x *) let x_sym = Z3.mk_string_symbol ctx "x" in let x = Z3.mk_const ctx x_sym int_sort in (* assert x >= "big number" *) let c1 = Z3.mk_ge ctx x big_number in printf "assert: x >= 'big number'\n"; Z3.assert_cnstr ctx c1; (* create a backtracking point *) printf "push\n"; Z3.push ctx; printf "number of scopes: %d\n" (Z3.get_num_scopes ctx); (* assert x <= 3 *) let c2 = Z3.mk_le ctx x three in printf "assert: x <= 3\n"; Z3.assert_cnstr ctx c2; (* context is inconsistent at this point *) check2 ctx Z3.L_FALSE; (* backtrack: the constraint x <= 3 will be removed, since it was asserted after the last push. *) printf "pop\n"; Z3.pop ctx 1; printf "number of scopes: %d\n" (Z3.get_num_scopes ctx); (* the context is consistent again. *) check2 ctx Z3.L_TRUE; (* new constraints can be asserted... *) (* create y *) let y_sym = Z3.mk_string_symbol ctx "y" in let y = Z3.mk_const ctx y_sym int_sort in (* assert y > x *) let c3 = Z3.mk_gt ctx y x in printf "assert: y > x\n"; Z3.assert_cnstr ctx c3; (* the context is still consistent. *) check2 ctx Z3.L_TRUE; Z3.del_context ctx; end;; (** Prove that {e f(x, y) = f(w, v) implies y = v } when {e f } is injective in the second argument. *) let quantifier_example1() = begin printf "\nquantifier_example1\n"; (* If quantified formulas are asserted in a logical context, then Z3 may return L_UNDEF. In this case, the model produced by Z3 should be viewed as a potential/candidate model. Limiting the number of iterations of the model finder for quantified formulas. *) let ctx = mk_context [|("MBQI_MAX_ITERATIONS", "10")|] in (* declare function f *) let int_sort = Z3.mk_int_sort ctx in let f_name = Z3.mk_string_symbol ctx "f" in let f = Z3.mk_func_decl ctx f_name [|int_sort; int_sort|] int_sort in (* assert that f is injective in the second argument. *) assert_inj_axiom ctx f 1; (* create x, y, v, w, fxy, fwv *) let x = mk_int_var ctx "x" in let y = mk_int_var ctx "y" in let v = mk_int_var ctx "v" in let w = mk_int_var ctx "w" in let fxy = mk_binary_app ctx f x y in let fwv = mk_binary_app ctx f w v in (* assert f(x, y) = f(w, v) *) let p1 = Z3.mk_eq ctx fxy fwv in Z3.assert_cnstr ctx p1; (* prove f(x, y) = f(w, v) implies y = v *) let p2 = Z3.mk_eq ctx y v in printf "prove: f(x, y) = f(w, v) implies y = v\n"; prove ctx p2 true; (* disprove f(x, y) = f(w, v) implies x = w *) (* using check2 instead of prove *) let p3 = Z3.mk_eq ctx x w in let not_p3 = Z3.mk_not ctx p3 in Z3.assert_cnstr ctx not_p3; printf "disprove: f(x, y) = f(w, v) implies x = w\n"; printf "that is: not(f(x, y) = f(w, v) implies x = w) is satisfiable\n"; check2 ctx Z3.L_UNDEF; Printf.printf "reason for last failure: %d (7 = quantifiers)\n" (if Z3.get_search_failure(ctx) = Z3.QUANTIFIERS then 7 else -1); Z3.del_context ctx; end;; (** Prove {e store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)) }. This example demonstrates how to use the array theory. *) let array_example1() = begin printf "\narray_example1\n"; let ctx = mk_context [||] in let int_sort = Z3.mk_int_sort ctx in let array_sort = Z3.mk_array_sort ctx int_sort int_sort in let a1 = mk_var ctx "a1" array_sort in let a2 = mk_var ctx "a2" array_sort in let i1 = mk_var ctx "i1" int_sort in let i2 = mk_var ctx "i2" int_sort in let i3 = mk_var ctx "i3" int_sort in let v1 = mk_var ctx "v1" int_sort in let v2 = mk_var ctx "v2" int_sort in let st1 = Z3.mk_store ctx a1 i1 v1 in let st2 = Z3.mk_store ctx a2 i2 v2 in let sel1 = Z3.mk_select ctx a1 i3 in let sel2 = Z3.mk_select ctx a2 i3 in (* create antecedent *) let antecedent = Z3.mk_eq ctx st1 st2 in (* create consequent: i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3) *) let ds = [| Z3.mk_eq ctx i1 i3; Z3.mk_eq ctx i2 i3; Z3.mk_eq ctx sel1 sel2; |] in let consequent = Z3.mk_or ctx ds in (* prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)) *) let thm = Z3.mk_implies ctx antecedent consequent in printf "prove: store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3))\n"; printf "%s\n" (Z3.ast_to_string ctx thm); prove ctx thm true; Z3.del_context ctx; end;; (** Show that {e distinct(a_0, ... , a_n) } is unsatisfiable when {e a_i's } are arrays from boolean to boolean and n > 4. This example also shows how to use the {e distinct } construct. *) let array_example2() = begin printf "\narray_example2\n"; for n = 2 to 5 do printf "n = %d\n" n; let ctx = mk_context [||] in let bool_sort = Z3.mk_bool_sort ctx in let array_sort = Z3.mk_array_sort ctx bool_sort bool_sort in (* create arrays *) let a = Array.init n (fun i->Z3.mk_const ctx (Z3.mk_int_symbol ctx i) array_sort) in (* assert distinct(a[0], ..., a[n]) *) let d = Z3.mk_distinct ctx a in printf "%s\n" (Z3.ast_to_string ctx d); Z3.assert_cnstr ctx d; (* context is satisfiable if n < 5 *) check2 ctx (if n < 5 then Z3.L_TRUE else Z3.L_FALSE); Z3.del_context ctx; done end;; (** Simple array type construction/deconstruction example. *) let array_example3() = begin printf "\narray_example3\n"; let ctx = mk_context [||] in let bool_sort = Z3.mk_bool_sort ctx in let int_sort = Z3.mk_int_sort ctx in let array_sort = Z3.mk_array_sort ctx int_sort bool_sort in let (domain,range) = Z3.get_array_sort ctx array_sort in printf "domain: "; display_sort ctx stdout domain; printf "\n"; printf "range: "; display_sort ctx stdout range; printf "\n"; if (not (Z3.is_eq_sort ctx int_sort domain)) || (not (Z3.is_eq_sort ctx bool_sort range)) then exitf "invalid array type"; Z3.del_context ctx; end;; (** Simple tuple type example. It creates a tuple that is a pair of real numbers. *) let tuple_example1() = begin printf "\ntuple_example1\n"; let ctx = mk_context [||] in let real_sort = Z3.mk_real_sort ctx in (* Create pair (tuple) type *) let mk_tuple_name = Z3.mk_string_symbol ctx "mk_pair" in let proj_names_0 = Z3.mk_string_symbol ctx "get_x" in let proj_names_1 = Z3.mk_string_symbol ctx "get_y" in let proj_names = [|proj_names_0; proj_names_1|] in let proj_sorts = [|real_sort;real_sort|] in (* Z3_mk_tuple_sort will set mk_tuple_decl and proj_decls *) let (pair_sort,mk_tuple_decl,proj_decls) = Z3.mk_tuple_sort ctx mk_tuple_name proj_names proj_sorts in let get_x_decl = proj_decls.(0) in (* function that extracts the first element of a tuple. *) let get_y_decl = proj_decls.(1) in (* function that extracts the second element of a tuple. *) printf "tuple_sort: "; display_sort ctx stdout pair_sort; printf "\n"; begin (* prove that get_x(mk_pair(x,y)) == 1 implies x = 1*) let x = mk_real_var ctx "x" in let y = mk_real_var ctx "y" in let app1 = mk_binary_app ctx mk_tuple_decl x y in let app2 = mk_unary_app ctx get_x_decl app1 in let one = Z3.mk_numeral ctx "1" real_sort in let eq1 = Z3.mk_eq ctx app2 one in let eq2 = Z3.mk_eq ctx x one in let thm = Z3.mk_implies ctx eq1 eq2 in printf "prove: get_x(mk_pair(x, y)) = 1 implies x = 1\n"; prove ctx thm true; (* disprove that get_x(mk_pair(x,y)) == 1 implies y = 1*) let eq3 = Z3.mk_eq ctx y one in let thm = Z3.mk_implies ctx eq1 eq3 in printf "disprove: get_x(mk_pair(x, y)) = 1 implies y = 1\n"; prove ctx thm false; end; begin (* prove that get_x(p1) = get_x(p2) and get_y(p1) = get_y(p2) implies p1 = p2 *) let p1 = mk_var ctx "p1" pair_sort in let p2 = mk_var ctx "p2" pair_sort in let x1 = mk_unary_app ctx get_x_decl p1 in let y1 = mk_unary_app ctx get_y_decl p1 in let x2 = mk_unary_app ctx get_x_decl p2 in let y2 = mk_unary_app ctx get_y_decl p2 in let antecedents_0 = Z3.mk_eq ctx x1 x2 in let antecedents_1 = Z3.mk_eq ctx y1 y2 in let antecedents = [|antecedents_0; antecedents_1|] in let antecedent = Z3.mk_and ctx antecedents in let consequent = Z3.mk_eq ctx p1 p2 in let thm = Z3.mk_implies ctx antecedent consequent in printf "prove: get_x(p1) = get_x(p2) and get_y(p1) = get_y(p2) implies p1 = p2\n"; prove ctx thm true; (* disprove that get_x(p1) = get_x(p2) implies p1 = p2 *) let thm = Z3.mk_implies ctx (antecedents.(0)) consequent in printf "disprove: get_x(p1) = get_x(p2) implies p1 = p2\n"; prove ctx thm false; end; begin (* demonstrate how to use the mk_tuple_update function *) (* prove that p2 = update(p1, 0, 10) implies get_x(p2) = 10 *) let p1 = mk_var ctx "p1" pair_sort in let p2 = mk_var ctx "p2" pair_sort in let one = Z3.mk_numeral ctx "1" real_sort in let ten = Z3.mk_numeral ctx "10" real_sort in let updt = mk_tuple_update ctx p1 0 ten in let antecedent = Z3.mk_eq ctx p2 updt in let x = mk_unary_app ctx get_x_decl p2 in let consequent = Z3.mk_eq ctx x ten in let thm = Z3.mk_implies ctx antecedent consequent in printf "prove: p2 = update(p1, 0, 10) implies get_x(p2) = 10\n"; prove ctx thm true; (* disprove that p2 = update(p1, 0, 10) implies get_y(p2) = 10 *) let y = mk_unary_app ctx get_y_decl p2 in let consequent = Z3.mk_eq ctx y ten in let thm = Z3.mk_implies ctx antecedent consequent in printf "disprove: p2 = update(p1, 0, 10) implies get_y(p2) = 10\n"; prove ctx thm false; end; Z3.del_context ctx; end;; (** Simple bit-vector example. This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers *) let bitvector_example1() = begin printf "\nbitvector_example1\n"; let ctx = mk_context [||] in let bv_sort = Z3.mk_bv_sort ctx 32 in let x = mk_var ctx "x" bv_sort in let zero = Z3.mk_numeral ctx "0" bv_sort in let ten = Z3.mk_numeral ctx "10" bv_sort in let x_minus_ten = Z3.mk_bvsub ctx x ten in (* bvsle is signed less than or equal to *) let c1 = Z3.mk_bvsle ctx x ten in let c2 = Z3.mk_bvsle ctx x_minus_ten zero in let thm = Z3.mk_iff ctx c1 c2 in printf "disprove: x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers\n"; prove ctx thm false; Z3.del_context ctx; end;; (** Find x and y such that: x ^ y - 103 == x * y *) let bitvector_example2() = begin printf "\nbitvector_example2\n"; let ctx = mk_context [||] in (* construct x ^ y - 103 == x * y *) let bv_sort = Z3.mk_bv_sort ctx 32 in let x = mk_var ctx "x" bv_sort in let y = mk_var ctx "y" bv_sort in let x_xor_y = Z3.mk_bvxor ctx x y in let c103 = Z3.mk_numeral ctx "103" bv_sort in let lhs = Z3.mk_bvsub ctx x_xor_y c103 in let rhs = Z3.mk_bvmul ctx x y in let ctr = Z3.mk_eq ctx lhs rhs in printf "find values of x and y, such that x ^ y - 103 == x * y\n"; Z3.assert_cnstr ctx ctr; check ctx Z3.L_TRUE; Z3.del_context ctx; end;; (** Demonstrate how to use #Z3_eval. *) let eval_example1() = begin printf "\neval_example1\n"; let ctx = mk_context [||] in let x = mk_int_var ctx "x" in let y = mk_int_var ctx "y" in let two = mk_int ctx 2 in (* assert x < y *) let c1 = Z3.mk_lt ctx x y in Z3.assert_cnstr ctx c1; (* assert x > 2 *) let c2 = Z3.mk_gt ctx x two in Z3.assert_cnstr ctx c2; (* find model for the constraints above *) (match Z3.check_and_get_model ctx with | (Z3.L_TRUE, m) -> begin let args = [|x; y|] in printf "MODEL:\n%s" (Z3.model_to_string ctx m); let x_plus_y = Z3.mk_add ctx args in printf "\nevaluating x+y\n"; (match Z3.eval ctx m x_plus_y with | (true,v) -> printf "result = "; display_ast ctx stdout v; printf "\n"; | _ -> exitf "failed to evaluate: x+y"; ); (Z3.del_model ctx m); end; | (_,_) -> exitf "the constraints are satisfiable"; ); Z3.del_context ctx; end;; (** Several logical context can be used simultaneously. *) let two_contexts_example1() = begin printf "\ntwo_contexts_example1\n"; (* using the same (default) configuration to initialized both logical contexts. *) let ctx1 = mk_context [||] in let ctx2 = mk_context [||] in let x1 = Z3.mk_const ctx1 (Z3.mk_int_symbol ctx1 0) (Z3.mk_bool_sort ctx1) in let x2 = Z3.mk_const ctx2 (Z3.mk_int_symbol ctx2 0) (Z3.mk_bool_sort ctx2) in Z3.del_context ctx1; (* ctx2 can still be used. *) printf "%s\n" (Z3.ast_to_string ctx2 x2); Z3.del_context ctx2; end;; (** Demonstrates how error codes can be read insted of registering an error handler. *) let error_code_example1() = begin printf "\nerror_code_example1\n"; let ctx = mk_context [||] in let x = mk_bool_var ctx "x" in let x_decl = Z3.get_app_decl ctx (Z3.to_app ctx x) in Z3.assert_cnstr ctx x; match Z3.check_and_get_model ctx with | (Z3.L_TRUE,m) -> begin let (ok, v) = Z3.eval_func_decl ctx m x_decl in printf "last call succeeded.\n"; (* The following call will fail since the value of x is a boolean *) (try ignore(Z3.get_numeral_string ctx v) with | _ -> printf "last call failed.\n"); (Z3.del_model ctx m); Z3.del_context ctx; end | (_,_) -> exitf "unexpected result"; end;; (** Demonstrates how Z3 exceptions can be used. *) let error_code_example2() = begin printf "\nerror_code_example2\n%!"; let ctx = mk_context [||] in try let x = mk_int_var ctx "x" in let y = mk_bool_var ctx "y" in printf "before Z3_mk_iff\n"; let app = Z3.mk_iff ctx x y in printf "unreachable"; with | _ -> printf "Z3 error: type error.\n"; Z3.del_context ctx; end;; (** Demonstrates how to use the SMTLIB parser. *) let parser_example1() = begin printf "\nparser_example1\n"; let ctx = mk_context [||] in let str = "(benchmark tst :extrafuns ((x Int) (y Int)) :formula (> x y) :formula (> x 0))" in let (formulas,_,_) = Z3.parse_smtlib_string_x ctx str [||] [||] [||] [||] in let f i c = printf "formula %d: %s\n" i (Z3.ast_to_string ctx c); Z3.assert_cnstr ctx c; in Array.iteri f formulas; check ctx Z3.L_TRUE; Z3.del_context ctx; end;; (** Demonstrates how to initialize the parser symbol table. *) let parser_example2() = begin printf "\nparser_example2\n%!"; let ctx = mk_context [||] in let x = mk_int_var ctx "x" in let x_decl = Z3.get_app_decl ctx (Z3.to_app ctx x) in let y = mk_int_var ctx "y" in let y_decl = Z3.get_app_decl ctx (Z3.to_app ctx y) in let decls = [| x_decl; y_decl |] in let a = Z3.mk_string_symbol ctx "a" in let b = Z3.mk_string_symbol ctx "b" in let names = [| a; b |] in let str = "(benchmark tst :formula (> a b))" in let f = Z3.parse_smtlib_string_formula ctx str [||] [||] names decls in printf "formula: %s\n" (Z3.ast_to_string ctx f); Z3.assert_cnstr ctx f; check ctx Z3.L_TRUE; Z3.del_context ctx; end;; (** Demonstrates how to initialize the parser symbol table. *) let parser_example3() = begin printf "\nparser_example3\n%!"; let ctx = mk_context [| |] in let int_sort = Z3.mk_int_sort ctx in let g_name = Z3.mk_string_symbol ctx "g" in let g = Z3.mk_func_decl ctx g_name [| int_sort; int_sort |] int_sort in let str = "(benchmark tst :formula (forall (x Int) (y Int) (implies (= x y) (= (g x 0) (g 0 y)))))" in assert_comm_axiom ctx g; let thm = Z3.parse_smtlib_string_formula ctx str [||] [||] [|g_name|] [|g|] in printf "formula: %s\n" (Z3.ast_to_string ctx thm); prove ctx thm true; Z3.del_context ctx; end;; (** Display the declarations, assumptions and formulas in a SMT-LIB string. *) let parser_example4() = begin printf "\nparser_example4\n%!"; let ctx = mk_context [||] in let str = "(benchmark tst :extrafuns ((x Int) (y Int)) :assumption (= x 20) :formula (> x y) :formula (> x 0))" in (* arithmetic theory is automatically initialized, when an int/real variable or arith operation is created using the API. Since no such function is invoked in this example, we should do that manually. *) let (formulas, assumptions, decls) = Z3.parse_smtlib_string_x ctx str [||] [||] [||] [||] in let f prefix i n = printf "%s %d: %s\n" prefix i (Z3.ast_to_string ctx n) in Array.iteri (fun i n -> printf "declaration %d: %s\n" i (Z3.func_decl_to_string ctx n)) decls; Array.iteri (f "assumption") assumptions; Array.iteri (f "formula") formulas; Z3.del_context ctx; end;; (** Demonstrates how to handle parser errors using Z3 exceptions. *) let parser_example5() = begin printf "\nparser_example5\n"; let ctx = mk_context [||] in try (* the following string has a parsing error: missing parenthesis *) let str = "(benchmark tst :extrafuns ((x Int (y Int)) :formula (> x y) :formula (> x 0))" in ignore(Z3.parse_smtlib_string_x ctx str [||] [||] [||] [||]); with | _ -> (printf "Z3 error: parser error.\n"; printf "Error message: '%s'.\n" (Z3.get_smtlib_error ctx) ); Z3.del_context ctx; end;; (** Example for creating an if-then-else expression. *) let ite_example() = begin printf "\nite_example\n%!"; let ctx = mk_context [||] in let f = Z3.mk_false ctx in let one = mk_int ctx 1 in let zero = mk_int ctx 0 in let ite = Z3.mk_ite ctx f one zero in printf "term: %s\n" (Z3.ast_to_string ctx ite); Z3.del_context ctx; end;; (** Create an enumeration data type. Several more examples of creating and using data-types (lists, trees, records) are provided for the C-based API. The translation from the examples in C to use the OCaml API follow the same pattern that is used here. *) let enum_example() = begin printf "\nenum_example\n"; let ctx = mk_context [||] in let name = Z3.mk_string_symbol ctx "fruit" in let enum_names = [| Z3.mk_string_symbol ctx "apple"; Z3.mk_string_symbol ctx "banana"; Z3.mk_string_symbol ctx "orange" |] in let (fruit, enum_consts, enum_testers) = Z3.mk_enumeration_sort ctx name enum_names in printf "%s\n" (Z3.func_decl_to_string ctx enum_consts.(0)); printf "%s\n" (Z3.func_decl_to_string ctx enum_consts.(1)); printf "%s\n" (Z3.func_decl_to_string ctx enum_consts.(2)); printf "%s\n" (Z3.func_decl_to_string ctx enum_testers.(0)); printf "%s\n" (Z3.func_decl_to_string ctx enum_testers.(1)); printf "%s\n" (Z3.func_decl_to_string ctx enum_testers.(2)); let apple = Z3.mk_app ctx (enum_consts.(0)) [||] in let banana = Z3.mk_app ctx (enum_consts.(1)) [||] in let orange = Z3.mk_app ctx (enum_consts.(2)) [||] in (* Apples are different from oranges *) prove ctx (Z3.mk_not ctx (Z3.mk_eq ctx apple orange)) true; (* Apples pass the apple test *) prove ctx (Z3.mk_app ctx enum_testers.(0) [| apple |]) true; (* Oranges fail the apple test *) prove ctx (Z3.mk_app ctx enum_testers.(0) [| orange |]) false; prove ctx (Z3.mk_not ctx (Z3.mk_app ctx enum_testers.(0) [| orange |])) true; let fruity = mk_var ctx "fruity" fruit in (* If something is fruity, then it is an apple, banana, or orange *) let ors = [| Z3.mk_eq ctx fruity apple; Z3.mk_eq ctx fruity banana; Z3.mk_eq ctx fruity orange |] in prove ctx (Z3.mk_or ctx ors) true; Z3.del_context ctx; end;; (** Example for extracting unsatisfiable core and proof. The example uses the function check_assumptions which allows passing in additional hypotheses. The unsatisfiable core is a subset of these additional hypotheses. *) let unsat_core_and_proof_example() = begin printf "\nunsat_core_and_proof_example\n%!"; let ctx = mk_context [| ("PROOF_MODE","2") |] in let pa = mk_bool_var ctx "PredA" in let pb = mk_bool_var ctx "PredB" in let pc = mk_bool_var ctx "PredC" in let pd = mk_bool_var ctx "PredD" in let p1 = mk_bool_var ctx "P1" in let p2 = mk_bool_var ctx "P2" in let p3 = mk_bool_var ctx "P3" in let p4 = mk_bool_var ctx "P4" in let assumptions = [| Z3.mk_not ctx p1; Z3.mk_not ctx p2; Z3.mk_not ctx p3; Z3.mk_not ctx p4 |] in let f1 = Z3.mk_and ctx [| pa; pb; pc |] in let f2 = Z3.mk_and ctx [| pa; Z3.mk_not ctx pc; pb |] in let f3 = Z3.mk_or ctx [| Z3.mk_not ctx pa; Z3.mk_not ctx pc |] in let f4 = pd in let core_dummy = [| pa; pa; pa; pa |] in Z3.assert_cnstr ctx (Z3.mk_or ctx [| f1; p1 |]); Z3.assert_cnstr ctx (Z3.mk_or ctx [| f2; p2 |]); Z3.assert_cnstr ctx (Z3.mk_or ctx [| f3; p3 |]); Z3.assert_cnstr ctx (Z3.mk_or ctx [| f4; p4 |]); let result = Z3.check_assumptions ctx assumptions 4 core_dummy in (match result with | (Z3.L_FALSE, _, proof, core_size, core) -> printf "unsat\n"; printf "proof: %s\n" (Z3.ast_to_string ctx proof); printf("\ncore:\n"); for i = 0 to core_size - 1 do printf "%s\n" (Z3.ast_to_string ctx (core.(i))); done; printf("\n") | (_,_,_,_,_)-> assert false; ); (* delete logical context *) Z3.del_context(ctx); end (** *) let get_implied_equalities_example() = begin printf "\nget_implied_equalities example\n%!"; let ctx = mk_context [| |] in let int_ty = Z3.mk_int_sort ctx in let a = mk_int_var ctx "a" in let b = mk_int_var ctx "b" in let c = mk_int_var ctx "c" in let d = mk_int_var ctx "d" in let f = Z3.mk_func_decl ctx (Z3.mk_string_symbol ctx "f") [| int_ty |] int_ty in let fa = Z3.mk_app ctx f [| a |] in let fb = Z3.mk_app ctx f [| b |] in let fc = Z3.mk_app ctx f [| c |] in let terms = [| a; b; c; d; fa; fb; fc |] in Z3.assert_cnstr ctx (Z3.mk_eq ctx a b); Z3.assert_cnstr ctx (Z3.mk_eq ctx b c); Z3.assert_cnstr ctx (Z3.mk_le ctx fc b); Z3.assert_cnstr ctx (Z3.mk_le ctx b fa); let is_sat, class_ids = Z3.get_implied_equalities ctx terms in for i = 0 to 6 do printf "Class %s |-> %d\n" (Z3.ast_to_string ctx (terms.(i))) (class_ids.(i)); done; printf "asserting f(a) <= b\n"; Z3.assert_cnstr ctx (Z3.mk_le ctx fa b); let is_sat, class_ids = Z3.get_implied_equalities ctx terms in for i = 0 to 6 do printf "Class %s |-> %d\n" (Z3.ast_to_string ctx (terms.(i))) (class_ids.(i)); done; (* delete logical context *) Z3.del_context(ctx) end;; let main() = begin ignore (Z3.open_log("ml.log")); display_version(); simple_example(); demorgan(); find_model_example1(); find_model_example2(); prove_example1(); prove_example2(); push_pop_example1(); quantifier_example1(); array_example1(); array_example2(); array_example3(); tuple_example1(); bitvector_example1(); bitvector_example2(); eval_example1(); two_contexts_example1(); error_code_example1(); error_code_example2(); parser_example1(); parser_example2(); parser_example3(); parser_example4(); parser_example5(); (* numeral_example(); *) ite_example(); (* list_example(); *) (* tree_example(); *) (* forest_example(); *) (* binary_tree_example(); *) enum_example(); unsat_core_and_proof_example(); abstract_example(); get_implied_equalities_example(); (* incremental_example1(); *) (* reference_counter_example(); *) (* smt2parser_example(); *) (* substitute_example(); *) (* substitute_vars_example(); *) end;; let _ = main();;