Zulip Chat Archive
Stream: Berkeley Lean Seminar
Topic: Massot Exercise 0037
Calvin McPhail-Snyder (Jun 12 2020 at 15:53):
I had a question about exercise 0037 in the Massot tutorial. The solution is
-- A sequence admits at most one limit
-- 0037
example : seq_limit u l → seq_limit u l' → l = l' :=
begin
intros hyp,
intros hyp',
apply eq_of_abs_sub_le_all,
intros ε ε_pos,
cases hyp (ε/2) (by linarith) with N hN,
cases hyp' (ε/2) (by linarith) with N' hN',
calc |l - l'| = |(l - u (max N N')) + (u (max N N') - l')| : by ring
... ≤ |l - u (max N N')| + |u (max N N') - l'| : by apply abs_add
... ≤ |u (max N N') - l| + |u (max N N') - l'| : by rw abs_sub
... ≤ ε : by linarith [hN (max N N') (le_max_left _ _), hN' (max N N') (le_max_right _ _)]
end
I mostly get why this works, but I don't really understand what's happening in the last line here: what's that syntax?
I think I would better understand a solution along the lines of
-- A sequence admits at most one limit
-- 0037
example : seq_limit u l → seq_limit u l' → l = l' :=
begin
intros hyp,
intros hyp',
apply eq_of_abs_sub_le_all,
intros ε ε_pos,
cases hyp (ε/2) (by linarith) with N hN,
cases hyp' (ε/2) (by linarith) with N' hN',
let n := max N N'
calc |l - l'| = |(l - u n) + (u n) - l')| : by ring
... ≤ |l - u n| + |u n - l'| : by apply abs_add
... ≤ |u n - l| + |u n - l'| : by rw abs_sub
... ≤ ε/2 + ε/2 : by linarith [ ??? ]
... ≤ ε : by ring
end
but I couldn't get this working: you need to add lemmas that n is bigger than N and N', and I got lost doing this; I'm also not quite sure how to feed those lemmas to linarith or similar. Is the awkwardness of carrying around these extra lemmas why the solution is in that form? Or is that a valid style and I just don't know quite how to do it?
Pedro Minicz (Jun 12 2020 at 16:10):
Its not inconvenient at all to have those extra hypothesis imo. Specially because they are just specialized versions of hN
and hN'
.
example : seq_limit u l → seq_limit u l' → l = l' :=
begin
intros hyp,
intros hyp',
apply eq_of_abs_sub_le_all,
intros ε ε_pos,
cases hyp (ε/2) (by linarith) with N hN,
cases hyp' (ε/2) (by linarith) with N' hN',
let n := max N N',
specialize hN n (le_max_left _ _),
specialize hN' n (le_max_right _ _),
calc |l - l'| = |(l - u n) + ((u n) - l')| : by ring
... ≤ |l - u n| + |u n - l'| : by apply abs_add
... ≤ |u n - l| + |u n - l'| : by rw abs_sub
... ≤ ε/2 + ε/2 : by linarith
... ≤ ε : by ring
end
Pedro Minicz (Jun 12 2020 at 16:11):
You could have also given them directly to linarith
:
example : seq_limit u l → seq_limit u l' → l = l' :=
begin
intros hyp,
intros hyp',
apply eq_of_abs_sub_le_all,
intros ε ε_pos,
cases hyp (ε/2) (by linarith) with N hN,
cases hyp' (ε/2) (by linarith) with N' hN',
let n := max N N',
calc |l - l'| = |(l - u n) + ((u n) - l')| : by ring
... ≤ |l - u n| + |u n - l'| : by apply abs_add
... ≤ |u n - l| + |u n - l'| : by rw abs_sub
... ≤ ε/2 + ε/2 : by linarith [hN n (le_max_left _ _), hN' n (le_max_right _ _)]
... ≤ ε : by ring
end
Pedro Minicz (Jun 12 2020 at 16:12):
You can give extra lemmas for linarith
to work with by listing them inside []
.
Pedro Minicz (Jun 12 2020 at 16:12):
linarith
will then use these extra lemmas as if they were in the context.
Pedro Minicz (Jun 12 2020 at 16:15):
Essentially: have h := ..., linarith
is effectively the same as linarith [...]
.
Pedro Minicz (Jun 12 2020 at 16:19):
Note that this is the case with multiple tactics. For example, simp
by itself it will only simplify a handful of lemmas, however, if you want to to use more you can use simp [lemma_1, lemma_2]
.
Patrick Lutz (Jun 12 2020 at 16:46):
Part of what's going on here is also that linarith
is capable of doing a small amount of arithmetic, like knowing that if and then . So the ring
on the last line is not necessary
Calvin McPhail-Snyder (Jun 12 2020 at 16:54):
Thanks! That clarified things.
Calvin McPhail-Snyder (Jun 12 2020 at 16:56):
Here the underscore syntax is some sort of anonymous function?
Pedro Minicz (Jun 12 2020 at 17:18):
Lean will try to figure out what to put _
. I could have also been explicit: le_max_left N N'
.
Pedro Minicz (Jun 12 2020 at 17:18):
I find that in some cases its way better to use _
, and if you do too I think you will also eventually become frustrated that Lean can't infer even more!
Pedro Minicz (Jun 12 2020 at 17:20):
A notable example I recently ran into was and_not_self (mk (quotient s) * mk ↥s ≤ mk (quotient s) * mk ↥s)
, which (in my context) could be reduced to the much more legible and_not_self _
Kevin Buzzard (Jun 12 2020 at 17:44):
@Calvin McPhail-Snyder you can switch between term and tactic mode at any time. I think set
can be easier to use than let
sometimes. Here is the solution you posted with set
instead of let
, and your ???
replaced by some sorrys:
example : seq_limit u l → seq_limit u l' → l = l' :=
begin
intros hyp,
intros hyp',
apply eq_of_abs_sub_le_all,
intros ε ε_pos,
cases hyp (ε/2) (by linarith) with N hN,
cases hyp' (ε/2) (by linarith) with N' hN',
set n := max N N' with hn,
calc |l - l'| = |(l - u n) + (u n - l')| : by ring
... ≤ |l - u n| + |u n - l'| : by apply abs_add
... ≤ |u n - l| + |u n - l'| : by rw abs_sub
... ≤ ε/2 + ε/2 : by linarith [hN n begin sorry end, hN' n begin sorry end ]
... ≤ ε : by ring
end
You can just fill in those sorrys in tactic mode, they're independent little puzzles. The advantage of set
is that the hypothesis n = max N N'
has a name now. It's true by definition, but this can be intimidating for beginners.
Kevin Buzzard (Jun 12 2020 at 17:46):
Once you've convinced yourself that exact le_max left _ _
solves the first sorry, you can replace begin exact X end
with X
, and this is what Patrick did.
Kyle Miller (Jun 12 2020 at 18:57):
Kevin Buzzard said:
The advantage of
set
is that the hypothesisn = max N N'
has a name now. It's true by definition, but this can be intimidating for beginners.
One thread I've been slowly trying to pull is to understand why let
definitions aren't picked up by linarith
. Mario mentioned it was only recently that a tactic could even see the definition of something, so there's that. I could see there being some sort of performance penalty, but not in the adding of the equalities to the context: rfl
of definitionally equal things should be basically free. From perusing the code, linarith
looks for all inequalities and breaks them up into linear factors ("atoms"), and by default it doesn't try too hard to check whether different atoms are the same.
While I'm somewhat willing to make Lean happy, I'd rather it be the other way around.
(I thought I read that there was going to be a big change to linarith
in the next few weeks, once someone got back, but I can't find it again.)
Kevin Buzzard (Jun 12 2020 at 19:07):
I'm not a computer scientist, but my impression is the following. I don't understand what linarith
is doing, but I know for sure that if you just give it straight hypotheses h1 : a < b
h2 : b < c
h3 : c < 5
and then have goal a < 10
then linarith
just works. And then you start wondering why it can't solve a <= max a b
because that's also some trivial linear inequality, right? Or why it can't see through let
bindings. So I went through a phase of basically reporting "bug reports" of the form "linarith
should do this, right? It's trivial" and believe you me, the CS community here has bent over backwards to accommodate my requests for tactics to make life easier for mathematicians -- but when it came to requests like this, it was explained to me that what I was doing was basically asking for "mission creep". linarith
does what it does, and does it well, but it doesn't do things like digging into let bindings or applying le_max_left or abs_le etc -- there is some sort of basic principle of computer science I think, and these requests to start asking for extra functionality are somehow going against this principle.
Kevin Buzzard (Jun 12 2020 at 19:11):
import tactic
example (a b : ℕ) (h : a ≤ b) : a ≤ b :=
begin
let n := a,
change n ≤ b, -- n = a by definition so `change` works
linarith -- still works
end
It does seem to be able to see through let binders. It's stuff like le_max_left
and abs_le
which it doesn't know.
Kyle Miller (Jun 12 2020 at 19:11):
I think I've already accepted linarith
not being able to prove anything like a <= max a b
. I also know that mission creep is exactly "why can't it just do this one extra thing?" But, this is "in the current context, turn the definitions into equalities as if you had used set
."
Kyle Miller (Jun 12 2020 at 19:11):
It doesn't seem to see things like that if its let n := a + 1
rather than just a variable. I'll have to check again.
Kevin Buzzard (Jun 12 2020 at 19:12):
Can you show me the explicit example where let
is failing and set
fixes it? linarith is doing great with random simple examples obfuscated by let
, for me
Kevin Buzzard (Jun 12 2020 at 19:14):
There are changes to linarith
on the cards -- go to the mathlib github page, click on "pull requests" and then search for linarith. You can see the current state of the pull requests and what people are saying about them. Rob Lewis is the guy who wrote it and who was away for a while recently.
Kyle Miller (Jun 12 2020 at 19:18):
example (a b : ℤ) (h : a ≤ b) : a ≤ b :=
begin
let n := a,
have h : n ≤ b + 1, linarith, -- OK
end
example (a b : ℤ) (h : a ≤ b) : a ≤ b :=
begin
let n := a + 1,
have h : n ≤ b + 1, linarith, -- not OK
end
example (a b : ℤ) (h : a ≤ b) : a ≤ b :=
begin
set n := a + 1 with eq,
have h : n ≤ b + 1, linarith, -- OK
end
Kyle Miller (Jun 12 2020 at 19:23):
Kevin Buzzard said:
There are changes to
linarith
on the cards -- go to the mathlib github page
Thanks for directing me to the PRs. I'll keep an eye open, and once it's in maybe I'll try to figure out how to modify linarith
to deal with let
definitions and see if it causes anything to go wrong.
Kyle Miller (Jun 12 2020 at 19:26):
Here are the examples, a bit less contrived:
example (a b : ℤ) (h : a ≤ b) : a ≤ b :=
begin
let n := a,
change n ≤ b,
linarith, -- OK
end
example (a b : ℤ) (h : a ≤ b) : a + 1 ≤ b + 1 :=
begin
let n := a + 1,
change n ≤ b + 1,
linarith, -- not OK
end
example (a b : ℤ) (h : a ≤ b) : a + 1 ≤ b + 1 :=
begin
set n := a + 1 with eq,
change n ≤ b + 1,
linarith, -- OK
end
Kevin Buzzard (Jun 12 2020 at 19:40):
Because I don't understand let
, I fear that asking Rob/Mario to make this work is precisely one of these "just one more thing" requests. It's not even clear to me that a PR would be welcome. If you understand more about computers than I do then feel free to engage them on the topic over in #maths, they'll happily try and explain the issues to you I'm sure. For all I know it is a reasonable request. Once when this came up Rob wrote set
for me, and I've been using set
ever since.
Kyle Miller (Jun 12 2020 at 19:50):
Thanks for pushing for set
-- it's definitely a nice quality of life improvement.
I think the reason we've been noticing this is because of all the limit proofs in Massot's tutorials. You frequently need to specialize some limit at some particular epsilon, and before we knew about set
we would avoid let
definitions altogether because linarith
wouldn't be able to fill in the positivity proof:
set ε := (u n - l)/2 with εeq,
specialize h ε (by linarith),
Kevin Buzzard (Jun 12 2020 at 20:02):
A lot of the enhancements made by Rob and Mario and people Rob has supervised have come from people just trying to do maths in #maths, running into problems, asking for help, and then these people have selflessly made tools for us. We have norm_num
because I was whinging that I couldn't solve (4 : real) < 5. Then I started getting undergraduates interested and they ran into problems with algebra, so Mario wrote ring
, and then they ran into problems with moving freely from nat
to int
and so Rob supervised a student to write norm_cast
. I have had an extremely positive experience with these CS people. Whenever we run into problems, they try to help us out. This is one of the reasons that I've stuck around -- we run into problems when trying to do mathematics, and they solve them for us. I think one of the reasons it works like this is that writing tactics in Lean is perhaps not too hard to do if you know what you're doing -- or perhaps these people are looking for publications -- who knows. But if you are trying to do mathematics and running into problems, #maths is the place to go.
Calvin McPhail-Snyder (Jun 12 2020 at 21:07):
@Kevin Buzzard I didn't know about set
and I think my frustration was caused by trying to use let
instead. Thanks!
Patrick Lutz (Jun 12 2020 at 21:07):
I think set
was written after the tutorials were made right?
Patrick Lutz (Jun 12 2020 at 21:09):
At some point when I was doing the tutorials I kept writing things like have : \exists N, N \ge N1 \and N \ge N2 := begin use max N1 N2, ....
and then doing cases with the resulting statement to get around this issue
Patrick Lutz (Jun 12 2020 at 21:09):
learning about set
was pretty nice after that
Last updated: Dec 20 2023 at 11:08 UTC