Theory Birkhoff_Finite_Distributive_Lattices
theory Birkhoff_Finite_Distributive_Lattices
  imports
    "HOL-Library.Finite_Lattice"
    "HOL.Transcendental"
begin
unbundle lattice_syntax
text ‹ The proof of Birkhoff's representation theorem for finite
       distributive lattices @{cite birkhoffRingsSets1937} presented
       here follows Davey and Priestley @{cite daveyChapterRepresentationFinite2002}. ›
section ‹ Atoms, Join Primes and Join Irreducibles \label{sec:join-irreducibles} ›
text ‹ Atomic elements are defined as follows. ›
definition (in bounded_lattice_bot) atomic :: "'a ⇒ bool" where
  "atomic x ≡ x ≠ ⊥ ∧ (∀ y. y ≤ x ⟶ y = ⊥ ∨ y = x)"
text ‹ Two related concepts are ∗‹join-prime› elements and ∗‹join-irreducible› 
       elements. ›
definition (in bounded_lattice_bot) join_prime :: "'a ⇒ bool" where
  "join_prime x ≡ x ≠ ⊥ ∧ (∀ y z . x ≤ y ⊔ z ⟶ x ≤ y ∨ x ≤ z)"
definition (in bounded_lattice_bot) join_irreducible :: "'a ⇒ bool" where
  "join_irreducible x ≡ x ≠ ⊥ ∧ (∀ y z . y < x ⟶ z < x ⟶ y ⊔ z < x)"
lemma (in bounded_lattice_bot) join_irreducible_def':
  "join_irreducible x = (x ≠ ⊥ ∧ (∀ y z . x = y ⊔ z ⟶ x = y ∨ x = z))"
  unfolding join_irreducible_def
  by (metis 
        nless_le
        sup.bounded_iff
        sup.cobounded1
        sup_ge2)
text ‹ Every join-prime is also join-irreducible. ›
lemma (in bounded_lattice_bot) join_prime_implies_join_irreducible:
  assumes "join_prime x"
  shows "join_irreducible x"
  using assms
  unfolding 
    join_irreducible_def' 
    join_prime_def
  by (simp add: dual_order.eq_iff)
text ‹ In the special case when the underlying lattice is
       distributive, the join-prime elements and join-irreducible
       elements coincide. ›
class bounded_distrib_lattice_bot = bounded_lattice_bot +
  assumes sup_inf_distrib1: "x ⊔ (y ⊓ z) = (x ⊔ y) ⊓ (x ⊔ z)"
begin
subclass distrib_lattice
  by (unfold_locales, metis (full_types) sup_inf_distrib1)
end
context complete_distrib_lattice
begin
subclass bounded_distrib_lattice_bot
  by (unfold_locales, 
      metis (full_types) 
        sup_inf_distrib1)
end
lemma (in bounded_distrib_lattice_bot) join_irreducible_is_join_prime:
  "join_irreducible x = join_prime x"
proof
  assume "join_prime x"
  thus "join_irreducible x"
    by (simp add: join_prime_implies_join_irreducible)
next
  assume "join_irreducible x"
  {
    fix y z
    assume "x ≤ y ⊔ z"
    hence "x = x ⊓ (y ⊔ z)"
      by (metis local.inf.orderE)
    hence "x = (x ⊓ y) ⊔ (x ⊓ z)"
      using inf_sup_distrib1 by auto
    hence "(x = x ⊓ y) ∨ (x = x ⊓ z)"
      using ‹join_irreducible x›
      unfolding join_irreducible_def'
      by metis
    hence "(x ≤ y) ∨ (x ≤ z)"
      by (metis (full_types) local.inf.cobounded2)
  }
  thus "join_prime x"
    by (metis 
          ‹join_irreducible x› 
          join_irreducible_def' 
          join_prime_def)
qed
text ‹ Every atomic element is join-irreducible. ›
lemma (in bounded_lattice_bot) atomic_implies_join_prime:
  assumes "atomic x"
  shows "join_irreducible x"
  using assms
  unfolding 
    atomic_def 
    join_irreducible_def'
  by (metis (no_types, opaque_lifting) 
        sup.cobounded2 
        sup_bot.right_neutral)   
text ‹ In the case of Boolean algebras, atomic elements and
       join-prime elements are one-in-the-same. ›
lemma (in boolean_algebra) join_prime_is_atomic:
  "atomic x = join_prime x"
