- What Lean is
- Getting started
- Handing a finite check to
decide - Counting with
Finset - An upper bound from a single injection
- Series and inequalities
- Having Lean check a certificate
- It passed — but does it say what you meant?
- How to read a statement
- What is realistically too heavy for Lean
- Common pitfalls
- Glossary
- How Lean works
How to read a statement — when you are handed "checked in Lean"
A result handed over as "checked in Lean" can be confirmed on the receiving end. Four things are left to the machine, and four are for you to read. The first half takes a few minutes; the substance is in the second half.
- When you are handed "checked in Lean"
- ① Re-run the check with your own hands
- ② Search for the words that mark a hole
- ③ Look at the list of axioms
- ④ Read the statement of the main theorem and follow every definition
- ⑤ Make a list of the hypotheses
- ⑥ Are controls in place?
- ⑦ Is there a table of the boundary between Lean, paper and computation?
- Checklist
When you are handed "checked in Lean"
When you receive a result described as "checked in Lean", there are seven things you can confirm. The first four are left to the machine and are over in a few minutes. The last three are for you to read, and they are the substance.
The order matters. The first half looks at "is the proof really closed?", the second half at "is the closed statement the problem you were asking about?". It is quite normal for the first half to pass and the second half not to, and the four types this takes are listed in 08.
① Re-run the check with your own hands
Unpack the bundle and run the check through on your own machine. The Lean verification bundle comes with verify.sh, which sets the parallelism from the machine's RAM and CPU count, runs lake exe cache get and the whole build in one go, and finally summarises the list of axioms and the number of holes. Unpack and run that one script; that is all.
When you want to look at one particular file, the fastest way is to hand a self-contained single file (the concatenated version), which depends on no other file, straight to lake env lean. The concatenated version reads nothing but the one line import Mathlib, so that single file is known to close on its own.
$ time lake env lean PerfectFamily.lean 'PerfectFamily.cube3_bool' does not depend on any axioms 'PerfectFamily.dir_count' depends on axioms: [propext, Quot.sound] ...(22 more lines: the `#print axioms` output looked at in ③) real 0m7.367s $ echo $? 0
The exit code is not the only thing to look at. Lean can emit warnings even when there are no errors, and among the warnings are things that matter in later steps (a use of sorry, a deprecated name). Confirm that there is not a single line of error or warning. In the run above, what appears is only the output of #print axioms; errors and warnings were 0 lines, and the exit code was 0.
If the check does not pass, suspect the environment first. The versions of Lean and mathlib are pinned by the bundle's lean-toolchain and lake-manifest.json, so if the versions match, the results agree. The ways a version mismatch makes things fail are collected at the end of 11 Common pitfalls.
② Search for the words that mark a hole
Lean has ways of writing "put the proof off until later", and a theorem that uses them still passes the check. Search the whole source mechanically.
grep -rn -E 'sorry|admit|native_decide|nativeDecide|ofReduceBool' .
| Word | What it does | How to read it if it is there |
|---|---|---|
sorry | Leaves the proof blank. That declaration depends on an axiom called sorryAx | The theorem is not proved. Nor is anything that depends on it |
admit | Another name for the same thing as sorry | The same |
native_decide | Runs the decision as compiled code and trusts the result. Lean's kernel does not re-examine the computation | What has to be trusted widens from Lean's kernel to the compiler and the runtime. The axiom Lean.ofReduceBool appears |
A theorem that uses sorry also shows up as a warning. Put one in as a trial and check, and this comes out.
warning: declaration uses `sorry`
This warning is a single line, and in a long output it gets overlooked. The grep of ② and the axiom check of ④ exist to look at the same thing from another side.
③ Look at the list of axioms
#print axioms theorem-name lists what the theorem ultimately depends on. It is the result of following the whole dependency tree, so a hole anywhere along the way necessarily shows up.
#print axioms PerfectFamily.no_perfectZ_of_five
The output looks like this.
'PerfectFamily.no_perfectZ_of_five' depends on axioms: [propext, Quot.sound]
There is a single standard for reading it.
| What appears | Meaning |
|---|---|
propext, Classical.choice, Quot.sound | The three standard axioms of Lean and mathlib. Ordinary mathematics is built on top of them. Whether all three appear, only some, or none is a matter of whether the proof used classical logic or quotient types; it is not a matter of strength |
sorryAx | There is a hole. The theorem is not proved |
Lean.ofReduceBool | It went through native_decide. A computation outside the kernel is being trusted |
| Anything else | A custom axiom has been added. Read what was added |
A clean axiom column guarantees nothing about the statement meaning anything. A theorem with a false hypothesis, or a universal statement over the empty set, passes within the three standard axioms (real examples in 08 §04). This step looks only at "is the proof closed?".
When there are several headline theorems, keep one file that is nothing but a list of #print axioms, and every check will print all the axioms. In the bundle, ChkAll.lean plays that role.
④ Read the statement of the main theorem and follow every definition
From here on is the substance. Do not read the theorem's name; read the statement. Then open every definition that appears in the statement, one by one, and match it against the definition on paper.
As an example, take a theorem about families of edges of the hypercube. The statement is this.
theorem no_perfectZ_of_five {d : ℕ} (hd : 5 ≤ d) (J : Fin d → W d → Bool) : ¬ PerfectZ J
What can be read off is only "for d ≥ 5, PerfectZ is satisfied by no J". Without knowing what PerfectZ is, this theorem says nothing. Open it.
/--
`J μ x`: the edge from `x` towards `x + e_μ` is in the family.
`PerfectZ J`: for every plaquette `(μ, ν, x)` (`μ ≠ ν`), exactly one of its 4 edges
`(μ, x)`, `(μ, x+e_ν)`, `(ν, x)`, `(ν, x+e_μ)` is in `J`.
-/
def PerfectZ (J : Fin d → W d → Bool) : Prop :=
∀ μ ν : Fin d, μ ≠ ν → ∀ x : W d,
(J μ x).toNat + (J μ (shift x ν)).toNat
+ (J ν x).toNat + (J ν (shift x μ)).toNat = 1
What has to be matched here is whether "the 4 edges of the plaquette" are the same 4 edges as on paper. Look at the four terms one at a time. shift x ν is a definition not yet opened, so open that too.
def shift (x : W d) (i : Fin d) : W d := Function.update x i (x i + 1)
It is the function that takes x and increases only its i-th coordinate by 1. So the four terms are: out of x, the edge in direction μ; out of x + e_ν, the edge in direction μ; out of x, the edge in direction ν; and out of x + e_μ, the edge in direction ν — these are the 4 edges of the square in the plane (μ, ν). It agrees with the definition on paper.
Look inside W d as well.
/-- The vertices of `ℤ^d`. -/ abbrev W (d : ℕ) : Type := Fin d → ℤ
It is the infinite lattice. No period is imposed, so the theorem includes families that are not periodic. This is a point that bears directly on the strength of the statement, and you cannot see it without looking at the type. There is a separate theorem stating the same "perfect family" on the periodic lattice Fin d → ZMod L, and that one and this one are different theorems.
This sequence is the procedure.
The procedure for following definitions
- Start from the statement of the main theorem. Do not read the name
- List the definitions (
def,structure,abbrev) that appear in the statement - Open them one by one. If another definition appears inside what you opened, open that too. Go down until you reach built-in types and mathlib definitions
- At the bottom, match each one against the definition on paper. Look at four things: the count, the direction, the boundary (is equality included?), and the type (finite or infinite? is there a period?)
- If a difference comes up, decide whether that difference makes the conclusion stronger or weaker
Following this procedure is what turns up the type in 08 §02, "two definitions under the same word". Even when the names of the definitions are the same, if what you find inside them differs, they are different theorems.
⑤ Make a list of the hypotheses
What is lined up to the left of the : in the statement are the hypotheses. For each one, decide who satisfies it.
| Kind of hypothesis | Example | How to treat it |
|---|---|---|
| Conditions on types | [Fintype S], [NeZero L], [DecidableEq E] | A declaration that the object has this shape. Not a hypothesis that was pushed out |
| Conditions on the objects | 2 ∣ L, 5 ≤ d, ‖U e‖ = 1 | The theorem's range of application. Outside that range it says nothing |
| Mathematics that was pushed out | h6 : ∀ e, (…).card = 6 | This is where it matters. Confirm whether some other theorem supplies this, or nothing does |
The third kind is the crux. Pushing the awkward part out into a hypothesis and closing the rest is a normal way to proceed, but if nobody satisfies the hypothesis that was pushed out, the conclusion cannot be used yet. 08 §05 has a real instance where the hypotheses go away across three stages of theorems stating the same conclusion.
There is one way to tell. Is there, in the same bundle, a theorem giving a concrete example that satisfies that hypothesis? If there is, the push-out has been resolved; if not, it has not.
⑥ Are controls in place?
A decision that returned "it holds" under decide may be a decision that simply lets everything through. Controls are what rule this out.
| What is put in | What it rules out | |
|---|---|---|
| Positive control | An example with concrete values that satisfy the hypotheses substituted in | The statement being vacuously true |
| Negative control | A theorem that puts in something which must not pass and confirms that it fails | The decision being so coarse that it lets everything through |
For a negative control, look for whether something that breaks the correct answer in just one place is included. If only the empty one and the full one fail, a coarse decision procedure would still pass. Real examples are in 08 §04 (b).
If there is not a single control, that does not mean the result is wrong, but the receiving end will have to write its own controls and confirm. It is only a matter of feeding extreme inputs to the decision function and running decide, so a few lines will do.
⑦ Is there a table of the boundary between Lean, paper and computation?
A body of results ought to come with a table saying which grade each row is. When there is no table, the following three have been handed over mixed together.
Leanmachine-checked. Look for a theorem name attached. A row with no name gives you no means of confirming it. papera proof exists but has not been machine-checked. Look for a statement of how far the paper part reaches. computationthe range confirmed by computation on that machine. Look for it not having been made into a claim for the outside.
The point when looking at the table is to find the seam between the Lean rows and the paper rows. If the seam is right before the conclusion, the conclusion is of paper grade. 08 §06 has two examples, both written so that the seam is visible in one place.
One more thing: look for whether the row "the enumeration is exhaustive" is in the table. In a result that lines up candidates and knocks them down one by one, the candidates lined up being all of them is a separate grade. This row is easily dropped, and when it is, the whole upper bound looks machine-checked (10 §03).
Checklist
Left to the machine (a few minutes)
- Re-run the check with your own hands. For a concatenated version, a single
lake env lean 〈file〉. Look not only for exit code 0 but for the output being empty (no warnings either) grep -rn -E 'sorry|admit|native_decide|nativeDecide|ofReduceBool' .returns 0 hits#print axioms 〈main theorem〉stays withinpropext,Classical.choice,Quot.sound. NeithersorryAxnorLean.ofReduceBoolappears- The versions of Lean and mathlib match
lean-toolchainandlake-manifest.json
Read (this is the substance)
- Follow every definition in the statement of the main theorem down to the built-ins and mathlib. Match the count, the direction, the boundary and the type against the definitions on paper
- List the hypotheses. Sort them into conditions on types, range of application, and mathematics that was pushed out; confirm who satisfies the third
- Are there positive and negative controls? Does the negative control include "the correct answer broken in just one place"?
- Is there a table of the boundary between Lean, paper and computation? Do the Lean rows have theorem names attached? Is the row "the enumeration is exhaustive" not missing?
The commonest shape is that 1–4 pass and somewhere in 5–8 does not. The result is then not wrong; rather, the statement is not what you thought it was. The type of the difference falls under one of the four in 08.
Next: 10 When Lean is too heavy — the background for reading, on the receiving end, "why is this row still paper?". Terms are in the 12 Glossary.