UCL MATH0109 Theorem Proving in Lean Notes
1. Introduction
2. Foundations
2.1 A_functions
exact- if your goal is
⊢ Aand you have a termx : A, then exactxwill close the goal
- if your goal is
sorry- this tactic closes the goal without a proof. It is used as a placeholder until we are ready to complete the proof
apply- if we have a function
f : A → Band our goal is⊢ B, thenapply freduces our goal to⊢ A
- if we have a function
intro- if our goal is a function type
⊢ A → B, thenintro awill introduce a terma : Ainto the local context and change our goal to⊢ B
- if our goal is a function type
2.2 B_or_and_imp
left / right- if our goal is
⊢ P ∨ Q, thenleftreduces this to⊢ Pwhile right reduces this to⊢ Q
- if our goal is
cases- if we have
h : P ∨ Q, thencases hgives us two goals withh : Pin the first local context andh : Qin the second
- if we have
constructor- if our goal is
⊢ P ∧ Q, this gives us two new goals of⊢ Pand⊢ Q. Similarly, if our goal is⊢ P ↔ Q, then it gives two new goals⊢ P → Qand⊢ Q → P - if our goal is
⊢ P ↔ Q, thenconstructorwill convert this into two goals:⊢ P → Qand⊢ Q → P
- if our goal is
- if our goal is
obtain- if we have
h : P ∧ Qin the local context, thenobtain ⟨hp, hq⟩ := hreplaceshbyhp : Pandhq : Qin the local context - we can use
obtain ⟨hpq, hqp⟩ := hto converth : P ↔ Aintohpq : P → Qandhqp : Q → Pin the local context - if we have
h : P ↔ Qin the local context, thenh.1 : P → Qandh.2 : Q → P
- if we have
2.3 C_not_false
contradiction- prove any goal if you already have a contradiction in the local context
by_cases P(orby_cases hp : P)- replace the current goal by two separate goals, the first assuming P is true and the second assuming P is false
exfalso- replace the current goal by
False
- replace the current goal by
triv- prove the goal
True
- prove the goal
contrapose- convert any goal
P → Qinto its contrapositive
- convert any goal
2.4 D_quantifiers
Universal ∀
apply h- close any goal of the form
P a, givenh : ∀x, P xin the local context
- close any goal of the form
specialize h a- replace
h : ∀x, P xbyh : P ain the local context
- replace
intro a- if the goal is
∀x, P x, then introduce a new variableaand change the goal toP a
- if the goal is
Existential ∃
use a- if the goal is
∃x, P x, then change the goal toP a(IfP ais already in the local context, then the goal is closed automatically)
- if the goal is
obtain ⟨a,ha⟩ := h- if
h : ∃x, P xis in the local context then create a new termain the local context and a new hypothesisha : P a - if our hypothesis in the local context is
h : ∃x, ∃y, ..., thenobtain ⟨x, y, hxy⟩ := hwill give usxandy
- if
Negated quantifiers ¬
In Lean ¬P is formally defined as P → False so it is an implication.
push_neg- move all
¬symbols as far to the right as possible. In particular, replace¬∀x, P xby∃x, ¬P x, and similarly with¬∃ - use it to simplify a hypothesis in the local context
hwithpush_neg at h
- move all
by_contra h- add a hypothesis
hstating that the current goal is false, and change the goal toFalse
- add a hypothesis
2.5 E_equality_rfl_rw
rfl- if your goal is
⊢ a = bandais definitionally equal tob, thenrflwill close the goal
- if your goal is
rw- if we have a hypothesis
h : a = bin the local context, thenrw [h]will replace every occurence ofabybin the goal - we can also do
rw [← h]to replacebbyain the goal - if
h2is another term in the local context thenrw [h] at h2replacesabybinh2 - the
ainrwais short forassumption - we can group a sequence of rewrites together as follows
rw [h1, h2] - if we have a hypothesis (or theorem) of the form
h : ∀ a b , ... = ..., then we can userw [h i j]to rewrite using this equality in the casea = iandb = j(we can also userw [h]and Lean will chooseiandjfor us)
- if we have a hypothesis
2.6 F_nat
induction n- if our goal is of the form
⊢ P nwherenis an arbitrary natural number, then this allows us to do induction onn. We get two goals one for0and one forsucc n, in the latter we also have an inductive hypothesis saying that the result holds forn1 2 3 4 5 6 7
theorem zero_add (n : N) : 0 + n = n := by induction n with | zero => rfl | succ n ih => rw [add_succ, ih]
- if our goal is of the form
cases n- this allows us to prove a result
⊢ P nby considering the two cases,0andsucc nseparately (but we no longer have the inductive hypothesis)1 2 3 4 5 6 7
theorem zero_pow (n : N) (h : n ≠ 0) : 0 ^ n = 0:= by cases n with | zero => contradiction | succ n => rw [pow_succ,zero_mul]
- this allows us to prove a result
2.7 G_ext
ext- if our goal is to show that
⊢ f = gwheref g : A → B, we can useext xto introducex : Ainto the local context and our goal becomes⊢ f x = g x extalso allows to prove equalities between other types, such as complex numbers, matrices, etc.- sometimes
extdoes too much, for example if we want to prove that two complex matrices are equal, we can useext1to apply one extensionality lemma at a time
- if our goal is to show that
- Functions: fun => notation
- We have already seen how to define functions using tactics, however it will be useful to also know the function notation that Lean uses to display functions in the Infoview.
1 2 3 4 5 6 7 8
-- tactic definition def double1 : ℕ → ℕ := by intro n exact 2 * n -- fun => notation def double2 : ℕ → ℕ := fun n => 2 * n
- We have already seen how to define functions using tactics, however it will be useful to also know the function notation that Lean uses to display functions in the Infoview.
- Sets
- If
A : Type, then we can form the type of subsets of A, calledSet A. Two sets are equal iff they contain exactly the same elements. Applying theexttactic allows us to prove set identities using the tactics introduced to prove basic results in logic. If
x : Aands t : Set Athenx ∈ sis the Propx is a member of sProving set identities is just logic in disguise.x ∈ s ∪ tisx ∈ s ∨ x ∈ tx ∈ s ∩ tisx ∈ s ∧ x ∈ tx ∉ sis¬ x ∈ swhich isx ∈ s → Falsex ∈ sᶜis another way of writingx ∉ sx ∈ s \ tisx ∈ s ∧ x ∉ ts ⊆ tis∀x, x ∈ s → x ∈ tNote that
Ais not a term of typeSet A. We useunivto refer to theSet Athat isall of A. We also have the empty set∅.x ∈ univis the PropTruex ∈ ∅is the PropFalse
- If
2.8 H_Mathlib
Mathlib
“Mathlib” is a huge database of mathematical theorems all written in lean. It currently has well over 3000 files, each containing many theorems. The first line of this file allows us to use any theorem in Mathlib.
exact? / apply?- Often, we are faced with proving a very simple goal, which is almost certainly already in Mathlib. Typing
exact?will search Mathlib and try to find the theorem that we need. exact?searchs Mathlib for a result that will close the goal.apply?gives suggestions for a lemma to apply whenexact?fails.
- Often, we are faced with proving a very simple goal, which is almost certainly already in Mathlib. Typing
le_of_lt- We can look up the theorem
le_of_ltin the API documentation of Mathlib to see exactly what it says. We can also use the command#checkto do this. Also “ctrl-click” onle_of_ltwill take us to the file in Mathlib where this theorem is proved. - We can think of the theorem
le_of_ltas a function, which takes a long list of arguments and returns a proof of the statementa < b. The arguments are listed before the:. - Note that some of the arguments are contained in
( ), some in{ }and some in[ ]. The arguments contained in( )are called explicit arguments, and the others are called implicit arguments. If we typele_of_lt h, then lean will assume thathis the explicit argument. It is usually not necessary to provide the implicit arguments, because lean can work out for itself what values they need to take. In the case ofle_of_lt, the only explicit argument ish : a < b. If we give it a value ofh, then it can deduce the whataandbare, and also thatα = ℝ, since this is the type of the variablesaandb.
- We can look up the theorem
3. Analysis
3.1 A_structured_tactics
refinerefineis likeexactexcept that we can replace any explicit argument that we don’t currently have in our local context by?_and Lean will add this as a new goal- if
exact my_lemma hwould close the current goal, thenrefine my_lemma ?hreduces our goal to⊢ h
congr!- apply congrence lemmas to try to reduce the goal to several smaller goals that may be easier to prove
- Congruence lemmas: if
f = ganda = bthenf a = g b. We can prove this easily usingrwbut the tacticcongr!will do it for us - sometimes
congr!is too aggressive and results in goals that are false. We can control this usingcongr! nwheren = 1,2,..
convert- if our goal is
⊢ e, thenconvert dtells Lean to try to usedand produce new subgoals to prove thatd = e convertis similar torefinebut it works when the goal is not exactly the same as the term we use. It introduces new goals for us to prove that the given term is in fact correct. It uses the same strategies ascongr!and can be controlled in a similar way usingconvert h using nwheren = 1, 2, ...
- if our goal is
symm- if our goal is
⊢ A ∼ Bwhere∼is a symmetric relation then symm changes the goal to⊢ B ∼ A - if
h : A ∼ Bis in the local context thenh.symmisB ∼ A
- if our goal is
trans- if our goal is
⊢ A ∼ Bwhere∼is a transitive relation, thentrans Cconverts this into two goals⊢ A ∼ Cand⊢ C ∼ B
- if our goal is
3.2 B_higher_tactics
ring- prove identities in commutative rings (eg ℝ, ℤ, ℚ)
norm_num- close goals only involving numerical expressions
decide- close the goal by applying an algorithm for deciding if the goal is true or false
linarith- prove results involving linear (in)equalities and arithmetic
- e.g. the goal can be solved by
linarith1 2 3
abc: ℕ h: a + b + c = 3 * c ⊢ 2 * c = a + b
nlinarith- extension of linarith to some simple non-linear examples such as quadratics
3.3 C_have
have h : P- introduce a new goal
P, and add the hypothesish : Pto the local context of the current goal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
import Mathlib.Tactic.Basic import Mathlib.Data.Real.Basic /-- xₙ → a if for any ε > 0 there is N ∈ ℕ such that for all n ≥ N we have |xₙ - a| < ε -/ def sLim (x : ℕ → ℝ) (a : ℝ) : Prop := ∀ ε, 0 < ε → ∃ N, ∀ n, N ≤ n → |x n - a| < ε notation "limₙ " => sLim /-- The sequence `1/(n+1) → 0` -/ theorem one_over_nat : limₙ (fun n => (n + 1)⁻¹) 0 := by intro ε hε dsimp have h : ∃ N : ℕ, N > ε⁻¹ · exact exists_nat_gt ε⁻¹ obtain ⟨N,hN⟩ := h use N intro n hn rw [sub_zero] have h : |(n+1:ℝ)⁻¹| = (n+1:ℝ)⁻¹ · rw [abs_eq_self] apply le_of_lt exact Nat.inv_pos_of_nat rw [h] have : n+1 > ε⁻¹ · trans (N:ℝ) · norm_cast exact Nat.lt_succ.mpr hn · assumption exact inv_lt_of_inv_lt hε this
- introduce a new goal
3.4 D_calc
calc- A
calc-block is a useful way of writing a proof which consists of a series of rearrangements of a formula. This way of writing proofs is very similar to the way that most mathematicians write proofs on paper, so the resulting proofs are easy to read. - When dealing with inequalities in
calc-blocks, the tacticrelis often useful.relis similar torw, but substitutes inequalities rather than equalities.
- A
3.5 E1_intro_to_finsets
- Finsets
- Finite sets, such as ${0, 1, 2,…, n}$ have a special type in Lean, they are called
Finsets. Ifs : Finset αthensis a finite set of terms of type α. In many respects we can treat them likeSet α.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
-- Most standard set notation is still valid #check n ∈ s #check s ⊆ t #check s ∩ t #check s ∪ t #check s \ t --#check sᶜ -- fails since the complement of a `Finset ℕ` is never finite -- In general there is no `univ : Finset α` (unless `α` is itself finite) similiarly there is no `sᶜ`. -- We `open` the `Finset` namespace so that we can write `range` instead of `Finset.range` etc. open Finset #check s.Nonempty -- ∃x , x ∈ s #check Disjoint s t -- s ∩ t = ∅ #check range n -- {0,1,...,n - 1} as a Finset ℕ #check ({n} : Finset ℕ) -- {n} as a Finset ℕ #check insert n s -- s ∪ {n} #check s.erase n -- s \ {n} #check s.image f -- {f x | x ∈ s} #check s.card -- |s| the number of elements in s -- We can `filter` a set to obtain the subset with a given property. #check s.filter Even -- { x | x ∈ s and x is even} #eval Ico a (b + 1) -- {a,a+1,..,b} defaults to ∅ if b ≤ a
mem_union:a ∈ s ∪ t ↔ a ∈ s ∨ a ∈ tmem_Ico:x ∈ Ico a b ↔ a ≤ x ∧ x < bThere are two different
maximumfunctions defined fors : Finset PwhenPis any LinearOrder such asℕorℝ1 2 3 4 5
#check Finset.max' -- this requires a proof that s is Nonempty and then returns a value in `P` #check Finset.max -- this returns a value in `WithBot P` which we can think of as `P` with an extra -- element that is < everything in `P`.
requring a proof:
use s.max' hwherehis the proof thats.NonemptyIf
S : α → Finset βandI : Finset αthenI.biUnion Sis the finite union of the Finsets indexed byI.- The cardinality of
s : Finset αiss.card.
- Finite sets, such as ${0, 1, 2,…, n}$ have a special type in Lean, they are called
- Finite sums
∑ i in range n.succ, i:0 + 1 + 2 + ... + nsum_range_succ:∑ x in range (n + 1), f x = ∑ x in range n, f x + f nsum_range_one:∑ k in range 1, f k = f 0card_eq_sum_ones:card s = ∑ x in s, 1sum_range_add_sum_Ico:Finset.sum_range_add_sum_Ico.{v} {β : Type v} [inst✝ : AddCommMonoid β] (f : ℕ → β) {m n : ℕ} (h : m ≤ n) : ∑ k in range m, f k + ∑ k in Ico m n, f k = ∑ k in range n, f k
3.6 E2_let
letlet I : Finset ℕ := range K.succa new way of using
have:have hne : I.Nonempty := by exact nonempty_range_succ
3.7 F_casts
- divisibility
a ∣ b(typed as\|) means that there is somecsuch thatb = a * c(useis helpful here)Nat.div_mul_div_comm:Nat.div_mul_div_comm {m n k l : ℕ} (hmn : n ∣ m) (hkl : l ∣ k) : m / n * (k / l) = m * k / (n * l)Nat.mul_div_mul:Nat.mul_div_mul {m : ℕ} (n k : ℕ) (H : 0 < m) : m * n / (m * k) = n / k
push_cast- change
↑(a + b)to↑a + ↑b
- change
norm_cast- sometimes try
norm_cast at hornorm_cast at h ⊢ - If
d n : ℕand we have the hypothesish: d ∣ nthen norm_cast can prove that((n / d : ℕ) : ℝ)equals(n : ℝ)/(d : ℝ), but without this hypothesis it simply isn’t true.
- sometimes try
cancel_denoms- change
a = b / 2to2 * a = b
- change
4. Structures and classes
4.1 A_structures_and_classes
- Structures
In Lean, there are many
Types, for exampleℕ,ℤ,ℚ,ℝ,ℂ,ℕ → ℝ,Set ℕ, … etc.A common way of defining a new
Typeis using the commandstructure.Below, we define a new
TypecalledPlanewhose terms can be thought of as being points in the plane. They each have anx-coordinate and ay-coordinate, both of which are integers.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
@[ext] -- automatically generates a lemma `Plane.ext`, -- which gives us a way of proving that two terms of type `Plane` are equal. -- The lemma is used by the `ext` tactic. structure Plane where x : ℤ y : ℤ -- `x` and `y` are called "fields" of the structure `Plane`. /- We can define terms of type `Plane` in one of the following equivalent methods: -/ def A : Plane where x := 1 y := 3 def B : Plane := ⟨-4,7⟩ def origin : Plane := {x := 0, y:= 0}
- Classes
- later
4.2 B_defining_a_class
- Group
- Let’s define the notion of a group in lean. Recall that a group is a set
G, together with- a multiplication operation
G → G → G, - a function
G → Gtaking an elementxto an elementx⁻¹, - a certain element in
Gcalled1. Furthermore,Gmust satisfy the group axioms: - multiplication in
Gis associative, 1is a 2-sided identity element,- For every element
x, the elementx⁻¹is a 2-sided inverse ofx.1 2 3 4 5 6 7 8 9 10 11 12 13
/- The following code tells lean what it means for `G` to be a group. Note that `Mul` and `Inv` and `One` are also classes, and you can see their definition by control-clicking on them. -/ class MyGroup (G : Type) extends Mul G, Inv G, One G where ax_assoc : ∀ x y z : G, (x * y) * z = x * (y * z) ax_mul_one : ∀ x : G, x * 1 = x ax_one_mul : ∀ x : G, 1 * x = x ax_mul_inv : ∀ x : G, x * x⁻¹ = 1 ax_inv_mul : ∀ x : G, x⁻¹ * x = 1 #check (MyGroup)
- a multiplication operation
- Let’s define the notion of a group in lean. Recall that a group is a set
4.3 C_groups
Subgroup
In lean,
Subgroup Gis aType, defined as a structure with fieldscarrier- a subset ofG(the elements of the subgroup);mul_mem'- a proof that ifgandhare incarrierthen so isg * h;one_mem'- a proof that1is incarrier;inv_mem'- a proof that ifg ∈ carriertheng⁻¹ ∈ carrier.1 2 3 4 5 6 7 8 9 10 11 12 13 14
open Set -- Show that `{1}` is a subgroup of `G` def Trivial_subgroup : Subgroup G where carrier := {1} mul_mem' := by intro a b ha hb rw [mem_singleton_iff] at * rw [ha, hb, one_mul] one_mem' := rfl inv_mem' := by intro a ha dsimp at ha ⊢ rw [mem_singleton_iff] at * rw [ha, inv_one]
Homomorphism
If
GandHare groups, thenG →* His theTypeof group homomorphisms fromGtoH. This is astructurewith fields:toFuna functionG → H;map_mul'a proof thattoFun (g * g') = toFun g * toFun g';map_one'a proof thattoFun 1 = 1.1 2 3 4 5 6 7 8 9 10 11
/- Show that the function taking every element of `G` to `1 : H` is a homomorphism. -/ def trivial_hom : G →* H where toFun := fun _ ↦ 1 map_one' := rfl map_mul' := by intro x y dsimp exact self_eq_mul_left.mpr rfl
Isomorphism
If
GandHare groups, thenG ≃+ His theTypeof group isomorphisms fromGtoH. This is astructurewith fields:toFuna functionG → H;invFunthe inverse function of the above function;left_inva proof thatinvFunis a left inverse oftoFun;right_inva proof thatinvFunis a right inverse oftoFun;map_mul'a proof thattoFun (g * g') = toFun g * toFun g'.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/- Prove that if there is a surjective homomorphism `φ : G →+ ℤ` then `G` is isomorphic to `φ.ker × ℤ`. -/ lemma equiv_prod_of_ontoInt [AddCommGroup G] (φ : G →+ ℤ) (hφ : Function.Surjective (φ : G → ℤ)) : φ.ker × ℤ ≃+ G := by specialize hφ 1 set g := hφ.choose with g_def have g_spec := hφ.choose_spec exact { toFun := fun ⟨x,n⟩ ↦ x + n • g invFun := fun x ↦ ⟨⟨x - φ x • g, by simp [AddMonoidHom.mem_ker, g_spec]⟩, φ x⟩ left_inv := by intro ⟨⟨_,h⟩,_⟩ rw [AddMonoidHom.mem_ker] at h dsimp ext <;> · simp [g_spec, h] right_inv := by intro; simp map_add' := by intros dsimp rw [←g_def, add_zsmul, add_assoc, add_assoc] congr 1 rw [add_comm, add_assoc] congr 1 apply add_comm }
4.4 D_simp
simpsimpis a high level tactic, which repeatedly searches for ways torwa goal in order to make it “simpler”. It doesn’t search through the whole of Mathlib, but only thelemmas andtheorems marked with the attribute@[simp]just before their statement. Suchlemmas are always equations or iff statements, in which the RHS is in some sense simpler than the LHS.- To see exactly what
simpdoes, typing this line after importing:set_option trace.Meta.Tactic.simp.rewrite true. - If we type
simp?instead ofsimp, then lean will tell us which lemmas it has used, and offer to replace yoursimpby an equivalentsimp only .... This should always be done ifsimpdoes not completely close the goal.
