Theory Relation_Toolkit

section ‹ Relation Toolkit ›

theory Relation_Toolkit
  imports Set_Toolkit Overriding
begin

subsection ‹ Conversions ›

text ‹ The majority of the relation toolkit is also part of HOL. We just need to generalise
  some of the syntax. ›

declare [[coercion rel_apply]]
declare [[coercion pfun_app]]
declare [[coercion pfun_of]]


text ‹ The following definition is semantically identical to @{const pfun_graph}, but is used to 
  represent coercions with associated reasoning. ›

definition rel_of_pfun :: "'a ⇸ 'b ⇒ 'a ↔ 'b" ("[_]⇸") where
[code_unfold]: "rel_of_pfun = pfun_graph"

declare [[coercion rel_of_pfun]]
declare [[coercion pfun_of_pinj]]

notation pfun_of_pinj ("[_]⤔")

subsection ‹ First component projection ›

text ‹ Z supports n-ary Cartesian products. We cannot support such structures directly in 
  Isabelle/HOL, but instead add the projection notations for the first and second components.
  A homogeneous finite Cartesian product type also exists in the Multivariate Analysis package. ›

abbreviation (input) "first ≡ fst"
notation first ("_.1" [999] 999)

subsection ‹ Second component projection ›

abbreviation (input) "second ≡ snd"
notation second ("_.2" [999] 999)

subsection ‹ Maplet ›

subsection ‹ Domain ›

hide_const (open) dom

consts dom :: "'f ⇒ 'a set"

adhoc_overloading
  dom ⇌ Map.dom and
  dom ⇌ Relation.Domain and
  dom ⇌ Partial_Fun.pdom and
  dom ⇌ Finite_Fun.fdom and
  dom ⇌ Partial_Inj.pidom

subsection ‹ Range ›

hide_const (open) ran

consts ran :: "'f ⇒ 'a set"

adhoc_overloading
  ran ⇌ Map.ran and
  ran ⇌ Relation.Range and
  ran ⇌ Partial_Fun.pran and
  ran ⇌ Finite_Fun.fran and
  ran ⇌ Partial_Inj.piran

subsection ‹ Identity relation ›

notation Id_on ("id[_]")

subsection ‹ Relational composition ›

notation relcomp (infixr ";" 75)

subsection ‹ Functional composition ›

text ‹ Composition is probably the most difficult of the Z functions to implement correctly. Firstly,
  the notation @{term "(∘)"} is already defined for HOL functions, and we need to respect that
  in order to use the HOL library functions. Secondly, Z composition can be used to compose 
  heterogeneous relations and functions, which is not easy to type infer. Consequently, we opt
  to use adhoc overloading here. ›

consts zcomp :: "'f ⇒ 'g ⇒ 'h" 

adhoc_overloading
  zcomp ⇌ Fun.comp and
  zcomp ⇌ pfun_comp and
  zcomp ⇌ ffun_comp

text ‹ Once we overload @{term Fun.comp}, we need to at least have output syntax set up. ›

notation (output) zcomp (infixl "∘" 55)

bundle Z_Relation_Syntax
begin

no_notation Fun.comp (infixl "∘" 55)
notation zcomp (infixl "∘" 55)

end

subsection ‹ Domain restriction and subtraction ›

consts dom_res :: "'a set ⇒ 'r ⇒ 'r" (infixr "◁" 85)

abbreviation ndres (infixr "⩤" 85) where "ndres A P ≡ CONST dom_res (- A) P" 

adhoc_overloading 
  dom_res ⇌ rel_domres
  and dom_res ⇌ pdom_res
  and dom_res ⇌ fdom_res
  and dom_res ⇌ pinj_dres

syntax "_ndres" :: "logic ⇒ logic ⇒ logic" 
translations "_ndres A P" == "CONST dom_res (- A) P"

subsection ‹ Range restriction and subtraction ›

consts ran_res :: "'r ⇒ 'a set ⇒ 'r" (infixl "▷" 86)

abbreviation nrres (infixl "⩥" 86) where "nrres P A ≡ CONST ran_res P (- A)"

adhoc_overloading 
  ran_res ⇌ rel_ranres
  and ran_res ⇌ pran_res
  and ran_res ⇌ fran_res
  and ran_res ⇌ pinj_rres

subsection ‹ Relational inversion ›

notation converse ("(_∼)" [1000] 999)

lemma relational_inverse: "r∼ = {(p.2, p.1) | p. p ∈ r}"
  by auto

subsection ‹ Relational image ›

notation Image ("_⦇_⦈" [990] 990)

lemma Image_eq: "Image r a = {p.2 | p. p ∈ r ∧ p.1 ∈ a}"
  by (auto simp add: Image_def)

subsection ‹ Overriding ›

lemma dom_override: "dom ((Q :: 'a ↔ 'b) ⊕ R) = (dom Q) ∪ (dom R)"
  by (simp add: Un_Int_distrib2)

lemma override_Un: "dom Q ∩ dom R = {} ⟹ Q ⊕ R = Q ∪ R"
  by (simp add: override_eq Int_commute rel_domres_compl_disj)

subsection ‹ Proof Support ›

text ‹ The objective of these laws is to, as much as possible, convert relational constructions
  into functional ones. The benefit is better proof automation in the more type constrained setting. ›

lemma rel_of_pfun_eq_iff [simp]: "[f]⇸ = [g]⇸ ⟷ f = g"
  by (simp add: pfun_eq_graph rel_of_pfun_def)

lemma rel_of_pfun_le_iff [simp]: "[f]⇸ ⊆ [g]⇸ ⟷ f ≤ g"
  by (simp add: pfun_graph_le_iff rel_of_pfun_def)

lemma rel_of_pfun_pabs: "[pabs A P f]⇸ = {(k, v). k ∈ A ∧ P k ∧ v = f k}"
  by (simp add: rel_of_pfun_def pfun_graph_pabs)

lemma rel_of_pfun_apply [simp]: "[f]⇸ x = f x"
  by (simp add: rel_of_pfun_def)

lemma rel_of_pfun_functional [simp]: "functional [f]⇸"
  by (simp add: rel_of_pfun_def)

lemma rel_of_pfun_override [simp]: "[f]⇸ ⊕ [g]⇸ = [f ⊕ g]⇸"
  by (simp add: pfun_graph_override rel_of_pfun_def)

lemma rel_of_pfun_comp [simp]: "[f]⇸ O [g]⇸ = [g ∘p f]⇸"
  by (simp add: pfun_graph_comp rel_of_pfun_def)

lemma pfun_comp_inv: "[f ∘p g]⇸∼ = [f]⇸∼ O [g]⇸∼"
  by (metis converse_relcomp rel_of_pfun_comp)

lemma rel_of_pfun_dom [simp]: "Domain [f]⇸ = pdom f"
  by (simp add: Dom_pfun_graph rel_of_pfun_def)

lemma rel_of_pfun_ran [simp]: "Range [f]⇸ = pran f"
  by (simp add: Range_pfun_graph rel_of_pfun_def)

lemma rel_of_pfun_domres [simp]: "A ◁ [f]⇸ = [A ◁ f]⇸"
  by (simp add: pfun_graph_domres rel_of_pfun_def)

lemma rel_of_pfun_ranres [simp]: "[f]⇸ ▷ A = [f ▷ A]⇸"
  by (simp add: rel_of_pfun_def pfun_graph_rres)

lemma rel_of_pfun_image [simp]: "[f]⇸ ⦇ A ⦈ = pfun_image f A"
  by (simp add: Image_as_rel_domres)

lemma rel_of_pfun_member_iff [simp]:
  "(k, v) ∈ [f]⇸ ⟷ (k ∈ dom f ∧ f k = v)"
  by (simp add: rel_of_pfun_def)

lemma rel_of_pinj_conv [simp]: "[[f]⤔]⇸¯ = [[pinv f]⤔]⇸"
  by (simp add: pfun_graph_pfun_inv pinv.rep_eq rel_of_pfun_def)

lemma dom_pinv [simp]: "dom [pinv f]⤔ = ran [f]⤔"
  by (simp add: pinv.rep_eq)

lemma ran_pinv [simp]: "ran [pinv f]⤔ = dom [f]⤔"
  by (metis dom_pinv pinv_pinv)

lemma pfun_inj_rel_conv [simp]: "pfun_inj f ⟹ [f]⇸∼ = [pfun_inv f]⇸"
  by (simp add: pfun_graph_pfun_inv rel_of_pfun_def)

end