proof
  assume "atomic x"
  {
    fix y z
    assume "x ≤ y ⊔ z"
    hence "x = (x ⊓ y) ⊔ (x ⊓ z)"
      using inf.absorb1 inf_sup_distrib1 by fastforce
    moreover
    have "x ≤ y ∨ (x ⊓ y) = ⊥"
         "x ≤ z ∨ (x ⊓ z) = ⊥"
      using ‹atomic x› inf.cobounded1 inf.cobounded2
      unfolding atomic_def
      by fastforce+
    ultimately have "x ≤ y ∨ x ≤ z"
      using ‹atomic x› atomic_def by auto
  }
  thus "join_prime x"
    using ‹atomic x› join_prime_def atomic_def
    by auto
next
  assume "join_prime x"
  {
    fix y
    assume "y ≤ x" "y ≠ x"
    hence "x = x ⊔ y"
      using sup.orderE by blast
    also have "… = (x ⊔ y) ⊓ (y ⊔ -y)"
      by simp
    finally have "x = (x ⊓ -y) ⊔ y"
      by (simp add: sup_inf_distrib2)
    hence "x ≤ -y"
      using 
        ‹join_prime x›
        ‹y ≠ x› 
        ‹y ≤ x›
        antisym_conv
        inf_le2
        sup_neg_inf 
      unfolding join_prime_def
      by blast
    hence "y ≤ y ⊓ -y"
      by (metis
            ‹x = x ⊔ y›
            inf.orderE
            inf_compl_bot_right
            inf_sup_absorb
            order_refl
            sup.commute)
    hence "y = ⊥"
      using sup_absorb2 by fastforce
  }
  thus "atomic x" 
    using ‹join_prime x›
    unfolding 
      atomic_def
      join_prime_def 
    by auto
qed
text ‹ All atomic elements are disjoint. ›
lemma (in bounded_lattice_bot) atomic_disjoint:
  assumes "atomic α"
      and "atomic β"
    shows "(α = β) ⟷ (α ⊓ β ≠ ⊥)"
proof
  assume "α = β"
  hence "α ⊓ β = α"
    by simp
  thus "α ⊓ β ≠ ⊥"
    using ‹atomic α›
    unfolding atomic_def
    by auto
next
  assume "α ⊓ β ≠ ⊥"
  hence "β ≤ α ∧ α ≤ β"
    by (metis 
          assms 
          atomic_def 
          inf_absorb2 
          inf_le1 
          inf_le2)
  thus "α = β" by auto
qed
definition (in bounded_lattice_bot) atomic_elements (‹𝒜›) where
  "𝒜 ≡ {a . atomic a}"
definition (in bounded_lattice_bot) join_irreducible_elements (‹𝒥›) where
  "𝒥 ≡ {a . join_irreducible a}"
section ‹ Birkhoff's Representation Theorem For Finite Distributive Lattices \label{section:birkhoffs-theorem} ›
text ‹ Birkhoff's representation theorem for finite distributive
       lattices follows from the fact that every non-‹⊥› element
       can be represented by the join-irreducible elements beneath it. ›
text ‹ In this section we merely demonstrate the representation aspect of
       Birkhoff's theorem. In \S\ref{section:isomorphism} we show this 
       representation is a lattice homomorphism. ›
text ‹ The fist step to representing elements is to show that there ∗‹exist›
       join-irreducible elements beneath them. This is done by showing if there is 
       no join-irreducible element, we can make a descending chain with more elements 
       than the finite Boolean algebra under consideration. ›
fun (in order) descending_chain_list :: "'a list ⇒ bool" where
  "descending_chain_list [] = True"
