Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: provide proof that set-coequalizers of finite sets are finite? #1004

Open
finegeometer opened this issue May 7, 2023 · 2 comments

Comments

@finegeometer
Copy link

I recently found myself in need of a theorem that any set-coequalizer of finite sets is finite. Would it make sense to add this theorem to the standard library, either in Cubical.Data.FinSet.Constructors or in Cubical.HITs.SetCoequalizer.Properties?

For reference, here's the proof I eventually managed to construct. This code would need some work to bring it up to the standards of this library, but it at least demonstrates that the theorem is correct, along with one possible method of proving it. (I'd be curious to know if there's a shorter proof!)

{-# OPTIONS --cubical --safe #-}

-- Theorem: A set-coequalizer of finite sets is finite.
module FinCoeq where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv using (_≃_)
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism using (isoToEquiv; iso)
open import Cubical.Foundations.Structure using (⟨_⟩; str)
open import Cubical.Foundations.Function using (case_of_; case_return_of_)
open import Cubical.Data.FinSet as FinSet using (FinSet; isFinSet)
open import Cubical.Data.FinSet.Induction
open import Cubical.Data.FinSet.Constructors
open import Cubical.Relation.Nullary

import Cubical.Data.Empty as ⊥
import Cubical.Data.Unit as ⊤
import Cubical.Data.Sum as ⊎
import Cubical.Data.Sigma as Σ
import Cubical.HITs.SetCoequalizer as SetCoeq

abstract
  private
    -- The coequalizer of the unique functions `0 → X` is X.
    Coeq0 :  (X : FinSet ℓ-zero) (f g : ⟨ 𝟘 {ℓ-zero} ⟩  ⟨ X ⟩)  ⟨ X ⟩ ≃ SetCoeq.SetCoequalizer f g
    Coeq0 X f g = isoToEquiv
      (iso
        SetCoeq.inc
        (SetCoeq.rec (FinSet.isFinSet→isSet (str X)) (λ x  x) λ ())
        (SetCoeq.elimProp (λ _  SetCoeq.squash _ _) (λ _  refl))
        (λ b  refl))

    -- I define an eliminator into sets for set-coequalizers, since it seems to be missing from the standard library.
    module _ {A B : Type} {f g : A  B} {C : SetCoeq.SetCoequalizer f g  Type}
      (Cset :  s  isSet (C s))
      (h : (b : B)  C (SetCoeq.inc b))
      (hcoeqs : (a : A)  PathP (λ i  C (SetCoeq.coeq a i)) (h (f a)) (h (g a))) where

      CoeqElim : (s : SetCoeq.SetCoequalizer f g)  C s
      CoeqElim (SetCoeq.inc x) = h x
      CoeqElim (SetCoeq.coeq a i) = hcoeqs a i
      CoeqElim (SetCoeq.squash s t p q i j) =
        isOfHLevel→isOfHLevelDep 2 Cset (CoeqElim s) (CoeqElim t) (cong CoeqElim p) (cong CoeqElim q) (SetCoeq.squash s t p q) i j

    module Merge (X : FinSet ℓ-zero) {left right : ⟨ X ⟩} (l≠r : ¬ (left ≡ right)) where
      Merge : FinSet ℓ-zero
      Merge = _ , isFinSetΣ X (λ x  _ , (isFinSet¬ (_ , isFinSet≡ X x left)))

      testFn : (x : ⟨ X ⟩)  Dec (x ≡ left)
      testFn x = FinSet.isFinSet→Discrete (str X) x left

      testFn-left : testFn left ≡ yes refl
      testFn-left = case testFn left return _≡ yes refl of λ
        { (yes eq)  cong yes (FinSet.isFinSet→isSet (str X) left left eq refl)
        ; (no pf)  ⊥.rec (pf refl)
        }

      testFn-notLeft :  {x} (pf : ¬ (x ≡ left))  testFn x ≡ no pf
      testFn-notLeft {x} pf = case testFn x return _≡ no pf of λ
        { (yes eq)  ⊥.rec (pf eq)
        ; (no pf')  cong no (isProp¬ (x ≡ left) pf' pf)
        }

      handler : {x : ⟨ X ⟩}  Dec (x ≡ left)  ⟨ Merge ⟩
      handler {x} d = 
        case d of λ
          { (yes _)  right , λ eq  l≠r (sym eq)
          ; (no pf)  x , pf
          }

      toMerge : ⟨ X ⟩  ⟨ Merge ⟩
      toMerge x = handler (testFn x)

      toMerge-left : fst (toMerge left) ≡ right
      toMerge-left = subst (λ d  fst (handler d) ≡ right) (sym testFn-left) refl

      toMerge-notLeft :  {x}  ¬ (x ≡ left)  fst (toMerge x) ≡ x
      toMerge-notLeft {x} neq = subst (λ d  fst (handler d) ≡ x) (sym (testFn-notLeft neq)) refl

      toMerge-merge : toMerge left ≡ toMerge right
      toMerge-merge = Σ.Σ≡Prop (λ x  isProp¬ (x ≡ left)) (toMerge-left ∙ sym (toMerge-notLeft (λ eq  l≠r (sym eq))))

  -- The main theorem.
  isFinSetCoeq :  {A B : FinSet ℓ-zero} (f g : ⟨ A ⟩  ⟨ B ⟩)  isFinSet (SetCoeq.SetCoequalizer f g)
  isFinSetCoeq {A} {B} = elimProp𝟙+
    (λ A   (B : FinSet ℓ-zero) (f g : ⟨ A ⟩  ⟨ B ⟩)  isFinSet (SetCoeq.SetCoequalizer f g))
    (λ _  isPropΠ3 λ _ _ _  FinSet.isPropIsFinSet)
    (λ B f g  FinSet.EquivPresIsFinSet (Coeq0 B f g) (str B))
    (λ {A} IH B f g 
      let
        Previous : FinSet ℓ-zero
        Previous = _ , IH B (λ a  f (⊎.inr a)) (λ a  g (⊎.inr a))

        redundant : Dec (Path ⟨ Previous ⟩ (SetCoeq.inc (f (⊎.inl ⊤.tt*))) (SetCoeq.inc (g (⊎.inl ⊤.tt*))))
        redundant = FinSet.isFinSet→Discrete (str Previous) _ _
      in case redundant of λ
        { (yes pf) 
          let
            fwd : ⟨ Previous ⟩  SetCoeq.SetCoequalizer f g
            fwd = SetCoeq.rec SetCoeq.squash SetCoeq.inc (λ a  SetCoeq.coeq (⊎.inr a))

            bwd : SetCoeq.SetCoequalizer f g  ⟨ Previous ⟩
            bwd = SetCoeq.rec SetCoeq.squash SetCoeq.inc (λ {(⊎.inl tt*)  pf; (⊎.inr a)  SetCoeq.coeq a})

            fwd-bwd :  x  fwd (bwd x) ≡ x
            fwd-bwd = CoeqElim (λ _  isSet→isGroupoid SetCoeq.squash _ _) (λ _  refl)
              (λ _  toPathP (SetCoeq.squash _ _ _ _))

            bwd-fwd :  x  bwd (fwd x) ≡ x
            bwd-fwd = CoeqElim (λ _  isSet→isGroupoid SetCoeq.squash _ _) (λ _  refl)
              (λ _  toPathP (SetCoeq.squash _ _ _ _))

            same-as-previous : ⟨ Previous ⟩ ≃ SetCoeq.SetCoequalizer f g
            same-as-previous = isoToEquiv (iso fwd bwd fwd-bwd bwd-fwd)
          in FinSet.EquivPresIsFinSet same-as-previous (str Previous)
        ; (no pf) 
          let
            open Merge Previous pf

            weaken-coequalizer :  b₁ b₂ 
              Path (SetCoeq.SetCoequalizer (λ a  f (⊎.inr a)) (λ a  g (⊎.inr a)))
                (SetCoeq.inc b₁) (SetCoeq.inc b₂) 
              Path (SetCoeq.SetCoequalizer f g)
                (SetCoeq.inc b₁) (SetCoeq.inc b₂)
            weaken-coequalizer b₁ b₂ eq i = CoeqElim (λ _  SetCoeq.squash) SetCoeq.inc (λ a  SetCoeq.coeq (⊎.inr a)) (eq i)

            fwd : ⟨ Merge ⟩  SetCoeq.SetCoequalizer f g
            fwd = λ (x , _)  SetCoeq.rec SetCoeq.squash SetCoeq.inc (λ a  SetCoeq.coeq (⊎.inr a)) x

            bwd : SetCoeq.SetCoequalizer f g  ⟨ Merge ⟩
            bwd = SetCoeq.rec
              (FinSet.isFinSet→isSet (str Merge))
              (λ b  toMerge (SetCoeq.inc b))
              λ {(⊎.inl tt*)  toMerge-merge; (⊎.inr a)  cong toMerge (SetCoeq.coeq a)}

            fwd-bwd :  x  fwd (bwd x) ≡ x
            fwd-bwd = CoeqElim
              (λ _  isSet→isGroupoid SetCoeq.squash _ _)
              (λ b  case (testFn (SetCoeq.inc b)) return (λ d  fwd (handler d) ≡ SetCoeq.inc b) of λ
                { (yes eq) 
                  sym (weaken-coequalizer b (f (⊎.inl ⊤.tt*)) eq ∙ SetCoeq.coeq (⊎.inl ⊤.tt*))
                ; (no neq)  refl
                })
              (λ _  toPathP (SetCoeq.squash _ _ _ _))

            bwd-fwd-help :  x  (notLeft : ¬ (x ≡ SetCoeq.inc (f (⊎.inl ⊤.tt*))))  fst (bwd (fwd (x , notLeft))) ≡ x
            bwd-fwd-help = CoeqElim
              (λ _  isSet→ (isSet→isGroupoid SetCoeq.squash _ _))
              (λ b notLeft  toMerge-notLeft notLeft)
              (λ a i  toMerge-notLeft)

            bwd-fwd :  x  bwd (fwd x) ≡ x
            bwd-fwd = λ (x , notLeft) 
              Σ.Σ≡Prop
                (λ x  isProp¬ (x ≡ SetCoeq.inc (f (⊎.inl ⊤.tt*))))
                (bwd-fwd-help x notLeft)

            same-as-new : ⟨ Merge ⟩ ≃ SetCoeq.SetCoequalizer f g
            same-as-new = isoToEquiv (iso fwd bwd fwd-bwd bwd-fwd)
          in FinSet.EquivPresIsFinSet same-as-new (str Merge)
        })
    A B
@finegeometer
Copy link
Author

Something just occurred to me: If you want the theorem to compute efficiently, it should probably be reimplemented using the union-find algorithm. Computational behavior wasn't a concern for my application, but it might be for some.

@felixwellen
Copy link
Collaborator

Also without that, I think that statement is a good addition to the library. Please make a (draft) pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants