Zulip Chat Archive

Stream: mathlib4

Topic: aroots


Patrick Massot (Oct 09 2023 at 02:21):

New challenge coming from the silly idea of having some concrete examples in #mil:

import Mathlib
open Polynomial Complex

example : aroots (X^2 + 1 : [X])  = {I, -I} := sorry

Patrick Massot (Oct 09 2023 at 02:22):

I'll sleep while our European number theorists work on this.

Patrick Massot (Oct 09 2023 at 02:24):

I guess they will pretend this is analysis, not number theory, so let me also ask about

import Mathlib
open Polynomial

example : aroots (2*X - 1 : [X])  = {1/2} := sorry

Patrick Massot (Oct 09 2023 at 02:46):

While we are with number theory challenges, what about

import Mathlib.RingTheory.DedekindDomain.Ideal

example {ι : Type*} [Fintype ι] (a : ι  ) (e : ι  )
  (coprime :  i j, i  j  IsCoprime (a i) (a j)) : ZMod ( i, a i) ≃+*  i, ZMod (a i) := by
  sorry

It may or may not help to start with

example {ι : Type*} [Fintype ι] (P : ι  ) (e : ι  ) (prime :  i, Prime (P i))
  (coprime :  i j, i  j  P i  P j) :
       i, (Ideal.span ({P i} : Set )) ^ e i ≃+*  i,   (Ideal.span {P i}) ^ e i := by
  apply IsDedekindDomain.quotientEquivPiOfProdEq
  all_goals { sorry }

Patrick Massot (Oct 09 2023 at 02:47):

and it may or may not help to use docs#ZMod.chineseRemainder.

Patrick Massot (Oct 09 2023 at 02:49):

This question is in the context of writing in #mil: "we have a fancy Chinese remainder theorem docs#Ideal.quotientInfRingEquivPiQuotient but of course it easily implies the stupidly elementary one, as long we notice intersection and product are the same there since integers form a Dedekind domain".

Patrick Massot (Oct 09 2023 at 02:50):

Now I remember why we never do examples or elementary versions in Mathlib.

Yongyi Chen (Oct 09 2023 at 03:04):

I actually did something similar for fun last week: proving that the roots of x2+2x+1x^2 + 2x + 1 are {1,1}\{-1,-1\} as a muiltiset.

import Mathlib
open Polynomial

noncomputable abbrev my_poly : [X] := X^2 + 2 * X + 1

example : my_poly.roots = {-1, -1} := by
  have factored : my_poly = (X + 1) * (X + 1) := by ring
  rw [factored]
  have p_ne_zero : my_poly  0 := mt (congr_arg fun p => coeff p 0) (by simp only [coeff_add, coeff_X_pow, ite_false,
    mul_coeff_zero, coeff_X_zero, mul_zero, coeff_one_zero, zero_add, coeff_zero, one_ne_zero])
  rw [roots_mul (factored  p_ne_zero)]
  have := roots_X_sub_C (-1 : )
  simp only [map_neg, map_one, sub_neg_eq_add] at this
  rw [this]
  trivial

I used roots instead of aroots though. So maybe a strategy is to base change to C\mathbb C then do something like what I did.

Yongyi Chen (Oct 09 2023 at 03:56):

Patrick Massot said:

I guess they will pretend this is analysis, not number theory, so let me also ask about

import Mathlib
open Polynomial

example : aroots (2*X - 1 : [X])  = {1/2} := sorry

My solution:

import Mathlib
import Polynomial

example : aroots (2*X - 1 : [X])  = {1/2} := by
  simp only [aroots_def, algebraMap_int_eq, Polynomial.map_sub, Polynomial.map_mul, Polynomial.map_ofNat, map_X,
    Polynomial.map_one]
  have := roots_X_sub_C (1 / 2 : )
  rw [ roots_C_mul, mul_sub,  C_mul, mul_one_div, div_self_of_invertible] at this
  all_goals trivial

Found that a forward proof is easiest because simp likes to turn C a into a : ℚ[x] which is actually harder to work with.

Yongyi Chen (Oct 09 2023 at 04:43):

Patrick Massot said:

New challenge coming from the silly idea of having some concrete examples in #mil:

