src/HOL/Tools/Quickcheck/exhaustive_generators.ML
changeset 42195 1e7b62c93f5d
parent 42176 c7b6d8d9922e
child 42214 9ca13615c619
equal deleted inserted replaced
42194:bd416284a432 42195:1e7b62c93f5d
     7 signature EXHAUSTIVE_GENERATORS =
     7 signature EXHAUSTIVE_GENERATORS =
     8 sig
     8 sig
     9   val compile_generator_expr:
     9   val compile_generator_expr:
    10     Proof.context -> (term * term list) list -> int list -> term list option * Quickcheck.report option
    10     Proof.context -> (term * term list) list -> int list -> 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 put_counterexample: (unit -> int -> int -> term list option)
    13   val put_counterexample: (unit -> int -> int -> term list option)
    13     -> Proof.context -> Proof.context
    14     -> Proof.context -> Proof.context
    14   val put_counterexample_batch: (unit -> (int -> term list option) list)
    15   val put_counterexample_batch: (unit -> (int -> term list option) list)
    15     -> Proof.context -> Proof.context
    16     -> Proof.context -> Proof.context
    16   val smart_quantifier : bool Config.T;
    17   val put_validator_batch: (unit -> (int -> bool) list) -> Proof.context -> Proof.context
    17   val quickcheck_pretty : bool Config.T;
    18   val smart_quantifier : bool Config.T
       
    19   val quickcheck_pretty : bool Config.T
    18   val setup: theory -> theory
    20   val setup: theory -> theory
    19 end;
    21 end;
    20 
    22 
    21 structure Exhaustive_Generators : EXHAUSTIVE_GENERATORS =
    23 structure Exhaustive_Generators : EXHAUSTIVE_GENERATORS =
    22 struct
    24 struct
   236     val none_t = @{term "None :: term list option"}
   238     val none_t = @{term "None :: term list option"}
   237     fun mk_safe_if (cond, then_t, else_t) =
   239     fun mk_safe_if (cond, then_t, else_t) =
   238       @{term "Quickcheck_Exhaustive.catch_match :: term list option => term list option => term list option"} $
   240       @{term "Quickcheck_Exhaustive.catch_match :: term list option => term list option => term list option"} $
   239         (@{term "If :: bool => term list option => term list option => term list option"}
   241         (@{term "If :: bool => term list option => term list option => term list option"}
   240         $ cond $ then_t $ else_t) $ none_t;
   242         $ cond $ then_t $ else_t) $ none_t;
   241     fun strip_imp (Const(@{const_name HOL.implies},_) $ A $ B) = apfst (cons A) (strip_imp B)
       
   242         | strip_imp A = ([], A)
       
   243     fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v)
   243     fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v)
   244     fun mk_naive_test_term t =
   244     fun mk_naive_test_term t =
   245       fold_rev mk_exhaustive_closure (frees ~~ term_vars) (mk_safe_if (t, none_t, return)) 
   245       fold_rev mk_exhaustive_closure (frees ~~ term_vars) (mk_safe_if (t, none_t, return)) 
   246     fun mk_smart_test_term' concl bound_vars assms =
   246     fun mk_smart_test_term' concl bound_vars assms =
   247       let
   247       let
   253       in
   253       in
   254         fold_rev mk_exhaustive_closure (map lookup vars) (mk_safe_if check)
   254         fold_rev mk_exhaustive_closure (map lookup vars) (mk_safe_if check)
   255       end
   255       end
   256     fun mk_smart_test_term t =
   256     fun mk_smart_test_term t =
   257       let
   257       let
   258         val (assms, concl) = strip_imp t
   258         val (assms, concl) = Quickcheck_Common.strip_imp t
   259       in
   259       in
   260         mk_smart_test_term' concl [] assms
   260         mk_smart_test_term' concl [] assms
   261       end
   261       end
   262     val mk_test_term =
   262     val mk_test_term =
   263       if Config.get ctxt smart_quantifier then mk_smart_test_term else mk_naive_test_term 
   263       if Config.get ctxt smart_quantifier then mk_smart_test_term else mk_naive_test_term 
   265 
   265 
   266 val mk_parametric_generator_expr =
   266 val mk_parametric_generator_expr =
   267   Quickcheck_Common.gen_mk_parametric_generator_expr 
   267   Quickcheck_Common.gen_mk_parametric_generator_expr 
   268     ((mk_generator_expr, absdummy (@{typ "code_numeral"}, @{term "None :: term list option"})),
   268     ((mk_generator_expr, absdummy (@{typ "code_numeral"}, @{term "None :: term list option"})),
   269       @{typ "code_numeral => term list option"})
   269       @{typ "code_numeral => term list option"})
   270   
   270 
       
   271 fun mk_validator_expr ctxt t =
       
   272   let
       
   273     fun bounded_forallT T = (T --> @{typ bool}) --> @{typ code_numeral} --> @{typ bool}
       
   274     val thy = ProofContext.theory_of ctxt
       
   275     val ctxt' = Variable.auto_fixes t ctxt
       
   276     val ([depth_name], ctxt'') = Variable.variant_fixes ["depth"] ctxt'
       
   277     val depth = Free (depth_name, @{typ code_numeral})
       
   278     fun mk_bounded_forall (s, T) t =
       
   279       Const (@{const_name "Quickcheck_Exhaustive.bounded_forall_class.bounded_forall"}, bounded_forallT T)
       
   280         $ lambda (Free (s, T)) t $ depth
       
   281     fun mk_implies (cond, then_t) =
       
   282       @{term "If :: bool => bool => bool => bool"} $ cond $ then_t $ @{term False}
       
   283     fun mk_naive_test_term t = fold_rev mk_bounded_forall (Term.add_frees t []) t 
       
   284     fun mk_smart_test_term' concl bound_frees assms =
       
   285       let
       
   286         fun vars_of t = subtract (op =) bound_frees (Term.add_frees t [])
       
   287         val (vars, check) =
       
   288           case assms of [] => (vars_of concl, concl)
       
   289             | assm :: assms => (vars_of assm, mk_implies (assm,
       
   290                 mk_smart_test_term' concl (union (op =) (vars_of assm) bound_frees) assms))
       
   291       in
       
   292         fold_rev mk_bounded_forall vars check
       
   293       end
       
   294     fun mk_smart_test_term t =
       
   295       let
       
   296         val (assms, concl) = Quickcheck_Common.strip_imp t
       
   297       in
       
   298         mk_smart_test_term' concl [] assms
       
   299       end
       
   300     val mk_test_term =
       
   301       if Config.get ctxt smart_quantifier then mk_smart_test_term else mk_naive_test_term 
       
   302   in lambda depth (mk_test_term t) end
       
   303 
       
   304 
   271 (** generator compiliation **)
   305 (** generator compiliation **)
   272 
   306 
   273 structure Counterexample = Proof_Data
   307 structure Counterexample = Proof_Data
   274 (
   308 (
   275   type T = unit -> int -> int -> term list option
   309   type T = unit -> int -> int -> term list option
   284   (* FIXME avoid user error with non-user text *)
   318   (* FIXME avoid user error with non-user text *)
   285   fun init _ () = error "Counterexample"
   319   fun init _ () = error "Counterexample"
   286 );
   320 );
   287 val put_counterexample_batch = Counterexample_Batch.put;
   321 val put_counterexample_batch = Counterexample_Batch.put;
   288 
   322 
       
   323 structure Validator_Batch = Proof_Data
       
   324 (
       
   325   type T = unit -> (int -> bool) list
       
   326   (* FIXME avoid user error with non-user text *)
       
   327   fun init _ () = error "Counterexample"
       
   328 );
       
   329 val put_validator_batch = Validator_Batch.put;
       
   330 
       
   331 
   289 val target = "Quickcheck";
   332 val target = "Quickcheck";
   290   
   333   
   291 fun compile_generator_expr ctxt ts =
   334 fun compile_generator_expr ctxt ts =
   292   let
   335   let
   293     val thy = ProofContext.theory_of ctxt
   336     val thy = ProofContext.theory_of ctxt
   294     val t' = mk_parametric_generator_expr ctxt ts;
   337     val t' = mk_parametric_generator_expr ctxt ts;
   295     val compile = Code_Runtime.dynamic_value_strict
   338     val compile = Code_Runtime.dynamic_value_strict
   296       (Counterexample.get, put_counterexample, "Exhaustive_Generators.put_counterexample")
   339       (Counterexample.get, put_counterexample, "Exhaustive_Generators.put_counterexample")
   297       thy (SOME target) (fn proc => fn g =>
   340       thy (SOME target) (fn proc => fn g =>
   298         fn card => fn size => g card size |> (Option.map o map) proc) t' [];
   341         fn card => fn size => g card size |> (Option.map o map) proc) t' []
   299   in
   342   in
   300     fn [card, size] => rpair NONE (compile card size |> 
   343     fn [card, size] => rpair NONE (compile card size |> 
   301       (if Config.get ctxt quickcheck_pretty then
   344       (if Config.get ctxt quickcheck_pretty then
   302         Option.map (map Quickcheck_Common.post_process_term) else I))
   345         Option.map (map Quickcheck_Common.post_process_term) else I))
   303   end;
   346   end;
   308     val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts;
   351     val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts;
   309     val compiles = Code_Runtime.dynamic_value_strict
   352     val compiles = Code_Runtime.dynamic_value_strict
   310       (Counterexample_Batch.get, put_counterexample_batch,
   353       (Counterexample_Batch.get, put_counterexample_batch,
   311         "Exhaustive_Generators.put_counterexample_batch")
   354         "Exhaustive_Generators.put_counterexample_batch")
   312       thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc))
   355       thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc))
   313       (HOLogic.mk_list @{typ "code_numeral => term list option"} ts') [];
   356       (HOLogic.mk_list @{typ "code_numeral => term list option"} ts') []
   314   in
   357   in
   315     map (fn compile => fn size => compile size
   358     map (fn compile => fn size => compile size
   316       |> Option.map (map Quickcheck_Common.post_process_term)) compiles
   359       |> Option.map (map Quickcheck_Common.post_process_term)) compiles
   317   end;
   360   end;
   318   
   361 
       
   362 fun compile_validator_exprs ctxt ts =
       
   363   let
       
   364     val thy = ProofContext.theory_of ctxt
       
   365     val ts' = map (mk_validator_expr ctxt) ts;
       
   366   in
       
   367     Code_Runtime.dynamic_value_strict
       
   368       (Validator_Batch.get, put_validator_batch, "Exhaustive_Generators.put_validator_batch")
       
   369       thy (SOME target) (K I) (HOLogic.mk_list @{typ "code_numeral => bool"} ts') []
       
   370   end;
       
   371 
   319 (** setup **)
   372 (** setup **)
   320 
   373 
   321 val setup =
   374 val setup =
   322   Datatype.interpretation
   375   Datatype.interpretation
   323     (Quickcheck_Common.ensure_sort_datatype (@{sort exhaustive}, instantiate_exhaustive_datatype))
   376     (Quickcheck_Common.ensure_sort_datatype (@{sort exhaustive}, instantiate_exhaustive_datatype))
   324   #> setup_smart_quantifier
   377   #> setup_smart_quantifier
   325   #> setup_quickcheck_pretty
   378   #> setup_quickcheck_pretty
   326   #> Context.theory_map (Quickcheck.add_generator ("exhaustive", compile_generator_expr))
   379   #> Context.theory_map (Quickcheck.add_generator ("exhaustive", compile_generator_expr))
   327   #> Context.theory_map (Quickcheck.add_batch_generator ("exhaustive", compile_generator_exprs));
   380   #> Context.theory_map (Quickcheck.add_batch_generator ("exhaustive", compile_generator_exprs))
       
   381   #> Context.theory_map (Quickcheck.add_batch_validator ("exhaustive", compile_validator_exprs));
   328 
   382 
   329 end;
   383 end;