Theory Galois_Connection

(*  Title:      HOL/Algebra/Galois_Connection.thy
    Author:     Alasdair Armstrong and Simon Foster
    Copyright:  Alasdair Armstrong and Simon Foster
*)

theory Galois_Connection
  imports Complete_Lattice
begin

section β€ΉGalois connectionsβ€Ί

subsection β€ΉDefinition and basic propertiesβ€Ί

record ('a, 'b, 'c, 'd) galcon =
  orderA :: "('a, 'c) gorder_scheme" ("𝒳ı")
  orderB :: "('b, 'd) gorder_scheme" ("𝒴ı")
  lower  :: "'a β‡’ 'b" ("Ο€*Δ±")
  upper  :: "'b β‡’ 'a" ("Ο€*Δ±")

type_synonym ('a, 'b) galois = "('a, 'b, unit, unit) galcon"

abbreviation "inv_galcon G ≑ ⦇ orderA = inv_gorder π’΄β‡˜G⇙, orderB = inv_gorder π’³β‡˜G⇙, lower = upper G, upper = lower G ⦈"

definition comp_galcon :: "('b, 'c) galois β‡’ ('a, 'b) galois β‡’ ('a, 'c) galois" (infixr "∘g" 85)
  where "G ∘g F = ⦇ orderA = orderA F, orderB = orderB G, lower = lower G ∘ lower F, upper = upper F ∘ upper G ⦈"

definition id_galcon :: "'a gorder β‡’ ('a, 'a) galois" ("Ig") where
"Ig(A) = ⦇ orderA = A, orderB = A, lower = id, upper = id ⦈"


subsection β€ΉWell-typed connectionsβ€Ί

locale connection =
  fixes G (structure)
  assumes is_order_A: "partial_order 𝒳"
  and is_order_B: "partial_order 𝒴"
  and lower_closure: "Ο€* ∈ carrier 𝒳 β†’ carrier 𝒴"
  and upper_closure: "Ο€* ∈ carrier 𝒴 β†’ carrier 𝒳"
begin

  lemma lower_closed: "x ∈ carrier 𝒳 ⟹ Ο€* x ∈ carrier 𝒴"
    using lower_closure by auto

  lemma upper_closed: "y ∈ carrier 𝒴 ⟹ Ο€* y ∈ carrier 𝒳"
    using upper_closure by auto

end


subsection β€ΉGalois connectionsβ€Ί
  
locale galois_connection = connection +
  assumes galois_property: "⟦x ∈ carrier 𝒳; y ∈ carrier π’΄βŸ§ ⟹ Ο€* x βŠ‘β‡˜π’΄β‡™ y ⟷ x βŠ‘β‡˜π’³β‡™ Ο€* y"
