Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proved QHat.rat_meet_zHat and QHat.rat_join_zHat. #161

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

DjangoPeeters
Copy link

@DjangoPeeters DjangoPeeters commented Oct 4, 2024

Proved QHat.rat_meet_zHat and QHat.rat_join_zHat.

@DjangoPeeters DjangoPeeters changed the title Proved QHat.rat_meet_zHat Proved QHat.rat_meet_zHat and QHat.rat_join_zHat. Oct 5, 2024
Copy link
Contributor

@Ruben-VandeVelde Ruben-VandeVelde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, but since you asked for feedback, here's some things I might change. Please don't let the wall of text demotivate you; I'm just hoping you might find my suggestions interesting.

lemma rat_meet_zHat : ratsub ⊓ zHatsub = zsub := by
apply le_antisymm
· intro x ⟨⟨l, hl⟩, ⟨r, hr⟩⟩
simp at hl hr; rcases lowestTerms x with ⟨⟨N, z, hNz, hx⟩, unique⟩
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We avoid using simp without only in the middle of a proof:

Suggested change
simp at hl hr; rcases lowestTerms x with ⟨⟨N, z, hNz, hx⟩, unique⟩
simp only [AddMonoidHom.coe_coe, Algebra.TensorProduct.includeLeft_apply,
Algebra.TensorProduct.includeRight_apply] at hl hr
rcases lowestTerms x with ⟨⟨N, z, hNz, hx⟩, unique⟩

Comment on lines 329 to 335
have : PNat.val l.den.toPNat' = l.den := by simp [l.den_pos]
simp [IsCoprime]; rw [this]
have hlp := (ZMod.isUnit_iff_coprime l.num.natAbs l.den).2 l.reduced
let m := l.num; have hm : l.num = m := by rfl
rcases m with m | m
· simp [hm] at *; exact hlp
· simp [hm] at *; rw [← neg_add, add_comm]; exact IsUnit.neg hlp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you add

lemma ZMod.isUnit_natAbs {z : ℤ} {N : ℕ} : IsUnit (z.natAbs : ZMod N) ↔ IsUnit (z : ZMod N) := by
  cases z.natAbs_eq with
  | inl h | inr h => rw [h]; simp [-Int.natCast_natAbs]

then you can rewrite this as

