diff --git a/bin/dune b/bin/dune index 0fe64358fdda03515cf58ec8dfa738cdfc87575b..45f3f6d1325e66f8f6a675268bdd0f51ebf1192d 100644 --- a/bin/dune +++ b/bin/dune @@ -3,5 +3,4 @@ (section bin) (files (findmodule.py as findmodule.py) - (nnenum.sh as nnenum.sh)) -) + (nnenum.sh as nnenum.sh))) diff --git a/lib/onnx/dune b/lib/onnx/dune index 0ced853d1999ef061703a71b0346d21bd8a6cc26..ae47764e9d1ef1fc911e72084072b4ab02690fd4 100644 --- a/lib/onnx/dune +++ b/lib/onnx/dune @@ -5,7 +5,10 @@ (synopsis "ONNX parser for CAISAR")) (rule - (deps onnx_protoc.proto generate_onnx_interface.sh (package ocaml-protoc-plugin)) + (deps + onnx_protoc.proto + generate_onnx_interface.sh + (package ocaml-protoc-plugin)) (targets onnx_protoc.ml) (action (run ./generate_onnx_interface.sh))) diff --git a/src/interpretation.ml b/src/interpretation.ml new file mode 100644 index 0000000000000000000000000000000000000000..eb1cdc00bf9db887ab74764ad998bcd5b912beca --- /dev/null +++ b/src/interpretation.ml @@ -0,0 +1,128 @@ +(**************************************************************************) +(* *) +(* This file is part of CAISAR. *) +(* *) +(* Copyright (C) 2022 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* You can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +module CRE = Reduction_engine (* Caisar Reduction Engine *) +open Why3 +open Base + +type dataset = { dataset : string } [@@deriving show] +type caisar_op = Dataset of dataset [@@deriving show] + +type caisar_env = { + dataset_ty : Ty.ty; + caisar_op_of_ls : caisar_op Term.Hls.t; + ls_of_caisar_op : (caisar_op, Term.lsymbol) Hashtbl.t; + cwd : string; +} + +let ls_of_caisar_op engine op = + let caisar_env = CRE.user_env engine in + Hashtbl.find_or_add caisar_env.ls_of_caisar_op op ~default:(fun () -> + let id = Ident.id_fresh "caisar_op" in + let ty = match op with Dataset _ -> caisar_env.dataset_ty in + let ls = Term.create_fsymbol id [] ty in + Hashtbl.Poly.add_exn caisar_env.ls_of_caisar_op ~key:op ~data:ls; + Term.Hls.add caisar_env.caisar_op_of_ls ls op; + ls) + +let caisar_op_of_ls engine ls = + let caisar_env = CRE.user_env engine in + Term.Hls.find caisar_env.caisar_op_of_ls ls + +let term_of_caisar_op engine op = + Term.t_app_infer (ls_of_caisar_op engine op) [] + +let caisar_env env cwd = + let th = Env.read_theory env [ "caisar" ] "Interpretation" in + let ts_dataset = Theory.ns_find_ts th.Theory.th_export [ "dataset" ] in + { + dataset_ty = Ty.ty_app ts_dataset []; + ls_of_caisar_op = Hashtbl.Poly.create (); + caisar_op_of_ls = Term.Hls.create 10; + cwd; + } + +let print_caisar_op fmt caisar_env = + Pp.print_iter2 Term.Hls.iter Pp.newline Pp.comma Pretty.print_ls pp_caisar_op + fmt caisar_env.caisar_op_of_ls + +let compute_size_of_dataset ~cwd s = + let d = Caml.Filename.concat cwd s in + Array.length (Caml.Sys.readdir d) + +let builtin_caisar : caisar_env CRE.built_in_theories list = + let open_dataset : _ CRE.builtin = + fun engine _ l _ -> + match l with + | [ Term { t_node = Tconst (ConstStr dataset); _ } ] -> + Term (term_of_caisar_op engine (Dataset { dataset })) + | _ -> invalid_arg "We want a string! ;)" + in + let size : _ CRE.builtin = + fun engine _ l _ -> + match l with + | [ Term { t_node = Tapp (ls, []); _ } ] -> ( + match caisar_op_of_ls engine ls with + | Dataset { dataset } -> + let cwd = (CRE.user_env engine).cwd in + Int (BigInt.of_int (compute_size_of_dataset ~cwd dataset))) + | _ -> invalid_arg "We want a string! ;)" + in + [ + ( [ "caisar" ], + "Interpretation", + [], + [ ("open_dataset", None, open_dataset); ("size", None, size) ] ); + ] + +let interpret_task ~cwd env task = + let known_map = Task.task_known task in + let caisar_env = caisar_env env cwd in + let params = + { + CRE.compute_defs = true; + compute_builtin = true; + compute_def_set = Term.Sls.empty; + compute_max_quantifier_domain = Int.max_value; + } + in + let engine = CRE.create params env known_map caisar_env builtin_caisar in + let f = Task.task_goal_fmla task in + let f = CRE.normalize ~limit:1000 engine Term.Mvs.empty f in + Fmt.pr "%a : %a@.%a@." Pretty.print_pr (Task.task_goal task) Pretty.print_term + f print_caisar_op caisar_env + +let interpret ?debug ?format ~loadpath file = + let cwd = + match file with + | Verification.File.Stdin -> Unix.getcwd () + | File s -> Caml.Filename.dirname s + | JSON _ -> Unix.getcwd () (*todo *) + in + let env, _config, mstr_theory = + Verification.open_file ?debug ?format ~loadpath file + in + Wstdlib.Mstr.iter + (fun _ theory -> + List.iter (Task.split_theory theory None None) ~f:(fun task -> + interpret_task ~cwd env task)) + mstr_theory diff --git a/src/interpretation.mli b/src/interpretation.mli new file mode 100644 index 0000000000000000000000000000000000000000..68649f274c7cb2c8bd730daad025bff68d8fb658 --- /dev/null +++ b/src/interpretation.mli @@ -0,0 +1,30 @@ +(**************************************************************************) +(* *) +(* This file is part of CAISAR. *) +(* *) +(* Copyright (C) 2022 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* You can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Base + +val interpret : + ?debug:bool -> + ?format:string -> + loadpath:string list -> + Verification.File.t -> + unit diff --git a/src/main.ml b/src/main.ml index 00b42e06141554673f595fd6ec5299bdb182573c..77a4933e0692a1e07c800a77cbf25f74970dd5ff 100644 --- a/src/main.ml +++ b/src/main.ml @@ -204,6 +204,10 @@ let verify_json ?memlimit ?timelimit ?outfile json = ~f:(record_verification_result jin.id verification_result) | _ -> failwith "Unexpected more than one theory from a JSON file" +let interpret format loadpath files = + let debug = log_level_is_debug () in + List.iter ~f:(Interpretation.interpret ~debug ?format ~loadpath) files + let exec_cmd cmdname cmd = Logs.debug (fun m -> m "Execution of command '%s'" cmdname); cmd () @@ -257,6 +261,19 @@ let timelimit = Arg.( value & opt (some string) None & info [ "t"; "timelimit" ] ~doc ~docv:"TIME") +let loadpath = + let doc = "Additional loadpath." in + Arg.(value & opt_all string [ "." ] & info [ "L"; "loadpath" ] ~doc) + +let format = + let doc = "File format." in + Arg.(value & opt (some string) None & info [ "format" ] ~doc) + +let files = + let doc = "File to verify." in + let file_or_stdin = Arg.conv' Verification.File.(of_string, pretty) in + Arg.(non_empty & pos_all file_or_stdin [] & info [] ~doc ~docv:"FILE") + let verify_cmd = let cmdname = "verify" in let info = @@ -265,14 +282,6 @@ let verify_cmd = ~doc ~man:[ `S Manpage.s_description; `P doc ] in - let loadpath = - let doc = "Additional loadpath." in - Arg.(value & opt_all string [ "." ] & info [ "L"; "loadpath" ] ~doc) - in - let format = - let doc = "File format." in - Arg.(value & opt (some string) None & info [ "format" ] ~doc) - in let prover = let all_provers = Prover.list_available () in let doc = @@ -297,11 +306,6 @@ let verify_cmd = let doc = "Dataset $(docv) (CSV format only)." in Arg.(value & opt (some file) None & info [ "dataset" ] ~doc ~docv:"FILE") in - let files = - let doc = "File to verify." in - let file_or_stdin = Arg.conv' Verification.File.(of_string, pretty) in - Arg.(non_empty & pos_all file_or_stdin [] & info [] ~doc ~docv:"FILE") - in let term = Term.( const @@ -355,6 +359,24 @@ let verify_json_cmd = in Cmd.v info term +let interpret_cmd = + let cmdname = "interpret" in + let doc = + "Interpret the goal and print the strategy that will be executed." + in + let info = + Cmd.info cmdname ~sdocs:Manpage.s_common_options ~exits:Cmd.Exit.defaults + ~doc + ~man:[ `S Manpage.s_description; `P doc ] + in + let term = + Term.( + const (fun format loadpath files _ -> + exec_cmd cmdname (fun () -> interpret format loadpath files)) + $ format $ loadpath $ files $ setup_logs) + in + Cmd.v info term + let default_info = let doc = "A platform for characterizing the safety and robustness of artificial \ @@ -389,7 +411,7 @@ let () = let () = try Cmd.group ~default:default_cmd default_info - [ config_cmd; verify_cmd; verify_json_cmd ] + [ config_cmd; verify_cmd; verify_json_cmd; interpret_cmd ] |> Cmd.eval ~catch:false |> Caml.exit with exn when not (log_level_is_debug ()) -> Logs.err (fun m -> m "@[%a@]" Why3.Exn_printer.exn_printer exn) diff --git a/src/reduction_engine.ml b/src/reduction_engine.ml new file mode 100644 index 0000000000000000000000000000000000000000..cba302c08b0f0147e61d47b1772113209aca8192 --- /dev/null +++ b/src/reduction_engine.ml @@ -0,0 +1,1399 @@ +(********************************************************************) +(* *) +(* The Why3 Verification Platform / The Why3 Development Team *) +(* Copyright 2010-2022 -- Inria - CNRS - Paris-Saclay University *) +(* *) +(* This software is distributed under the terms of the GNU Lesser *) +(* General Public License version 2.1, with the special exception *) +(* on linking described in file LICENSE. *) +(* *) +(********************************************************************) + +open Why3 +open Term + +[@@@ocaml.warning "-9"] + +let debug = Debug.register_info_flag ~desc:"" "Reduction_engine" + +(* {2 Values} *) + +type value = + | Term of term (* invariant: is in normal form *) + | Int of BigInt.t + | Real of Number.real_value + +(** {2 Environment} *) + +type rule = Svs.t * term list * term + +type params = { + compute_defs : bool; + compute_builtin : bool; + compute_def_set : Term.Sls.t; + compute_max_quantifier_domain : int; +} + +type 'a builtin = 'a engine -> lsymbol -> value list -> Ty.ty option -> value + +and 'a engine = { + env : Env.env; + known_map : Decl.decl Ident.Mid.t; + rules : rule list Mls.t; + params : params; + ls_lt : lsymbol; (* The lsymbol for [int.Int.(<)] *) + user_env : 'a; + builtins : 'a builtin Hls.t; +} + +let user_env x = x.user_env + +let v_attr_copy orig v = + match v with Int _ -> v | Real _ -> v | Term t -> Term (t_attr_copy orig t) + +let term_of_value v = + let open Number in + match v with + | Term t -> t + | Int n -> t_int_const n + | Real { rv_sig = s; rv_pow2 = pow2; rv_pow5 = pow5 } -> + t_real_const ~pow2 ~pow5 s + +exception NotNum + +let big_int_of_const c = + match c with + | Constant.ConstInt i -> i.Number.il_int + | Constant.ConstReal _ -> assert false + | Constant.ConstStr _ -> assert false + +let big_int_of_value v = + match v with + | Int n -> n + | Real _ -> assert false + | Term { t_node = Tconst c } -> big_int_of_const c + | Term _ -> raise NotNum + +let real_of_const c = + match c with + | Constant.ConstReal r -> r.Number.rl_real + | Constant.ConstInt _ -> assert false + | Constant.ConstStr _ -> assert false + +let real_of_value v = + match v with + | Real r -> r + | Int _ -> assert false + | Term { t_node = Tconst c } -> real_of_const c + | Term _ -> raise NotNum + +(* {2 Builtin symbols} *) + +(* all builtin functions *) + +exception Undetermined + +let to_bool b = if b then t_true else t_false + +let is_zero v = + try BigInt.eq (big_int_of_value v) BigInt.zero with NotNum -> false + +let is_one v = + try BigInt.eq (big_int_of_value v) BigInt.one with NotNum -> false + +let eval_int_op op simpl _ ls l ty = + match l with + | [ t1; t2 ] -> ( + try + let n1 = big_int_of_value t1 in + let n2 = big_int_of_value t2 in + Int (op n1 n2) + with NotNum | Division_by_zero -> simpl ls t1 t2 ty) + | _ -> assert false + +let simpl_add _ls t1 t2 _ty = + if is_zero t1 then t2 else if is_zero t2 then t1 else raise Undetermined + +let simpl_sub _ls t1 t2 _ty = if is_zero t2 then t1 else raise Undetermined + +let simpl_mul _ls t1 t2 _ty = + if is_zero t1 + then t1 + else if is_zero t2 + then t2 + else if is_one t1 + then t2 + else if is_one t2 + then t1 + else raise Undetermined + +let simpl_div _ls t1 t2 _ty = + if is_zero t2 then raise Undetermined; + if is_zero t1 then t1 else if is_one t2 then t1 else raise Undetermined + +let simpl_mod _ls t1 t2 _ty = + if is_zero t2 then raise Undetermined; + if is_zero t1 + then t1 + else if is_one t2 + then Int BigInt.zero + else raise Undetermined + +let simpl_minmax _ls v1 v2 _ty = + match (v1, v2) with + | Term t1, Term t2 -> if t_equal t1 t2 then v1 else raise Undetermined + | _ -> raise Undetermined + +let eval_int_rel op _ _ls l _ty = + match l with + | [ t1; t2 ] -> ( + try + let n1 = big_int_of_value t1 in + let n2 = big_int_of_value t2 in + Term (to_bool (op n1 n2)) + with NotNum | Division_by_zero -> + raise Undetermined (* t_app_value ls l ty *)) + | _ -> assert false + +let eval_int_uop op _ _ls l _ty = + match l with + | [ t1 ] -> ( + try + let n1 = big_int_of_value t1 in + Int (op n1) + with NotNum | Division_by_zero -> + raise Undetermined (* t_app_value ls l ty *)) + | _ -> assert false + +let real_align ~pow2 ~pow5 r = + let open Number in + let { rv_sig = s; rv_pow2 = p2; rv_pow5 = p5 } = r in + let s = BigInt.mul s (BigInt.pow_int_pos_bigint 2 (BigInt.sub p2 pow2)) in + let s = BigInt.mul s (BigInt.pow_int_pos_bigint 5 (BigInt.sub p5 pow5)) in + s + +let eval_real_op op simpl _ ls l ty = + match l with + | [ t1; t2 ] -> ( + try + let n1 = real_of_value t1 in + let n2 = real_of_value t2 in + Real (op n1 n2) + with NotNum -> simpl ls t1 t2 ty) + | _ -> assert false + +let eval_real_uop op _ _ls l _ty = + match l with + | [ t1 ] -> ( + try + let n1 = real_of_value t1 in + Real (op n1) + with NotNum -> raise Undetermined) + | _ -> assert false + +let eval_real_rel op _ _ls l _ty = + let open Number in + match l with + | [ t1; t2 ] -> ( + try + let n1 = real_of_value t1 in + let n2 = real_of_value t2 in + let s1, s2 = + let { rv_sig = s1; rv_pow2 = p21; rv_pow5 = p51 } = n1 in + let { rv_sig = s2; rv_pow2 = p22; rv_pow5 = p52 } = n2 in + if BigInt.sign s1 <> BigInt.sign s2 + then (s1, s2) + else + let pow2 = BigInt.min p21 p22 in + let pow5 = BigInt.min p51 p52 in + let s1 = real_align ~pow2 ~pow5 n1 in + let s2 = real_align ~pow2 ~pow5 n2 in + (s1, s2) + in + Term (to_bool (op s1 s2)) + with NotNum -> raise Undetermined (* t_app_value ls l ty *)) + | _ -> assert false + +let real_0 = Number.real_value BigInt.zero +let real_1 = Number.real_value BigInt.one + +let is_real t r = + try Number.compare_real (real_of_value t) r = 0 with NotNum -> false + +let real_opp r = + let open Number in + let { rv_sig = s; rv_pow2 = pow2; rv_pow5 = pow5 } = r in + real_value ~pow2 ~pow5 (BigInt.minus s) + +let real_add r1 r2 = + let open Number in + let { rv_sig = s1; rv_pow2 = p21; rv_pow5 = p51 } = r1 in + let { rv_sig = s2; rv_pow2 = p22; rv_pow5 = p52 } = r2 in + if BigInt.eq s1 BigInt.zero + then r2 + else if BigInt.eq s2 BigInt.zero + then r1 + else + let pow2 = BigInt.min p21 p22 in + let pow5 = BigInt.min p51 p52 in + let s1 = real_align ~pow2 ~pow5 r1 in + let s2 = real_align ~pow2 ~pow5 r2 in + real_value ~pow2 ~pow5 (BigInt.add s1 s2) + +let real_sub r1 r2 = + let open Number in + let { rv_sig = s1; rv_pow2 = p21; rv_pow5 = p51 } = r1 in + let { rv_sig = s2; rv_pow2 = p22; rv_pow5 = p52 } = r2 in + if BigInt.eq s2 BigInt.zero + then r1 + else if BigInt.eq s1 BigInt.zero + then real_value ~pow2:p22 ~pow5:p52 (BigInt.minus s2) + else + let pow2 = BigInt.min p21 p22 in + let pow5 = BigInt.min p51 p52 in + let s1 = real_align ~pow2 ~pow5 r1 in + let s2 = real_align ~pow2 ~pow5 r2 in + real_value ~pow2 ~pow5 (BigInt.sub s1 s2) + +let real_mul r1 r2 = + let open Number in + let { rv_sig = s1; rv_pow2 = p21; rv_pow5 = p51 } = r1 in + let { rv_sig = s2; rv_pow2 = p22; rv_pow5 = p52 } = r2 in + let s = BigInt.mul s1 s2 in + let pow2 = BigInt.add p21 p22 in + let pow5 = BigInt.add p51 p52 in + real_value ~pow2 ~pow5 s + +let simpl_real_add _ls t1 t2 _ty = + if is_real t1 real_0 + then t2 + else if is_real t2 real_0 + then t1 + else raise Undetermined + +let simpl_real_sub _ls t1 t2 _ty = + if is_real t2 real_0 then t1 else raise Undetermined + +let simpl_real_mul _ls t1 t2 _ty = + if is_real t1 real_0 + then t1 + else if is_real t2 real_0 + then t2 + else if is_real t1 real_1 + then t2 + else if is_real t2 real_1 + then t1 + else raise Undetermined + +let real_pow r1 r2 = + let open Number in + let { rv_sig = s1; rv_pow2 = p21; rv_pow5 = p51 } = r1 in + let { rv_sig = s2; rv_pow2 = p22; rv_pow5 = p52 } = r2 in + if BigInt.le s1 BigInt.zero + then raise Undetermined + else if BigInt.lt p22 BigInt.zero || BigInt.lt p52 BigInt.zero + then raise NotNum + else + let s1 = try BigInt.to_int s1 with Failure _ -> raise NotNum in + let s2 = BigInt.mul s2 (BigInt.pow_int_pos_bigint 2 p22) in + let s2 = BigInt.mul s2 (BigInt.pow_int_pos_bigint 5 p52) in + let s = + if s1 = 1 + then BigInt.one + else if BigInt.lt s2 BigInt.zero + then raise NotNum + else BigInt.pow_int_pos_bigint s1 s2 + in + let pow2 = BigInt.mul p21 s2 in + let pow5 = BigInt.mul p51 s2 in + Number.real_value ~pow2 ~pow5 s + +let simpl_real_pow _ls t1 _t2 _ty = + if is_real t1 real_1 then t1 else raise Undetermined + +let real_from_int _ _ls l _ty = + match l with + | [ t ] -> ( + try + let n = big_int_of_value t in + Real (Number.real_value n) + with NotNum -> raise Undetermined) + | _ -> assert false + +type 'a built_in_theories = + Env.pathname + * string + * (string * (Ty.tysymbol -> unit)) list + * (string * lsymbol ref option * 'a builtin) list + +let built_in_theories : unit -> 'a built_in_theories list = + fun () -> + [ + (* ["bool"],"Bool", [], [ "True", None, eval_true ; "False", None, + eval_false ; ] ; *) + ( [ "int" ], + "Int", + [], + [ + (Ident.op_infix "+", None, eval_int_op BigInt.add simpl_add); + (Ident.op_infix "-", None, eval_int_op BigInt.sub simpl_sub); + (Ident.op_infix "*", None, eval_int_op BigInt.mul simpl_mul); + (Ident.op_prefix "-", None, eval_int_uop BigInt.minus); + (Ident.op_infix "<", None, eval_int_rel BigInt.lt); + (Ident.op_infix "<=", None, eval_int_rel BigInt.le); + (Ident.op_infix ">", None, eval_int_rel BigInt.gt); + (Ident.op_infix ">=", None, eval_int_rel BigInt.ge); + ] ); + ( [ "int" ], + "MinMax", + [], + [ + ("min", None, eval_int_op BigInt.min simpl_minmax); + ("max", None, eval_int_op BigInt.max simpl_minmax); + ] ); + ( [ "int" ], + "ComputerDivision", + [], + [ + ("div", None, eval_int_op BigInt.computer_div simpl_div); + ("mod", None, eval_int_op BigInt.computer_mod simpl_mod); + ] ); + ( [ "int" ], + "EuclideanDivision", + [], + [ + ("div", None, eval_int_op BigInt.euclidean_div simpl_div); + ("mod", None, eval_int_op BigInt.euclidean_mod simpl_mod); + ] ); + ( [ "real" ], + "Real", + [], + [ + (Ident.op_prefix "-", None, eval_real_uop real_opp); + (Ident.op_infix "+", None, eval_real_op real_add simpl_real_add); + (Ident.op_infix "-", None, eval_real_op real_sub simpl_real_sub); + (Ident.op_infix "*", None, eval_real_op real_mul simpl_real_mul); + (Ident.op_infix "<", None, eval_real_rel BigInt.lt); + (Ident.op_infix "<=", None, eval_real_rel BigInt.le); + (Ident.op_infix ">", None, eval_real_rel BigInt.gt); + (Ident.op_infix ">=", None, eval_real_rel BigInt.ge); + ] ); + ([ "real" ], "FromInt", [], [ ("from_int", None, real_from_int) ]); + ( [ "real" ], + "PowerReal", + [], + [ ("pow", None, eval_real_op real_pow simpl_real_pow) ] ); + (* ["map"],"Map", ["map", builtin_map_type], [ "const", Some ls_map_const, + eval_map_const; "get", Some ls_map_get, eval_map_get; "set", Some + ls_map_set, eval_map_set; ] ; *) + ] + +let add_builtin_th env ((l, n, t, d) : 'a built_in_theories) = + let th = Env.read_theory env.env l n in + List.iter + (fun (id, r) -> + let ts = Theory.ns_find_ts th.Theory.th_export [ id ] in + r ts) + t; + List.iter + (fun (id, r, f) -> + let ls = Theory.ns_find_ls th.Theory.th_export [ id ] in + Hls.add env.builtins ls f; + match r with None -> () | Some r -> r := ls) + d + +let get_builtins env built_in_user = + Hls.clear env.builtins; + List.iter (add_builtin_th env) (built_in_theories ()); + List.iter (add_builtin_th env) built_in_user + +(* {2 the reduction machine} *) + +(* OBSOLETE COMMENT + + A configuration is a pair (t,s) where t is a stack of terms and s is a stack + of function symbols. + + A configuration ([t1;..;tn],[f1;..;fk]) represents a whole term, its model, + as defined recursively by + + model([t],[]) = t + + model(t1::..::tn::t,f::s) = model(f(t1,..,tn)::t,s) where f is of arity n + + A given term can be "exploded" into a configuration by reversing the rules + above + + During reduction, the terms in the first stack are kept in normal form + (value). The normalization process can be defined as the repeated application + of the following rules. + + ([t],[]) --> t // t is in normal form + + if f(t1,..,tn) is not a redex then (t1::..::tn::t,f::s) --> + (f(t1,..,tn)::t,s) + + if f(t1,..,tn) is a redex l sigma for a rule l -> r then (t1::..::tn::t,f::s) + --> (subst(sigma) @ t,explode(r) @ s) *) + +type substitution = term Mvs.t + +type cont = + | Kapp of lsymbol * Ty.ty option + | Kif of term * term * substitution + | Klet of vsymbol * term * substitution + | Kcase of term_branch list * substitution + | Keps of vsymbol + | Kquant of quant * vsymbol list * trigger + | Kbinop of binop + | Knot + | Keval of term * substitution + +type config = { + value_stack : value list; + cont_stack : (cont * term) list; + (* second term is the original term, for attribute and loc copy *) +} + +(* This global variable is used to approximate a count of the elementary + simplifications that are done during normalization. This is used for + transformation step. *) +let rec_step_limit = ref 0 + +exception NoMatch of (term * term * term option) option +exception NoMatchpat of (pattern * pattern) option + +let rec pattern_renaming (bound_vars, mt) p1 p2 = + match (p1.pat_node, p2.pat_node) with + | Pwild, Pwild -> (bound_vars, mt) + | Pvar v1, Pvar v2 -> ( + try + let mt = Ty.ty_match mt v1.vs_ty v2.vs_ty in + let bound_vars = Mvs.add v2 v1 bound_vars in + (bound_vars, mt) + with Ty.TypeMismatch _ -> raise (NoMatchpat (Some (p1, p2)))) + | Papp (ls1, tl1), Papp (ls2, tl2) when ls_equal ls1 ls2 -> + List.fold_left2 pattern_renaming (bound_vars, mt) tl1 tl2 + | Por (p1a, p1b), Por (p2a, p2b) -> + let bound_vars, mt = pattern_renaming (bound_vars, mt) p1a p2a in + pattern_renaming (bound_vars, mt) p1b p2b + | Pas (p1, v1), Pas (p2, v2) -> ( + try + let mt = Ty.ty_match mt v1.vs_ty v2.vs_ty in + let bound_vars = Mvs.add v2 v1 bound_vars in + pattern_renaming (bound_vars, mt) p1 p2 + with Ty.TypeMismatch _ -> raise (NoMatchpat (Some (p1, p2)))) + | _ -> raise (NoMatchpat (Some (p1, p2))) + +let first_order_matching (vars : Svs.t) (largs : term list) (args : term list) : + Ty.ty Ty.Mtv.t * substitution = + let rec loop bound_vars ((mt, mv) as sigma) largs args = + match (largs, args) with + | [], [] -> sigma + | t1 :: r1, t2 :: r2 -> ( + (* Format.eprintf "matching terms %a and %a...@." Pretty.print_term t1 + Pretty.print_term t2; *) + match t1.t_node with + | Tvar vs when Svs.mem vs vars -> ( + try + let t = Mvs.find vs mv in + if t_equal t t2 + then loop bound_vars sigma r1 r2 + else raise (NoMatch (Some (t1, t2, Some t))) + with Not_found -> ( + try + let ts = Ty.ty_match mt vs.vs_ty (t_type t2) in + let fv2 = t_vars t2 in + if Mvs.is_empty (Mvs.set_inter bound_vars fv2) + then loop bound_vars (ts, Mvs.add vs t2 mv) r1 r2 + else raise (NoMatch (Some (t1, t2, None))) + with Ty.TypeMismatch _ -> raise (NoMatch (Some (t1, t2, None))))) + | Tapp (ls1, args1) -> ( + match t2.t_node with + | Tapp (ls2, args2) when ls_equal ls1 ls2 -> ( + let mt, mv = + loop bound_vars sigma (List.rev_append args1 r1) + (List.rev_append args2 r2) + in + try (Ty.oty_match mt t1.t_ty t2.t_ty, mv) + with Ty.TypeMismatch _ -> raise (NoMatch (Some (t1, t2, None)))) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | Tquant (q1, bv1) -> ( + match t2.t_node with + | Tquant (q2, bv2) when q1 = q2 -> + let vl1, _tl1, term1 = t_open_quant bv1 in + let vl2, _tl2, term2 = t_open_quant bv2 in + let bound_vars, term1, mt = + try + List.fold_left2 + (fun (bound_vars, term1, mt) e1 e2 -> + let mt = Ty.ty_match mt e1.vs_ty e2.vs_ty in + let bound_vars = Mvs.add e2 e1 bound_vars in + (bound_vars, term1, mt)) + (bound_vars, term1, mt) vl1 vl2 + with Invalid_argument _ | Ty.TypeMismatch _ -> + raise (NoMatch (Some (t1, t2, None))) + in + loop bound_vars (mt, mv) (term1 :: r1) (term2 :: r2) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | Tbinop (b1, t1_l, t1_r) -> ( + match t2.t_node with + | Tbinop (b2, t2_l, t2_r) when b1 = b2 -> + loop bound_vars (mt, mv) (t1_l :: t1_r :: r1) (t2_l :: t2_r :: r2) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | Tif (t11, t12, t13) -> ( + match t2.t_node with + | Tif (t21, t22, t23) -> + loop bound_vars (mt, mv) (t11 :: t12 :: t13 :: r1) + (t21 :: t22 :: t23 :: r2) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | Tlet (td1, tb1) -> ( + match t2.t_node with + | Tlet (td2, tb2) -> + let v1, tl1 = t_open_bound tb1 in + let v2, tl2 = t_open_bound tb2 in + let mt = + try Ty.ty_match mt v1.vs_ty v2.vs_ty + with Ty.TypeMismatch _ -> raise (NoMatch (Some (t1, t2, None))) + in + let bound_vars = Mvs.add v2 v1 bound_vars in + loop bound_vars (mt, mv) (td1 :: tl1 :: r1) (td2 :: tl2 :: r2) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | Tcase (ts1, tbl1) -> ( + match t2.t_node with + | Tcase (ts2, tbl2) -> ( + try + let bound_vars, mt, l1, l2 = + List.fold_left2 + (fun (bound_vars, mt, l1, l2) tb1 tb2 -> + let p1, tb1 = t_open_branch tb1 in + let p2, tb2 = t_open_branch tb2 in + let bound_vars, mt = + pattern_renaming (bound_vars, mt) p1 p2 + in + (bound_vars, mt, tb1 :: l1, tb2 :: l2)) + (bound_vars, mt, ts1 :: r1, ts2 :: r2) + tbl1 tbl2 + in + loop bound_vars (mt, mv) l1 l2 + with Invalid_argument _ -> raise (NoMatch (Some (t1, t2, None)))) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | Teps tb1 -> ( + match t2.t_node with + | Teps tb2 -> + let v1, td1 = t_open_bound tb1 in + let v2, td2 = t_open_bound tb2 in + let mt = + try Ty.ty_match mt v1.vs_ty v2.vs_ty + with Ty.TypeMismatch _ -> raise (NoMatch (Some (t1, t2, None))) + in + let bound_vars = Mvs.add v2 v1 bound_vars in + loop bound_vars (mt, mv) (td1 :: r1) (td2 :: r2) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | Tnot t1 -> ( + match t2.t_node with + | Tnot t2 -> loop bound_vars sigma (t1 :: r1) (t2 :: r2) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | Tvar v1 -> ( + match t2.t_node with + | Tvar v2 -> ( + try + if vs_equal v1 (Mvs.find v2 bound_vars) + then loop bound_vars sigma r1 r2 + else raise (NoMatch (Some (t1, t2, None))) + with Not_found -> assert false) + | _ -> raise (NoMatch (Some (t1, t2, None)))) + | (Tconst _ | Ttrue | Tfalse) when t_equal t1 t2 -> + loop bound_vars sigma r1 r2 + | Tconst _ | Ttrue | Tfalse -> raise (NoMatch (Some (t1, t2, None)))) + | _ -> raise (NoMatch None) + in + loop Mvs.empty (Ty.Mtv.empty, Mvs.empty) largs args + +exception Irreducible + +let one_step_reduce engine ls args = + try + let rules = Mls.find ls engine.rules in + let rec loop rules = + match rules with + | [] -> raise Irreducible + | (vars, largs, rhs) :: rem -> ( + try + let sigma = first_order_matching vars largs args in + (sigma, rhs) + with NoMatch _ -> loop rem) + in + loop rules + with Not_found -> raise Irreducible + +let rec matching ((mt, mv) as sigma) t p = + match p.pat_node with + | Pwild -> sigma + | Pvar v -> (mt, Mvs.add v t mv) + | Por (p1, p2) -> ( + try matching sigma t p1 with NoMatch _ -> matching sigma t p2) + | Pas (p, v) -> matching (mt, Mvs.add v t mv) t p + | Papp (ls1, pl) -> ( + match t.t_node with + | Tapp (ls2, tl) -> + if ls_equal ls1 ls2 + then List.fold_left2 matching sigma tl pl + else if ls2.ls_constr > 0 + then raise (NoMatch None) + else raise Undetermined + | _ -> raise Undetermined) + +let rec extract_first n acc l = + if n = 0 + then (acc, l) + else + match l with + | x :: r -> extract_first (n - 1) (x :: acc) r + | [] -> assert false + +(** If [t1] matches [i1 < vs] or [i1 <= vs] and [t2] matches [vs < i2] or + [vs <= i2], then return the bounds [i1(+1), vs, i2(-1)]. The +1/-1 shift the + bounds for strict unequalities operators to obtain inclusive bounds. *) +let bounds ls_lt t1 t2 = + let lt order = function + (* match [i < vs] or [vs < i], return [i, vs] *) + | { t_node = Tapp (ls, [ t1; t2 ]) } when ls_equal ls ls_lt -> ( + assert (Ty.(ty_equal (t_type t1) ty_int && ty_equal (t_type t2) ty_int)); + match order t1 t2 with + | ( { t_node = Tconst (Constant.ConstInt { Number.il_int = i }) }, + { t_node = Tvar vs } ) -> + (i, vs) + | _ -> raise Exit) + | _ -> raise Exit + in + let eq order = function + (* match [i = vs] or [vs = i], return [i, vs] *) + | { t_node = Tapp (ls, [ t1; t2 ]) } + when ls_equal ls Term.ps_equ + && Ty.ty_equal (t_type t1) Ty.ty_int + && Ty.ty_equal (t_type t2) Ty.ty_int -> ( + match order t1 t2 with + | ( { t_node = Tconst (Constant.ConstInt { Number.il_int = i }) }, + { t_node = Tvar vs } ) + when Ty.ty_equal vs.vs_ty Ty.ty_int -> + (i, vs) + | _ -> raise Exit) + | _ -> raise Exit + in + let le order = function + (* match [i <= vs] or [vs <= i], return [i, vs] *) + | { t_node = Tbinop (Tor, t1, t2) } -> + let i, vs = lt order t1 in + let i', vs' = eq order t2 in + if not (BigInt.eq i i' && vs_equal vs vs') then raise Exit; + (i, vs) + | _ -> raise Exit + in + let bound order shift t = + (* match [i op vs] or [vs op i], op is [<] or [<=] *) + try le order t + with Exit -> + let i, vs = lt order t in + (shift i, vs) + in + let i1, vs = bound (fun t1 t2 -> (t1, t2)) BigInt.succ t1 in + let i2, vs' = bound (fun t1 t2 -> (t2, t1)) BigInt.pred t2 in + if not (vs_equal vs vs') then raise Exit; + (i1, vs, i2) + +(** [reduce_bounded_quant ls_lt limit t sigma st rem] detects a bounded + quantification and reduces it to conjunction: + + forall v. a < v < b. t(v) --> f(a+1) /\ ... /\ f(b-1) + + @param ls_lt lsymbol for [int.Int.(<)] + @param limit + Reduce if the difference between upper and lower bound is at most + [limit]. + + TODO: + + - expand to bounded quantifications on range values + - compatiblity with reverse direction (forall i. b > i > a -> t) + - detect SPARK-style [forall i. if a < i /\ i < b then t else true] *) +let reduce_bounded_quant ls_lt limit t sigma st rem = + match (st, rem) with + (* st = a < vs < b :: _; rem = -> :: forall vs :: _ *) + | ( Term { t_node = Tbinop (Tand, t1, t2) } :: st, + ( (Kbinop Timplies, _) + :: (Kquant ((Tforall as quant), [ vs ], _), _) + :: rem + | (Kbinop Tand, _) :: (Kquant ((Texists as quant), [ vs ], _), _) :: rem + ) ) + when Ty.ty_equal vs.vs_ty Ty.ty_int -> + let t_empty, binop = + match quant with Tforall -> (t_true, Tand) | Texists -> (t_false, Tor) + in + let a, vs', b = bounds ls_lt t1 t2 in + if not (vs_equal vs vs') then raise Exit; + if BigInt.(gt (sub b a) (of_int limit)) then raise Exit; + if BigInt.gt a b + then (* empty range *) + { value_stack = Term t_empty :: st; cont_stack = rem } + else + let rec loop rem i = + if BigInt.lt i a + then rem + else + let t_i = t_const (Constant.int_const i) Ty.ty_int in + let rem = + (* conjunction for all i > a *) + if BigInt.gt i a then (Kbinop binop, t_true) :: rem else rem + in + let rem = (Keval (t, Mvs.add vs t_i sigma), t_true) :: rem in + loop rem (BigInt.pred i) + in + { value_stack = st; cont_stack = loop rem b } + | _ -> raise Exit + +let rec reduce engine c = + match (c.value_stack, c.cont_stack) with + | _, [] -> assert false + | st, (Keval (t, sigma), orig) :: rem -> ( + let limit = engine.params.compute_max_quantifier_domain in + try reduce_bounded_quant engine.ls_lt limit t sigma st rem + with Exit -> reduce_eval engine st t ~orig sigma rem) + | [], (Kif _, _) :: _ -> assert false + | v :: st, (Kif (t2, t3, sigma), orig) :: rem -> ( + match v with + | Term { t_node = Ttrue } -> + incr rec_step_limit; + { + value_stack = st; + cont_stack = (Keval (t2, sigma), t_attr_copy orig t2) :: rem; + } + | Term { t_node = Tfalse } -> + incr rec_step_limit; + { + value_stack = st; + cont_stack = (Keval (t3, sigma), t_attr_copy orig t3) :: rem; + } + | Term t1 -> ( + match (t1.t_node, t2.t_node, t3.t_node) with + | ( Tapp (ls, [ b0; { t_node = Tapp (ls1, _) } ]), + Tapp (ls2, _), + Tapp (ls3, _) ) + when ls_equal ls ps_equ && ls_equal ls1 fs_bool_true + && ls_equal ls2 fs_bool_true && ls_equal ls3 fs_bool_false -> + incr rec_step_limit; + { value_stack = Term (t_attr_copy orig b0) :: st; cont_stack = rem } + | _ -> + { + value_stack = + Term + (t_attr_copy orig (t_if t1 (t_subst sigma t2) (t_subst sigma t3))) + :: st; + cont_stack = rem; + }) + | Int _ | Real _ -> assert false (* would be ill-typed *)) + | [], (Klet _, _) :: _ -> assert false + | t1 :: st, (Klet (v, t2, sigma), orig) :: rem -> + incr rec_step_limit; + let t1 = term_of_value t1 in + { + value_stack = st; + cont_stack = (Keval (t2, Mvs.add v t1 sigma), t_attr_copy orig t2) :: rem; + } + | [], (Kcase _, _) :: _ -> assert false + | (Int _ | Real _) :: _, (Kcase _, _) :: _ -> assert false + | Term t1 :: st, (Kcase (tbl, sigma), orig) :: rem -> + reduce_match st t1 ~orig tbl sigma rem + | ( ([] | [ _ ] | (Int _ | Real _) :: _ | Term _ :: (Int _ | Real _) :: _), + (Kbinop _, _) :: _ ) -> + assert false + | Term t1 :: Term t2 :: st, (Kbinop op, orig) :: rem -> + incr rec_step_limit; + { + value_stack = Term (t_attr_copy orig (t_binary_simp op t2 t1)) :: st; + cont_stack = rem; + } + | [], (Knot, _) :: _ -> assert false + | (Int _ | Real _) :: _, (Knot, _) :: _ -> assert false + | Term t :: st, (Knot, orig) :: rem -> + incr rec_step_limit; + { + value_stack = Term (t_attr_copy orig (t_not_simp t)) :: st; + cont_stack = rem; + } + | st, (Kapp (ls, ty), orig) :: rem -> reduce_app engine st ~orig ls ty rem + | [], (Keps _, _) :: _ -> assert false + | (Int _ | Real _) :: _, (Keps _, _) :: _ -> assert false + | Term t :: st, (Keps v, orig) :: rem -> + { + value_stack = Term (t_attr_copy orig (t_eps_close v t)) :: st; + cont_stack = rem; + } + | [], (Kquant _, _) :: _ -> assert false + | (Int _ | Real _) :: _, (Kquant _, _) :: _ -> assert false + | Term t :: st, (Kquant (q, vl, tr), orig) :: rem -> + { + value_stack = Term (t_attr_copy orig (t_quant_close_simp q vl tr t)) :: st; + cont_stack = rem; + } + +and reduce_match st u ~orig tbl sigma cont = + let rec iter tbl = + match tbl with + | [] -> assert false (* pattern matching not exhaustive *) + | b :: rem -> ( + let p, t = t_open_branch b in + try + let mt', mv' = matching (Ty.Mtv.empty, sigma) u p in + (* Format.eprintf "Pattern-matching succeeded:@\nmt' = @["; Ty.Mtv.iter + (fun tv ty -> Format.eprintf "%a -> %a," Pretty.print_tv tv + Pretty.print_ty ty) mt'; Format.eprintf "@]@\n"; Format.eprintf "mv' + = @["; Mvs.iter (fun v t -> Format.eprintf "%a -> %a," + Pretty.print_vs v Pretty.print_term t) mv'; Format.eprintf "@]@."; + Format.eprintf "branch before inst: %a@." Pretty.print_term t; *) + let mv'', t = t_subst_types mt' mv' t in + (* Format.eprintf "branch after types inst: %a@." Pretty.print_term t; + Format.eprintf "mv'' = @["; Mvs.iter (fun v t -> Format.eprintf "%a + -> %a," Pretty.print_vs v Pretty.print_term t) mv''; Format.eprintf + "@]@."; *) + incr rec_step_limit; + { + value_stack = st; + cont_stack = (Keval (t, mv''), t_attr_copy orig t) :: cont; + } + with NoMatch _ -> iter rem) + in + try iter tbl + with Undetermined -> + let dmy = t_var (create_vsymbol (Ident.id_fresh "__dmy") (t_type u)) in + let tbls = + match t_subst sigma (t_case dmy tbl) with + | { t_node = Tcase (_, tbls) } -> tbls + | _ -> assert false + in + { + value_stack = Term (t_attr_copy orig (t_case u tbls)) :: st; + cont_stack = cont; + } + +and reduce_eval eng st t ~orig sigma rem = + let orig = t_attr_copy orig t in + match t.t_node with + | Tvar v -> ( + match Ident.Mid.find v.vs_name eng.known_map with + | Decl.{ d_node = Dlogic dl } -> + incr rec_step_limit; + let _, ls_defn = + List.find + (fun (ls, _) -> + String.equal ls.ls_name.Ident.id_string v.vs_name.Ident.id_string) + dl + in + let vs, t = Decl.open_ls_defn ls_defn in + let aux vs t = + (* Create ε fc. λ vs. fc @ vs = t to make the value from known_map + compatible to reduce_func_app *) + let ty = Opt.get t.t_ty in + let app_ty = Ty.ty_func vs.vs_ty ty in + let fc = create_vsymbol (Ident.id_fresh "fc") app_ty in + t_eps + (t_close_bound fc + (t_quant Tforall + (t_close_quant [ vs ] [] + (t_app ps_equ + [ t_app fs_func_app [ t_var fc; t_var vs ] (Some ty); t ] + None)))) + in + let t = List.fold_right aux vs t in + { value_stack = Term t :: st; cont_stack = rem } + | _ -> assert false + | exception Not_found -> ( + match Mvs.find v sigma with + | t -> + incr rec_step_limit; + { value_stack = Term (t_attr_copy orig t) :: st; cont_stack = rem } + | exception Not_found -> + (* this may happen, e.g when computing below a quantified formula *) + (* Format.eprintf "Tvar not found: %a@." Pretty.print_vs v; assert + false *) + { value_stack = Term orig :: st; cont_stack = rem })) + | Tif (t1, t2, t3) -> + { + value_stack = st; + cont_stack = (Keval (t1, sigma), t1) :: (Kif (t2, t3, sigma), orig) :: rem; + } + | Tlet (t1, tb) -> + let v, t2 = t_open_bound tb in + { + value_stack = st; + cont_stack = (Keval (t1, sigma), t1) :: (Klet (v, t2, sigma), orig) :: rem; + } + | Tcase (t1, tbl) -> + { + value_stack = st; + cont_stack = (Keval (t1, sigma), t1) :: (Kcase (tbl, sigma), orig) :: rem; + } + | Tbinop (op, t1, t2) -> + { + value_stack = st; + cont_stack = + (Keval (t1, sigma), t1) + :: (Keval (t2, sigma), t2) + :: (Kbinop op, orig) :: rem; + } + | Tnot t1 -> + { + value_stack = st; + cont_stack = (Keval (t1, sigma), t1) :: (Knot, orig) :: rem; + } + | Teps tb -> + let v, t1 = t_open_bound tb in + { + value_stack = st; + cont_stack = (Keval (t1, sigma), t1) :: (Keps v, orig) :: rem; + } + | Tquant (q, tq) -> + let vl, tr, t1 = t_open_quant tq in + { + value_stack = st; + cont_stack = (Keval (t1, sigma), t1) :: (Kquant (q, vl, tr), orig) :: rem; + } + | Tapp (ls, tl) -> + let args = List.rev_map (fun t -> (Keval (t, sigma), t)) tl in + { + value_stack = st; + cont_stack = List.rev_append args ((Kapp (ls, t.t_ty), orig) :: rem); + } + | Ttrue | Tfalse | Tconst _ -> + { value_stack = Term orig :: st; cont_stack = rem } + +and reduce_app engine st ls ~orig ty rem_cont = + if ls_equal ls ps_equ + then + match st with + | t2 :: t1 :: rem_st -> ( + try reduce_equ ~orig rem_st t1 t2 rem_cont + with Undetermined -> reduce_app_no_equ engine st ls ~orig ty rem_cont) + | _ -> assert false + else if ls_equal ls fs_func_app + then + match st with + | t2 :: t1 :: rem_st -> ( + try reduce_func_app ~orig ty rem_st t1 t2 rem_cont + with Undetermined -> reduce_app_no_equ engine st ls ~orig ty rem_cont) + | _ -> assert false + else reduce_app_no_equ engine st ls ~orig ty rem_cont + +and reduce_func_app ~orig _ty rem_st t1 t2 rem_cont = + (* attempt to decompile t1 under the form (epsilon fc. forall x. fc @ x = + body) that is equivalent to \x.body *) + match t1 with + | Term { t_node = Teps tb } -> ( + let fc, t = Term.t_open_bound tb in + match t.t_node with + | Tquant (Tforall, tq) -> ( + let vl, trig, t = t_open_quant tq in + let process lhs body equ elim = + let rvl = List.rev vl in + let rec remove_var lhs rvh rvt = + match lhs.t_node with + | Tapp (ls2, [ lhs1; ({ t_node = Tvar v1 } as arg) ]) + when ls_equal ls2 fs_func_app && vs_equal v1 rvh -> ( + match (rvt, lhs1) with + | rvh :: rvt, _ -> + let lhs1, fc2 = remove_var lhs1 rvh rvt in + let lhs2 = t_app ls2 [ lhs1; arg ] lhs.t_ty in + (t_attr_copy lhs lhs2, fc2) + | [], { t_node = Tvar fc1 } when vs_equal fc1 fc -> + let fcn = fc.vs_name in + let fc2 = Ident.id_derive fcn.Ident.id_string fcn in + let fc2 = create_vsymbol fc2 (t_type lhs) in + (t_attr_copy lhs (t_var fc2), fc2) + | _ -> raise Undetermined) + | _ -> raise Undetermined + in + match rvl with + | rvh :: rvt -> ( + let lhs, fc2 = remove_var lhs rvh rvt in + let vh, vl = + match vl with [] -> assert false | vh :: vl -> (vh, vl) + in + let t2 = term_of_value t2 in + match vl with + | [] -> elim body vh t2 + | _ -> + let eq = equ lhs body in + let tq = t_quant Tforall (t_close_quant vl trig eq) in + let body = t_attr_copy t (t_eps_close fc2 tq) in + { + value_stack = rem_st; + cont_stack = + (Keval (body, Mvs.add vh t2 Mvs.empty), t_attr_copy orig body) + :: rem_cont; + }) + | _ -> raise Undetermined + in + match t.t_node with + | Tapp (ls1, [ lhs; body ]) when ls_equal ls1 ps_equ -> + let equ lhs body = t_attr_copy t (t_app ps_equ [ lhs; body ] None) in + let elim body vh t2 = + { + value_stack = rem_st; + cont_stack = + (Keval (body, Mvs.add vh t2 Mvs.empty), t_attr_copy orig body) + :: rem_cont; + } + in + process lhs body equ elim + | Tbinop (Tiff, ({ t_node = Tapp (ls1, [ lhs; tr ]) } as teq), body) + when ls_equal ls1 ps_equ && t_equal tr t_bool_true -> + let equ lhs body = + let lhs = t_attr_copy teq (t_app ps_equ [ lhs; tr ] None) in + t_attr_copy t (t_binary Tiff lhs body) + in + let elim body vh t2 = + let body = t_if body t_bool_true t_bool_false in + { + value_stack = rem_st; + cont_stack = + (Keval (body, Mvs.add vh t2 Mvs.empty), t_attr_copy orig body) + :: rem_cont; + } + in + process lhs body equ elim + | _ -> raise Undetermined) + | _ -> raise Undetermined) + | _ -> raise Undetermined + +and reduce_app_no_equ engine st ls ~orig ty rem_cont = + let arity = List.length ls.ls_args in + let args, rem_st = extract_first arity [] st in + try + let f = Hls.find engine.builtins ls in + let v = f engine ls args ty in + { value_stack = v_attr_copy orig v :: rem_st; cont_stack = rem_cont } + with Not_found | Undetermined -> ( + let args = List.map term_of_value args in + match Ident.Mid.find ls.ls_name engine.known_map with + | exception Not_found -> + Debug.dprintf debug "Reduction engine, ident not found: %s@." + ls.ls_name.Ident.id_string; + { + value_stack = Term (t_attr_copy orig (t_app ls args ty)) :: rem_st; + cont_stack = rem_cont; + } + | d -> ( + let rewrite () = + (* try a rewrite rule *) + try + (* Format.eprintf "try a rewrite rule on %a@." Pretty.print_ls ls; *) + let (mt, mv), rhs = one_step_reduce engine ls args in + (* Format.eprintf "rhs = %a@." Pretty.print_term rhs; Format.eprintf + "sigma = "; Mvs.iter (fun v t -> Format.eprintf "%a -> %a," + Pretty.print_vs v Pretty.print_term t) (snd sigma); Format.eprintf + "@."; Format.eprintf "try a type match: %a and %a@." + (Pp.print_option Pretty.print_ty) ty (Pp.print_option + Pretty.print_ty) rhs.t_ty; *) + (* let type_subst = Ty.oty_match Ty.Mtv.empty rhs.t_ty ty in + Format.eprintf "subst of rhs: "; Ty.Mtv.iter (fun tv ty -> + Format.eprintf "%a -> %a," Pretty.print_tv tv Pretty.print_ty ty) + type_subst; Format.eprintf "@."; let rhs = t_ty_subst type_subst + Mvs.empty rhs in let sigma = Mvs.map (t_ty_subst type_subst + Mvs.empty) sigma in Format.eprintf "rhs = %a@." Pretty.print_term + rhs; Format.eprintf "sigma = "; Mvs.iter (fun v t -> Format.eprintf + "%a -> %a," Pretty.print_vs v Pretty.print_term t) sigma; + Format.eprintf "@."; *) + let mv, rhs = t_subst_types mt mv rhs in + incr rec_step_limit; + { + value_stack = rem_st; + cont_stack = (Keval (rhs, mv), orig) :: rem_cont; + } + with Irreducible -> + { + value_stack = Term (t_attr_copy orig (t_app ls args ty)) :: rem_st; + cont_stack = rem_cont; + } + in + match d.Decl.d_node with + | Decl.Dtype _ | Decl.Dprop _ -> assert false + | Decl.Dlogic dl -> + (* regular definition *) + let d = List.assq ls dl in + if engine.params.compute_defs + || Term.Sls.mem ls engine.params.compute_def_set + then + let vl, e = Decl.open_ls_defn d in + let add (mt, mv) x y = + (Ty.ty_match mt x.vs_ty (t_type y), Mvs.add x y mv) + in + let mt, mv = List.fold_left2 add (Ty.Mtv.empty, Mvs.empty) vl args in + let mt = Ty.oty_match mt e.t_ty ty in + let mv, e = t_subst_types mt mv e in + { + value_stack = rem_st; + cont_stack = (Keval (e, mv), orig) :: rem_cont; + } + else rewrite () + | Decl.Dparam _ | Decl.Dind _ -> rewrite () + | Decl.Ddata dl -> ( + (* constructor or projection *) + try + match args with + | [ { t_node = Tapp (ls1, tl1) } ] -> + (* if ls is a projection and ls1 is a constructor, we should compute + that projection *) + let rec iter dl = + match dl with + | [] -> raise Exit + | (_, csl) :: rem -> + let rec iter2 csl = + match csl with + | [] -> iter rem + | (cs, prs) :: rem2 -> + if ls_equal cs ls1 + then + (* we found the right constructor *) + let rec iter3 prs tl1 = + match (prs, tl1) with + | Some pr :: prs, t :: tl1 -> + if ls_equal ls pr + then + (* projection found! *) + { + value_stack = Term (t_attr_copy orig t) :: rem_st; + cont_stack = rem_cont; + } + else iter3 prs tl1 + | None :: prs, _ :: tl1 -> iter3 prs tl1 + | _ -> raise Exit + in + iter3 prs tl1 + else iter2 rem2 + in + iter2 csl + in + iter dl + | _ -> raise Exit + with Exit -> rewrite ()))) + +and reduce_equ ~(* engine *) orig st v1 v2 cont = + (* try *) + match (v1, v2) with + | Int n1, Int n2 -> + let b = to_bool (BigInt.eq n1 n2) in + incr rec_step_limit; + { value_stack = Term (t_attr_copy orig b) :: st; cont_stack = cont } + | Real r1, Real r2 -> + let b = to_bool (Number.compare_real r1 r2 = 0) in + incr rec_step_limit; + { value_stack = Term (t_attr_copy orig b) :: st; cont_stack = cont } + | Int n, Term { t_node = Tconst c } | Term { t_node = Tconst c }, Int n -> ( + try + let n' = big_int_of_const c in + let b = to_bool (BigInt.eq n n') in + incr rec_step_limit; + { value_stack = Term (t_attr_copy orig b) :: st; cont_stack = cont } + with NotNum -> raise Undetermined) + | Real r, Term { t_node = Tconst c } | Term { t_node = Tconst c }, Real r -> ( + try + let r' = real_of_const c in + let b = to_bool (Number.compare_real r r' = 0) in + incr rec_step_limit; + { value_stack = Term (t_attr_copy orig b) :: st; cont_stack = cont } + with NotNum -> raise Undetermined) + | Real _, Term _ | Term _, Real _ -> raise Undetermined + | Int _, Term _ | Term _, Int _ -> raise Undetermined + | Int _, Real _ | Real _, Int _ -> assert false + | Term t1, Term t2 -> reduce_term_equ ~orig st t1 t2 cont +(* with Undetermined -> { value_stack = Term (t_equ (term_of_value v1) + (term_of_value v2)) :: st; cont_stack = cont; } *) + +and reduce_term_equ ~orig st t1 t2 cont = + if t_equal t1 t2 + then + let () = incr rec_step_limit in + { value_stack = Term (t_attr_copy orig t_true) :: st; cont_stack = cont } + else + match (t1.t_node, t2.t_node) with + | Tconst c1, Tconst c2 -> ( + match (c1, c2) with + | Constant.ConstInt i1, Constant.ConstInt i2 -> + let b = BigInt.eq i1.Number.il_int i2.Number.il_int in + incr rec_step_limit; + { + value_stack = Term (t_attr_copy orig (to_bool b)) :: st; + cont_stack = cont; + } + | _ -> raise Undetermined) + | Tapp (ls1, tl1), Tapp (ls2, tl2) + when ls1.ls_constr > 0 && ls2.ls_constr > 0 -> + if ls_equal ls1 ls2 + then + let rec aux sigma t tyl l1 l2 = + match (tyl, l1, l2) with + | [], [], [] -> (sigma, t) + | ty :: tyl, t1 :: tl1, t2 :: tl2 -> + let v1 = create_vsymbol (Ident.id_fresh "") ty in + let v2 = create_vsymbol (Ident.id_fresh "") ty in + aux + (Mvs.add v1 t1 (Mvs.add v2 t2 sigma)) + (t_and_simp (t_equ (t_var v1) (t_var v2)) t) + tyl tl1 tl2 + | _ -> assert false + in + let sigma, t = aux Mvs.empty t_true ls1.ls_args tl1 tl2 in + let () = incr rec_step_limit in + { value_stack = st; cont_stack = (Keval (t, sigma), orig) :: cont } + else + { + value_stack = Term (t_attr_copy orig t_false) :: st; + cont_stack = cont; + } + | ( Tif (b, { t_node = Tapp (ls1, _) }, { t_node = Tapp (ls2, _) }), + Tapp (ls3, _) ) + when ls_equal ls3 fs_bool_true && ls_equal ls1 fs_bool_true + && ls_equal ls2 fs_bool_false -> + incr rec_step_limit; + { value_stack = Term (t_attr_copy orig b) :: st; cont_stack = cont } + | _ -> raise Undetermined + +let rec reconstruct c = + match (c.value_stack, c.cont_stack) with + | [ Term t ], [] -> t + | _, [] -> assert false + | _, (k, orig) :: rem -> + let t, st = + match (c.value_stack, k) with + | st, Keval (t, sigma) -> (t_subst sigma t, st) + | [], Kif _ -> assert false + | v :: st, Kif (t2, t3, sigma) -> + (t_if (term_of_value v) (t_subst sigma t2) (t_subst sigma t3), st) + | [], Klet _ -> assert false + | t1 :: st, Klet (v, t2, sigma) -> + (t_let_close v (term_of_value t1) (t_subst sigma t2), st) + | [], Kcase _ -> assert false + | v :: st, Kcase (tbl, sigma) -> + (t_subst sigma (t_case (term_of_value v) tbl), st) + | ([] | [ _ ]), Kbinop _ -> assert false + | t1 :: t2 :: st, Kbinop op -> + (t_binary_simp op (term_of_value t2) (term_of_value t1), st) + | [], Knot -> assert false + | t :: st, Knot -> (t_not (term_of_value t), st) + | st, Kapp (ls, ty) -> + let args, rem_st = extract_first (List.length ls.ls_args) [] st in + let args = List.map term_of_value args in + (t_app ls args ty, rem_st) + | [], Keps _ -> assert false + | t :: st, Keps v -> (t_eps_close v (term_of_value t), st) + | [], Kquant _ -> assert false + | t :: st, Kquant (q, vl, tr) -> + (t_quant_close_simp q vl tr (term_of_value t), st) + in + reconstruct + { value_stack = Term (t_attr_copy orig t) :: st; cont_stack = rem } + +(** iterated reductions *) + +let normalize ?step_limit ~limit engine sigma t0 = + rec_step_limit := 0; + let rec many_steps c n = + match (c.value_stack, c.cont_stack) with + | [ Term t ], [] -> t + | _, [] -> assert false + | _ -> ( + if n = limit + then + let t1 = reconstruct c in + (* Loc.warning "reduction of term %a takes more than %d steps, aborted at %a.@." + * Pretty.print_term t0 limit Pretty.print_term t1; *) + t1 + else + match step_limit with + | None -> + let c = reduce engine c in + many_steps c (n + 1) + | Some step_limit -> + if !rec_step_limit >= step_limit + then reconstruct c + else + let c = reduce engine c in + many_steps c (n + 1)) + in + let c = { value_stack = []; cont_stack = [ (Keval (t0, sigma), t0) ] } in + many_steps c 0 + +(* the rewrite engine *) + +let create p env km user_env built_in_user = + let th = Env.read_theory env [ "int" ] "Int" in + let ls_lt = Theory.ns_find_ls th.Theory.th_export [ Ident.op_infix "<" ] in + let env = + { + env; + known_map = km; + rules = Mls.empty; + params = p; + ls_lt; + builtins = Hls.create 17; + user_env; + } + in + if p.compute_builtin then get_builtins env built_in_user; + env + +exception NotARewriteRule of string + +let extract_rule _km t = + (* let check_ls ls = try let _ = Hls.find builtins ls in raise + (NotARewriteRule "root of lhs of rule must not be a built-in symbol") with + Not_found -> let d = Ident.Mid.find ls.ls_name km in match d.Decl.d_node + with | Decl.Dtype _ | Decl.Dprop _ -> assert false | Decl.Dlogic _ -> raise + (NotARewriteRule "root of lhs of rule must not be defined symbol") | + Decl.Ddata _ -> raise (NotARewriteRule "root of lhs of rule must not be a + constructor nor a projection") | Decl.Dparam _ | Decl.Dind _ -> () in *) + let check_vars acc t1 t2 = + (* check that quantified variables all appear in the lefthand side + (quantified variables not appearing could be removed and those appearing + on right hand side cannot be guessed during rewriting). *) + let vars_lhs = t_vars t1 in + if Svs.exists (fun vs -> not (Mvs.mem vs vars_lhs)) acc + then raise (NotARewriteRule "lhs should contain all variables"); + (* check the same with type variables *) + if not + (Ty.Stv.subset + (t_ty_freevars Ty.Stv.empty t2) + (t_ty_freevars Ty.Stv.empty t2)) + then raise (NotARewriteRule "lhs should contain all type variables") + in + + let rec aux acc t = + match t.t_node with + | Tquant (Tforall, q) -> + let vs, _, t = t_open_quant q in + aux (List.fold_left (fun acc v -> Svs.add v acc) acc vs) t + | Tbinop (Tiff, t1, t2) -> ( + match t1.t_node with + | Tapp (ls, args) -> + (* check_ls ls; *) + check_vars acc t1 t2; + (acc, ls, args, t2) + | _ -> raise (NotARewriteRule "lhs of <-> should be a predicate symbol")) + | Tapp (ls, [ t1; t2 ]) when ls == ps_equ -> ( + match t1.t_node with + | Tapp (ls, args) -> + (* check_ls ls; *) + check_vars acc t1 t2; + (acc, ls, args, t2) + | _ -> raise (NotARewriteRule "lhs of = should be a function symbol")) + | _ -> + raise + (NotARewriteRule + "rule should be of the form forall ... t1 = t2 or f1 <-> f2") + in + aux Svs.empty t + +let add_rule t e = + let vars, ls, args, r = extract_rule e.known_map t in + let rules = try Mls.find ls e.rules with Not_found -> [] in + { e with rules = Mls.add ls ((vars, args, r) :: rules) e.rules } diff --git a/src/reduction_engine.mli b/src/reduction_engine.mli new file mode 100644 index 0000000000000000000000000000000000000000..53407b21dc4ffbc5d16c4fe31d0ae29f2c474ded --- /dev/null +++ b/src/reduction_engine.mli @@ -0,0 +1,147 @@ +(********************************************************************) +(* *) +(* The Why3 Verification Platform / The Why3 Development Team *) +(* Copyright 2010-2022 -- Inria - CNRS - Paris-Saclay University *) +(* *) +(* This software is distributed under the terms of the GNU Lesser *) +(* General Public License version 2.1, with the special exception *) +(* on linking described in file LICENSE. *) +(* *) +(********************************************************************) + +(** A reduction engine for Why3 terms *) + +(* terms are normalized with respect to + + 1) built-in computation rules + + a) on propositional connectives, e.g. + + f /\ true -> f + + b) on integers, e.g. + + 35 + 7 -> 42 + + c) on projections of pairs and of other ADTs, e.g + + fst (x,y) -> x + + cdr (Cons x y) -> y + + d) on defined function symbols, e.g. + + function sqr (x:int) = x * x + + sqr 4 -> 4 * 4 -> 16 + + sqr x -> x * x + + e) (TODO?) on booleans, e.g. + + True xor False -> True + + f) (TODO?) on reals, e.g. + + 1.0 + 2.5 -> 3.5 + + 2) axioms declared as rewrite rules, thanks to the "rewrite" metas, e.g. if + + function dot : t -> t -> t axiom assoc: forall x y z, dot (dot x y) z = dot x + (dot y z) meta "rewrite" assoc + + then + + dot (dot a b) (dot c d) -> dot a (dot b (dot c d)) + + axioms used as rewrite rules must be either of the form + + forall ... t1 = t2 + + or + + forall ... f1 <-> f2 + + where the root symbol of t1 (resp. f1) is a non-interpreted function symbol + (resp. non-interpreted predicate symbol) + + rewriting is done from left to right *) +open Why3 + +type 'a engine +(** abstract type for reduction engines *) + +val user_env : 'a engine -> 'a + +type params = { + compute_defs : bool; + compute_builtin : bool; + compute_def_set : Term.Sls.t; + compute_max_quantifier_domain : int; +} +(** Configuration of the engine. . [compute_defs]: if set to true, automatically + compute symbols using known definitions. Otherwise, only symbols in + [compute_def_set] will be computed. . [compute_builtin]: if set to true, + compute builtin functions. . [compute_max_quantifier_domain]: maximum domain + size for the reduction of bounded quantifications *) + +type value = + | Term of Why3.Term.term (* invariant: is in normal form *) + | Int of BigInt.t + | Real of Number.real_value + +type 'a builtin = + 'a engine -> Why3.Term.lsymbol -> value list -> Ty.ty option -> value + +type 'a built_in_theories = + Env.pathname + * string + * (string * (Ty.tysymbol -> unit)) list + * (string * Why3.Term.lsymbol ref option * 'a builtin) list + +val create : + params -> + Env.env -> + Decl.decl Ident.Mid.t -> + 'a -> + 'a built_in_theories list -> + 'a engine +(** [create env known_map] creates a reduction engine with . builtins theories + (int.Int, etc.) extracted from [env] . known declarations from [known_map] . + empty set of rewrite rules *) + +exception NotARewriteRule of string + +val add_rule : Term.term -> 'a engine -> 'a engine +(** [add_rule t e] turns [t] into a new rewrite rule and returns the new engine. + + raise NotARewriteRule if [t] cannot be seen as a rewrite rule according to + the general rules given above. *) + +val normalize : + ?step_limit:int -> + limit:int -> + 'a engine -> + Term.term Term.Mvs.t -> + Term.term -> + Term.term +(** [normalize e sigma t] normalizes the term [t] with respect to the engine [e] + with an initial variable environment [sigma]. + + parameter [limit] provides a maximum number of steps for execution. When + limit is reached, the partially reduced term is returned. parameter + [step_limit] provides a maximum number of steps on reductions that would + change the term even after reconstruction. *) + +open Term + +exception NoMatch of (term * term * term option) option +(** [NoMatch (t1, t2, t3)] Cannot match [t1] with [t2]. If [t3] exists then [t1] + is already matched with [t3]. *) + +exception NoMatchpat of (pattern * pattern) option + +type substitution = term Mvs.t + +val first_order_matching : + Svs.t -> term list -> term list -> Ty.ty Ty.Mtv.t * substitution diff --git a/src/verification.ml b/src/verification.ml index 8fe43e4d62d58ce27cf604b9f6ebbe4e0c3eda2e..1a7162ed2893184454caec1df7a254b7b7f768a2 100644 --- a/src/verification.ml +++ b/src/verification.ml @@ -274,10 +274,25 @@ let call_prover ?dataset ~limit config env prover config_prover driver task = let id = Task.task_goal task in { id; prover_answer; additional_info } -let verify ?(debug = false) ?format ~loadpath ?memlimit ?timelimit ?dataset - prover ?prover_altern file = +let open_file ?(debug = false) ?format ~loadpath file = if debug then Debug.set_flag (Debug.lookup_flag "call_prover"); let env, config = create_env ~debug loadpath in + let mstr_theory = + match file with + | File.Stdin -> + Env.(read_channel ?format base_language env "stdin" Caml.stdin) + | File file -> + let mlw_files, _ = Env.(read_file ?format base_language env file) in + mlw_files + | JSON jin -> + let th = JSON.theory_of_input env jin in + Wstdlib.Mstr.singleton th.th_name.id_string th + in + (env, config, mstr_theory) + +let verify ?debug ?format ~loadpath ?memlimit ?timelimit ?dataset prover + ?prover_altern file = + let env, config, mstr_theory = open_file ?debug ?format ~loadpath file in let main = Whyconf.get_main config in let limit = let memlimit = Option.value memlimit ~default:(Whyconf.memlimit main) in @@ -292,17 +307,6 @@ let verify ?(debug = false) ?format ~loadpath ?memlimit ?timelimit ?dataset Call_provers.limit_mem = memlimit; } in - let mstr_theory = - match file with - | File.Stdin -> - Env.(read_channel ?format base_language env "stdin" Caml.stdin) - | File file -> - let mlw_files, _ = Env.(read_file ?format base_language env file) in - mlw_files - | JSON jin -> - let th = JSON.theory_of_input env jin in - Wstdlib.Mstr.singleton th.th_name.id_string th - in let config_prover = let altern = let default = diff --git a/src/verification.mli b/src/verification.mli index 8dc39393c8207ff54c87d58efdc8bafd3acb83c3..0dabf9670ffe682990390fb261ee1926707ae4f4 100644 --- a/src/verification.mli +++ b/src/verification.mli @@ -23,7 +23,10 @@ open Why3 module File : sig - type t + type t = private + | Stdin + | File of string + | JSON of JSON.input val of_json_input : JSON.input -> (t, string) result val of_string : string -> (t, string) result @@ -71,3 +74,15 @@ val verify : @return for each theory, an [answer] for each goal of the theory, respecting the order of appearance. *) + +val open_file : + ?debug:bool -> + ?format:string -> + loadpath:string list -> + File.t -> + Env.env * Whyconf.config * Theory.theory Wstdlib.Mstr.t +(** [open_file ?debug ?format ~loadpath file] just opens the given file. + + @param debug when set, enables debug information. + @param format is the [file] format. + @param loadpath is the additional loadpath. *) diff --git a/stdlib/caisar.mlw b/stdlib/caisar.mlw index aeea7093ede433c836821d58fd184d9c7097a2e4..13583bb6717ccf627ea755f124a9239c4f49da89 100644 --- a/stdlib/caisar.mlw +++ b/stdlib/caisar.mlw @@ -80,3 +80,18 @@ theory NN function relu (a: t): t = if a .> (0.0:t) then a else (0.0:t) end + +theory Interpretation + use bool.Bool + use int.Int + + type input + type dataset + + function open_dataset string: dataset + function size dataset: int + + function get (i:int) (d:dataset) : input + + predicate forall_ (d: dataset) (f: input -> bool) = forall i:int. -1 < i < size d -> f (get i d) +end diff --git a/tests/datasets/a/a001.png b/tests/datasets/a/a001.png new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/datasets/a/a002.png b/tests/datasets/a/a002.png new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/dune b/tests/dune index 26d29551ce04dff1407fd7b9534efbadc0354950..8db19e46cb68c6ba9fc2e8eac42565dbcb5c0514 100644 --- a/tests/dune +++ b/tests/dune @@ -10,5 +10,6 @@ bin/aimos bin/cvc5 bin/nnenum.sh - filter_tmpdir.sh) + filter_tmpdir.sh + (glob_files "datasets/a/*")) (package caisar)) diff --git a/tests/interpretation.t b/tests/interpretation.t new file mode 100644 index 0000000000000000000000000000000000000000..31dcd1a89e47f5b01d3aa3242820e98199857992 --- /dev/null +++ b/tests/interpretation.t @@ -0,0 +1,43 @@ +Test interpret + $ caisar interpret -L . --format whyml - 2>&1 <<EOF | ./filter_tmpdir.sh + > theory T + > use caisar.Interpretation + > use int.Int + > + > goal G1: 1+1=2 + > + > goal G2: 1+1=3 + > + > goal G3: size (open_dataset "datasets/a") = 2 + > + > goal G4: + > let dataset = open_dataset "datasets/a" in + > size dataset = 2 + > + > predicate robust (i: input) + > + > goal G5: + > let dataset = open_dataset "datasets/a" in + > forall_ dataset (fun i -> robust i) + > + > goal G6: + > let dataset = open_dataset "datasets/a" in + > forall i:int. i=1+(size dataset) -> i < 4 + > end + > EOF + G1 : true + + G2 : false + + G3 : true + caisar_op, + (Interpretation.Dataset { Interpretation.dataset = "datasets/a" }) + G4 : true + caisar_op1, + (Interpretation.Dataset { Interpretation.dataset = "datasets/a" }) + G5 : robust (get 0 caisar_op2) /\ robust (get 1 caisar_op2) + caisar_op2, + (Interpretation.Dataset { Interpretation.dataset = "datasets/a" }) + G6 : forall i:int. i = 3 -> i < 4 + caisar_op3, + (Interpretation.Dataset { Interpretation.dataset = "datasets/a" })