begin

  lemma is_weak_order_A: "weak_partial_order 𝒳"
  proof -
    interpret po: partial_order 𝒳
      by (metis is_order_A)
    show ?thesis ..
  qed

  lemma is_weak_order_B: "weak_partial_order 𝒴"
  proof -
    interpret po: partial_order 𝒴
      by (metis is_order_B)
    show ?thesis ..
  qed

  lemma right: "⟦x ∈ carrier 𝒳; y ∈ carrier 𝒴; Ο€* x βŠ‘β‡˜π’΄β‡™ y⟧ ⟹ x βŠ‘β‡˜π’³β‡™ Ο€* y"
    by (metis galois_property)

  lemma left: "⟦x ∈ carrier 𝒳; y ∈ carrier 𝒴; x βŠ‘β‡˜π’³β‡™ Ο€* y⟧ ⟹ Ο€* x βŠ‘β‡˜π’΄β‡™ y"
    by (metis galois_property)

  lemma deflation: "y ∈ carrier 𝒴 ⟹ Ο€* (Ο€* y) βŠ‘β‡˜π’΄β‡™ y"
    by (metis Pi_iff is_weak_order_A left upper_closure weak_partial_order.le_refl)

  lemma inflation: "x ∈ carrier 𝒳 ⟹ x βŠ‘β‡˜π’³β‡™ Ο€* (Ο€* x)"
    by (metis (no_types, lifting) PiE galois_connection.right galois_connection_axioms is_weak_order_B lower_closure weak_partial_order.le_refl)

  lemma lower_iso: "isotone 𝒳 𝒴 Ο€*"
  proof (auto simp add:isotone_def)
    show "weak_partial_order 𝒳"
      by (metis is_weak_order_A)
    show "weak_partial_order 𝒴"
      by (metis is_weak_order_B)
    fix x y
    assume a: "x ∈ carrier 𝒳" "y ∈ carrier 𝒳" "x βŠ‘β‡˜π’³β‡™ y"
    have b: "Ο€* y ∈ carrier 𝒴"
      using a(2) lower_closure by blast
    then have "Ο€* (Ο€* y) ∈ carrier 𝒳"
      using upper_closure by blast
    then have "x βŠ‘β‡˜π’³β‡™ Ο€* (Ο€* y)"
      by (meson a inflation is_weak_order_A weak_partial_order.le_trans)
    thus "Ο€* x βŠ‘β‡˜π’΄β‡™ Ο€* y"
      by (meson b a(1) Pi_iff galois_property lower_closure upper_closure)
  qed

  lemma upper_iso: "isotone 𝒴 𝒳 Ο€*"
    apply (auto simp add:isotone_def)
    apply (metis is_weak_order_B)
    apply (metis is_weak_order_A)
    apply (metis (no_types, lifting) Pi_mem deflation is_weak_order_B lower_closure right upper_closure weak_partial_order.le_trans)
  done

  lemma lower_comp: "x ∈ carrier 𝒳 ⟹ Ο€* (Ο€* (Ο€* x)) = Ο€* x"
    by (meson deflation funcset_mem inflation is_order_B lower_closure lower_iso partial_order.le_antisym upper_closure use_iso2)

  lemma lower_comp': "x ∈ carrier 𝒳 ⟹ (Ο€* ∘ Ο€* ∘ Ο€*) x = Ο€* x"
    by (simp add: lower_comp)

  lemma upper_comp: "y ∈ carrier 𝒴 ⟹ Ο€* (Ο€* (Ο€* y)) = Ο€* y"
  proof -
    assume a1: "y ∈ carrier 𝒴"
    hence f1: "Ο€* y ∈ carrier 𝒳" using upper_closure by blast 
    have f2: "Ο€* (Ο€* y) βŠ‘β‡˜π’΄β‡™ y" using a1 deflation by blast
    have f3: "Ο€* (Ο€* (Ο€* y)) ∈ carrier 𝒳"
      using f1 lower_closure upper_closure by auto 
    have "Ο€* (Ο€* y) ∈ carrier 𝒴" using f1 lower_closure by blast   
    thus "Ο€* (Ο€* (Ο€* y)) = Ο€* y"
      by (meson a1 f1 f2 f3 inflation is_order_A partial_order.le_antisym upper_iso use_iso2) 
  qed

  lemma upper_comp': "y ∈ carrier 𝒴 ⟹ (Ο€* ∘ Ο€* ∘ Ο€*) y = Ο€* y"
    by (simp add: upper_comp)

  lemma adjoint_idem1: "idempotent 𝒴 (Ο€* ∘ Ο€*)"
    by (simp add: idempotent_def is_order_B partial_order.eq_is_equal upper_comp)

  lemma adjoint_idem2: "idempotent 𝒳 (Ο€* ∘ Ο€*)"
    by (simp add: idempotent_def is_order_A partial_order.eq_is_equal lower_comp)

  lemma fg_iso: "isotone 𝒴 𝒴 (Ο€* ∘ Ο€*)"
    by (metis iso_compose lower_closure lower_iso upper_closure upper_iso)

  lemma gf_iso: "isotone 𝒳 𝒳 (Ο€* ∘ Ο€*)"
    by (metis iso_compose lower_closure lower_iso upper_closure upper_iso)

  lemma semi_inverse1: "x ∈ carrier 𝒳 ⟹ Ο€* x = Ο€* (Ο€* (Ο€* x))"
    by (metis lower_comp)

  lemma semi_inverse2: "x ∈ carrier 𝒴 ⟹ Ο€* x = Ο€* (Ο€* (Ο€* x))"
    by (metis upper_comp)

  theorem lower_by_complete_lattice:
    assumes "complete_lattice 𝒴" "x ∈ carrier 𝒳"
    shows "Ο€*(x) = β¨…β‡˜π’΄β‡™ { y ∈ carrier 𝒴. x βŠ‘β‡˜π’³β‡™ Ο€*(y) }"
  proof -
    interpret Y: complete_lattice 𝒴
      by (simp add: assms)

    show ?thesis
    proof (rule Y.le_antisym)
      show x: "Ο€* x ∈ carrier 𝒴"
        using assms(2) lower_closure by blast
      show "Ο€* x βŠ‘β‡˜π’΄β‡™ β¨…β‡˜π’΄β‡™{y ∈ carrier 𝒴. x βŠ‘β‡˜π’³β‡™ Ο€* y}"
      proof (rule Y.weak.inf_greatest)
        show "{y ∈ carrier 𝒴. x βŠ‘β‡˜π’³β‡™ Ο€* y} βŠ† carrier 𝒴"
          by auto
        show "Ο€* x ∈ carrier 𝒴" by (fact x)
        fix z
        assume "z ∈ {y ∈ carrier 𝒴. x βŠ‘β‡˜π’³β‡™ Ο€* y}" 
        thus "Ο€* x βŠ‘β‡˜π’΄β‡™ z"
          using assms(2) left by auto
      qed
      show "β¨…β‡˜π’΄β‡™{y ∈ carrier 𝒴. x βŠ‘β‡˜π’³β‡™ Ο€* y} βŠ‘β‡˜π’΄β‡™ Ο€* x"
      proof (rule Y.weak.inf_lower)
        show "{y ∈ carrier 𝒴. x βŠ‘β‡˜π’³β‡™ Ο€* y} βŠ† carrier 𝒴"
          by auto
        show "Ο€* x ∈ {y ∈ carrier 𝒴. x βŠ‘β‡˜π’³β‡™ Ο€* y}"
        proof (auto)
          show "Ο€* x ∈ carrier 𝒴" by (fact x)
          show "x βŠ‘β‡˜π’³β‡™ Ο€* (Ο€* x)"
            using assms(2) inflation by blast
        qed
      qed
      show "β¨…β‡˜π’΄β‡™{y ∈ carrier 𝒴. x βŠ‘β‡˜π’³β‡™ Ο€* y} ∈ carrier 𝒴"
       by (auto intro: Y.weak.inf_closed)
    qed
  qed

  theorem upper_by_complete_lattice:
    assumes "complete_lattice 𝒳" "y ∈ carrier 𝒴"
    shows "Ο€*(y) = β¨†β‡˜π’³β‡™ { x ∈ carrier 𝒳. Ο€*(x) βŠ‘β‡˜π’΄β‡™ y }"
  proof -
    interpret X: complete_lattice 𝒳
      by (simp add: assms)
    show ?thesis
    proof (rule X.le_antisym)
      show y: "Ο€* y ∈ carrier 𝒳"
        using assms(2) upper_closure by blast
      show "Ο€* y βŠ‘β‡˜π’³β‡™ β¨†β‡˜π’³β‡™{x ∈ carrier 𝒳. Ο€* x βŠ‘β‡˜π’΄β‡™ y}"
      proof (rule X.weak.sup_upper)
        show "{x ∈ carrier 𝒳. Ο€* x βŠ‘β‡˜π’΄β‡™ y} βŠ† carrier 𝒳"
          by auto
        show "Ο€* y ∈ {x ∈ carrier 𝒳. Ο€* x βŠ‘β‡˜π’΄β‡™ y}"
        proof (auto)
          show "Ο€* y ∈ carrier 𝒳" by (fact y)
          show "Ο€* (Ο€* y) βŠ‘β‡˜π’΄β‡™ y"
            by (simp add: assms(2) deflation)
        qed
      qed
      show "β¨†β‡˜π’³β‡™{x ∈ carrier 𝒳. Ο€* x βŠ‘β‡˜π’΄β‡™ y} βŠ‘β‡˜π’³β‡™ Ο€* y"
      proof (rule X.weak.sup_least)
        show "{x ∈ carrier 𝒳. Ο€* x βŠ‘β‡˜π’΄β‡™ y} βŠ† carrier 𝒳"
          by auto
        show "Ο€* y ∈ carrier 𝒳" by (fact y)
        fix z
        assume "z ∈ {x ∈ carrier 𝒳. Ο€* x βŠ‘β‡˜π’΄β‡™ y}" 
        thus "z βŠ‘β‡˜π’³β‡™ Ο€* y"
          by (simp add: assms(2) right)
      qed
      show "β¨†β‡˜π’³β‡™{x ∈ carrier 𝒳. Ο€* x βŠ‘β‡˜π’΄β‡™ y} ∈ carrier 𝒳"
       by (auto intro: X.weak.sup_closed)
    qed
  qed