Suggested change
have : PNat.val l.den.toPNat' = l.den := by simp [l.den_pos]
simp [IsCoprime]; rw [this]
have hlp := (ZMod.isUnit_iff_coprime l.num.natAbs l.den).2 l.reduced
let m := l.num; have hm : l.num = m := by rfl
rcases m with m | m
· simp [hm] at *; exact hlp
· simp [hm] at *; rw [← neg_add, add_comm]; exact IsUnit.neg hlp
simp_rw [IsCoprime, ZHat.intCast_val, ← ZMod.isUnit_natAbs, ZMod.isUnit_iff_coprime,
PNat.toPNat'_coe l.den_pos]
exact l.reduced

That's a lot of changes at one. I:

  1. Replaced the simp [IsCoprime] by the output of simp? [IsCoprime]

  2. Noted that the cases split didn't use anything specific about l.num, and tried to extract the general statement into the lemma above. That left me with rw [ZMod.isUnit_natAbs] at hlp; exact hlp to finish the proof.

  3. Looked at hlp and noticed that I could move the ZMod.isUnit_iff_coprime application out: have hlp := l.reduced; rw [← ZMod.isUnit_iff_coprime, ZMod.isUnit_natAbs] at hlp; exact hlp

  4. Rather than working on hlp until it matched the goal, I worked on the goal until it matched hlp: rw [← ZMod.isUnit_natAbs, ZMod.isUnit_iff_coprime]; have hlp := l.reduced; exact hlp

  5. Suspect that this should be provable with a single lemma, and found it by searching loogle for Nat.toPNat'

  6. This got me to

      simp only [IsCoprime, ZHat.intCast_val]
      rw [PNat.toPNat'_coe l.den_pos]
      rw [← ZMod.isUnit_natAbs, ZMod.isUnit_iff_coprime]
      exact l.reduced

at which point I collapsed everything into a single simp_rw, though I had to move PNat.toPNat'_coe l.den_pos to the end for a reason I didn't investigate.

rcases m with m | m
· simp [hm] at *; exact hlp
· simp [hm] at *; rw [← neg_add, add_comm]; exact IsUnit.neg hlp
have cop2 : IsCoprime 1 r := ⟨default, by simp [ZMod.instUnique.uniq]⟩
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's best to avoid using instance names explicitly. This could be:

Suggested change
have cop2 : IsCoprime 1 r := ⟨default, by simp [ZMod.instUnique.uniq]
have cop2 : IsCoprime 1 r := ⟨default, by simp only [PNat.val_ofNat]; exact Subsingleton.elim _ _

or also

Suggested change
have cop2 : IsCoprime 1 r := ⟨default, by simp [ZMod.instUnique.uniq]⟩
have cop2 : IsCoprime 1 r := by
simp only [IsCoprime, PNat.val_ofNat]
exact isUnit_of_subsingleton _

mul_comm, ← zsmul_eq_mul, TensorProduct.smul_tmul, zsmul_eq_mul, mul_one]
rw [← PNat.toPNat'_coe l.den_pos, hx] at hcanon
have h := unique _ _ _ _ ⟨hNz, cop1, hcanon⟩
have : 1 = 1/(@Nat.cast ℚ Rat.instNatCast (PNat.val 1 : ℕ) : ℚ) := by rfl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can write the messy cast like this:

Suggested change
have : 1 = 1/(@Nat.cast ℚ Rat.instNatCast (PNat.val 1 : ℕ) : ℚ) := by rfl
have : 1 = 1 / (((1 : ℕ+) : ℕ) : ℚ) := by simp

and it's somehow an accident that rfl works, so I used simp instead

nth_rw 1 [← hl, ← Rat.num_div_den l, ← mul_one ((l.num : ℚ) / l.den), div_mul_comm,
mul_comm, ← zsmul_eq_mul, TensorProduct.smul_tmul, zsmul_eq_mul, mul_one]
rw [← PNat.toPNat'_coe l.den_pos, hx] at hcanon
have h := unique _ _ _ _ ⟨hNz, cop1, hcanon⟩
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A trick: since the result of unique is two equalities of variables, you can substitute them out with

Suggested change
have h := unique _ _ _ _ ⟨hNz, cop1, hcanon⟩
obtain ⟨rfl, rfl⟩ := unique _ _ _ _ ⟨hNz, cop1, hcanon⟩

(and then a bit of work below)

have h := unique _ _ _ _ ⟨hNz, cop1, hcanon⟩
have : 1 = 1/(@Nat.cast ℚ Rat.instNatCast (PNat.val 1 : ℕ) : ℚ) := by rfl
nth_rw 1 [← hx, ← h.1, ← h.2, ← hr, this] at hcanon
use l.num; rw [hx, h.2, (unique N 1 z r ⟨hNz, cop2, hcanon.symm⟩).1]; norm_num
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

norm_num isn't the greatest tactic to use here; I'd suggest instead

Suggested change
use l.num; rw [hx, h.2, (unique N 1 z r ⟨hNz, cop2, hcanon.symm⟩).1]; norm_num
use l.num; rw [hx, h.2, (unique N 1 z r ⟨hNz, cop2, hcanon.symm⟩).1]
simp only [AddMonoidHom.coe_coe, eq_intCast, PNat.val_ofNat, Nat.cast_one, ne_eq, one_ne_zero,
not_false_eq_true, div_self]

Comment on lines 345 to 347
calc
↑l.num = i₂ ↑l.num := by simp
_ = 1 ⊗ₜ[ℤ] ↑l.num:= by rfl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again the fact that rfl works is somewhat of an accident, but you could use this instead:

Suggested change
calc
↑l.num = i₂ ↑l.num := by simp
_ = 1 ⊗ₜ[ℤ] ↑l.num:= by rfl
calc
↑l.num = i₂ ↑l.num := by simp
_ = 1 ⊗ₜ[ℤ] ↑l.num:= by simp only [i₂, Algebra.TensorProduct.includeRight_apply]

You could also use

Suggested change
calc
↑l.num = i₂ ↑l.num := by simp
_ = 1 ⊗ₜ[ℤ] ↑l.num:= by rfl
rw [← map_intCast i₂, i₂, Algebra.TensorProduct.includeRight_apply]

or even

Suggested change
calc
↑l.num = i₂ ↑l.num := by simp
_ = 1 ⊗ₜ[ℤ] ↑l.num:= by rfl
rw [← map_intCast Algebra.TensorProduct.includeRight, Algebra.TensorProduct.includeRight_apply]

But then you could also add a lemma

@[simp]
lemma one_tmul_intCast {z : ℤ} : (1 : ℚ) ⊗ₜ[ℤ] (z : ZHat) = z := by
  rw [← map_intCast Algebra.TensorProduct.includeRight, Algebra.TensorProduct.includeRight_apply]

and you could replace the norm_num; calc entirely by simp.

Or you could even notice the lemma holds more generally:

@[simp]
lemma _root_.Algebra.TensorProduct.one_tmul_intCast {R : Type*} {A : Type*} {B : Type*}
    [CommRing R] [Ring A] [Algebra R A] [Ring B] [Algebra R B] {z : ℤ} :
    (1 : A) ⊗ₜ[R] (z : B) = (z : TensorProduct R A B) := by
  rw [← map_intCast Algebra.TensorProduct.includeRight, Algebra.TensorProduct.includeRight_apply]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the rfl an accident here? I found it funny and very useful when writing the proof lol.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes things are "accidentally true by definition". And then if the definition changes, they're no longer accidentally true by definition and the proof breaks. It's better to have a more robust proof.

Comment on lines 351 to 352
apply le_antisymm
simp; intro x _
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a dedicated lemma for this:

Suggested change
apply le_antisymm
simp; intro x _
rw [eq_top_iff]
intro x _

simp; intro x _
rcases x.canonicalForm with ⟨N, z, hNz⟩
rcases ZHat.nat_dense N z with ⟨q, r, hz, _⟩
have h : z - r = N * q := by simp [hz]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also possible, not necessarily better:

Suggested change
have h : z - r = N * q := by simp [hz]
have h : z - r = N * q := sub_eq_of_eq_add hz

Comment on lines 357 to 360
use ((r : ℤ) / N : ℚ) ⊗ₜ[ℤ] 1
constructor; simp; use 1 ⊗ₜ[ℤ] q
constructor; simp; nth_rw 1 [← mul_one ((r : ℤ) / N : ℚ), div_mul_comm,
mul_comm, ← zsmul_eq_mul, TensorProduct.smul_tmul, zsmul_eq_mul, mul_one]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer focusing dots to make it clear what goal you're working on:

Suggested change
use ((r : ℤ) / N : ℚ) ⊗ₜ[ℤ] 1
constructor; simp; use 1 ⊗ₜ[ℤ] q
constructor; simp; nth_rw 1 [← mul_one ((r : ℤ) / N : ℚ), div_mul_comm,
mul_comm, ← zsmul_eq_mul, TensorProduct.smul_tmul, zsmul_eq_mul, mul_one]
use ((r : ℤ) / N : ℚ) ⊗ₜ[ℤ] 1
constructor
· simp
use 1 ⊗ₜ[ℤ] q
constructor
· simp
nth_rw 1 [← mul_one ((r : ℤ) / N : ℚ), div_mul_comm,
mul_comm, ← zsmul_eq_mul, TensorProduct.smul_tmul, zsmul_eq_mul, mul_one]

or you could use:

Suggested change
use ((r : ℤ) / N : ℚ) ⊗ₜ[ℤ] 1
constructor; simp; use 1 ⊗ₜ[ℤ] q
constructor; simp; nth_rw 1 [← mul_one ((r : ℤ) / N : ℚ), div_mul_comm,
mul_comm, ← zsmul_eq_mul, TensorProduct.smul_tmul, zsmul_eq_mul, mul_one]
refine ⟨((r : ℤ) / N : ℚ) ⊗ₜ[ℤ] 1, by simp, 1 ⊗ₜ[ℤ] q, by simp, ?_⟩
nth_rw 1 [← mul_one ((r : ℤ) / N : ℚ), div_mul_comm,
mul_comm, ← zsmul_eq_mul, TensorProduct.smul_tmul, zsmul_eq_mul, mul_one]

@Ruben-VandeVelde
Copy link
Contributor

And please also add \leanok after \begin{proof} for both lemmas in blueprint/src/chapter/ch05automorphicformexample.tex

@DjangoPeeters
Copy link
Author

Done.

@DjangoPeeters
Copy link
Author

Ruben, I think your 2nd lemma gave an error...

@kbuzzard
Copy link
Collaborator

Thanks a lot for your work on this! Can you resolve the issues which have been dealt with so I can see what's going on? Sorry, I've been dealing with real life for most of this week but now I've got more time for FLT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants