mirror of
https://github.com/Brandon-Rozek/website.git
synced 2024-10-30 01:12:07 -04:00
123 lines
4.6 KiB
Markdown
123 lines
4.6 KiB
Markdown
---
|
||
title: "Coercing Ints to Nats for Induction in Lean 4"
|
||
date: 2024-03-27T21:51:02-04:00
|
||
draft: false
|
||
tags: ["Formal Methods"]
|
||
math: true
|
||
medium_enabled: false
|
||
---
|
||
|
||
Warning: This post is what happens when I solve a problem that's already solved within Mathlib. I find it interesting enough to share though. Hopefully this will inspire techniques to use within your own proofs.
|
||
|
||
---
|
||
|
||
Before getting into how to do induction on integers through nat coercion, let's discuss the proper way of solving this problem using `mathlib`.
|
||
|
||
Let $x$ be an integer greater than some integer $m$. Show that $x$ satisfies some property. For example, prove $x > -5 \rightarrow x > -10$.
|
||
|
||
Okay, I hear you, `linarith` can do this for us. Let's pretend that doesn't exist for now and show how we can go about solving this using `mathlib`.
|
||
|
||
```lean4
|
||
example (x : ℤ): x > -5 → x > -10 := by
|
||
-- Int.le_induction expects something of the form m ≤ x → ...
|
||
show -4 ≤ x → x > -10
|
||
|
||
-- Int.le_induction doesn't let you specify the
|
||
-- motive so you need to make it easy for it to induce
|
||
let P : ℤ → Prop := fun x => x > -10
|
||
|
||
-- Base Case
|
||
have H_base : P (-4) := by decide
|
||
|
||
-- Inductive case
|
||
have H_ind : ∀ (n : ℤ), -4 ≤ n → P n → P (n + 1) := by
|
||
intro (n : ℤ)
|
||
intro (HH1 : -4 ≤ n)
|
||
intro (HH2 : n > -10)
|
||
show n + 1 > -10
|
||
exact lt_add_of_lt_of_pos HH2 (show 0 < 1 by decide)
|
||
|
||
-- Use the induction principle
|
||
exact show (-4 ≤ x → x > -10) from Int.le_induction H_base H_ind x
|
||
```
|
||
|
||
`Int.le_induction` is a nice and clean solution which I highly recommend you use. It makes sense too, prove that the property works for the lower bound and then prove the induction case holds.
|
||
|
||
Now what if for some reason you couldn't rely `Int.le_induction` and you wanted to find another way to go about this? This is where coercion comes in.
|
||
|
||
Starting from the beginning of the proof, this time let's introduce the hypothesis.
|
||
|
||
```lean
|
||
intro (H : x > -5)
|
||
```
|
||
|
||
Now we want to create a natural number which we'll perform induction over. Similar to the mathlib proof, we want to start with the lower bound as the base case. Therefore, make it so when $x$ is the lower bound our new natural number variable $n$ is 0.
|
||
|
||
```lean4
|
||
let n : ℕ := Int.toNat (x + 5)
|
||
```
|
||
|
||
Our goal `x > -10` is still written in terms of the original variable $x$. To rewrite this, we'll need to prove the following relationship:
|
||
|
||
```lean4
|
||
have H1 : x = n - 5 := by
|
||
-- We should start off by saying this relationship
|
||
-- holds for the integer version of n
|
||
let nz : ℤ := x + 5
|
||
have H1_1 : x = nz - 5 := eq_sub_of_add_eq (show nz = x + 5 by rfl)
|
||
suffices nz - 5 = n - 5 by rewrite [H1_1]; assumption
|
||
|
||
-- Through congruence closure we can ignore the (- 5)
|
||
suffices nz = n by
|
||
let Minus5 : ℤ → ℤ := fun x => x - 5
|
||
show Minus5 nz = Minus5 n
|
||
exact congrArg Minus5 this
|
||
|
||
-- An integer coercion is only equal to a nat
|
||
-- if the integer was 0 or positive to begin with
|
||
have H1_2 : nz ≥ 0 := by
|
||
have H : nz - 5 > 0 - 5 := by rewrite [<- H1_1]; assumption
|
||
have H1 : nz - 5 ≥ 0 - 5 := Int.le_of_lt H
|
||
exact (Int.add_le_add_iff_right (-5)).mp H1
|
||
|
||
exact show nz = n from (Int.toNat_of_nonneg H1_2).symm
|
||
```
|
||
|
||
With $x$ written in terms of $n$, we can now rewrite our goal using our natural number.
|
||
|
||
```lean4
|
||
suffices n - 5 > (-10: ℤ) by rewrite [H1]; assumption
|
||
```
|
||
|
||
Here's the rest of the induction. Of course like before, I avoid the use of `linarith` so I wouldn't be cheating ;)
|
||
|
||
```lean4
|
||
have H_base : 0 - 5 > -10 := by decide
|
||
|
||
have H_ind : ∀ n : ℕ, n - 5 > (-10 : ℤ) → (n + 1) - 5 > (-10: ℤ) := by
|
||
intro n
|
||
intro (IH : n - 5 > (-10 : ℤ))
|
||
have IH : (-10: ℤ) < n - 5 := IH
|
||
show (-10: ℤ) < n + 1 - 5
|
||
|
||
have IH_2 : n + 1 - (5: ℤ) = n + (-5 : ℤ) + 1 := by calc
|
||
n + 1 - (5: ℤ) = n + 1 + (-5 : ℤ) := rfl
|
||
_ = n + (1 + (-5: ℤ)) := Int.add_assoc (↑n) 1 (-5)
|
||
_ = n + ((-5 : ℤ) + 1) := rfl
|
||
_ = n + (-5 : ℤ) + 1 := (Int.add_assoc (↑n) (-5) 1).symm
|
||
_ = n - (5: ℤ) + 1 := rfl
|
||
|
||
suffices -10 < n - (5: ℤ) + 1 by rewrite [IH_2]; assumption
|
||
|
||
exact lt_add_of_lt_of_pos IH (show 0 < 1 by decide)
|
||
|
||
exact show n - 5 > (-10: ℤ) from Nat.recOn
|
||
(motive := fun x: ℕ => x - (5 : ℤ) > (-10: ℤ))
|
||
n
|
||
H_base
|
||
H_ind
|
||
```
|
||
|
||
This version is slightly longer than the `Int.le_induction` version as we had to carry forward the `- 5` portion of the equations. While it might not make sense in the integer case to perform this coercion, I'm hopeful that I can use a technique similar to this on other inductive types in the future.
|
||
|
||
Let me know if you end up using this or if you have any other cool induction techniques in your tool belt. Until next time.
|