File ‹metric_arith.ML›
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
fun app_union_ct_pair f ct_pair = uncurry (union (op aconvc)) (apply2 f ct_pair)
val trace = Attrib.setup_config_bool \<^binding>‹metric_trace› (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>‹metric_argo_timeout› (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);
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 addsimps 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 addsimps nnf_simps
in
simp_tac nnf_ctxt THEN'
K (trace_tac ctxt "NNF form")
end
fun unfold_tac ctxt =
asm_full_simp_tac (put_simpset HOL_basic_ss ctxt addsimps (
Proof_Context.get_thms ctxt @{named_theorems metric_unfold}))
fun pre_arith_tac ctxt =
simp_tac (put_simpset HOL_basic_ss ctxt addsimps (
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 addsimps 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_>‹Ex _ for _› = true
| is_exists \<^Const_>‹Trueprop for t› = is_exists t
| is_exists _ = false
fun is_forall \<^Const_>‹All _ for _› = true
| is_forall \<^Const_>‹Trueprop for t› = is_forall t
| is_forall _ = false
fun find_points ctxt metric_ty ct =
let
fun find 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 _ =>
let val (x, body) = Thm.dest_abs_global ct
in filter_out (free_in x) (find body) end
| _ => [])
in
(case find ct of
[] =>
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
fun find_dist metric_ty =
let
fun find ct =
(case Thm.term_of ct of
\<^Const_>‹dist T for _ _› => if T = metric_ty then [ct] else []
| _ $ _ => app_union_ct_pair find (Thm.dest_comb ct)
| Abs _ =>
let val (x, body) = Thm.dest_abs_global ct
in filter_out (free_in x) (find body) end
| _ => [])
in find end
fun find_eq metric_ty =
let
fun find ct =
(case Thm.term_of ct of
\<^Const_>‹HOL.eq T for _ _› =>
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 _ =>
let val (x, body) = Thm.dest_abs_global ct
in filter_out (free_in x) (find body) end
| _ => [])
in find end
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
addsimps @{thms finite.emptyI finite_insert empty_iff insert_iff})))
val image_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt addsimps @{thms image_empty image_insert})
val dist_refl_sym_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt addsimps @{thms dist_commute dist_self})
val algebra_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt addsimps
@{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 addsimps @{thms insert_absorb2 insert_commute})
val sup_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt addsimps @{thms cSup_singleton Sup_insert_insert})
val real_abs_dist_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt addsimps @{thms real_abs_dist})
val maxdist_thm =
\<^instantiate>‹'a = ‹Thm.ctyp_of_cterm x› and x and y and s = fset_ct in
lemma ‹finite s ⟹ x ∈ s ⟹ y ∈ s ⟹ dist x y ≡ SUP a∈s. ¦dist x a - dist a y¦›
for x y :: ‹'a::metric_space›
by (fact maxdist_thm)›
|> solve_prems
in
((Conv.rewr_conv maxdist_thm) then_conv
image_simp then_conv
dist_refl_sym_simp then_conv
algebra_simp then_conv
insert_simp then_conv
sup_simp then_conv
real_abs_dist_simp) ct
end
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
addsimps @{thms empty_iff insert_iff})))
val ball_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt addsimps
@{thms Set.ball_empty ball_insert})
val dist_refl_sym_simp =
Simplifier.rewrite (put_simpset HOL_ss ctxt addsimps @{thms dist_commute dist_self})
val metric_eq_thm =
\<^instantiate>‹'a = ‹Thm.ctyp_of_cterm x› and x and y and s = fset_ct in
lemma ‹x ∈ s ⟹ y ∈ s ⟹ x = y ≡ ∀a∈s. dist x a = dist y a›
for x y :: ‹'a::metric_space›
by (fact metric_eq_thm)›
|> solve_prems
in
((Conv.rewr_conv metric_eq_thm) then_conv
ball_simp then_conv
dist_refl_sym_simp) ct
end
fun augment_dist_pos metric_ty ct =
let fun inst dist_ct =
let val (x, y) = Thm.dest_binop dist_ct in
\<^instantiate>‹'a = ‹Thm.ctyp_of_cterm x› and x and y
in lemma ‹dist x y ≥ 0› for x y :: ‹'a::metric_space› by simp›
end
in map inst (find_dist metric_ty ct) end
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
val eq1 = map (maxdist_conv ctxt fset_ct) (find_dist metric_ty goal)
val eq2 = map (metric_eq_conv ctxt fset_ct) (find_eq metric_ty goal)
in
(K (trace_tac ctxt "Embedding into ℝ⇧n") THEN'
CONVERSION (Conv.top_sweep_rewrs_conv (eq1 @ eq2) ctxt)) i
end)
fun lin_real_arith_tac ctxt metric_ty = CSUBGOAL (fn (goal, i) =>
let val 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))
fun guess_metric ctxt tm =
let
val thy = Proof_Context.theory_of ctxt
fun find_dist t =
(case t of
\<^Const_>‹dist T for _ _› => 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_>‹HOL.eq T for l r› =>
if Sign.of_sort thy (T, \<^sort>‹metric_space›) 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
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>‹'a = ‹Thm.ctyp_of_cterm pt› and x = pt in
lemma (schematic) ‹P x ⟹ ∃x::'a. P x› by (rule exI)›
in
HEADGOAL (resolve_tac ctxt [ex_rule] ORELSE'
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)
else if 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_tac ctxt 1 THEN
IF_UNSOLVED (Object_Logic.full_atomize_tac ctxt 1) THEN
IF_UNSOLVED (prenex_tac ctxt 1) THEN
IF_UNSOLVED (nnf_tac ctxt 1) THEN
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))
end