import Mathlib
open Polynomial Complex

example : aroots (X^2 + 1 : [X])  = {I, -I} := sorry

example : aroots (X^2 + 1 : [X])  = {I, -I} := by
  simp only [aroots_def, Polynomial.map_add, Polynomial.map_pow, map_X, Polynomial.map_one]
  have factored : (X ^ 2 + 1 : [X]) = (X - C I) * (X - C (-I)) := by
    ring_nf
    simp only [map_neg, mul_neg, sub_self, zero_add,  C_mul, I_mul_I, neg_neg, map_one, add_comm]
  have p_ne_zero : (X ^ 2 + 1 : [X])  0 := mt (congr_arg fun p => coeff p 0) (by simp only [coeff_add,
    coeff_X_pow, ite_false, coeff_one_zero, zero_add, coeff_zero, one_ne_zero, not_false_eq_true])
  rw [factored, roots_mul (factored  p_ne_zero), roots_X_sub_C, roots_X_sub_C]
  rfl

Ruben Van de Velde (Oct 09 2023 at 05:33):

So much for leaving it to the Europeans (good morning) :)

Yongyi Chen (Oct 09 2023 at 05:41):

Good night!

Damiano Testa (Oct 09 2023 at 06:35):

Yongyi Chen said:

Patrick Massot said:

New challenge coming from the silly idea of having some concrete examples in #mil:

import Mathlib
open Polynomial Complex

example : aroots (X^2 + 1 : [X])  = {I, -I} := sorry

example : aroots (X^2 + 1 : [X])  = {I, -I} := by
  simp only [aroots_def, Polynomial.map_add, Polynomial.map_pow, map_X, Polynomial.map_one]
  have factored : (X ^ 2 + 1 : [X]) = (X - C I) * (X - C (-I)) := by
    ring_nf
    simp only [map_neg, mul_neg, sub_self, zero_add,  C_mul, I_mul_I, neg_neg, map_one, add_comm]
  have p_ne_zero : (X ^ 2 + 1 : [X])  0 := mt (congr_arg fun p => coeff p 0) (by simp only [coeff_add,
    coeff_X_pow, ite_false, coeff_one_zero, zero_add, coeff_zero, one_ne_zero, not_false_eq_true])
  rw [factored, roots_mul (factored  p_ne_zero), roots_X_sub_C, roots_X_sub_C]
  rfl

Just for didactic purposes, p_ne_zero in Yongyi Chen's proof can be split into

  have natDegree_p : natDegree (X ^ 2 + 1 : [X]) = 2 := by compute_degree!
  have p_ne_zero : (X ^ 2 + 1 : [X])  0 := by
    apply ne_zero_of_natDegree_gt (n := 1)
    linarith

Patrick Massot (Oct 09 2023 at 15:44):

Thank you very much @Yongyi Chen! We clearly need to work to make this a lot easier, but at least it's doable.

Patrick Massot (Oct 09 2023 at 18:57):

@Mario Carneiro I've heard rumors saying you're interested in merging CAS and ITP and that you need at least one new project a day. Could we have a polynomial tactic that normalizes (X - C Complex.I) * (X - C (-Complex.I)) to X^2 - C (Complex.I ^2) so that the above example becomes nice?

Mario Carneiro (Oct 09 2023 at 18:59):

I think you could probably do that using polyrith

Mario Carneiro (Oct 09 2023 at 18:59):

it's a ring equation modulo I^2 = -1

Mario Carneiro (Oct 09 2023 at 19:00):

which one is the #mwe example to simplify?

Patrick Massot (Oct 09 2023 at 19:00):

Not only modulo I^2 = -1, you also need to know that C is a ring morphism.

Patrick Massot (Oct 09 2023 at 19:00):

import Mathlib
open Polynomial Complex

