Zulip Chat Archive

Stream: general

Topic: Only two sorries remain for Banach-Tarski


Arthur Tilley (Dec 31 2025 at 22:27):

Hi everyone,

The first sorry is mentioned in a previous post (see this mwe).

The second is much simpler to state. The following should be a mwe.

import Mathlib

abbrev SO3 := Matrix.specialOrthogonalGroup (Fin 3) 

abbrev MAT:= Matrix (Fin 3) (Fin 3) 

noncomputable def as_complex (M : MAT) : Matrix (Fin 3) (Fin 3)  := (algebraMap  ).mapMatrix M

noncomputable def cpoly (g : SO3) := Matrix.charpoly (as_complex g.val)

lemma idlem (g : SO3) :  (cpoly g).roots.count 1 = 3  g = 1 := by
  sorry

I have tried two approaches here

I think the usual approach would be to use the fact that unitary matrices are diagonalizable, and in this case the diagonal matrix will be the identity matrix. But I can’t seem to find any theorems about diagonalizability in Mathlib, and I'm curious whether I'm missing something.

Another approach is to show that the geometric multiplicity of 1 is equal to the algebraic multiplicity. I found one theorem that gets us this but it requires that g (or the endomorphism corresponding to g) is Semisimple. But this seems to require showing that the minimal polynomial is X-1, and if I knew that I’d be done already.

