Skip to main content

Peano numbers in Lean

·2124 words

In this post, I will define the natural numbers using the Peano axioms, which will be referred to here as Peano numbers, and prove that the set of natural numbers \(\N\) equipped with ordinary addition and multiplication is a commutative semiring, i.e., that \((\N, +, 0)\) and \((\N, \cdot, 1)\) are both commutative monoids such that \(a \cdot 0 = 0\) and \(a \cdot (b + c) = a \cdot b + a \cdot c\).

The purpose of this post is to explore Lean’s capabilities in helping prove some simple theorems related to algebra.

Defining the Peano numbers

In Lean, the Peano numbers can be defined as an inductive type. In fact, the natural numbers in Lean are defined exactly like this. Likewise, I will define them like this:

inductive Peano : Type
| _0 : Peano
| S  : Peano → Peano

Here, _0 represents the natural number 0 and S represents the successor operation, and both are constructors for the type Peano. I will also define the natural number 1 with def _1 := S _0, which will be used to show some properties of the addition and multiplication operations.

Forming a commutative semiring with the natural numbers

As stated at the introduction of this post, the natural numbers equipped with ordinary addition and multiplication can be described as the commutative semiring \((\N, +, 0, \cdot, 1)\), which has the following properties:

  • \((\N, +, 0)\) is the commutative monoid of \(\N\) under addition, with identity element 0:
    • Addition associativity: \((a + b) + c = a + (b + c)\).
    • Addition identity element: \(a + 0 = a\).
    • Addition commutativity: \(a + b = b + a\).
  • \((\N, \cdot, 1)\) is the commutative monoid of \(\N\) under multiplication, with identity element 1:
    • Multiplication associativity: \((a \cdot b) \cdot c = a \cdot (b \cdot c)\).
    • Multiplication identity element: \(a \cdot 1 = a\).
    • Multiplication commutativity: \(a \cdot b = b \cdot a\).
  • Multiplication absorbing element \(\N\): \(a \cdot 0 = 0\).
  • Multiplication distributes over addition: \(a \cdot (b + c) = a \cdot b + a \cdot c\).
But why can’t we treat \((\N, +, 0, \cdot, 1)\) as a commutative ring instead?

This is because we would have to define the additive inverse \(-a\) s.t. \(a - a = 0\) and also define the multiplicative inverse \(a^{-1}\) s.t. \(aa^{-1} = 1\), both impossible given that we are working with the set of natural numbers.

Proving the properties in Lean

Now, we can start to prove all the described properties in Lean. I will not make the use of any library, like mathlib4, so everything will be done in vanilla Lean 4.

In this section, I will show the proofs for associativity, the identity element and commutativity, mostly inspired by the Wikipedia article with proofs involving addition. To start the proof, I will register the Add instance for the Peano type, allowing us to use the binary operator + for addition:

instance : Add Peano where
  add a b := _add a b
    where _add a b := match b with
      | _0   => a
      | S b' => S (_add a b')

Associativity

The associative property \((a + b) + c = a + (b + c)\) can be proven by induction on the number \(c\):