end

lemma dual_galois [simp]: " galois_connection ⦇ orderA = inv_gorder B, orderB = inv_gorder A, lower = f, upper = g ⦈ 
                          = galois_connection ⦇ orderA = A, orderB = B, lower = g, upper = f ⦈"
  by (auto simp add: galois_connection_def galois_connection_axioms_def connection_def dual_order_iff)

definition lower_adjoint :: "('a, 'c) gorder_scheme β‡’ ('b, 'd) gorder_scheme β‡’ ('a β‡’ 'b) β‡’ bool" where
  "lower_adjoint A B f ≑ βˆƒg. galois_connection ⦇ orderA = A, orderB = B, lower = f, upper = g ⦈"

definition upper_adjoint :: "('a, 'c) gorder_scheme β‡’ ('b, 'd) gorder_scheme β‡’ ('b β‡’ 'a) β‡’ bool" where
  "upper_adjoint A B g ≑ βˆƒf. galois_connection ⦇ orderA = A, orderB = B, lower = f, upper = g ⦈"

lemma lower_adjoint_dual [simp]: "lower_adjoint (inv_gorder A) (inv_gorder B) f = upper_adjoint B A f"
  by (simp add: lower_adjoint_def upper_adjoint_def)

lemma upper_adjoint_dual [simp]: "upper_adjoint (inv_gorder A) (inv_gorder B) f = lower_adjoint B A f"
  by (simp add: lower_adjoint_def upper_adjoint_def)

