Skip to content

Bags (finite multisets) are a ∂-fixpoint

open import Cubical.Data.FinSet as FinSet renaming (FinSet to FinSet*)
open import Cubical.Data.FinSet.Induction as Fin renaming (_+_ to _+ᶠ_) hiding (𝟙)
private
  FinSet = FinSet* ℓ-zero

The Bag container

We define a container Bag representing finite multisets. Its shapes are the (large) h-groupoid of of finite sets, i.e. types merely equivalent to some standard finite type Fin n. Over a finite set X : FinSet, the type of positions is simply the underlying type ⟨ X ⟩ : Type.

Bag : Container (ℓ-suc ℓ-zero) ℓ-zero
Bag .Container.Shape = FinSet
Bag .Container.Pos = ⟨_⟩

Bag is a symmetric container: the type of finite sets is an h-groupoid, and positions are a family of h-sets.

isSymmetricContainer : isTruncatedContainer h-groupoid h-set Bag
isSymmetricContainer .fst = isGroupoidFinSet
isSymmetricContainer .snd X = isFinSet→isSet (str X)

Operations on finite sets

For any finite set X, the type X ∖ x is a decidable subtype, hence a finite set.

isFinSetMinus :  {X : Type}  isFinSet X   x  isFinSet (X  x)
isFinSetMinus {X} is-finset-X x = isFinSetΣ (X , is-finset-X) λ x′  (¬ x  x′) , is-finset-≢ x′
  where
    is-finset-≢ :  x′  isFinSet (x  x′)
    is-finset-≢ x′ = isDecProp→isFinSet (isProp¬ _) (decNot (isFinSet→Discrete is-finset-X x x′))

This extends to an operation on finite sets.

_-ᶠ_ : (X : FinSet)  (x :  X )  FinSet
(X -ᶠ x) .fst =  X   x
(X -ᶠ x) .snd = isFinSetMinus (str X) x

For pointed finite sets, this defines a "predecessor" operation that removes the chosen point.

FinSet• : Type₁
FinSet• = Σ[ X  FinSet ]  X 

pred : FinSet•  FinSet
pred (X , x) = X -ᶠ x

The unit type is obviously finite,

𝟙ᶠ : FinSet

and we can always point a finite set by adding one:

succ : FinSet  FinSet•
succ X .fst = X +ᶠ 𝟙ᶠ
succ X .snd = nothing

Finite sets have decidable equality, hence all their points are isolated.

isIsolatedFin :  {X : FinSet} (x₀ :  X )  isIsolated x₀
isIsolatedFin {X} = Discrete→isIsolated (isFinSet→Discrete (str X))

IsolatedFinEquiv : (X : FinSet)   X  °   X 
IsolatedFinEquiv (X , is-finset) = Discrete→IsolatedEquiv $ isFinSet→Discrete {A = X} is-finset

This is enough to show that together, pred and succ form an isomorphism between finite- and pointed, finite sets:

pred-iso : Iso FinSet• FinSet
pred-iso .Iso.fun = pred
pred-iso .Iso.inv = succ
pred-iso .Iso.rightInv X = equivFun (FinSet≡ _ _) $ ua $ fin-equiv where
  fin-equiv : ( X +ᶠ 𝟙ᶠ   nothing)   X 
  fin-equiv = removeNothingEquiv
pred-iso .Iso.leftInv (X , x₀) = ΣPathP λ where
    .fst  fin-path
    .snd  pt-path
  where
    fin-equiv :  (X -ᶠ x₀) +ᶠ 𝟙ᶠ    X 
    fin-equiv = replace-isolated-equiv x₀ (isIsolatedFin {X = X} x₀)

    fin-path : (X -ᶠ x₀) +ᶠ 𝟙ᶠ  X
    fin-path = equivFun (FinSet≡ _ _) $ ua fin-equiv

    pt-path : PathP  i   fin-path i ) nothing x₀
    pt-path = ua-gluePath fin-equiv $ refl′ x₀

The ∂-fixpoint

The shapes of ∂ Bag are pointed finite sets, hence pred induces an equivalence with the shapes of Bag:

∂-shape-equiv : (Σ[ X  FinSet ] ( X  °))  FinSet
∂-shape-equiv =
  Σ[ X  FinSet ]  X  °
    ≃⟨ Σ-cong-equiv-snd IsolatedFinEquiv 
  FinSet•
    ≃⟨ isoToEquiv pred-iso 
  FinSet
    ≃∎

By definition, _-ᶠ_ is just removal of (isolated) points of a finite set. In particular, this is an equivalence between the positions of Bag and those of ∂ Bag.

∂-pos-equiv : (X : FinSet) (x :  X  °)  ( X  ∖° x)   X -ᶠ (forget-isolated x) 
∂-pos-equiv X x = idEquiv _