example : aroots (X^2 + 1 : [X])  = {I, -I} := by
  simp only [aroots_def, Polynomial.map_add, Polynomial.map_pow, map_X, Polynomial.map_one]
  have factored : (X ^ 2 + 1 : [X]) = (X - C I) * (X - C (-I)) := by
    ring_nf
    simp only [map_neg, mul_neg, sub_self, zero_add,  C_mul, I_mul_I, neg_neg, map_one, add_comm]
  have p_ne_zero : (X ^ 2 + 1 : [X])  0 := mt (congr_arg fun p => coeff p 0) (by simp only [coeff_add,
    coeff_X_pow, ite_false, coeff_one_zero, zero_add, coeff_zero, one_ne_zero, not_false_eq_true])
  rw [factored, roots_mul (factored  p_ne_zero), roots_X_sub_C, roots_X_sub_C]
  rfl

Patrick Massot (Oct 09 2023 at 19:01):

Both haves would be nice to optimize, but here I'm talking about the first one.

Mario Carneiro (Oct 09 2023 at 19:02):

I believe something like this was going to come up in our factorization project anyway, cc: @Alex J. Best

Mario Carneiro (Oct 09 2023 at 19:02):

this was supposed to be the "easy" direction

Mario Carneiro (Oct 09 2023 at 19:04):

I guess as long as you keep the C I isolated it is possible to do it without any ring morphism reasoning

Patrick Massot (Oct 09 2023 at 19:04):

Sure. Exactly your favorite kind of project: doable in one sitting.

Patrick Massot (Oct 09 2023 at 19:05):

Mario Carneiro said:

I guess as long as you keep the C I isolated it is possible to do it without any ring morphism reasoning

I don't understand what this means.

Mario Carneiro (Oct 09 2023 at 19:06):

I mean that you can prove (X ^ 2 + 1 : ℂ[X]) = (X - C I) * (X - -(C I)) as a ring equation modulo C I * C I = -1

Patrick Massot (Oct 09 2023 at 19:08):

Sure, but then you need ring morphism reasoning to go from I*I = -1 to what you need.

Mario Carneiro (Oct 09 2023 at 19:17):

I'm saying if that theorem is just available then you can use it for a polyrith proof

Mario Carneiro (Oct 09 2023 at 19:18):

that's not an unreasonable theorem to have in the library

Mario Carneiro (Oct 09 2023 at 19:21):

  have factored : (X ^ 2 + 1 : [X]) = (X - C I) * (X - C (-I)) := by
    rw [C_neg]
    linear_combination show (C I * C I : [X]) = -1 by simp [ C_mul]

Patrick Massot (Oct 09 2023 at 19:27):

That's a nice trick but using linear_combination like this is really strange.

Patrick Massot (Oct 09 2023 at 19:31):

My current compilation of tricks to make it readable is

example : aroots (X^2 + 1 : [X])  = {I, -I} := by
  suffices roots (X ^ 2 + 1 : [X]) = {I, -I} by simpa [aroots_def]
  have factored : (X ^ 2 + 1 : [X]) = (X - C I) * (X - C (-I)) := by
    rw [C_neg]
    linear_combination show (C I * C I : [X]) = -1 by simp [ C_mul]
  have p_ne_zero : (X - C I) * (X - C (-I))  0 := by
    intro H
    apply_fun eval 0 at H
    simp [eval] at H
  simp only [factored, roots_mul p_ne_zero, roots_X_sub_C]
  rfl

Mario Carneiro (Oct 09 2023 at 19:32):

well, it's a bit golfed, the more reasonable way to write it is:

theorem C_I_mul_C_I : (C I * C I : [X]) = -1 := by simp [ C_mul]

example : aroots (X^2 + 1 : [X])  = {I, -I} := by
  simp only [aroots_def, Polynomial.map_add, Polynomial.map_pow, map_X, Polynomial.map_one]
  have factored : (X ^ 2 + 1 : [X]) = (X - C I) * (X - C (-I)) := by
    rw [C_neg]
    linear_combination C_I_mul_C_I
  sorry

Mario Carneiro (Oct 09 2023 at 19:33):

or polyrith [C_I_mul_C_I] which generates that line

Patrick Massot (Oct 09 2023 at 19:35):

I still think the new lemma shouldn't be needed when using your new tactic. Only I_mul_I should be invoked.

Mario Carneiro (Oct 09 2023 at 19:36):

this isn't so much a new tactic as an extension to ring

Mario Carneiro (Oct 09 2023 at 19:36):

working with multivariate polynomial equations is literally what ring is designed to do

