Zulip Chat Archive

Stream: new members

Topic: Proving the roots of unity in \C are algebraic integers


Ryan Smith (Sep 10 2025 at 20:02):

Working on proving that complex characters of a finite group are algebraic integers. Shouldn't be too hard right? Digging around I found some related theorems such as IsPrimitiveRoot.isIntegral (why do we only have this for the primitive roots?).

If we don't have a theorem ready, it should be easy enough to just show that we have a root of X^n - 1 and that is monic? What is the endgame here though? I'd thought of using IsIntegral.of_aeval_monic but I'm not sure that helps since we still need to construct (hx : IsIntegral R ((aeval x) p))?

import Mathlib

universe u

open BigOperators Group ComplexConjugate
open Module Module.End
open Multiset
open Polynomial
open LinearMap

variable {G : Type} [Group G] [Finite G]

theorem root_unity_alg_int (z : ) (hz : z ^ (Nat.card G) = 1) : IsIntegral  z := by
  let p : [X] := (X : [X]) ^ (Nat.card G) - 1
  have hpos : 0 < Nat.card G := Nat.card_pos
  have hnez : Nat.card G  0 := by grind
  have hm : Monic p := monic_X_pow_sub_C (1 : ) hnez
  -- now what... IsIntegral.of_aeval_monic ?  AdjoinRoot.isIntegral_root' ?
  sorry

Ryan Smith (Sep 10 2025 at 20:06):

(deleted)

Kenny Lau (Sep 10 2025 at 20:38):

@Ryan Smith you've actually pointed out one of the (in my opinion) bigger flaws that exist: the underlying definition itself is not visible during Loogle search.

In this case, you need to look "under the definition" of IsIntegral and see that it is actually IsIntegralElem which is actually ∃ (p : Polynomial R), p.Monic ∧ Polynomial.eval₂ f x p = 0.

Kenny Lau (Sep 10 2025 at 20:38):

but even if you didn't know that, you could actually follow the path you were on, which is via IsIntegral.of_aeval_monic, because the result of aeval should be zero, and then you know that zero is integral

Kenny Lau (Sep 10 2025 at 20:41):

also, please PR this

Kenny Lau (Sep 10 2025 at 20:44):

I think @Chengyan Hu was doing something similar, you two might need to figure out what to do with the PR

Chengyan Hu (Sep 10 2025 at 20:48):

Yes it seems like we proved something similar.

Chengyan Hu (Sep 10 2025 at 20:49):

https://github.com/bourbaki120205/mathlib4/blob/project/Chengyan/Test%20copy%202.lean

Chengyan Hu (Sep 10 2025 at 20:49):

This is the link toward my previous work.

Chengyan Hu (Sep 10 2025 at 20:50):

I’m currently on holiday with my family and I’ll PR that as soon as I have time to read that long document

Ryan Smith (Sep 10 2025 at 21:42):

Thanks for the link, I hope the PR gets approved soon. This is so much shorter and more elegant than what I had.

How does this prove the lemma exactly? It looks like we're using a constructor, obviously we have the monic polynomial that \zeta satisfies as the first argument, and then the second is a proof that polynomials of this type are integral, but how does the third parameter fit in? What are we exact passing arguments to with these brackets?

lemma isIntegral_of_roots {n : } (hpos : 0 < n) {ζ : L} ( : ζ ^ n = 1) : IsIntegral  ζ :=
  X ^ n - 1, monic_X_pow_sub_C 1 (Nat.ne_of_lt hpos).symm, by simp []

Ruben Van de Velde (Sep 10 2025 at 22:14):

It's the eval_2 condition that Kenny mentions a few messages ago

Ruben Van de Velde (Sep 10 2025 at 22:15):

The brackets contain first the witness for the Exists and then the two proofs for each side of the And

Ryan Smith (Sep 10 2025 at 23:35):

I see, and the constructor separated out the And clause so we end up with three arguments.

Ryan Smith (Sep 11 2025 at 01:24):

One code question I've had for a while about arguments. This snippet now runs (yes!) but I find myself sometimes writing very long have blocks which I only ever use once and then throw away, like the way we do it here.

Is there a way to combine my last two statements (have and exact) together? It's not important for the correctness of this theorem but there's a lot of short hand in mathlib so I'm trying to understand.

theorem char_value_is_integral (V : FDRep  G) (g : G) : IsIntegral  (V.character g) := by
  rcases exists_char_eigenvalue_ms V g with s, hs
  rw [char_eq_eigenvalue_ms_sum V g s hs]
  let f :  ×    := fun t  t.1
  have :  t  s.toEnumFinset, IsIntegral  (f t) := by
    rintro μ, n 
    have heig : HasEigenvalue (V.ρ g) μ := mem_eigen_ms_eigenvalue g s hs μ (Multiset.mem_of_mem_toEnumFinset )
    have hrunity : μ ^ Nat.card G = 1 := eigenvalue_root_unity V g μ heig
    have hpos : 0 < Nat.card G := Nat.card_pos
    exact isIntegral_of_roots hpos hrunity
  exact IsIntegral.sum f this

Lawrence Wu (llllvvuu) (Sep 11 2025 at 01:56):

It's hard to say because there's no #mwe, but probably you can do something like

theorem char_value_is_integral (V : FDRep  G) (g : G) : IsIntegral  (V.character g) := by
  rcases exists_char_eigenvalue_ms V g with s, hs
  rw [char_eq_eigenvalue_ms_sum V g s hs]
  apply IsIntegral.sum
  rintro μ, n 
  apply isIntegral_of_roots
  . exact Nat.card_pos
  . apply eigenvalue_root_unity
    apply mem_eigen_ms_eigenvalue

I don't think there's anything wrong with the original style though.

Ryan Smith (Sep 11 2025 at 02:11):

That's interesting, you can apply a function without specifying the arguments and they become goals?

Lawrence Wu (llllvvuu) (Sep 11 2025 at 02:18):

yes, and check out refine for a more flexible alternative

Ryan Smith (Sep 11 2025 at 02:22):

That's really neat, when I read about refine before it made no sense at all tbh but that's really useful. One webpage describes it as "exact with holes" which makes a lot of sense

Rado Kirov (Sep 11 2025 at 03:03):

One other trick that can be useful is using the short-hand anonymous function syntax (no idea if that is what it is called in lean), if you need to provide some args but let move the rest into goals, e.g. if f needs 5 args apply (f x . y . z) will provide 3 and leave the other two. But again refine f x ?_ y ?_ z is probably more ideomatic.

Ryan Smith (Sep 11 2025 at 03:59):

Thanks, these sort of "deferred arguments" make it easier to avoid defining stuff I'll never use again.


Last updated: Dec 20 2025 at 21:32 UTC