@[simp]
theorem add_associativity (a b c : Peano) : (a + b) + c = a + (b + c) := by
    induction c with
    | _0 => rfl
    | S c' ih => calc
      a + b + S c' = S (a + b + c')   := by rfl
      _            = S (a + (b + c')) := by rw [ih]
      _            = a + (b + S c')   := by rfl

Here, rfl stands for reflexivity, a Lean tactic that tries to close the current goal using the reflexive property, and ih is the inductive hypothesis, defined as \(a + b + c\' = a + (b + c\')\), where \(c\'\) is a natural number s.t. its successor is equal to \(c\). I also defined this theorem with @[simp], which states that this theorem can be used by the simp tactic to simplify the main goal.

Identity element

The identity element for addition is 0, so we have to prove that \(a + 0 = a\). I will also prove that \(0 + a = a\), which will be useful when showing the proof for the addition commutativity.

For the right-identity, we can simply use the rfl tactic, because Lean will match \(a + 0\) with the addition definition:

@[simp]
theorem right_add_identity (a : Peano) : a + _0 = a := by
  rfl

For the left-identity, the proof can be easily done by induction on \(a\):

@[simp]
theorem left_add_identity (a : Peano) : _0 + a = a := by
  induction a with
  | _0 => rfl
  | S a' ih => calc
    _0 + S a' = S (_0 + a') := by rfl
    _         = S a'        := by rw [ih]

Commutativity

The commutative property of addition can be divided into two steps: 1. prove the property for a natural number \(a\) and \(1\) (\(a + 1 = 1 + a\)). 2. From the last property, prove the same but for all pairs of natural numbers (\(a + b = b + a\)).

The first step can be done with induction on the number \(a\), very similar to the left_add_identity proof:

@[simp]
theorem add_commutativity_1 (a : Peano) : a + _1 = _1 + a := by
  induction a with
  | _0 => rfl
  | S a' ih => calc
    S a' + _1 = S (a' + _1) := by rfl
    _         = S (_1 + a') := by rw [ih]
    _         = _1 + S a'   := by rfl

Finally, we can show the addition commutativity with the simp tactic, that will apply all the theorems defined previously when possible.

@[simp]
theorem add_commutativity (a b : Peano) : a + b = b + a := by
  induction b with
  | _0 => simp
  | S b' ih => calc
    a + S b' = a + b' + _1 := by rfl
    _        = b' + _1 + a := by simp [ih]
    _        = S b' + a    := by rfl

Here, the first simp is applying both right_add_identity and left_add_identity to reduce the base statement \(a + 0 = 0 + a\) to \(a = a\). Moreover, the second simp, written as simp [ihb], tells Lean that the inductive hypothesis defined in ih should also be considere in the simplification process.

Now, I will do an analogous process from the last section. First, I will also register the Mul instance, allowing us to use the binary operator * for multiplication:

instance : Mul Peano where
  mul a b := _mul a b
    where _mul a b := match b with
      | _0   => _0
      | S b' => a + _mul a b'

Identity and annihilating elements

Similar to the addition identity element proofs, I will show the identity (\(a \cdot 1 = 1\)) and annihilating (\(a \cdot 0 = 0\)) element property for both sides:

@[simp]
theorem right_mul_identity (a : Peano) : a * _1 = a := by
  rfl

@[simp]
theorem left_mul_identity (a : Peano) : _1 * a = a := by
  induction a with
  | _0 => rfl
  | S a' ih => calc
    _1 * S a' = _1 + _1 * a' := by rfl
    _         = a' + _1      := by rw [ih, add_commutativity]
@[simp]
theorem right_mul_annihilating (a : Peano) : a * _0 = _0 := by
  rfl

@[simp]
theorem left_mul_annihilating (a : Peano) : _0 * a = _0 := by
  induction a with
  | _0 => rfl
  | S a' ih => calc
    _0 * S a' = _0 + _0 * a' := by rfl
    _         = _0           := by rw [ih]; rfl

Distributivity

The distributive property was a bit tricky for me, because it would be much easier to prove it with the associative property of multiplication, and I couldn’t find a way elaborate a proof for associativity without depending on the on the distributive property.

However, I think I landed on a satisfactory proof. For the right-distributivity, where we want to show that \((b + c) \cdot a = b \cdot a + c \cdot a\), I used the main properties defined for addition, with emphasis to the addition associativity, which I used with the repeat property, that applies a tactic until until it fails:

@[simp]
theorem right_mul_distributivity (a b c : Peano) : (b + c) * a = b * a + c * a := by
    induction a with
    | _0 => simp
    | S a' ih => calc
      (b + c) * S a' = (b + c) + (b + c) * a'      := by rfl
      _              = b + (b * a' + c + c * a')   := by simp [ih]
      _              = (b + b * a') + (c + c * a') := by repeat (rw [add_associativity])
      _              = b * S a' + c * S a'         := by rfl

For the left-distributivity, where we want to show that \(a \cdot (b + c) = a \cdot b + a \cdot c\), I had a hard time with the simp tactic, because it started to run very slowly when I used it without any arguments. Because of this, I had to pass the only option, that limits simp to just use the expressions inside the brackets:

@[simp]
theorem left_mul_distributivity (a b c : Peano) : a * (b + c) = a * b + a * c := by
  induction a with
  | _0 => simp
  | S a' ih => calc
    S a' * (b + c)  = (a' + _1) * (b + c)           := by rfl
    _               = a' * (b + c) + _1 * (b + c)   := by rw [right_mul_distributivity]
    _               = a' * (b + c) + b + c          := by rw [left_mul_identity, add_associativity]
    _               = b + a' * b + c + a' * c       := by simp [ih]
    _               = (_1 + a') * b + (_1 + a') * c := by simp only [left_mul_identity, right_mul_distributivity, add_associativity]
    _               = S a' * b + S a' * c           := by simp only [add_commutativity]; rfl

Associativity

Basically, the associative property of multiplication, \((a \cdot b) \cdot c = a \cdot (b \cdot c)\), can be proved by induction on \(c\), where we have to use the left-distributivity of multiplication, proved in the last subsection:

@[simp]
theorem mul_associativity (a b c : Peano) : (a * b) * c = a * (b * c) := by
  induction c with
  | _0 => rfl
  | S c' ih => calc
    a * b * S c' = a * b + a * b * c' := by rfl
    _            = a * (b + b * c')   := by rw [ih, left_mul_distributivity]
    _            = a * (b * S c')     := by rfl

Commutativity

Finally, we can prove the last property related to multiplication, the commutativity. Because we have all the previous properties already defined, prove this property is very simple:

@[simp]
theorem mul_commutativity (a b: Peano) : a * b = b * a := by
  induction b with
  | _0 => rw [left_mul_annihilating, right_mul_annihilating]
  | S b' ih => calc
    a * S b' = a * (b' + _1)   := by rfl
    _        = a * b' + a * _1 := by rw [left_mul_distributivity]
    _        = _1 * a + b' * a := by simp [ih]
    _        = (b' + _1) * a   := by rw [right_mul_distributivity, add_commutativity]

Conclusion

For some months until now, I had the wish to learn a proof assistant language, and I have considered some languages like Coq (now Rocq), Agda, Idris, etc. However, in the last weeks, I saw that Lean, specially at the version 4, is a very capable and developed language for interactive theorem proving with a very passionate community, and proving all the theorems of this post was a fun exercise to see what I could do in the language. In the future, I hope to learn more about Lean and even start to prove things that are relevant for projects that I will be working.

If you want to see the full code, I recommend to see the gist I created. I will be updating it if I find simpler or more elegant proofs for the theorems (because I don’t know if I will update this blog post with the modifications).