Zulip Chat Archive

Stream: new members

Topic: Coercion between nat and rat


Patrick Stevens (Jun 28 2020 at 13:43):

How was I able to prove this obviously untrue statement? By which I mean, how can I phrase the statement I wanted?

lemma bad (b p : nat) : (b : ℚ) / (p : ℚ) = (((b : ℕ) / (p : ℕ)) : ℚ) := rfl

This is Obviously False: 1/2 is 1/2 in the rationals and 0 in the naturals. How do I phrase bad in such a way that rfl doesn't prove it and instead I need to add the hypothesis that p divides b?

Reid Barton (Jun 28 2020 at 13:46):

Type inference is outside in.

Reid Barton (Jun 28 2020 at 13:46):

(Generally speaking.)

Patrick Stevens (Jun 28 2020 at 13:51):

I hoped I'd be able to completely override the type inference algorithm by specifying the types of everything - I presume the problem is that the right-hand / has implicitly upcasted its two explicitly nat inputs. How can I stop it doing that? I ask because I have the following:

lemma r (p : nat) (p_prime : nat.prime p) (b : nat) (b_nonzero : b  0) (dvd : p  b) : (padic_val_nat p b) - 1 = (padic_val_nat p (b / p)) :=
begin
let e := @padic_val_rat.div p p_prime b p (nat.cast_ne_zero.mpr b_nonzero) (nat.cast_ne_zero.mpr (nat.prime.ne_zero p_prime)),
rw @padic_val_rat.padic_val_rat_self p (nat.prime.one_lt p_prime) at e,
rw  @padic_val_rat_of_nat p b at e,
--have s : (b : ℚ) / (p : ℚ) = (((b : ℕ) / (p : ℕ)) : ℚ) := rfl,
--rw ← @padic_val_rat_of_nat p (b / p) at e,
end