Together, these define an equivalence of the containers ∂ Bag and Bag.

∂-Bag-equiv : Equiv ( Bag) Bag
∂-Bag-equiv .Equiv.shape = ∂-shape-equiv
∂-Bag-equiv .Equiv.pos = uncurry ∂-pos-equiv

Fixpoints of ±-closed universes

The argument above does apply to any subuniverse of types specified by a predicate P : Type → hProp, as long as P is closed under addition and removal of single points.

module Universe (P : Type  Type)
  (is-prop-P :  A  isProp (P A))
  (is-P-+1 :  {A : Type}  P A  P (A  𝟙))
  (is-P-∖ :  {A : Type}  P A  (a : A °)  P (A ∖° a))
  where
  U : Type₁
  U = Σ[ X  Type ] P X

  uBag : Container (ℓ-suc ℓ-zero) ℓ-zero
  uBag .Container.Shape = U
  uBag .Container.Pos = ⟨_⟩

  _-ᵁ_ : (X : U)  (x :  X  °)  U
  (X -ᵁ x) .fst =  X  ∖° x
  (X -ᵁ x) .snd = is-P-∖ (str X) x

  _+1 : U  U
  (X +1) .fst =  X   𝟙
  (X +1) .snd = is-P-+1 (str X)

  ∂-uBag-shape-Iso : Iso (Σ[ X  U ] ( X  °)) U
  ∂-uBag-shape-Iso .Iso.fun (X , x) = X -ᵁ x
  ∂-uBag-shape-Iso .Iso.inv X .fst = X +1
  ∂-uBag-shape-Iso .Iso.inv X .snd = nothing°
  ∂-uBag-shape-Iso .Iso.rightInv X = Σ≡Prop is-prop-P $ ua $ removeNothingEquiv
  ∂-uBag-shape-Iso .Iso.leftInv (X , @(x₀ , isolated-x₀)) = ΣPathP (U-path , pt-path) where
    U-equiv : ( X   x₀)  𝟙   X 
    U-equiv = replace-isolated-equiv x₀ isolated-x₀

    U-path : (X -ᵁ ) +1  X
    U-path = Σ≡Prop is-prop-P $ ua U-equiv

    pt-path : PathP  i   U-path i  °) nothing° 
    pt-path = IsolatedPathP {B = ⟨_⟩} {p = U-path} (ua-gluePath U-equiv (refl′ x₀))

  ∂-uBag-shape : (Σ[ X  U ] ( X  °))  U
  ∂-uBag-shape = isoToEquiv ∂-uBag-shape-Iso

  ∂-uBag : Equiv ( uBag) uBag
  ∂-uBag .Equiv.shape = ∂-uBag-shape
  ∂-uBag .Equiv.pos (X , x) = idEquiv  X -ᵁ x 

Sub-countable types

An example of the above are sub-countable types, i.e. those that merely embed into ℕ. These define a container representing multisets with cardinality of at most ℵ₀.

module SubNat where
  open import Cubical.Data.Nat
  open import Cubical.Functions.Embedding

  isSub : (X : Type)  Type _
  isSub X =  X   ∥₁

  isPropIsSub :  X  isProp (isSub X)
  isPropIsSub X = isPropPropTrunc

  isSub-⊤ : isSub 𝟙
  isSub-⊤ = PT.∣ const 0 , hasPropFibers→isEmbedding  { n ( , _) ( , _)  Σ≡Prop  _  isSetℕ _ _) refl }) ∣₁

  isSub-+1 :  {X}  isSub X  isSub (X  𝟙)
  isSub-+1 {X} = PT.map _+1 where module _ (ι : X  ) where
    suc-ι : (X  𝟙)  
    suc-ι (just x) = suc (ι .fst x)
    suc-ι nothing = 0

    _+1 : (X  𝟙)  
    _+1 .fst = suc-ι
    _+1 .snd = injEmbedding isSetℕ cancel where
      cancel :  {x y : X  𝟙}  suc-ι x  suc-ι y  x  y
      cancel {x = just x} {y = just y} p = cong just (isEmbedding→Inj (ι .snd) x y (injSuc p))
      cancel {x = just x} {y = nothing} = ex-falso  snotz
      cancel {x = nothing} {y = just y} = ex-falso  znots
      cancel {x = nothing} {y = nothing} _ = refl′ nothing

  isSub-∖ :  {X}  isSub X   x  isSub (X ∖° x)
  isSub-∖ {X} = PT.rec (isPropΠ λ x  isPropIsSub (X ∖° x)) λ ι (x , _)  PT.∣ compEmbedding ι (forget-remove-embedding x) ∣₁

  open Universe isSub isPropIsSub isSub-+1 isSub-∖
    renaming (uBag to ℕBag)

  ∂-ℕBag : Equiv ( ℕBag) ℕBag
  ∂-ℕBag = ∂-uBag