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 ›

definition "TUNIV (a::'a itself) = (UNIV :: 'a set)"

syntax "_tvar" :: "type  logic" ("[_]T")
translations "['a]T" == "CONST TUNIV TYPE('a)"

lemma TUNIV_mem [simp]: "x  ['a]T"
  by (simp add: TUNIV_def)

subsection ‹ Relational Function Operations ›

text ‹ These functions are all adapted from their ISO Z counterparts. ›

definition rel_apply :: "('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 "e3"} such that @{term "(e2, e3)"} is in @{term "e1"}, then 
  the value of @{term "e1(e2)r"} is @{term e3}, otherwise each @{term "e1(e2)r"} has a fixed but 
  unknown value (i.e. @{const undefined}). ›

definition rel_domres :: "'a set  ('a  'b)  'a  'b" (infixr "r" 85) where
"rel_domres A R = {p  R. fst p  A}"

lemma rel_domres_math_def: "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}. ›

definition rel_ranres :: "('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. ›

class pre_restrict =
  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. ›

definition cmpt_prod :: "('a  'b)  ('a  'b)  bool"
  where [simp]: "cmpt_prod R S = ((Domain R) r S = (Domain S) r R)"
definition res_prod :: "('a  'b)  ('a  'b)  'a  'b" 
  where [simp]: "res_prod R S = ((- Domain S) r R)  S"
instance ..
end

instantiation set :: (pre_restrict) oplus
begin

definition oplus_set :: "'a set  'a set  'a set" where
"oplus_set = res"

instance ..

end

definition rel_update :: "('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. ›

definition rel_disjoint :: "('a  'b set)  bool" where
"rel_disjoint f = ( p  f.  q  f. p  q  snd p  snd q = {})"

definition rel_partitions :: "('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_Image: "Range P = P `` UNIV"
  by (simp add: Image_def Range_iff set_eq_iff)

lemma Range_relcomp: "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 rel_domres_empty [simp]: "{} r R = {}"
  by (simp add: rel_domres_def)

lemma rel_domres_UNIV [simp]: "UNIV r R = R"
  by (simp add: rel_domres_def)

lemma rel_domres_nil [simp]: "A r {} = {}"
  by (simp add: rel_domres_def)

lemma rel_domres_inter [simp]: "A r B r R = (A  B) r R"
  by (auto simp add: rel_domres_def)

lemma rel_domres_compl_disj: "A  Domain P = {}  (- A) r P = P"
  by (auto simp add: rel_domres_def)

lemma rel_domres_notin_Dom: "k  Domain(f)  (- {k}) r f = f"
  by (simp add: rel_domres_compl_disj)

lemma rel_domres_Id_on: "A r R = Id_on A O R"
  by (auto simp add: rel_domres_def Id_on_def relcomp_unfold)