In scope is a term of type padic_val_rat p (↑b / ↑p) = ↑(padic_val_nat p b) - 1, and I know padic_val_rat_of_nat can turn the left-hand side into a padic_val_nat (it's currently talking about padic_val_rat) if I can make that b / p term appear as an upcasted natural, rather than a rational

Mario Carneiro (Jun 28 2020 at 13:53):

there should be a theorem nat.cast_div (but there probably isn't right now)

Reid Barton (Jun 28 2020 at 13:55):

The way to write your statement is something like (untested)
lemma bad (b p : nat) : (b / p : ℚ) = (b / p : ℕ)

Mario Carneiro (Jun 28 2020 at 13:55):

:up:

Patrick Stevens (Jun 28 2020 at 16:07):

Writing either of these functions is by a large margin the hardest thing I've experienced in Lean. My approach must be completely wrong, because it feels like Lean is fighting me as hard as it can in a way I've never experienced from a computer. It would be a lot easier if I could simply prevent it from doing any coercion locally, because then at least I could easily control what types anything had!

theorem cast_dvd {α : Type*} [semiring α] [has_div α] (m n : ) (n_dvd : n  m) (n_nonzero : n  0) : ((m / n : ) : α) = m / n :=
begin
  induction m with m ind_m,
  { cases n,
    { trivial },
    have r : (n.succ)  0 := nat.cast_ne_zero.mpr n_nonzero,
    have s : n + 1  0, by simp,
    norm_num,
    sorry,
  },
  { sorry, },
end

open padic_val_rat

lemma thing (p : nat) (p_prime : nat.prime p) (b : nat) (b_nonzero : b  0) (dvd : p  b) : (padic_val_nat p (b / p)) = (padic_val_nat p b) - 1 :=
begin
  let e := @padic_val_rat.div p p_prime b p (nat.cast_ne_zero.mpr b_nonzero) (nat.cast_ne_zero.mpr (nat.prime.ne_zero p_prime)),
  rw @padic_val_rat.padic_val_rat_self p (nat.prime.one_lt p_prime) at e,
  rw  @padic_val_rat_of_nat p b at e,
  rw <- @cast_dvd  _ _ b p dvd (nat.prime.ne_zero p_prime) at e,
  rw <- @padic_val_rat_of_nat at e,
  have r : 1  padic_val_nat p b, by sorry, -- I can do that one
-- this is nonsense
  let e : @coe   _ _ = (padic_val_nat p b : ) - (1 : ) := @nat.cast_sub  _ _ 1 (padic_val_nat p b) r,
  sorry,
end

Mario Carneiro (Jun 28 2020 at 16:26):

You can always control coercion by putting more type ascriptions in, or putting \u directly in the term where you want the coercion to appear

Mario Carneiro (Jun 28 2020 at 16:28):

in particular you can use double type ascription like ((x + y : nat) : int) to make sure a coercion gets inserted between the type ascriptions

Mario Carneiro (Jun 28 2020 at 16:30):

cast_dvd is not true in the generality you have stated for it

Mario Carneiro (Jun 28 2020 at 16:30):

the division on alpha has nothing to do with the algebraic structure

Mario Carneiro (Jun 28 2020 at 16:35):

Here's a true version of your theorem (possibly not maximally general)

theorem cast_dvd {α : Type*} [field α] (m n : ) (n_dvd : n  m) (n_nonzero : (n:α)  0) : ((m / n : ) : α) = m / n :=
begin
  rcases n_dvd with k, rfl,
  have : n  0, {rintro rfl, simpa using n_nonzero},
  rw nat.mul_div_cancel_left _ (nat.pos_iff_ne_zero.2 this),
  rw [nat.cast_mul, mul_div_cancel_left _ n_nonzero],
end

Mario Carneiro (Jun 28 2020 at 16:43):

Maybe this will help you for the second part:

lemma thing (p : nat) (p_prime : nat.prime p) (b : nat) (b_nonzero : b  0) (dvd : p  b) : (padic_val_nat p (b / p)) = (padic_val_nat p b) - 1 :=
begin
  let e := @padic_val_rat.div p p_prime b p (nat.cast_ne_zero.mpr b_nonzero) (nat.cast_ne_zero.mpr (nat.prime.ne_zero p_prime)),
  rw @padic_val_rat.padic_val_rat_self p (nat.prime.one_lt p_prime) at e,
  rw  @padic_val_rat_of_nat p b at e,
  rw <- @cast_dvd  _ b p dvd (nat.cast_ne_zero.2 $ nat.prime.ne_zero p_prime) at e,
  rw <- @padic_val_rat_of_nat at e,
  apply int.coe_nat_inj,
  rw [e, int.coe_nat_sub], {simp},
  sorry,
end

Mario Carneiro (Jun 28 2020 at 16:50):

You also don't have to provide as many arguments to the rewrites if you do them from left to right:

lemma thing (p : nat) (p_prime : nat.prime p) (b : nat) (b_nonzero : b  0) (dvd : p  b) : (padic_val_nat p (b / p)) = (padic_val_nat p b) - 1 :=
begin
  apply int.coe_nat_inj,
  have b0' : (b:)  0 := nat.cast_ne_zero.2 b_nonzero,
  have p0' : (p:)  0,
  { rw nat.cast_ne_zero, exact nat.prime.ne_zero p_prime },
  haveI : fact (nat.prime p) := p_prime,
  rw [padic_val_rat_of_nat, cast_dvd _ _ dvd p0',
    padic_val_rat.div p b0' p0',
    padic_val_rat.padic_val_rat_self p_prime.one_lt,
    int.coe_nat_sub, padic_val_rat_of_nat, int.coe_nat_one],
  sorry,
end

Mario Carneiro (Jun 28 2020 at 16:51):

@Patrick Stevens

Kevin Buzzard (Jun 28 2020 at 19:25):

You just have to learn the knack. lemma bad (b p : nat) : (b : ℚ) / (p : ℚ) = (((b : ℕ) / (p : ℕ)) : ℚ) := rfl works because Lean knows the RHS is a rational so it deduces that / is rat.div` and then it coerces b and p into Q before doing the division. You need to tell Lean the division is still happening in nat, that's what it guesses incorrectly.

Patrick Stevens (Jun 28 2020 at 21:33):

Thanks all, I'll stare at these

Kevin Buzzard (Jun 28 2020 at 21:34):

It took me a while. You go from outside in. I used to be forever checking types when I was doing this the first time

Patrick Stevens (Jun 29 2020 at 07:36):

This is what I mean when I say I don't know how to control the type of anything: I have not yet found any type annotations I can place on the last line while retaining an expression that type-checks, and I don't know what types the things on the left or right hand side of f are. When I hover over them in the Lean widget, it tells me it's coercing to Z, but when I try and annotate anything as being in Z, the nat.cast_sub stops compiling, with an error message that tells me two identical terms are not equal. (Obviously the terms aren't identical - one of them has been coerced to a different type than the other. But I have no idea what those types are.)

import data.nat.prime
import data.nat.multiplicity
import data.padics.padic_norm
import tactic
import ring_theory.multiplicity

theorem cast_dvd {α : Type*} [field α] (m n : ) (n_dvd : n  m) (n_nonzero : (n:α)  0) : ((m / n : ) : α) = m / n :=
begin
  rcases n_dvd with k, rfl,
  have : n  0, {rintro rfl, simpa using n_nonzero},
  rw nat.mul_div_cancel_left _ (nat.pos_iff_ne_zero.2 this),
  rw [nat.cast_mul, mul_div_cancel_left _ n_nonzero],
end

lemma padic_val_nat_of_quot (p : nat) (p_prime : nat.prime p) (b : nat) (b_nonzero : b  0) (dvd : p  b) : (padic_val_nat p (b / p)) = (padic_val_nat p b) - 1 :=
begin
  let e := @padic_val_rat.div p p_prime b p (nat.cast_ne_zero.mpr b_nonzero) (nat.cast_ne_zero.mpr (nat.prime.ne_zero p_prime)),
  rw @padic_val_rat.padic_val_rat_self p (nat.prime.one_lt p_prime) at e,
  rw  @padic_val_rat_of_nat p b at e,
  rw <- @cast_dvd  _ b p dvd (nat.cast_ne_zero.mpr (nat.prime.ne_zero p_prime)) at e,
  rw <- @padic_val_rat_of_nat at e,
  have r : 1  padic_val_nat p b := by sorry, -- I can do this

  have f : (padic_val_nat p b - 1) = (padic_val_nat p b) - 1 := @nat.cast_sub  _ _ _ _ r,
end

The aim is to append these lines to get a u that type-checks, but currently f.symm is of the wrong type (although the error message does not help me work out what that type is):

  have s : (padic_val_nat p b : ) - 1 = (padic_val_nat p b) - 1, by congr,
  rw <- s at e,
  let u := eq.trans e (f.symm),

Patrick Stevens (Jun 29 2020 at 07:40):

Ah, I found an unhelpful type annotation I can place without breaking things: I can note that either of the 1s is a nat. But that doesn't tell me what anything has been coerced to.

Scott Morrison (Jun 29 2020 at 08:11):

I don't have long to look at this now, but the fact that you are writing by hand seems like a bad idea. Always write (x : T) when you want to make coercions.

Scott Morrison (Jun 29 2020 at 08:12):

What do you want f to say?

Patrick Massot (Jun 29 2020 at 08:16):

When I copy-paste your code I get unknown identifier 'padic_val_nat'.

Scott Morrison (Jun 29 2020 at 08:17):

Hmm... I don't!

Patrick Massot (Jun 29 2020 at 08:18):

Oops, I needed to restart Lean after updating.

Patrick Massot (Jun 29 2020 at 08:19):

Now I can ask "What do you want f to say?".

Scott Morrison (Jun 29 2020 at 08:20):

So if you replace the last line with

  have f : ((padic_val_nat p b - 1 : ) : ) = (padic_val_nat p b : ) - ((1 : ) : ) := by convert @nat.cast_sub  _ _ _ _ r,

Scott Morrison (Jun 29 2020 at 08:20):

you at least get a manageable error message

Patrick Massot (Jun 29 2020 at 08:20):

But at least I can finish the proof: exact_mod_cast e.

Scott Morrison (Jun 29 2020 at 08:20):

:-)

Scott Morrison (Jun 29 2020 at 08:22):

I would advise trusting in Patrick's answer, but if you want to dig deeper you'll have to work out why there are two different ways the coercion is happening. :-(

Scott Morrison (Jun 29 2020 at 08:22):

The unsolved goal from my last line is coe_base = nat.cast_coe, ... which isn't a good sign!

Patrick Massot (Jun 29 2020 at 08:38):

I did a bit of cleanup, but now I should work instead:

import data.nat.prime
import data.nat.multiplicity
import data.padics.padic_norm
import tactic
import ring_theory.multiplicity

theorem cast_dvd {α : Type*} [field α] {m n : } (n_dvd : n  m) (n_nonzero : (n:α)  0) : ((m / n : ) : α) = m / n :=
begin
  rcases n_dvd with k, rfl,
  have : n  0, {rintro rfl, simpa using n_nonzero},
  rw nat.mul_div_cancel_left _ (nat.pos_iff_ne_zero.2 this),
  rw [nat.cast_mul, mul_div_cancel_left _ n_nonzero],
end

@[norm_cast]
theorem cast_dvd_char_zero {α : Type*} [field α] [char_zero α ] {m n : } (n_dvd : n  m) : ((m / n : ) : α) = m / n :=
begin
  by_cases hn : n = 0,
  { subst hn,
    simp },
  exact cast_dvd n_dvd (nat.cast_ne_zero.mpr hn),
end

lemma padic_val_nat_of_quot {p : } (p_prime : nat.prime p) {b : } (b_nonzero : b  0) (dvd : p  b) :
  (padic_val_nat p (b / p)) = (padic_val_nat p b) - 1 :=
begin
  haveI : fact p.prime := p_prime,
  have e := padic_val_rat.div p (nat.cast_ne_zero.mpr b_nonzero) (nat.cast_ne_zero.mpr (nat.prime.ne_zero p_prime)),
  rw padic_val_rat.padic_val_rat_self (nat.prime.one_lt p_prime) at e,
  have r : 1  padic_val_nat p b := sorry, -- I can do this
  exact_mod_cast e,
end

Patrick Stevens (Jun 29 2020 at 11:50):

Scott Morrison said:

I don't have long to look at this now, but the fact that you are writing by hand seems like a bad idea. Always write (x : T) when you want to make coercions.

Ah OK, thanks - I was essentially copy-pasting out of the goal window at that point; just wanted to construct terms that I could rw by to make progress

Patrick Stevens (Jun 29 2020 at 11:51):

Thanks again all, I will stare harder at these examples

Kevin Buzzard (Jun 29 2020 at 11:59):

I often find myself deleting arrows which I've cut and pasted from goal windows


Last updated: Dec 20 2023 at 11:08 UTC