(* Title: Tools/induct.ML Author: Markus Wenzel, TU Muenchen
Proof by cases, induction, and coinduction.
*)
signature INDUCT_ARGS = sig val cases_default: thm val atomize: thm list val rulify: thm list val rulify_fallback: thm list val equal_def: thm val dest_def: term -> (term * term) option val trivial_tac: Proof.context -> int -> tactic end;
signature INDUCT = sig (*rule declarations*) val vars_of: term -> term list val dest_rules: Proof.context ->
{type_cases: (string * thm) list, pred_cases: (string * thm) list,
type_induct: (string * thm) list, pred_induct: (string * thm) list,
type_coinduct: (string * thm) list, pred_coinduct: (string * thm) list} val print_rules: Proof.context -> unit val lookup_casesT: Proof.context -> string -> thm option val lookup_casesP: Proof.context -> string -> thm option val lookup_inductT: Proof.context -> string -> thm option val lookup_inductP: Proof.context -> string -> thm option val lookup_coinductT: Proof.context -> string -> thm option val lookup_coinductP: Proof.context -> string -> thm option val find_casesT: Proof.context -> typ -> thm list val find_casesP: Proof.context -> term -> thm list val find_inductT: Proof.context -> typ -> thm list val find_inductP: Proof.context -> term -> thm list val find_coinductT: Proof.context -> typ -> thm list val find_coinductP: Proof.context -> term -> thm list val cases_type: string -> attribute val cases_pred: string -> attribute val cases_del: attribute val induct_type: string -> attribute val induct_pred: string -> attribute val induct_del: attribute val coinduct_type: string -> attribute val coinduct_pred: string -> attribute val coinduct_del: attribute val map_simpset: (Proof.context -> Proof.context) -> Context.generic -> Context.generic val induct_simp_add: attribute val induct_simp_del: attribute val no_simpN: string val casesN: string val inductN: string val coinductN: string val typeN: string val predN: string val setN: string (*proof methods*) val arbitrary_tac: Proof.context -> int -> (string * typ) list -> int -> tactic val add_defs: (binding option * (term * bool)) optionlist -> Proof.context ->
(term optionlist * thm list) * Proof.context val atomize_term: Proof.context -> term -> term val atomize_cterm: Proof.context -> conv val atomize_tac: Proof.context -> int -> tactic val inner_atomize_tac: Proof.context -> int -> tactic val rulified_term: Proof.context -> thm -> term val rulify_tac: Proof.context -> int -> tactic val simplified_rule: Proof.context -> thm -> thm val simplify_tac: Proof.context -> int -> tactic val trivial_tac: Proof.context -> int -> tactic val rotate_tac: int -> int -> int -> tactic val internalize: Proof.context -> int -> thm -> thm val guess_instance: Proof.context -> thm -> int -> thm -> thm Seq.seq val cases_context_tactic: bool -> term optionlistlist -> thm option ->
thm list -> int -> context_tactic val cases_tac: Proof.context -> bool -> term optionlistlist -> thm option ->
thm list -> int -> tactic val get_inductT: Proof.context -> term optionlistlist -> thm listlist val gen_induct_context_tactic: ((Rule_Cases.info * int) * thm -> (Rule_Cases.info * int) * thm) -> bool -> (binding option * (term * bool)) optionlistlist ->
(string * typ) listlist -> term optionlist -> thm listoption ->
thm list -> int -> context_tactic val gen_induct_tac: Proof.context ->
((Rule_Cases.info * int) * thm -> (Rule_Cases.info * int) * thm) -> bool -> (binding option * (term * bool)) optionlistlist ->
(string * typ) listlist -> term optionlist -> thm listoption ->
thm list -> int -> tactic val induct_context_tactic: bool ->
(binding option * (term * bool)) optionlistlist ->
(string * typ) listlist -> term optionlist -> thm listoption ->
thm list -> int -> context_tactic val induct_tac: Proof.context -> bool ->
(binding option * (term * bool)) optionlistlist ->
(string * typ) listlist -> term optionlist -> thm listoption ->
thm list -> int -> tactic val coinduct_context_tactic: term optionlist -> term optionlist -> thm option ->
thm list -> int -> context_tactic val coinduct_tac: Proof.context -> term optionlist -> term optionlist -> thm option ->
thm list -> int -> tactic val gen_induct_setup: binding ->
(bool -> (binding option * (term * bool)) optionlistlist ->
(string * typ) listlist -> term optionlist -> thm listoption ->
thm list -> int -> context_tactic) -> local_theory -> local_theory end;
(** variables -- ordered left-to-right, preferring right **)
fun vars_of tm =
rev (distinct (op =) (Term.fold_aterms (fn t as Var _ => cons t | _ => I) tm []));
local
val mk_var = Net.encode_type o #2 o Term.dest_Var;
fun concl_var which thm = mk_var (which (vars_of (Thm.concl_of thm))) handleList.Empty => raise THM ("No variables in conclusion of rule", 0, [thm]);
in
fun left_var_prem thm = mk_var (hd (vars_of (hd (Thm.take_prems_of 1 thm)))) handleList.Empty => raise THM ("No variables in major premise of rule", 0, [thm]);
val left_var_concl = concl_var hd; val right_var_concl = concl_var List.last;
end;
(** constraint simplification **)
(* rearrange parameters and premises to allow application of one-point-rules *)
fun swap_params_conv ctxt i j cv = let fun conv1 0 ctxt = Conv.forall_conv (cv o snd) ctxt
| conv1 k ctxt =
Conv.rewr_conv @{thm swap_params} then_conv
Conv.forall_conv (conv1 (k - 1) o snd) ctxt; fun conv2 0 ctxt = conv1 j ctxt
| conv2 k ctxt = Conv.forall_conv (conv2 (k - 1) o snd) ctxt; in conv2 i ctxt end;
fun swap_prems_conv 0 = Conv.all_conv
| swap_prems_conv i =
Conv.implies_concl_conv (swap_prems_conv (i - 1)) then_conv
Conv.rewr_conv Drule.swap_prems_eq;
fun find_eq ctxt t = let val l = length (Logic.strip_params t); val Hs = Logic.strip_assums_hyp t; funfind (i, t) =
(case Induct_Args.dest_def (Object_Logic.drop_judgment ctxt t) of
SOME (Bound j, _) => SOME (i, j)
| SOME (_, Bound j) => SOME (i, j)
| _ => NONE); in
(case get_first find (map_index I Hs) of
NONE => NONE
| SOME (0, 0) => NONE
| SOME (i, j) => SOME (i, l - j - 1, j)) end;
fun mk_swap_rrule ctxt ct =
(case find_eq ctxt (Thm.term_of ct) of
NONE => NONE
| SOME (i, k, j) => SOME (swap_params_conv ctxt k j (K (swap_prems_conv i)) ct));
val _ =
Outer_Syntax.command \<^command_keyword>\<open>print_induct_rules\<close> "print induction and cases rules"
(Scan.succeed (Toplevel.keep (print_rules o Toplevel.context_of)));
(* access rules *)
local
fun lookup_rule which ctxt =
AList.lookup (op =) (Item_Net.content (which (get_local ctxt)))
#> Option.map (Thm.transfer' ctxt);
fun find_rules which how ctxt x =
Item_Net.retrieve (which (get_local ctxt)) (how x)
|> map (Thm.transfer' ctxt o snd);
in
val lookup_casesT = lookup_rule (#1 o #1); val lookup_casesP = lookup_rule (#2 o #1); val lookup_inductT = lookup_rule (#1 o #2); val lookup_inductP = lookup_rule (#2 o #2); val lookup_coinductT = lookup_rule (#1 o #3); val lookup_coinductP = lookup_rule (#2 o #3);
val find_casesT = find_rules (#1 o #1) Net.encode_type; val find_casesP = find_rules (#2 o #1) I; val find_inductT = find_rules (#1 o #2) Net.encode_type; val find_inductP = find_rules (#2 o #2) I; val find_coinductT = find_rules (#1 o #3) Net.encode_type; val find_coinductP = find_rules (#2 o #3) I;
end;
(** attributes **)
local
fun mk_att f g name =
Thm.mixed_attribute (fn (context, thm) => let val thm' = g thm; val context' = if Thm.is_free_dummy thm then context else Data.map (f (name, Thm.trim_context thm')) context; in (context', thm') end);
fun del_att which =
Thm.declaration_attribute (fn th => Data.map (which (apply2 (fn rs =>
fold Item_Net.remove (filter_rules rs th) rs))));
fun add_casesT rule x = @{apply 4(1)} (apfst (Item_Net.update rule)) x; fun add_casesP rule x = @{apply 4(1)} (apsnd (Item_Net.update rule)) x; fun add_inductT rule x = @{apply 4(2)} (apfst (Item_Net.update rule)) x; fun add_inductP rule x = @{apply 4(2)} (apsnd (Item_Net.update rule)) x; fun add_coinductT rule x = @{apply 4(3)} (apfst (Item_Net.update rule)) x; fun add_coinductP rule x = @{apply 4(3)} (apsnd (Item_Net.update rule)) x;
val consumes0 = Rule_Cases.default_consumes 0; val consumes1 = Rule_Cases.default_consumes 1;
in
val cases_type = mk_att add_casesT consumes0; val cases_pred = mk_att add_casesP consumes1; val cases_del = del_att @{apply 4(1)};
val induct_type = mk_att add_inductT consumes0; val induct_pred = mk_att add_inductP consumes1; val induct_del = del_att @{apply 4(2)};
val coinduct_type = mk_att add_coinductT consumes0; val coinduct_pred = mk_att add_coinductP consumes1; val coinduct_del = del_att @{apply 4(3)};
fun map_simpset f context =
context |> (Data.map o @{apply 4(4)} o Simplifier.simpset_map (Context.proof_of context)) f;
fun induct_simp f =
Thm.declaration_attribute (fn thm => map_simpset (f thm));
val induct_simp_add = induct_simp Simplifier.add_simp; val induct_simp_del = induct_simp Simplifier.del_simp;
end;
(** attribute syntax **)
val no_simpN = "no_simp"; val casesN = "cases"; val inductN = "induct"; val coinductN = "coinduct";
val typeN = "type"; val predN = "pred"; val setN = "set";
local
fun spec k arg =
Scan.lift (Args.$$$ k -- Args.colon) |-- arg ||
Scan.lift (Args.$$$ k) >> K "";
fun rulified_term ctxt thm = let val rulify = rulify_term (Proof_Context.theory_of ctxt); val (As, B) = Logic.strip_horn (Thm.prop_of thm); in Logic.list_implies (map rulify As, rulify B) end;
fun insts_env ctxt env = let val pairs = Vartab.dest (Envir.term_env env); val types = Vartab.dest (Envir.type_env env); val ts = map (Thm.cterm_of ctxt o Envir.norm_term env o #2 o #2) pairs; val xs = map #1 pairs ~~ map Thm.typ_of_cterm ts; val instT =
TVars.build (types |> fold (fn (ai, (S, T)) => TVars.add ((ai, S), Thm.ctyp_of ctxt T))); val inst = Vars.build (fold2 (fn x => fn t => Vars.add (x, t)) xs ts); in (instT, inst) end;
in
fun guess_instance ctxt rule i st = let val maxidx = Thm.maxidx_of st; val goal = Thm.term_of (Thm.cprem_of st i); (*exception Subscript*) val params = Syntax_Trans.variant_bounds ctxt goal (Logic.strip_params goal); in ifnot (null params) then
(warning ("Cannot determine rule instantiation due to pending parameter(s): " ^
commas_quote (map (Syntax.string_of_term ctxt o Syntax_Trans.mark_bound_abs) params));
Seq.single rule) else let val rule' = Thm.incr_indexes (maxidx + 1) rule; val concl = Logic.strip_assums_concl goal; in
Unify.smash_unifiers (Context.Proof ctxt)
[(Thm.concl_of rule', concl)] (Envir.empty (Thm.maxidx_of rule'))
|> Seq.map (fn env => Drule.instantiate_normalize (insts_env ctxt env) rule') end end handle General.Subscript => Seq.empty;
end;
(* special renaming of rule parameters *)
fun special_rename_params ctxt [[SOME (Free (z, Type (T, _)))]] [thm] = let val x = Name.clean (Variable.revert_fixed ctxt z); fun index _ [] = []
| index i (y :: ys) = if x = y then x ^ string_of_int i :: index (i + 1) ys else y :: index i ys; fun rename_params [] = []
| rename_params ((y, Type (U, _)) :: ys) =
(if U = T then x else y) :: rename_params ys
| rename_params ((y, _) :: ys) = y :: rename_params ys; fun rename_asm A = let val xs = rename_params (Logic.strip_params A); val xs' =
(casefilter (fn x' => x' = x) xs of
[] => xs
| [_] => xs
| _ => index 1 xs); in Logic.list_rename_params xs' A end; fun rename_prop prop = letval (As, C) = Logic.strip_horn prop in Logic.list_implies (map rename_asm As, C) end; val thm' = Thm.renamed_prop (rename_prop (Thm.prop_of thm)) thm; in [Rule_Cases.save thm thm'] end
| special_rename_params _ _ ths = ths;
(* arbitrary_tac *)
local
fun goal_prefix k ((c as \<^Const_>\<open>Pure.all _\<close>) $ Abs (a, T, B)) = c $ Abs (a, T, goal_prefix k B)
| goal_prefix 0 _ = Term.dummy_prop
| goal_prefix k ((c as \<^Const_>\<open>Pure.imp\<close>) $ A $ B) = c $ A $ goal_prefix (k - 1) B
| goal_prefix _ _ = Term.dummy_prop;
fun goal_params k \<^Const_>\<open>Pure.all _ for \<open>Abs (_, _, B)\<close>\<close> = goal_params k B + 1
| goal_params 0 _ = 0
| goal_params k \<^Const_>\<open>Pure.imp for _ B\<close> = goal_params (k - 1) B
| goal_params _ _ = 0;
fun meta_spec_tac ctxt n (x, T) = SUBGOAL (fn (goal, i) => let val v = Free (x, T); fun spec_rule prfx (xs, body) =
@{thm Pure.meta_spec}
|> Thm.rename_params_rule ([Name.clean (Variable.revert_fixed ctxt x)], 1)
|> Thm.lift_rule (Thm.cterm_of ctxt prfx)
|> `(Thm.prop_of #> Logic.strip_assums_concl)
|-> (fn pred $ arg =>
infer_instantiate ctxt
[(#1 (dest_Var (head_of pred)), Thm.cterm_of ctxt (Logic.rlist_abs (xs, body))),
(#1 (dest_Var (head_of arg)), Thm.cterm_of ctxt (Logic.rlist_abs (xs, v)))]);
fun goal_concl k xs \<^Const_>\<open>Pure.all _ for \<open>Abs (a, T, B)\<close>\<close> = goal_concl k ((a, T) :: xs) B
| goal_concl 0 xs B = ifnot (Term.exists_subterm (fn t => t aconv v) B) then NONE else SOME (xs, absfree (x, T) (Term.incr_boundvars 1 B))
| goal_concl k xs \<^Const_>\<open>Pure.imp for _ B\<close> = goal_concl (k - 1) xs B
| goal_concl _ _ _ = NONE; in
(case goal_concl n [] goal of
SOME concl =>
(compose_tac ctxt (false, spec_rule (goal_prefix n goal) concl, 1) THEN'
resolve_tac ctxt [asm_rl]) i
| NONE => all_tac) end);
fun miniscope_tac p =
CONVERSION o
Conv.params_conv p (fn ctxt =>
Simplifier.rewrite_wrt ctxt true [Thm.symmetric Drule.norm_hhf_eq]);
in
fun arbitrary_tac _ _ [] = K all_tac
| arbitrary_tac ctxt n xs = SUBGOAL (fn (goal, i) =>
(EVERY' (map (meta_spec_tac ctxt n) xs) THEN'
(miniscope_tac (goal_params n goal) ctxt)) i);
end;
(* add_defs *)
fun add_defs def_insts = let fun add (SOME (_, (t, true))) ctxt = ((SOME t, []), ctxt)
| add (SOME (SOME x, (t, _))) ctxt = letval ([(lhs, (_, th))], ctxt') =
Local_Defs.define [((x, NoSyn), ((Thm.def_binding x, []), t))] ctxt in ((SOME lhs, [th]), ctxt') end
| add (SOME (NONE, (t as Free _, _))) ctxt = ((SOME t, []), ctxt)
| add (SOME (NONE, (t, _))) ctxt = let val (s, _) = Name.variant "x" (Variable.names_of ctxt); val x = Binding.name s; val ([(lhs, (_, th))], ctxt') = ctxt
|> Local_Defs.define [((x, NoSyn), ((Thm.def_binding x, []), t))]; in ((SOME lhs, [th]), ctxt') end
| add NONE ctxt = ((NONE, []), ctxt); in fold_map add def_insts #> apfst (split_list #> apsnd flat) end;
(* induct_tac *)
(* rule selection scheme: `A x` induct ... - predicate/set induction induct x - type induction ... induct ... r - explicit rule
*)
fun named_rule k arg get =
Scan.lift (Args.$$$ k -- Args.colon) |-- Scan.repeat arg :|--
(fn names => Scan.peek (fn context => Scan.succeed (names |> map (fn name =>
(case get (Context.proof_of context) name of SOME x => x
| NONE => error ("No rule for " ^ k ^ " " ^ quote name))))));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.