(* Title: Pure/raw_simplifier.ML Author: Tobias Nipkow and Stefan Berghofer, TU Muenchen
Higher-order Simplification.
*)
signature BASIC_RAW_SIMPLIFIER = sig val simp_depth_limit: int Config.T val simp_trace_depth_limit: int Config.T val simp_debug: bool Config.T val simp_trace: bool Config.T type cong_name = bool * string type rrule val mk_rrules: Proof.context -> thm -> rrule list val eq_rrule: rrule * rrule -> bool type proc = Proof.context -> cterm -> thm option type solver val mk_solver: string -> (Proof.context -> int -> tactic) -> solver type simpset val empty_ss: simpset val merge_ss: simpset * simpset -> simpset datatype proc_kind = Simproc | Congproc ofbool type simproc val cert_simproc: theory ->
{name: string, kind: proc_kind, lhss: term list,
proc: proc Morphism.entity, identifier: thm list} -> simproc val transform_simproc: morphism -> simproc -> simproc val trim_context_simproc: simproc -> simproc val simpset_of: Proof.context -> simpset val put_simpset: simpset -> Proof.context -> Proof.context val simpset_map: Proof.context -> (Proof.context -> Proof.context) -> simpset -> simpset val map_theory_simpset: (Proof.context -> Proof.context) -> theory -> theory val empty_simpset: Proof.context -> Proof.context val clear_simpset: Proof.context -> Proof.context
val rewrite_rule: Proof.context -> thm list -> thm -> thm val rewrite_goals_rule: Proof.context -> thm list -> thm -> thm val rewrite_goals_tac: Proof.context -> thm list -> tactic val rewrite_goal_tac: Proof.context -> thm list -> int -> tactic val prune_params_tac: Proof.context -> tactic val fold_rule: Proof.context -> thm list -> thm -> thm val fold_goals_tac: Proof.context -> thm list -> tactic val norm_hhf: Proof.context -> thm -> thm val norm_hhf_protect: Proof.context -> thm -> thm end;
signature RAW_SIMPLIFIER = sig
include BASIC_RAW_SIMPLIFIER
exception SIMPLIFIER ofstring * thm list val dest_simps: simpset -> (Thm_Name.T * thm) list val dest_congs: simpset -> (cong_name * thm) list val dest_ss: simpset ->
{simps: (Thm_Name.T * thm) list,
simprocs: (string * term list) list,
congprocs: (string * {lhss: term list, proc: proc Morphism.entity}) list,
congs: (cong_name * thm) list,
weak_congs: cong_name list,
loopers: stringlist,
unsafe_solvers: stringlist,
safe_solvers: stringlist} val init_simpset: thm list -> Proof.context -> Proof.context type trace_ops val set_trace_ops: trace_ops -> theory -> theory val map_ss: (Proof.context -> Proof.context) -> Context.generic -> Context.generic val mksimps: Proof.context -> thm -> thm list val get_mksimps_context: Proof.context -> (thm -> Proof.context -> thm list * Proof.context) val set_mksimps_context: (thm -> Proof.context -> thm list * Proof.context) ->
Proof.context -> Proof.context val add_simps: thm list -> Proof.context -> Proof.context val add_simp: thm -> Proof.context -> Proof.context val del_simps: thm list -> Proof.context -> Proof.context val del_simp: thm -> Proof.context -> Proof.context val flip_simps: thm list -> Proof.context -> Proof.context val flip_simp: thm -> Proof.context -> Proof.context val mk_cong: Proof.context -> thm -> thm val set_mkcong: (Proof.context -> thm -> thm) -> Proof.context -> Proof.context val add_eqcong: thm -> Proof.context -> Proof.context val del_eqcong: thm -> Proof.context -> Proof.context val add_cong: thm -> Proof.context -> Proof.context val del_cong: thm -> Proof.context -> Proof.context val add_proc: simproc -> Proof.context -> Proof.context val del_proc: simproc -> Proof.context -> Proof.context val set_mksym: (Proof.context -> thm -> thm option) -> Proof.context -> Proof.context val set_mkeqTrue: (Proof.context -> thm -> thm option) -> Proof.context -> Proof.context val set_term_ord: term ord -> Proof.context -> Proof.context val subgoal_tac: Proof.context -> int -> tactic val set_subgoaler: (Proof.context -> int -> tactic) -> Proof.context -> Proof.context val prems_of: Proof.context -> thm list val add_prems: thm list -> Proof.context -> Proof.context val loop_tac: Proof.context -> int -> tactic val set_loop: (Proof.context -> int -> tactic) -> Proof.context -> Proof.context val add_loop: string * (Proof.context -> int -> tactic) -> Proof.context -> Proof.context val del_loop: string -> Proof.context -> Proof.context val solvers: Proof.context -> solver list * solver list val solver: Proof.context -> solver -> int -> tactic val set_safe_solver: solver -> Proof.context -> Proof.context val add_safe_solver: solver -> Proof.context -> Proof.context val set_unsafe_solver: solver -> Proof.context -> Proof.context val add_unsafe_solver: solver -> Proof.context -> Proof.context val set_solvers: solver list -> Proof.context -> Proof.context val default_mk_sym: Proof.context -> thm -> thm option val set_reorient: (Proof.context -> term list -> term -> term -> bool) ->
Proof.context -> Proof.context val rewrite_cterm: bool * bool * bool ->
(Proof.context -> thm -> thm option) -> Proof.context -> conv val rewrite_term: theory -> thm list -> (term -> term option) list -> term -> term val rewrite_thm: bool * bool * bool ->
(Proof.context -> thm -> thm option) -> Proof.context -> thm -> thm val generic_rewrite_goal_tac: bool * bool * bool ->
(Proof.context -> tactic) -> Proof.context -> int -> tactic val rewrite0: Proof.context -> bool -> conv val rewrite_wrt: Proof.context -> bool -> thm list -> conv val rewrite0_rule: Proof.context -> thm -> thm end;
signature LEGACY_RAW_SIMPLIFIER = sig
include RAW_SIMPLIFIER val rewrite: Proof.context -> bool -> thm list -> conv end
(* Remarks: - elhs is used for matching, lhs only for preservation of bound variable names; - fo is set iff either elhs is first-order (no Var is applied), in which case fo-matching is complete, or elhs is not a pattern, in which case there is nothing better to do;
*)
(* FIXME: it seems that the conditions on extra variables are too liberal if prems are nonempty: does solving the prems really guarantee instantiation of all its Vars? Better: a dynamic check each time a rule is applied.
*) fun rewrite_rule_extra_vars prems elhs erhs = let val elhss = elhs :: prems; val tvars = TVars.build (fold TVars.add_tvars elhss); val vars = Vars.build (fold Vars.add_vars elhss); in
erhs |> Term.exists_type (Term.exists_subtype
(fn TVar v => not (TVars.defined tvars v) | _ => false)) orelse
erhs |> Term.exists_subterm
(fn Var v => not (Vars.defined vars v) | _ => false) end;
fun mk_rrule2 {thm, name, lhs, elhs, perm} = let val t = Thm.term_of elhs; val fo = Pattern.first_order t orelse not (Pattern.pattern t); val extra = rrule_extra_vars elhs thm; in {thm = thm, name = name, lhs = lhs, elhs = elhs, extra = extra, fo = fo, perm = perm} end;
(*simple test for looping rewrite rules and stupid orientations*) fun default_reorient ctxt prems lhs rhs =
rewrite_rule_extra_vars prems lhs rhs
orelse
is_Var (head_of lhs)
orelse (* turns t = x around, which causes a headache if x is a local variable - usually it is very useful :-( is_Free rhs andalso not(is_Free lhs) andalso not(Logic.occs(rhs,lhs)) andalso not(exists_subterm is_Var lhs) orelse
*) exists (fn t => Logic.occs (lhs, t)) (rhs :: prems)
orelse
null prems andalso Pattern.matches (Proof_Context.theory_of ctxt) (lhs, rhs) (*the condition "null prems" is necessary because conditional rewrites with extra variables in the conditions may terminate although
the rhs is an instance of the lhs; example: ?m < ?n \<Longrightarrow> f ?n \<equiv> f ?m *)
orelse
is_Const lhs andalso not (is_Const rhs);
(*A simpset contains data required during conversion: rules: discrimination net of rewrite rules; prems: current premises; depth: simp_depth and exceeded flag; congs: association list of congruence rules and a list of `weak' congruence constants. A congruence is `weak' if it avoids normalization of some argument. procs: simplification procedures indexed via discrimination net simprocs: functions that prove rewrite rules on the fly; congprocs: functions that prove congruence rules on the fly; mk_rews: mk: turn simplification thms into rewrite rules (and update context); mk_cong: prepare congruence rules; mk_sym: turn \<equiv> around; mk_eq_True: turn P into P \<equiv> True;
term_ord: for ordered rewriting;*)
datatype simpset =
Simpset of
{rules: rrule Net.net,
prems: thm list,
depth: int * bool Unsynchronized.ref} *
{congs: thm Congtab.table * cong_name list,
procs: term procedure Net.net * term procedure Net.net,
mk_rews:
{mk: thm -> Proof.context -> thm list * Proof.context,
mk_cong: Proof.context -> thm -> thm,
mk_sym: Proof.context -> thm -> thm option,
mk_eq_True: Proof.context -> thm -> thm option,
reorient: Proof.context -> term list -> term -> term -> bool},
term_ord: term ord,
subgoal_tac: Proof.context -> int -> tactic,
loop_tacs: (string * (Proof.context -> int -> tactic)) list,
solvers: solver list * solver list};
val rules' = Net.merge eq_rrule (rules1, rules2); val prems' = Thm.merge_thms (prems1, prems2); val depth' = if #1 depth1 < #1 depth2 then depth2 else depth1; val congs' = Congtab.merge (K true) (congs1, congs2); val weak' = merge (op =) (weak1, weak2); val simprocs' = Net.merge eq_procedure_id (simprocs1, simprocs2); val congprocs' = Net.merge eq_procedure_id (congprocs1, congprocs2); val loop_tacs' = AList.merge (op =) (K true) (loop_tacs1, loop_tacs2); val unsafe_solvers' = merge eq_solver (unsafe_solvers1, unsafe_solvers2); val solvers' = merge eq_solver (solvers1, solvers2); in
make_simpset ((rules', prems', depth'), ((congs', weak'), (simprocs', congprocs'),
mk_rews, term_ord, subgoal_tac, loop_tacs', (unsafe_solvers', solvers'))) end;
(** context data **)
structure Simpset = Generic_Data
( type T = simpset; val empty = empty_ss; val merge = merge_ss;
);
val simpset_of = Simpset.get o Context.Proof;
fun map_simpset f = Context.proof_map (Simpset.map f); fun map_simpset1 f = map_simpset (fn Simpset (ss1, ss2) => Simpset (map_ss1 f ss1, ss2)); fun map_simpset2 f = map_simpset (fn Simpset (ss1, ss2) => Simpset (ss1, map_ss2 f ss2));
fun put_simpset ss = map_simpset (K ss);
fun simpset_map ctxt f ss = ctxt |> put_simpset ss |> f |> simpset_of;
val empty_simpset = put_simpset empty_ss;
fun map_theory_simpset f thy = let val ctxt' = f (Proof_Context.init_global thy); val thy' = Proof_Context.theory_of ctxt'; in Context.theory_map (Simpset.map (K (simpset_of ctxt'))) thy'end;
fun map_ss f = Context.mapping (map_theory_simpset (f o Context_Position.not_really)) f;
fun subgoal_tac ctxt = (#subgoal_tac o internal_ss o simpset_of) ctxt ctxt;
fun loop_tac ctxt =
FIRST' (map (fn (_, tac) => tac ctxt) (rev ((#loop_tacs o internal_ss o simpset_of) ctxt)));
val solvers = #solvers o internal_ss o simpset_of
(* simp depth *)
(* The simp_depth_limit is meant to abort infinite recursion of the simplifier early but should not terminate "normal" executions. As of 2017, 25 would suffice; 40 builds in a safety margin.
*)
val simp_depth_limit = Config.declare_int ("simp_depth_limit", \<^here>) (K 40); val simp_trace_depth_limit = Config.declare_int ("simp_trace_depth_limit", \<^here>) (K 1);
fun inc_simp_depth ctxt =
ctxt |> map_simpset1 (fn (rules, prems, (depth, exceeded)) =>
(rules, prems,
(depth + 1, if depth = Config.get ctxt simp_trace_depth_limit then Unsynchronized.reffalseelse exceeded)));
fun simp_depth ctxt = letval Simpset ({depth = (depth, _), ...}, _) = simpset_of ctxt in depth end;
(* diagnostics *)
exception SIMPLIFIER ofstring * thm list;
val simp_debug = Config.declare_bool ("simp_debug", \<^here>) (K false); val simp_trace = Config.declare_bool ("simp_trace", \<^here>) (K false);
fun cond_warning ctxt msg = if Context_Position.is_really_visible ctxt then warning (msg ()) else ();
fun cond_tracing' ctxt flag msg = if Config.get ctxt flag then let val Simpset ({depth = (depth, exceeded), ...}, _) = simpset_of ctxt; val depth_limit = Config.get ctxt simp_trace_depth_limit; in if depth > depth_limit then if ! exceeded then () else (tracing "simp_trace_depth_limit exceeded!"; exceeded := true) else (tracing (enclose "[""]" (string_of_int depth) ^ msg ()); exceeded := false) end else ();
fun cond_tracing ctxt = cond_tracing' ctxt simp_trace;
fun print_term ctxt s t =
s ^ "\n" ^ Syntax.string_of_term ctxt t;
fun print_thm ctxt msg (name, th) = let val sffx = if Thm_Name.is_empty name then"" else" " ^ quote (Global_Theory.print_thm_name ctxt name) ^ ":"; in print_term ctxt (msg ^ sffx) (Thm.full_prop_of th) end;
fun decomp_simp thm = let val prop = Thm.prop_of thm; val prems = Logic.strip_imp_prems prop; val concl = Drule.strip_imp_concl (Thm.cprop_of thm); val (lhs, rhs) = Thm.dest_equals concl handle TERM _ => raise SIMPLIFIER ("Rewrite rule not a meta-equality", [thm]); val elhs = Thm.dest_arg (Thm.cprop_of (Thm.eta_conversion lhs)); val erhs = Envir.eta_contract (Thm.term_of rhs); val perm =
var_perm (Thm.term_of elhs, erhs) andalso not (Thm.term_of elhs aconv erhs) andalso not (is_Var (Thm.term_of elhs)); in (prems, Thm.term_of lhs, elhs, Thm.term_of rhs, perm) end;
end;
fun decomp_simp' thm = letval (_, lhs, _, rhs, _) = decomp_simp thm in if Thm.nprems_of thm > 0 thenraise SIMPLIFIER ("Bad conditional rewrite rule", [thm]) else (lhs, rhs) end;
fun mk_eq_True ctxt (thm, name) =
(case #mk_eq_True (get_mk_rews ctxt) ctxt thm of
NONE => []
| SOME eq_True => letval (_, lhs, elhs, _, _) = decomp_simp eq_True; in [{thm = eq_True, name = name, lhs = lhs, elhs = elhs, perm = false}] end);
(*create the rewrite rule and possibly also the eq_True variant,
in case there are extra vars on the rhs*) fun rrule_eq_True ctxt thm name lhs elhs rhs thm2 = letval rrule = {thm = thm, name = name, lhs = lhs, elhs = elhs, perm = false} in if rewrite_rule_extra_vars [] lhs rhs then
mk_eq_True ctxt (thm2, name) @ [rrule] else [rrule] end;
fun mk_rrule ctxt (thm, name) = letval (prems, lhs, elhs, rhs, perm) = decomp_simp thm in if perm then [{thm = thm, name = name, lhs = lhs, elhs = elhs, perm = true}] else (*weak test for loops*) if rewrite_rule_extra_vars prems lhs rhs orelse is_Var (Thm.term_of elhs) then mk_eq_True ctxt (thm, name) else rrule_eq_True ctxt thm name lhs elhs rhs thm end |> map (fn {thm, name, lhs, elhs, perm} =>
{thm = Thm.trim_context thm, name = name, lhs = lhs,
elhs = Thm.trim_context_cterm elhs, perm = perm});
fun orient_rrule ctxt (thm, name) = let val (prems, lhs, elhs, rhs, perm) = decomp_simp thm; val {reorient, mk_sym, ...} = get_mk_rews ctxt; in if perm then [{thm = thm, name = name, lhs = lhs, elhs = elhs, perm = true}] elseif reorient ctxt prems lhs rhs then if reorient ctxt prems rhs lhs then mk_eq_True ctxt (thm, name) else
(case mk_sym ctxt thm of
NONE => []
| SOME thm' => letval (_, lhs', elhs', rhs', _) = decomp_simp thm' in rrule_eq_True ctxt thm' name lhs' elhs' rhs' thm end) else rrule_eq_True ctxt thm name lhs elhs rhs thm end;
fun extract_rews {sym} thm ctxt = let val mk = #mk (get_mk_rews ctxt); val (rews, ctxt') = mk thm ctxt; val rews' = if sym then rews RL [Drule.symmetric_thm] else rews; in (map (rpair (Thm.get_name_hint thm)) rews', ctxt') end;
fun del_simps thms ctxt =
comb_simps (del_rrule true) (map mk_rrule2 o mk_rrule ctxt) {sym = false} thms ctxt;
fun del_simps_quiet thms ctxt =
comb_simps (del_rrule false) (map mk_rrule2 o mk_rrule ctxt) {sym = false} thms ctxt;
fun del_simp thm = del_simps [thm];
fun flip_simps thms = del_simps_quiet thms #> add_flipped_simps thms;
fun flip_simp thm = flip_simps [thm];
(* This code checks if the symetric version of a rule is already in the simpset. However, the variable names in the two versions of the rule may differ. Thus the current test modulo eq_rrule is too weak to be useful and needs to be refined.
fun is_full_cong_prems [] [] = true
| is_full_cong_prems [] _ = false
| is_full_cong_prems (p :: prems) varpairs =
(case Logic.strip_assums_concl p of Const ("Pure.eq", _) $ lhs $ rhs => letval (x, xs) = strip_comb lhs and (y, ys) = strip_comb rhs in
is_Var x andalso forall is_Bound xs andalso not (has_duplicates (op =) xs) andalso xs = ys andalso
member (op =) varpairs (x, y) andalso
is_full_cong_prems prems (remove (op =) (x, y) varpairs) end
| _ => false);
fun is_full_cong thm = let val prems = Thm.prems_of thm and concl = Thm.concl_of thm; val (lhs, rhs) = Logic.dest_equals concl; val (f, xs) = strip_comb lhs and (g, ys) = strip_comb rhs; in
f = g andalso not (has_duplicates (op =) (xs @ ys)) andalso length xs = length ys andalso
is_full_cong_prems prems (xs ~~ ys) end;
in
fun mk_cong ctxt = #mk_cong (get_mk_rews ctxt) ctxt;
fun add_eqcong thm ctxt = ctxt |> map_simpset2
(fn (congs, procs, mk_rews, term_ord, subgoal_tac, loop_tacs, solvers) => let val (lhs, _) = Logic.dest_equals (Thm.concl_of thm) handle TERM _ => raise SIMPLIFIER ("Congruence not a meta-equality", [thm]); (*val lhs = Envir.eta_contract lhs;*) val a = the (cong_name (head_of lhs)) handleOption.Option => raise SIMPLIFIER ("Congruence must start with a constant or free variable", [thm]); val (xs, weak) = congs; val xs' = Congtab.update (a, Thm.trim_context thm) xs; val weak' = if is_full_cong thm then weak else a :: weak; in ((xs', weak'), procs, mk_rews, term_ord, subgoal_tac, loop_tacs, solvers) end);
fun del_eqcong thm ctxt = ctxt |> map_simpset2
(fn (congs, procs, mk_rews, term_ord, subgoal_tac, loop_tacs, solvers) => let val (lhs, _) = Logic.dest_equals (Thm.concl_of thm) handle TERM _ => raise SIMPLIFIER ("Congruence not a meta-equality", [thm]); (*val lhs = Envir.eta_contract lhs;*) val a = the (cong_name (head_of lhs)) handleOption.Option => raise SIMPLIFIER ("Congruence must start with a constant", [thm]); val (xs, _) = congs; val xs' = Congtab.delete_safe a xs; val weak' = Congtab.fold (fn (a, th) => if is_full_cong th then I else insert (op =) a) xs' []; in ((xs', weak'), procs, mk_rews, term_ord, subgoal_tac, loop_tacs, solvers) end);
fun add_cong thm ctxt = add_eqcong (mk_cong ctxt thm) ctxt; fun del_cong thm ctxt = del_eqcong (mk_cong ctxt thm) ctxt;
val trace_ops = Trace_Ops.get o Proof_Context.theory_of; fun trace_invoke args ctxt = #trace_invoke (trace_ops ctxt) args ctxt; fun trace_rrule args ctxt = #trace_rrule (trace_ops ctxt) args ctxt; fun trace_simproc args ctxt = #trace_simproc (trace_ops ctxt) args ctxt;
(** rewriting **)
(* Uses conversions, see: L C Paulson, A higher-order implementation of rewriting, Science of Computer Programming 3 (1983), pages 119-149.
*)
fun check_conv ctxt msg thm thm' = let val thm'' = Thm.transitive thm thm' handle THM _ => let val nthm' =
Thm.transitive (Thm.symmetric (Drule.beta_eta_conversion (Thm.lhs_of thm'))) thm' in Thm.transitive thm nthm' handle THM _ => let val nthm =
Thm.transitive thm (Drule.beta_eta_conversion (Thm.rhs_of thm)) in Thm.transitive nthm nthm' end end val _ = if msg then cond_tracing ctxt (fn () => print_thm0 ctxt "SUCCEEDED" thm') else (); in SOME thm''end handle THM _ => let val _ $ _ $ prop0 = Thm.prop_of thm; val _ =
cond_tracing ctxt (fn () =>
print_thm0 ctxt "Proved wrong theorem (bad subgoaler?)" thm' ^ "\n" ^
print_term ctxt "Should have proved:" prop0); in NONE end;
(* mk_procrule *)
fun mk_procrule ctxt thm = let val (prems, lhs, elhs, rhs, _) = decomp_simp thm val thm' = Thm.close_derivation \<^here> thm; in if rewrite_rule_extra_vars prems lhs rhs then (cond_warning ctxt (fn () => print_thm0 ctxt "Extra vars on rhs:" thm); []) else [mk_rrule2 {thm = thm', name = Thm_Name.empty, lhs = lhs, elhs = elhs, perm = false}] end;
(* rewritec: conversion to apply the meta simpset to a term *)
(*Since the rewriting strategy is bottom-up, we avoid re-normalizing already normalized terms by carrying around the rhs of the rewrite rule just applied. This is called the `skeleton'. It is decomposed in parallel with the term. Once a Var is encountered, the corresponding term is already in normal form.
skel0 is a dummy skeleton that is to enforce complete normalization.*)
(*Use rhs as skeleton only if the lhs does not contain unnormalized bits. The latter may happen iff there are weak congruence rules for constants
in the lhs.*)
fun weak_cong weak lhs = if null weak thenfalse(*optimization*) else exists_subterm
(fn Const (a, _) => member (op =) weak (true, a)
| Free (a, _) => member (op =) weak (false, a)
| _ => false) lhs
fun uncond_skel ((_, weak), congprocs, (lhs, rhs)) = if weak_cong weak lhs then skel0 elseif Net.is_empty congprocs then rhs (*optimization*) elseifexists (is_weak_congproc o procedure_kind) (Net.match_term congprocs lhs) then skel0 else rhs;
(*Behaves like unconditional rule if rhs does not contain vars not in the lhs. Otherwise those vars may become instantiated with unnormalized terms
while the premises are solved.*)
fun cond_skel (args as (_, _, (lhs, rhs))) = if Vars.subset (vars_set rhs, vars_set lhs) then uncond_skel args else skel0;
IMPORTANT: rewrite rules must not introduce new Vars or TVars!
*)
fun rewritec (prover, maxt) ctxt t = let val Simpset ({rules, ...}, {congs, procs = (simprocs, congprocs), term_ord, ...}) =
simpset_of ctxt; val eta_thm = Thm.eta_conversion t; val eta_t' = Thm.rhs_of eta_thm; val eta_t = Thm.term_of eta_t'; fun rew rrule = let val {thm = thm0, name, lhs, elhs = elhs0, extra, fo, perm} = rrule; val thm = Thm.transfer' ctxt thm0; val elhs = Thm.transfer_cterm' ctxt elhs0; val prop = Thm.prop_of thm; val (rthm, elhs') = if maxt = ~1 orelse not extra then (thm, elhs) else (Thm.incr_indexes (maxt + 1) thm, Thm.incr_indexes_cterm (maxt + 1) elhs);
val insts = if fo then Thm.first_order_match (elhs', eta_t') else Thm.match (elhs', eta_t'); val thm' = Thm.instantiate insts (Thm.rename_boundvars lhs eta_t rthm); val prop' = Thm.prop_of thm'; val unconditional = Logic.no_prems prop'; val (lhs', rhs') = Logic.dest_equals (Logic.strip_imp_concl prop'); val trace_args = {unconditional = unconditional, cterm = eta_t', thm = thm', rrule = rrule}; in if perm andalso is_greater_equal (term_ord (rhs', lhs')) then
(cond_tracing ctxt (fn () =>
print_thm ctxt "Cannot apply permutative rewrite rule" (name, thm) ^ "\n" ^
print_thm0 ctxt "Term does not become smaller:" thm');
NONE) else
(cond_tracing ctxt (fn () =>
print_thm ctxt "Applying instance of rewrite rule" (name, thm)); if unconditional then
(cond_tracing ctxt (fn () => print_thm0 ctxt "Rewriting:" thm');
trace_rrule trace_args ctxt (fn ctxt' => let val lr = Logic.dest_equals prop; val SOME thm'' = check_conv ctxt' false eta_thm thm'; in SOME (thm'', uncond_skel (congs, congprocs, lr)) end)) else
(cond_tracing ctxt (fn () => print_thm0 ctxt "Trying to rewrite:" thm'); if simp_depth ctxt > Config.get ctxt simp_depth_limit then (cond_tracing ctxt (fn () => "simp_depth_limit exceeded - giving up"); NONE) else
trace_rrule trace_args ctxt (fn ctxt' =>
(case prover ctxt' thm'of
NONE => (cond_tracing ctxt' (fn () => print_thm0 ctxt'"FAILED" thm'); NONE)
| SOME thm2 =>
(case check_conv ctxt' true eta_thm thm2 of
NONE => NONE
| SOME thm2' => let val concl = Logic.strip_imp_concl prop; val lr = Logic.dest_equals concl; in SOME (thm2', cond_skel (congs, congprocs, lr)) end))))) end;
fun rews [] = NONE
| rews (rrule :: rrules) = letval opt = rew rrule handle Pattern.MATCH => NONE in (case opt of NONE => rews rrules | some => some) end;
fun sort_rrules rrs = let fun is_simple ({thm, ...}: rrule) =
(case Thm.prop_of thm of Const ("Pure.eq", _) $ _ $ _ => true
| _ => false); fun sort [] (re1, re2) = re1 @ re2
| sort (rr :: rrs) (re1, re2) = if is_simple rr then sort rrs (rr :: re1, re2) else sort rrs (re1, rr :: re2); in sort rrs ([], []) end;
fun proc_rews [] = NONE
| proc_rews (Procedure {name, proc, lhs, ...} :: ps) = if Pattern.matches (Proof_Context.theory_of ctxt) (lhs, Thm.term_of t) then
(cond_tracing' ctxt simp_debug (fn () =>
print_term ctxt ("Trying procedure " ^ quote name ^ " on:") eta_t);
(let val ctxt' = Config.put simp_trace (Config.get ctxt simp_debug) ctxt val res = trace_simproc {name = name, cterm = eta_t'} ctxt'
(fn ctxt'' => Morphism.form_context' ctxt'' proc eta_t') incase res of
NONE => (cond_tracing' ctxt simp_debug (fn () => "FAILED"); proc_rews ps)
| SOME raw_thm =>
(cond_tracing ctxt (fn () =>
print_thm0 ctxt ("Procedure " ^ quote name ^ " produced rewrite rule:") raw_thm);
(case rews (mk_procrule ctxt raw_thm) of
NONE =>
(cond_tracing ctxt (fn () =>
print_term ctxt ("IGNORED result of simproc " ^ quote name ^ " -- does not match") (Thm.term_of t));
proc_rews ps)
| some => some)) end)) else proc_rews ps; in
(case eta_t of
Abs _ $ _ => SOME (Thm.transitive eta_thm (Thm.beta_conversion false eta_t'), skel0)
| _ =>
(case rews (sort_rrules (Net.match_term rules eta_t)) of
NONE => proc_rews (Net.match_term simprocs eta_t)
| some => some)) end;
(* apply congprocs *)
(* pattern order: p1 GREATER p2: p1 is more general than p2, p1 matches p2 but not vice versa p1 LESS p2: p1 is more specific than p2, p2 matches p1 but not vice versa p1 EQUAL p2: both match each other or neither match each other
*)
fun pattern_order thy = let fun matches arg = can (Pattern.match thy arg) (Vartab.empty, Vartab.empty); in
fn (p1, p2) => if matches (p1, p2) then if matches (p2, p1) then EQUAL else GREATER else if matches (p2, p1) then LESS else EQUAL end;
fun app_congprocs ctxt ct = let val thy = Proof_Context.theory_of ctxt; val Simpset (_, {procs = (_, congprocs), ...}) = simpset_of ctxt;
val eta_ct = Thm.rhs_of (Thm.eta_conversion ct);
fun proc_congs [] = NONE
| proc_congs (Procedure {name, lhs, proc, ...} :: ps) = if Pattern.matches thy (lhs, Thm.term_of ct) then let val _ =
cond_tracing' ctxt simp_debug (fn () =>
print_term ctxt ("Trying procedure " ^ quote name ^ " on:") (Thm.term_of eta_ct));
val ctxt' = Config.put simp_trace (Config.get ctxt simp_debug) ctxt; val res =
trace_simproc {name = name, cterm = eta_ct} ctxt'
(fn ctxt'' => Morphism.form_context' ctxt'' proc eta_ct); in
(case res of
NONE => (cond_tracing' ctxt simp_debug (fn () => "FAILED"); proc_congs ps)
| SOME raw_thm =>
(cond_tracing ctxt (fn () =>
print_thm0 ctxt ("Procedure " ^ quote name ^ " produced congruence rule:")
raw_thm);
SOME (raw_thm, skel0))) end else proc_congs ps; in
Net.match_term congprocs (Thm.term_of eta_ct)
|> sort (pattern_order thy o apply2 procedure_lhs)
|> proc_congs end;
(* conversion to apply a congruence rule to a term *)
fun congc prover ctxt maxt cong t = let val rthm = Thm.incr_indexes (maxt + 1) cong; val rlhs = fst (Thm.dest_equals (Drule.strip_imp_concl (Thm.cprop_of rthm))); val insts = Thm.match (rlhs, t) (* Thm.match can raise Pattern.MATCH;
is handled when congc is called *) val thm' =
Thm.instantiate insts (Thm.rename_boundvars (Thm.term_of rlhs) (Thm.term_of t) rthm); val _ = cond_tracing ctxt (fn () => print_thm0 ctxt "Applying congruence rule:" thm'); fun err (msg, thm) = (cond_tracing ctxt (fn () => print_thm0 ctxt msg thm); NONE); in
(case prover thm' of
NONE => err ("Congruence proof failed. Could not prove", thm')
| SOME thm2 =>
(case check_conv ctxt true (Drule.beta_eta_conversion t) thm2 of
NONE => err ("Congruence proof failed. Should not have proved", thm2)
| SOME thm2' => if op aconv (apply2 Thm.term_of (Thm.dest_equals (Thm.cprop_of thm2'))) then NONE else SOME thm2')) end;
val vA = (("A", 0), propT); val vB = (("B", 0), propT); val vC = (("C", 0), propT);
fun transitive1 NONE NONE = NONE
| transitive1 (SOME thm1) NONE = SOME thm1
| transitive1 NONE (SOME thm2) = SOME thm2
| transitive1 (SOME thm1) (SOME thm2) = SOME (Thm.transitive thm1 thm2);
fun transitive2 thm = transitive1 (SOME thm); fun transitive3 thm = transitive1 thm o SOME;
fun bottomc ((simprem, useprem, mutsimp), prover, maxidx) = let fun botc skel ctxt t = if is_Var skel then NONE else
(case subc skel ctxt t of
some as SOME thm1 =>
(case rewritec (prover, maxidx) ctxt (Thm.rhs_of thm1) of
SOME (thm2, skel2) =>
transitive2 (Thm.transitive thm1 thm2)
(botc skel2 ctxt (Thm.rhs_of thm2))
| NONE => some)
| NONE =>
(case rewritec (prover, maxidx) ctxt t of
SOME (thm2, skel2) => transitive2 thm2
(botc skel2 ctxt (Thm.rhs_of thm2))
| NONE => NONE))
and try_botc ctxt t =
(case botc skel0 ctxt t of
SOME trec1 => trec1
| NONE => Thm.reflexive t)
and subc skel ctxt t0 = letval Simpset (_, {congs, ...}) = simpset_of ctxt in
(case Thm.term_of t0 of
Abs (a, _, _) => letval ((v, t'), ctxt') = Variable.dest_abs_cterm t0 ctxt in
(case botc (skel_body skel) ctxt' t'of
SOME thm => SOME (Thm.abstract_rule a v thm)
| NONE => NONE) end
| t $ _ =>
(case t of Const ("Pure.imp", _) $ _ => impc t0 ctxt
| Abs _ => letval thm = Thm.beta_conversion false t0 in
(case subc skel0 ctxt (Thm.rhs_of thm) of
NONE => SOME thm
| SOME thm' => SOME (Thm.transitive thm thm')) end
| _ => let fun appc () = letval (ct, cu) = Thm.dest_comb t0 in
(case botc (skel_fun skel) ctxt ct of
SOME thm1 =>
(case botc (skel_arg skel) ctxt cu of
SOME thm2 => SOME (Thm.combination thm1 thm2)
| NONE => SOME (Thm.combination thm1 (Thm.reflexive cu)))
| NONE =>
(case botc (skel_arg skel) ctxt cu of
SOME thm1 => SOME (Thm.combination (Thm.reflexive ct) thm1)
| NONE => NONE)) end;
val (h, ts) = strip_comb t;
(*Prefer congprocs over plain cong rules. In congprocs prefer most specific rules. If there is a matching congproc, then look into the result: 1. plain equality: consider normalisation complete (just as with a plain congruence rule),
2. conditional rule: treat like congruence rules like SOME cong case below.*)
fun app_cong () =
(case app_congprocs ctxt t0 of
SOME (thm, _) => SOME thm
| NONE => Option.mapPartial (Congtab.lookup (fst congs)) (cong_name h)); in
(case app_cong () of
NONE => appc ()
| SOME cong => (*post processing: some partial applications h t1 ... tj, j <= length ts,
may be a redex. Example: map (\<lambda>x. x) = (\<lambda>xs. xs) wrt map_cong*)
(let val thm = congc (prover ctxt) ctxt maxidx cong t0; val t = the_default t0 (Option.map Thm.rhs_of thm); val (cl, cr) = Thm.dest_comb t handle CTERM _ => Thm.dest_comb t0; (*e.g. congproc has
normalized such that head is removed from t*) val dVar = Var (("", 0), dummyT); val skel = list_comb (h, replicate (length ts) dVar); in
(case botc skel ctxt cl of
NONE => thm
| SOME thm' => transitive3 thm (Thm.combination thm' (Thm.reflexive cr))) endhandle Pattern.MATCH => appc ())) end)
| _ => NONE) end and impc ct ctxt = if mutsimp then mut_impc0 [] ct [] [] ctxt else nonmut_impc ct ctxt
and rules_of_prem prem ctxt = if maxidx_of_term (Thm.term_of prem) <> ~1 then
(cond_tracing ctxt (fn () =>
print_term ctxt "Cannot add premise as rewrite rule because it contains (type) unknowns:"
(Thm.term_of prem));
(([], NONE), ctxt)) else let val (asm, ctxt') = Thm.assume_hyps prem ctxt; val (rews, ctxt'') = extract_safe_rrules asm ctxt'; in ((rews, SOME asm), ctxt'') end
and add_rrules (rrss, asms) ctxt =
(fold o fold) insert_rrule rrss ctxt |> add_prems (map_filter I asms)
and disch r prem eq = let val (lhs, rhs) = Thm.dest_equals (Thm.cprop_of eq); val eq' =
Thm.implies_elim
(Thm.instantiate (TVars.empty, Vars.make3 (vA, prem) (vB, lhs) (vC, rhs))
Drule.imp_cong)
(Thm.implies_intr prem eq); in ifnot r then eq' else let val (prem', concl) = Thm.dest_implies lhs; val (prem'', _) = Thm.dest_implies rhs; in
Thm.transitive
(Thm.transitive
(Thm.instantiate (TVars.empty, Vars.make3 (vA, prem') (vB, prem) (vC, concl))
Drule.swap_prems_eq)
eq')
(Thm.instantiate (TVars.empty, Vars.make3 (vA, prem) (vB, prem'') (vC, concl))
Drule.swap_prems_eq) end end
| mut_impc (prem :: prems) concl (rrs :: rrss) (asm :: asms)
prems' rrss' asms' eqns ctxt changed k =
(case (if k = 0 then NONE else botc skel0 (add_rrules
(rev rrss' @ rrss, rev asms' @ asms) ctxt) prem) of
NONE => mut_impc prems concl rrss asms (prem :: prems')
(rrs :: rrss') (asm :: asms') (NONE :: eqns) ctxt changed
(if k = 0 then 0 else k - 1)
| SOME eqn => let val prem' = Thm.rhs_of eqn; val tprems = map Thm.term_of prems; val i = 1 + fold Integer.max (map (fn p =>
find_index (fn q => q aconv p) tprems) (Thm.hyps_of eqn)) ~1; val ((rrs', asm'), ctxt') = rules_of_prem prem' ctxt; in
mut_impc prems concl rrss asms (prem' :: prems')
(rrs' :: rrss') (asm' :: asms')
(SOME (fold_rev (disch true)
(take i prems)
(Drule.imp_cong_rule eqn (Thm.reflexive (Drule.list_implies
(drop i prems, concl))))) :: eqns)
ctxt' (length prems') ~1 end)
(*legacy code -- only for backwards compatibility*) and nonmut_impc ct ctxt = let val (prem, conc) = Thm.dest_implies ct; val thm1 = if simprem then botc skel0 ctxt prem else NONE; val prem1 = the_default prem (Option.map Thm.rhs_of thm1); val ctxt1 = ifnot useprem then ctxt else letval ((rrs, asm), ctxt') = rules_of_prem prem1 ctxt in add_rrules ([rrs], [asm]) ctxt' end; in
(case botc skel0 ctxt1 conc of
NONE =>
(case thm1 of
NONE => NONE
| SOME thm1' => SOME (Drule.imp_cong_rule thm1' (Thm.reflexive conc)))
| SOME thm2 => letval thm2' = disch false prem1 thm2 in
(case thm1 of
NONE => SOME thm2'
| SOME thm1' =>
SOME (Thm.transitive (Drule.imp_cong_rule thm1' (Thm.reflexive conc)) thm2')) end) end;
in try_botc end;
(* Meta-rewriting: rewrites t to u and returns the theorem t \<equiv> u *)
(* Parameters: mode = (simplify A, use A in simplifying B, use prems of B (if B is again a meta-impl.) to simplify A) when simplifying A \<Longrightarrow> B prover: how to solve premises in conditional rewrites and congruences
*)
fun rewrite_cterm mode prover raw_ctxt raw_ct = let val ct = raw_ct
|> Thm.transfer_cterm' raw_ctxt
|> Thm.adjust_maxidx_cterm ~1; val maxidx = Thm.maxidx_of_cterm ct;
(*Rewrite the subgoals of a proof state (represented by a theorem)*) fun rewrite_goals_rule ctxt thms th =
Conv.fconv_rule (Conv.prems_conv ~1 (rewrite_cterm (true, true, true) simple_prover
(init_simpset thms ctxt))) th;
(** meta-rewriting tactics **)
(*Rewrite all subgoals*) fun rewrite_goals_tac ctxt defs = PRIMITIVE (rewrite_goals_rule ctxt defs);
(*Rewrite one subgoal*) fun generic_rewrite_goal_tac mode prover_tac ctxt i thm = if 0 < i andalso i <= Thm.nprems_of thm then
Seq.single (Conv.gconv_rule (rewrite_cterm mode (SINGLE o prover_tac) ctxt) i thm) else Seq.empty;
(*Prunes all redundant parameters from the proof state by rewriting.*) fun prune_params_tac ctxt = rewrite_goals_tac ctxt [Drule.triv_forall_equality];
(* for folding definitions, handling critical pairs *)
(*The depth of nesting in a term*) fun term_depth (Abs (_, _, t)) = 1 + term_depth t
| term_depth (f $ t) = 1 + Int.max (term_depth f, term_depth t)
| term_depth _ = 0;
val lhs_of_thm = #1 o Logic.dest_equals o Thm.prop_of;
(*folding should handle critical pairs! E.g. K \<equiv> Inl 0, S \<equiv> Inr (Inl 0)
Returns longest lhs first to avoid folding its subexpressions.*) fun sort_lhs_depths defs = letval keylist = AList.make (term_depth o lhs_of_thm) defs val keys = sort_distinct (rev_order o int_ord) (map #2 keylist) inmap (AList.find (op =) keylist) keys end;
val rev_defs = sort_lhs_depths o map Thm.symmetric;
fun fold_rule ctxt defs = fold (rewrite_rule ctxt) (rev_defs defs); fun fold_goals_tac ctxt defs = EVERY (map (rewrite_goals_tac ctxt) (rev_defs defs));
(* HHF normal form: \<And> before \<Longrightarrow>, outermost \<And> generalized *)
local
fun gen_norm_hhf protect ss ctxt0 th0 = let val (ctxt, th) = Thm.join_transfer_context (ctxt0, th0); val th' = if Drule.is_norm_hhf protect (Thm.prop_of th) then th else
Conv.fconv_rule (rewrite_cterm (true, false, false) (K (K NONE)) (put_simpset ss ctxt)) th; in th' |> Thm.adjust_maxidx_thm ~1 |> Variable.gen_all ctxt end;
val hhf_ss =
Context.the_local_context ()
|> init_simpset Drule.norm_hhf_eqs
|> simpset_of;
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 und die Messung sind noch experimentell.