Zulip Chat Archive
Stream: new members
Topic: refer to R to prove things in N?
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 22:38):
I'm proving that there is no natural number such that 2n = 1. One way of doing this would be to point out that multiplication by a non-zero constant is injective, and that multiplication in R generalises multiplication in N, and since we already have 2*(1/2) = 1, this would imply n = 1/2, which is not a natural number.
Kenny Lau (Oct 11 2018 at 22:39):
and yet you asked us about why you need universes to prove fermat's last theorem :P
Mario Carneiro (Oct 11 2018 at 22:39):
they are both valid questions
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 22:40):
My question is: Can this be formalised in Lean? How would you state "multiplication in R generalises multiplication in N"?
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 22:40):
Ok, wait, you can state that pretty easily, but how would you use that?
Kenny Lau (Oct 11 2018 at 22:40):
nat.cast_mul, I guess
Kenny Lau (Oct 11 2018 at 22:40):
but you see
Kenny Lau (Oct 11 2018 at 22:41):
how would you prove that 1/2 is not a natural number?
Mario Carneiro (Oct 11 2018 at 22:41):
There are other ways to prove that theorem, but sure you can do it that way
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 22:41):
@Kenny Lau 1/2 is not natural because 1/2 is between 0 and 1, but 1 is the successor of 0 and nothing can be between an element and its successor.
Kenny Lau (Oct 11 2018 at 22:42):
so you also want nat.cast_lt :P
Kenny Lau (Oct 11 2018 at 22:42):
can't omega
solve this? @Mario Carneiro
Kenny Lau (Oct 11 2018 at 22:44):
example : ∀ n, 2*n ≠ 1 := λ n, nat.cases_on n dec_trivial $ λ n H, nat.no_confusion $ nat.succ_inj H
Kenny Lau (Oct 11 2018 at 22:50):
example : ¬∃ n, 2*n = 1 := by simp only [eq_comm]; exact dec_trivial
Kenny Lau (Oct 11 2018 at 22:50):
example : ∀ n, 2*n ≠ 1 := λ n H, absurd (Exists.intro n H.symm) dec_trivial
Mario Carneiro (Oct 11 2018 at 22:54):
I think this is a faithful rendition of your proof:
example (n : ℕ) : 2 * n ≠ 1 := λ h, begin have : 2 * (n:ℝ) = 1, {simpa using congr_arg (coe : ℕ → ℝ) h}, rw [mul_comm, ← div_eq_iff_mul_eq (two_ne_zero : (2:ℝ)≠0)] at this, have h0 : ((0:ℕ):ℝ) < (1 / 2 : ℝ), {simp, norm_num}, have h1 : (1 / 2 : ℝ) < (1:ℕ), {simp, norm_num}, rw [this, nat.cast_lt] at h0 h1, exact not_le_of_lt h1 h0 end
Kenny Lau (Oct 11 2018 at 22:54):
```lean
Kenny Lau (Oct 11 2018 at 22:55):
Also, who needs the reals, we only need Q
Mario Carneiro (Oct 11 2018 at 22:55):
I know
Mario Carneiro (Oct 11 2018 at 22:56):
but if I did that then I wouldn't be "referring to R to prove things in N"
Kenny Lau (Oct 11 2018 at 22:56):
that's mostly a comment to him
Mario Carneiro (Oct 11 2018 at 22:56):
If you use Q instead the two lemmas h0
and h1
can be proven by dec_trivial
Mario Carneiro (Oct 11 2018 at 22:59):
what's going on in your proof? I'm surprised that dec_trivial
proves ¬∃ (n : ℕ), 1 = 2 * n
Kenny Lau (Oct 11 2018 at 22:59):
it's dvd in disguise :P
Mario Carneiro (Oct 11 2018 at 23:00):
oh whoa, didn't realize dvd
was reducible
Kenny Lau (Oct 11 2018 at 23:00):
me neither
Kenny Lau (Oct 11 2018 at 23:00):
this is the residue of a longer proof
Mario Carneiro (Oct 11 2018 at 23:01):
to answer your question, yes omega
can handle this
Mario Carneiro (Oct 11 2018 at 23:01):
I think linarith
can too if you fiddle with the statement a bit
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:02):
I think this is a faithful rendition of your proof:
example (n : ℕ) : 2 * n ≠ 1 := λ h, begin have : 2 * (n:ℝ) = 1, {simpa using congr_arg (coe : ℕ → ℝ) h}, rw [mul_comm, ← div_eq_iff_mul_eq (two_ne_zero : (2:ℝ)≠0)] at this, have h0 : ((0:ℕ):ℝ) < (1 / 2 : ℝ), {simp, norm_num}, have h1 : (1 / 2 : ℝ) < (1:ℕ), {simp, norm_num}, rw [this, nat.cast_lt] at h0 h1, exact not_le_of_lt h1 h0 end
I'm trying to unpack that, but Lean says it fails to synthesise type class instance (I fixed one bracket).
Mario Carneiro (Oct 11 2018 at 23:04):
where is the error, and where did you fix a bracket?
Mario Carneiro (Oct 11 2018 at 23:04):
you need import data.real.basic tactic.norm_num
in the header
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:05):
Yeah, I've done that.
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:05):
Wait, I put the wrong bracket. It works now.
Mario Carneiro (Oct 11 2018 at 23:08):
kenny, here is a marginally less obfuscatory (and shorter!) version of your proof:
example (n : ℕ) : 2*n ≠ 1 := λ h, (dec_trivial : ¬ 2 ∣ 1) ⟨n, h.symm⟩
Kenny Lau (Oct 11 2018 at 23:12):
import data.real.basic example (n : ℕ) : 2 * n ≠ 1 := λ h, begin have h1 : 0 < 2 * n ∧ 2 * n < 2, { rw h, exact dec_trivial }, rw [← @nat.cast_lt ℝ, ← @nat.cast_lt ℝ, nat.cast_mul] at h1, rw [nat.cast_zero, nat.cast_bit0, nat.cast_one] at h1, have h2 : 0 < (2:ℝ), { exact two_pos }, have h3 : (2:ℝ) ≠ 0, { exact ne_of_gt h2 }, rw [← div_lt_div_right h2, mul_div_cancel_left _ h3, and_comm] at h1, rw [← div_lt_div_right h2, mul_div_cancel_left _ h3, and_comm] at h1, rw [div_self h3, zero_div, ← nat.cast_zero, ← nat.cast_one] at h1, rw [nat.cast_lt, nat.cast_lt] at h1, cases h1 with h4 h5, clear h h2 h3, revert n h5, exact dec_trivial end
Kenny Lau (Oct 11 2018 at 23:12):
is this a more faithful rendition?
Kenny Lau (Oct 11 2018 at 23:12):
(sorry, can't get nat.cast_div
to work, guess it doesn't exist)
Mario Carneiro (Oct 11 2018 at 23:13):
lol, of course not
Mario Carneiro (Oct 11 2018 at 23:14):
it's not true... I'm not even sure what the statement would be
Kenny Lau (Oct 11 2018 at 23:14):
cast it to a char_zero decidable_division_ring or something like that
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:19):
import data.real.basic example (n : ℕ) : 2 * n ≠ 1 := λ h, begin have h1 : 0 < 2 * n ∧ 2 * n < 2, { rw h, exact dec_trivial }, rw [← @nat.cast_lt ℝ, ← @nat.cast_lt ℝ, nat.cast_mul] at h1, rw [nat.cast_zero, nat.cast_bit0, nat.cast_one] at h1, have h2 : 0 < (2:ℝ), { exact two_pos }, have h3 : (2:ℝ) ≠ 0, { exact ne_of_gt h2 }, rw [← div_lt_div_right h2, mul_div_cancel_left _ h3, and_comm] at h1, rw [← div_lt_div_right h2, mul_div_cancel_left _ h3, and_comm] at h1, rw [div_self h3, zero_div, ← nat.cast_zero, ← nat.cast_one] at h1, rw [nat.cast_lt, nat.cast_lt] at h1, cases h1 with h4 h5, clear h h2 h3, revert n h5, exact dec_trivial end
Ok, I'm lost here -- what exactly does nat.cast do?
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:19):
And what are the up-arrow signs lean gives in the feedback?
Kenny Lau (Oct 11 2018 at 23:20):
it's the coercion function from nat
to real
Mario Carneiro (Oct 11 2018 at 23:20):
the up arrows are nat.cast
Kenny Lau (Oct 11 2018 at 23:20):
n
is a natural number and "up-arrow n" is a real number
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:20):
Ah ok.
Mario Carneiro (Oct 11 2018 at 23:21):
and there are theorems saying ↑(n + m) = ↑n + ↑m
and so on, that's nat.cast_add
Mario Carneiro (Oct 11 2018 at 23:21):
this is how you turn a whole equation from talking about naturals to talking about reals or vice versa
Bryan Gin-ge Chen (Oct 11 2018 at 23:22):
Coercions are discussed here in TPiL.
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:22):
And nat.cast_lt means "n < m implies ↑n < ↑m"?
Mario Carneiro (Oct 11 2018 at 23:22):
it's an iff
Mario Carneiro (Oct 11 2018 at 23:22):
so you can turn a nat inequality into a real inequality or vice versa
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:24):
I see. And perhaps a ridiculously elementary question, but what exactly are the @ signs? Why does nat.cast_mul not have them while the others do?
Bryan Gin-ge Chen (Oct 11 2018 at 23:27):
@function
means that you have to provide the implicit arguments to function
. Implicit arguments are those that lean tries to infer and are denoted by curly braces.
Bryan Gin-ge Chen (Oct 11 2018 at 23:28):
See this section of TPiL.
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:29):
Oh ok, that makes sense. So e.g. if I was simplifying a calculational proof and had to simplify 1*x
to x
, then I could use @one_mul x
if one_mul
didn't work?
Kenny Lau (Oct 11 2018 at 23:29):
you would need way more arguments than x
Kenny Lau (Oct 11 2018 at 23:29):
it would be something like @one_mul \R _ x
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:30):
What does the underscore do?
Bryan Gin-ge Chen (Oct 11 2018 at 23:30):
Yeah, I forgot to say you have to provide type class arguments as well (stuff in square brackets).
Bryan Gin-ge Chen (Oct 11 2018 at 23:30):
The underscore tells lean to try to infer that argument.
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:31):
(deleted)
Kenny Lau (Oct 11 2018 at 23:31):
you know, there was a time when I tried to type something and decided that part of a sentence can be inferred from the previous part of the sentence, and subconsciously typed an underscore
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:32):
Oh wait, do you mean the brackets in the definitions themselves?
Kenny Lau (Oct 11 2018 at 23:34):
if you do #check @one_mul
then you can see all of the arguments required
Mario Carneiro (Oct 11 2018 at 23:36):
The reason nat.cast_lt
got a @
and none of the others did is because at the beginning it's not clear what we are casting to. nat.cast
actually works for a whole bunch of target types not just ℝ
. Once the up arrows are introduced they carry enough information to figure out what type we are talking about
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:40):
Ok, that makes sense. I also don't understand the bit0 stuff. Doesn't bit0 just double things? Why does it matter that the thing being casted is written as twice of something?
Kenny Lau (Oct 11 2018 at 23:44):
2
is just a notation. it really means bit0 1
Kenny Lau (Oct 11 2018 at 23:44):
try set_option pp.notations false
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:47):
Yeah I get that, but if we were dealing with 1000 instead of 2, do we need to write a series of bit1's and bit0's there? What if we were dealing with a general natural number instead of 2?
Kenny Lau (Oct 11 2018 at 23:48):
simp
will deal with it for you, I just don't really like using simp
Kenny Lau (Oct 11 2018 at 23:48):
change that to, I just really don't like using simp
Abhimanyu Pallavi Sudhir (Oct 11 2018 at 23:51):
You mean just "simp at h1"? Ok, yeah, that works.
Mario Carneiro (Oct 11 2018 at 23:51):
If we are dealing with a general natural number it is easier because it is just \u n
Mario Carneiro (Oct 11 2018 at 23:52):
The problem with \u 1000
vs 1000
is that the terms are completely different (the type of the expression is stored in all the subterms), and so we have to rewrite all the way through the term
Mario Carneiro (Oct 11 2018 at 23:55):
set_option pp.all true #check (1000:ℤ) -- @bit0.{0} int int.has_add -- (@bit0.{0} int int.has_add -- (@bit0.{0} int int.has_add -- (@bit1.{0} int int.has_one int.has_add -- (@bit0.{0} int int.has_add -- (@bit1.{0} int int.has_one int.has_add -- (@bit1.{0} int int.has_one int.has_add -- (@bit1.{0} int int.has_one int.has_add -- (@bit1.{0} int int.has_one int.has_add (@has_one.one.{0} int int.has_one))))))))) : -- int #check ((1000:ℕ):ℤ) -- @coe.{1 1} nat int (@coe_to_lift.{1 1} nat int (@coe_base.{1 1} nat int int.has_coe)) -- (@bit0.{0} nat nat.has_add -- (@bit0.{0} nat nat.has_add -- (@bit0.{0} nat nat.has_add -- (@bit1.{0} nat nat.has_one nat.has_add -- (@bit0.{0} nat nat.has_add -- (@bit1.{0} nat nat.has_one nat.has_add -- (@bit1.{0} nat nat.has_one nat.has_add -- (@bit1.{0} nat nat.has_one nat.has_add -- (@bit1.{0} nat nat.has_one nat.has_add (@has_one.one.{0} nat nat.has_one)))))))))) : -- int
Kenny Lau (Oct 12 2018 at 00:02):
The thing is, simp
is somewhat expensive, although not as expensive as norm_num
Mario Carneiro (Oct 12 2018 at 00:03):
is norm_num
actually more expensive?
Kenny Lau (Oct 12 2018 at 00:03):
oops, I was thinking about ring
Kenny Lau (Oct 12 2018 at 00:03):
I was typing one thing and thinking about another thing
Kenny Lau (Oct 12 2018 at 00:04):
Anyway, I've been replacing simp
with less expensive tactics from files in mathlib
Abhimanyu Pallavi Sudhir (Oct 12 2018 at 00:09):
Ok, I managed to work through and understand the proofs (at least the two longer ones -- will be a while before I can understand Kenny's two-line proofs). Thanks a lot! (that was my missing little lemma in proving p even if p^2 even).
Mario Carneiro (Oct 12 2018 at 00:10):
the two line proofs are easy: your theorem is by definition the same as "2 does not divide 1" and this is decidable (there is an instance in mathlib for it)
Abhimanyu Pallavi Sudhir (Oct 12 2018 at 00:10):
Why does expense/efficiency matter? You only need to run it once anyway.
Abhimanyu Pallavi Sudhir (Oct 12 2018 at 00:10):
the two line proofs are easy: your theorem is by definition the same as "2 does not divide 1" and this is decidable (there is an instance in mathlib for it)
Oh, ok. I just couldn't see through the notation.
Mario Carneiro (Oct 12 2018 at 00:11):
It doesn't matter too much, but once you start building up a huge library of maths the costs add up, and everyone who downloads mathlib will need to compile it, and mathlib itself changes on a daily basis and needs to be recompiled
Mario Carneiro (Oct 12 2018 at 00:12):
so it's not really a one time cost unless you view mathlib as fixed
Mario Carneiro (Oct 12 2018 at 00:12):
Most users can do so, but I can't since I'm a maintainer and neither can many contributors that jump between branches of mathlib
Kevin Buzzard (Oct 12 2018 at 06:50):
@Abhimanyu Pallavi Sudhir just to let you know that theorems like this (real 1/2 not the image of an integer), which are needed to do my problem sheets but which I would not expect a general undergraduate with 1 week's Lean experience to be able to do, are actually part of a small library I am writing which enables a generic UG to solve the problem sheet questions. See here https://github.com/ImperialCollegeLondon/M1F_example_sheets/tree/master/src/xenalib and in particular note the file called real_half_not_an_integer.lean
. I am trying to do these messy parts for the students so they can just use them instead of running into walls.
Abhimanyu Pallavi Sudhir (Oct 12 2018 at 06:55):
@Kevin Buzzard Oh ok -- I wasn't really doing this for a problem sheet, though.
Patrick Massot (Oct 12 2018 at 07:03):
Kevin, why do you keep up arrows in statement? Is it meant to clarify the statement for human readers?
Kevin Buzzard (Oct 12 2018 at 16:20):
@Abhimanyu Pallavi Sudhir -- ha! I assumed you were working on Q7! @Patrick Massot you mean things like ¬ ∃ n : ℤ, (1/2 : ℚ) = ↑n
? I guess so! I think the fact that Z is not a subset of Q is quite confusing for beginners. Maybe the arrow means "note: something a bit funny is going on here."
Patrick Massot (Oct 12 2018 at 16:58):
Yes, I meant that line. Lean wouldn't care if you wrote it without that up arrow
Kevin Buzzard (Oct 12 2018 at 18:09):
Right (although you'd see the arrow in the goal ;-) ).
Patrick Massot (Oct 12 2018 at 19:01):
You don't have to: set_option pp.coercions false
:wink:
Abhimanyu Pallavi Sudhir (Oct 12 2018 at 21:27):
I managed to prove the statement in a "morally similar" way but without casting to R (or Q) -- it ends up being kinda long, see theorem one_not_even
at https://github.com/abhimanyupallavisudhir/lean/blob/master/num_theo_theorems.lean
Kenny Lau (Oct 12 2018 at 21:27):
de_morgan
is not_exists
Kenny Lau (Oct 12 2018 at 21:28):
all your by norm_num
is rfl
Kevin Buzzard (Oct 12 2018 at 21:37):
We don't teach the first years complicated tactics like rfl
Kevin Buzzard (Oct 12 2018 at 21:38):
we stick to the simple ones like norm_num
Kevin Buzzard (Oct 12 2018 at 21:38):
they're a darn sight easier to learn
Simon Hudon (Oct 12 2018 at 21:58):
rfl
is not necessarily faster than norm_num
. The main idea of norm_num
is actually to do things faster than than rfl
and I think it worked out pretty well.
Kenny Lau (Oct 12 2018 at 21:58):
fair enough
Last updated: Dec 20 2023 at 11:08 UTC