Theory Relation_Extra
section ‹ Relational Universe ›
theory Relation_Extra
imports "HOL-Library.FuncSet" "HOL-Library.AList" List_Extra Overriding
begin
no_notation SCons (infixr ‹##› 65)
text ‹ This theory develops a universe for a Z-like relational language, including the core
operators of the ISO Z metalanguage. Much of this already exists in @{theory HOL.Relation},
but we need to add some additional functions and sets. It characterises relations, partial
functions, total functions, and finite functions. ›
subsection ‹ Type Syntax›
text ‹ We set up some nice syntax for heterogeneous relations at the type level ›
syntax
"_rel_type" :: "type ⇒ type ⇒ type" (infixr "↔" 0)
translations
(type) "'a ↔ 'b" == (type) "('a × 'b) set"
text ‹ Setup pretty printing for homogeneous relations. ›
print_translation ‹
let fun tr' ctx [ Const (@{type_syntax "prod"},_) $ a $ b ] =
if a = b then Syntax.const @{type_syntax "rel"} $ a else raise Match;
in [(@{type_syntax "set"},tr')]
end
›
subsection ‹ Notation for types as sets ›
" (a::'a itself) = (UNIV :: 'a set)"
syntax "_tvar" :: "type ⇒ logic" ("[_]⇩T")
translations "['a]⇩T" == "CONST TUNIV TYPE('a)"
lemma [simp]: "x ∈ ['a]⇩T"
by (simp add: TUNIV_def)
subsection ‹ Relational Function Operations ›
text ‹ These functions are all adapted from their ISO Z counterparts. ›
:: "('a ↔ 'b) ⇒ 'a ⇒ 'b" ("_'(_')⇩r" [999,0] 999) where
"rel_apply R x = (if (∃! y. (x, y) ∈ R) then THE y. (x, y) ∈ R else undefined)"
text ‹ If there exists a unique @{term "e⇩3"} such that @{term "(e⇩2, e⇩3)"} is in @{term "e⇩1"}, then
the value of @{term "e⇩1(e⇩2)⇩r"} is @{term e⇩3}, otherwise each @{term "e⇩1(e⇩2)⇩r"} has a fixed but
unknown value (i.e. @{const undefined}). ›
:: "'a set ⇒ ('a ↔ 'b) ⇒ 'a ↔ 'b" (infixr "⊲⇩r" 85) where
"rel_domres A R = {p ∈ R. fst p ∈ A}"
lemma : "A ⊲⇩r R = {(k, v) ∈ R. k ∈ A}"
by (auto simp add: rel_domres_def)
text ‹ Domain restriction (@{term "A ⊲⇩r R"} contains the set of pairs in @{term R}, such that the
first element of every such pair in in @{term A}. ›
:: "('a ↔ 'b) ⇒ 'b set ⇒ 'a ↔ 'b" (infixl "⊳⇩r" 85) where
"rel_ranres R A = {p ∈ R. snd p ∈ A}"
text ‹ We employ some type class trickery to enable a polymorphic operator for override that can
instantiate @{typ "'a set"}, which is needed for relational overriding. The following class's
sole purpose is to allow pairs to be the only valid instantiation element for the set type. ›
=
fixes cmpt :: "'a set ⇒ 'a set ⇒ bool"
and res :: "'a set ⇒ 'a set ⇒ 'a set"
instantiation prod :: (type, type) pre_restrict
begin
text ‹ Relations are compatible is they agree on the values for maplets they both possess. ›
cmpt_prod :: "('a ↔ 'b) ⇒ ('a ↔ 'b) ⇒ bool"
where [simp]: "cmpt_prod R S = ((Domain R) ⊲⇩r S = (Domain S) ⊲⇩r R)"
res_prod :: "('a ↔ 'b) ⇒ ('a ↔ 'b) ⇒ 'a ↔ 'b"
where [simp]: "res_prod R S = ((- Domain S) ⊲⇩r R) ∪ S"
..
end
instantiation set :: (pre_restrict) oplus
begin
oplus_set :: "'a set ⇒ 'a set ⇒ 'a set" where
"oplus_set = res"
..
end
:: "('a ↔ 'b) ⇒ 'a ⇒ 'b ⇒ 'a ↔ 'b" where
"rel_update R k v = R ⊕ {(k, v)}"
text ‹ Relational update adds a new pair to a relation. ›
:: "('a ↔ 'b set) ⇒ bool" where
"rel_disjoint f = (∀ p ∈ f. ∀ q ∈ f. p ≠ q ⟶ snd p ∩ snd q = {})"
:: "('a ↔ 'b set) ⇒ 'b set ⇒ bool" where
"rel_partitions f a = (rel_disjoint f ∧ ⋃ (Range f) = a)"
subsection ‹ Domain laws ›
declare Domain_Un_eq [simp]
lemma Domain_Preimage: "Domain P = P¯ `` UNIV"
by (simp add: Image_def Domain_unfold)
lemma Domain_relcomp [simp]: "Domain (P O Q) = Domain (P ⊳⇩r Domain Q)"
by (force simp add: Domain_iff rel_ranres_def)
lemma Domain_relcomp_conv: "Domain (P O Q) = (P¯ `` Domain(Q))"
by (simp add: Domain_Preimage converse_relcomp relcomp_Image)
lemma Domain_set: "Domain (set xs) = set (map fst xs)"
by (simp add: Domain_fst)
subsection ‹ Range laws ›
lemma : "Range P = P `` UNIV"
by (simp add: Image_def Range_iff set_eq_iff)
lemma : "Range (P O Q) = (Q `` Range(P))"
by (simp add: Range_Image relcomp_Image)
subsection ‹ Domain Restriction ›
lemma Domain_rel_domres [simp]: "Domain (A ⊲⇩r R) = A ∩ Domain(R)"
by (auto simp add: rel_domres_def)
lemma [simp]: "{} ⊲⇩r R = {}"
by (simp add: rel_domres_def)
lemma [simp]: "UNIV ⊲⇩r R = R"
by (simp add: rel_domres_def)
lemma [simp]: "A ⊲⇩r {} = {}"
by (simp add: rel_domres_def)
lemma [simp]: "A ⊲⇩r B ⊲⇩r R = (A ∩ B) ⊲⇩r R"
by (auto simp add: rel_domres_def)
lemma : "A ∩ Domain P = {} ⟹ (- A) ⊲⇩r P = P"
by (auto simp add: rel_domres_def)
lemma : "k ∉ Domain(f) ⟹ (- {k}) ⊲⇩r f = f"
by (simp add: rel_domres_compl_disj)
lemma : "A ⊲⇩r R = Id_on A O R"
by (auto simp add: rel_domres_def Id_on_def relcomp_unfold)
lemma [simp]:
"A ⊲⇩r insert (k, v) R = (if (k ∈ A) then insert (k, v) (A ⊲⇩r R) else A ⊲⇩r R)"
by (auto simp add: rel_domres_def)
lemma [simp]: "x ∉ Domain P ⟹ (insert x A) ⊲⇩r P = A ⊲⇩r P"
by (auto simp add: rel_domres_def)
lemma : "R `` A = Range (A ⊲⇩r R)"
by (auto simp add: rel_domres_def)
lemma [simp]: "A ⊲⇩r (S ∪ R) = (A ⊲⇩r S) ∪ (A ⊲⇩r R)"
by (auto simp add: rel_domres_def)
subsection ‹ Range Restriction ›
lemma [simp]: "P ⊳⇩r UNIV = P"
by (auto simp add: rel_ranres_def)
lemma [simp]: "(P ∪ Q) ⊳⇩r A = (P ⊳⇩r A ∪ Q ⊳⇩r A)"
by (auto simp add: rel_ranres_def)
lemma [simp]: "(P O Q) ⊳⇩r A = P O (Q ⊳⇩r A)"
by (auto simp add: rel_ranres_def relcomp_unfold prod.case_eq_if)
lemma [simp]: "(P ⊳⇩r A)¯ = A ⊲⇩r P¯"
by (auto simp add: rel_domres_def rel_ranres_def)
lemma : "A ⊆ B ⟹ f ⊳⇩r A ≤ f ⊳⇩r B"
by (simp add: Collect_mono_iff in_mono rel_ranres_def)
subsection ‹ Relational Override ›
= pre_restrict +
assumes : "cmpt P Q ⟹ cmpt Q P"
and : "cmpt {} P"
assumes : "res P P = P"
and : "res P (res Q R) = res (res P Q) R"
and : "res {} P = P"
and : "cmpt P Q ⟹ res P Q = res Q P"
and : "⟦ cmpt P Q; cmpt (res P Q) R ⟧ ⟹ cmpt P R"
and : "⟦ cmpt P Q; cmpt P R; cmpt Q R ⟧ ⟹ cmpt (res P Q) R"
lemma : "cmpt (P :: 'a ↔ 'b) Q ⟹ cmpt (res P Q) R ⟹ cmpt P R"
by (fastforce simp add: rel_domres_def Domain_iff)
prod :: (type, type) restrict
by (intro_classes, simp_all only: res_cmpt_rel, auto simp add: rel_domres_def Domain_unfold)
instantiation set :: (restrict) override
begin
compatible_set :: "'a set ⇒ 'a set ⇒ bool" where
"compatible_set = cmpt"
apply (intro_classes, simp_all add: oplus_set_def compatible_set_def res_idem res_assoc res_lzero cmpt_sym cmpt_empty res_comm res_cmptI)
using res_cmpt apply blast
done
end
lemma : "R ⊕ S = ((- Domain S) ⊲⇩r R) ∪ S"
by (simp add: oplus_set_def)
lemma Domain_rel_override [simp]: "Domain (R ⊕ S) = Domain(R) ∪ Domain(S)"
by (auto simp add: oplus_set_def)
lemma : "Range(R ⊕ S) ⊆ Range(R) ∪ Range(S)"
by (auto simp add: oplus_set_def rel_domres_def)
lemma : "R ## S = (Domain R ⊲⇩r S = Domain S ⊲⇩r R)"
by (simp add: compatible_set_def)
lemma : "Domain R ⊲⇩r S = Domain S ⊲⇩r R ⟹ R ## S"
by (simp add: compatible_rel)
subsection ‹ Functional Relations ›
abbreviation :: "('a ↔ 'b) ⇒ bool" where
"functional R ≡ single_valued R"
lemma : "functional R ⟷ inj_on fst R"
by (force simp add: single_valued_def inj_on_def)
lemma : "functional R ⟷ R¯ O R ⊆ Id"
apply (simp add: functional_def subset_iff relcomp_unfold, safe)
using inj_on_eq_iff apply fastforce
apply (metis inj_onI surjective_pairing)
done
lemma :
assumes "functional R" "(x, y) ∈ R"
shows "R(x)⇩r = y"
by (metis (no_types, lifting) Domain.intros DomainE assms(1) assms(2) single_valuedD rel_apply_def theI_unique)
lemma : "functional R ⟹ (x, y) ∈ R ⟷ (x ∈ Domain R ∧ R(x)⇩r = y)"
by (auto simp add: functional_apply)
lemma :
assumes "functional R" "x ∈ Domain(R)"
shows "(x, R(x)⇩r) ∈ R"
using assms(1) assms(2) functional_apply by fastforce
lemma [intro!]: "⟦ functional R; functional S ⟧ ⟹ functional (R ⊕ S)"
by (auto simp add: functional_algebraic oplus_set_def rel_domres_def)
lemma [intro!]: "⟦ functional R; functional S; R ## S ⟧ ⟹ functional (R ∪ S)"
by (metis functional_override le_sup_iff override_comm override_eq single_valued_subset subsetI)
:: "('a × 'b) list ⇒ bool" where
"functional_list xs = (∀ x y z. ListMem (x,y) xs ∧ ListMem (x,z) xs ⟶ y = z)"
lemma [simp]: "functional (insert (x,y) g) ⟷ (g``{x} ⊆ {y} ∧ functional g)"
by (auto simp add:functional_def inj_on_def image_def)
lemma [simp]: "functional_list []"
by (simp add:functional_list_def ListMem_iff)
lemma : "functional_list xs ⟷ functional (set xs)"
apply (induct xs)
apply (simp add:functional_def)
apply (simp add:functional_def functional_list_def ListMem_iff)
apply (safe)
apply (force)
apply (force)
apply (force)
apply (force)
apply (force)
apply (force)
apply (force)
apply (force)
done
:: "('a ⇒ 'b) ⇒ ('a ↔ 'b)" where
"fun_rel f = {(x, y). y = f x}"
lemma : "functional (fun_rel f)"
by (simp add: fun_rel_def functional_def)
(metis (mono_tags, lifting) Product_Type.Collect_case_prodD inj_onI prod.expand)
lemma [simp]: "(fun_rel f)(x)⇩r = f x"
by (simp add: fun_rel_def rel_apply_def)
text ‹ Make a relation functional by removing any pairs that have duplicate distinct values. ›
:: "('a ↔ 'b) ⇒ ('a ↔ 'b)" where
"mk_functional R = {(x, y) ∈ R. ∀ y'. (x, y') ∈ R ⟶ y = y'}"
lemma [simp]: "functional (mk_functional R)"
by (auto simp add: mk_functional_def single_valued_def)
lemma : "functional R ⟹ mk_functional R = R"
by (auto simp add: mk_functional_def single_valued_def)
lemma : "mk_functional (mk_functional R) = mk_functional R"
using mk_functional mk_functional_fp by blast
lemma [simp]: "mk_functional R ⊆ R"
by (auto simp add: mk_functional_def)
lemma Domain_mk_functional: "Domain (mk_functional R) ⊆ Domain R"
by (auto simp add: mk_functional_def)
:: "('a × 'b) set ⇒ 'a set" where
"single_valued_dom R = {x ∈ Domain(R). ∃ y. R `` {x} = {y}}"
lemma : "mk_functional R = single_valued_dom R ⊲⇩r R"
by (auto simp add: mk_functional_def single_valued_dom_def rel_domres_math_def Domain_unfold)
(metis Image_singleton_iff singletonD)
subsection ‹ Left-Total Relations›
:: "'a set ⇒ ('a ↔ 'b) ⇒ bool" where
"left_totalr_on A R ⟷ (∀x∈A. ∃y. (x, y) ∈ R)"
abbreviation " R ≡ left_totalr_on UNIV R"
lemma : "left_totalr R ⟷ Id ⊆ R O R¯"
by (auto simp add: left_totalr_on_def)
lemma : "left_totalr (fun_rel f)"
by (simp add: left_totalr_on_def fun_rel_def)
subsection ‹ Injective Relations ›
:: "('a ↔ 'b) ⇒ bool" where
"injective R = (functional R ∧ R O R¯ ⊆ Id)"
lemma :
assumes "functional R" "⋀ x y. ⟦ x ∈ Domain R; y ∈ Domain R; R(x)⇩r = R(y)⇩r ⟧ ⟹ x = y"
shows "injective R"
using assms by (auto simp add: injective_def functional_apply_iff)
subsection ‹ Relation Sets ›
:: "'a set ⇒ 'b set ⇒ ('a ↔ 'b) set" (infixr "↔" 55) where
"rel_typed A B = {R. Domain(R) ⊆ A ∧ Range(R) ⊆ B}"
lemma : "⟦ Domain(R) ⊆ A; Range(R) ⊆ B ⟧ ⟹ R ∈ A ↔ B"
by (simp add: rel_typed_def)
:: "'a set ⇒ 'b set ⇒ ('a ↔ 'b) set" (infixr "→⇩p" 55) where
"rel_pfun A B = {R. R ∈ A ↔ B ∧ functional R}"
lemma : "⟦ R ∈ A ↔ B; functional R ⟧ ⟹ R ∈ A →⇩p B"
by (simp add: rel_pfun_def)
:: "'a set ⇒ 'b set ⇒ ('a ↔ 'b) set" (infixr "→⇩t" 55) where
"rel_tfun A B = {R. R ∈ A →⇩p B ∧ left_totalr R}"
:: "'a set ⇒ 'b set ⇒ ('a ↔ 'b) set" (infixr "→⇩f" 55) where
"rel_ffun A B = {R. R ∈ A →⇩p B ∧ finite(Domain R)}"
subsection ‹ Closure Properties ›
text ‹ These can be seen as typing rules for relational functions ›
named_theorems
lemma [rclos]: "R ∈ rel_ffun A B ⟹ R ∈ A →⇩p B"
by (simp add: rel_ffun_def)
lemma [rclos]: "R ∈ A →⇩t B ⟹ R ∈ A →⇩p B"
by (simp add: rel_tfun_def)
lemma [rclos]: "R ∈ A →⇩p B ⟹ R ∈ A ↔ B"
by (simp add: rel_pfun_def)
lemma [rclos]: "{} ∈ rel_ffun A B"
by (simp add: rel_ffun_def rel_pfun_def rel_typed_def)
lemma [rclos]: "⟦ x ∈ Domain(R); R ∈ A →⇩p B ⟧ ⟹ R(x)⇩r ∈ B"
using functional_apply by (fastforce simp add: rel_pfun_def rel_typed_def)
lemma [rclos]: "⟦ x ∈ A; R ∈ A →⇩t B ⟧ ⟹ R(x)⇩r ∈ B"
by (metis (no_types, lifting) Domain_iff iso_tuple_UNIV_I left_totalr_on_def mem_Collect_eq rel_pfun_apply rel_tfun_def)
lemma [rclos]: "⟦ R ∈ A ↔ B; x ∈ A; y ∈ B ⟧ ⟹ insert (x, y) R ∈ A ↔ B"
by (simp add: rel_typed_def)
lemma [rclos]: "⟦ R ∈ A →⇩p B; x ∈ A; y ∈ B; x ∉ Domain(R) ⟧ ⟹ insert (x, y) R ∈ A →⇩p B"
by (auto intro: rclos simp add: rel_pfun_def)
lemma [rclos]: "⟦ R ∈ A →⇩p B; S ∈ A →⇩p B ⟧ ⟹ (R ⊕ S) ∈ A →⇩p B"
apply (rule rel_pfun_intro)
apply (rule rel_typed_intro)
apply (simp_all add: rel_pfun_def rel_typed_def, safe)
apply (metis (no_types, opaque_lifting) Range.simps Range_Un_eq Range_rel_override Un_iff rev_subsetD)
done
subsection ‹ Code Generation ›
lemma [code]: "(set xs)¯ = set (map (λ(x, y). (y, x)) xs)"
by (induct xs, auto)
lemma [code]: "A ⊲⇩r set xs = set (AList.restrict A xs)"
by (induct xs, simp_all, safe, simp_all)
lemma [code]: "set xs `` A = set (map snd (AList.restrict A xs))"
by (simp add: Image_as_rel_domres rel_domres_alist Range_snd)
lemma : "{x ∈ set xs. P x} = set (filter P xs)"
by auto
lemma [code]:
"single_valued_dom (set xs) = set (filter (λx. length (remdups (map snd (AList.restrict {x} xs))) = 1) (map fst xs))"
by (simp only: single_valued_dom_def set_map[THEN sym] Image_alist Domain_set set_singleton_iff list_singleton_iff Collect_set)
lemma : "AList.restrict (set (filter P (map fst xs))) xs = filter (λ (x, y). P x) xs"
by (auto intro: filter_cong simp add: Domain.intros fst_eq_Domain AList.restrict_eq)
lemma [code]:
"mk_functional (set xs) = set (filter (λ (x,y). length (remdups (map snd (AList.restrict {x} xs))) = 1) xs)"
by (simp only: mk_functional_single_valued_dom rel_domres_alist single_valued_dom_alist AList_restrict_in_dom)
lemma [code]:
"rel_apply (set xs) k =
(let ys = filter (λ (k', v). k = k') xs in
if (length ys > 0 ∧ ys = replicate (length ys) (hd ys)) then snd (hd ys) else undefined)"
proof -
let ?ys = "filter (λ(k', v). k = k') xs"
have 1: "⟦ ?ys ≠ []; ?ys = replicate (length ?ys) (hd ?ys) ⟧ ⟹
set xs(k)⇩r = snd (hd ?ys)"
proof -
assume ys: "?ys ≠ []" "?ys = replicate (length ?ys) (hd ?ys)"
have kmem: "⋀ y. (k, y) ∈ set xs ⟷ (k, y) ∈ set ?ys"
by simp
from ys obtain v where v: "(k, v) ∈ set xs"
using hd_in_set by fastforce
hence ys':"?ys = replicate (length ?ys) (k, v)"
by (metis (mono_tags) case_prodI filter_set in_set_replicate member_filter ys(2))
hence "snd (hd ?ys) = v"
by (metis hd_replicate replicate_0 snd_conv ys(1))
moreover have "(THE y. (k, y) ∈ set xs) = v"
by (metis (no_types, lifting) v in_set_replicate kmem snd_conv the_equality ys')
moreover have "(∃!y. (k, y) ∈ set xs)"
by (metis Pair_inject v in_set_replicate kmem ys')
ultimately show "set xs(k)⇩r = snd (hd ?ys)"
by (simp add: rel_apply_def)
qed
have 2: "?ys = [] ⟹ set xs(k)⇩r = undefined"
proof -
assume "filter (λ(k', v). k = k') xs = []"
hence "∄v. (k, v) ∈ set xs"
by (metis (mono_tags, lifting) case_prodI filter_empty_conv)
thus "set xs(k)⇩r = undefined"
by (auto simp add: rel_apply_def)
qed
have 3: "?ys ≠ replicate (length ?ys) (hd ?ys) ⟹ set xs(k)⇩r = undefined"
proof -
assume ys: "?ys ≠ replicate (length ?ys) (hd ?ys)"
have keys: "∀ (k', v') ∈ set ?ys. k' = k"
by auto
show "set xs(k)⇩r = undefined"
proof (cases "length ?ys = 0")
case True
then show ?thesis
using ys by fastforce
next
case False
hence "length ?ys > 1"
by (metis hd_in_set in_set_conv_nth length_0_conv less_one linorder_neqE_nat replicate_length_same ys)
have "fst (hd ?ys) = k"
using False hd_in_set by force
have "¬(∀ (k, v) ∈ set ?ys. v = snd (hd ?ys))"
proof
assume "(∀ (k, v) ∈ set ?ys. v = snd (hd ?ys))"
hence "(∀ p ∈ set ?ys. p = (k, snd (hd ?ys)))"
by fastforce
hence "?ys = replicate (length ?ys) (hd ?ys)"
by (metis False length_0_conv list.set_sel(1) replicate_length_same)
thus False
using ys by blast
qed
then obtain v where "(k, v) ∈ set ?ys" "v ≠ snd (hd ?ys)"
by fastforce
hence "(¬ (∃!y. (k, y) ∈ set xs))"
using False list.set_sel(1) by fastforce
then show ?thesis
by (simp add: rel_apply_def)
qed
qed
from 1 2 3 show ?thesis
by (simp add: Let_unfold)
qed
end