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)