lemma lower_type: "lower_adjoint A B f ⟹ f ∈ carrier A β†’ carrier B"
  by (auto simp add:lower_adjoint_def galois_connection_def galois_connection_axioms_def connection_def)

lemma upper_type: "upper_adjoint A B g ⟹ g ∈ carrier B β†’ carrier A"
  by (auto simp add:upper_adjoint_def galois_connection_def galois_connection_axioms_def connection_def)


subsection β€ΉComposition of Galois connectionsβ€Ί

lemma id_galois: "partial_order A ⟹ galois_connection (Ig(A))"
  by (simp add: id_galcon_def galois_connection_def galois_connection_axioms_def connection_def)

lemma comp_galcon_closed:
  assumes "galois_connection G" "galois_connection F" "π’΄β‡˜F⇙ = π’³β‡˜G⇙"
  shows "galois_connection (G ∘g F)"
proof -
  interpret F: galois_connection F
    by (simp add: assms)
  interpret G: galois_connection G
    by (simp add: assms)
  
  have "partial_order π’³β‡˜G ∘g F⇙"
    by (simp add: F.is_order_A comp_galcon_def)
  moreover have "partial_order π’΄β‡˜G ∘g F⇙"
    by (simp add: G.is_order_B comp_galcon_def)
  moreover have "Ο€*β‡˜G⇙ ∘ Ο€*β‡˜F⇙ ∈ carrier π’³β‡˜F⇙ β†’ carrier π’΄β‡˜G⇙"
    using F.lower_closure G.lower_closure assms(3) by auto
  moreover have "Ο€*β‡˜F⇙ ∘ Ο€*β‡˜G⇙ ∈ carrier π’΄β‡˜G⇙ β†’ carrier π’³β‡˜F⇙"
    using F.upper_closure G.upper_closure assms(3) by auto
  moreover 
  have "β‹€ x y. ⟦x ∈ carrier π’³β‡˜F⇙; y ∈ carrier π’΄β‡˜G⇙ ⟧ ⟹ 
               (Ο€*β‡˜G⇙ (Ο€*β‡˜F⇙ x) βŠ‘β‡˜π’΄β‡˜G⇙⇙ y) = (x βŠ‘β‡˜π’³β‡˜F⇙⇙ Ο€*β‡˜F⇙ (Ο€*β‡˜G⇙ y))"
    by (metis F.galois_property F.lower_closure G.galois_property G.upper_closure assms(3) Pi_iff)
  ultimately show ?thesis
    by (simp add: comp_galcon_def galois_connection_def galois_connection_axioms_def connection_def)
