mirror of
https://github.com/Z3Prover/z3
synced 2025-04-06 17:44:08 +00:00
210 lines
7.1 KiB
OCaml
210 lines
7.1 KiB
OCaml
(** Examples of using the OCaml API for Z3. *)
|
|
|
|
(**/**)
|
|
(* pause documentation *)
|
|
|
|
(*
|
|
@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 and print datatypes
|
|
*)
|
|
let mk_datatypes ctx generator =
|
|
let datatypes = Z3.mk_datatypes ctx generator in
|
|
printf "datatype created:\n%!" ;
|
|
Array.iter (fun (sort, ctors) ->
|
|
printf "sort: %s\n%!" (Z3.sort_to_string ctx sort) ;
|
|
Array.iter (fun {Z3.constructor; recognizer; accessors} ->
|
|
printf "constructor: %s%! recognizer: %s%! accessors:"
|
|
(Z3.func_decl_to_string ctx constructor)
|
|
(Z3.func_decl_to_string ctx recognizer) ;
|
|
Array.iter (fun accessor ->
|
|
printf " %s%!" (Z3.func_decl_to_string ctx accessor)
|
|
) accessors ;
|
|
printf "\n"
|
|
) ctors
|
|
) datatypes ;
|
|
printf "\n" ;
|
|
datatypes
|
|
;;
|
|
|
|
|
|
(**
|
|
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
|
|
;;
|
|
|
|
(* resume documentation *)
|
|
(**/**)
|
|
|
|
|
|
(**
|
|
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 slv f is_valid =
|
|
(* save the current state of the context *)
|
|
Z3.solver_push ctx slv ;
|
|
|
|
let not_f = Z3.mk_not ctx f in
|
|
Z3.solver_assert ctx slv not_f ;
|
|
|
|
(match Z3.solver_check ctx slv with
|
|
| Z3.L_FALSE ->
|
|
(* proved *)
|
|
printf "valid\n" ;
|
|
if not is_valid then exitf "unexpected result"
|
|
| Z3.L_UNDEF ->
|
|
(* Z3 failed to prove/disprove f. *)
|
|
printf "unknown\n" ;
|
|
let m = Z3.solver_get_model ctx slv in
|
|
(* 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.L_TRUE ->
|
|
(* disproved *)
|
|
printf "invalid\n" ;
|
|
let m = Z3.solver_get_model ctx slv in
|
|
(* 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"
|
|
);
|
|
(* restore context *)
|
|
Z3.solver_pop ctx slv 1
|
|
;;
|
|
|
|
|
|
(* n-ary trees and forests in OCaml *)
|
|
type tree = Leaf of int | Node of forest
|
|
and forest = tree list
|
|
|
|
(**
|
|
Demonstrates the usage of {!Z3.mk_datatypes} with an example of forests of trees.
|
|
*)
|
|
let forest_example () =
|
|
let ctx = Z3.mk_context [] in
|
|
let slv = Z3.mk_solver ctx
|
|
in
|
|
let int_sort = Z3.mk_int_sort ctx in
|
|
let sym name = Z3.mk_string_symbol ctx name
|
|
in
|
|
(* n-ary trees and forests in Z3 *)
|
|
match
|
|
mk_datatypes ctx
|
|
(function [|tree; forest|] -> Some
|
|
[|(sym"tree",
|
|
[|{Z3.constructor_desc= sym"leaf"; recognizer_desc= sym"is_leaf"; accessor_descs= [|(sym"data", int_sort)|]};
|
|
{Z3.constructor_desc= sym"node"; recognizer_desc= sym"is_node"; accessor_descs= [|(sym"children", forest)|]}|]);
|
|
(sym"forest",
|
|
[|{Z3.constructor_desc= sym"nil" ; recognizer_desc= sym"is_nil" ; accessor_descs= [||]};
|
|
{Z3.constructor_desc= sym"cons"; recognizer_desc= sym"is_cons"; accessor_descs= [|(sym"hd", tree); (sym"tl", forest)|]}|])|]
|
|
| _ -> None
|
|
)
|
|
with
|
|
[|(tree,
|
|
[|{Z3.constructor= leaf; recognizer= is_leaf; accessors= [|data|]};
|
|
{Z3.constructor= node; recognizer= is_node; accessors= [|children|]}|]);
|
|
(forest,
|
|
[|{Z3.constructor= nil ; recognizer= is_nil ; accessors= [||]};
|
|
{Z3.constructor= cons; recognizer= is_cons; accessors= [|hd; tl|]}|])|]
|
|
->
|
|
(* translate from OCaml to Z3 *)
|
|
let rec ml2z3_tree = function
|
|
| Leaf(i) -> Z3.mk_app ctx leaf [|Z3.mk_int ctx i (Z3.mk_int_sort ctx)|]
|
|
| Node(f) -> Z3.mk_app ctx node [|ml2z3_forest f|]
|
|
|
|
and ml2z3_forest = function
|
|
| [] -> Z3.mk_app ctx nil [||]
|
|
| t :: f -> Z3.mk_app ctx cons [|ml2z3_tree t; ml2z3_forest f|]
|
|
in
|
|
|
|
(* construct some OCaml trees *)
|
|
let t0 = Leaf 0 in
|
|
let t12 = Node [Leaf 1; Leaf 2] in
|
|
let t123 = Node [t12; Leaf 3] in
|
|
let t1212 = Node [t12; t12] in
|
|
let t412 = Node [Leaf 4; t12] in
|
|
|
|
(* construct some Z3 trees using the translation from OCaml *)
|
|
let t1 = ml2z3_tree t12 in printf "t1: %s\n%!" (Z3.ast_to_string ctx t1) ;
|
|
let t2 = ml2z3_tree t123 in printf "t2: %s\n%!" (Z3.ast_to_string ctx t2) ;
|
|
let t3 = ml2z3_tree t1212 in printf "t3: %s\n%!" (Z3.ast_to_string ctx t3) ;
|
|
let t4 = ml2z3_tree t412 in printf "t4: %s\n%!" (Z3.ast_to_string ctx t4) ;
|
|
let f1 = ml2z3_forest [t0] in printf "f1: %s\n%!" (Z3.ast_to_string ctx f1) ;
|
|
let f2 = ml2z3_forest [t12] in printf "f2: %s\n%!" (Z3.ast_to_string ctx f2) ;
|
|
let f3 = ml2z3_forest [t12; t0] in printf "f3: %s\n%!" (Z3.ast_to_string ctx f3) ;
|
|
|
|
(* or using the Z3 API *)
|
|
let nil = Z3.mk_app ctx nil [||] in
|
|
let cons t f = Z3.mk_app ctx cons [|t; f|] in
|
|
let leaf i = Z3.mk_app ctx leaf [|Z3.mk_int ctx i (Z3.mk_int_sort ctx)|] in
|
|
let node f = Z3.mk_app ctx node [|f|] in
|
|
|
|
let t0 = leaf 0 in
|
|
let t12 = node (cons (leaf 1) (cons (leaf 2) nil)) in
|
|
let t123 = node (cons t12 (cons (leaf 3) nil)) in
|
|
let t1212 = node (cons t12 (cons t12 nil)) in
|
|
let t412 = node (cons (leaf 4) (cons t12 nil)) in
|
|
|
|
let t1 = t12 in printf "t1: %s\n%!" (Z3.ast_to_string ctx t1) ;
|
|
let t2 = t123 in printf "t2: %s\n%!" (Z3.ast_to_string ctx t2) ;
|
|
let t3 = t1212 in printf "t3: %s\n%!" (Z3.ast_to_string ctx t3) ;
|
|
let t4 = t412 in printf "t4: %s\n%!" (Z3.ast_to_string ctx t4) ;
|
|
let f1 = cons t0 nil in printf "f1: %s\n%!" (Z3.ast_to_string ctx f1) ;
|
|
let f2 = cons t12 nil in printf "f2: %s\n%!" (Z3.ast_to_string ctx f2) ;
|
|
let f3 = cons t12 f1 in printf "f3: %s\n%!" (Z3.ast_to_string ctx f3) ;
|
|
|
|
(* nil != cons(nil,nil) *)
|
|
prove ctx slv (Z3.mk_not ctx (Z3.mk_eq ctx nil f1)) true ;
|
|
prove ctx slv (Z3.mk_not ctx (Z3.mk_eq ctx (leaf 5) t1)) true ;
|
|
|
|
(* cons(x,u) = cons(x, v) => u = v *)
|
|
let u = mk_var ctx "u" forest in
|
|
let v = mk_var ctx "v" forest in
|
|
let x = mk_var ctx "x" tree in
|
|
let y = mk_var ctx "y" tree in
|
|
let l1 = cons x u in printf "l1: %s\n%!" (Z3.ast_to_string ctx l1) ;
|
|
let l2 = cons y v in printf "l2: %s\n%!" (Z3.ast_to_string ctx l2) ;
|
|
|
|
prove ctx slv (Z3.mk_implies ctx (Z3.mk_eq ctx l1 l2) (Z3.mk_eq ctx u v)) true ;
|
|
prove ctx slv (Z3.mk_implies ctx (Z3.mk_eq ctx l1 l2) (Z3.mk_eq ctx x y)) true ;
|
|
|
|
(* is_nil(u) or is_cons(u) *)
|
|
prove ctx slv (Z3.mk_or ctx [|Z3.mk_app ctx is_nil [|u|]; Z3.mk_app ctx is_cons [|u|]|]) true ;
|
|
|
|
(* occurs check u != cons(x,u) *)
|
|
prove ctx slv (Z3.mk_not ctx (Z3.mk_eq ctx u l1)) true ;
|
|
|
|
| _ ->
|
|
exitf "unexpected datatype signature"
|
|
;;
|
|
|
|
let _ =
|
|
ignore( Z3.open_log "test_mlapi.log" );
|
|
forest_example () ;
|
|
;;
|