lemma rel_domres_insert [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 rel_domres_insert_set [simp]: "x  Domain P  (insert x A) r P = A r P"
  by (auto simp add: rel_domres_def)

lemma Image_as_rel_domres: "R `` A = Range (A r R)"
  by (auto simp add: rel_domres_def)

lemma rel_domres_Un [simp]: "A r (S  R) = (A r S)  (A r R)"
  by (auto simp add: rel_domres_def)

subsection ‹ Range Restriction ›

lemma rel_ranres_UNIV [simp]: "P r UNIV = P"
  by (auto simp add: rel_ranres_def)

lemma rel_ranres_Un [simp]: "(P  Q) r A = (P r A  Q r A)"
  by (auto simp add: rel_ranres_def)

lemma rel_ranres_relcomp [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 conv_rel_domres [simp]: "(P r A)¯ = A r P¯"
  by (auto simp add: rel_domres_def rel_ranres_def)

lemma rel_ranres_le: "A  B  f r A  f r B"
  by (simp add: Collect_mono_iff in_mono rel_ranres_def)

subsection ‹ Relational Override ›

class restrict = pre_restrict +
  assumes cmpt_sym: "cmpt P Q  cmpt Q P"
  and cmpt_empty: "cmpt {} P"
  assumes res_idem: "res P P = P"
  and res_assoc: "res P (res Q R) = res (res P Q) R"
  and res_lzero: "res {} P = P"
  and res_comm: "cmpt P Q  res P Q = res Q P"
  and res_cmpt: " cmpt P Q; cmpt (res P Q) R   cmpt P R"
  and res_cmptI: " cmpt P Q; cmpt P R; cmpt Q R   cmpt (res P Q) R"

lemma res_cmpt_rel: "cmpt (P :: 'a  'b) Q  cmpt (res P Q) R  cmpt P R"
  by (fastforce simp add: rel_domres_def Domain_iff)

instance 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
definition compatible_set :: "'a set  'a set  bool" where
"compatible_set = cmpt"

instance 
  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 override_eq: "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_rel_override: "Range(R  S)  Range(R)  Range(S)"
  by (auto simp add: oplus_set_def rel_domres_def)

lemma compatible_rel: "R ## S = (Domain R r S = Domain S r R)"
  by (simp add: compatible_set_def)

lemma compatible_relI: "Domain R r S = Domain S r R  R ## S"
  by (simp add: compatible_rel)

subsection ‹ Functional Relations ›

abbreviation functional :: "('a  'b)  bool" where
"functional R  single_valued R"

lemma functional_def: "functional R  inj_on fst R"
  by (force simp add: single_valued_def inj_on_def)

lemma functional_algebraic: "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 functional_apply: 
  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_apply_iff: "functional R  (x, y)  R  (x  Domain R  R(x)r = y)"
  by (auto simp add: functional_apply)

lemma functional_elem:
  assumes "functional R" "x  Domain(R)"
  shows "(x, R(x)r)  R"
  using assms(1) assms(2) functional_apply by fastforce

lemma functional_override [intro!]: " functional R; functional S   functional (R  S)"
  by (auto simp add: functional_algebraic oplus_set_def rel_domres_def)

lemma functional_union [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)
  
definition functional_list :: "('a × 'b) list  bool" where
"functional_list xs = ( x y z. ListMem (x,y) xs  ListMem (x,z) xs  y = z)"

lemma functional_insert [simp]: "functional (insert (x,y) g)  (g``{x}  {y}  functional g)"
  by (auto simp add:functional_def inj_on_def image_def)

lemma functional_list_nil[simp]: "functional_list []"
  by (simp add:functional_list_def ListMem_iff)

lemma functional_list: "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

definition fun_rel :: "('a  'b)  ('a  'b)" where
"fun_rel f = {(x, y). y = f x}"

lemma functional_fun_rel: "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 rel_apply_fun [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. ›

definition mk_functional :: "('a  'b)  ('a  'b)" where
"mk_functional R = {(x, y)  R.  y'. (x, y')  R  y = y'}"

lemma mk_functional [simp]: "functional (mk_functional R)"
  by (auto simp add: mk_functional_def single_valued_def)

lemma mk_functional_fp: "functional R  mk_functional R = R"
  by (auto simp add: mk_functional_def single_valued_def)

lemma mk_functional_idem: "mk_functional (mk_functional R) = mk_functional R"
  using mk_functional mk_functional_fp by blast

lemma mk_functional_subset [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)

definition single_valued_dom :: "('a × 'b) set  'a set" where
"single_valued_dom R = {x  Domain(R).  y. R `` {x} = {y}}"

lemma mk_functional_single_valued_dom: "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›

definition left_totalr_on :: "'a set  ('a  'b)  bool" where
"left_totalr_on A R  (xA. y. (x, y)  R)"

abbreviation "left_totalr R  left_totalr_on UNIV R"

lemma left_totalr_algebraic: "left_totalr R  Id  R O R¯"
  by (auto simp add: left_totalr_on_def)

lemma left_totalr_fun_rel: "left_totalr (fun_rel f)"
  by (simp add: left_totalr_on_def fun_rel_def)

subsection ‹ Injective Relations ›

definition injective :: "('a  'b)  bool" where
"injective R = (functional R  R O R¯  Id)"

lemma injectiveI: 
  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 ›

definition rel_typed :: "'a set  'b set  ('a  'b) set" (infixr "" 55) where
"rel_typed A B = {R. Domain(R)  A  Range(R)  B}" ― ‹ Relations ›

lemma rel_typed_intro: " Domain(R)  A; Range(R)  B   R  A  B"
  by (simp add: rel_typed_def)

definition rel_pfun :: "'a set  'b set  ('a  'b) set" (infixr "p" 55) where
"rel_pfun A B = {R. R  A  B  functional R}" ― ‹ Partial Functions ›

lemma rel_pfun_intro: " R  A  B; functional R   R  A p B"
  by (simp add: rel_pfun_def)

definition rel_tfun :: "'a set  'b set  ('a  'b) set" (infixr "t" 55) where
"rel_tfun A B = {R. R  A p B  left_totalr R}" ― ‹ Total Functions ›

definition rel_ffun :: "'a set  'b set  ('a  'b) set" (infixr "f" 55) where
"rel_ffun A B = {R. R  A p B  finite(Domain R)}" ― ‹ Finite Functions ›

subsection ‹ Closure Properties ›

text ‹ These can be seen as typing rules for relational functions ›

named_theorems rclos

lemma rel_ffun_is_pfun [rclos]: "R  rel_ffun A B  R  A p B"
  by (simp add: rel_ffun_def)

lemma rel_tfun_is_pfun [rclos]: "R  A t B  R  A p B"
  by (simp add: rel_tfun_def)

lemma rel_pfun_is_typed [rclos]: "R  A p B  R  A  B"
  by (simp add: rel_pfun_def)

lemma rel_ffun_empty [rclos]: "{}  rel_ffun A B"
  by (simp add: rel_ffun_def rel_pfun_def rel_typed_def)

lemma rel_pfun_apply [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 rel_tfun_apply [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 rel_typed_insert [rclos]: " R  A  B; x  A; y  B   insert (x, y) R  A  B"
  by (simp add: rel_typed_def)

lemma rel_pfun_insert [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 rel_pfun_override [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 rel_conv_alist [code]: "(set xs)¯ = set (map (λ(x, y). (y, x)) xs)"
  by (induct xs, auto)

lemma rel_domres_alist [code]: "A r set xs = set (AList.restrict A xs)"
  by (induct xs, simp_all, safe, simp_all)

lemma Image_alist [code]: "set xs `` A = set (map snd (AList.restrict A xs))"
  by (simp add: Image_as_rel_domres rel_domres_alist Range_snd)

lemma Collect_set: "{x  set xs. P x} = set (filter P xs)"
  by auto

lemma single_valued_dom_alist [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_in_dom: "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 mk_functional_alist [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 rel_apply_set [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