qed

lemma comp_galcon_right_unit [simp]: "F ∘g Ig(π’³β‡˜F⇙) = F"
  by (simp add: comp_galcon_def id_galcon_def)

lemma comp_galcon_left_unit [simp]: "Ig(π’΄β‡˜F⇙) ∘g F = F"
  by (simp add: comp_galcon_def id_galcon_def)

lemma galois_connectionI:
  assumes
    "partial_order A" "partial_order B"
    "L ∈ carrier A β†’ carrier B" "R ∈ carrier B β†’ carrier A"
    "isotone A B L" "isotone B A R" 
    "β‹€ x y. ⟦ x ∈ carrier A; y ∈ carrier B ⟧ ⟹ L x βŠ‘β‡˜B⇙ y ⟷ x βŠ‘β‡˜A⇙ R y"
  shows "galois_connection ⦇ orderA = A, orderB = B, lower = L, upper = R ⦈"
  using assms by (simp add: galois_connection_def connection_def galois_connection_axioms_def)

lemma galois_connectionI':
  assumes
    "partial_order A" "partial_order B"
    "L ∈ carrier A β†’ carrier B" "R ∈ carrier B β†’ carrier A"
    "isotone A B L" "isotone B A R" 
    "β‹€ X. X ∈ carrier(B) ⟹ L(R(X)) βŠ‘β‡˜B⇙ X"
    "β‹€ X. X ∈ carrier(A) ⟹ X βŠ‘β‡˜A⇙ R(L(X))"
  shows "galois_connection ⦇ orderA = A, orderB = B, lower = L, upper = R ⦈"
  using assms
  by (auto simp add: galois_connection_def connection_def galois_connection_axioms_def, (meson PiE isotone_def weak_partial_order.le_trans)+)


subsection β€ΉRetractsβ€Ί

locale retract = galois_connection +
  assumes retract_property: "x ∈ carrier 𝒳 ⟹ Ο€* (Ο€* x) βŠ‘β‡˜π’³β‡™ x"
begin
  lemma retract_inverse: "x ∈ carrier 𝒳 ⟹ Ο€* (Ο€* x) = x"
    by (meson funcset_mem inflation is_order_A lower_closure partial_order.le_antisym retract_axioms retract_axioms_def retract_def upper_closure)

  lemma retract_injective: "inj_on Ο€* (carrier 𝒳)"
    by (metis inj_onI retract_inverse)
end  

theorem comp_retract_closed:
  assumes "retract G" "retract F" "π’΄β‡˜F⇙ = π’³β‡˜G⇙"
  shows "retract (G ∘g F)"
proof -
  interpret f: retract F
    by (simp add: assms)
  interpret g: retract G
    by (simp add: assms)
  interpret gf: galois_connection "(G ∘g F)"
    by (simp add: assms(1) assms(2) assms(3) comp_galcon_closed retract.axioms(1))
  show ?thesis
  proof
    fix x
    assume "x ∈ carrier π’³β‡˜G ∘g F⇙"
    thus "le π’³β‡˜G ∘g F⇙ (Ο€*β‡˜G ∘g F⇙ (Ο€*β‡˜G ∘g F⇙ x)) x"
      using assms(3) f.inflation f.lower_closed f.retract_inverse g.retract_inverse by (auto simp add: comp_galcon_def)
  qed
qed


subsection β€ΉCoretractsβ€Ί
  
locale coretract = galois_connection +
  assumes coretract_property: "y ∈ carrier 𝒴 ⟹ y βŠ‘β‡˜π’΄β‡™ Ο€* (Ο€* y)"
begin
  lemma coretract_inverse: "y ∈ carrier 𝒴 ⟹ Ο€* (Ο€* y) = y"
    by (meson coretract_axioms coretract_axioms_def coretract_def deflation funcset_mem is_order_B lower_closure partial_order.le_antisym upper_closure)
 
  lemma retract_injective: "inj_on Ο€* (carrier 𝒴)"
    by (metis coretract_inverse inj_onI)
end  

theorem comp_coretract_closed:
  assumes "coretract G" "coretract F" "π’΄β‡˜F⇙ = π’³β‡˜G⇙"
  shows "coretract (G ∘g F)"