Patrick Massot (Oct 09 2023 at 19:36):

Ok, granted: Your new extension of ring.

Alex J. Best (Oct 09 2023 at 20:16):

Kazuhiko Sakaguchi was telling me this week that the coq ring tactic pushes all ring homomorphisms inwards as a preprocessing step (and even does this recursively or some such)

Patrick Massot (Oct 09 2023 at 21:39):

Patrick Massot said:

While we are with number theory challenges, what about

import Mathlib.RingTheory.DedekindDomain.Ideal

example {ι : Type*} [Fintype ι] (a : ι  ) (e : ι  )
  (coprime :  i j, i  j  IsCoprime (a i) (a j)) : ZMod ( i, a i) ≃+*  i, ZMod (a i) := by
  sorry

I had enough sleep so I stopped waiting on European people:

open BigOperators Ideal

lemma IsCoprime.natCast {R : Type*} [CommSemiring R] {a b : } (h : IsCoprime a b) :
    IsCoprime (a : R) (b : R) := by
  rcases h with u, v, H
  use u, v
  rw_mod_cast [H]
  exact Nat.cast_one

theorem Ideal.iInf_span_singleton_natCast {R : Type*} [CommSemiring R] {ι : Type*} [Fintype ι]
    {I : ι  } (hI :  (i j : ι), i  j  IsCoprime (I i) (I j)) :
     (i : ι), Ideal.span {(I i : R)} = Ideal.span {(( i : ι, I i : ) : R)} := by
  rw [Ideal.iInf_span_singleton, Nat.cast_prod]
  exact fun i j h  (hI i j h).natCast

example {ι : Type*} [Fintype ι] (a : ι  ) (coprime :  i j, i  j  IsCoprime (a i) (a j)) :
    ZMod ( i, a i) ≃+*  i, ZMod (a i) :=
  have :  (i j : ι), i  j  IsCoprime (span {(a i : )}) (span {(a j : )}) :=
    fun i j h  (isCoprime_span_singleton_iff _ _).mpr ((coprime i j h).natCast (R := ))
  Int.quotientSpanNatEquivZMod _ |>.symm.trans <|
  quotEquivOfEq (iInf_span_singleton_natCast (R := ) coprime) |>.symm.trans <|
  quotientInfRingEquivPiQuotient _ this |>.trans <|
  RingEquiv.piCongrRight fun i  Int.quotientSpanNatEquivZMod (a i)

Patrick Massot (Oct 09 2023 at 21:40):

It would be easier to read without lean4#2217 of course.

Eric Wieser (Oct 09 2023 at 22:11):

#6509 contains a workaround for lean4#2217

Patrick Massot (Oct 09 2023 at 22:19):

Nice! Is there any obstacle to moving forward?

Patrick Massot (Oct 09 2023 at 22:22):

In the meantime I opened #7599 with the ZMod Chinese remainder.

Eric Wieser (Oct 09 2023 at 22:30):

Just someone's time to implement it, and the fact that you have to write both sides of the morphism every time

Eric Wieser (Oct 09 2023 at 22:31):

By both sides, I'm referring to the LHS in

  calc
    A ≃ₗ[R] B := f
    B ≃ₗ[R] C := g
    C ≃ₗ[R] D := h

Patrick Massot (Oct 09 2023 at 22:36):

Oh, that's indeed disappointing.

Eric Wieser (Oct 09 2023 at 22:38):

Maybe if you locally turn off the "refuse to find instances by unification" behavior it works

Eric Wieser (Oct 09 2023 at 22:38):

I don't remember how to do that, or whether it's actually possible in this situation

Timo Carlin-Burns (Oct 09 2023 at 22:44):

Patrick Massot said:

While we are with number theory challenges, what about

import Mathlib.RingTheory.DedekindDomain.Ideal

example {ι : Type*} [Fintype ι] (a : ι  ) (e : ι  )
  (coprime :  i j, i  j  IsCoprime (a i) (a j)) : ZMod ( i, a i) ≃+*  i, ZMod (a i) := by
  sorry

Don't you want docs#Nat.Coprime not docs#IsCoprime ? IsCoprime n m for natural numbers is only true if n or m is 1.

