src/Provers/induct_method.ML
changeset 18178 9e4dfe031525
parent 18147 31634a2af39e
child 18205 744803a2d5a5
equal deleted inserted replaced
18177:7041d038acb0 18178:9e4dfe031525
    19 
    19 
    20 signature INDUCT_METHOD =
    20 signature INDUCT_METHOD =
    21 sig
    21 sig
    22   val cases_tac: Proof.context -> bool -> term option list list -> thm option ->
    22   val cases_tac: Proof.context -> bool -> term option list list -> thm option ->
    23     thm list -> int -> RuleCases.tactic
    23     thm list -> int -> RuleCases.tactic
    24   val induct_tac: Proof.context -> bool -> term option list list ->
    24   val fix_tac: Proof.context -> (string * typ) list -> int -> tactic
       
    25   val induct_tac: Proof.context -> bool -> (string option * term) option list list ->
    25     thm option -> (string * typ) list -> thm list -> int -> RuleCases.tactic
    26     thm option -> (string * typ) list -> thm list -> int -> RuleCases.tactic
    26   val setup: (theory -> theory) list
    27   val setup: (theory -> theory) list
    27 end;
    28 end;
    28 
    29 
    29 functor InductMethodFun(Data: INDUCT_METHOD_DATA): INDUCT_METHOD =
    30 functor InductMethodFun(Data: INDUCT_METHOD_DATA): INDUCT_METHOD =
    65     align "Rule has fewer variables than instantiations given" xs ts
    66     align "Rule has fewer variables than instantiations given" xs ts
    66     |> List.mapPartial prep_var
    67     |> List.mapPartial prep_var
    67   end;
    68   end;
    68 
    69 
    69 
    70 
       
    71 (* warn_open *)
       
    72 
       
    73 fun warn_open true = warning "Encountered open rule cases -- deprecated"
       
    74   | warn_open false = ();
       
    75 
       
    76 
    70 
    77 
    71 (** cases method **)
    78 (** cases method **)
    72 
    79 
    73 (*
    80 (*
    74   rule selection scheme:
    81   rule selection scheme:
    75           cases         - classical case split
    82           cases         - default case split
    76     <x:A> cases ...     - set cases
    83     `x:A` cases ...     - set cases
    77           cases t       - type cases
    84           cases t       - type cases
    78     ...   cases ... R   - explicit rule
    85     ...   cases ... r   - explicit rule
    79 *)
    86 *)
    80 
    87 
    81 local
    88 local
    82 
    89 
    83 fun resolveq_cases_tac make ruleq i st =
    90 fun find_casesT ctxt ((SOME t :: _) :: _) = InductAttrib.find_casesT ctxt (Term.fastype_of t)
    84   ruleq |> Seq.maps (fn (rule, (cases, facts)) =>
       
    85     (Method.insert_tac facts THEN' Tactic.rtac rule) i st
       
    86     |> Seq.map (rpair (make (Thm.theory_of_thm rule, Thm.prop_of rule) cases)));
       
    87 
       
    88 fun find_casesT ctxt ((SOME t :: _) :: _) = InductAttrib.find_casesT ctxt (fastype_of t)
       
    89   | find_casesT _ _ = [];
    91   | find_casesT _ _ = [];
    90 
    92 
    91 fun find_casesS ctxt (fact :: _) = InductAttrib.find_casesS ctxt fact
    93 fun find_casesS ctxt (fact :: _) = InductAttrib.find_casesS ctxt fact
    92   | find_casesS _ _ = [];
    94   | find_casesS _ _ = [];
    93 
    95 
   110           let val rules = find_casesS ctxt facts @ find_casesT ctxt insts @ [Data.cases_default] in
   112           let val rules = find_casesS ctxt facts @ find_casesT ctxt insts @ [Data.cases_default] in
   111             Method.trace ctxt rules;
   113             Method.trace ctxt rules;
   112             Seq.maps (Seq.try inst_rule) (Seq.of_list rules)
   114             Seq.maps (Seq.try inst_rule) (Seq.of_list rules)
   113           end
   115           end
   114       | SOME r => Seq.single (inst_rule r));
   116       | SOME r => Seq.single (inst_rule r));
   115 
   117     val ruleq_cases = ruleq |> Seq.maps (fn (th, (cases, n)) =>
   116     fun prep_rule (th, (cases, n)) = Seq.map (apsnd (rpair (Library.drop (n, facts))) o rpair cases)
   118       Method.multi_resolves (Library.take (n, facts)) [th]
   117       (Method.multi_resolves (Library.take (n, facts)) [th]);
   119       |> Seq.map (apsnd (rpair (Library.drop (n, facts))) o rpair cases));
   118   in resolveq_cases_tac (RuleCases.make is_open NONE) (Seq.maps prep_rule ruleq) end;
   120 
       
   121     fun make_cases rule = RuleCases.make (tap warn_open is_open) NONE (thy, Thm.prop_of rule);
       
   122   in
       
   123     fn i => fn st =>
       
   124       ruleq_cases |> Seq.maps (fn (rule, (cases, facts)) =>
       
   125         (Method.insert_tac facts THEN' Tactic.rtac rule) i st
       
   126         |> Seq.map (rpair (make_cases rule cases)))
       
   127   end;
   119 
   128 
   120 val cases_meth = Method.METHOD_CASES o ((Seq.DETERM o HEADGOAL) oo
   129 val cases_meth = Method.METHOD_CASES o ((Seq.DETERM o HEADGOAL) oo
   121   (fn (ctxt, (is_open, (insts, opt_rule))) => cases_tac ctxt is_open insts opt_rule));
   130   (fn (ctxt, (is_open, (insts, opt_rule))) => cases_tac ctxt is_open insts opt_rule));
   122 
   131 
   123 end;
   132 end;
   124 
   133 
   125 
   134 
   126 
   135 
   127 (** induct method **)
   136 (** induct method **)
   128 
   137 
   129 (*
   138 (* fixes *)
   130   rule selection scheme:
       
   131     <x:A> induct ...     - set induction
       
   132           induct x       - type induction
       
   133     ...   induct ... R   - explicit rule
       
   134 *)
       
   135 
   139 
   136 local
   140 local
       
   141 
       
   142 val meta_spec = PureThy.get_thm Pure.thy (Name "meta_spec");
       
   143 
       
   144 fun meta_spec_tac ctxt (x, T) i st = SUBGOAL (fn (goal, _) =>
       
   145   let
       
   146     val thy = Thm.theory_of_thm st;
       
   147     val cert = Thm.cterm_of thy;
       
   148     val certT = Thm.ctyp_of thy;
       
   149 
       
   150     val v = Free (x, T);
       
   151     val _ = Term.exists_subterm (fn t => t aconv v) goal orelse
       
   152       error ("No variable " ^ ProofContext.string_of_term ctxt v ^ " in subgoal");
       
   153     val P = Term.absfree (x, T, goal);
       
   154     val rule = meta_spec
       
   155       |> Drule.instantiate' [SOME (certT T)] [SOME (cert P), SOME (cert v)]
       
   156       |> Thm.rename_params_rule ([x], 1);
       
   157   in compose_tac (false, rule, 1) end i) i st;
       
   158 
       
   159 in
       
   160 
       
   161 fun fix_tac ctxt fixes =
       
   162   (case gen_duplicates (op =) fixes of
       
   163     [] => EVERY' (map (meta_spec_tac ctxt) (rev fixes))
       
   164   | dups => error ("Duplicate specification of variable(s): " ^
       
   165       commas (map (ProofContext.string_of_term ctxt o Free) dups)));
       
   166 
       
   167 end;
       
   168 
       
   169 
       
   170 (* defs *)
       
   171 
       
   172 fun add_defs def_insts =
       
   173   let
       
   174     fun add (SOME (SOME x, t)) ctxt =
       
   175           let val ((lhs, def), ctxt') = ProofContext.add_def (x, t) ctxt
       
   176           in ((SOME (Free lhs), [def]), ctxt') end
       
   177       | add (SOME (NONE, t)) ctxt = ((SOME t, []), ctxt)
       
   178       | add NONE ctxt = ((NONE, []), ctxt);
       
   179   in fold_map add def_insts #> apfst (split_list #> apsnd List.concat) end;
   137 
   180 
   138 
   181 
   139 (* atomize and rulify *)
   182 (* atomize and rulify *)
   140 
   183 
   141 fun atomize_term thy =
   184 fun atomize_term thy =
   155   Tactic.rewrite_goal_tac Data.rulify1 THEN'
   198   Tactic.rewrite_goal_tac Data.rulify1 THEN'
   156   Tactic.rewrite_goal_tac Data.rulify2 THEN'
   199   Tactic.rewrite_goal_tac Data.rulify2 THEN'
   157   Tactic.norm_hhf_tac;
   200   Tactic.norm_hhf_tac;
   158 
   201 
   159 val localize = Goal.norm_hhf o Tactic.simplify false Data.localize;
   202 val localize = Goal.norm_hhf o Tactic.simplify false Data.localize;
   160 
       
   161 
       
   162 (* fix_tac *)
       
   163 
       
   164 local
       
   165 
       
   166 val meta_spec = PureThy.get_thm Pure.thy (Name "meta_spec");
       
   167 
       
   168 fun meta_spec_tac ctxt (x, T) i st = SUBGOAL (fn (goal, _) =>
       
   169   let
       
   170     val thy = Thm.theory_of_thm st;
       
   171     val cert = Thm.cterm_of thy;
       
   172     val certT = Thm.ctyp_of thy;
       
   173 
       
   174     val v = Free (x, T);
       
   175     val _ = Term.exists_subterm (fn t => t aconv v) goal orelse
       
   176       error ("No occurrence of " ^ ProofContext.string_of_term ctxt v ^ " in subgoal");
       
   177     val P = Term.absfree (x, T, goal);
       
   178     val rule = meta_spec
       
   179       |> Drule.instantiate' [SOME (certT T)] [SOME (cert P), SOME (cert v)]
       
   180       |> Thm.rename_params_rule ([x], 1);
       
   181   in compose_tac (false, rule, 1) end i) i st;
       
   182 
       
   183 in
       
   184 
       
   185 fun fix_tac ctxt fixes = EVERY' (map (meta_spec_tac ctxt) (rev fixes));
       
   186 
       
   187 end;
       
   188 
   203 
   189 
   204 
   190 (* internalize implications -- limited to atomic prems *)
   205 (* internalize implications -- limited to atomic prems *)
   191 
   206 
   192 local
   207 local
   227           |> Drule.standard'
   242           |> Drule.standard'
   228           |> RuleCases.save r]
   243           |> RuleCases.save r]
   229         end;
   244         end;
   230 
   245 
   231 
   246 
   232 (* divinate rule instantiation (cannot handle pending goal parameters) *)
   247 (* divinate rule instantiation -- cannot handle pending goal parameters *)
   233 
   248 
   234 fun dest_env thy (env as Envir.Envir {iTs, ...}) =
   249 fun dest_env thy (env as Envir.Envir {iTs, ...}) =
   235   let
   250   let
   236     val cert = Thm.cterm_of thy;
   251     val cert = Thm.cterm_of thy;
   237     val certT = Thm.ctyp_of thy;
   252     val certT = Thm.ctyp_of thy;
   260         |> Seq.map (fn env => Drule.instantiate (dest_env thy env) rule')
   275         |> Seq.map (fn env => Drule.instantiate (dest_env thy env) rule')
   261       end
   276       end
   262   end handle Subscript => Seq.empty;
   277   end handle Subscript => Seq.empty;
   263 
   278 
   264 
   279 
   265 (* compose tactics with cases *)
   280 (* special renaming of rule parameters *)
   266 
   281 
   267 fun resolveq_cases_tac' ctxt make is_open ruleq fixes i st =
   282 fun special_rename_params ctxt [[SOME (Free (z, Type (T, _)))]] thm =
   268   ruleq |> Seq.maps (fn (rule, (cases, k, more_facts)) =>
       
   269     (Method.insert_tac more_facts THEN' fix_tac ctxt fixes THEN' atomize_tac) i st
       
   270     |> Seq.maps (fn st' =>
       
   271       divinate_inst (internalize k rule) i st'
       
   272       |> Seq.maps (fn rule' =>
       
   273         Tactic.rtac rule' i st'
       
   274         |> Seq.map (rpair (make is_open (SOME (Thm.prop_of rule')) (rulified_term rule') cases)))));
       
   275 
       
   276 infix 1 THEN_ALL_NEW_CASES;
       
   277 
       
   278 fun (tac1 THEN_ALL_NEW_CASES tac2) i st =
       
   279   st |> tac1 i |> Seq.maps (fn (st', cases) =>
       
   280     Seq.map (rpair cases) (Seq.INTERVAL tac2 i (i + nprems_of st' - nprems_of st) st'));
       
   281 
       
   282 
       
   283 (* find rules *)
       
   284 
       
   285 (*rename all outermost !!-bound vars of type T in all premises of thm to x,
       
   286   possibly indexed to avoid clashes*)
       
   287 fun rename [[SOME (Free (x, Type (T, _)))]] thm =
       
   288       let
   283       let
       
   284         val x = the_default z (ProofContext.revert_skolem ctxt z);
   289         fun index i [] = []
   285         fun index i [] = []
   290           | index i (y :: ys) =
   286           | index i (y :: ys) =
   291               if x = y then x ^ string_of_int i :: index (i + 1) ys
   287               if x = y then x ^ string_of_int i :: index (i + 1) ys
   292               else y :: index i ys;
   288               else y :: index i ys;
   293         fun rename_params [] = []
   289         fun rename_params [] = []
   304         fun rename_prop p =
   300         fun rename_prop p =
   305           let val (As, C) = Logic.strip_horn p
   301           let val (As, C) = Logic.strip_horn p
   306           in Logic.list_implies (map rename_asm As, C) end;
   302           in Logic.list_implies (map rename_asm As, C) end;
   307         val cp' = cterm_fun rename_prop (Thm.cprop_of thm);
   303         val cp' = cterm_fun rename_prop (Thm.cprop_of thm);
   308         val thm' = Thm.equal_elim (Thm.reflexive cp') thm;
   304         val thm' = Thm.equal_elim (Thm.reflexive cp') thm;
   309       in Thm.put_name_tags (Thm.get_name_tags thm) thm' end
   305       in RuleCases.save thm thm' end
   310   | rename _ thm = thm;
   306   | special_rename_params _ _ thm = thm;
       
   307 
       
   308 
       
   309 (* induct_tac *)
       
   310 
       
   311 (*
       
   312   rule selection scheme:
       
   313     `x:A` induct ...     - set induction
       
   314           induct x       - type induction
       
   315     ...   induct ... r   - explicit rule
       
   316 *)
       
   317 
       
   318 local
   311 
   319 
   312 fun find_inductT ctxt insts =
   320 fun find_inductT ctxt insts =
   313   fold_rev multiply (insts |> List.mapPartial (fn [] => NONE | ts => List.last ts)
   321   fold_rev multiply (insts |> List.mapPartial (fn [] => NONE | ts => List.last ts)
   314     |> map (InductAttrib.find_inductT ctxt o fastype_of)) [[]]
   322     |> map (InductAttrib.find_inductT ctxt o fastype_of)) [[]]
   315   |> map join_rules |> List.concat |> map (rename insts);
   323   |> map join_rules |> List.concat;
   316 
   324 
   317 fun find_inductS ctxt (fact :: _) = InductAttrib.find_inductS ctxt fact
   325 fun find_inductS ctxt (fact :: _) = InductAttrib.find_inductS ctxt fact
   318   | find_inductS _ _ = [];
   326   | find_inductS _ _ = [];
   319 
   327 
   320 in
   328 in
   321 
   329 
   322 
   330 fun induct_tac ctxt is_open def_insts opt_rule fixes facts =
   323 (* main tactic *)
       
   324 
       
   325 fun induct_tac ctxt is_open insts opt_rule fixes facts =
       
   326   let
   331   let
   327     val thy = ProofContext.theory_of ctxt;
   332     val thy = ProofContext.theory_of ctxt;
   328     val cert = Thm.cterm_of thy;
   333     val cert = Thm.cterm_of thy;
       
   334 
       
   335     val ((insts, defs), defs_ctxt) = fold_map add_defs def_insts ctxt |>> split_list;
   329 
   336 
   330     fun rule_versions r = Seq.cons (r, Seq.filter (not o curry Thm.eq_thm r)
   337     fun rule_versions r = Seq.cons (r, Seq.filter (not o curry Thm.eq_thm r)
   331         (Seq.make (fn () => SOME (localize r, Seq.empty))))
   338         (Seq.make (fn () => SOME (localize r, Seq.empty))))
   332       |> Seq.map (rpair (RuleCases.get r));
   339       |> Seq.map (rpair (RuleCases.get r));
   333 
   340 
   339         |> Drule.cterm_instantiate) r);
   346         |> Drule.cterm_instantiate) r);
   340 
   347 
   341     val ruleq =
   348     val ruleq =
   342       (case opt_rule of
   349       (case opt_rule of
   343         NONE =>
   350         NONE =>
   344           let val rules = find_inductS ctxt facts @ find_inductT ctxt insts in
   351           let val rules = find_inductS ctxt facts @
       
   352             map (special_rename_params defs_ctxt insts) (find_inductT ctxt insts)
       
   353           in
   345             conditional (null rules) (fn () => error "Unable to figure out induct rule");
   354             conditional (null rules) (fn () => error "Unable to figure out induct rule");
   346             Method.trace ctxt rules;
   355             Method.trace ctxt rules;
   347             rules |> Seq.of_list |> Seq.maps rule_versions |> Seq.maps (Seq.try inst_rule)
   356             rules |> Seq.of_list |> Seq.maps rule_versions |> Seq.maps (Seq.try inst_rule)
   348           end
   357           end
   349       | SOME r => r |> rule_versions |> Seq.map inst_rule);
   358       | SOME r => r |> rule_versions |> Seq.map inst_rule);
   350 
   359     val ruleq_cases = ruleq |> Seq.maps (fn (th, (cases, n)) =>
   351     fun prep_rule (th, (cases, n)) =
   360       Method.multi_resolves (Library.take (n, facts)) [th]
   352       Seq.map (rpair (cases, n - length facts, Library.drop (n, facts)))
   361       |> Seq.map (rpair (cases, n - length facts, Library.drop (n, facts))));
   353         (Method.multi_resolves (Library.take (n, facts)) [th]);
   362 
   354     val tac = resolveq_cases_tac' ctxt RuleCases.make is_open (Seq.maps prep_rule ruleq) fixes;
   363     fun make_cases rule =
   355   in tac THEN_ALL_NEW_CASES rulify_tac end;
   364       RuleCases.make (tap warn_open is_open) (SOME (Thm.prop_of rule)) (rulified_term rule);
       
   365   in
       
   366     (fn i => fn st => ruleq_cases |> Seq.maps (fn (rule, (cases, k, more_facts)) =>
       
   367       (Method.insert_tac (List.concat defs @ more_facts)  (* FIXME CONJUNCTS *)
       
   368         THEN' fix_tac defs_ctxt fixes
       
   369         THEN' atomize_tac) i st |> Seq.maps (fn st' =>
       
   370         divinate_inst (internalize k rule) i st' |> Seq.maps (fn rule' =>
       
   371           Tactic.rtac rule' i st'
       
   372           |> Seq.maps (ProofContext.exports defs_ctxt ctxt)
       
   373           |> Seq.map (rpair (make_cases rule' cases))))))
       
   374     THEN_ALL_NEW_CASES rulify_tac
       
   375   end;
   356 
   376 
   357 val induct_meth = Method.RAW_METHOD_CASES o ((Seq.DETERM o HEADGOAL) oo
   377 val induct_meth = Method.RAW_METHOD_CASES o ((Seq.DETERM o HEADGOAL) oo
   358   (fn (ctxt, (is_open, (insts, (opt_rule, fixes)))) =>
   378   (fn (ctxt, (is_open, (insts, (opt_rule, fixes)))) =>
   359     induct_tac ctxt is_open insts opt_rule fixes));
   379     induct_tac ctxt is_open insts opt_rule fixes));
   360 
   380 
   382   Scan.lift (Args.$$$ ruleN -- Args.colon) |-- Attrib.local_thm;
   402   Scan.lift (Args.$$$ ruleN -- Args.colon) |-- Attrib.local_thm;
   383 
   403 
   384 val cases_rule = rule InductAttrib.lookup_casesT InductAttrib.lookup_casesS;
   404 val cases_rule = rule InductAttrib.lookup_casesT InductAttrib.lookup_casesS;
   385 val induct_rule = rule InductAttrib.lookup_inductT InductAttrib.lookup_inductS;
   405 val induct_rule = rule InductAttrib.lookup_inductT InductAttrib.lookup_inductS;
   386 
   406 
   387 val more_args =
   407 val inst = Scan.lift (Args.$$$ "_") >> K NONE || Args.local_term >> SOME;
   388   (Args.$$$ InductAttrib.typeN || Args.$$$ InductAttrib.setN || Args.$$$ ruleN ||
   408 
   389     Args.$$$ ofN || Args.$$$ fixingN) -- Args.colon;
   409 val def_inst =
   390 
   410   ((Scan.lift (Args.name --| (Args.$$$ "\\<equiv>" || Args.$$$ "==")) >> SOME)
   391 val term = Scan.unless (Scan.lift more_args) Args.local_term;
   411       -- Args.local_term) >> SOME ||
   392 val term_dummy = Scan.unless (Scan.lift more_args)
   412     inst >> Option.map (pair NONE);
   393   (Scan.lift (Args.$$$ "_") >> K NONE || Args.local_term >> SOME);
       
   394 
       
   395 val instss = Args.and_list (Scan.repeat term_dummy);
       
   396 
   413 
   397 val free = Scan.state -- Args.local_term >> (fn (_, Free v) => v | (ctxt, t) =>
   414 val free = Scan.state -- Args.local_term >> (fn (_, Free v) => v | (ctxt, t) =>
   398   error ("Bad free variable: " ^ ProofContext.string_of_term ctxt t));
   415   error ("Bad free variable: " ^ ProofContext.string_of_term ctxt t));
   399 val fixing = Scan.optional (Scan.lift (Args.$$$ fixingN -- Args.colon) |-- Scan.repeat1 free) [];
   416 val fixing = Scan.optional (Scan.lift (Args.$$$ fixingN -- Args.colon) |-- Scan.repeat1 free) [];
   400 
   417 
       
   418 fun unless_more_args scan = Scan.unless (Scan.lift
       
   419   ((Args.$$$ InductAttrib.typeN || Args.$$$ InductAttrib.setN || Args.$$$ ruleN ||
       
   420     Args.$$$ ofN || Args.$$$ fixingN) -- Args.colon)) scan;
       
   421 
   401 in
   422 in
   402 
   423 
   403 val cases_args =
   424 val cases_args = Method.syntax (Args.mode openN --
   404   Method.syntax (Args.mode openN -- (instss -- Scan.option cases_rule));
   425   (Args.and_list (Scan.repeat (unless_more_args inst)) -- Scan.option cases_rule));
   405 val induct_args =
   426 
   406   Method.syntax (Args.mode openN -- (instss -- (Scan.option induct_rule -- fixing)));
   427 val induct_args = Method.syntax (Args.mode openN --
       
   428   (Args.and_list (Scan.repeat (unless_more_args def_inst)) --
       
   429   (Scan.option induct_rule -- fixing)));
   407 
   430 
   408 end;
   431 end;
   409 
   432 
   410 
   433 
   411 
   434