proof -
  interpret f: coretract F
    by (simp add: assms)
  interpret g: coretract G
    by (simp add: assms)
  interpret gf: galois_connection "(G ∘g F)"
    by (simp add: assms(1) assms(2) assms(3) comp_galcon_closed coretract.axioms(1))
  show ?thesis
  proof
    fix y
    assume "y ∈ carrier π’΄β‡˜G ∘g F⇙"
    thus "le π’΄β‡˜G ∘g F⇙ y (Ο€*β‡˜G ∘g F⇙ (Ο€*β‡˜G ∘g F⇙ y))"
      by (simp add: comp_galcon_def assms(3) f.coretract_inverse g.coretract_property g.upper_closed)
  qed
qed


subsection β€ΉGalois Bijectionsβ€Ί
  
locale galois_bijection = connection +
  assumes lower_iso: "isotone 𝒳 𝒴 Ο€*" 
  and upper_iso: "isotone 𝒴 𝒳 Ο€*"
  and lower_inv_eq: "x ∈ carrier 𝒳 ⟹ Ο€* (Ο€* x) = x"
  and upper_inv_eq: "y ∈ carrier 𝒴 ⟹ Ο€* (Ο€* y) = y"
begin

  lemma lower_bij: "bij_betw Ο€* (carrier 𝒳) (carrier 𝒴)"
    by (rule bij_betwI[where g="Ο€*"], auto intro: upper_inv_eq lower_inv_eq upper_closed lower_closed)  

  lemma upper_bij: "bij_betw Ο€* (carrier 𝒴) (carrier 𝒳)"
    by (rule bij_betwI[where g="Ο€*"], auto intro: upper_inv_eq lower_inv_eq upper_closed lower_closed)  

sublocale gal_bij_conn: galois_connection
  apply (unfold_locales, auto)
  using lower_closed lower_inv_eq upper_iso use_iso2 apply fastforce
  using lower_iso upper_closed upper_inv_eq use_iso2 apply fastforce
done

sublocale gal_bij_ret: retract
  by (unfold_locales, simp add: gal_bij_conn.is_weak_order_A lower_inv_eq weak_partial_order.le_refl)

sublocale gal_bij_coret: coretract
  by (unfold_locales, simp add: gal_bij_conn.is_weak_order_B upper_inv_eq weak_partial_order.le_refl)

end

theorem comp_galois_bijection_closed:
  assumes "galois_bijection G" "galois_bijection F" "π’΄β‡˜F⇙ = π’³β‡˜G⇙"
  shows "galois_bijection (G ∘g F)"
proof -
  interpret f: galois_bijection F
    by (simp add: assms)
  interpret g: galois_bijection G
    by (simp add: assms)
  interpret gf: galois_connection "(G ∘g F)"
    by (simp add: assms(3) comp_galcon_closed f.gal_bij_conn.galois_connection_axioms g.gal_bij_conn.galois_connection_axioms galois_connection.axioms(1))
  show ?thesis
  proof
    show "isotone π’³β‡˜G ∘g F⇙ π’΄β‡˜G ∘g F⇙ Ο€*β‡˜G ∘g F⇙"
      by (simp add: comp_galcon_def, metis comp_galcon_def galcon.select_convs(1) galcon.select_convs(2) galcon.select_convs(3) gf.lower_iso)
    show "isotone π’΄β‡˜G ∘g F⇙ π’³β‡˜G ∘g F⇙ Ο€*β‡˜G ∘g F⇙"
      by (simp add: gf.upper_iso)
    fix x
    assume "x ∈ carrier π’³β‡˜G ∘g F⇙"
    thus "Ο€*β‡˜G ∘g F⇙ (Ο€*β‡˜G ∘g F⇙ x) = x"
      using assms(3) f.lower_closed f.lower_inv_eq g.lower_inv_eq by (auto simp add: comp_galcon_def)
  next
    fix y
    assume "y ∈ carrier π’΄β‡˜G ∘g F⇙"
    thus "Ο€*β‡˜G ∘g F⇙ (Ο€*β‡˜G ∘g F⇙ y) = y"
      by (simp add: comp_galcon_def assms(3) f.upper_inv_eq g.upper_closed g.upper_inv_eq)
  qed
qed

end