Motivation
When I was looking at how to do simple addition in decimal, I saw that we use the associativity property of natural numbers.
For example, 15 + 26 ≡ (10 + 5) + (20 + 6) ≡ 10 + 20 + (5 + 6) ≡ (10 + 20 + 10) + 1 ≡ 40 + 1 ≡ 41.
In the following, I saw that it was always possible, with just the associativity property, to transform any addition to 1+(1+(1+...)).
In Cubical Type Theory, there are quotient types. Therefore, it is possible to create natural numbers with associativity property in their types.
I will use this to prove that natural numbers without zero can be defined using their associativity property.
Imports
Importing Cubical Libraries:
{-# OPTIONS --cubical #-}
open import Agda.Primitive
open import Cubical.Foundations.Prelude
open import Cubical.HITs.SetTruncation
open import Cubical.HITs.S1
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
using (Iso; isoToPath)
open import Cubical.Relation.Nullary.Base
open import Cubical.Relation.Nullary.Properties
open import Cubical.Data.Empty as ⊥
open import Cubical.Data.Unit renaming (Unit to ⊤)
open import Cubical.Data.Bool
open import Cubical.Data.Int hiding (_+_; _+'_)
open import Cubical.Data.Nat hiding (_+_)
To use it later:
_≠_ : ∀ {ℓ} {A : Set ℓ} → A → A → _
x ≠ y = ¬ (x ≡ y)
Defintion
The natural numbers are defined using Peano axioms. So a natural number can be zero or the successor of another natural number.
data ℕ' : Set where zero' : ℕ' suc' : ℕ' → ℕ'
But it can also be defined using its associativity property. I will define it starting from one and I will show why this definition is wrong:
module wrong-definition where
infixl 6 _+_
data N : Set where
one : N
_+_ : N → N → N
assoc : (a b c : N) → a + b + c ≡ a + (b + c)
In cubical type theory, there are high inductive types (HITs), so two equalities are not always the same. Because of that, one + one + one + one can be equal to one + (one + (one + one)) in two ways:
o+o+o+o : N
o+o+o+o = one + one + one + one
o+[o+[o+o]] : N
o+[o+[o+o]] = one + (one + (one + one))
o≡[o] : Set
o≡[o] = o+o+o+o ≡ o+[o+[o+o]]
o≡[o]₁ : o≡[o]
o≡[o]₁ = o+o+o+o
≡⟨ assoc _ _ _ ⟩
(one + one + (one + one))
≡⟨ assoc _ _ _ ⟩
o+[o+[o+o]] ∎
o≡[o]₂ : o≡[o]
o≡[o]₂ = o+o+o+o
≡⟨ cong (_+ one) (assoc _ _ _) ⟩
one + (one + one) + one
≡⟨ assoc _ _ _ ⟩
one + ((one + one) + one)
≡⟨ cong (one +_) (assoc _ _ _) ⟩
o+[o+[o+o]] ∎
Now, I will prove that both of these equalities (o≡[o]₁ and o≡[o]₂) are different:
parity : N → Bool parity one = true parity (m + n) = parity m ⊕ parity n parity (assoc m n l i) = sym (⊕-assoc (parity m) (parity n) (parity l)) i _ : parity (one + one) ≡ false _ = refl _ : ∀ i → parity (assoc one one one i) ≡ true _ = λ i → refl model : N → S¹ model one = base model (_ + _) = base model (assoc x _ _ i) = (if parity x then refl else loop) i toΩS¹ : o≡[o] → ΩS¹ toΩS¹ = cong model toℤ : o≡[o] → ℤ toℤ p = winding (toΩS¹ p) o≡[o]₁-ℤ : toℤ o≡[o]₁ ≡ pos 1 o≡[o]₁-ℤ = refl o≡[o]₂-ℤ : toℤ o≡[o]₂ ≡ pos 0 o≡[o]₂-ℤ = refl f : o≡[o] → ℕ f n = abs (toℤ n) _ : f o≡[o]₁ ≡ 1 _ = refl _ : f o≡[o]₂ ≡ 0 _ = refl o≡[o]₁≠o≡[o]₂ : o≡[o]₁ ≠ o≡[o]₂ o≡[o]₁≠o≡[o]₂ p = snotz (cong f p)
The secret to prove it is to try to find a function f that goes from each equality to a different value.
In the code above, the first equality (o≡[o]₁) goes to 1 while the second (o≡[o]₂) goes to 0.
So proving that f o≡[o]₁ ≠ f o≡[o]₂, I got o≡[o]₁ ≠ o≡[o]₂.
Because of the problem that two equalities are not always the same in Cubical Type Theory, it is usually necessary to truncate the Set. So the natural numbers will be defined in this way:
infixl 6 _+_ data N : Set where one : N _+_ : N → N → N assoc : (a b c : N) → a + b + c ≡ a + (b + c) trunc : isSet N
In Agda, it is possible to overload the natural numbers. So when I write 1, it will be one; when I write 2, it will be one + one and so on.
I will define the overload for this Natural number:
open import Cubical.Data.Nat.Literals public
constraintNumber : ℕ → Set
constraintNumber zero = ⊥
constraintNumber (suc _) = ⊤
fromNat' : (n : ℕ) ⦃ _ : constraintNumber n ⦄ → N
fromNat' zero ⦃ () ⦄
fromNat' (suc zero) = one
fromNat' (suc (suc n)) = fromNat' (suc n) + one
instance
NumN : HasFromNat N
NumN = record { Constraint = constraintNumber ; fromNat = fromNat' }
idN : N → N
idN n = n
_ : idN 1 ≡ one
_ = refl
_ : idN 2 ≡ one + one
_ = refl
_ : idN 3 ≡ one + one + one
_ = refl
To use these natural numbers, it is good to have eliminators:
module Elim {ℓ'} {B : N → Type ℓ'}
(one* : B one) (_+*_ : {m n : N} → B m → B n → B (m + n))
(assoc* : {x y z : N} (x' : B x) (y' : B y) (z' : B z)
→ PathP (λ i → B (assoc x y z i))
((x' +* y') +* z') (x' +* (y' +* z')))
(trunc* : (n : N) → isSet (B n)) where
f : (n : N) → B n
f one = one*
f (m + n) = f m +* f n
f (assoc x y z i) = assoc* (f x) (f y) (f z) i
f (trunc m n p q i j) =
isOfHLevel→isOfHLevelDep 2 trunc* (f m) (f n)
(cong f p) (cong f q) (trunc m n p q) i j
module ElimProp {ℓ'} {B : N → Type ℓ'} (BProp : {n : N} → isProp (B n))
(one* : B one) (_+*_ : {m n : N} → B m → B n → B (m + n)) where
f : (n : N) → B n
f = Elim.f {B = B} one* _+*_
(λ {x} {y} {z} x' y' z' →
toPathP (BProp (transp (λ i → B (assoc x y z i)) i0
((x' +* y') +* z')) (x' +* (y' +* z'))))
λ n → isProp→isSet BProp
module Rec {ℓ'} {B : Type ℓ'} (BType : isSet B)
(one* : B) (_+*_ : B → B → B)
(assoc* : (a b c : B) → (a +* b) +* c ≡ a +* (b +* c)) where
f : N → B
f = Elim.f one* (λ m n → m +* n) assoc* λ _ → BType
Proving isomorphism of natural numbers with associativity and Peano natural numbers
First, we will define the natural numbers starting from one:
data N' : Set where one' : N' s : N' → N'
Let’s define the isomorphism:
to : N' → N from : N → N' from∘to : ∀ a → from (to a) ≡ a to∘from : ∀ n → to (from n) ≡ n iso : Iso N N' iso .Iso.fun = from iso .Iso.inv = to iso .Iso.rightInv = from∘to iso .Iso.leftInv = to∘from
With the isomorphism, it is possible to prove the equality of the two sets with univalence:
N≡N' : N ≡ N' N≡N' = isoToPath iso
Let’s define the definition of addition and prove the associativity property of Peano natural numbers to use it later:
_+'_ : N' → N' → N' one' +' b = s b s a +' b = s (a +' b) assoc' : ∀ a b c → (a +' b) +' c ≡ a +' (b +' c) assoc' one' b c i = s (b +' c) assoc' (s a) b c i = s (assoc' a b c i)
I will assume that the Peano natural numbers are a set and I will prove the easiest parts of the definitions above with this information.
N'-Set : isSet N' to one' = 1 to (s a) = 1 + to a from one = one' from (a + b) = from a +' from b from (assoc a b c i) = assoc' (from a) (from b) (from c) i from (trunc m n p q i j) = N'-Set _ _ (λ k → from (p k)) (λ k → from (q k)) i j from∘to one' _ = one' from∘to (s a) i = s (from∘to a i)
To prove that Peano natural numbers are a set, I will use the theorem that every discrete type is a set.
N'-Discrete : Discrete N' N'-Set = Discrete→isSet N'-Discrete
It is straightforward to prove that Peano natural numbers are discrete:
dec : N' → N'
dec one' = one'
dec (s m) = m
suc-eq : ∀ {m n} → s m ≡ s n → m ≡ n
suc-eq eq i = dec (eq i)
P : N' -> Set
P one' = ⊤
P (s _) = ⊥
N'-Discrete one' one' = yes λ i → one'
N'-Discrete one' (s n) = no λ eq → transport (λ j → P (eq j)) tt
N'-Discrete (s m) one' = no λ eq → transport (λ j → P (eq (~ j))) tt
N'-Discrete (s m) (s n) with N'-Discrete m n
... | yes eq = yes (cong s eq)
... | no ¬eq = no λ eq → ¬eq (suc-eq eq)
The last thing to prove is to∘from using the elimination of naturals and the following addition lemma:
add-lemma : ∀ a b → to (a +' b) ≡ to a + to b
to∘from = ElimProp.f (trunc _ _) (λ i → one)
λ {a} {b} m n → add-lemma (from a) (from b) ∙ (λ i → m i + n i)
add-lemma one' b = refl
add-lemma (s a) b = (λ i → one + (add-lemma a b i))
∙ sym (assoc one (to a) (to b))
Applications
Vectors
In the same way that natural numbers are defined using their associativity property, vectors can also be defined in this way:
infixl 20 _++_
data Vec (A : Set) : N → Set where
[_] : A → Vec A one
_++_ : ∀ {m n} (xs : Vec A m) (ys : Vec A n) → Vec A (m + n)
assoc : ∀ {m n p} (xs : Vec A m) (ys : Vec A n) (zs : Vec A p)
→ PathP (λ i → Vec A (assoc m n p i)) (xs ++ ys ++ zs) (xs ++ (ys ++ zs))
isSetVec : ∀ {n} → isSet (Vec A n)
exVec₁ : Vec N 1
exVec₁ = [ 1 ]
exVec₂ : Vec N 3
exVec₂ = [ 3 ] ++ [ 2 ] ++ [ 1 ]