| "descending_chain_list [x] = True"
| "descending_chain_list (x # x' # xs)
     = (x < x' ∧ descending_chain_list (x' # xs))"
lemma (in order) descending_chain_list_tail:
  assumes "descending_chain_list (s # S)"
  shows "descending_chain_list S"
  using assms
  by (induct S, auto)
lemma (in order) descending_chain_list_drop_penultimate:
  assumes "descending_chain_list (s # s' # S)"
  shows "descending_chain_list (s # S)"
  using assms
  by (induct S, simp, auto)
lemma (in order) descending_chain_list_less_than_others:
  assumes "descending_chain_list (s # S)"
  shows   "∀s' ∈ set S. s < s'"
  using assms
  by (induct S, 
        auto, 
        simp add: descending_chain_list_drop_penultimate)
lemma (in order) descending_chain_list_distinct:
  assumes "descending_chain_list S"
  shows "distinct S"
  using assms
  by (induct S,
      simp,
      meson
        descending_chain_list_less_than_others
        descending_chain_list_tail
        distinct.simps(2)
        less_irrefl)
lemma (in finite_distrib_lattice) join_irreducible_lower_bound_exists:
  assumes "¬ (x ≤ y)"
  shows "∃ z ∈ 𝒥. z ≤ x ∧ ¬ (z ≤ y)"
proof (rule ccontr)
  assume ⋆: "¬ (∃ z ∈ 𝒥. z ≤ x ∧ ¬ (z ≤ y))"
  {
    fix z :: 'a
    assume 
      "z ≤ x"
      "¬ (z ≤ y)"
    with ⋆ obtain p q where
        "p < z"
        "q < z"
        "p ⊔ q = z"
      by (metis (full_types) 
            bot_least
            dual_order.not_eq_order_implies_strict
            join_irreducible_def'
            join_irreducible_elements_def
            sup_ge1
            sup_ge2 
            mem_Collect_eq)
    hence "¬ (p ≤ y) ∨ ¬ (q ≤ y)"
      by (metis (full_types) ‹¬ z ≤ y› sup_least)
    hence "∃ p < z. ¬ (p ≤ y)"
      by (metis ‹p < z› ‹q < z›)
  }
  note fresh = this
  {
    fix n :: nat
    have "∃ S . descending_chain_list S
                  ∧ length S = n
                  ∧ (∀s ∈ set S. s ≤ x ∧ ¬ (s ≤ y))"
    proof (induct n)
      case 0
      then show ?case by simp
    next
      case (Suc n)
      then show ?case proof (cases "n = 0")
        case True
        hence "descending_chain_list [x]
                 ∧ length [x] = Suc n
                 ∧ (∀s ∈ set [x]. s ≤ x ∧ ¬ (s ≤ y))"
          by (metis 
                Suc 
                assms 
                length_0_conv 
                length_Suc_conv 
                descending_chain_list.simps(2)
                le_less set_ConsD)
        then show ?thesis
          by blast
      next
        case False
        from this obtain s S where
            "descending_chain_list (s # S)"
            "length (s # S) = n"
            "∀s ∈ set (s # S). s ≤ x ∧ ¬ (s ≤ y)"
          using 
            Suc.hyps 
            length_0_conv 
            descending_chain_list.elims(2)
          by metis
        note A = this
        hence "s ≤ x" "¬ (s ≤ y)" by auto
        obtain s' :: 'a where
          "s' < s"
          "¬ (s' ≤ y)"
          using 
            fresh [OF ‹s ≤ x› ‹¬ (s ≤ y)›]
          by auto
        note B = this
        let ?S' = "s' # s # S"
        from A and B have
          "descending_chain_list ?S'"
          "length ?S' = Suc n"
          "∀s ∈ set ?S'. s ≤ x ∧ ¬ (s ≤ y)"
            by auto
        then show ?thesis by blast
      qed
    qed
  }
  from this obtain S :: "'a list" where
    "descending_chain_list S"
    "length S = 1 + (card (UNIV::'a set))"
    by auto
  hence "card (set S) = 1 + (card (UNIV::'a set))"
    using descending_chain_list_distinct
          distinct_card
    by fastforce
  hence "¬ card (set S) ≤ card (UNIV::'a set)"
    by presburger
  thus "False"
    using card_mono finite_UNIV by blast
qed
definition (in bounded_lattice_bot)
  join_irreducibles_embedding :: "'a ⇒ 'a set" (‹⦃ _ ⦄› [50]) where
  "⦃ x ⦄ ≡ {a ∈ 𝒥. a ≤ x}"
text ‹ We can now show every element is exactly the suprema of the 
       join-irreducible elements beneath them in any distributive lattice. ›
theorem (in finite_distrib_lattice) sup_join_prime_embedding_ident:
   "x = ⨆ ⦃ x ⦄"
proof -
  have "∀ a ∈ ⦃ x ⦄. a ≤ x"
    by (metis (no_types, lifting) 
          join_irreducibles_embedding_def 
          mem_Collect_eq)
  hence "⨆ ⦃ x ⦄ ≤ x"
    by (simp add: Sup_least)
  moreover
  {
    fix y :: 'a
    assume "⨆ ⦃ x ⦄ ≤ y"
    have "x ≤ y"
    proof (rule ccontr)
      assume "¬ x ≤ y"
      from this obtain a where
          "a ∈ 𝒥"
          "a ≤ x"
          "¬ a ≤ y"
        using join_irreducible_lower_bound_exists [OF ‹¬ x ≤ y›]
        by metis
      hence "a ∈ ⦃ x ⦄"
        by (metis (no_types, lifting) 
              join_irreducibles_embedding_def 
              mem_Collect_eq)
      hence "a ≤ y"
        using ‹⨆⦃ x ⦄ ≤ y›
              Sup_upper
              order.trans
        by blast
      thus "False"
        by (metis (full_types) ‹¬ a ≤ y›)
    qed
  }
  ultimately show ?thesis
    using antisym_conv by blast
qed
text ‹ Just as ‹x = ⨆ ⦃ x ⦄›, the reverse is also true; ‹λ x. ⦃ x ⦄›
       and ‹λ S. ⨆ S› are inverses where ‹S ∈ 𝒪𝒥›, the set of downsets
       in ‹Pow 𝒥›. ›
definition (in bounded_lattice_bot) down_irreducibles (‹𝒪𝒥›) where
  "𝒪𝒥 ≡ { S ∈ Pow 𝒥 . (∃ x . S = ⦃ x ⦄) }"
lemma (in finite_distrib_lattice) join_irreducible_embedding_sup_ident:
  assumes "S ∈ 𝒪𝒥"
  shows "S = ⦃ ⨆ S ⦄"
proof -
  obtain x where
      "S = ⦃ x ⦄"
    using 
      ‹S ∈ 𝒪𝒥›
    unfolding 
      down_irreducibles_def
    by auto
  with ‹S ∈ 𝒪𝒥› have "∀ s ∈ S. s ∈ 𝒥 ∧ s ≤ ⨆ S"
    unfolding 
      down_irreducibles_def
      Pow_def
    using Sup_upper
    by fastforce
  hence "S ⊆ ⦃ ⨆ S ⦄"
    unfolding join_irreducibles_embedding_def
    by blast
  moreover
  {
    fix y
    assume
      "y ∈ 𝒥"
      "y ≤ ⨆ S"
    have "finite S" by auto
    from ‹finite S› and ‹y ≤ ⨆ S› have "∃ s ∈ S. y ≤ s"
    proof (induct S rule: finite_induct)
      case empty
      hence "y ≤ ⊥"
        by (metis Sup_empty)
      then show ?case
        using
          ‹y ∈ 𝒥›
        unfolding 
          join_irreducible_elements_def
          join_irreducible_def
        by (metis (mono_tags, lifting) 
              le_bot 
              mem_Collect_eq)
    next
      case (insert s S)
      hence "y ≤ s ∨ y ≤ ⨆ S"
        using
          ‹y ∈ 𝒥›
        unfolding 
          join_irreducible_elements_def
          join_irreducible_is_join_prime
          join_prime_def
        by auto
      then show ?case
        by (metis (full_types) 
              insert.hyps(3) 
              insertCI)
    qed
    hence "y ≤ x"
      by (metis (no_types, lifting) 
            ‹S = ⦃ x ⦄›
            join_irreducibles_embedding_def
            order_trans 
            mem_Collect_eq)
    hence "y ∈ S"
      by (metis (no_types, lifting) 
            ‹S = ⦃ x ⦄› 
            ‹y ∈ 𝒥› 
            join_irreducibles_embedding_def 
            mem_Collect_eq)
  }
  hence "⦃ ⨆ S ⦄ ⊆ S"
    unfolding 
      join_irreducibles_embedding_def
    by blast
  ultimately show ?thesis by auto
qed
text ‹ Given that ‹λ x. ⦃ x ⦄› has a left and right inverse, we can show
       it is a ∗‹bijection›. ›
text ‹ The bijection below is recognizable as a form of ∗‹Birkhoff's Representation Theorem› 
       for finite distributive lattices. ›
theorem (in finite_distrib_lattice) birkhoffs_theorem:
  "bij_betw (λ x. ⦃ x ⦄) UNIV 𝒪𝒥"
  unfolding bij_betw_def
proof
  {
    fix x y
    assume "⦃ x ⦄ = ⦃ y ⦄"
    hence "⨆ ⦃ x ⦄ = ⨆ ⦃ y ⦄"
      by simp
    hence "x = y"
      using sup_join_prime_embedding_ident
      by auto
  }
  thus "inj (λ x. ⦃ x ⦄)"
    unfolding inj_def
    by auto
next
  show "range (λ x. ⦃ x ⦄) = 𝒪𝒥"
    unfolding 
      down_irreducibles_def
      join_irreducibles_embedding_def
    by auto
qed
section ‹ Finite Ditributive Lattice Isomorphism \label{section:isomorphism} ›
text ‹ The form of Birkhoff's theorem presented in \S\ref{section:birkhoffs-theorem}
       simply gave a bijection between a finite distributive lattice and the
       downsets of its join-irreducible elements. This relationship can be
       extended to a full-blown ∗‹lattice homomorphism›. In particular
       we have the following properties:
       ▪ ‹⊥› and ‹⊤› are preserved; specifically ‹⦃ ⊥ ⦄ = {}› and
         ‹⦃ ⊤ ⦄ = 𝒥›.
       ▪ Order is preserved: ‹x ≤ y = (⦃ x ⦄ ⊆ ⦃ y ⦄)›.
       ▪ ‹λ x . ⦃ x ⦄› is a lower complete semi-lattice homomorphism, mapping
         ‹⦃ ⨆ X ⦄ = (⋃ x ∈ X . ⦃ x ⦄)›.
       ▪ In addition to preserving arbitrary joins, ‹λ x . ⦃ x ⦄› is a
         lattice homomorphism, since it also preserves finitary meets with
         ‹ ⦃ x ⊓ y ⦄ = ⦃ x ⦄ ∩ ⦃ y ⦄ ›. Arbitrary meets are also preserved, 
         but relative to a top element ‹𝒥›, or in other words 
         ‹ ⦃ ⨅ X ⦄ = 𝒥 ∩ (⋂ x ∈ X. ⦃ x ⦄) ›.
       ▪ In the case of a Boolean algebra, complementation corresponds to 
         relative set complementation via ‹⦃ - x ⦄ = 𝒥 - ⦃ x ⦄›.
›
lemma (in finite_distrib_lattice) join_irreducibles_bot:
  "⦃ ⊥ ⦄ = {}"
  unfolding
    join_irreducibles_embedding_def
    join_irreducible_elements_def
    join_irreducible_is_join_prime
    join_prime_def
  by (simp add: bot_unique)
lemma (in finite_distrib_lattice) join_irreducibles_top:
  "⦃ ⊤ ⦄ = 𝒥"
  unfolding
    join_irreducibles_embedding_def
    join_irreducible_elements_def
    join_irreducible_is_join_prime
    join_prime_def
  by auto
lemma (in finite_distrib_lattice) join_irreducibles_order_isomorphism:
  "x ≤ y = (⦃ x ⦄ ⊆ ⦃ y ⦄)"
  by (rule iffI, 
        metis (mono_tags, lifting) 
          join_irreducibles_embedding_def 
          order_trans 
          mem_Collect_eq 
          subsetI,
        metis (full_types) 
          Sup_subset_mono 
          sup_join_prime_embedding_ident)
lemma (in finite_distrib_lattice) join_irreducibles_join_homomorphism:
  "⦃ x ⊔ y ⦄ = ⦃ x ⦄ ∪ ⦃ y ⦄"
proof
  show "⦃ x ⊔ y ⦄ ⊆ ⦃ x ⦄ ∪ ⦃ y ⦄"
    unfolding
      join_irreducibles_embedding_def
      join_irreducible_elements_def
      join_irreducible_is_join_prime
      join_prime_def
    by blast
next
  show "⦃ x ⦄ ∪ ⦃ y ⦄ ⊆ ⦃ x ⊔ y ⦄"
    unfolding
      join_irreducibles_embedding_def
      join_irreducible_elements_def
      join_irreducible_is_join_prime
      join_prime_def
    using
      le_supI1
      sup.absorb_iff1
      sup.assoc
    by force
qed
lemma (in finite_distrib_lattice) join_irreducibles_sup_homomorphism:
  "⦃ ⨆ X ⦄ = (⋃ x ∈ X . ⦃ x ⦄)"
proof -
  have "finite X"
    by simp
  thus ?thesis
  proof (induct X rule: finite_induct)
    case empty
    then show ?case by (simp add: join_irreducibles_bot)
  next
    case (insert x X)
    then show ?case by (simp add: join_irreducibles_join_homomorphism)
  qed
qed
lemma (in finite_distrib_lattice) join_irreducibles_meet_homomorphism:
  "⦃ x ⊓ y ⦄ = ⦃ x ⦄ ∩ ⦃ y ⦄"
  unfolding
    join_irreducibles_embedding_def
  by auto
text ‹ Arbitrary meets are also preserved, but relative to a top element ‹𝒥›. ›
lemma (in finite_distrib_lattice) join_irreducibles_inf_homomorphism:
  "⦃ ⨅ X ⦄ = 𝒥 ∩ (⋂ x ∈ X. ⦃ x ⦄)"
proof -
  have "finite X"
    by simp
  thus ?thesis
  proof (induct X rule: finite_induct)
    case empty
    then show ?case by (simp add: join_irreducibles_top)
  next
    case (insert x X)
    then show ?case by (simp add: join_irreducibles_meet_homomorphism, blast)
  qed
qed
text ‹ Finally, we show that complementation is preserved. ›
text ‹ To begin, we define the class of finite Boolean algebras.
       This class is simply an extension of @{class boolean_algebra},
       extended with \<^term>‹finite UNIV› as per the axiom class @{class finite}. We also
       also extend the language of the class with ∗‹infima› and ∗‹suprema› 
       (i.e. ‹⨅ A› and ‹⨆ A› respectively). ›
class finite_boolean_algebra = boolean_algebra + finite + Inf + Sup +
  assumes Inf_def: "⨅ A = Finite_Set.fold (⊓) ⊤ A"
  assumes Sup_def: "⨆ A = Finite_Set.fold (⊔) ⊥ A"
begin
text ‹ Finite Boolean algebras are trivially a subclass of finite
       distributive lattices, which are necessarily ∗‹complete›. ›
subclass finite_distrib_lattice_complete
  using
    Inf_fin.coboundedI
    Sup_fin.coboundedI
    finite_UNIV
    le_bot
    top_unique
    Inf_def
    Sup_def
  by (unfold_locales, blast, fastforce+)
subclass bounded_distrib_lattice_bot
  by (unfold_locales, metis sup_inf_distrib1)
end
lemma (in finite_boolean_algebra) join_irreducibles_complement_homomorphism:
  "⦃ - x ⦄ = 𝒥 - ⦃ x ⦄"
proof
  show "⦃ - x ⦄ ⊆ 𝒥 - ⦃ x ⦄"
  proof
    fix j
    assume "j ∈ ⦃ - x ⦄"
    hence "j ∉ ⦃ x ⦄"
      unfolding
        join_irreducibles_embedding_def
        join_irreducible_elements_def
        join_irreducible_is_join_prime
        join_prime_def
      by (metis
            (mono_tags, lifting)
            CollectD
            bot_unique
            inf.boundedI
            inf_compl_bot)
    thus "j ∈ 𝒥 - ⦃ x ⦄"
      using ‹j ∈ ⦃ - x ⦄›
      unfolding
        join_irreducibles_embedding_def
      by blast
  qed
next
  show "𝒥 - ⦃ x ⦄ ⊆ ⦃ - x ⦄"
  proof
    fix j
    assume "j ∈ 𝒥 - ⦃ x ⦄"
    hence "j ∈ 𝒥" and "¬ j ≤ x"
      unfolding join_irreducibles_embedding_def
      by blast+
    moreover have "j ≤ x ⊔ -x"
      by auto
    ultimately have "j ≤ -x"
      unfolding
        join_irreducible_elements_def
        join_irreducible_is_join_prime
        join_prime_def
      by blast
    thus "j ∈ ⦃ - x ⦄"
      unfolding join_irreducibles_embedding_def
      using ‹j ∈ 𝒥›
      by auto
  qed
qed
section ‹ Cardinality ›
text ‹ Another consequence of Birkhoff's theorem from \S\ref{section:birkhoffs-theorem}
       is that every finite Boolean algebra has a cardinality which is 
       a power of two. This gives a bound on the number of 
       atoms/join-prime/irreducible elements, which must be logarithmic in 
       the size of the finite Boolean algebra they belong to. ›
text ‹ We first show that ‹𝒪𝒥›, the downsets of the join-irreducible elements 
       ‹𝒥›, are the same as the powerset of ‹𝒥› in any finite Boolean algebra. ›
lemma (in finite_boolean_algebra) 𝒪𝒥_is_Pow_𝒥:
  "𝒪𝒥 = Pow 𝒥"
proof
  show "𝒪𝒥 ⊆ Pow 𝒥"
    unfolding down_irreducibles_def
    by auto
next
  show "Pow 𝒥 ⊆ 𝒪𝒥"
  proof (rule ccontr)
    assume "¬ Pow 𝒥 ⊆ 𝒪𝒥"
    from this obtain S where
        "S ⊆ 𝒥"
        "∀ x. S ≠ {a ∈ 𝒥. a ≤ x}"
      unfolding 
        down_irreducibles_def
        join_irreducibles_embedding_def
      by auto
    hence "S ≠ {a ∈ 𝒥. a ≤ ⨆ S}"
      by auto
    moreover 
    have "∀ s ∈ S . s ∈ 𝒥 ∧ s ≤ ⨆ S"
      by (metis (no_types, lifting) 
            ‹S ⊆ 𝒥› 
            Sup_upper subsetD)
    hence "S ⊆ {a ∈ 𝒥. a ≤ ⨆ S}"
      by (metis (mono_tags, lifting) Ball_Collect)
    ultimately have "∃ y ∈ 𝒥 . y ≤ ⨆ S ∧ y ∉ S"
      by (metis (mono_tags, lifting) 
            mem_Collect_eq 
            subsetI 
            subset_antisym)
    moreover
    {
      fix y
      assume 
        "y ∈ 𝒥"
        "y ≤ ⨆ S"
      from 
        finite [of S]
        ‹y ≤ ⨆ S›
        ‹S ⊆ 𝒥›
      have "y ∈ S"
      proof (induct S rule: finite_induct)
        case empty
        hence "y ≤ ⊥"
          by (metis (full_types) local.Sup_empty)
        then show ?case
          using ‹y ∈ 𝒥›
          unfolding 
            join_irreducible_elements_def
            join_irreducible_def
          by (metis (mono_tags, lifting) 
                le_bot 
                mem_Collect_eq)
      next
        case (insert s S)
        hence "y ≤ s ∨ y ≤ ⨆ S"
          using ‹y ∈ 𝒥›
          unfolding 
            join_irreducible_elements_def
            join_irreducible_is_join_prime
            join_prime_def
          by simp
        moreover
        {
          assume "y ≤ s"
          have "atomic s"
            by (metis in_mono 
                  insert.prems(2) 
                  insertCI 
                  join_irreducible_elements_def 
                  join_irreducible_is_join_prime 
                  join_prime_is_atomic 
                  mem_Collect_eq)
          hence "y = s"
            by (metis (no_types, lifting) 
                  ‹y ∈ 𝒥› 
                  ‹y ≤ s› 
                  atomic_def 
                  join_irreducible_def 
                  join_irreducible_elements_def 
                  mem_Collect_eq)
        }
        ultimately show ?case
          by (metis   
                insert.prems(2) 
                insert_iff 
                insert_subset 
                insert(3))
      qed
    }
    ultimately show False by auto
  qed
qed
  
lemma (in finite_boolean_algebra) UNIV_card:
  "card (UNIV::'a set) = card (Pow 𝒥)"
  using 
    bij_betw_same_card [where f="λx. ⦃ x ⦄"]
    birkhoffs_theorem
  unfolding 
    𝒪𝒥_is_Pow_𝒥
  by blast
lemma finite_Pow_card:
  assumes "finite X"
  shows "card (Pow X) = 2 powr (card X)"
  using assms
proof (induct X rule: finite_induct)
  case empty
  then show ?case by fastforce
next
  case (insert x X)
  have "0 ≤ (2 :: real)" by auto
  hence two_powr_one: "(2 :: real) = 2 powr 1" by fastforce
  have "bij_betw (λ x. fst x ∪ snd x) ({{},{x}} × Pow X) (Pow (insert x X))"
    unfolding bij_betw_def
  proof
    {
      fix y z
      assume 
        "y ∈ {{}, {x}} × Pow X"
        "z ∈ {{}, {x}} × Pow X"
        "fst y ∪ snd y = fst z ∪ snd z"
        (is "?Uy = ?Uz")
      hence 
          "x ∉ snd y"
          "x ∉ snd z"
          "fst y = {x} ∨ fst y = {}"
          "fst z = {x} ∨ fst z = {}"
        using insert.hyps(2) by auto
      hence 
          "x ∈ ?Uy ⟷ fst y = {x}"
          "x ∈ ?Uz ⟷ fst z = {x}"
          "x ∉ ?Uy ⟷ fst y = {}"
          "x ∉ ?Uz ⟷ fst z = {}"
          "snd y = ?Uy - {x}"
          "snd z = ?Uz - {x}"
        by auto
      hence 
          "x ∈ ?Uy ⟷ y = ({x}, ?Uy - {x})"
          "x ∈ ?Uz ⟷ z = ({x}, ?Uz - {x})"
          "x ∉ ?Uy ⟷ y = ({}, ?Uy - {x})"
          "x ∉ ?Uz ⟷ z = ({}, ?Uz - {x})"
        by (metis fst_conv prod.collapse)+
      hence "y = z"
        using ‹?Uy = ?Uz›
        by metis
    }
    thus "inj_on (λx. fst x ∪ snd x) ({{}, {x}} × Pow X)"
      unfolding inj_on_def
      by auto
  next
    show "(λx. fst x ∪ snd x) ` ({{}, {x}} × Pow X) = Pow (insert x X)"
    proof (intro equalityI subsetI)
      fix y
      assume "y ∈ (λx. fst x ∪ snd x) ` ({{}, {x}} × Pow X)"
      from this obtain z where
         "z ∈ ({{}, {x}} × Pow X)"
         "y = fst z ∪ snd z"
        by auto
      hence 
          "snd z ⊆ X"
          "fst z ⊆ insert x X"
        using SigmaE by auto
      thus "y ∈ Pow (insert x X)"
        using ‹y = fst z ∪ snd z› by blast
    next
      fix y
      assume "y ∈ Pow (insert x X)"
      let ?z = "(if x ∈ y then {x} else {}, y - {x})"
      have "?z ∈ ({{}, {x}} × Pow X)"
        using ‹y ∈ Pow (insert x X)› by auto
      moreover have "(λx. fst x ∪ snd x) ?z = y"
        by auto
      ultimately show "y ∈ (λx. fst x ∪ snd x) ` ({{}, {x}} × Pow X)"
        by blast
    qed
  qed
  hence "card (Pow (insert x X)) = card ({{},{x}} × Pow X)"
    using bij_betw_same_card by fastforce
  also have "… = 2 * card (Pow X)"
    by (simp add: insert.hyps(1))
  also have "… = 2 * (2 powr (card X))"
    by (simp add: insert.hyps(3))
  also have "… = (2 powr 1) * 2 powr (card X)"
    using two_powr_one
    by fastforce
  also have "… = 2 powr (1 + card X)"
    by (simp add: powr_add)
  also have "… = 2 powr (card (insert x X))"
    by (simp add: insert.hyps(1) insert.hyps(2))
  finally show ?case .
qed
lemma (in finite_boolean_algebra) UNIV_card_powr_2:
  "card (UNIV::'a set) = 2 powr (card 𝒥)"
  using 
    finite [of 𝒥]
    finite_Pow_card [of 𝒥]
    UNIV_card
  by linarith
lemma (in finite_boolean_algebra) join_irreducibles_card_log_2:
  "card 𝒥 = log 2 (card (UNIV :: 'a set))"
proof (cases "card (UNIV :: 'a set) = 1")
  case True
  hence "∃ x :: 'a. UNIV = {x}"
    using card_1_singletonE by blast
  hence "∀ x y :: 'a. x ∈ UNIV ⟶ y ∈ UNIV ⟶ x = y"
    by (metis (mono_tags) singletonD)
  hence "∀ x y :: 'a. x = y"
    by blast
  hence "∀ x. x = ⊥"
    by blast
  hence "𝒥 = {}"
    unfolding 
      join_irreducible_elements_def
      join_irreducible_is_join_prime
      join_prime_def
    by blast
  hence "card 𝒥 = (0 :: real)"
    by simp
  moreover
  have "log 2 (card (UNIV :: 'a set)) = 0"
    by (simp add: True)
  ultimately show ?thesis by auto
next
  case False
  hence "0 < 2 powr (card 𝒥)" "2 powr (card 𝒥) ≠ 1"
    using finite_UNIV_card_ge_0 finite UNIV_card_powr_2
    by (simp, linarith)
  hence "log 2 (2 powr (card 𝒥)) = card 𝒥"
    by simp
  then show ?thesis
    using UNIV_card_powr_2
    by simp
qed
end