src/HOL/Tools/Quickcheck/exhaustive_generators.ML
changeset 51143 0a2371e7ced3
parent 51126 df86080de4cb
child 51672 d5c5e088ebdf
equal deleted inserted replaced
51142:ac9e909fe55d 51143:0a2371e7ced3
     8 sig
     8 sig
     9   val compile_generator_expr:
     9   val compile_generator_expr:
    10     Proof.context -> (term * term list) list -> bool -> int list -> (bool * term list) option * Quickcheck.report option
    10     Proof.context -> (term * term list) list -> bool -> int list -> (bool * term list) option * Quickcheck.report option
    11   val compile_generator_exprs: Proof.context -> term list -> (int -> term list option) list
    11   val compile_generator_exprs: Proof.context -> term list -> (int -> term list option) list
    12   val compile_validator_exprs: Proof.context -> term list -> (int -> bool) list
    12   val compile_validator_exprs: Proof.context -> term list -> (int -> bool) list
    13   val put_counterexample: (unit -> int -> bool -> int -> (bool * term list) option)
    13   val put_counterexample: (unit -> Code_Numeral.natural -> bool -> Code_Numeral.natural -> (bool * term list) option)
    14     -> Proof.context -> Proof.context
    14     -> Proof.context -> Proof.context
    15   val put_counterexample_batch: (unit -> (int -> term list option) list)
    15   val put_counterexample_batch: (unit -> (Code_Numeral.natural -> term list option) list)
    16     -> Proof.context -> Proof.context
    16     -> Proof.context -> Proof.context
    17   val put_validator_batch: (unit -> (int -> bool) list) -> Proof.context -> Proof.context
    17   val put_validator_batch: (unit -> (Code_Numeral.natural -> bool) list) -> Proof.context -> Proof.context
    18   exception Counterexample of term list
    18   exception Counterexample of term list
    19   val smart_quantifier : bool Config.T
    19   val smart_quantifier : bool Config.T
    20   val optimise_equality : bool Config.T
    20   val optimise_equality : bool Config.T
    21   val quickcheck_pretty : bool Config.T
    21   val quickcheck_pretty : bool Config.T
    22   val setup_exhaustive_datatype_interpretation : theory -> theory
    22   val setup_exhaustive_datatype_interpretation : theory -> theory
    48 
    48 
    49 (** abstract syntax **)
    49 (** abstract syntax **)
    50 
    50 
    51 fun termifyT T = HOLogic.mk_prodT (T, @{typ "unit => Code_Evaluation.term"});
    51 fun termifyT T = HOLogic.mk_prodT (T, @{typ "unit => Code_Evaluation.term"});
    52 
    52 
    53 val size = @{term "i :: code_numeral"}
    53 val size = @{term "i :: natural"}
    54 val size_pred = @{term "(i :: code_numeral) - 1"}
    54 val size_pred = @{term "(i :: natural) - 1"}
    55 val size_ge_zero = @{term "(i :: code_numeral) > 0"}
    55 val size_ge_zero = @{term "(i :: natural) > 0"}
    56 
    56 
    57 fun mk_none_continuation (x, y) =
    57 fun mk_none_continuation (x, y) =
    58   let
    58   let
    59     val (T as Type(@{type_name "option"}, _)) = fastype_of x
    59     val (T as Type(@{type_name "option"}, _)) = fastype_of x
    60   in
    60   in
    79 val exhaustiveN = "exhaustive";
    79 val exhaustiveN = "exhaustive";
    80 val full_exhaustiveN = "full_exhaustive";
    80 val full_exhaustiveN = "full_exhaustive";
    81 val bounded_forallN = "bounded_forall";
    81 val bounded_forallN = "bounded_forall";
    82 
    82 
    83 fun fast_exhaustiveT T = (T --> @{typ unit})
    83 fun fast_exhaustiveT T = (T --> @{typ unit})
    84   --> @{typ code_numeral} --> @{typ unit}
    84   --> @{typ natural} --> @{typ unit}
    85 
    85 
    86 fun exhaustiveT T = (T --> resultT) --> @{typ code_numeral} --> resultT
    86 fun exhaustiveT T = (T --> resultT) --> @{typ natural} --> resultT
    87 
    87 
    88 fun bounded_forallT T = (T --> @{typ bool}) --> @{typ code_numeral} --> @{typ bool}
    88 fun bounded_forallT T = (T --> @{typ bool}) --> @{typ natural} --> @{typ bool}
    89 
    89 
    90 fun full_exhaustiveT T = (termifyT T --> resultT) --> @{typ code_numeral} --> resultT
    90 fun full_exhaustiveT T = (termifyT T --> resultT) --> @{typ natural} --> resultT
    91 
    91 
    92 fun check_allT T = (termifyT T --> resultT) --> resultT
    92 fun check_allT T = (termifyT T --> resultT) --> resultT
    93 
    93 
    94 fun mk_equation_terms generics (descr, vs, Ts) =
    94 fun mk_equation_terms generics (descr, vs, Ts) =
    95   let
    95   let
   321     val ctxt' = Variable.auto_fixes t ctxt
   321     val ctxt' = Variable.auto_fixes t ctxt
   322     val names = Term.add_free_names t []
   322     val names = Term.add_free_names t []
   323     val frees = map Free (Term.add_frees t [])
   323     val frees = map Free (Term.add_frees t [])
   324     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   324     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   325     val ([depth_name], _) = Variable.variant_fixes ["depth"] ctxt'
   325     val ([depth_name], _) = Variable.variant_fixes ["depth"] ctxt'
   326     val depth = Free (depth_name, @{typ code_numeral})
   326     val depth = Free (depth_name, @{typ natural})
   327     fun return _ = @{term "throw_Counterexample :: term list => unit"} $
   327     fun return _ = @{term "throw_Counterexample :: term list => unit"} $
   328       (HOLogic.mk_list @{typ "term"}
   328       (HOLogic.mk_list @{typ "term"}
   329         (map (fn t => HOLogic.mk_term_of (fastype_of t) t) (frees @ eval_terms)))
   329         (map (fn t => HOLogic.mk_term_of (fastype_of t) t) (frees @ eval_terms)))
   330     fun mk_exhaustive_closure (free as Free (_, T)) t =
   330     fun mk_exhaustive_closure (free as Free (_, T)) t =
   331       Const (@{const_name "Quickcheck_Exhaustive.fast_exhaustive_class.fast_exhaustive"},
   331       Const (@{const_name "Quickcheck_Exhaustive.fast_exhaustive_class.fast_exhaustive"},
   351     val names = Term.add_free_names t []
   351     val names = Term.add_free_names t []
   352     val frees = map Free (Term.add_frees t [])
   352     val frees = map Free (Term.add_frees t [])
   353     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   353     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   354     val ([depth_name, genuine_only_name], _) =
   354     val ([depth_name, genuine_only_name], _) =
   355       Variable.variant_fixes ["depth", "genuine_only"] ctxt'
   355       Variable.variant_fixes ["depth", "genuine_only"] ctxt'
   356     val depth = Free (depth_name, @{typ code_numeral})
   356     val depth = Free (depth_name, @{typ natural})
   357     val genuine_only = Free (genuine_only_name, @{typ bool}) 
   357     val genuine_only = Free (genuine_only_name, @{typ bool}) 
   358     val return = mk_return (HOLogic.mk_list @{typ "term"}
   358     val return = mk_return (HOLogic.mk_list @{typ "term"}
   359         (map (fn t => HOLogic.mk_term_of (fastype_of t) t) frees @ map mk_safe_term eval_terms))
   359         (map (fn t => HOLogic.mk_term_of (fastype_of t) t) frees @ map mk_safe_term eval_terms))
   360     fun mk_exhaustive_closure (free as Free (_, T)) t =
   360     fun mk_exhaustive_closure (free as Free (_, T)) t =
   361       Const (@{const_name "Quickcheck_Exhaustive.exhaustive_class.exhaustive"}, exhaustiveT T)
   361       Const (@{const_name "Quickcheck_Exhaustive.exhaustive_class.exhaustive"}, exhaustiveT T)
   373     val names = Term.add_free_names t []
   373     val names = Term.add_free_names t []
   374     val frees = map Free (Term.add_frees t [])
   374     val frees = map Free (Term.add_frees t [])
   375     val ([depth_name, genuine_only_name], ctxt'') =
   375     val ([depth_name, genuine_only_name], ctxt'') =
   376       Variable.variant_fixes ["depth", "genuine_only"] ctxt'
   376       Variable.variant_fixes ["depth", "genuine_only"] ctxt'
   377     val (term_names, _) = Variable.variant_fixes (map (prefix "t_") names) ctxt''
   377     val (term_names, _) = Variable.variant_fixes (map (prefix "t_") names) ctxt''
   378     val depth = Free (depth_name, @{typ code_numeral})
   378     val depth = Free (depth_name, @{typ natural})
   379     val genuine_only = Free (genuine_only_name, @{typ bool})    
   379     val genuine_only = Free (genuine_only_name, @{typ bool})    
   380     val term_vars = map (fn n => Free (n, @{typ "unit => term"})) term_names
   380     val term_vars = map (fn n => Free (n, @{typ "unit => term"})) term_names
   381     fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v)
   381     fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v)
   382     val return = mk_return (HOLogic.mk_list @{typ term}
   382     val return = mk_return (HOLogic.mk_list @{typ term}
   383           (map (fn v => v $ @{term "()"}) term_vars @ map mk_safe_term eval_terms))
   383           (map (fn v => v $ @{term "()"}) term_vars @ map mk_safe_term eval_terms))
   400   in lambda genuine_only (lambda depth (mk_test_term t)) end
   400   in lambda genuine_only (lambda depth (mk_test_term t)) end
   401 
   401 
   402 fun mk_parametric_generator_expr mk_generator_expr =
   402 fun mk_parametric_generator_expr mk_generator_expr =
   403   Quickcheck_Common.gen_mk_parametric_generator_expr 
   403   Quickcheck_Common.gen_mk_parametric_generator_expr 
   404     ((mk_generator_expr,
   404     ((mk_generator_expr,
   405       absdummy @{typ bool} (absdummy @{typ "code_numeral"} (Const (@{const_name "None"}, resultT)))),
   405       absdummy @{typ bool} (absdummy @{typ natural} (Const (@{const_name "None"}, resultT)))),
   406       @{typ bool} --> @{typ "code_numeral"} --> resultT)
   406       @{typ bool} --> @{typ natural} --> resultT)
   407 
   407 
   408 fun mk_validator_expr ctxt t =
   408 fun mk_validator_expr ctxt t =
   409   let
   409   let
   410     fun bounded_forallT T = (T --> @{typ bool}) --> @{typ code_numeral} --> @{typ bool}
   410     fun bounded_forallT T = (T --> @{typ bool}) --> @{typ natural} --> @{typ bool}
   411     val ctxt' = Variable.auto_fixes t ctxt
   411     val ctxt' = Variable.auto_fixes t ctxt
   412     val names = Term.add_free_names t []
   412     val names = Term.add_free_names t []
   413     val frees = map Free (Term.add_frees t [])
   413     val frees = map Free (Term.add_frees t [])
   414     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   414     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   415     val ([depth_name], _) = Variable.variant_fixes ["depth"] ctxt'
   415     val ([depth_name], _) = Variable.variant_fixes ["depth"] ctxt'
   416     val depth = Free (depth_name, @{typ code_numeral})
   416     val depth = Free (depth_name, @{typ natural})
   417     fun mk_bounded_forall (Free (s, T)) t =
   417     fun mk_bounded_forall (Free (s, T)) t =
   418       Const (@{const_name "Quickcheck_Exhaustive.bounded_forall_class.bounded_forall"}, bounded_forallT T)
   418       Const (@{const_name "Quickcheck_Exhaustive.bounded_forall_class.bounded_forall"}, bounded_forallT T)
   419         $ lambda (Free (s, T)) t $ depth
   419         $ lambda (Free (s, T)) t $ depth
   420     fun mk_safe_if (cond, then_t, else_t) genuine = mk_if (cond, then_t, else_t genuine)
   420     fun mk_safe_if (cond, then_t, else_t) genuine = mk_if (cond, then_t, else_t genuine)
   421     fun mk_let _ def v_opt t e = mk_let_expr (the_default def v_opt, t, e)
   421     fun mk_let _ def v_opt t e = mk_let_expr (the_default def v_opt, t, e)
   441 
   441 
   442 (* FIXME just one data slot (record) per program unit *)
   442 (* FIXME just one data slot (record) per program unit *)
   443 
   443 
   444 structure Counterexample = Proof_Data
   444 structure Counterexample = Proof_Data
   445 (
   445 (
   446   type T = unit -> int -> bool -> int -> (bool * term list) option
   446   type T = unit -> Code_Numeral.natural -> bool -> Code_Numeral.natural -> (bool * term list) option
   447   (* FIXME avoid user error with non-user text *)
   447   (* FIXME avoid user error with non-user text *)
   448   fun init _ () = error "Counterexample"
   448   fun init _ () = error "Counterexample"
   449 );
   449 );
   450 val put_counterexample = Counterexample.put;
   450 val put_counterexample = Counterexample.put;
   451 
   451 
   452 structure Counterexample_Batch = Proof_Data
   452 structure Counterexample_Batch = Proof_Data
   453 (
   453 (
   454   type T = unit -> (int -> term list option) list
   454   type T = unit -> (Code_Numeral.natural -> term list option) list
   455   (* FIXME avoid user error with non-user text *)
   455   (* FIXME avoid user error with non-user text *)
   456   fun init _ () = error "Counterexample"
   456   fun init _ () = error "Counterexample"
   457 );
   457 );
   458 val put_counterexample_batch = Counterexample_Batch.put;
   458 val put_counterexample_batch = Counterexample_Batch.put;
   459 
   459 
   460 structure Validator_Batch = Proof_Data
   460 structure Validator_Batch = Proof_Data
   461 (
   461 (
   462   type T = unit -> (int -> bool) list
   462   type T = unit -> (Code_Numeral.natural -> bool) list
   463   (* FIXME avoid user error with non-user text *)
   463   (* FIXME avoid user error with non-user text *)
   464   fun init _ () = error "Counterexample"
   464   fun init _ () = error "Counterexample"
   465 );
   465 );
   466 val put_validator_batch = Validator_Batch.put;
   466 val put_validator_batch = Validator_Batch.put;
   467 
   467 
   468 
   468 
   469 val target = "Quickcheck";
   469 val target = "Quickcheck";
   470 
   470 
   471 fun compile_generator_expr ctxt ts =
   471 fun compile_generator_expr_raw ctxt ts =
   472   let
   472   let
   473     val thy = Proof_Context.theory_of ctxt
   473     val thy = Proof_Context.theory_of ctxt
   474     val mk_generator_expr = 
   474     val mk_generator_expr = 
   475       if Config.get ctxt fast then mk_fast_generator_expr
   475       if Config.get ctxt fast then mk_fast_generator_expr
   476       else if Config.get ctxt bounded_forall then mk_bounded_forall_generator_expr
   476       else if Config.get ctxt bounded_forall then mk_bounded_forall_generator_expr
   485     fn genuine_only => fn [card, size] => rpair NONE (compile card genuine_only size |> 
   485     fn genuine_only => fn [card, size] => rpair NONE (compile card genuine_only size |> 
   486       (if Config.get ctxt quickcheck_pretty then
   486       (if Config.get ctxt quickcheck_pretty then
   487         Option.map (apsnd (map Quickcheck_Common.post_process_term)) else I))
   487         Option.map (apsnd (map Quickcheck_Common.post_process_term)) else I))
   488   end;
   488   end;
   489 
   489 
   490 fun compile_generator_exprs ctxt ts =
   490 fun compile_generator_expr ctxt ts =
       
   491   let
       
   492     val compiled = compile_generator_expr_raw ctxt ts;
       
   493   in fn genuine_only => fn [card, size] =>
       
   494     compiled genuine_only [Code_Numeral.natural_of_integer card, Code_Numeral.natural_of_integer size]
       
   495   end;
       
   496 
       
   497 fun compile_generator_exprs_raw ctxt ts =
   491   let
   498   let
   492     val thy = Proof_Context.theory_of ctxt
   499     val thy = Proof_Context.theory_of ctxt
   493     val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts;
   500     val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts;
   494     val compiles = Code_Runtime.dynamic_value_strict
   501     val compiles = Code_Runtime.dynamic_value_strict
   495       (Counterexample_Batch.get, put_counterexample_batch,
   502       (Counterexample_Batch.get, put_counterexample_batch,
   496         "Exhaustive_Generators.put_counterexample_batch")
   503         "Exhaustive_Generators.put_counterexample_batch")
   497       thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc))
   504       thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc))
   498       (HOLogic.mk_list @{typ "code_numeral => term list option"} ts') []
   505       (HOLogic.mk_list @{typ "natural => term list option"} ts') []
   499   in
   506   in
   500     map (fn compile => fn size => compile size
   507     map (fn compile => fn size => compile size
   501       |> Option.map (map Quickcheck_Common.post_process_term)) compiles
   508       |> (Option.map o map) Quickcheck_Common.post_process_term) compiles
   502   end;
   509   end;
   503 
   510 
   504 fun compile_validator_exprs ctxt ts =
   511 fun compile_generator_exprs ctxt ts =
       
   512   compile_generator_exprs_raw ctxt ts
       
   513   |> map (fn f => fn size => f (Code_Numeral.natural_of_integer size));
       
   514 
       
   515 fun compile_validator_exprs_raw ctxt ts =
   505   let
   516   let
   506     val thy = Proof_Context.theory_of ctxt
   517     val thy = Proof_Context.theory_of ctxt
   507     val ts' = map (mk_validator_expr ctxt) ts
   518     val ts' = map (mk_validator_expr ctxt) ts
   508   in
   519   in
   509     Code_Runtime.dynamic_value_strict
   520     Code_Runtime.dynamic_value_strict
   510       (Validator_Batch.get, put_validator_batch, "Exhaustive_Generators.put_validator_batch")
   521       (Validator_Batch.get, put_validator_batch, "Exhaustive_Generators.put_validator_batch")
   511       thy (SOME target) (K I) (HOLogic.mk_list @{typ "code_numeral => bool"} ts') []
   522       thy (SOME target) (K I) (HOLogic.mk_list @{typ "natural => bool"} ts') []
   512   end;
   523   end;
       
   524 
       
   525 fun compile_validator_exprs ctxt ts =
       
   526   compile_validator_exprs_raw ctxt ts
       
   527   |> map (fn f => fn size => f (Code_Numeral.natural_of_integer size));
   513 
   528 
   514 fun size_matters_for thy Ts = not (forall (fn T => Sign.of_sort thy (T,  @{sort check_all})) Ts)
   529 fun size_matters_for thy Ts = not (forall (fn T => Sign.of_sort thy (T,  @{sort check_all})) Ts)
   515 
   530 
   516 val test_goals =
   531 val test_goals =
   517   Quickcheck_Common.generator_test_goal_terms ("exhaustive", (size_matters_for, compile_generator_expr));
   532   Quickcheck_Common.generator_test_goal_terms ("exhaustive", (size_matters_for, compile_generator_expr));