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