module Data.Fin.Base where
Finite setsπ
The type is the type with exactly elements. We define it as a record type, packaging a natural number with a proof that
record Fin (n : Nat) : Type where constructor fin field lower : Nat β¦ bounded β¦ : Irr (lower Nat.< n)
While the type
is a homotopy proposition, we would like to
enforce that constructions on
definitionally do not depend on the given proof. Therefore, we
have wrapped it in Irr
.
In dependent type theory, itβs common to instead define the finite
sets as an inductive family indexed over Nat
. However, in cubical
type theory, there is a good reason to avoid inductive families: they
have subst
as an additional
constructor, including along constant paths. This makes the
normal form of any expression involving substitution in an indexed
family, even if the thing being transported is a constructor form, very
bad.
Instead, we would like the subst
operation on Fin
to definitionally
commute with the constructors, and (if possible) to definitionally
preserve the underlying numeric value. Defining Fin
as an indexed type with
an irrelevant proof field achieves exactly this:
private cast : β {m n} β m β‘ n β Fin m β Fin n cast p (fin n β¦ i β¦) = record { lower = n ; bounded = subst (n Nat.<_) p <$> i } _ : β {m n} {p : m β‘ n} {x : Fin m} β subst Fin p x β‘ cast p x _ = refl
We can still prove that the definition above is equivalent
to an inductive family. In particular, itβs generated by the
βconstructorsβ fzero
and fsuc
.
_ : β {n} β Fin (suc n) _ = fzero fsuc : β {n} β Fin n β Fin (suc n) fsuc (fin n β¦ x β¦) = fin (suc n) β¦ Nat.sβ€s <$> x β¦
from-nat : β (n : Nat) β Fin (suc n) from-nat zero = fzero from-nat (suc n) = fsuc (from-nat n) module _ {m n : Nat} (p : m β‘ n) (x : Fin m) where _ : subst (Fin β suc) p (fsuc x) β‘ fsuc (subst Fin p x) _ = refl _ : subst (Fin β suc) p fzero β‘ fzero _ = refl
Viewing elements of a finite setπ
Agda has good support, through the with
abstraction,
to pretend that a type is inductively generated. We therefore expose a
type Fin-view
which allows us to discriminate on whether an element of
is fzero
or fsuc
.
data Fin-view : {n : Nat} β Fin n β Type where zero : β {n} β Fin-view {suc n} fzero suc : β {n} (i : Fin n) β Fin-view {suc n} (fsuc i)
Of course, this family has a section, expressing that any finite number is generated by one of the two βconstructorsβ.
fin-view : β {n} (i : Fin n) β Fin-view i fin-view {suc n} fzero = zero fin-view {suc n} (fin (suc i) β¦ b β¦) = suc (fin i β¦ Nat.β€-peel <$> b β¦) fin-view {zero} (fin _ β¦ forget i β¦) = absurd (Nat.Β¬sucβ€0 i)
As a first application, we can prove that Fin 0
is
empty:
Fin-absurd : Fin 0 β β₯ Fin-absurd i with fin-view i ... | ()
We can also define an elimination principle for the family which applies the view at βevery levelβ.
Fin-elim : β {β} (P : β {n} β Fin n β Type β) β (β {n} β P {suc n} fzero) β (β {i} (j : Fin i) β P j β P (fsuc j)) β β {n} (i : Fin n) β P i Fin-elim P pfzero pfsuc i with fin-view i ... | zero = pfzero ... | suc i = pfsuc i (Fin-elim P pfzero pfsuc i)
fin-ap : β {n} {x y : Fin n} β x .lower β‘ y .lower β x β‘ y fin-ap p = apβ (Ξ» x y β fin x β¦ y β¦) p (to-pathp refl)
Next, we move on to one of the most useful functions for Fin
: strength
.
This allows us to (possibly) strengthen the upper bound on some
Fin n
.
strengthen : β {n} β Fin (suc n) β Fin (suc n) β Fin n strengthen {n = zero} k with fin-view k ... | vzero = inl fzero strengthen {n = suc n} k with fin-view k ... | zero = inr fzero ... | suc i = β-map fsuc fsuc (strengthen i)
On the other hand, weaken
does the opposite:
it relaxes the upper bound on some Fin n
, allowing us to
regard it as a Fin (suc n)
.
weaken : β {n} β Fin n β Fin (suc n) weaken n with fin-view n ... | zero = fzero ... | suc i = fsuc (weaken i)
We can also relax the upper bounds if m β€ n
.
inject : β {m n} β m Nat.β€ n β Fin m β Fin n inject p (fin n β¦ b β¦) = fin n β¦ (Ξ» e β Nat.β€-trans e p) <$> b β¦
Discretenessπ
Since the finite set of size is a subset of the natural numbers, which have decidable equality, it follows that also has decidable equality.
instance Discrete-Fin : β {n} β Discrete (Fin n) Discrete-Fin {x = x} {y} with x .lower β‘? y .lower ... | yes p = yes (fin-ap p) ... | no Β¬p = no Ξ» p β Β¬p (ap lower p)
In particular, Hedbergβs theorem shows that is a set.
opaque Fin-is-set : β {n} β is-set (Fin n) Fin-is-set = Discreteβis-set Discrete-Fin instance H-Level-Fin : β {n k} β H-Level (Fin n) (2 + k) H-Level-Fin = basic-instance 2 Fin-is-set
However, we can also mimic parts of the proof that Nat
itself is discrete, and
obtain useful combinators for reasoning with finite sets. For example,
the zero element in
is never equal to a successor:
fzeroβ fsuc : β {n k p} β Β¬ Path (Fin (suc n)) fzero (fin (suc k) β¦ p β¦) fzeroβ fsuc p = Nat.zeroβ suc (ap lower p)
fsucβ fzero : β {n k p} β Β¬ Path (Fin (suc n)) (fin (suc k) β¦ p β¦) fzero fsucβ fzero p = Nat.sucβ zero (ap lower p)
Moreover, since we can implement a βpredecessorβ operation, we get
that fsuc
is an
injection.
fsuc-inj : β {n} {i j : Fin n} β fsuc i β‘ fsuc j β i β‘ j fsuc-inj {n = suc n} p = ap pred p where pred : Fin (suc (suc n)) β Fin (suc n) pred n with fin-view n ... | zero = fzero ... | suc i = i
instance Number-Fin : β {n} β Number (Fin n) Number-Fin {n} .Number.Constraint k = k Nat.< n Number-Fin {n} .Number.fromNat k β¦ e β¦ = fin k β¦ forget e β¦ open import Data.Nat.Base using (0β€x ; sβ€s') public Fin-cases : β {β} {n} {P : Fin (suc n) β Type β} β P 0 β (β x β P (fsuc x)) β β x β P x Fin-cases p0 ps n with fin-view n ... | zero = p0 ... | suc i = ps i fin-cons : β {β} {n} {P : Fin (suc n) β Type β} β P 0 β (β x β P (fsuc x)) β β x β P x fin-cons = Fin-cases
Orderingπ
Keeping with the view that Fin
represents the type of
bounded natural numbers, we can re-use the ordering on Nat
to induce an ordering on
Fin
. This means that any
lemmas about the ordering on natural numbers apply immediately to the
ordering on standard finite sets.
_β€_ : β {n} β Fin n β Fin n β Type i β€ j = i .lower Nat.β€ j .lower infix 7 _β€_ _<_ : β {n} β Fin n β Fin n β Type i < j = i .lower Nat.< j .lower infix 7 _<_
Next, we define a pair of functions squish
and skip
, which are the
building blocks for all monotone functions between Fin
. squish i
takes a j : Fin (suc n)
to a Fin n
by mapping
both i
and i+1
to i
. Its
counterpart skip i
takes some j : Fin n
to a
Fin (suc n)
by skipping over i
instead.
squish : β {n} β Fin n β Fin (suc n) β Fin n squish n k with fin-view n | fin-view k ... | zero | zero = fzero ... | zero | suc j = j ... | suc i | zero = fzero ... | suc i | suc j = fsuc (squish i j) skip : β {n} β Fin (suc n) β Fin n β Fin (suc n) skip n k with fin-view n ... | zero = fsuc k ... | suc i with fin-view k ... | zero = fzero ... | suc j = fsuc (skip i j)
Arithmeticπ
split-+ : β {m n} β Fin (m + n) β Fin m β Fin n split-+ {m = zero} i = inr i split-+ {m = suc m} k with fin-view k ... | zero = inl fzero ... | suc i = β-map fsuc id (split-+ i) avoid : β {n} (i j : Fin (suc n)) β i β j β Fin n avoid {n = n} i j iβ j with fin-view i | fin-view j ... | zero | zero = absurd (iβ j refl) ... | zero | suc j = j avoid {n = suc n} _ _ iβ j | suc i | zero = fzero avoid {n = suc n} _ _ iβ j | suc i | suc j = fsuc (avoid i j (iβ j β ap fsuc)) fshift : β {n} (m : Nat) β Fin n β Fin (m + n) fshift zero i = i fshift (suc m) i = fsuc (fshift m i) opposite : β {n} β Fin n β Fin n opposite {n = n} i with fin-view i opposite {n = suc n} _ | zero = from-nat n opposite {n = suc n} _ | suc i = weaken (opposite i)
Vector operationsπ
_[_β_] : β {β} {A : Type β} {n} β (Fin n β A) β Fin (suc n) β A β Fin (suc n) β A _[_β_] {n = n} p i a j with fin-view i | fin-view j _[_β_] {n = n} Ο fzero a fzero | zero | zero = a _[_β_] {n = n} Ο fzero a .(fsuc j) | zero | suc j = Ο j _[_β_] {n = suc n} Ο .(fsuc i) a .fzero | suc i | zero = Ο fzero _[_β_] {n = suc n} Ο .(fsuc i) a .(fsuc j) | suc i | suc j = ((Ο β fsuc) [ i β a ]) j delete : β {β} {A : Type β} {n} β (Fin (suc n) β A) β Fin (suc n) β Fin n β A delete Ο i j = Ο (skip i j)