src/HOL/Tools/coinduction.ML
changeset 61841 4d3527b94f2a
parent 60784 4f590c08fd5d
child 61844 007d3b34080f
equal deleted inserted replaced
61840:a3793600cb93 61841:4d3527b94f2a
     6 Coinduction method that avoids some boilerplate compared to coinduct.
     6 Coinduction method that avoids some boilerplate compared to coinduct.
     7 *)
     7 *)
     8 
     8 
     9 signature COINDUCTION =
     9 signature COINDUCTION =
    10 sig
    10 sig
    11   val coinduction_tac: Proof.context -> term list -> thm option -> thm list -> cases_tactic
    11   val coinduction_tac: term list -> thm option -> thm list -> int -> context_tactic
    12 end;
    12 end;
    13 
    13 
    14 structure Coinduction : COINDUCTION =
    14 structure Coinduction : COINDUCTION =
    15 struct
    15 struct
    16 
    16 
    35     val n = nth (Thm.prems_of st) (i - 1) |> Logic.strip_assums_hyp |> length;
    35     val n = nth (Thm.prems_of st) (i - 1) |> Logic.strip_assums_hyp |> length;
    36   in
    36   in
    37     (THEN_ALL_NEW_SKIP skip tac (REPEAT_DETERM_N n o eresolve0_tac [thin_rl])) i st
    37     (THEN_ALL_NEW_SKIP skip tac (REPEAT_DETERM_N n o eresolve0_tac [thin_rl])) i st
    38   end;
    38   end;
    39 
    39 
    40 fun coinduction_tac ctxt raw_vars opt_raw_thm prems = HEADGOAL (SUBGOAL_CASES (fn (goal, _) =>
    40 fun coinduction_tac raw_vars opt_raw_thm prems =
    41   let
    41   CONTEXT_SUBGOAL (fn (goal, i) => fn (ctxt, st) =>
    42     val lhs_of_eq = HOLogic.dest_Trueprop #> HOLogic.dest_eq #> fst;
    42     let
    43     fun find_coinduct t =
    43       val lhs_of_eq = HOLogic.dest_Trueprop #> HOLogic.dest_eq #> fst;
    44       Induct.find_coinductP ctxt t @
    44       fun find_coinduct t =
    45       (try (Induct.find_coinductT ctxt o fastype_of o lhs_of_eq) t |> the_default [])
    45         Induct.find_coinductP ctxt t @
    46     val raw_thm =
    46         (try (Induct.find_coinductT ctxt o fastype_of o lhs_of_eq) t |> the_default []);
    47       (case opt_raw_thm of
    47       val raw_thm =
    48         SOME raw_thm => raw_thm
    48         (case opt_raw_thm of
    49       | NONE => goal |> Logic.strip_assums_concl |> find_coinduct |> hd);
    49           SOME raw_thm => raw_thm
    50     val skip = Integer.max 1 (Rule_Cases.get_consumes raw_thm) - 1
    50         | NONE => goal |> Logic.strip_assums_concl |> find_coinduct |> hd);
    51     val cases = Rule_Cases.get raw_thm |> fst
    51       val skip = Integer.max 1 (Rule_Cases.get_consumes raw_thm) - 1;
    52   in
    52       val cases = Rule_Cases.get raw_thm |> fst;
    53     HEADGOAL (
    53     in
    54       Object_Logic.rulify_tac ctxt THEN'
    54       ((Object_Logic.rulify_tac ctxt THEN'
    55       Method.insert_tac prems THEN'
    55         Method.insert_tac ctxt prems THEN'
    56       Object_Logic.atomize_prems_tac ctxt THEN'
    56         Object_Logic.atomize_prems_tac ctxt THEN'
    57       DELETE_PREMS_AFTER skip (Subgoal.FOCUS (fn {concl, context = ctxt, params, prems, ...} =>
    57         DELETE_PREMS_AFTER skip (Subgoal.FOCUS (fn {concl, context = ctxt, params, prems, ...} =>
    58         let
    58           let
    59           val vars = raw_vars @ map (Thm.term_of o snd) params;
    59             val vars = raw_vars @ map (Thm.term_of o snd) params;
    60           val names_ctxt = ctxt
    60             val names_ctxt = ctxt
    61             |> fold Variable.declare_names vars
    61               |> fold Variable.declare_names vars
    62             |> fold Variable.declare_thm (raw_thm :: prems);
    62               |> fold Variable.declare_thm (raw_thm :: prems);
    63           val thm_concl = Thm.cprop_of raw_thm |> strip_imp_concl;
    63             val thm_concl = Thm.cprop_of raw_thm |> strip_imp_concl;
    64           val (instT, inst) = Thm.match (thm_concl, concl);
    64             val (instT, inst) = Thm.match (thm_concl, concl);
    65           val rhoTs = map (fn (v, T) => (TVar v, Thm.typ_of T)) instT;
    65             val rhoTs = map (fn (v, T) => (TVar v, Thm.typ_of T)) instT;
    66           val rhots = map (fn (v, t) => (Var v, Thm.term_of t)) inst;
    66             val rhots = map (fn (v, t) => (Var v, Thm.term_of t)) inst;
    67           val xs = hd (Thm.prems_of raw_thm) |> HOLogic.dest_Trueprop |> strip_comb |> snd
    67             val xs = hd (Thm.prems_of raw_thm) |> HOLogic.dest_Trueprop |> strip_comb |> snd
    68             |> map (subst_atomic_types rhoTs);
    68               |> map (subst_atomic_types rhoTs);
    69           val raw_eqs = map (fn x => (x, AList.lookup op aconv rhots x |> the)) xs;
    69             val raw_eqs = map (fn x => (x, AList.lookup op aconv rhots x |> the)) xs;
    70           val ((names, ctxt), Ts) = map_split (apfst fst o dest_Var o fst) raw_eqs
    70             val ((names, ctxt), Ts) = map_split (apfst fst o dest_Var o fst) raw_eqs
    71             |>> (fn names => Variable.variant_fixes names names_ctxt) ;
    71               |>> (fn names => Variable.variant_fixes names names_ctxt) ;
    72           val eqs =
    72             val eqs =
    73             @{map 3} (fn name => fn T => fn (_, rhs) =>
    73               @{map 3} (fn name => fn T => fn (_, rhs) =>
    74               HOLogic.mk_eq (Free (name, T), rhs))
    74                 HOLogic.mk_eq (Free (name, T), rhs))
    75             names Ts raw_eqs;
    75               names Ts raw_eqs;
    76           val phi = eqs @ map (HOLogic.dest_Trueprop o Thm.prop_of) prems
    76             val phi = eqs @ map (HOLogic.dest_Trueprop o Thm.prop_of) prems
    77             |> try (Library.foldr1 HOLogic.mk_conj)
    77               |> try (Library.foldr1 HOLogic.mk_conj)
    78             |> the_default @{term True}
    78               |> the_default @{term True}
    79             |> Ctr_Sugar_Util.list_exists_free vars
    79               |> Ctr_Sugar_Util.list_exists_free vars
    80             |> Term.map_abs_vars (Variable.revert_fixed ctxt)
    80               |> Term.map_abs_vars (Variable.revert_fixed ctxt)
    81             |> fold_rev Term.absfree (names ~~ Ts)
    81               |> fold_rev Term.absfree (names ~~ Ts)
    82             |> Thm.cterm_of ctxt;
    82               |> Thm.cterm_of ctxt;
    83           val thm = infer_instantiate' ctxt [SOME phi] raw_thm;
    83             val thm = infer_instantiate' ctxt [SOME phi] raw_thm;
    84           val e = length eqs;
    84             val e = length eqs;
    85           val p = length prems;
    85             val p = length prems;
    86         in
    86           in
    87           HEADGOAL (EVERY' [resolve_tac ctxt [thm],
    87             HEADGOAL (EVERY' [resolve_tac ctxt [thm],
    88             EVERY' (map (fn var =>
    88               EVERY' (map (fn var =>
    89               resolve_tac ctxt
    89                 resolve_tac ctxt
    90                 [infer_instantiate' ctxt [NONE, SOME (Thm.cterm_of ctxt var)] exI]) vars),
    90                   [infer_instantiate' ctxt [NONE, SOME (Thm.cterm_of ctxt var)] exI]) vars),
    91             if p = 0 then Ctr_Sugar_Util.CONJ_WRAP' (K (resolve_tac ctxt [refl])) eqs
    91               if p = 0 then Ctr_Sugar_Util.CONJ_WRAP' (K (resolve_tac ctxt [refl])) eqs
    92             else
    92               else
    93               REPEAT_DETERM_N e o (resolve_tac ctxt [conjI] THEN' resolve_tac ctxt [refl]) THEN'
    93                 REPEAT_DETERM_N e o (resolve_tac ctxt [conjI] THEN' resolve_tac ctxt [refl]) THEN'
    94               Ctr_Sugar_Util.CONJ_WRAP' (resolve_tac ctxt o single) prems,
    94                 Ctr_Sugar_Util.CONJ_WRAP' (resolve_tac ctxt o single) prems,
    95             K (ALLGOALS_SKIP skip
    95               K (ALLGOALS_SKIP skip
    96                (REPEAT_DETERM_N (length vars) o (eresolve_tac ctxt [exE] THEN' rotate_tac ~1) THEN'
    96                  (REPEAT_DETERM_N (length vars) o (eresolve_tac ctxt [exE] THEN' rotate_tac ~1) THEN'
    97                DELETE_PREMS_AFTER 0 (Subgoal.FOCUS (fn {prems, params, context = ctxt, ...} =>
    97                  DELETE_PREMS_AFTER 0 (Subgoal.FOCUS (fn {prems, params, context = ctxt, ...} =>
    98                  (case prems of
    98                    (case prems of
    99                    [] => all_tac
    99                      [] => all_tac
   100                  | inv :: case_prems =>
   100                    | inv :: case_prems =>
   101                      let
   101                        let
   102                        val (init, last) = funpow_yield (p + e - 1) (HOLogic.conj_elim ctxt) inv;
   102                          val (init, last) = funpow_yield (p + e - 1) (HOLogic.conj_elim ctxt) inv;
   103                        val inv_thms = init @ [last];
   103                          val inv_thms = init @ [last];
   104                        val eqs = take e inv_thms;
   104                          val eqs = take e inv_thms;
   105                        fun is_local_var t =
   105                          fun is_local_var t =
   106                          member (fn (t, (_, t')) => t aconv (Thm.term_of t')) params t;
   106                            member (fn (t, (_, t')) => t aconv (Thm.term_of t')) params t;
   107                         val (eqs, assms') =
   107                           val (eqs, assms') =
   108                           filter_in_out (is_local_var o lhs_of_eq o Thm.prop_of) eqs;
   108                             filter_in_out (is_local_var o lhs_of_eq o Thm.prop_of) eqs;
   109                         val assms = assms' @ drop e inv_thms
   109                           val assms = assms' @ drop e inv_thms
   110                       in
   110                         in
   111                         HEADGOAL (Method.insert_tac (assms @ case_prems)) THEN
   111                           HEADGOAL (Method.insert_tac ctxt (assms @ case_prems)) THEN
   112                         Ctr_Sugar_General_Tactics.unfold_thms_tac ctxt eqs
   112                           Ctr_Sugar_General_Tactics.unfold_thms_tac ctxt eqs
   113                       end)) ctxt)))])
   113                         end)) ctxt)))])
   114         end) ctxt) THEN'
   114           end) ctxt) THEN'
   115       K (prune_params_tac ctxt))
   115         K (prune_params_tac ctxt)) i) st
   116     #> Seq.maps (fn st =>
   116       |> Seq.maps (fn st' =>
   117       CASES (Rule_Cases.make_common ctxt (Thm.prop_of st) cases) all_tac st)
   117         CONTEXT_CASES (Rule_Cases.make_common ctxt (Thm.prop_of st') cases) all_tac (ctxt, st'))
   118   end));
   118     end);
   119 
   119 
   120 local
   120 local
   121 
   121 
   122 val ruleN = "rule"
   122 val ruleN = "rule"
   123 val arbitraryN = "arbitrary"
   123 val arbitraryN = "arbitrary"
   150 
   150 
   151 val _ =
   151 val _ =
   152   Theory.setup
   152   Theory.setup
   153     (Method.setup @{binding coinduction}
   153     (Method.setup @{binding coinduction}
   154       (arbitrary -- Scan.option coinduct_rule >>
   154       (arbitrary -- Scan.option coinduct_rule >>
   155         (fn (arbitrary, opt_rule) => fn ctxt => fn facts =>
   155         (fn (arbitrary, opt_rule) => fn _ => fn facts =>
   156           Seq.DETERM (coinduction_tac ctxt arbitrary opt_rule facts)))
   156           Seq.DETERM (coinduction_tac arbitrary opt_rule facts 1)))
   157       "coinduction on types or predicates/sets");
   157       "coinduction on types or predicates/sets");
   158 
   158 
   159 end;
   159 end;
   160 
   160 
   161 end;
   161 end;