(https://github.com/aetilley/banach-tarski)

Happy New Year,
Arthur

Aaron Hill (Jan 01 2026 at 02:14):

One possible approach:

Aaron Hill (Jan 01 2026 at 02:18):

it might also be useful to have that maxGenEigenSpace = eigenspace for a IsStarNormal linear map (which should include Matrix.specialOrthogonalGroup). I have an aristotle-generated proof of this here: https://github.com/Aaron1011/mathlib4/blob/1a06f50ee097ae52bafb8cf4d0c683b0a64b8078/Mathlib/Analysis/Matrix/StarNormalEigen.lean#L140

Li Xuanji (Jan 01 2026 at 03:46):

I threw this into Aristotle out of curiosity and it closed the goal. The key idea seems to be to use the explicit formula for the determinant of a 3x3 matrix to get an explicit formula for the characteristic polynomial.

import Mathlib

set_option maxHeartbeats 0
set_option linter.style.longLine false

abbrev SO3 := Matrix.specialOrthogonalGroup (Fin 3) 

abbrev MAT:= Matrix (Fin 3) (Fin 3) 

noncomputable def as_complex (M : MAT) : Matrix (Fin 3) (Fin 3)  := (algebraMap  ).mapMatrix M

noncomputable def cpoly (g : SO3) := Matrix.charpoly (as_complex g.val)

lemma idlem (g : SO3) :  (cpoly g).roots.count 1 = 3  g = 1 := by
  -- Since the only real roots of $P_g$ are $\pm 1$, and $P_g$ has three roots, they must all be $1$. Hence, $g$ is the identity matrix.
  have h_roots : (Matrix.charpoly (as_complex g.val)).roots = {1, 1, 1}  g = 1 := by
    have h_roots : (Matrix.charpoly (as_complex g.val)).roots = {1, 1, 1}  Matrix.charpoly (as_complex g.val) = (Polynomial.X - Polynomial.C 1) ^ 3 := by
      intro h_roots
      have h_poly : (Matrix.charpoly (as_complex g.val)).roots = {1, 1, 1} := by
        exact h_roots;
      rw [  Polynomial.prod_multiset_X_sub_C_of_monic_of_roots_card_eq ( show Polynomial.Monic ( Matrix.charpoly ( as_complex g.val ) ) from ?_ ) ];
      · norm_num [ h_poly ];
        ring;
      · aesop;
      · exact Matrix.charpoly_monic _;
    intro h; specialize h_roots h; simp_all +decide [ Matrix.charpoly, Matrix.det_fin_three ] ;
    -- By comparing coefficients, we can see that $g$ must be the identity matrix.
    have h_coeff :  i j, (g.val i j : ) = if i = j then 1 else 0 := by
      -- By comparing coefficients of $X^2$ on both sides of the equation, we can derive that the sum of the diagonal elements of $g$ must be 3.
      have h_diag_sum : (g.val 0 0 : ) + (g.val 1 1 : ) + (g.val 2 2 : ) = 3 := by
        have h₁ := congr_arg ( Polynomial.eval 0 ) h_roots; have h₂ := congr_arg ( Polynomial.eval 1 ) h_roots; have h₃ := congr_arg ( Polynomial.eval ( -1 ) ) h_roots; norm_num [ Complex.ext_iff ] at *; linarith!;
      -- Since $g$ is orthogonal, we have $g^T g = I$.
      have h_orthogonal : (g.val.transpose * g.val : Matrix (Fin 3) (Fin 3) ) = 1 := by
        have := g.2.1; simp_all +decide [ Matrix.mul_eq_one_comm ] ;
        exact this.2;
      -- Since $g$ is orthogonal, we have $g^T g = I$. Therefore, the sum of the squares of the entries in each row is 1.
      have h_row_squares :  i, (g.val i 0 : )^2 + (g.val i 1 : )^2 + (g.val i 2 : )^2 = 1 := by
        intro i; have := congr_fun ( congr_fun h_orthogonal i ) i; simp_all +decide [ Matrix.mul_apply, Fin.sum_univ_three ] ;
        have := congr_fun ( congr_fun ( show ( g.val * g.val.transpose : Matrix ( Fin 3 ) ( Fin 3 )  ) = 1 from by simpa [ Matrix.mul_eq_one_comm ] using h_orthogonal ) i ) i; simp_all +decide [ Matrix.mul_apply, Fin.sum_univ_three ] ; ring_nf at *; aesop;
      -- Since the sum of the squares of the entries in each row is 1 and the sum of the diagonal elements is 3, each diagonal element must be 1.
      have h_diag_one :  i, (g.val i i : ) = 1 := by
        norm_cast at *; simp_all +decide [ Fin.forall_fin_succ ] ;
        refine'  _, _, _  <;> nlinarith only [ h_diag_sum, h_row_squares, sq_nonneg ( ( g.val 0 0 :  ) - 1 ), sq_nonneg ( ( g.val 1 1 :  ) - 1 ), sq_nonneg ( ( g.val 2 2 :  ) - 1 ) ];
      simp_all +decide [ Fin.forall_fin_succ ];
      exact   by nlinarith only [ h_row_squares.1 ], by nlinarith only [ h_row_squares.1 ] ,  by nlinarith only [ h_row_squares.2.1 ], by nlinarith only [ h_row_squares.2.1 ] , by nlinarith only [ h_row_squares.2.2 ], by nlinarith only [ h_row_squares.2.2 ] ⟩;
    ext i j; specialize h_coeff i j; aesop;
  -- Since the polynomial is of degree 3, if 1 is a root with multiplicity 3, then the polynomial must be $(x-1)^3$.
  have h_poly_form : (Matrix.charpoly (as_complex g.val)).roots = {1, 1, 1}  Multiset.count 1 (Matrix.charpoly (as_complex g.val)).roots = 3 := by
    have h_poly_form : (Matrix.charpoly (as_complex g.val)).degree = 3 := by
      simp +decide [ Matrix.charpoly_degree_eq_dim ];
    -- Since the polynomial is of degree 3, if 1 is a root with multiplicity 3, then the polynomial must be $(x-1)^3$. Hence, the roots must be exactly {1, 1, 1}.
    have h_poly_form : (Matrix.charpoly (as_complex g.val)).roots = {1, 1, 1}  Multiset.count 1 (Matrix.charpoly (as_complex g.val)).roots = 3 := by
      have h_poly_form : (Matrix.charpoly (as_complex g.val)).roots = {1, 1, 1}  Multiset.count 1 (Matrix.charpoly (as_complex g.val)).roots = 3 := by
        aesop
      have h_poly_form' : Multiset.count 1 (Matrix.charpoly (as_complex g.val)).roots = 3  (Matrix.charpoly (as_complex g.val)).roots = {1, 1, 1} := by
        intro h_count
        have h_poly_form' : Multiset.card (Matrix.charpoly (as_complex g.val)).roots = 3 := by
          have h_poly_form' : Multiset.card (Matrix.charpoly (as_complex g.val)).roots  3 := by
            exact le_trans ( Polynomial.card_roots' _ ) ( Polynomial.natDegree_le_of_degree_le <| le_of_eq ‹_› );
          exact le_antisymm h_poly_form' ( by linarith [ Multiset.count_le_card 1 ( Matrix.charpoly ( as_complex g.val ) |> Polynomial.roots ) ] );
        have h_poly_form' :  x  (Matrix.charpoly (as_complex g.val)).roots, x = 1 := by
          intro x hx; contrapose! h_count; simp_all +decide ;
          have h_poly_form' : Polynomial.rootMultiplicity 1 (Matrix.charpoly (as_complex g.val)) + Polynomial.rootMultiplicity x (Matrix.charpoly (as_complex g.val))  3 := by
            have h_poly_form' : Polynomial.rootMultiplicity 1 (Matrix.charpoly (as_complex g.val)) + Polynomial.rootMultiplicity x (Matrix.charpoly (as_complex g.val))  Multiset.card (Matrix.charpoly (as_complex g.val)).roots := by
              have h_poly_form' : Polynomial.rootMultiplicity 1 (Matrix.charpoly (as_complex g.val)) + Polynomial.rootMultiplicity x (Matrix.charpoly (as_complex g.val))  Multiset.card (Multiset.filter (fun y => y = 1) (Matrix.charpoly (as_complex g.val)).roots) + Multiset.card (Multiset.filter (fun y => y = x) (Matrix.charpoly (as_complex g.val)).roots) := by
                rw [ Multiset.filter_eq', Multiset.filter_eq' ] ; aesop;
              refine le_trans h_poly_form' ?_;
              rw [  Multiset.card_add ] ; exact Multiset.card_le_card <| Multiset.le_iff_count.mpr fun y => by by_cases hy : y = 1 <;> by_cases hy' : y = x <;> aesop;
            linarith;
          linarith [ show Polynomial.rootMultiplicity x ( Matrix.charpoly ( as_complex g.val ) ) > 0 from Nat.pos_of_ne_zero ( by aesop ) ];
        exact Multiset.eq_replicate.mpr  by assumption, h_poly_form' 
      exact h_poly_form, h_poly_form'⟩;
    exact h_poly_form;
  exact fun h => h_roots <| h_poly_form.mpr h

Arthur Tilley (Jan 01 2026 at 19:16):

Aaron Hill said:

it might also be useful to have that maxGenEigenSpace = eigenspace for a IsStarNormal linear map (which should include Matrix.specialOrthogonalGroup). I have an aristotle-generated proof of this here: https://github.com/Aaron1011/mathlib4/blob/1a06f50ee097ae52bafb8cf4d0c683b0a64b8078/Mathlib/Analysis/Matrix/StarNormalEigen.lean#L140

Your lemma star_normal_maxGenEigenspace_eq_eigenspace appears to be exactly what I need, but it it timing out after 3 million heartbeats. Is this expected?

(I just grabbed it and the lemma above that it depends on an pasted them into a file. Not sure if this is ok. I didn't see you setting the maxHeartbeats so maybe I'm doing something wrong.)

EDIT: Ok, I decided to use the Aristotle proof for now (for another reason that I leave as a comment in the most recent commit). Although I hope to revisit this one day.

This means there is Only One Sorry remaining for the project and that is the issue of flattening bases / matrices that I outline here

(I'll suppress the urge to tag general with a new post, but I'll update the linked thread.)

Violeta Hernández (Jan 03 2026 at 08:23):

Aristotle can often generate very verbose and slow proofs. But it shouldn't be too hard to clean them up and golf them.

Arthur Tilley (Jan 03 2026 at 19:39):

Thanks @Violeta Hernández

The Aristotle code that @Li Xuanji provided is running fairly quickly. The lemma that was timing out is, I think, not Aristotle code, but I could have misunderstood Aaron. (I'll also note that I've noticed that a timeout in one place can be due to an error in another, so I might have made a mistake elsewhere.)

Anyway, I'm happy to say that the project is now sorry-free.

Li Xuanji (Jan 04 2026 at 04:47):

In case you want a less computational proof of idlem, I generalized it to n to force Aristotle to avoid using the matrix entries.

https://gist.github.com/ldct/19c9058719ab7076c9b590e994ca40ee

(Zulip seems to have some trouble with the size of the file, so I threw it up on Github)


Last updated: Feb 28 2026 at 14:05 UTC