(* Title: HOL/Analysis/metric_arith.ML Author: Maximilian Schäffeler (port from HOL Light)
A decision procedure for metric spaces.
*)
signature METRIC_ARITH = sig val trace: bool Config.T val argo_timeout: real Config.T val metric_arith_tac : Proof.context -> int -> tactic end
structure Metric_Arith : METRIC_ARITH = struct
(* apply f to both cterms in ct_pair, merge results *) fun app_union_ct_pair f ct_pair = uncurry (union (op aconvc)) (apply2 f ct_pair)
val trace = Attrib.setup_config_bool \<^binding>\<open>metric_trace\<close> (K false)
fun trace_tac ctxt msg = if Config.get ctxt trace then print_tac ctxt msg else all_tac
val argo_timeout = Attrib.setup_config_real \<^binding>\<open>metric_argo_timeout\<close> (K 20.0)
fun argo_ctxt ctxt = let val ctxt1 = if Config.get ctxt trace then Config.map (Argo_Tactic.trace) (K "basic") ctxt else ctxt in Config.put Argo_Tactic.timeout (Config.get ctxt1 argo_timeout) ctxt1 end
fun free_in v t =
Term.exists_subterm (fn u => u aconv Thm.term_of v) (Thm.term_of t);
(* build a cterm set with elements cts of type ty *) fun mk_ct_set ctxt ty = map Thm.term_of #>
HOLogic.mk_set ty #>
Thm.cterm_of ctxt
fun prenex_tac ctxt = let val prenex_simps = Proof_Context.get_thms ctxt @{named_theorems metric_prenex} val prenex_ctxt = put_simpset HOL_basic_ss ctxt |> Simplifier.add_simps prenex_simps in
simp_tac prenex_ctxt THEN'
K (trace_tac ctxt "Prenex form") end
fun nnf_tac ctxt = let val nnf_simps = Proof_Context.get_thms ctxt @{named_theorems metric_nnf} val nnf_ctxt = put_simpset HOL_basic_ss ctxt |> Simplifier.add_simps nnf_simps in
simp_tac nnf_ctxt THEN'
K (trace_tac ctxt "NNF form") end
fun pre_arith_tac ctxt =
simp_tac (put_simpset HOL_basic_ss ctxt
|> Simplifier.add_simps (Proof_Context.get_thms ctxt @{named_theorems metric_pre_arith})) THEN'
K (trace_tac ctxt "Prepared for decision procedure")
fun dist_refl_sym_tac ctxt = let val refl_sym_simps = @{thms dist_self dist_commute add_0_right add_0_left simp_thms} val refl_sym_ctxt = put_simpset HOL_basic_ss ctxt |> Simplifier.add_simps refl_sym_simps in
simp_tac refl_sym_ctxt THEN'
K (trace_tac ctxt ("Simplified using " ^ @{make_string} refl_sym_simps)) end
fun is_exists \<^Const_>\<open>Ex _ for _\<close> = true
| is_exists \<^Const_>\<open>Trueprop for t\<close> = is_exists t
| is_exists _ = false
fun is_forall \<^Const_>\<open>All _ for _\<close> = true
| is_forall \<^Const_>\<open>Trueprop for t\<close> = is_forall t
| is_forall _ = false
(* find all free points in ct of type metric_ty *) fun find_points ctxt metric_ty ct = let funfind ct =
(if Thm.typ_of_cterm ct = metric_ty then [ct] else []) @
(case Thm.term_of ct of
_ $ _ => app_union_ct_pair find (Thm.dest_comb ct)
| Abs _ => (*ensure the point doesn't contain the bound variable*) letval (x, body) = Thm.dest_abs_global ct in filter_out (free_in x) (find body) end
| _ => []) in
(casefind ct of
[] => (*if no point can be found, invent one*) let val names = Variable.names_of ctxt |> Term.declare_free_names (Thm.term_of ct) val x = Free (#1 (Name.variant "x" names), metric_ty) in [Thm.cterm_of ctxt x] end
| points => points) end
(* find all cterms "dist x y" in ct, where x and y have type metric_ty *) fun find_dist metric_ty = let funfind ct =
(case Thm.term_of ct of
\<^Const_>\<open>dist T for _ _\<close> => if T = metric_ty then [ct] else []
| _ $ _ => app_union_ct_pair find (Thm.dest_comb ct)
| Abs _ => letval (x, body) = Thm.dest_abs_global ct in filter_out (free_in x) (find body) end
| _ => []) infindend
(* find all "x=y", where x has type metric_ty *) fun find_eq metric_ty = let funfind ct =
(case Thm.term_of ct of
\<^Const_>\<open>HOL.eq T for _ _\<close> => if T = metric_ty then [ct] else app_union_ct_pair find (Thm.dest_binop ct)
| _ $ _ => app_union_ct_pair find (Thm.dest_comb ct)
| Abs _ => letval (x, body) = Thm.dest_abs_global ct in filter_out (free_in x) (find body) end
| _ => []) infindend
(* rewrite ct of the form "dist x y" using maxdist_thm *) fun maxdist_conv ctxt fset_ct ct = let val (x, y) = Thm.dest_binop ct val solve_prems =
rule_by_tactic ctxt (ALLGOALS (simp_tac (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms finite.emptyI finite_insert empty_iff insert_iff}))) val image_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms image_empty image_insert}) val dist_refl_sym_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms dist_commute dist_self}) val algebra_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt |> Simplifier.add_simps
@{thms diff_self diff_0_right diff_0 abs_zero abs_minus_cancel abs_minus_commute}) val insert_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms insert_absorb2 insert_commute}) val sup_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms cSup_singleton Sup_insert_insert}) val real_abs_dist_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms real_abs_dist}) val maxdist_thm =
\<^instantiate>\<open>'a = \Thm.ctyp_of_cterm x\ and x and y and s = fset_ct in
lemma \<open>finite s \<Longrightarrow> x \<in> s \<Longrightarrow> y \<in> s \<Longrightarrow> dist x y \<equiv> SUP a\<in>s. \<bar>dist x a - dist a y\<bar>\<close>
for x y :: \<open>'a::metric_space\
by (fact maxdist_thm)\<close>
|> solve_prems in
((Conv.rewr_conv maxdist_thm) then_conv (* SUP to Sup *)
image_simp then_conv
dist_refl_sym_simp then_conv
algebra_simp then_conv (* eliminate duplicate terms in set *)
insert_simp then_conv (* Sup to max *)
sup_simp then_conv
real_abs_dist_simp) ct end
(* rewrite ct of the form "x=y" using metric_eq_thm *) fun metric_eq_conv ctxt fset_ct ct = let val (x, y) = Thm.dest_binop ct val solve_prems =
rule_by_tactic ctxt (ALLGOALS (simp_tac (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms empty_iff insert_iff}))) val ball_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms Set.ball_empty ball_insert}) val dist_refl_sym_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt
|> Simplifier.add_simps @{thms dist_commute dist_self}) val metric_eq_thm =
\<^instantiate>\<open>'a = \Thm.ctyp_of_cterm x\ and x and y and s = fset_ct in
lemma \<open>x \<in> s \<Longrightarrow> y \<in> s \<Longrightarrow> x = y \<equiv> \<forall>a\<in>s. dist x a = dist y a\<close>
for x y :: \<open>'a::metric_space\
by (fact metric_eq_thm)\<close>
|> solve_prems in
((Conv.rewr_conv metric_eq_thm) then_conv (*convert \<forall>x\<in>{x\<^sub>1,...,x\<^sub>n}. P x to P x\<^sub>1 \<and> ... \<and> P x\<^sub>n*)
ball_simp then_conv
dist_refl_sym_simp) ct end
(* build list of theorems "0 \<le> dist x y" for all dist terms in ct *) fun augment_dist_pos metric_ty ct = letfun inst dist_ct = letval (x, y) = Thm.dest_binop dist_ct in
\<^instantiate>\<open>'a = \Thm.ctyp_of_cterm x\ and x and y in lemma \<open>dist x y \<ge> 0\<close> for x y :: \<open>'a::metric_space\ by simp\ end inmap inst (find_dist metric_ty ct) end
(* apply maxdist_conv and metric_eq_conv to the goal, thereby embedding the goal in (\<real>\<^sup>n,dist\<^sub>\<infinity>) *) fun embedding_tac ctxt metric_ty = CSUBGOAL (fn (goal, i) => let val points = find_points ctxt metric_ty goal val fset_ct = mk_ct_set ctxt metric_ty points (*embed all subterms of the form "dist x y" in (\<real>\<^sup>n,dist\<^sub>\<infinity>)*) val eq1 = map (maxdist_conv ctxt fset_ct) (find_dist metric_ty goal) (*replace point equality by equality of components in \<real>\<^sup>n*) val eq2 = map (metric_eq_conv ctxt fset_ct) (find_eq metric_ty goal) in
(K (trace_tac ctxt "Embedding into \\<^sup>n") THEN'
CONVERSION (Conv.top_sweep_rewrs_conv (eq1 @ eq2) ctxt)) i end)
(* decision procedure for linear real arithmetic *) fun lin_real_arith_tac ctxt metric_ty = CSUBGOAL (fn (goal, i) => letval dist_thms = augment_dist_pos metric_ty goal in Argo_Tactic.argo_tac (argo_ctxt ctxt) dist_thms i end)
fun basic_metric_arith_tac ctxt metric_ty =
SELECT_GOAL (
dist_refl_sym_tac ctxt 1 THEN
IF_UNSOLVED (embedding_tac ctxt metric_ty 1) THEN
IF_UNSOLVED (pre_arith_tac ctxt 1) THEN
IF_UNSOLVED (lin_real_arith_tac ctxt metric_ty 1))
(* tries to infer the metric space from ct from dist terms,
if no dist terms are present, equality terms will be used *) fun guess_metric ctxt tm = let val thy = Proof_Context.theory_of ctxt fun find_dist t =
(case t of
\<^Const_>\<open>dist T for _ _\<close> => SOME T
| t1 $ t2 => (case find_dist t1 of NONE => find_dist t2 | some => some)
| Abs _ => find_dist (#2 (Term.dest_abs_global t))
| _ => NONE) fun find_eq t =
(case t of
\<^Const_>\<open>HOL.eq T for l r\<close> => if Sign.of_sort thy (T, \<^sort>\<open>metric_space\<close>) then SOME T else (case find_eq l of NONE => find_eq r | some => some)
| t1 $ t2 => (case find_eq t1 of NONE => find_eq t2 | some => some)
| Abs _ => find_eq (#2 (Term.dest_abs_global t))
| _ => NONE) in
(case find_dist tm of
SOME ty => ty
| NONE => case find_eq tm of
SOME ty => ty
| NONE => error "No Metric Space was found") end
(* solve \<exists> by proving the goal for a single witness from the metric space *) fun exists_tac ctxt = CSUBGOAL (fn (goal, i) => let val _ = \<^assert> (i = 1) val metric_ty = guess_metric ctxt (Thm.term_of goal) val points = find_points ctxt metric_ty goal
fun try_point_tac ctxt pt = let val ex_rule =
\<^instantiate>\<open>'a = \Thm.ctyp_of_cterm pt\ and x = pt in
lemma (schematic) \<open>P x \<Longrightarrow> \<exists>x::'a. P x\ by (rule exI)\ in
HEADGOAL (resolve_tac ctxt [ex_rule] ORELSE' (*variable doesn't occur in body*)
resolve_tac ctxt @{thms exI}) THEN
trace_tac ctxt ("Removed existential quantifier, try " ^ @{make_string} pt) THEN
HEADGOAL (try_points_tac ctxt) end and try_points_tac ctxt = SUBGOAL (fn (g, _) => if is_exists g then
FIRST (map (try_point_tac ctxt) points) elseif is_forall g then
resolve_tac ctxt @{thms HOL.allI} 1 THEN
Subgoal.FOCUS (fn {context = ctxt', ...} =>
trace_tac ctxt "Removed universal quantifier"THEN
try_points_tac ctxt' 1) ctxt 1 else basic_metric_arith_tac ctxt metric_ty 1) in try_points_tac ctxt 1 end)
fun metric_arith_tac ctxt =
SELECT_GOAL ( (*unfold common definitions to get rid of sets*)
unfold_tac ctxt 1 THEN (*remove all meta-level connectives*)
IF_UNSOLVED (Object_Logic.full_atomize_tac ctxt 1) THEN (*convert goal to prenex form*)
IF_UNSOLVED (prenex_tac ctxt 1) THEN (*and NNF to ?*)
IF_UNSOLVED (nnf_tac ctxt 1) THEN (*turn all universally quantified variables into free variables, by focusing the subgoal*)
REPEAT (HEADGOAL (resolve_tac ctxt @{thms HOL.allI})) THEN
IF_UNSOLVED (SUBPROOF (fn {context = ctxt', ...} =>
trace_tac ctxt' "Focused on subgoal" THEN
exists_tac ctxt' 1) ctxt 1))
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.