Patrick Massot (Oct 09 2023 at 22:46):

Oh thanks! The theorem was still true (and indeed Lean agrees, but a bit disappointing).

Timo Carlin-Burns (Oct 09 2023 at 23:41):

Here is a version for CommRings.. I don't know if the theorem is true in a CommSemiring since we can't deduce IsCoprime from Nat.Coprime

open BigOperators Ideal

lemma IsCoprime.intCast {R : Type*} [CommRing R] {a b : } (h : IsCoprime a b) :
    IsCoprime (a : R) (b : R) := by
  rcases h with u, v, H
  use u, v
  rw_mod_cast [H]
  exact Int.cast_one

lemma Nat.Coprime.cast {R : Type*} [CommRing R] {a b : } (h : Nat.Coprime a b) :
    IsCoprime (a : R) (b : R) := by
  rw [ isCoprime_iff_coprime] at h
  rw [ Int.cast_ofNat a,  Int.cast_ofNat b]
  exact IsCoprime.intCast h

theorem Ideal.iInf_span_singleton_natCast {R : Type*} [CommRing R] {ι : Type*} [Fintype ι]
    {I : ι  } (hI :  (i j : ι), i  j  Nat.Coprime (I i) (I j)) :
     (i : ι), Ideal.span {(I i : R)} = Ideal.span {(( i : ι, I i : ) : R)} := by
  rw [Ideal.iInf_span_singleton, Nat.cast_prod]
  exact fun i j h  (hI i j h).cast

noncomputable example {ι : Type*} [Fintype ι] (a : ι  ) (coprime :  i j, i  j  Nat.Coprime (a i) (a j)) :
    ZMod ( i, a i) ≃+*  i, ZMod (a i) :=
  have :  (i j : ι), i  j  IsCoprime (span {(a i : )}) (span {(a j : )}) :=
    fun i j h  (isCoprime_span_singleton_iff _ _).mpr ((coprime i j h).cast (R := ))
  Int.quotientSpanNatEquivZMod _ |>.symm.trans <|
  quotEquivOfEq (iInf_span_singleton_natCast (R := ) coprime) |>.symm.trans <|
  quotientInfRingEquivPiQuotient _ this |>.trans <|
  RingEquiv.piCongrRight fun i  Int.quotientSpanNatEquivZMod (a i)

Patrick Massot (Oct 10 2023 at 00:36):

Thanks!

Timo Carlin-Burns (Oct 10 2023 at 03:23):

Indeed, the statement is false in general for a CommSemiring. I thought it would be a fun exercise to try and prove a counterexample:

semiring counterexample

Mario Carneiro (Oct 10 2023 at 04:49):

@Patrick Massot said:

Mario Carneiro I've heard rumors saying you're interested in merging CAS and ITP and that you need at least one new project a day. Could we have a polynomial tactic that normalizes (X - C Complex.I) * (X - C (-Complex.I)) to X^2 - C (Complex.I ^2) so that the above example becomes nice?

Here it is: #7601. The linear_combination proof is still a little scuffed because it doesn't know how to make use of equalities in the wrong type (it doesn't know what ring hom to use to lift the equation), but after lifting I_mul_I ring is able to get it:

example (X : [X]) : X ^ 2 + 1 = (X - C I) * (X - C (-I)) := by
  have := congr_arg (C :   [X]) I_mul_I
  linear_combination this

Patrick Massot (Oct 10 2023 at 13:14):

It looks great but the PR doesn't build.

Patrick Massot (Oct 10 2023 at 14:07):

And the failure seem to be indeed about ring and not an unrelated timeout.

Patrick Massot (Oct 10 2023 at 14:22):

In defense of ring, this docs#odd_sq_dvd_geom_sum₂_sub is a really nasty proof. And I guess ring doesn't like docs#Finset.sum.

Patrick Massot (Oct 10 2023 at 14:22):

Even the linkifier doesn't like that lemma.

Patrick Massot (Oct 10 2023 at 14:27):

I meant this proof

Patrick Massot (Oct 10 2023 at 14:34):

It would be nice to drastically simplify this proof, but it would also be nice to make sure ring does not time out in such hostile territory.


Last updated: Dec 20 2023 at 11:08 UTC