import GlimpseOfLean.Library.Basic
import Mathlib.Data.Complex.Exponential
open Real
Computing
The ring tactic
One of the earliest kind of proofs one encounters while learning mathematics is proving by
a calculation. It may not sound like a proof, but this is actually using lemmas expressing
properties of operations on numbers. Of course we usually don't want to invoke those explicitly
so mathlib has a tactic ring
taking care of proving equalities that follow by applying
the properties of all commutative rings.
example (a b c : ℝ) : (a * b) * c = b * (a * c) := by
ring
It's your turn, replace the word sorry below by a proof. In this case the proof is just ring
.
After you prove something, you will see a small "No goals" message, which is the indication that
your proof is finished.
example (a b : ℝ) : (a+b)^2 = a^2 + 2*a*b + b^2 := by
-- sorry
ring
-- sorry
In the first example above, take a closer look at where Lean displays parentheses.
The ring
tactic certainly knows about associativity of multiplication, but sometimes
it is useful to understand that binary operation really are binary and an expression like
a*b*c
is read as (a*b)*c
by Lean and the fact that this is equal to a*(b*c)
is a
lemma that is used by the ring
tactic when needed.
The rewriting tactic
Let us now see how to compute using assumptions relating the involved numbers.
This uses the fundamental property of equality: if two
mathematical objects A and B are equal then, in any statement involving A, one can replace A
by B. This operation is called rewriting, and the basic Lean tactic for this is rw
.
Carefully step through the proof below and try to understand what is happening.
example (a b c d e : ℝ) (h : a = b + c) (h' : b = d - e) : a + e = d + c := by
rw [h]
rw [h']
ring
Note the rw
tactic changes the current goal. After the first line of the above proof,
the new goal is b + c + e = d + c
. So you can read this first proof step as saying:
"I wanted to prove, a + e = d + c
but, since assumption h
tells me a = b + c
,
it suffices to prove b + c + e = d + c
."
The rw
tactic needs to be told every rewrite step. Later on we will see more powerful tactics
that can automate the tedious steps for you.
One can actually do several rewritings in one command.
example (a b c d e : ℝ) (h : a = b + c) (h' : b = d - e) : a + e = d + c := by
rw [h, h']
ring
Note that putting your cursor between h
andh'
shows you the intermediate proof state.
Note also the subtle background color change in the tactic state that show you in green what is new and in red what is about to change.
Now try it yourself. Note that ring can still do calculations,
but it doesn't use the assumptions h
and h'
example (a b c d : ℝ) (h : b = d + d) (h' : a = b + c) : a + b = c + 4 * d := by
-- sorry
rw [h', h]
ring
-- sorry
Rewriting with a lemma
In the previous examples, we rewrote the goal using a local assumption. But we can
also use lemmas. For instance let us prove a lemma about exponentiation.
Since ring
only knows how to prove things from the axioms of rings,
it doesn't know how to work with exponentiation.
For the following lemma, we will rewrite twice with the lemma
exp_add x y
, which is a proof that exp(x+y) = exp(x) * exp(y)
.
example (a b c : ℝ) : exp (a + b + c) = exp a * exp b * exp c := by
rw [exp_add (a + b) c]
rw [exp_add a b]
Note also that after the second rw
the goal becomes
exp a * exp b * exp c = exp a * exp b * exp c
and Lean immediately declares the proof is done.
If we don't provide arguments to the lemmas, Lean will rewrite the first matching subexpression. In our example this is good enough. Sometimes more control is needed.
example (a b c : ℝ) : exp (a + b + c) = exp a * exp b * exp c := by
rw [exp_add, exp_add]
Let's do an exercise, where you also have to use
exp_sub x y : exp(x-y) = exp(x) / exp(y)
and exp_zero : exp 0 = 1
.
Recall that a + b - c
means (a + b) - c
.
You can either use ring
or rewrite with mul_one x : x * 1 = x
to simplify the denominator on the
right-hand side.
example (a b c : ℝ) : exp (a + b - c) = (exp a * exp b) / (exp c * exp 0) := by
-- sorry
rw [exp_sub, exp_add, exp_zero, mul_one]
-- sorry
Rewriting from right to left
Since equality is a symmetric relation, we can also replace the right-hand side of an
equality by the left-hand side using ←
as in the following example.
example (a b c d e : ℝ) (h : a = b + c) (h' : a + e = d + c) : b + c + e = d + c := by
rw [← h, h']
Whenever you see in a Lean file a symbol that you don't see on your keyboard, such as ←, you can put your mouse cursor above it and learn from a tooltip how to type it. In the case of ←, you can type it by typing "\l ", so backslash-l-space.
Note this rewriting from right to left story is all about sides in the equality you want to
use, not about sides in what you want to prove. The rw [← h]
in the previous example
replaced the right-hand side by the left-hand side, so it looked for b + c
in the current
goal and replaced it with a
.
example (a b c d : ℝ) (h : a = b + b) (h' : b = c) (h'' : a = d) : b + c = d := by
-- sorry
rw [← h', ← h, ← h'']
-- sorry
Rewriting in a local assumption
We can also perform rewriting in an assumption of the local context, using for instance
rw [exp_add x y] at h
in order to replace exp(x + y)
by exp(x) * exp(y)
in assumption h
.
The exact
tactic allows you to give an explicit proof term to prove the current goal.
example (a b c d : ℝ) (h : c = d*a + b) (h' : b = d) : c = d*a + d := by
rw [h'] at h
-- Our assumption `h` is now exactly what we have to prove
exact h
Calculation layout using calc
What is written in the above example is very far away from what we would write on
paper. Let's now see how to get a more natural layout (and also return to using ring
instead of explicit lemma invocations).
After each :=
below, the goal is to prove equality with the preceding line
(or the left-hand on the first line).
Carefully check you understand this by putting your cursor after each by
and looking
at the tactic state.
example (a b c d : ℝ) (h : c = b*a - d) (h' : d = a*b) : c = 0 := by
calc
c = b*a - d := by rw [h]
_ = b*a - a*b := by rw [h']
_ = 0 := by ring
Let's do some exercises using calc
.
example (a b c : ℝ) (h : a = b + c) : exp (2 * a) = (exp b) ^ 2 * (exp c) ^ 2 := by
calc
exp (2 * a) = exp (2 * (b + c)) := by
inline sorry
rw [h]/- inline sorry -/
_ = exp ((b + b) + (c + c)) := by
inline sorry
ring/- inline sorry -/
_ = exp (b + b) * exp (c + c) := by
inline sorry
rw [exp_add]/- inline sorry -/
_ = (exp b * exp b) * (exp c * exp c) := by
inline sorry
rw [exp_add, exp_add]/- inline sorry -/
_ = (exp b) ^ 2 * (exp c)^2 := by
inline sorry
ring/- inline sorry -/
From a practical point of view, when writing such a proof, it is sometimes convenient to: * pause the tactic state view update in VScode by clicking the Pause icon button in the top right corner of the Lean Infoview panel. * write the full calculation, ending each line with ":= ?_" * resume tactic state update by clicking the Play icon button and fill in proofs.
The underscores should be placed below the left-hand-side of the first line below the calc
.
Aligning the equal signs and :=
signs is not necessary but looks tidy.
example (a b c d : ℝ) (h : c = d*a + b) (h' : b = a*d) : c = 2*a*d := by
-- sorry
calc
c = d*a + b := by rw [h]
_ = d*a + a*d := by rw [h']
_ = 2*a*d := by ring
-- sorry
Congratulations, this is the end of your first exercise file! You've seen what typing
a Lean proof looks like and have learned about the following tactics:
* ring
* rw
* exact
* calc