From b324e65dd233bf97e51b4f6d6f49a57e7a3abd16 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Mon, 21 Sep 2026 18:02:29 +0000 Subject: [PATCH 1/6] Import complete euclidean-jordan proof development --- LeanPool.lean | 1 + LeanPool/EuclideanJordan.lean | 69 + LeanPool/EuclideanJordan/EuclideanJordan.lean | 59 + .../EuclideanJordan/Block.lean | 203 +++ .../EuclideanJordan/Bridge.lean | 98 + .../EuclideanJordan/Class.lean | 257 +++ .../EuclideanJordan/Connection.lean | 540 ++++++ .../EuclideanJordan/FormallyReal.lean | 120 ++ .../EuclideanJordan/Frame.lean | 176 ++ .../EuclideanJordan/FrameExists.lean | 272 +++ .../EuclideanJordan/FramePeirce.lean | 466 +++++ .../EuclideanJordan/FramePeirceMul.lean | 364 ++++ .../EuclideanJordan/HermitianBilin.lean | 73 + .../EuclideanJordan/HermitianCarrier.lean | 349 ++++ .../EuclideanJordan/Order.lean | 675 +++++++ .../EuclideanJordan/OrderAuto.lean | 493 +++++ .../EuclideanJordan/OrderUnitSpace.lean | 309 ++++ .../EuclideanJordan/Orthogonal.lean | 95 + .../EuclideanJordan/Pattern.lean | 102 ++ .../EuclideanJordan/Peirce.lean | 345 ++++ .../EuclideanJordan/PeirceMul.lean | 240 +++ .../EuclideanJordan/PeirceSubalgebra.lean | 243 +++ .../EuclideanJordan/Power.lean | 153 ++ .../EuclideanJordan/PowerAssoc.lean | 155 ++ .../EuclideanJordan/EuclideanJordan/Rank.lean | 191 ++ .../EuclideanJordan/Spectral.lean | 599 ++++++ .../EuclideanJordan/Subalgebra.lean | 158 ++ .../EuclideanJordan/TraceForm.lean | 333 ++++ .../EuclideanJordan/Vendor.lean | 20 + .../Vendor/ContinuousLinearMap.lean | 73 + .../EuclideanJordan/Vendor/HermitianMat.lean | 21 + .../Vendor/HermitianMat/Basic.lean | 771 ++++++++ .../Vendor/HermitianMat/CFC.lean | 1427 +++++++++++++++ .../Vendor/HermitianMat/Inner.lean | 516 ++++++ .../Vendor/HermitianMat/Jordan.lean | 155 ++ .../Vendor/HermitianMat/NonSingular.lean | 201 +++ .../Vendor/HermitianMat/Order.lean | 699 +++++++ .../Vendor/HermitianMat/Proj.lean | 536 ++++++ .../Vendor/HermitianMat/Reindex.lean | 112 ++ .../Vendor/HermitianMat/Trace.lean | 249 +++ .../Vendor/IsMaximalSelfAdjoint.lean | 64 + .../EuclideanJordan/Vendor/Isometry.lean | 645 +++++++ .../EuclideanJordan/Vendor/LinearEquiv.lean | 127 ++ .../EuclideanJordan/Vendor/Matrix.lean | 1599 +++++++++++++++++ .../EuclideanJordan/Vendor/Misc.lean | 183 ++ .../EuclideanJordan/Vendor/Tactic.lean | 13 + .../Vendor/Tactic/Commutes.lean | 203 +++ .../Vendor/Tactic/Commutes/Attribute.lean | 21 + .../EuclideanJordan/Witness.lean | 256 +++ .../EuclideanJordan/FramePeirceSolution.lean | 338 ++++ LeanPool/EuclideanJordan/KoecherSolution.lean | 119 ++ .../EuclideanJordan/SpectralSolution.lean | 69 + .../EuclideanJordan/StructureSolution.lean | 507 ++++++ .../EuclideanJordan/TraceFormSolution.lean | 159 ++ LeanPool/projects.yml | 38 + 55 files changed, 16259 insertions(+) create mode 100644 LeanPool/EuclideanJordan.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Block.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Class.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Order.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Power.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/ContinuousLinearMap.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/NonSingular.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/IsMaximalSelfAdjoint.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/LinearEquiv.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes/Attribute.lean create mode 100644 LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean create mode 100644 LeanPool/EuclideanJordan/FramePeirceSolution.lean create mode 100644 LeanPool/EuclideanJordan/KoecherSolution.lean create mode 100644 LeanPool/EuclideanJordan/SpectralSolution.lean create mode 100644 LeanPool/EuclideanJordan/StructureSolution.lean create mode 100644 LeanPool/EuclideanJordan/TraceFormSolution.lean diff --git a/LeanPool.lean b/LeanPool.lean index 50fa6e5b30..86e3a0c6f8 100644 --- a/LeanPool.lean +++ b/LeanPool.lean @@ -1551,6 +1551,7 @@ import LeanPool.ErdosTuzaValtr.Main.Lemmas.JoinN2N2 import LeanPool.ErdosTuzaValtr.Main.Lemmas.JoinN2N3JoinN3N2 import LeanPool.ErdosTuzaValtr.Main.Lemmas.JoinN2N3N2 import LeanPool.ErdosTuzaValtr.Main.Main +import LeanPool.EuclideanJordan import LeanPool.EvenGraphCycles import LeanPool.EventStructures import LeanPool.EventStructures.Basic diff --git a/LeanPool/EuclideanJordan.lean b/LeanPool/EuclideanJordan.lean new file mode 100644 index 0000000000..1d5fedc702 --- /dev/null +++ b/LeanPool/EuclideanJordan.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ + +import LeanPool.EuclideanJordan.EuclideanJordan +import LeanPool.EuclideanJordan.EuclideanJordan.Block +import LeanPool.EuclideanJordan.EuclideanJordan.Bridge +import LeanPool.EuclideanJordan.EuclideanJordan.Class +import LeanPool.EuclideanJordan.EuclideanJordan.Connection +import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +import LeanPool.EuclideanJordan.EuclideanJordan.Frame +import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul +import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin +import LeanPool.EuclideanJordan.EuclideanJordan.HermitianCarrier +import LeanPool.EuclideanJordan.EuclideanJordan.Order +import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto +import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace +import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal +import LeanPool.EuclideanJordan.EuclideanJordan.Pattern +import LeanPool.EuclideanJordan.EuclideanJordan.Peirce +import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul +import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra +import LeanPool.EuclideanJordan.EuclideanJordan.Power +import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc +import LeanPool.EuclideanJordan.EuclideanJordan.Rank +import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra +import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes.Attribute +import LeanPool.EuclideanJordan.EuclideanJordan.Witness +import LeanPool.EuclideanJordan.FramePeirceSolution +import LeanPool.EuclideanJordan.KoecherSolution +import LeanPool.EuclideanJordan.SpectralSolution +import LeanPool.EuclideanJordan.StructureSolution +import LeanPool.EuclideanJordan.TraceFormSolution + +/-! +# Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition + +Source: url:https://github.com/ehrlich-b/euclidean-jordan +Authors: Bryan Ehrlich +Status: verified +Main declarations: `EuclideanJordan.frameBlock_isInternal`, `EuclideanJordan.finrank_frameBlock_diag` +Tags: nonassociative-algebra +MSC: 17C20, 17C27, 17C37, 17C65, 17A15, 46L70 +-/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan.lean b/LeanPool/EuclideanJordan/EuclideanJordan.lean new file mode 100644 index 0000000000..678c501812 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Block +import LeanPool.EuclideanJordan.EuclideanJordan.Bridge +import LeanPool.EuclideanJordan.EuclideanJordan.Class +import LeanPool.EuclideanJordan.EuclideanJordan.Connection +import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +import LeanPool.EuclideanJordan.EuclideanJordan.Frame +import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul +import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin +import LeanPool.EuclideanJordan.EuclideanJordan.HermitianCarrier +import LeanPool.EuclideanJordan.EuclideanJordan.Order +import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto +import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace +import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal +import LeanPool.EuclideanJordan.EuclideanJordan.Pattern +import LeanPool.EuclideanJordan.EuclideanJordan.Peirce +import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul +import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra +import LeanPool.EuclideanJordan.EuclideanJordan.Power +import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc +import LeanPool.EuclideanJordan.EuclideanJordan.Rank +import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra +import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes.Attribute +import LeanPool.EuclideanJordan.EuclideanJordan.Witness + +/-! +# Euclidean Jordan algebras in Lean 4 + +Root import for the library. See `README.md` for the headline results. +-/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean new file mode 100644 index 0000000000..a0d0af7503 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean @@ -0,0 +1,203 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Frame + + +/-! +# The rank-two block, split into its three Peirce pieces + +For orthogonal idempotents `p, q`, the Peirce-1 space of the rank-two block `q' = p + q` +decomposes: + + `J₁(p + q) = J₁(p) ⊕ (J_{1/2}(p) ∩ J_{1/2}(q)) ⊕ J₁(q)`. + +`exists_block_split` is the forward half and `block_mul_eq_self` the converse, so together +they characterise `J₁(p+q)` exactly. This is the Faraut–Korányi coherence-block structure at +the smallest interesting size. + +## The argument + +`L_p` commutes with `L_q` (`opCommute_of_orthogonal`), so `L_p` commutes with `L_{p+q}`, so +**`L_{p+q}` commutes with every polynomial in `L_p`** — and the three Peirce projections of +`p` are such polynomials, hence they all preserve `J₁(p+q)`. +★ The subject of that chain shifts, and an earlier draft let it shift silently ("…hence with +every polynomial in `L_p`", with `L_p` still the subject, which is trivially true and not what +is meant). Caught on the arc's second audit pass; a true sentence whose grammar names the +wrong subject is the same defect kind as a true conclusion with a false reason. Apply +them to `x ∈ J₁(p+q)`: the pieces have `p`-eigenvalues `1`, `1/2`, `0`, and since +`q ∘ z = (p+q) ∘ z − p ∘ z = z − p ∘ z` on `J₁(p+q)`, their `q`-eigenvalues are the +complements `0`, `1/2`, `1`. **The three admissible patterns are exactly the three ways +`μ + ν = 1` can happen with `μ, ν ∈ {0, 1/2, 1}`** — which is why the block has three pieces +and not nine. + +★ **The eigenvalue trichotomy is what makes this finite**, but note it is not *invoked*: the +projections do the work directly. The trichotomy explains the shape of the answer rather than +appearing in the proof. + +## `q` is not assumed idempotent, and that is not an oversight + +`exists_block_split` needs only **`p ∘ p = p` and `p ∘ q = 0`**. Lean's unused-variable linter +caught the `hq` hypothesis being dead and it was removed rather than silenced. The reason is +visible in the argument above: every claim about `q` is derived from +`q ∘ z = (p+q) ∘ z − p ∘ z`, which never asks what `q ∘ q` is. + +★ What `hq` buys is the *interpretation*: without it `p + q` need not be idempotent, so +"`J₁(p+q)`" is not a Peirce space and "`J₁(q)`" is not either — the conclusions are still true, +they just stop being a Peirce decomposition. Callers wanting the FK reading should have `hq` +in hand from `IsOrthIdemFamily`; the theorem simply does not need to be told. +-/ + +namespace EuclideanJordan + +section Block + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] + +omit [IsCommJordan J] in +/-- If `L_c` commutes with `L_p` then it commutes with the Peirce-1 projection of `p`. -/ +theorem mul_peirceOne_comm {c p : J} (h : ∀ w : J, c * (p * w) = p * (c * w)) (x : J) : + c * peirceOne p x = peirceOne p (c * x) := by + simp only [peirceOne_apply, mul_sub, mul_smul_comm', h] + +omit [IsCommJordan J] in +/-- The same for the Peirce-`1/2` projection. -/ +theorem mul_peirceHalf_comm {c p : J} (h : ∀ w : J, c * (p * w) = p * (c * w)) (x : J) : + c * peirceHalf p x = peirceHalf p (c * x) := by + simp only [peirceHalf_apply, mul_sub, mul_smul_comm', h] + +omit [IsCommJordan J] in +/-- The same for the Peirce-`0` projection. -/ +theorem mul_peirceZero_comm {c p : J} (h : ∀ w : J, c * (p * w) = p * (c * w)) (x : J) : + c * peirceZero p x = peirceZero p (c * x) := by + simp only [peirceZero_apply, mul_add, mul_sub, mul_smul_comm', h] + +variable {p q : J} + +omit [IsScalarTower ℝ J J] in +/-- `L_{p+q}` commutes with `L_p`, for `p` idempotent and orthogonal to `q`. -/ +theorem add_mul_comm_left (hp : p * p = p) (hpq : p * q = 0) (w : J) : + (p + q) * (p * w) = p * ((p + q) * w) := by + rw [add_mul, add_mul, mul_add, opCommute_of_orthogonal hp hpq w] + +/-- **The rank-two block splits.** Every element of `J₁(p+q)` is the sum of an element of +`J₁(p)`, an element of the coherence space `J_{1/2}(p) ∩ J_{1/2}(q)`, and an element of +`J₁(q)` — and the `q`-eigenvalues come out as the complements of the `p`-eigenvalues. + +★ Needs only `p ∘ p = p` and `p ∘ q = 0`; see the module docstring on why `q ∘ q = q` is not +required. -/ +theorem exists_block_split (hp : p * p = p) (hpq : p * q = 0) {x : J} (hx : (p + q) * x = x) : + ∃ a b c : J, (p * a = a ∧ q * a = 0) ∧ (p * b = (2 : ℝ)⁻¹ • b ∧ q * b = (2 : ℝ)⁻¹ • b) + ∧ (p * c = 0 ∧ q * c = c) ∧ x = a + b + c := by + have hcomm := add_mul_comm_left hp hpq + refine ⟨peirceOne p x, peirceHalf p x, peirceZero p x, ⟨mul_peirceOne hp x, ?_⟩, + ⟨mul_peirceHalf hp x, ?_⟩, ⟨mul_peirceZero hp x, ?_⟩, (peirce_add_add p x).symm⟩ + · have h1 : p * peirceOne p x + q * peirceOne p x = peirceOne p x := by + rw [← add_mul, mul_peirceOne_comm hcomm x, hx] + rw [mul_peirceOne hp x] at h1 + refine add_left_cancel (a := peirceOne p x) ?_ + rw [add_zero] + exact h1 + · have h2 : p * peirceHalf p x + q * peirceHalf p x = peirceHalf p x := by + rw [← add_mul, mul_peirceHalf_comm hcomm x, hx] + rw [mul_peirceHalf hp x] at h2 + have hstep : q * peirceHalf p x = peirceHalf p x - (2 : ℝ)⁻¹ • peirceHalf p x := + eq_sub_of_add_eq (by rw [add_comm]; exact h2) + rw [hstep] + module + · have h3 : p * peirceZero p x + q * peirceZero p x = peirceZero p x := by + rw [← add_mul, mul_peirceZero_comm hcomm x, hx] + rw [mul_peirceZero hp x, zero_add] at h3 + exact h3 + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +/-- **The converse.** Each of the three admissible eigenvalue patterns lands in `J₁(p+q)`, so +together with `exists_block_split` this characterises the block exactly. + +The three cases are `1 + 0`, `1/2 + 1/2` and `0 + 1` — the only ways two elements of +`{0, 1/2, 1}` sum to `1`. -/ +theorem block_mul_eq_self {z : J} (h : (p * z = z ∧ q * z = 0) + ∨ (p * z = (2 : ℝ)⁻¹ • z ∧ q * z = (2 : ℝ)⁻¹ • z) ∨ (p * z = 0 ∧ q * z = z)) : + (p + q) * z = z := by + rw [add_mul] + rcases h with ⟨h1, h2⟩ | ⟨h1, h2⟩ | ⟨h1, h2⟩ + · rw [h1, h2, add_zero] + · rw [h1, h2]; module + · rw [h1, h2, zero_add] + +/-! ### The Peirce projections of orthogonal idempotents commute + +`EuclideanJordan/PeirceMul.lean` named this as the ingredient missing for the frame-level joint +decomposition: *"closing the field needs the joint Peirce decomposition over a frame, which +needs these rules plus the commutation of the projections of distinct frame idempotents."* +Here it is. + +The generic lemmas below take **any** linear `F` commuting with `L_q` and conclude that `F` +commutes with each Peirce projection of `q`. Instantiating `F` at a Peirce projection of `p` +gives all nine commutations; the three diagonal ones are named, and the mixed six are one +line each from the same lemmas. -/ + +section ProjComm + +omit [IsCommJordan J] in +/-- A linear map commuting with `L_q` commutes with `q`'s Peirce-1 projection. -/ +theorem peirceOne_comm_of_mul_comm {q : J} {F : J →ₗ[ℝ] J} (h : ∀ w, q * F w = F (q * w)) + (x : J) : peirceOne q (F x) = F (peirceOne q x) := by + simp only [peirceOne_apply, h, map_sub, map_smul] + +omit [IsCommJordan J] in +/-- The same for the Peirce-`1/2` projection. -/ +theorem peirceHalf_comm_of_mul_comm {q : J} {F : J →ₗ[ℝ] J} (h : ∀ w, q * F w = F (q * w)) + (x : J) : peirceHalf q (F x) = F (peirceHalf q x) := by + simp only [peirceHalf_apply, h, map_sub, map_smul] + +omit [IsCommJordan J] in +/-- The same for the Peirce-`0` projection. -/ +theorem peirceZero_comm_of_mul_comm {q : J} {F : J →ₗ[ℝ] J} (h : ∀ w, q * F w = F (q * w)) + (x : J) : peirceZero q (F x) = F (peirceZero q x) := by + simp only [peirceZero_apply, h, map_add, map_sub, map_smul] + +/-- For orthogonal idempotents, `L_q` commutes with `p`'s Peirce-1 projection. -/ +theorem mul_peirceOne_comm_orth (hq : q * q = q) (hpq : p * q = 0) (x : J) : + q * peirceOne p x = peirceOne p (q * x) := + mul_peirceOne_comm (fun w => opCommute_of_orthogonal hq (by rw [mul_comm]; exact hpq) w) x + +/-- For orthogonal idempotents, `L_q` commutes with `p`'s Peirce-`1/2` projection. -/ +theorem mul_peirceHalf_comm_orth (hq : q * q = q) (hpq : p * q = 0) (x : J) : + q * peirceHalf p x = peirceHalf p (q * x) := + mul_peirceHalf_comm (fun w => opCommute_of_orthogonal hq (by rw [mul_comm]; exact hpq) w) x + +/-- For orthogonal idempotents, `L_q` commutes with `p`'s Peirce-`0` projection. -/ +theorem mul_peirceZero_comm_orth (hq : q * q = q) (hpq : p * q = 0) (x : J) : + q * peirceZero p x = peirceZero p (q * x) := + mul_peirceZero_comm (fun w => opCommute_of_orthogonal hq (by rw [mul_comm]; exact hpq) w) x + +/-- **The Peirce-1 projections of orthogonal idempotents commute.** -/ +theorem peirceOne_comm_peirceOne (hq : q * q = q) (hpq : p * q = 0) (x : J) : + peirceOne q (peirceOne p x) = peirceOne p (peirceOne q x) := + peirceOne_comm_of_mul_comm (F := peirceOne p) (mul_peirceOne_comm_orth hq hpq) x + +/-- **The Peirce-`1/2` projections of orthogonal idempotents commute** — the projection onto +the coherence space is well defined independently of which idempotent is applied first. -/ +theorem peirceHalf_comm_peirceHalf (hq : q * q = q) (hpq : p * q = 0) (x : J) : + peirceHalf q (peirceHalf p x) = peirceHalf p (peirceHalf q x) := + peirceHalf_comm_of_mul_comm (F := peirceHalf p) (mul_peirceHalf_comm_orth hq hpq) x + +/-- **The Peirce-`0` projections of orthogonal idempotents commute.** -/ +theorem peirceZero_comm_peirceZero (hq : q * q = q) (hpq : p * q = 0) (x : J) : + peirceZero q (peirceZero p x) = peirceZero p (peirceZero q x) := + peirceZero_comm_of_mul_comm (F := peirceZero p) (mul_peirceZero_comm_orth hq hpq) x + +end ProjComm + +end Block + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean new file mode 100644 index 0000000000..1ddb6fe067 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Pattern + + +/-! +# Unbundled Jordan algebras: the multiplication as a bilinear map + +The rest of this library runs on the **typeclasses** `[NonUnitalNonAssocCommRing J] +[IsCommJordan J] [Module ℝ J]`. A caller who instead holds the Jordan product as a **bundled +bilinear map** `m : J →ₗ[ℝ] J →ₗ[ℝ] J` over `[NormedAddCommGroup J] [Module ℝ J]` cannot simply +assume both: that gives two different `AddCommGroup J` instances, `Module ℝ J` then fails to +synthesise at the use site, and the result is a textbook Mathlib diamond. + +**This file dodges it.** `ringOfBilinear` builds the multiplicative structure *on the ambient +additive group* — `{ (inferInstance : AddCommGroup J) with mul := fun x y => m x y, … }` — so +only one `AddCommGroup` is ever in play. Nothing is assumed twice. + +## What that buys + +Two sample transfers are given, both stated with `m` alone: + +* `peirce_poly_bilinear` — the Peirce polynomial identity `2 c(c(c y)) + c y = 3 c(c y)` for an + idempotent `c`; +* `opCommute_scalarOn_bilinear` — the Jordan multiplication operators `L_a` and `L_b` commute, + for `a` scalar on `range c` and `b` in the `1`-eigenspace of `L_c`. This is the load-bearing + Faraut–Korányi operator-commutation hypothesis. + +`EuclideanJordan/Spectral.lean` and `EuclideanJordan/Order.lean` use the same device at scale: +`spectral_resolution_bilinear` and `orderUnitSpaceOfBilinear` are stated over `m` and proved by +installing `ringOfBilinear` locally. + +★ **The device has a hard limit.** Only results whose *statements* are expressible with `m` +alone cross over; anything whose statement needs the ring instance — `jpow`, and so Albert's +power-associativity theorem — cannot be bridged this way, because the instance would have to +exist before the statement elaborates. +-/ +namespace EuclideanJordan + +section Bridge + +variable {J : Type*} [NormedAddCommGroup J] [Module ℝ J] + +/-- Build the multiplicative structure ON the ambient additive group, from a bilinear map. -/ +@[instance_reducible] +def ringOfBilinear (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (hcomm : ∀ x y, m x y = m y x) : + NonUnitalNonAssocCommRing J := + { (inferInstance : AddCommGroup J) with + mul := fun x y => m x y + left_distrib := fun a b c => (m a).map_add b c + right_distrib := fun a b c => by + show m (a + b) c = m a c + m b c + rw [map_add]; rfl + zero_mul := fun a => by + show m 0 a = 0 + rw [map_zero]; rfl + mul_zero := fun a => (m a).map_zero + mul_comm := hcomm } + + +variable (m : J →ₗ[ℝ] J →ₗ[ℝ] J) + +/-- `m` is linear in its first argument — the scalar-tower law for the constructed ring. -/ +theorem smul_bilinear (r : ℝ) (a b : J) : m (r • a) b = r • m a b := by + rw [map_smul]; rfl + +/-- **The Peirce polynomial identity, in bilinear-map vocabulary.** -/ +theorem peirce_poly_bilinear (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + {c : J} (hc : m c c = c) (y : J) : + (2 : ℕ) • m c (m c (m c y)) + m c y = (3 : ℕ) • m c (m c y) := by + letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + letI : IsCommJordan J := ⟨hjordan⟩ + exact peirce_poly hc y + +/-- **Operator commutation in bilinear-map vocabulary**: `L_a` and `L_b` commute at `w`, for +`a` scalar on `range c` and `b` in the `1`-eigenspace of `L_c`. -/ +theorem opCommute_scalarOn_bilinear (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + {c a a₀ b : J} {mu : ℝ} (hc : m c c = c) (ha : a = mu • c + a₀) + (ha₀ : m c a₀ = 0) (hb : m c b = b) (w : J) : + m a (m b w) = m b (m a w) := by + letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + letI : IsCommJordan J := ⟨hjordan⟩ + letI : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ + exact opCommute_scalarOn hc ha ha₀ hb w + +end Bridge + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean new file mode 100644 index 0000000000..4af054892c --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean @@ -0,0 +1,257 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Order +import LeanPool.EuclideanJordan.EuclideanJordan.Pattern + + +/-! +# The Euclidean Jordan algebra class + +The abstract modules of this library built before this one — `Peirce`, `PeirceMul`, +`Orthogonal`, `Frame`, `Power`, `PowerAssoc`, `FormallyReal`, `Subalgebra`, `Block`, `Pattern` +and `Spectral` — state their hypotheses as a *tuple* drawn from +`[NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] [IsScalarTower ℝ J J] +[IsFormallyReal J] [Module.Finite ℝ J]`, each module taking the sub-tuple it needs; or — on the +Euclidean side of `EuclideanJordan/Order.lean` and in `Spectral`'s interface section — as a bilinear map +`m : J →ₗ[ℝ] J →ₗ[ℝ] J` carrying `hcomm`/`hjordan`/`hassoc` as ordinary hypotheses. (`Witness` +and `Spectral`'s concrete section state theirs over `HermitianMat` instead.) Both abstract +vocabularies are correct and neither is a *class*, so a theorem about a Euclidean Jordan +algebra cannot be stated by naming one. + +This file names one. `EuclideanJordanAlgebra J` is a real inner-product space with a +commutative bilinear product with unit, satisfying the Jordan identity and the associativity of +the inner product — Faraut–Korányi's definition (FK III.1). + +★ **Hypothesis direction.** The class is *weaker* than the textbook definition in two +respects, which is the correct direction for an import — a theorem proved over this class +applies to the textbook setting, not the other way round. First, finite-dimensionality is +folded into the textbook definition and is carried here as a separate `[FiniteDimensional ℝ J]` +argument. Second, a common presentation fixes the inner product to be the trace form +`⟪x, y⟫ = tr(x ∘ y)` for the Jordan trace, whereas the class asks only that *some* +positive-definite associative inner product exist. `inner_mul_one` below shows the gap is +smaller than it looks: any associative inner product satisfies `⟪x ∘ y, 1⟫ = ⟪x, y⟫`, so it *is* +the trace form of the linear functional `z ↦ ⟪z, 1⟫`. It need not be the form of the *Jordan* +trace — rescaling an associative inner product by a positive constant keeps it associative — +and nothing here claims otherwise. + +## The shape, and the diamond it dodges + +★ The product is placed **on top of** the additive group of the inner-product space, never +alongside a second one. Assuming `[NormedAddCommGroup J]` and `[NonUnitalNonAssocCommRing J]` +simultaneously produces two `AddCommGroup J` instances and `Module ℝ J` then fails to +synthesise; `EuclideanJordan/Bridge.lean` records that diamond and `ringOfBilinear` dodges it by building +the multiplicative structure on the *ambient* additive group. This class is that dodge +promoted from a `def` to a `class`: it `extends Mul J, One J` over +`[NormedAddCommGroup J] [InnerProductSpace ℝ J]`, so only one `AddCommGroup J` is ever in play +and `instNonUnitalNonAssocCommRing` below is built from `inferInstance` on the nose. + +Consequently `ringOfBilinear (jmulₗ J) mul_comm = instNonUnitalNonAssocCommRing` holds by `rfl` +(`ringOfBilinear_jmulₗ`), which is the statement that the class and the bilinear vocabulary of +`EuclideanJordan/Order.lean` are the same structure and not merely isomorphic ones. + +## What finite-dimensionality is, and is not, needed for + +`FiniteDimensional ℝ J` is deliberately **not** a field of the class. It is genuinely required +downstream: `EuclideanJordan/Spectral.lean` records that its `spectral_resolution_bilinear` — which is +`spectral_resolution_complete` in bilinear vocabulary, and carries the same hypotheses minus the +inner product — is false without it, `ℝ[X]` satisfying every other hypothesis with no nonconstant +resolution. So the dimension is carried as a separate instance argument at exactly the theorems +that need it, and `spectral_resolution_complete'` below is one of them. + +★ It is *not* needed for formal reality. `instIsFormallyReal` below is unconditional: pairing a +vanishing sum of squares against the unit turns `∑ᵢ ⟪xᵢ ∘ xᵢ, 1⟫` into `∑ᵢ ⟪xᵢ, xᵢ⟫` by one +application of `inner_assoc`, and a vanishing sum of nonnegative reals has vanishing terms. + +This corrects the build plan on two points. The plan derived the instance from +`EuclideanJordan/Spectral.lean`'s `isFormallyReal_of_fin` under `[FiniteDimensional ℝ J]`. That lemma +cannot supply it: `isFormallyReal_of_fin` *takes formal reality as a hypothesis*, in `Fin k` +form, and does nothing but reindex it to the `Finset` form the class `IsFormallyReal` carries. +The derivation had to come from the inner product instead — and once it does, the dimension +hypothesis turns out to be unused. + +## Scope + +Almost all of this file is repackaging: the two restatements at the end (`spectral_resolution_complete'`, `peirce_add_add'`) discharge the claim +that the existing layer is reachable from the class, and are not new results. + +★ Two declarations are *not* repackaging, and the file should not be described as if they were. +`inner_mul_one`, and `instIsFormallyReal` resting on it, derive formal reality from the +associative inner product, and the existing layer does not contain that derivation anywhere: it +takes formal reality as a hypothesis at every abstract site (`EuclideanJordan/Spectral.lean`'s +`isFormallyReal_of_fin` *receives* it and does nothing but reindex; `EuclideanJordan/Order.lean`'s +`orderUnitSpaceOfBilinear` receives it as `[IsFormallyReal J]`), and derives it only on the +concrete carrier, in `EuclideanJordan/Witness.lean`'s `instIsFormallyReal` for `HermitianMat d 𝕜`. Both new +declarations are short; the point is only that "this file contains no new mathematics" would be +false. + +★ One hazard to record for later modules. `instNonUnitalNonAssocCommRing` fires on any type +carrying `EuclideanJordanAlgebra`, and `HermitianMat d 𝕜` already carries a `Mul` — from +`EuclideanJordan/Vendor/HermitianMat/Jordan.lean`'s `scoped instance : CommMagma +(HermitianMat d 𝕜)`, whose product is `HermitianMat.symmMul`. Nothing declares +`EuclideanJordanAlgebra (HermitianMat d 𝕜)` today, and until something does the two never meet; +if one is ever declared, that scoped instance and this class's `toMul` will both be in scope +inside `open HermMul` sections and one of them has to give way. +-/ + +noncomputable section + +namespace EuclideanJordan + +/-- A **Euclidean Jordan algebra**: a real inner-product space carrying a commutative bilinear +product with unit, satisfying the Jordan identity and the associativity of the inner product. + +This is Faraut–Korányi's definition (FK III.1), weakened in the two ways the module docstring +records: finite-dimensionality is *not* a field, and the inner product is an arbitrary +associative one rather than the Jordan trace form. Both weakenings run in the import-safe +direction. -/ +class EuclideanJordanAlgebra (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + extends Mul J, One J where + /-- The Jordan product is commutative. -/ + mul_comm : ∀ x y : J, x * y = y * x + /-- The Jordan product is additive in its left argument. -/ + add_mul : ∀ x y z : J, (x + y) * z = x * z + y * z + /-- The Jordan product is homogeneous in its left argument. -/ + smul_mul : ∀ (r : ℝ) (x y : J), (r • x) * y = r • (x * y) + /-- `1` is a unit for the Jordan product. -/ + one_mul : ∀ x : J, (1 : J) * x = x + /-- The Jordan identity, `x ∘ (x² ∘ y) = x² ∘ (x ∘ y)`. -/ + jordan : ∀ x y : J, x * ((x * x) * y) = (x * x) * (x * y) + /-- The inner product is associative: `⟪x ∘ y, z⟫ = ⟪y, x ∘ z⟫`. This is what "Euclidean" + adds to "formally real"; `EuclideanJordan/Order.lean` carries the same condition as the hypothesis + `hassoc`. -/ + inner_assoc : ∀ x y z : J, inner ℝ (x * y) z = inner ℝ y (x * z) + +namespace EuclideanJordanAlgebra + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] + +/-- Left multiplication by `0` is `0` — the one ring axiom the class does not state, obtained +from additivity at `(0, 0, a)`. -/ +theorem zero_mul' (a : J) : (0 : J) * a = 0 := by + have h : (0 : J) * a + (0 : J) * a = (0 : J) * a + 0 := by + rw [add_zero, ← add_mul, add_zero] + exact add_left_cancel h + +/-- The ring structure, built on the **ambient** additive group. -/ +instance instNonUnitalNonAssocCommRing : NonUnitalNonAssocCommRing J := + { (inferInstance : AddCommGroup J), (inferInstance : Mul J) with + left_distrib := fun a b c => by + rw [mul_comm a (b + c), add_mul, mul_comm b a, mul_comm c a] + right_distrib := add_mul + zero_mul := zero_mul' + mul_zero := fun a => by rw [mul_comm, zero_mul'] + mul_comm := mul_comm } + +/-- Mathlib's Jordan class. Its field `lmul_comm_rmul_rmul` is oriented +`a ∘ b ∘ (a ∘ a) = a ∘ (b ∘ (a ∘ a))`, which is the class's `jordan` field read through +commutativity twice. -/ +instance instIsCommJordan : IsCommJordan J := + ⟨fun a b => by rw [mul_comm (a * b) (a * a), ← jordan a b, mul_comm (a * a) b]⟩ + +instance instIsScalarTower : IsScalarTower ℝ J J := ⟨smul_mul⟩ + +/-- Required by `Submodule`-valued and `NonUnitalSubalgebra`-valued subobject constructions +downstream; do not remove because nothing in this file uses it. -/ +instance instSMulCommClass : SMulCommClass ℝ J J := + ⟨fun r x y => by + change r • (x * y) = x * (r • y) + rw [mul_comm x (r • y), smul_mul, mul_comm y x]⟩ + +theorem mul_one' (x : J) : x * (1 : J) = x := by rw [mul_comm, one_mul] + +/-- **The inner product is a trace form.** `⟪x ∘ y, 1⟫ = ⟪x, y⟫`: one application of +`inner_assoc` against the unit. So the linear functional `z ↦ ⟪z, 1⟫` plays the role the +article's `tr` plays, and the inner product's positive-definiteness is available as +positive-definiteness of that form on products. It is *not* claimed that this functional is the +Jordan trace — see the module docstring. -/ +theorem inner_mul_one (x y : J) : (inner ℝ (x * y) (1 : J) : ℝ) = inner ℝ x y := by + rw [inner_assoc x y 1, mul_one' x, real_inner_comm] + +/-- **Formal reality, from the inner product.** Unconditional on the dimension — see the +module docstring. -/ +instance instIsFormallyReal : IsFormallyReal J := by + refine ⟨fun {ι} s f hsum i hi => ?_⟩ + have hz : (∑ j ∈ s, (inner ℝ (f j) (f j) : ℝ)) = 0 := by + have h0 : (inner ℝ (∑ j ∈ s, f j * f j) (1 : J) : ℝ) = 0 := by rw [hsum, inner_zero_left] + rw [sum_inner] at h0 + simpa only [fun x : J => inner_mul_one x x] using h0 + have hnn : ∀ j ∈ s, (0 : ℝ) ≤ inner ℝ (f j) (f j) := fun _ _ => real_inner_self_nonneg + exact inner_self_eq_zero.mp ((Finset.sum_eq_zero_iff_of_nonneg hnn).mp hz i hi) + +/-- The associativity of the inner product in its other orientation, `⟪x ∘ y, z⟫ = ⟪x, y ∘ z⟫`, +obtained from the field by commuting the product. -/ +theorem inner_assoc' (x y z : J) : (inner ℝ (x * y) z : ℝ) = inner ℝ x (y * z) := by + rw [mul_comm x y, inner_assoc y x z] + +end EuclideanJordanAlgebra + +/-! ## The bridge to the bilinear-map vocabulary + +`EuclideanJordan/Order.lean`'s Euclidean section and `EuclideanJordan/Spectral.lean`'s interface section state +everything over a bundled `m : J →ₗ[ℝ] J →ₗ[ℝ] J` carrying `hcomm`, `hjordan`, `hassoc` and a +`Fin k`-indexed formal-reality hypothesis. `jmulₗ` is the class's product in that vocabulary +and the five lemmas after it are exactly that hypothesis tuple, so a consumer of +`orderUnitSpaceOfBilinear`, `inner_left_coeff`, `isArchimedean_ofBilinear`, +`isSoS_iff_exists_sq` or `spectral_resolution_bilinear` supplies them by name rather than +rebuilding them. -/ + +/-- The Jordan product of a `EuclideanJordanAlgebra` as a bundled bilinear map. -/ +def jmulₗ (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + [EuclideanJordanAlgebra J] : J →ₗ[ℝ] J →ₗ[ℝ] J := + LinearMap.mk₂ ℝ (· * ·) EuclideanJordanAlgebra.add_mul EuclideanJordanAlgebra.smul_mul + (fun x y z => mul_add x y z) (fun r x y => mul_smul_comm r x y) + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] + +@[simp] theorem jmulₗ_apply (x y : J) : jmulₗ J x y = x * y := rfl + +/-- ★ The class and `EuclideanJordan/Bridge.lean`'s `ringOfBilinear` produce the **same** ring structure, +definitionally. This is the precise sense in which the class does not introduce a second +multiplicative structure alongside the one the existing layer runs on. -/ +theorem ringOfBilinear_jmulₗ : + ringOfBilinear (jmulₗ J) EuclideanJordanAlgebra.mul_comm = + EuclideanJordanAlgebra.instNonUnitalNonAssocCommRing (J := J) := rfl + +theorem jmulₗ_comm (x y : J) : jmulₗ J x y = jmulₗ J y x := EuclideanJordanAlgebra.mul_comm x y + +theorem jmulₗ_jordan (a b : J) : + jmulₗ J (jmulₗ J a b) (jmulₗ J a a) = jmulₗ J a (jmulₗ J b (jmulₗ J a a)) := + IsCommJordan.lmul_comm_rmul_rmul a b + +theorem jmulₗ_inner_assoc (x y z : J) : + (inner ℝ (jmulₗ J x y) z : ℝ) = inner ℝ y (jmulₗ J x z) := + EuclideanJordanAlgebra.inner_assoc x y z + +theorem jmulₗ_one_mul (y : J) : jmulₗ J 1 y = y := EuclideanJordanAlgebra.one_mul y + +/-- Formal reality in the `Fin k` form `spectral_resolution_bilinear` and +`isFormallyReal_of_fin` take. -/ +theorem jmulₗ_formallyReal (k : ℕ) (f : Fin k → J) (h : (∑ i, jmulₗ J (f i) (f i)) = 0) + (i : Fin k) : f i = 0 := + IsFormallyReal.eq_zero_of_sum_mul_self Finset.univ f h i (Finset.mem_univ i) + +/-! ## The existing layer, restated over the class -/ + +/-- **The spectral theorem with completeness, over the class.** `EuclideanJordan/Spectral.lean`'s +`spectral_resolution_complete` carries the unit as an explicit hypothesis `he : ∀ y, e ∘ y = y` +because it has no `One`; the class supplies it. -/ +theorem spectral_resolution_complete' [FiniteDimensional ℝ J] (x : J) : + ∃ (n : ℕ) (c : Fin n → J) (lam : Fin n → ℝ), + IsOrthIdemFamily c ∧ (∑ i, c i) = 1 ∧ x = ∑ i, lam i • c i := + spectral_resolution_complete 1 EuclideanJordanAlgebra.one_mul x + +/-- **The Peirce decomposition at a single idempotent, over the class.** `EuclideanJordan/Peirce.lean`'s +`peirce_add_add` needs no idempotency hypothesis: the three projections sum to the identity for +every `c`. -/ +theorem peirce_add_add' (c y : J) : peirceOne c y + peirceHalf c y + peirceZero c y = y := + peirce_add_add c y + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean new file mode 100644 index 0000000000..f5c50f936a --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean @@ -0,0 +1,540 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul + + +/-! +# Connections between frame blocks + +`EuclideanJordan/FramePeirceMul.lean` gives the multiplication table of a Jordan frame's blocks. This file +proves the two identities that turn that table into an *algebra*, and they are the engine of +Jacobson coordinatization. + +## The two identities + +**(1) An off-diagonal block element squares to a multiple of `pᵢ + pⱼ`.** +`frameBlockRaw_mul_self_eq` already gives `x ∘ x = a • pᵢ + b • pⱼ` for `x ∈ V_{ij}`. Here +`a = b` (`frameBlockRaw_sq_coeff_eq`), so the square is a multiple of the *idempotent* +`pᵢ + pⱼ` and its coefficient is a quadratic form on the block (`exists_sq_smul`, +`sq_eq_inner_smul`). + +★ **The proof is power associativity, not the trace form.** Computing `x⁴` as `x² ∘ x²` and as +`x ∘ (x ∘ x²)` — the same element, by `EuclideanJordan/PowerAssoc.lean`'s `jpow_mul_jpow` — gives +`a² = a(a+b)/2` and `b² = b(a+b)/2` after pairing against `pᵢ` and `pⱼ`. Adding those, +`a² + b² = (a+b)²/2 = 2ab`, so `(a - b)² = 0`. The inner product enters only to *read off* +those two scalar equations from an equation between elements; the identity `a = b` itself is +algebraic. + +**(2) `x ∘ (x ∘ y) = (a/4) • y` for `x ∈ V_{ij}` and `y ∈ V_{jk}`** (`block_sq_act`). So `2 L_x` +restricted to `V_{jk}` squares to the scalar `a`. + +★ **The plain Jordan identity gives nothing here.** Both sides of `x ∘ (x² ∘ y) = x² ∘ (x ∘ y)` +reduce to `½ a • (x ∘ y)` using only the eigenvalue rules, so it is satisfied for *any* value of +`x ∘ (x ∘ y)`. What works is the fully **linearised** Jordan identity — Mathlib's +`two_nsmul_lie_lmul_lmul_add_add_eq_zero`, restated pointwise here as `lin_jordan` — at +`(x, x, y)`, evaluated at `pⱼ`. Six terms; `x ∘ y ∈ V_{ik}` is annihilated by `pⱼ`, `pⱼ` halves +`x` and `y`, and what survives is `-(x ∘ (x ∘ y)) + (a/4) • y = 0`. + +## Connectors + +A **connector** on `(i, j)` is a `c ∈ V_{ij}` with `c ∘ c = pᵢ + pⱼ`. A nonzero block has one +(`exists_isConnector`: normalise by `a^{-1/2}`), and identity (2) at `a = 1` makes +`y ↦ 2 (c ∘ y)` an involution, hence a linear equivalence `V_{jk} ≃ V_{ik}` (`connEquiv`). So +connected blocks have the same dimension, connectivity is transitive, and a block is nonzero +exactly when it carries a connector. + +The **composition law** `block_mul_sq` says the coefficients multiply: if `x ∘ x = a • (pᵢ + pⱼ)` +and `y ∘ y = b • (pⱼ + p_k)` then `(2 (x ∘ y)) ∘ (2 (x ∘ y)) = (ab) • (pᵢ + p_k)`. It follows +from (2) by pairing, once `⟪pᵢ, pᵢ⟫ = ⟪pⱼ, pⱼ⟫`. ★ That identification is **not** unconditional: +`inner_p_eq_of_sq` gets it from (1) — the two pairings of `x ∘ x` both return `½‖x‖²`, so +`a τᵢ = a τⱼ` — and then needs `a ≠ 0` to cancel. `V_{ij} = 0` really does leave `τᵢ` and `τⱼ` +unrelated, and `block_mul_sq` disposes of that case separately rather than through the +identification. + +## Scope + +★ `rank J = n` is **not** available and nothing here is a step towards it; a `JordanFrame J n` is +carried as data and its cardinality bounds the rank from below, nothing more. + +★ Nothing here says a frame *is* connected. Connectors are hypotheses everywhere they appear. +The statement "a simple `J` has every block nonzero" is **not proved anywhere in this +library**, which has no notion of a Jordan ideal to state simplicity with. +-/ + +noncomputable section + +namespace EuclideanJordan + +open EuclideanJordanAlgebra + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] +variable {n : ℕ} + +/-! ## The linearised Jordan identity -/ + +/-- **The fully linearised Jordan identity, evaluated at a point.** + +Mathlib's `two_nsmul_lie_lmul_lmul_add_add_eq_zero` states +`2 • (⁅L a, L (b∘c)⁆ + ⁅L b, L (c∘a)⁆ + ⁅L c, L (a∘b)⁆) = 0` in `AddMonoid.End J`; this is that +equation applied to `w`, with the `2 •` divided out (legitimate because `J` is a real vector +space). -/ +theorem lin_jordan (a b c w : J) : + (a * ((b * c) * w) - (b * c) * (a * w)) + + (b * ((c * a) * w) - (c * a) * (b * w)) + + (c * ((a * b) * w) - (a * b) * (c * w)) = 0 := by + have H := two_nsmul_lie_lmul_lmul_add_add_eq_zero (A := J) a b c + have H' := DFunLike.congr_fun H w + simp only [Ring.lie_def, AddMonoid.End.mulLeft] at H' + have h2 : (2 : ℕ) • ((a * (b * c * w) - b * c * (a * w)) + (b * (c * a * w) - c * a * (b * w)) + + (c * (a * b * w) - a * b * (c * w))) = 0 := H' + have h3 : ((2 : ℕ) : ℝ) • ((a * (b * c * w) - b * c * (a * w)) + + (b * (c * a * w) - c * a * (b * w)) + (c * (a * b * w) - a * b * (c * w))) = 0 := by + rw [Nat.cast_smul_eq_nsmul]; exact h2 + rcases smul_eq_zero.mp h3 with h | h + · norm_num at h + · exact h + +/-! ## The frame in the inner product -/ + +/-- Distinct frame members are orthogonal in the inner product, not merely in the algebra: +`⟪pᵢ, pⱼ⟫ = ⟪pᵢ ∘ pⱼ, 1⟫ = 0` by `EuclideanJordan/Class.lean`'s `inner_mul_one`. -/ +theorem inner_p_p_of_ne (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) : + (inner ℝ (F.p i) (F.p j) : ℝ) = 0 := by + rw [← inner_mul_one (F.p i) (F.p j), F.orthIdem.orth i j hij, inner_zero_left] + +/-- `⟪pᵢ, pᵢ⟫ > 0` — the frame members are nonzero and the inner product is definite. -/ +theorem inner_p_self_pos (F : JordanFrame J n) (i : Fin n) : + 0 < (inner ℝ (F.p i) (F.p i) : ℝ) := + real_inner_self_pos.mpr (F.p_ne_zero i) + +/-- Pairing an element of `V_{ij}` against `pᵢ`: `⟪x ∘ x, pᵢ⟫ = ½‖x‖²`. -/ +theorem inner_sq_p_of_mem (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) : + (inner ℝ (x * x) (F.p i) : ℝ) = (2 : ℝ)⁻¹ * (inner ℝ x x : ℝ) := by + rw [inner_assoc x x (F.p i), _root_.mul_comm x (F.p i), + frameBlockRaw_mul_left_half F hij hx, real_inner_smul_right] + +/-! ## The square of an off-diagonal element + +★ `EuclideanJordan/FramePeirceMul.lean`'s `frameBlockRaw_mul_self_eq` gives `x ∘ x = a • pᵢ + b • pⱼ`. +This section proves `a = b`, so that the square is a multiple of the *idempotent* `pᵢ + pⱼ` +and the coefficient is a quadratic form. The argument is power associativity, not the trace +form: `x⁴` computed as `x² ∘ x²` and as `x ∘ (x ∘ x²)` gives `a² = a(a+b)/2` and +`b² = b(a+b)/2`, whence `(a - b)² = 0`. -/ + +section Square + +variable [FiniteDimensional ℝ J] + +omit [FiniteDimensional ℝ J] in +/-- `x⁴ = x² ∘ x²` and `x⁴ = x ∘ (x ∘ x²)` agree — `EuclideanJordan/PowerAssoc.lean`'s `jpow_mul_jpow` at +`(1, 1)`, unfolded. -/ +theorem sq_mul_sq_eq (x : J) : (x * x) * (x * x) = x * (x * (x * x)) := by + have h := jpow_mul_jpow x 1 1 + simpa using h + +omit [FiniteDimensional ℝ J] in +/-- **The two coefficients of `x ∘ x` on `V_{ij}` are equal.** -/ +theorem frameBlockRaw_sq_coeff_eq (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) {a b : ℝ} (hab : x * x = a • F.p i + b • F.p j) : + a = b := by + have hxi : F.p i * x = (2 : ℝ)⁻¹ • x := frameBlockRaw_mul_left_half F hij hx + have hxj : F.p j * x = (2 : ℝ)⁻¹ • x := frameBlockRaw_mul_right_half F hij hx + -- `x ∘ (x ∘ x) = ((a + b)/2) • x` + have hcube : x * (x * x) = ((a + b) / 2) • x := by + rw [hab] + simp only [mul_add, mul_smul_comm, _root_.mul_comm x (F.p i), _root_.mul_comm x (F.p j), + hxi, hxj] + module + -- `x ∘ (x ∘ (x ∘ x)) = ((a + b)/2) • (a • pᵢ + b • pⱼ)` + have hquart : x * (x * (x * x)) = ((a + b) / 2 * a) • F.p i + ((a + b) / 2 * b) • F.p j := by + rw [hcube, mul_smul_comm, hab] + module + -- `(x ∘ x) ∘ (x ∘ x) = a² • pᵢ + b² • pⱼ` + have hsqsq : (x * x) * (x * x) = (a * a) • F.p i + (b * b) • F.p j := by + rw [hab] + simp only [_root_.add_mul, mul_add, smul_mul, mul_smul_comm, F.orthIdem.idem i, + F.orthIdem.idem j, F.orthIdem.orth i j hij, F.orthIdem.orth j i (Ne.symm hij)] + module + have hkey : (a * a) • F.p i + (b * b) • F.p j + = ((a + b) / 2 * a) • F.p i + ((a + b) / 2 * b) • F.p j := by + rw [← hsqsq, ← hquart]; exact sq_mul_sq_eq x + have hτi : (0 : ℝ) < inner ℝ (F.p i) (F.p i) := inner_p_self_pos F i + have hτj : (0 : ℝ) < inner ℝ (F.p j) (F.p j) := inner_p_self_pos F j + have hij' : (inner ℝ (F.p i) (F.p j) : ℝ) = 0 := inner_p_p_of_ne F hij + have hji' : (inner ℝ (F.p j) (F.p i) : ℝ) = 0 := inner_p_p_of_ne F (Ne.symm hij) + have hA : a * a = (a + b) / 2 * a := by + have := congrArg (fun z : J => (inner ℝ z (F.p i) : ℝ)) hkey + simp only [inner_add_left, real_inner_smul_left, hji'] at this + field_simp [hij', hji'] at this + nlinarith [this, hτi] + have hB : b * b = (a + b) / 2 * b := by + have := congrArg (fun z : J => (inner ℝ z (F.p j) : ℝ)) hkey + simp only [inner_add_left, real_inner_smul_left, hij'] at this + field_simp [hij', hji'] at this + nlinarith [this, hτj] + have hsq : (a - b) ^ 2 = 0 := by nlinarith [hA, hB] + have := pow_eq_zero_iff (n := 2) (by norm_num) |>.mp hsq + linarith [this] + +/-- **`x ∘ x = a • (pᵢ + pⱼ)` for `x ∈ V_{ij}`**, with `a ≥ 0` and `a = 0` only at `x = 0`. + +The idempotent `pᵢ + pⱼ` is the unit of the Peirce subalgebra the block lives in, so this says +the square of a block element is a nonnegative multiple of that unit. -/ +theorem exists_sq_smul (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) : + ∃ a : ℝ, x * x = a • (F.p i + F.p j) + ∧ a * (inner ℝ (F.p i) (F.p i) : ℝ) = (2 : ℝ)⁻¹ * (inner ℝ x x : ℝ) := by + obtain ⟨a, b, hab⟩ := frameBlockRaw_mul_self_eq F hij hx hx + have hba : a = b := frameBlockRaw_sq_coeff_eq F hij hx hab + subst hba + refine ⟨a, by rw [hab]; module, ?_⟩ + have h := inner_sq_p_of_mem F hij hx + rw [hab] at h + simp only [inner_add_left, real_inner_smul_left, inner_p_p_of_ne F (Ne.symm hij)] at h + linarith [h] + +end Square + +/-! ## The key operator identity -/ + +/-- **`x ∘ (x ∘ y) = (a/4) • y`** for `x ∈ V_{ij}`, `y ∈ V_{jk}` with `x ∘ x = a • (pᵢ + pⱼ)`. + +This is the whole engine of coordinatization: it says `2 L_x` restricted to `V_{jk}` squares to +the scalar `a`, so a *normalised* `x` gives an involution `V_{jk} ≃ V_{ik}`, and it is what makes +the coordinate product multiplicative on norms. + +The proof is `lin_jordan` at `(x, x, y)` evaluated at `pⱼ`. Every term collapses: +`x ∘ y ∈ V_{ik}` is annihilated by `pⱼ`, `pⱼ` halves both `x` and `y`, and `x ∘ x` acts on `pⱼ` +as `a` and on `y` as `a/2`. What survives is `-(x ∘ (x ∘ y)) + (a/4) • y = 0`. + +★ The plain (unlinearised) Jordan identity gives **nothing** here: both of its sides reduce to +`½ a • (x ∘ y)` by the eigenvalue rules alone. The linearisation is essential. -/ +theorem block_sq_act (F : JordanFrame J n) {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) + (hik : i ≠ k) {x y : J} (hx : x ∈ frameBlockRaw F i j) (hy : y ∈ frameBlockRaw F j k) + {a : ℝ} (hxx : x * x = a • (F.p i + F.p j)) : + x * (x * y) = (a / 4) • y := by + have hxy : x * y ∈ frameBlockRaw F i k := frameBlockRaw_mul_middle F hij hjk hik hx hy + have hpjxy : (x * y) * F.p j = 0 := by + rw [_root_.mul_comm] + exact frameBlockRaw_mul_eq_zero F (Ne.symm hij) hjk hxy + have hpjx : x * F.p j = (2 : ℝ)⁻¹ • x := by + rw [_root_.mul_comm]; exact frameBlockRaw_mul_right_half F hij hx + have hpjy : y * F.p j = (2 : ℝ)⁻¹ • y := by + rw [_root_.mul_comm]; exact frameBlockRaw_mul_left_half F hjk hy + have hpiy : F.p i * y = 0 := frameBlockRaw_mul_eq_zero F hij hik hy + have hxxpj : (x * x) * F.p j = a • F.p j := by + rw [hxx] + simp only [smul_mul, _root_.add_mul, F.orthIdem.idem j, F.orthIdem.orth i j hij] + module + have hxxy : (x * x) * y = (a / 2) • y := by + rw [hxx] + simp only [smul_mul, _root_.add_mul, hpiy, frameBlockRaw_mul_left_half F hjk hy] + module + have H := lin_jordan x x y (F.p j) + rw [_root_.mul_comm y x, hpjxy, hpjx, hxxpj] at H + simp only [mul_zero, mul_smul_comm, hpjy, hxxy, _root_.mul_comm (x * y) x] at H + linear_combination (norm := module) -H + +/-! ## Consequences of the square rule -/ + +section SquareConsequences + +/-- Pairing `x ∘ x = a • (pᵢ + pⱼ)` against `pᵢ`. -/ +theorem sq_coeff_left (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) {a : ℝ} (hxx : x * x = a • (F.p i + F.p j)) : + a * (inner ℝ (F.p i) (F.p i) : ℝ) = (2 : ℝ)⁻¹ * (inner ℝ x x : ℝ) := by + have h := inner_sq_p_of_mem F hij hx + rw [hxx] at h + simp only [real_inner_smul_left, inner_add_left, inner_p_p_of_ne F (Ne.symm hij)] at h + linarith [h] + +/-- Pairing `x ∘ x = a • (pᵢ + pⱼ)` against `pⱼ`. -/ +theorem sq_coeff_right (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) {a : ℝ} (hxx : x * x = a • (F.p i + F.p j)) : + a * (inner ℝ (F.p j) (F.p j) : ℝ) = (2 : ℝ)⁻¹ * (inner ℝ x x : ℝ) := by + have hx' : x ∈ frameBlockRaw F j i := (frameBlockRaw_comm F i j) ▸ hx + have h := inner_sq_p_of_mem F (Ne.symm hij) hx' + rw [hxx] at h + simp only [real_inner_smul_left, inner_add_left, inner_p_p_of_ne F hij] at h + linarith [h] + +/-- A block element whose square vanishes is itself zero — positive-definiteness, not +formal reality. -/ +theorem eq_zero_of_sq_coeff_zero (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) (hxx : x * x = (0 : ℝ) • (F.p i + F.p j)) : x = 0 := by + have h := sq_coeff_left F hij hx hxx + rw [zero_mul] at h + have h0 : (inner ℝ x x : ℝ) = 0 := by linarith [h] + exact inner_self_eq_zero (𝕜 := ℝ) |>.mp h0 + +/-- **`⟪pᵢ, pᵢ⟫ = ⟪pⱼ, pⱼ⟫` as soon as `V_{ij}` contains a nonzero element.** + +The two pairings of `x ∘ x = a • (pᵢ + pⱼ)` both return `½‖x‖²`, so `a τᵢ = a τⱼ`, and `a ≠ 0` +because `a τᵢ = ½‖x‖² > 0`. This is what makes the coordinate norm form below unambiguous: +the normalisation constant is the same at every index a connection reaches. -/ +theorem inner_p_eq_of_sq (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) {a : ℝ} (hxx : x * x = a • (F.p i + F.p j)) (ha : a ≠ 0) : + (inner ℝ (F.p i) (F.p i) : ℝ) = (inner ℝ (F.p j) (F.p j) : ℝ) := + mul_left_cancel₀ ha ((sq_coeff_left F hij hx hxx).trans (sq_coeff_right F hij hx hxx).symm) + +end SquareConsequences + +/-! ## The composition law on the blocks + +★ This is the result the coordinate algebra is built out of: the blocks compose, so a +normalised element of `V_{ij}` acts on `V_{jk}` as an isometry up to the scalar `a`. -/ + +section Composition + +variable [FiniteDimensional ℝ J] + +omit [FiniteDimensional ℝ J] in +/-- **`‖x ∘ y‖² = (a/4)‖y‖²`** for `x ∈ V_{ij}`, `y ∈ V_{jk}`: `block_sq_act` paired against +`y` through the associativity of the inner product. -/ +theorem inner_mul_self_of_block (F : JordanFrame J n) {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) + (hik : i ≠ k) {x y : J} (hx : x ∈ frameBlockRaw F i j) (hy : y ∈ frameBlockRaw F j k) + {a : ℝ} (hxx : x * x = a • (F.p i + F.p j)) : + (inner ℝ (x * y) (x * y) : ℝ) = a / 4 * (inner ℝ y y : ℝ) := by + rw [inner_assoc x y (x * y), block_sq_act F hij hjk hik hx hy hxx, real_inner_smul_right] + +/-- **The composition law.** For `x ∈ V_{ij}` and `y ∈ V_{jk}` with `x ∘ x = a • (pᵢ + pⱼ)` and +`y ∘ y = b • (pⱼ + p_k)`, the product `2 (x ∘ y) ∈ V_{ik}` satisfies + + `(2 (x ∘ y)) ∘ (2 (x ∘ y)) = (a b) • (pᵢ + p_k)`. + +So the coefficient of the square — the norm form of the coordinate algebra — is multiplicative +under the doubled product `x ⊙ y = 2 (x ∘ y)`. Everything is bookkeeping around +`block_sq_act` except the identification `τᵢ = τⱼ`, which `inner_p_eq_of_sq` supplies. -/ +theorem block_mul_sq (F : JordanFrame J n) {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) + (hik : i ≠ k) {x y : J} (hx : x ∈ frameBlockRaw F i j) (hy : y ∈ frameBlockRaw F j k) + {a b : ℝ} (hxx : x * x = a • (F.p i + F.p j)) (hyy : y * y = b • (F.p j + F.p k)) : + ((2 : ℝ) • (x * y)) * ((2 : ℝ) • (x * y)) = (a * b) • (F.p i + F.p k) := by + rcases eq_or_ne a 0 with ha | ha + · subst ha + have hx0 : x = 0 := eq_zero_of_sq_coeff_zero F hij hx hxx + rw [hx0, zero_mul'] + simp + have hz : (2 : ℝ) • (x * y) ∈ frameBlockRaw F i k := + Submodule.smul_mem _ _ (frameBlockRaw_mul_middle F hij hjk hik hx hy) + obtain ⟨c, hc, hcspec⟩ := exists_sq_smul F hik hz + have hτ : (inner ℝ (F.p i) (F.p i) : ℝ) = (inner ℝ (F.p j) (F.p j) : ℝ) := + inner_p_eq_of_sq F hij hx hxx ha + have hyn : b * (inner ℝ (F.p j) (F.p j) : ℝ) = (2 : ℝ)⁻¹ * (inner ℝ y y : ℝ) := + sq_coeff_left F hjk hy hyy + have hzn : (inner ℝ ((2 : ℝ) • (x * y)) ((2 : ℝ) • (x * y)) : ℝ) + = a * (inner ℝ y y : ℝ) := by + rw [real_inner_smul_left, real_inner_smul_right, + inner_mul_self_of_block F hij hjk hik hx hy hxx] + ring + rw [hzn] at hcspec + have hτi : (0 : ℝ) < inner ℝ (F.p i) (F.p i) := inner_p_self_pos F i + have hcab : c = a * b := by + have h1 : c * (inner ℝ (F.p i) (F.p i) : ℝ) = a * b * (inner ℝ (F.p i) (F.p i) : ℝ) := by + linear_combination hcspec - a * hyn - a * b * hτ + exact mul_right_cancel₀ (ne_of_gt hτi) h1 + rw [hc, hcab] + +end Composition + +/-! ## Connectors + +A **connector** for the pair `(i, j)` is an element `c ∈ V_{ij}` with `c ∘ c = pᵢ + pⱼ`: a +square root of the rank-two idempotent inside the block. It exists exactly when the block is +nonzero, and `y ↦ 2 (c ∘ y)` is then an involutive linear isomorphism `V_{jk} ≃ V_{ik}`. -/ + +section Connector + +/-- A square root of `pᵢ + pⱼ` inside `V_{ij}`. -/ +def IsConnector (F : JordanFrame J n) (i j : Fin n) (c : J) : Prop := + c ∈ frameBlockRaw F i j ∧ c * c = F.p i + F.p j + +theorem IsConnector.mem {F : JordanFrame J n} {i j : Fin n} {c : J} (h : IsConnector F i j c) : + c ∈ frameBlockRaw F i j := h.1 + +theorem IsConnector.sq {F : JordanFrame J n} {i j : Fin n} {c : J} (h : IsConnector F i j c) : + c * c = F.p i + F.p j := h.2 + +theorem IsConnector.sq' {F : JordanFrame J n} {i j : Fin n} {c : J} (h : IsConnector F i j c) : + c * c = (1 : ℝ) • (F.p i + F.p j) := by rw [h.sq, one_smul] + +/-- A connector is symmetric in its two indices. -/ +theorem IsConnector.symm {F : JordanFrame J n} {i j : Fin n} {c : J} (h : IsConnector F i j c) : + IsConnector F j i c := + ⟨(frameBlockRaw_comm F i j) ▸ h.mem, by rw [h.sq, add_comm]⟩ + +/-- **`c ∘ (c ∘ y) = ¼ • y` for a connector `c` on `(i, j)` and `y ∈ V_{jk}`** — `block_sq_act` +at `a = 1`. -/ +theorem IsConnector.act {F : JordanFrame J n} {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) + (hik : i ≠ k) {c : J} (hc : IsConnector F i j c) {y : J} (hy : y ∈ frameBlockRaw F j k) : + c * (c * y) = (4 : ℝ)⁻¹ • y := by + have h := block_sq_act F hij hjk hik hc.mem hy hc.sq' + rw [h]; norm_num + +/-- The transfer map `y ↦ 2 (c ∘ y)` attached to a connector. -/ +def connMap (c : J) : J →ₗ[ℝ] J := (2 : ℝ) • jmulₗ J c + +@[simp] theorem connMap_apply (c y : J) : connMap c y = (2 : ℝ) • (c * y) := rfl + +/-- The transfer map carries `V_{jk}` into `V_{ik}`. -/ +theorem connMap_mem {F : JordanFrame J n} {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) + (hik : i ≠ k) {c : J} (hc : IsConnector F i j c) {y : J} (hy : y ∈ frameBlockRaw F j k) : + connMap c y ∈ frameBlockRaw F i k := + Submodule.smul_mem _ _ (frameBlockRaw_mul_middle F hij hjk hik hc.mem hy) + +/-- **The transfer map is an involution on `V_{jk}`.** Applying it twice multiplies by +`4 c ∘ (c ∘ ·) = 1`. Note the two applications run in opposite directions, `V_{jk} → V_{ik}` +and then `V_{ik} → V_{jk}`, so the hypothesis is used at both index orders. -/ +theorem connMap_connMap {F : JordanFrame J n} {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) + (hik : i ≠ k) {c : J} (hc : IsConnector F i j c) {y : J} (hy : y ∈ frameBlockRaw F j k) : + connMap c (connMap c y) = y := by + rw [connMap_apply, connMap_apply, mul_smul_comm, smul_smul, + IsConnector.act hij hjk hik hc hy, smul_smul] + norm_num + +variable [FiniteDimensional ℝ J] + +/-- **A nonzero block has a connector.** Normalise: `x ∘ x = a • (pᵢ + pⱼ)` with `a > 0`, and +`c := a^{-1/2} • x` has `c ∘ c = pᵢ + pⱼ`. -/ +theorem exists_isConnector (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) (hx0 : x ≠ 0) : ∃ c : J, IsConnector F i j c := by + obtain ⟨a, ha, haspec⟩ := exists_sq_smul F hij hx + have hxx : (0 : ℝ) < inner ℝ x x := real_inner_self_pos.mpr hx0 + have hτi : (0 : ℝ) < inner ℝ (F.p i) (F.p i) := inner_p_self_pos F i + have hapos : 0 < a := by nlinarith [haspec, hxx, hτi] + refine ⟨(Real.sqrt a)⁻¹ • x, Submodule.smul_mem _ _ hx, ?_⟩ + rw [smul_mul, mul_smul_comm, ha, smul_smul, smul_smul] + have hcoef : (Real.sqrt a)⁻¹ * (Real.sqrt a)⁻¹ * a = 1 := by + have hs : Real.sqrt a * Real.sqrt a = a := Real.mul_self_sqrt hapos.le + have hs0 : Real.sqrt a ≠ 0 := ne_of_gt (Real.sqrt_pos.mpr hapos) + field_simp + linarith [hs] + rw [hcoef, one_smul] + +/-- **The transfer map preserves the square coefficient.** If `y ∘ y = b • (pⱼ + p_k)` then +`(2 (c ∘ y)) ∘ (2 (c ∘ y)) = b • (pᵢ + p_k)` — `block_mul_sq` at `a = 1`. -/ +theorem connMap_sq {F : JordanFrame J n} {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) + (hik : i ≠ k) {c : J} (hc : IsConnector F i j c) {y : J} (hy : y ∈ frameBlockRaw F j k) + {b : ℝ} (hyy : y * y = b • (F.p j + F.p k)) : + connMap c y * connMap c y = b • (F.p i + F.p k) := by + have h := block_mul_sq F hij hjk hik hc.mem hy hc.sq' hyy + rw [connMap_apply, h, _root_.one_mul] + +/-- The transfer map carries connectors to connectors: `V_{jk} ∋ d ↦ 2 (c ∘ d)` is a connector +for `(i, k)`. -/ +theorem IsConnector.transfer {F : JordanFrame J n} {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) + (hik : i ≠ k) {c d : J} (hc : IsConnector F i j c) (hd : IsConnector F j k d) : + IsConnector F i k (connMap c d) := + ⟨connMap_mem hij hjk hik hc hd.mem, by + rw [connMap_sq hij hjk hik hc hd.mem hd.sq', one_smul]⟩ + +end Connector + +/-! ## Transfer as an equivalence, and connectivity + +★ The transfer map is not merely a map: it is an involution, hence a linear **isomorphism** +`V_{jk} ≃ V_{ik}`. So a frame all of whose blocks are connected has all its off-diagonal blocks +of the same dimension — the "coordinate algebra" does not depend on which block is read. -/ + +section Transfer + +/-- `pᵢ + pⱼ ≠ 0` for `i ≠ j`: pair it against `pᵢ`. -/ +theorem pair_ne_zero (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) : + F.p i + F.p j ≠ 0 := by + intro h + have := congrArg (fun z : J => (inner ℝ z (F.p i) : ℝ)) h + simp only [inner_add_left, inner_p_p_of_ne F (Ne.symm hij), add_zero, inner_zero_left] at this + exact absurd this (ne_of_gt (inner_p_self_pos F i)) + +/-- A connector is nonzero. -/ +theorem IsConnector.ne_zero {F : JordanFrame J n} {i j : Fin n} (hij : i ≠ j) {c : J} + (hc : IsConnector F i j c) : c ≠ 0 := by + intro h + have hsq := hc.sq + rw [h, zero_mul'] at hsq + exact pair_ne_zero F hij hsq.symm + +/-- **The transfer map as a linear equivalence `V_{jk} ≃ V_{ik}`.** Both directions are the +same map, read at the two index orders; `connMap_connMap` is both inverse laws. -/ +def connEquiv {F : JordanFrame J n} {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) (hik : i ≠ k) + {c : J} (hc : IsConnector F i j c) : + ↥(frameBlockRaw F j k) ≃ₗ[ℝ] ↥(frameBlockRaw F i k) where + toFun y := ⟨connMap c y, connMap_mem hij hjk hik hc y.2⟩ + map_add' y z := Subtype.ext (by simp only [Submodule.coe_add, map_add]) + map_smul' r y := Subtype.ext (by simp only [SetLike.val_smul, map_smul, RingHom.id_apply]) + invFun z := ⟨connMap c z, connMap_mem (Ne.symm hij) hik hjk hc.symm z.2⟩ + left_inv y := Subtype.ext (connMap_connMap hij hjk hik hc y.2) + right_inv z := Subtype.ext (connMap_connMap (Ne.symm hij) hik hjk hc.symm z.2) + +/-- **Connected blocks have the same dimension.** -/ +theorem finrank_frameBlockRaw_eq [FiniteDimensional ℝ J] {F : JordanFrame J n} {i j k : Fin n} + (hij : i ≠ j) (hjk : j ≠ k) (hik : i ≠ k) {c : J} (hc : IsConnector F i j c) : + Module.finrank ℝ ↥(frameBlockRaw F j k) = Module.finrank ℝ ↥(frameBlockRaw F i k) := + (connEquiv hij hjk hik hc).finrank_eq + +/-- **Connectivity is transitive.** A connector on `(i,j)` and one on `(j,k)` produce one on +`(i,k)`; this is `IsConnector.transfer` stated as the graph-theoretic fact the simplicity +argument consumes. ★ What is *not* proved anywhere in this tree is the other half of that +argument — that a **simple** `J` has every block nonzero. -/ +theorem exists_isConnector_trans [FiniteDimensional ℝ J] {F : JordanFrame J n} {i j k : Fin n} + (hij : i ≠ j) (hjk : j ≠ k) (hik : i ≠ k) + (h₁ : ∃ c : J, IsConnector F i j c) (h₂ : ∃ d : J, IsConnector F j k d) : + ∃ e : J, IsConnector F i k e := by + obtain ⟨c, hc⟩ := h₁ + obtain ⟨d, hd⟩ := h₂ + exact ⟨connMap c d, IsConnector.transfer hij hjk hik hc hd⟩ + +/-- A block is nonzero exactly when it has a connector. -/ +theorem exists_isConnector_iff [FiniteDimensional ℝ J] (F : JordanFrame J n) {i j : Fin n} + (hij : i ≠ j) : + (∃ c : J, IsConnector F i j c) ↔ frameBlockRaw F i j ≠ ⊥ := by + constructor + · rintro ⟨c, hc⟩ hbot + exact hc.ne_zero hij ((Submodule.eq_bot_iff _).mp hbot c hc.mem) + · intro hbot + obtain ⟨x, hx, hx0⟩ := (Submodule.ne_bot_iff _).mp hbot + exact exists_isConnector F hij hx hx0 + +end Transfer + +/-! ## The square coefficient as a quadratic form + +The coefficient of `x ∘ x` on `V_{ij}` is pinned by the inner product, so it is a genuine +quadratic form on the block rather than a choice. This is the form the coordinate algebra +carries. -/ + +section QuadForm + +/-- The coefficient of a multiple of `pᵢ + pⱼ` is unique — pair against `pᵢ`. -/ +theorem smul_pair_inj (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {a b : ℝ} + (h : a • (F.p i + F.p j) = b • (F.p i + F.p j)) : a = b := by + have := congrArg (fun z : J => (inner ℝ z (F.p i) : ℝ)) h + simp only [real_inner_smul_left, inner_add_left, inner_p_p_of_ne F (Ne.symm hij), + add_zero] at this + exact mul_right_cancel₀ (ne_of_gt (inner_p_self_pos F i)) this + +variable [FiniteDimensional ℝ J] + +/-- **`x ∘ x = (‖x‖² / 2τᵢ) • (pᵢ + pⱼ)`** for `x ∈ V_{ij}`, where `τᵢ = ⟪pᵢ, pᵢ⟫`. The +coefficient supplied by `exists_sq_smul` is exactly this ratio. -/ +theorem sq_eq_inner_smul (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) : + x * x = ((inner ℝ x x : ℝ) / (2 * (inner ℝ (F.p i) (F.p i) : ℝ))) • (F.p i + F.p j) := by + obtain ⟨a, ha, haspec⟩ := exists_sq_smul F hij hx + have hτ : (0 : ℝ) < inner ℝ (F.p i) (F.p i) := inner_p_self_pos F i + have : a = (inner ℝ x x : ℝ) / (2 * (inner ℝ (F.p i) (F.p i) : ℝ)) := by + field_simp + linarith [haspec] + rw [ha, this] + +end QuadForm + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean new file mode 100644 index 0000000000..23b99eb07d --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean @@ -0,0 +1,120 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc + + +/-! +# Formal reality, and the absence of nilpotents + +A Euclidean Jordan algebra is a finite-dimensional real Jordan algebra that is **formally +real**: a sum of squares vanishes only if every summand does. This file adds that hypothesis +as a mixin and draws the consequence the spectral theorem needs first — +**`nilpotent ⟹ zero`**. + +The nilpotence result is the first place in this development where **Albert's theorem does +real work**: `x^{k+1} ∘ x^{k+1} = x^{2k+2}` is a *product* identity, so without +`jpow_mul_jpow` there is no way to turn "some power vanishes" into "a square vanishes", +which is the only thing formal reality can see. + +## What is here + +* `IsFormallyReal` — the mixin, stated over a `Finset` sum so that it is the usual + many-summand condition rather than the two-summand special case. +* `eq_zero_of_mul_self_eq_zero` — the one-summand case: `x ∘ x = 0 ⟹ x = 0`. +* `eq_zero_of_jpow_eq_zero` — **no nilpotents**: if any power of `x` vanishes, `x = 0`. + +## What is not + +No trace form, no inner product, no finite-dimensionality, and **no spectral theorem**. The +spectral theorem additionally needs `ℝ[x]` to be finite-dimensional and reduced, and then the +classification of finite-dimensional reduced commutative `ℝ`-algebras. Formal reality is what +supplies "reduced"; the rest is not built. + +★ **The hypothesis has a carrier.** `EuclideanJordan/Witness.lean` carries +`instIsFormallyReal : IsFormallyReal (HermitianMat d 𝕜)`, so both theorems below are live on +`H_d(𝕜)` (`hermitian_eq_zero_of_jpow_eq_zero`); neither is conditional on a premise nothing +satisfies. + +★ The proof needed nothing new — `inner_self_nonneg` and `InnerProductCore.definite` are +vendored and `symmMul_self` says the Jordan square *is* the matrix square. What was missing was +the application, which is the third time in this arc that a residue named a hypothesis and a +conclusion whose connecting lemma was already in the tree (after row 35 and the FK fields). +**When a file's own docstring declares an exposure, try to close it before writing the sentence +that documents it.** +-/ + +namespace EuclideanJordan + +open Finset + +/-- **Formal reality.** A sum of squares vanishes only if every summand does. Stated over an +arbitrary `Finset` index rather than for two elements: the two-summand version does not +obviously imply the general one in a non-associative setting, because `x ∘ x + y ∘ y` need +not itself be a square. -/ +class IsFormallyReal (J : Type*) [Mul J] [AddCommMonoid J] : Prop where + /-- A vanishing sum of squares has vanishing summands. -/ + eq_zero_of_sum_mul_self : ∀ {ι : Type} (s : Finset ι) (f : ι → J), + ∑ i ∈ s, f i * f i = 0 → ∀ i ∈ s, f i = 0 + +section Basic + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsFormallyReal J] + +/-- The one-summand case: an element whose square vanishes is zero. -/ +theorem eq_zero_of_mul_self_eq_zero {x : J} (h : x * x = 0) : x = 0 := by + have := IsFormallyReal.eq_zero_of_sum_mul_self ({0} : Finset ℕ) (fun _ => x) (by simpa using h) + exact this 0 (Finset.mem_singleton_self 0) + +end Basic + +section Nilpotent + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsFormallyReal J] + +omit [IsFormallyReal J] in +/-- If one power vanishes, every higher power does. Uses Albert's theorem to split +`x^{m+1} = x^{n+1} ∘ x^{m−n}`. -/ +theorem jpow_eq_zero_of_le {x : J} {n : ℕ} (h : jpow x n = 0) {m : ℕ} (hm : n ≤ m) : + jpow x m = 0 := by + rcases Nat.eq_or_lt_of_le hm with rfl | hlt + · exact h + · have hsplit : jpow x n * jpow x (m - n - 1) = jpow x m := by + rw [jpow_mul_jpow] + congr 1 + omega + rw [← hsplit, h, zero_mul] + +/-- **A formally real Jordan algebra has no nilpotents.** If any power of `x` vanishes then +`x = 0`. + +★ This is where Albert's theorem earns its place. Formal reality can only see *squares*, and +turning "some power vanishes" into "a square vanishes" is exactly the product identity +`jpow_mul_jpow` supplies. In this file's indexing (`jpow x n = x^{n+1}`) the descent from +`jpow x (k+1) = 0` runs: `jpow x (k+k+1) = 0` by `jpow_eq_zero_of_le`, that element **is** +`jpow x k * jpow x k`, so formal reality gives `jpow x k = 0`, and the index has strictly +dropped. -/ +theorem eq_zero_of_jpow_eq_zero {x : J} : ∀ (n : ℕ), jpow x n = 0 → x = 0 := by + intro n + induction n using Nat.strong_induction_on with + | _ n ih => + intro h + match n with + | 0 => simpa using h + | (k + 1) => + have hsq : jpow x k * jpow x k = 0 := by + rw [jpow_mul_jpow] + exact jpow_eq_zero_of_le h (by omega) + exact ih k (by omega) (eq_zero_of_mul_self_eq_zero hsq) + +end Nilpotent + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean new file mode 100644 index 0000000000..82ce5142a6 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean @@ -0,0 +1,176 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal +import Mathlib.Algebra.BigOperators.Ring.Finset + + +/-! +# Orthogonal idempotent families, and three Faraut–Korányi facts + +Three Faraut–Korányi facts about a Jordan frame, each of which a coalescence argument would +otherwise have to carry as a hypothesis: + +| content | derived here as | +| --- | --- | +| scalar-on-`range q` and `J₂(q)` operator-commute | `opCommute_scalarOn_frame` | +| `V_{ij} ⊆ J₂(pᵢ + pⱼ)` | `mem_J2_of_half_half` | +| `rᵢ = rⱼ ⟹ a(r)` is scalar on `range(pᵢ + pⱼ)` | `diagFamily_scalarOn` | + +**All three are theorems rather than hypotheses**, given `EuclideanJordan/PeirceMul.lean`. + +★ **But not all three are theorems of the same depth, and the first draft of this paragraph +claimed they were** ("all three are theorems of the Jordan identity"). Only +`opCommute_scalarOn_frame` uses the Jordan identity — through `EuclideanJordan/PeirceMul.lean`. The +other two do not use it at all: `mem_J2_of_half_half` is `1/2 + 1/2 = 1`, and +`diagFamily_scalarOn` is a `Finset` split. Their `omit` lines say so, and a reader should +take the FK content of this file to be **one** theorem plus two pieces of bookkeeping that +become available once the frame equations exist. The bookkeeping still has to be done; it +is just not where the difficulty is. + +## The hypotheses, stated exactly + +Nothing below is stated over a structure that bundles a frame. Each theorem takes as explicit +hypotheses exactly the frame equations it uses: + +* `p i * p i = p i` and `p i * p j = 0` for `i ≠ j` — bundled as `IsOrthIdemFamily`; +* where a diagonal element is involved, that it is presented as `∑ k, f k • p k`. + +★★ **A consumer that carries its Jordan product as a bundled bilinear map cannot apply these +lemmas without a bridge, and the reason is a typeclass diamond rather than a missing +theorem.** This layer uses the *typeclass* `Mul J` from `NonUnitalNonAssocCommRing` together +with Mathlib's `IsCommJordan`. A structure carrying the product as +`jordan : J →ₗ[ℝ] J →ₗ[ℝ] J` over `[NormedAddCommGroup J] [InnerProductSpace ℝ J]`, with +operator-commutation defined through that map, supplies a *different* `AddCommGroup J` from +the one under `NonUnitalNonAssocCommRing`. With both in scope `Module ℝ J` fails to synthesise +at `peirceOne`'s use site: it is an `AddCommGroup` diamond, not a gap in the mathematics. +`EuclideanJordan/Bridge.lean` resolves it by building the ring on the *ambient* additive group, +so only one `AddCommGroup` is ever in play. Concrete carriers are unaffected — +`EuclideanJordan/Witness.lean` uses both worlds on `HermitianMat`, where the two are the same instance. + +★ **Completeness (`∑ p i = e`) is not assumed anywhere in this file.** None of the three +facts needs it; it is what the *spectral* theorem produces and what the rank argument +consumes. Keeping it out makes visible which results are independent of the spectral theorem. +-/ + +namespace EuclideanJordan + +open Finset + +section Family + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] +variable {ι : Type*} [Fintype ι] [DecidableEq ι] + +/-- A family of pairwise-orthogonal idempotents. Completeness is deliberately *not* part of +this definition — see the module docstring. -/ +structure IsOrthIdemFamily (p : ι → J) : Prop where + /-- Each member is idempotent. -/ + idem : ∀ i, p i * p i = p i + /-- Distinct members are orthogonal. -/ + orth : ∀ i j, i ≠ j → p i * p j = 0 + +namespace IsOrthIdemFamily + +variable {p : ι → J} + +omit [IsCommJordan J] [Module ℝ J] [Fintype ι] [DecidableEq ι] in +/-- **A sum over any subset of an orthogonal idempotent family is an idempotent.** In +particular `p i + p j` is, which is the rank-two block the results below run on. -/ +theorem sum_idem (hp : IsOrthIdemFamily p) (s : Finset ι) : + (∑ i ∈ s, p i) * (∑ i ∈ s, p i) = ∑ i ∈ s, p i := by + rw [Finset.sum_mul_sum] + refine Finset.sum_congr rfl fun i hi => ?_ + rw [Finset.sum_eq_single i (fun j hj hne => hp.orth i j (Ne.symm hne)) (fun h => absurd hi h)] + exact hp.idem i + +omit [IsCommJordan J] [Module ℝ J] [Fintype ι] [DecidableEq ι] in +/-- A member outside a subset is orthogonal to that subset's sum. -/ +theorem sum_mul_of_notMem (hp : IsOrthIdemFamily p) {s : Finset ι} {k : ι} (hk : k ∉ s) : + (∑ i ∈ s, p i) * p k = 0 := by + rw [Finset.sum_mul] + refine Finset.sum_eq_zero fun i hi => hp.orth i k ?_ + rintro rfl + exact hk hi + +end IsOrthIdemFamily + +end Family + +section Fields + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] +variable {ι : Type*} [Fintype ι] [DecidableEq ι] {p : ι → J} + +omit [IsCommJordan J] [IsScalarTower ℝ J J] [Fintype ι] [DecidableEq ι] in +/-- **The block containment, derived.** An element halved by `p i` and by `p j` — +that is, an element of the coherence block `V_{ij}` — lies in `J₂(p i + p j)`. + +The proof is that `1/2 + 1/2 = 1`; the content is entirely in the *definition* of `V_{ij}` +as a joint half-eigenspace, which is what the Peirce theory of `EuclideanJordan/Peirce.lean` licenses. -/ +theorem mem_J2_of_half_half {i j : ι} {x : J} (hi : p i * x = (2 : ℝ)⁻¹ • x) + (hj : p j * x = (2 : ℝ)⁻¹ • x) : (p i + p j) * x = x := by + rw [add_mul, hi, hj] + module + +omit [IsCommJordan J] [Module ℝ J] [IsScalarTower ℝ J J] [Fintype ι] [DecidableEq ι] in +/-- The complement of a rank-two block annihilates it: `(p i + p j) ∘ p k = 0` for +`k ∉ {i, j}`. -/ +theorem pair_mul_of_ne (hp : IsOrthIdemFamily p) {i j k : ι} (hki : k ≠ i) (hkj : k ≠ j) : + (p i + p j) * p k = 0 := by + rw [add_mul, hp.orth i k (Ne.symm hki), hp.orth j k (Ne.symm hkj), add_zero] + +omit [IsCommJordan J] in +/-- The off-block part of a diagonal family is annihilated by the block. -/ +theorem pair_mul_offblock (hp : IsOrthIdemFamily p) (f : ι → ℝ) (i j : ι) : + (p i + p j) * (∑ k ∈ univ \ {i, j}, f k • p k) = 0 := by + rw [Finset.mul_sum] + refine Finset.sum_eq_zero fun k hk => ?_ + have hk' := Finset.mem_sdiff.mp hk + have hki : k ≠ i := fun h => hk'.2 (by simp [h]) + have hkj : k ≠ j := fun h => hk'.2 (by simp [h]) + rw [mul_smul_comm', pair_mul_of_ne hp hki hkj, smul_zero] + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +/-- **The scalar-on-a-block decomposition, derived.** When two coordinates of `f` agree, the +diagonal family `∑ f k • p k` is *scalar on the range of* `p i + p j`: it splits as +`f i • (p i + p j)` plus a part the block annihilates. + +The decomposition is exhibited rather than assumed. -/ +theorem diagFamily_scalarOn (f : ι → ℝ) {i j : ι} (hij : i ≠ j) (h : f i = f j) : + ∑ k, f k • p k = f i • (p i + p j) + ∑ k ∈ univ \ {i, j}, f k • p k := by + have hsd := Finset.sum_sdiff (f := fun k => f k • p k) (Finset.subset_univ ({i, j} : Finset ι)) + rw [← hsd, Finset.sum_pair hij, ← h, ← smul_add] + abel + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +/-- The `i = j` case of `diagFamily_scalarOn`, for a consumer whose scalar-on-a-block +hypothesis carries no `i ≠ j` side condition. -/ +theorem diagFamily_scalarOn_self (f : ι → ℝ) (i : ι) : + ∑ k, f k • p k = (f i / 2) • (p i + p i) + ∑ k ∈ univ \ {i}, f k • p k := by + have hsd := Finset.sum_sdiff (f := fun k => f k • p k) (Finset.subset_univ ({i} : Finset ι)) + rw [← hsd, Finset.sum_singleton] + have : (f i / 2) • (p i + p i) = f i • p i := by module + rw [this] + abel + +/-- **The operator-commutation fact, derived.** For a +rank-two block `q = p i + p j` of an orthogonal idempotent family, a diagonal family with +`f i = f j` operator-commutes with every element of `J₂(q)`. -/ +theorem opCommute_scalarOn_frame (hp : IsOrthIdemFamily p) (f : ι → ℝ) {i j : ι} (hij : i ≠ j) + (h : f i = f j) {b : J} (hb : (p i + p j) * b = b) (w : J) : + (∑ k, f k • p k) * (b * w) = b * ((∑ k, f k • p k) * w) := + opCommute_scalarOn (add_idem_of_orthogonal (hp.idem i) (hp.idem j) (hp.orth i j hij)) + (diagFamily_scalarOn f hij h) (pair_mul_offblock hp f i j) hb w + +end Fields + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean new file mode 100644 index 0000000000..3a93139925 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean @@ -0,0 +1,272 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Rank + + +/-! +# Every nontrivial finite-dimensional Euclidean Jordan algebra carries a Jordan frame + +`EuclideanJordan/Rank.lean` defines `JordanFrame J n` — a complete family of `n` pairwise-orthogonal +primitive idempotents — and carries it as *data*, because a rank hypothesis supplies one. This file proves such data always exists: `exists_jordanFrame`, by strong +induction on `Module.finrank ℝ J` down the Peirce decomposition of a nontrivial idempotent. + +The induction itself is the expected one. If `1` is primitive the one-element family +`fun _ : Fin 1 => (1 : J)` is a frame. Otherwise primitivity fails at its third clause, which hands over an idempotent `c` with +`c ≠ 0` and `c ≠ 1`; `EuclideanJordan/PeirceSubalgebra.lean` makes `J₂(c)` and `J₀(c)` Euclidean Jordan +algebras with units `c` and `1 - c` and drops the dimension at both; and the two frames obtained +from the induction hypothesis concatenate along `Fin.append`. + +## ★ The step the build plan missed + +The plan priced this module as "mostly `Fin n ⊕ Fin m ≃ Fin (n + m)` re-indexing, not +mathematics". The re-indexing is indeed routine — it is `exists_frame_of_split` below, five +`Fin.addCases` splits and a `Fin.sum_univ_add`, and it compiled on the first attempt. The +prediction is wrong about where the content is. Concatenating the two frames requires each +member to be primitive **in `J`**, and what the induction hypothesis supplies is primitivity +*inside* the subalgebra. Those differ: the ambient statement quantifies over every idempotent +`y` of `J` with `d ∘ y = y`, the subalgebra statement only over those that additionally lie in +`J₂(c)`. Closing the gap is exactly + +> `J₂(d) ⊆ J₂(c)` for `d` an idempotent of `J₂(c)`, + +which is `eigen_one_of_eigen_one` below, and which is in neither +`EuclideanJordan/PeirceSubalgebra.lean` nor `EuclideanJordan/Rank.lean`. + +★ **What makes it work is changing which idempotent one decomposes at.** Attacking it at `c` — +rewriting `c ∘ x` as `c ∘ (d ∘ x)` and trying to move `c` inwards — is circular, because moving +`c` past `d` is what needs the conclusion; `EuclideanJordan/PeirceMul.lean`'s `mul_comm_of_eigen_one` only +relocates that difficulty: `mul_comm_of_eigen_one` at `(c, d, x)` yields +`c ∘ x = d ∘ (c ∘ x)`, which puts `c ∘ x` back inside `J₂(d)` rather than identifying it with +`x`. Decomposing at **`d`** instead makes it immediate: `d` annihilates `c - d` +(`mul_sub_eq_zero_of_eigen_one`, one line), so `c - d` lies in `J₀(d)` while `x` lies in +`J₂(d)`, and `EuclideanJordan/PeirceMul.lean`'s `eigen_one_mul_zero` — the rule that the two *extreme* +Peirce components of a single idempotent annihilate each other — kills `(c - d) ∘ x` outright. +Then `c ∘ x = d ∘ x + (c - d) ∘ x = x`. (`c - d` is also idempotent, but that is neither +proved nor used here.) + +★ `eigen_one_of_eigen_one` does **not** need `c` to be idempotent. The hypothesis was written +into the first draft, the unused-variable linter flagged it, and it was deleted rather than +underscored. Nothing is claimed about the remaining three: `hd`, `hcd` and `hx` are each used +by the proof, but whether any of them could be dropped was not tested. `peirceOneSub_le` +restates the lemma as the submodule inequality, and *there* `hc` reappears — not because the +mathematics needs it but because `peirceOneSub` is indexed by an idempotency proof, so the +ambient carrier cannot be named without one. + +## Scope + + +★ The frame produced here carries **no claim about its cardinality**. `exists_jordanFrame` +existentially quantifies `n`, and `EuclideanJordan/Rank.lean`'s module docstring records why `rank J = n` +is not available: `rank J` is a supremum over *all* orthogonal families of nonzero idempotents, +and bounding such a family by a frame's cardinality needs the frame Peirce decomposition or +frame conjugacy. Nothing here is a step towards it. Do not read `exists_jordanFrame` as +"`J` has rank `n`". +-/ + +noncomputable section + +namespace EuclideanJordan + +universe u + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] + +/-! ## `J₂(d) ⊆ J₂(c)`, the step the induction turns on -/ + +/-- For an idempotent `d` of `J₂(c)`, the complement `c - d` lies in `J₀(d)`: two rewrites and +`d ∘ d = d`. Idempotency of `c` is not used. -/ +theorem mul_sub_eq_zero_of_eigen_one {c d : J} (hd : d * d = d) (hcd : c * d = d) : + d * (c - d) = 0 := by + rw [mul_sub, _root_.mul_comm d c, hcd, hd, sub_self] + +/-- ★ **`J₂(d) ⊆ J₂(c)` for `d` an idempotent of `J₂(c)`.** An element fixed by `d` is fixed by +`c`. + +The proof decomposes at `d`, not at `c` — see the module docstring. `c - d` sits in `J₀(d)` by +`mul_sub_eq_zero_of_eigen_one` and `x` sits in `J₂(d)` by hypothesis, so `eigen_one_mul_zero` +at `d` gives +`x ∘ (c - d) = 0`, and `c ∘ x = d ∘ x + (c - d) ∘ x = x + 0`. + +Idempotency of `c` is not required. -/ +theorem eigen_one_of_eigen_one {c d x : J} (hd : d * d = d) (hcd : c * d = d) + (hx : d * x = x) : c * x = x := by + have h0 : (c - d) * x = 0 := by + rw [_root_.mul_comm, eigen_one_mul_zero hd hx (mul_sub_eq_zero_of_eigen_one hd hcd)] + have h : c * x = d * x + (c - d) * x := by rw [← add_mul, add_sub_cancel] + rw [h, h0, hx, add_zero] + +/-- The submodule form of `eigen_one_of_eigen_one`. `hc` is present only because +`peirceOneSub` is indexed by an idempotency proof. -/ +theorem peirceOneSub_le {c d : J} (hc : c * c = c) (hd : d * d = d) (hcd : c * d = d) : + peirceOneSub hd ≤ peirceOneSub hc := + fun _x hx => eigen_one_of_eigen_one hd hcd hx + +/-! ## Primitivity transfers out of a Peirce subalgebra + +Primitivity inside `J₂(c)` (resp. `J₀(c)`) implies primitivity in `J`. Apart from the +restatement `peirceOneSub_le`, which nothing downstream consumes, this is where +`eigen_one_of_eigen_one` is used, and it is the reason the induction closes. -/ + +/-- Primitivity from a weakened third clause: an idempotent `d` fixed by `u` is primitive as +soon as its splitting condition is checked on those idempotents that `u` also fixes, because +`eigen_one_of_eigen_one` supplies that side condition for free. -/ +theorem isPrimitive_of_unit {u d : J} (hd : d * d = d) (hd0 : d ≠ 0) (hud : u * d = d) + (h : ∀ y : J, y * y = y → d * y = y → u * y = y → y = 0 ∨ y = d) : IsPrimitive d := + ⟨hd, hd0, fun y hy hdy => h y hy hdy (eigen_one_of_eigen_one hd hud hdy)⟩ + +/-- A primitive idempotent of `J₂(c)` is primitive in `J`. -/ +theorem isPrimitive_coe_of_peirceOne {c : J} (hc : c * c = c) {d : ↥(peirceOneSub hc)} + (hd : IsPrimitive d) : IsPrimitive (d : J) := by + refine isPrimitive_of_unit (congrArg Subtype.val hd.idem) + (fun h => hd.ne_zero (Subtype.ext (h.trans (ZeroMemClass.coe_zero _).symm))) d.2 ?_ + intro y hy hdy hcy + rcases hd.eq_zero_or_eq (d := ⟨y, hcy⟩) (Subtype.ext hy) (Subtype.ext hdy) with h | h + · exact Or.inl (congrArg Subtype.val h) + · exact Or.inr (congrArg Subtype.val h) + +/-- A primitive idempotent of `J₀(c)` is primitive in `J`. The unit fed to +`isPrimitive_of_unit` is `1 - c`, and the side condition `(1 - c) ∘ y = y` is membership in +`J₀(c)` read backwards. -/ +theorem isPrimitive_coe_of_peirceZero {c : J} (hc : c * c = c) {d : ↥(peirceZeroSub hc)} + (hd : IsPrimitive d) : IsPrimitive (d : J) := by + have hd2 : c * (d : J) = 0 := d.2 + have hud : ((1 : J) - c) * (d : J) = (d : J) := by + rw [sub_mul, EuclideanJordanAlgebra.one_mul, hd2, sub_zero] + refine isPrimitive_of_unit (congrArg Subtype.val hd.idem) + (fun h => hd.ne_zero (Subtype.ext (h.trans (ZeroMemClass.coe_zero _).symm))) hud ?_ + intro y hy hdy hcy + have hmem : y ∈ peirceZeroSub hc := by + rw [sub_mul, EuclideanJordanAlgebra.one_mul] at hcy + exact sub_eq_self.mp hcy + rcases hd.eq_zero_or_eq (d := ⟨y, hmem⟩) (Subtype.ext hy) (Subtype.ext hdy) with h | h + · exact Or.inl (congrArg Subtype.val h) + · exact Or.inr (congrArg Subtype.val h) + +/-! ## Concatenating the two frames -/ + +/-- **The frames of `J₂(c)` and `J₀(c)` concatenate to a frame of `J`.** Along `Fin.append`; +the four orthogonality cases are the two blocks' own orthogonality and, across the blocks, +`eigen_one_mul_zero` at `c`. Completeness adds the two units `c` and `1 - c`. -/ +theorem exists_frame_of_split {c : J} (hc : c * c = c) {n₁ n₀ : ℕ} + (F₁ : JordanFrame ↥(peirceOneSub hc) n₁) (F₀ : JordanFrame ↥(peirceZeroSub hc) n₀) : + Nonempty (JordanFrame J (n₁ + n₀)) := by + classical + set a : Fin n₁ → J := fun i => (F₁.p i : J) with ha + set b : Fin n₀ → J := fun i => (F₀.p i : J) with hb + have haidem : ∀ i, a i * a i = a i := fun i => congrArg Subtype.val (F₁.orthIdem.idem i) + have hbidem : ∀ i, b i * b i = b i := fun i => congrArg Subtype.val (F₀.orthIdem.idem i) + have haorth : ∀ i j, i ≠ j → a i * a j = 0 := fun i j hij => + congrArg Subtype.val (F₁.orthIdem.orth i j hij) + have hborth : ∀ i j, i ≠ j → b i * b j = 0 := fun i j hij => + congrArg Subtype.val (F₀.orthIdem.orth i j hij) + have hcross : ∀ i j, a i * b j = 0 := fun i j => eigen_one_mul_zero hc (F₁.p i).2 (F₀.p j).2 + refine ⟨{ p := Fin.append a b, orthIdem := ⟨?_, ?_⟩, primitive := ?_, complete := ?_ }⟩ + · refine Fin.addCases ?_ ?_ + · intro i; simp only [Fin.append_left]; exact haidem i + · intro i; simp only [Fin.append_right]; exact hbidem i + · refine Fin.addCases ?_ ?_ + · intro i + refine Fin.addCases ?_ ?_ + · intro j hij + simp only [Fin.append_left] + exact haorth i j (fun h => hij (by rw [h])) + · intro j _ + simp only [Fin.append_left, Fin.append_right] + exact hcross i j + · intro i + refine Fin.addCases ?_ ?_ + · intro j _ + simp only [Fin.append_left, Fin.append_right] + rw [_root_.mul_comm]; exact hcross j i + · intro j hij + simp only [Fin.append_right] + exact hborth i j (fun h => hij (by rw [h])) + · refine Fin.addCases ?_ ?_ + · intro i; simp only [Fin.append_left] + exact isPrimitive_coe_of_peirceOne hc (F₁.primitive i) + · intro i; simp only [Fin.append_right] + exact isPrimitive_coe_of_peirceZero hc (F₀.primitive i) + · rw [Fin.sum_univ_add] + simp only [Fin.append_left, Fin.append_right] + have h1 : ∑ i, a i = c := by + rw [ha, ← AddSubmonoidClass.coe_finsetSum, F₁.complete] + exact coe_one_peirceOneSub hc + have h0 : ∑ i, b i = 1 - c := by + rw [hb, ← AddSubmonoidClass.coe_finsetSum, F₀.complete] + exact coe_one_peirceZeroSub hc + rw [h1, h0]; abel + +/-! ## The induction -/ + +/-- The induction carrier: quantified over the *type*, because the recursive calls land on the +two Peirce subalgebras rather than on `J`. The dimension bound is a `≤`, so the outer +induction is the ordinary one on `ℕ` rather than a well-founded recursion. -/ +theorem exists_jordanFrame_of_finrank_le (N : ℕ) : ∀ (J : Type u) [NormedAddCommGroup J] + [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] [FiniteDimensional ℝ J], + Module.finrank ℝ J ≤ N → (1 : J) ≠ 0 → ∃ n, Nonempty (JordanFrame J n) := by + induction N with + | zero => + intro J _ _ _ _ hle h1 + exact absurd (finrank_zero_iff_forall_zero.mp (Nat.le_zero.mp hle) 1) h1 + | succ N ih => + intro J _ _ _ _ hle h1 + by_cases hprim : IsPrimitive (1 : J) + · have horth : IsOrthIdemFamily (fun _ : Fin 1 => (1 : J)) := by + refine ⟨fun _ => EuclideanJordanAlgebra.one_mul 1, fun i j hij => ?_⟩ + exact absurd (Subsingleton.elim i j) hij + exact ⟨1, ⟨⟨fun _ => 1, horth, fun _ => hprim, by simp⟩⟩⟩ + · obtain ⟨c, hcidem, hc0, hc1⟩ : ∃ c : J, c * c = c ∧ c ≠ 0 ∧ c ≠ 1 := by + by_contra hno + refine hprim ⟨EuclideanJordanAlgebra.one_mul 1, h1, fun d hd _ => ?_⟩ + by_cases h : d = 0 + · exact Or.inl h + · by_cases h' : d = 1 + · exact Or.inr h' + · exact absurd ⟨d, hd, h, h'⟩ hno + have hlt1 : Module.finrank ℝ ↥(peirceOneSub hcidem) ≤ N := + Nat.lt_succ_iff.mp (lt_of_lt_of_le (finrank_peirceOneSub_lt hcidem hc1) hle) + have hlt0 : Module.finrank ℝ ↥(peirceZeroSub hcidem) ≤ N := + Nat.lt_succ_iff.mp (lt_of_lt_of_le (finrank_peirceZeroSub_lt hcidem hc0) hle) + have hu1 : (1 : ↥(peirceOneSub hcidem)) ≠ 0 := fun h => hc0 (by + have hco := congrArg Subtype.val h + rwa [coe_one_peirceOneSub, ZeroMemClass.coe_zero] at hco) + have hu0 : (1 : ↥(peirceZeroSub hcidem)) ≠ 0 := fun h => hc1 (by + have hco := congrArg Subtype.val h + rw [coe_one_peirceZeroSub, ZeroMemClass.coe_zero] at hco + linear_combination (norm := module) -hco) + obtain ⟨n₁, ⟨F₁⟩⟩ := ih _ hlt1 hu1 + obtain ⟨n₀, ⟨F₀⟩⟩ := ih _ hlt0 hu0 + exact ⟨n₁ + n₀, exists_frame_of_split hcidem F₁ F₀⟩ + +/-- **M3.** Every finite-dimensional Euclidean Jordan algebra with `1 ≠ 0` carries a Jordan +frame. The cardinality is existentially quantified and is *not* claimed to be the rank — see +the module docstring. -/ +theorem exists_jordanFrame (J : Type u) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + [EuclideanJordanAlgebra J] [FiniteDimensional ℝ J] (h1 : (1 : J) ≠ 0) : + ∃ n, Nonempty (JordanFrame J n) := + exists_jordanFrame_of_finrank_le (Module.finrank ℝ J) J le_rfl h1 + +/-- `1 ≠ 0` is exactly nontriviality: if `1 = 0` then `x = 1 ∘ x = 0` for every `x`. -/ +theorem one_ne_zero_of_nontrivial [Nontrivial J] : (1 : J) ≠ 0 := by + intro h + obtain ⟨x, y, hxy⟩ := exists_pair_ne J + refine hxy ?_ + have hz : ∀ z : J, z = 0 := fun z => by + rw [← EuclideanJordanAlgebra.one_mul z, h, EuclideanJordanAlgebra.zero_mul'] + rw [hz x, hz y] + +/-- **M3, stated over `Nontrivial`.** -/ +theorem exists_jordanFrame' (J : Type u) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + [EuclideanJordanAlgebra J] [FiniteDimensional ℝ J] [Nontrivial J] : + ∃ n, Nonempty (JordanFrame J n) := + exists_jordanFrame J one_ne_zero_of_nontrivial + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean new file mode 100644 index 0000000000..a085e8cdb3 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean @@ -0,0 +1,466 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists + + +/-! +# The frame Peirce decomposition: `J = ⨁_{i ≤ j} V_{ij}` + +For a Jordan frame `F = (p₁, …, pₙ)` this file builds the blocks + +* `V_{ii} := J₂(p i) = {x | p i ∘ x = x}`, +* `V_{ij} := {x | p i ∘ x = ½ • x ∧ p j ∘ x = ½ • x}` for `i ≠ j`, + +as a family `frameBlock F : Sym2 (Fin n) → Submodule ℝ J` (`Sym2` because `V_{ij} = V_{ji}`), +and proves `frameBlock_isInternal : DirectSum.IsInternal (frameBlock F)` — independence +(`frameBlock_iSupIndep`) plus spanning (`frameBlock_iSup_eq_top`). + +`frameBlockRaw_self` records that the diagonal block really is `EuclideanJordan/PeirceSubalgebra.lean`'s +`peirceOneSub`, so the two definitions of `J₂(p i)` in the tree do not drift apart. + +## ★ The reprice held in direction and was wrong about the mechanism + +The build plan and the task brief both priced this module as risk #2, with the diagnosis: the +eigenvalue combinatorics is *already done* in `EuclideanJordan/Pattern.lean`, what is missing is (a) +simultaneous diagonalisation / spanning and (b) the `DirectSum.IsInternal` packaging, and the +mechanism for (a) is to expand + + id = ∏ᵢ (P₁(pᵢ) + P½(pᵢ) + P₀(pᵢ)) = ∑_{μ : Fin n → Fin 3} ∏ᵢ P_{μ i}(pᵢ) + +and let the `Pattern` lemmas annihilate every `μ` outside the allowed set — the cost driver +being that "expand a `Finset.prod` of sums of commuting idempotent projections over a `Fintype` +and package it as `DirectSum.IsInternal`" is the archetypal Lean chore. + +(a) and (b) were indeed the missing parts. **The mechanism was not used, and neither was +`EuclideanJordan/Pattern.lean`.** `Pattern` is in scope here — it arrives through +`EuclideanJordan/Class.lean`'s import — and no declaration in this file mentions `sum_eigen_eq_one`, +`eigen_pattern_mem` or `eigen_pattern_card_le_two`. There is no `Finset.prod` of projections +below, and no sum over `Fin n → Fin 3`; neither string occurs in this file outside this +docstring. + +★ What replaced it is a *residual* argument, `frame_peirce_span`. Set + + z := x − ∑ᵢ P₁(pᵢ) x − ½ • ∑ᵢ P½(pᵢ) x. + +The eight composition rules of the `Compose` section below, together with +`sum_peirceHalf_erase` in the one place where `P½(p k) P½(p k) x` has to be matched against the +rest of the half-component, kill `P₁(p k) z` and `P½(p k) z` for every `k`; so `peirce_add_add` +at `p k` collapses to `z = P₀(p k) z`, whence `p k ∘ z = 0` for every `k`, whence +`z = 1 ∘ z = (∑ₖ p k) ∘ z = 0`. Nothing is expanded, because the *identity* is never expanded — +only its residual is tested, one idempotent at a time. + +★ The `½` in that identity is not a normalisation choice. `P½(pᵢ) x` is the whole +`i`-th half-eigencomponent, which is the sum of the `V_{ij}` parts over all `j ≠ i`; so an +off-diagonal block is counted once by `P½(pᵢ)` and again by `P½(pⱼ)`, and the sum over `i` +double-counts exactly. Breaking `P½(pᵢ) x` into its blocks is `sum_peirceHalf_erase`, which is +one of exactly two places `F.complete` is used; the other is the final `z = 1 ∘ z`. + +## What is *not* needed, and what is still missing + +★ **No proof below uses primitivity.** The string `primitive` does not occur in this file +outside this docstring: every proof runs on `F.orthIdem` and `F.complete` alone, so primitivity +enters the *statements* only through the `JordanFrame` hypothesis they carry, never through an +argument. (That is an observation about these proofs, not a claim that the results have been +restated at the weaker hypothesis — they have not.) Primitivity is what will collapse `V_{ii}` +to `ℝ ∙ pᵢ`: that is the next module's `dim V_{ii} = 1`, and it is not proved here. + +★ **`rank J = n` is still not available** and nothing here is a step towards it; the frame is +carried as data. See `EuclideanJordan/Rank.lean`'s module docstring. Do not read +`frameBlock_isInternal` as a statement about the rank. + +★ That guard got easier to trip once `EuclideanJordan/HermitianCarrier.lean` named a frame. +`diagJordanFrame : JordanFrame (HermitianMat n ℂ) (Fintype.card n)` puts a frame of cardinality +`Fintype.card n` on `H_n(ℂ)`, which reads like "`rank (H_n(ℂ)) = Fintype.card n`". It is not: +`EuclideanJordan/Rank.lean` supplies `JordanFrame.card_le_rank`, an inequality in one direction only, and +`rank` is a supremum over *every* orthogonal family of nonzero idempotents. A named frame of +cardinality `k` is a lower bound on the rank and nothing more. + +★ **The carrier arrived after this file — this paragraph used to say the opposite.** As +written on 2026-08-23 it recorded that the only `EuclideanJordanAlgebra` instances in the tree +were `EuclideanJordan/PeirceSubalgebra.lean`'s two, both *conditional* on an ambient +`[EuclideanJordanAlgebra J]`, so that nothing exhibited a base model and the results below +applied to no concrete algebra. That was true when written and is **false now**: +`EuclideanJordan/HermitianCarrier.lean`'s `instEuclideanJordanAlgebraHermitianMat` makes +`HermitianMat n 𝕜` an instance for every `RCLike 𝕜`, and its `hermitian_exists_jordanFrame` +and `hermitian_frameBlock_isInternal` (both under `[Nonempty n]`, which is what `1 ≠ 0` needs) +are `exists_jordanFrame` and `frameBlock_isInternal` read on that carrier. So the theorems +below are statements about a live object, not only about an unwitnessed class. Since +`1ad66d2` the carrier module goes past existence and *names* a frame — `diagJordanFrame`, the +diagonal matrix units, over ℂ — so `frameBlock_isInternal` has an instance with nothing left +existentially quantified. Note what that does and does not settle: see the rank guard above. + +## Scope + +-/ + +noncomputable section + +namespace EuclideanJordan + +open EuclideanJordanAlgebra + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] + +/-! ## Eigenspaces of left multiplication -/ + +/-- The `r`-eigenspace of `L_a`. -/ +def eigSub (a : J) (r : ℝ) : Submodule ℝ J where + carrier := {x : J | a * x = r • x} + add_mem' := fun {u v} hu hv => by + change a * (u + v) = r • (u + v) + rw [mul_add, hu, hv, smul_add] + zero_mem' := by change a * 0 = r • (0 : J); rw [mul_zero, smul_zero] + smul_mem' := fun t x hx => by + change a * (t • x) = r • (t • x) + rw [mul_smul_comm, hx, smul_comm] + +@[simp] theorem mem_eigSub {a : J} {r : ℝ} {x : J} : x ∈ eigSub a r ↔ a * x = r • x := Iff.rfl + +/-! ## The blocks -/ + +variable {n : ℕ} + +/-- The eigenvalue attached to the pair `(i, j)`: `1` on the diagonal, `1/2` off it. -/ +def blockCoef (i j : Fin n) : ℝ := if i = j then 1 else (2 : ℝ)⁻¹ + +theorem blockCoef_comm (i j : Fin n) : blockCoef i j = blockCoef j i := by + unfold blockCoef + by_cases h : i = j + · simp [h] + · simp [h, Ne.symm h] + +/-- `V_{ij}` before it is pushed through `Sym2`. -/ +def frameBlockRaw (F : JordanFrame J n) (i j : Fin n) : Submodule ℝ J := + eigSub (F.p i) (blockCoef i j) ⊓ eigSub (F.p j) (blockCoef i j) + +theorem frameBlockRaw_comm (F : JordanFrame J n) (i j : Fin n) : + frameBlockRaw F i j = frameBlockRaw F j i := by + unfold frameBlockRaw + rw [blockCoef_comm i j, inf_comm] + +/-- **`V_{ij}`.** For `i ≠ j` the joint `1/2`-eigenspace of `L_{p i}` and `L_{p j}`; on the +diagonal, `J₂(p i)`. -/ +def frameBlock (F : JordanFrame J n) : Sym2 (Fin n) → Submodule ℝ J := + Sym2.lift ⟨frameBlockRaw F, frameBlockRaw_comm F⟩ + +@[simp] theorem frameBlock_mk (F : JordanFrame J n) (i j : Fin n) : + frameBlock F s(i, j) = frameBlockRaw F i j := rfl + +theorem mem_frameBlockRaw_diag {F : JordanFrame J n} {i : Fin n} {x : J} : + x ∈ frameBlockRaw F i i ↔ F.p i * x = x := by + simp [frameBlockRaw, blockCoef] + +theorem mem_frameBlockRaw_off {F : JordanFrame J n} {i j : Fin n} (hij : i ≠ j) {x : J} : + x ∈ frameBlockRaw F i j ↔ F.p i * x = (2 : ℝ)⁻¹ • x ∧ F.p j * x = (2 : ℝ)⁻¹ • x := by + simp [frameBlockRaw, blockCoef, hij] + +/-- The diagonal block is `EuclideanJordan/PeirceSubalgebra.lean`'s `J₂(p i)` on the nose. -/ +theorem frameBlockRaw_self (F : JordanFrame J n) (i : Fin n) : + frameBlockRaw F i i = peirceOneSub (F.orthIdem.idem i) := + Submodule.ext fun _ => mem_frameBlockRaw_diag + +/-! ## What the frame does to a block element -/ + +/-- An element of `J₂(p i)` is annihilated by every other member of the frame. -/ +theorem frame_mul_eq_zero_of_eigen_one (F : JordanFrame J n) {i k : Fin n} (hik : k ≠ i) + {x : J} (hx : F.p i * x = x) : F.p k * x = 0 := by + have h := eigen_one_mul_zero (F.orthIdem.idem i) hx (F.orthIdem.orth i k (Ne.symm hik)) + rw [_root_.mul_comm]; exact h + +/-- An element halved by `p i` and by `p j` is annihilated by every other member of the frame: +it lies in `J₂(p i + p j)`, and every other `p k` lies in `J₀(p i + p j)`. -/ +theorem frame_mul_eq_zero_of_eigen_half (F : JordanFrame J n) {i j k : Fin n} (hij : i ≠ j) + (hki : k ≠ i) (hkj : k ≠ j) {x : J} (hi : F.p i * x = (2 : ℝ)⁻¹ • x) + (hj : F.p j * x = (2 : ℝ)⁻¹ • x) : F.p k * x = 0 := by + have hq : (F.p i + F.p j) * (F.p i + F.p j) = F.p i + F.p j := + add_idem_of_orthogonal (F.orthIdem.idem i) (F.orthIdem.idem j) (F.orthIdem.orth i j hij) + have hqx : (F.p i + F.p j) * x = x := mem_J2_of_half_half hi hj + have hqk : (F.p i + F.p j) * F.p k = 0 := pair_mul_of_ne F.orthIdem hki hkj + have h := eigen_one_mul_zero hq hqx hqk + rw [_root_.mul_comm]; exact h + +/-- **The eigenvalue of `L_{p k}` on `V_{ij}` is `0` for every `k` outside `{i, j}`.** -/ +theorem frameBlockRaw_mul_eq_zero (F : JordanFrame J n) {i j k : Fin n} (hki : k ≠ i) + (hkj : k ≠ j) {x : J} (hx : x ∈ frameBlockRaw F i j) : F.p k * x = 0 := by + by_cases hij : i = j + · subst hij + exact frame_mul_eq_zero_of_eigen_one F hki (mem_frameBlockRaw_diag.mp hx) + · obtain ⟨hi, hj⟩ := (mem_frameBlockRaw_off hij).mp hx + exact frame_mul_eq_zero_of_eigen_half F hij hki hkj hi hj + +/-! ## How the frame's Peirce projections compose + +The eight ways two of the projections `peirceOne (p i)`, `peirceHalf (p i)` can be applied in +succession. Every one is `EuclideanJordan/Block.lean`'s commutation plus a single-idempotent rule from +`EuclideanJordan/Peirce.lean`. -/ + +section Compose + +variable (F : JordanFrame J n) (x : J) {i k : Fin n} + +theorem peirceOne_peirceOne_self : peirceOne (F.p k) (peirceOne (F.p k) x) = + peirceOne (F.p k) x := peirceOne_of_eigen (mul_peirceOne (F.orthIdem.idem k) x) + +theorem peirceOne_peirceOne_of_ne (h : i ≠ k) : + peirceOne (F.p k) (peirceOne (F.p i) x) = 0 := + peirceOne_of_eigen_zero + (frame_mul_eq_zero_of_eigen_one F (Ne.symm h) (mul_peirceOne (F.orthIdem.idem i) x)) + +theorem peirceOne_peirceHalf_self : peirceOne (F.p k) (peirceHalf (F.p k) x) = 0 := + peirceOne_of_eigen_half (mul_peirceHalf (F.orthIdem.idem k) x) + +theorem peirceOne_peirceHalf_of_ne (h : i ≠ k) : + peirceOne (F.p k) (peirceHalf (F.p i) x) = 0 := by + have hcomm : peirceOne (F.p k) (peirceHalf (F.p i) x) + = peirceHalf (F.p i) (peirceOne (F.p k) x) := + peirceOne_comm_of_mul_comm (F := peirceHalf (F.p i)) + (mul_peirceHalf_comm_orth (F.orthIdem.idem k) (F.orthIdem.orth i k h)) x + rw [hcomm] + exact peirceHalf_of_eigen_zero + (frame_mul_eq_zero_of_eigen_one F h (mul_peirceOne (F.orthIdem.idem k) x)) + +theorem peirceHalf_peirceOne_self : peirceHalf (F.p k) (peirceOne (F.p k) x) = 0 := + peirceHalf_of_eigen (mul_peirceOne (F.orthIdem.idem k) x) + +theorem peirceHalf_peirceOne_of_ne (h : i ≠ k) : + peirceHalf (F.p k) (peirceOne (F.p i) x) = 0 := + peirceHalf_of_eigen_zero + (frame_mul_eq_zero_of_eigen_one F (Ne.symm h) (mul_peirceOne (F.orthIdem.idem i) x)) + +theorem peirceHalf_peirceHalf_self : peirceHalf (F.p k) (peirceHalf (F.p k) x) = + peirceHalf (F.p k) x := peirceHalf_of_eigen_half (mul_peirceHalf (F.orthIdem.idem k) x) + +theorem peirceHalf_peirceHalf_comm (h : i ≠ k) : + peirceHalf (F.p k) (peirceHalf (F.p i) x) = peirceHalf (F.p i) (peirceHalf (F.p k) x) := + peirceHalf_comm_peirceHalf (F.orthIdem.idem k) (F.orthIdem.orth i k h) x + +end Compose + +/-! ## The half-eigenspace of one frame member splits over the others -/ + +/-- **`J½(p k) = ∑_{m ≠ k} V_{km}`, in the form the spanning argument needs.** + +For `y` halved by `p k` and `m ≠ k`, `peirceOne (p m) y` lies in `J₂(p m)` — hence in `J₀(p k)` +— and simultaneously in `J½(p k)`, because `peirceOne (p m)` commutes with `L_{p k}`; so it +vanishes. Then `p m ∘ y = ½ • peirceHalf (p m) y`, and completeness turns +`∑ₘ p m ∘ y = y` into the claim. -/ +theorem sum_peirceHalf_erase (F : JordanFrame J n) {k : Fin n} {y : J} + (hy : F.p k * y = (2 : ℝ)⁻¹ • y) : + ∑ m ∈ Finset.univ.erase k, peirceHalf (F.p m) y = y := by + classical + have hone : ∀ m, m ≠ k → peirceOne (F.p m) y = 0 := by + intro m hm + have hcomm : F.p k * peirceOne (F.p m) y = peirceOne (F.p m) (F.p k * y) := + mul_peirceOne_comm_orth (F.orthIdem.idem k) (F.orthIdem.orth m k hm) y + have h1 : F.p k * peirceOne (F.p m) y = (2 : ℝ)⁻¹ • peirceOne (F.p m) y := by + rw [hcomm, hy, map_smul] + have h0 : F.p k * peirceOne (F.p m) y = 0 := + frame_mul_eq_zero_of_eigen_one F (Ne.symm hm) (mul_peirceOne (F.orthIdem.idem m) y) + have : (2 : ℝ)⁻¹ • peirceOne (F.p m) y = 0 := by rw [← h1, h0] + simpa using this + have hmul : ∀ m, m ≠ k → F.p m * y = (2 : ℝ)⁻¹ • peirceHalf (F.p m) y := by + intro m hm + calc F.p m * y = F.p m * (peirceOne (F.p m) y + peirceHalf (F.p m) y + peirceZero (F.p m) y) := by + rw [peirce_add_add] + _ = (2 : ℝ)⁻¹ • peirceHalf (F.p m) y := by + rw [mul_add, mul_add, mul_peirceOne (F.orthIdem.idem m), + mul_peirceHalf (F.orthIdem.idem m), mul_peirceZero (F.orthIdem.idem m), hone m hm] + module + have hsum : ∑ m, F.p m * y = y := by + rw [← Finset.sum_mul, F.complete, EuclideanJordanAlgebra.one_mul] + rw [← Finset.add_sum_erase _ _ (Finset.mem_univ k), hy] at hsum + have hstep : ∑ m ∈ Finset.univ.erase k, F.p m * y + = (2 : ℝ)⁻¹ • ∑ m ∈ Finset.univ.erase k, peirceHalf (F.p m) y := by + rw [Finset.smul_sum] + exact Finset.sum_congr rfl fun m hm => hmul m (Finset.ne_of_mem_erase hm) + rw [hstep] at hsum + have : (2 : ℝ)⁻¹ • ∑ m ∈ Finset.univ.erase k, peirceHalf (F.p m) y = (2 : ℝ)⁻¹ • y := by + linear_combination (norm := module) hsum + exact smul_right_injective J (by norm_num : (2 : ℝ)⁻¹ ≠ 0) this + +/-! ## Spanning -/ + +/-- **The frame Peirce decomposition, spanning half.** + +The proof is not an expansion of `∏ᵢ (P₁(pᵢ) + P½(pᵢ) + P₀(pᵢ))`. Set +`z := x - ∑ᵢ P₁(pᵢ) x - ½ ∑ᵢ P½(pᵢ) x`; the composition lemmas above kill `P₁(p k) z` and +`P½(p k) z` for every `k`, so `z = P₀(p k) z` and hence `p k ∘ z = 0` for every `k`, and +completeness gives `z = 1 ∘ z = 0`. -/ +theorem frame_peirce_span (F : JordanFrame J n) (x : J) : + x = ∑ i, peirceOne (F.p i) x + (2 : ℝ)⁻¹ • ∑ i, peirceHalf (F.p i) x := by + classical + have e1 : ∀ k, ∑ i, peirceOne (F.p k) (peirceOne (F.p i) x) = peirceOne (F.p k) x := by + intro k + rw [Finset.sum_eq_single k (fun b _ hb => peirceOne_peirceOne_of_ne F x hb) + (fun h => absurd (Finset.mem_univ k) h)] + exact peirceOne_peirceOne_self F x + have e2 : ∀ k, ∑ i, peirceOne (F.p k) (peirceHalf (F.p i) x) = 0 := fun k => + Finset.sum_eq_zero fun i _ => by + by_cases h : i = k + · subst h; exact peirceOne_peirceHalf_self F x + · exact peirceOne_peirceHalf_of_ne F x h + have e3 : ∀ k, ∑ i, peirceHalf (F.p k) (peirceOne (F.p i) x) = 0 := fun k => + Finset.sum_eq_zero fun i _ => by + by_cases h : i = k + · subst h; exact peirceHalf_peirceOne_self F x + · exact peirceHalf_peirceOne_of_ne F x h + have e4 : ∀ k, ∑ i, peirceHalf (F.p k) (peirceHalf (F.p i) x) + = peirceHalf (F.p k) x + peirceHalf (F.p k) x := by + intro k + rw [← Finset.add_sum_erase _ _ (Finset.mem_univ k)] + congr 1 + · exact peirceHalf_peirceHalf_self F x + · rw [Finset.sum_congr rfl fun i hi => peirceHalf_peirceHalf_comm F x + (Finset.ne_of_mem_erase hi)] + exact sum_peirceHalf_erase F (mul_peirceHalf (F.orthIdem.idem k) x) + obtain ⟨z, hzdef⟩ : ∃ z : J, + z = x - ∑ i, peirceOne (F.p i) x - (2 : ℝ)⁻¹ • ∑ i, peirceHalf (F.p i) x := ⟨_, rfl⟩ + have hA : ∀ k, peirceOne (F.p k) z = 0 := by + intro k + rw [hzdef] + simp only [map_sub, map_smul, map_sum] + rw [e1 k, e2 k] + module + have hB : ∀ k, peirceHalf (F.p k) z = 0 := by + intro k + rw [hzdef] + simp only [map_sub, map_smul, map_sum] + rw [e3 k, e4 k] + module + have hzero : ∀ k, F.p k * z = 0 := by + intro k + have hsplit := peirce_add_add (F.p k) z + rw [hA k, hB k, zero_add, zero_add] at hsplit + rw [← hsplit] + exact mul_peirceZero (F.orthIdem.idem k) z + have hz0 : z = 0 := by + have : (∑ i, F.p i) * z = 0 := by + rw [Finset.sum_mul] + exact Finset.sum_eq_zero fun i _ => hzero i + rwa [F.complete, EuclideanJordanAlgebra.one_mul] at this + rw [hzdef, sub_sub, sub_eq_zero] at hz0 + exact hz0 + +/-! ## The block projections -/ + +open scoped Classical in +/-- The projection onto `V_{ij}`: `P₁(p i)` on the diagonal, `P½(p i) ∘ P½(p j)` off it. -/ +def blockProj (F : JordanFrame J n) (i j : Fin n) : J →ₗ[ℝ] J := + if i = j then peirceOne (F.p i) else (peirceHalf (F.p i)).comp (peirceHalf (F.p j)) + +theorem blockProj_diag (F : JordanFrame J n) (i : Fin n) : + blockProj F i i = peirceOne (F.p i) := by + unfold blockProj; simp + +theorem blockProj_off (F : JordanFrame J n) {i j : Fin n} (h : i ≠ j) : + blockProj F i j = (peirceHalf (F.p i)).comp (peirceHalf (F.p j)) := by + unfold blockProj; simp [h] + +/-- `blockProj F i j` is the identity on `V_{ij}`. -/ +theorem blockProj_apply_eq_self (F : JordanFrame J n) {i j : Fin n} {x : J} + (hx : x ∈ frameBlockRaw F i j) : blockProj F i j x = x := by + by_cases hij : i = j + · subst hij + rw [blockProj_diag] + exact peirceOne_of_eigen (mem_frameBlockRaw_diag.mp hx) + · obtain ⟨hi, hj⟩ := (mem_frameBlockRaw_off hij).mp hx + rw [blockProj_off F hij, LinearMap.comp_apply, peirceHalf_of_eigen_half hj, + peirceHalf_of_eigen_half hi] + +/-- `blockProj F a b` annihilates every *other* block. -/ +theorem blockProj_apply_eq_zero (F : JordanFrame J n) {a b c d : Fin n} {x : J} + (hne : s(c, d) ≠ s(a, b)) (hx : x ∈ frameBlockRaw F c d) : blockProj F a b x = 0 := by + have hne' : ¬((c = a ∧ d = b) ∨ (c = b ∧ d = a)) := fun h => hne (Sym2.eq_iff.mpr h) + have hL : ¬(a = c ∧ b = d) := fun h => hne' (Or.inl ⟨h.1.symm, h.2.symm⟩) + have hR : ¬(a = d ∧ b = c) := fun h => hne' (Or.inr ⟨h.2.symm, h.1.symm⟩) + by_cases hab : a = b + · subst hab + rw [blockProj_diag] + by_cases hac : a = c + · have had : a ≠ d := fun h => hL ⟨hac, h⟩ + have hcd : c ≠ d := hac ▸ had + have := ((mem_frameBlockRaw_off hcd).mp hx).1 + rw [← hac] at this + exact peirceOne_of_eigen_half this + · by_cases had : a = d + · have hbc : a ≠ c := hac + have hcd : c ≠ d := fun h => hac (had.trans h.symm) + have := ((mem_frameBlockRaw_off hcd).mp hx).2 + rw [← had] at this + exact peirceOne_of_eigen_half this + · exact peirceOne_of_eigen_zero (frameBlockRaw_mul_eq_zero F hac had hx) + · rw [blockProj_off F hab, LinearMap.comp_apply] + by_cases hac : a = c + · have hbd : b ≠ d := fun h => hL ⟨hac, h⟩ + have hbc : b ≠ c := fun h => hab (hac.trans h.symm) + rw [peirceHalf_of_eigen_zero (frameBlockRaw_mul_eq_zero F hbc hbd hx), map_zero] + · by_cases had : a = d + · have hbc : b ≠ c := fun h => hR ⟨had, h⟩ + have hbd : b ≠ d := fun h => hab (had.trans h.symm) + rw [peirceHalf_of_eigen_zero (frameBlockRaw_mul_eq_zero F hbc hbd hx), map_zero] + · have hax : F.p a * x = 0 := frameBlockRaw_mul_eq_zero F hac had hx + refine peirceHalf_of_eigen_zero ?_ + rw [mul_peirceHalf_comm_orth (F.orthIdem.idem a) (F.orthIdem.orth b a (Ne.symm hab)), + hax, map_zero] + +/-! ## `DirectSum.IsInternal` -/ + +/-- For `i ≠ m`, `P½(p m) P½(p i) x` lies in `V_{im}`. -/ +theorem peirceHalf_peirceHalf_mem (F : JordanFrame J n) {i m : Fin n} (him : i ≠ m) (x : J) : + peirceHalf (F.p m) (peirceHalf (F.p i) x) ∈ frameBlockRaw F i m := by + refine (mem_frameBlockRaw_off him).mpr ⟨?_, mul_peirceHalf (F.orthIdem.idem m) _⟩ + rw [mul_peirceHalf_comm_orth (F.orthIdem.idem i) (F.orthIdem.orth m i (Ne.symm him)), + mul_peirceHalf (F.orthIdem.idem i), map_smul] + +theorem peirceOne_mem_frameBlock (F : JordanFrame J n) (i : Fin n) (x : J) : + peirceOne (F.p i) x ∈ frameBlock F s(i, i) := + mem_frameBlockRaw_diag.mpr (mul_peirceOne (F.orthIdem.idem i) x) + +/-- **The blocks span.** -/ +theorem frameBlock_iSup_eq_top (F : JordanFrame J n) : ⨆ s, frameBlock F s = ⊤ := by + classical + refine eq_top_iff.mpr fun x _ => ?_ + have hhalf : ∀ i, peirceHalf (F.p i) x ∈ ⨆ s, frameBlock F s := by + intro i + rw [← sum_peirceHalf_erase F (mul_peirceHalf (F.orthIdem.idem i) x)] + refine Submodule.sum_mem _ fun m hm => Submodule.mem_iSup_of_mem s(i, m) ?_ + exact peirceHalf_peirceHalf_mem F (Ne.symm (Finset.ne_of_mem_erase hm)) x + rw [frame_peirce_span F x] + refine Submodule.add_mem _ (Submodule.sum_mem _ fun i _ => ?_) + (Submodule.smul_mem _ _ (Submodule.sum_mem _ fun i _ => hhalf i)) + exact Submodule.mem_iSup_of_mem s(i, i) (peirceOne_mem_frameBlock F i x) + +/-- **The blocks are independent.** -/ +theorem frameBlock_iSupIndep (F : JordanFrame J n) : iSupIndep (frameBlock F) := by + classical + intro s + induction s using Sym2.ind with + | _ a b => + have hker : (⨆ t, ⨆ (_ : t ≠ s(a, b)), frameBlock F t) + ≤ LinearMap.ker (blockProj F a b) := by + refine iSup_le fun t => ?_ + induction t using Sym2.ind with + | _ c d => + refine iSup_le fun ht => fun y hy => ?_ + simp only [LinearMap.mem_ker] + exact blockProj_apply_eq_zero F ht hy + rw [Submodule.disjoint_def] + intro x hx hx' + have h1 : blockProj F a b x = x := blockProj_apply_eq_self F hx + have h0 : blockProj F a b x = 0 := hker hx' + rw [← h1, h0] + +/-- **The frame Peirce decomposition.** `J = ⨁_{i ≤ j} V_{ij}`. -/ +theorem frameBlock_isInternal (F : JordanFrame J n) : DirectSum.IsInternal (frameBlock F) := + (DirectSum.isInternal_submodule_iff_iSupIndep_and_iSup_eq_top _).mpr + ⟨frameBlock_iSupIndep F, frameBlock_iSup_eq_top F⟩ + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean new file mode 100644 index 0000000000..d1700ba712 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean @@ -0,0 +1,364 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce + + +/-! +# The Faraut–Korányi multiplication table relative to a Jordan frame + +`EuclideanJordan/FramePeirce.lean` builds the blocks `V_{ij}` of a Jordan frame `F = (p₁, …, pₙ)` and proves +`J = ⨁_{i ≤ j} V_{ij}`. This file multiplies them. The table: + +* `V_{ii} ∘ V_{ii} ⊆ V_{ii}` (`frameBlockRaw_diag_mul_diag`), +* `V_{ii} ∘ V_{ij} ⊆ V_{ij}` (`frameBlockRaw_diag_mul_off`), +* `V_{ij} ∘ V_{jk} ⊆ V_{ik}` for `i, j, k` distinct (`frameBlockRaw_mul_middle`), +* `V_{ij} ∘ V_{kl} = 0` when `{i,j} ∩ {k,l} = ∅` (`frameBlockRaw_mul_disjoint`), +* `V_{ij} ∘ V_{ij} ⊆ ℝ∙pᵢ + ℝ∙pⱼ` (`frameBlockRaw_mul_self_eq`), + +together with `dim V_{ii} = 1` (`finrank_frameBlockRaw_self`) and the eigenvalue rule +`pᵢ ∘ x = ½ • x` on `V_{ij}` (`frameBlockRaw_mul_left_half`). The last three lines of the table +are also stated as literal submodule inclusions through `EuclideanJordan/Class.lean`'s bundled `jmulₗ` and +`Submodule.map₂`, in the `Map₂` section; there is no `Mul` on `Submodule ℝ J` to state them with, +because Mathlib's `Submodule.mul` instance is declared for `[Semiring A] [Module R A] +[IsScalarTower R A A]` — an associative unital ring — which a Jordan algebra is not. + +## ★ Where the coefficients come from — the plan's one unpriced step + +The build plan priced every rule here as a short consequence of `EuclideanJordan/PeirceMul.lean`'s +single-idempotent rules **except** `V_{ij} ∘ V_{ij} ⊆ ℝ∙pᵢ + ℝ∙pⱼ`, which it left explicitly +unpriced: `eigen_half_mul_half` gives the projection identity, but *pinning the two coefficients* +was expected to need the trace form, and no in-tree lemma had been matched to that step. + +**The trace form is not used.** The step is the frame's completeness. For `x, y ∈ V_{ij}` put +`z := x ∘ y`. Every `p_k` with `k ∉ {i,j}` kills both factors, so `eigen_zero_mul_zero` kills +`z`; hence + + z = 1 ∘ z = (∑ₖ p_k) ∘ z = p_i ∘ z + p_j ∘ z, + +which is `frameBlockRaw_mul_self_split`. And `eigen_half_mul_half` at `p_i` says exactly +`p_i ∘ (p_i ∘ z) = p_i ∘ z`, i.e. `p_i ∘ z ∈ J₂(p_i) = V_{ii}` — so the two summands are already +in the two diagonal blocks (`frameBlockRaw_mul_self_left_mem`). That much is the *decomposition*, +and it needs neither primitivity nor finite-dimensionality. + +The **coefficients** then come from `V_{ii}` being a line, not from an inner-product computation: +`p_i ∘ z = a • p_i` because `dim V_{ii} = 1`. So the sub-item the plan could not price is really +two independent facts, and the trace form is in neither. What *is* load-bearing is the +completeness of the frame, used once, in `frameBlockRaw_mul_self_split`. + +## ★ Primitivity is spent here + +`EuclideanJordan/FramePeirce.lean` records that no proof in it uses primitivity at all. This file is not, +however, the first in the tree to *touch* the `IsPrimitive` clauses, and an earlier draft of this +docstring said so wrongly: `EuclideanJordan/Rank.lean`'s `JordanFrame.p_ne_zero` consumes the `ne_zero` clause, +and `EuclideanJordan/FrameExists.lean`'s `isPrimitive_coe_of_peirceOne` / `isPrimitive_coe_of_peirceZero` +consume the splitting clause `∀ d, d ∘ d = d → c ∘ d = d → d = 0 ∨ d = c` in full. What those two +do with it is *transport* it across the coercion `↥(J₂(c)) → J`; they extract no structural +consequence from it. + +This file is the first to spend it. Within this file the splitting clause is used at exactly one +theorem, `peirceOneSub_eq_span_of_isPrimitive`, which runs `EuclideanJordan/Class.lean`'s +`spectral_resolution_complete'` **inside** `J₂(c)` — legitimate because +`EuclideanJordan/PeirceSubalgebra.lean` gives `J₂(c)` its own `EuclideanJordanAlgebra` instance with unit `c` +— and reads off that every idempotent appearing in the resolution is `0` or `1`, by +`EuclideanJordan/Rank.lean`'s `isPrimitive_iff_of_idem`. A resolution all of whose idempotents are `0` or `1` +has every term a real multiple of `1 = c`. + +Orthogonality of the resolution is *not* used in that argument, only the two-valuedness; that is +why the proof does not have to rule out two indices both landing on `1`. + +The rest of the `Primitive` section is downstream of that one theorem, except that +`finrank_frameBlockRaw_self` also uses the `ne_zero` clause, through `JordanFrame.p_ne_zero`. +Everything *before* the `Primitive` section runs on `F.orthIdem` and `F.complete` alone. + +## Scope + + +★ `rank J = n` is **not** available and nothing here is a step towards it — `dim V_{ii} = 1` is a +statement about one block of a frame carried as data, not about the rank of `J`. + +★ **There is a carrier and a named frame**, so the theorems below are not statements about an +empty class: `EuclideanJordan/HermitianCarrier.lean` supplies `instEuclideanJordanAlgebraHermitianMat` and +`diagJordanFrame : JordanFrame (HermitianMat n ℂ) (Fintype.card n)`. `frameBlockRaw_mul_self_eq` is +consumed at `F = diagJordanFrame` through `EuclideanJordan/Connection.lean`'s `exists_sq_smul`. + +★ Do not quote this paragraph for the carrier's state; read +`EuclideanJordan/HermitianCarrier.lean`. +The durable part is the one below. + +★ `rank J = n` is not proved for **any** frame, `diagJordanFrame` included: `EuclideanJordan/Rank.lean` +bounds a frame's cardinality by the rank and by the dimension, and nothing anywhere converts +`dim V_{ii} = 1` into a statement about `rank J`. +-/ + +noncomputable section + +namespace EuclideanJordan + +open EuclideanJordanAlgebra + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] +variable {n : ℕ} + +/-! ## The eigenvalue rule on a block -/ + +/-- **`pᵢ ∘ x = ½ • x` for `x ∈ V_{ij}`, `i ≠ j`.** Definitional at `EuclideanJordan/FramePeirce.lean`'s +`frameBlockRaw`; named because the multiplication rules below take it as an argument constantly. -/ +theorem frameBlockRaw_mul_left_half (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) : F.p i * x = (2 : ℝ)⁻¹ • x := + ((mem_frameBlockRaw_off hij).mp hx).1 + +/-- The same at the second index. -/ +theorem frameBlockRaw_mul_right_half (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} + (hx : x ∈ frameBlockRaw F i j) : F.p j * x = (2 : ℝ)⁻¹ • x := + ((mem_frameBlockRaw_off hij).mp hx).2 + +/-! ## The table + +Each rule is read off the single-idempotent Faraut–Korányi rules of `EuclideanJordan/PeirceMul.lean`, applied +at one frame member at a time. `frameBlockRaw_mul_eq_zero` (in `EuclideanJordan/FramePeirce.lean`) supplies +the `0`-eigenvalue of every frame member outside a block's index pair. -/ + +/-- **`V_{ii} ∘ V_{ii} ⊆ V_{ii}`** — the diagonal block is a subalgebra. This is +`eigen_one_mul_one` verbatim; `EuclideanJordan/PeirceSubalgebra.lean` already uses it as the `Mul` field of +`J₂(pᵢ)`. -/ +theorem frameBlockRaw_diag_mul_diag (F : JordanFrame J n) {i : Fin n} {x y : J} + (hx : x ∈ frameBlockRaw F i i) (hy : y ∈ frameBlockRaw F i i) : + x * y ∈ frameBlockRaw F i i := + mem_frameBlockRaw_diag.mpr + (eigen_one_mul_one (F.orthIdem.idem i) (mem_frameBlockRaw_diag.mp hx) + (mem_frameBlockRaw_diag.mp hy)) + +/-- **`V_{ii} ∘ V_{ij} ⊆ V_{ij}`.** At `pᵢ` this is `eigen_one_mul_half`; at `pⱼ` it is +`eigen_zero_mul_half`, because `pⱼ` annihilates `J₂(pᵢ)`. -/ +theorem frameBlockRaw_diag_mul_off (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x y : J} + (hx : x ∈ frameBlockRaw F i i) (hy : y ∈ frameBlockRaw F i j) : + x * y ∈ frameBlockRaw F i j := by + have hxi : F.p i * x = x := mem_frameBlockRaw_diag.mp hx + have hxj : F.p j * x = 0 := + frameBlockRaw_mul_eq_zero F (Ne.symm hij) (Ne.symm hij) hx + refine (mem_frameBlockRaw_off hij).mpr ⟨?_, ?_⟩ + · exact eigen_one_mul_half (F.orthIdem.idem i) hxi (frameBlockRaw_mul_left_half F hij hy) + · exact eigen_zero_mul_half (F.orthIdem.idem j) hxj (frameBlockRaw_mul_right_half F hij hy) + +/-- **`V_{ij} ∘ V_{jk} ⊆ V_{ik}` for `i, j, k` distinct.** + +`pᵢ` halves `x` and kills `y`, `p_k` kills `x` and halves `y`; `eigen_zero_mul_half` at each of +them is the whole proof. The middle index enters only through the eigenvalue hypotheses on `x` +and `y`: nothing has to be shown about `pⱼ ∘ (x ∘ y)`, because membership in `V_{ik}` is a +condition at `pᵢ` and `p_k` only. -/ +theorem frameBlockRaw_mul_middle (F : JordanFrame J n) {i j k : Fin n} (hij : i ≠ j) + (hjk : j ≠ k) (hik : i ≠ k) {x y : J} (hx : x ∈ frameBlockRaw F i j) + (hy : y ∈ frameBlockRaw F j k) : x * y ∈ frameBlockRaw F i k := by + have hyi : F.p i * y = 0 := frameBlockRaw_mul_eq_zero F hij hik hy + have hxk : F.p k * x = 0 := frameBlockRaw_mul_eq_zero F (Ne.symm hik) (Ne.symm hjk) hx + refine (mem_frameBlockRaw_off hik).mpr ⟨?_, ?_⟩ + · have h := eigen_zero_mul_half (F.orthIdem.idem i) hyi (frameBlockRaw_mul_left_half F hij hx) + rwa [_root_.mul_comm y x] at h + · exact eigen_zero_mul_half (F.orthIdem.idem k) hxk (frameBlockRaw_mul_right_half F hjk hy) + +/-- **`V_{ij} ∘ V_{kl} = 0` when `{i,j}` and `{k,l}` are disjoint** — the two blocks annihilate +each other, not merely land in a common block. + +Both index pairs are allowed to be diagonal. If `i = j` then `x ∈ J₂(pᵢ)` and `y ∈ J₀(pᵢ)`, and +`eigen_one_mul_zero` finishes; otherwise the same argument runs at the rank-two idempotent +`q = pᵢ + pⱼ`, with `x ∈ J₂(q)` by `mem_J2_of_half_half` and `y ∈ J₀(q)` because `pᵢ` and `pⱼ` +each kill `y`. -/ +theorem frameBlockRaw_mul_disjoint (F : JordanFrame J n) {i j k l : Fin n} (hik : i ≠ k) + (hil : i ≠ l) (hjk : j ≠ k) (hjl : j ≠ l) {x y : J} (hx : x ∈ frameBlockRaw F i j) + (hy : y ∈ frameBlockRaw F k l) : x * y = 0 := by + have hyi : F.p i * y = 0 := frameBlockRaw_mul_eq_zero F hik hil hy + have hyj : F.p j * y = 0 := frameBlockRaw_mul_eq_zero F hjk hjl hy + by_cases hij : i = j + · subst hij + exact eigen_one_mul_zero (F.orthIdem.idem i) (mem_frameBlockRaw_diag.mp hx) hyi + · have hq : (F.p i + F.p j) * (F.p i + F.p j) = F.p i + F.p j := + add_idem_of_orthogonal (F.orthIdem.idem i) (F.orthIdem.idem j) (F.orthIdem.orth i j hij) + have hqx : (F.p i + F.p j) * x = x := + mem_J2_of_half_half (frameBlockRaw_mul_left_half F hij hx) + (frameBlockRaw_mul_right_half F hij hx) + have hqy : (F.p i + F.p j) * y = 0 := by rw [_root_.add_mul, hyi, hyj, add_zero] + exact eigen_one_mul_zero hq hqx hqy + +/-! ## `V_{ij} ∘ V_{ij}`: the decomposition, before any coefficients + +★ These three are the plan's unpriced step, and they use no trace form, no primitivity and no +finite-dimensionality — only `F.complete` and `EuclideanJordan/PeirceMul.lean`. -/ + +/-- **`x ∘ y = pᵢ ∘ (x ∘ y) + pⱼ ∘ (x ∘ y)` for `x, y ∈ V_{ij}`.** + +The one place completeness is used: every frame member outside `{i,j}` kills both factors, hence +their product by `eigen_zero_mul_zero`, so `∑ₖ p_k ∘ z` collapses to two terms and `∑ₖ p_k = 1` +says that sum is `z`. -/ +theorem frameBlockRaw_mul_self_split (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x y : J} + (hx : x ∈ frameBlockRaw F i j) (hy : y ∈ frameBlockRaw F i j) : + x * y = F.p i * (x * y) + F.p j * (x * y) := by + classical + have hsum : ∑ k, F.p k * (x * y) = x * y := by + rw [← Finset.sum_mul, F.complete, EuclideanJordanAlgebra.one_mul] + have hpair : ∑ k ∈ ({i, j} : Finset (Fin n)), F.p k * (x * y) = ∑ k, F.p k * (x * y) := by + refine Finset.sum_subset (Finset.subset_univ _) ?_ + intro k _ hk + have hki : k ≠ i := fun h => hk (by simp [h]) + have hkj : k ≠ j := fun h => hk (by simp [h]) + exact eigen_zero_mul_zero (F.orthIdem.idem k) + (frameBlockRaw_mul_eq_zero F hki hkj hx) (frameBlockRaw_mul_eq_zero F hki hkj hy) + rw [Finset.sum_pair hij] at hpair + exact (hpair.trans hsum).symm + +/-- The first summand of `frameBlockRaw_mul_self_split` lies in the diagonal block `V_{ii}`. +This is `eigen_half_mul_half` read as a membership: `L_{pᵢ}² = L_{pᵢ}` on `x ∘ y` says precisely +that `pᵢ ∘ (x ∘ y)` is fixed by `L_{pᵢ}`. -/ +theorem frameBlockRaw_mul_self_left_mem (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) + {x y : J} (hx : x ∈ frameBlockRaw F i j) (hy : y ∈ frameBlockRaw F i j) : + F.p i * (x * y) ∈ frameBlockRaw F i i := + mem_frameBlockRaw_diag.mpr + (eigen_half_mul_half (F.orthIdem.idem i) (frameBlockRaw_mul_left_half F hij hx) + (frameBlockRaw_mul_left_half F hij hy)) + +/-- The second summand lies in `V_{jj}`. -/ +theorem frameBlockRaw_mul_self_right_mem (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) + {x y : J} (hx : x ∈ frameBlockRaw F i j) (hy : y ∈ frameBlockRaw F i j) : + F.p j * (x * y) ∈ frameBlockRaw F j j := + mem_frameBlockRaw_diag.mpr + (eigen_half_mul_half (F.orthIdem.idem j) (frameBlockRaw_mul_right_half F hij hx) + (frameBlockRaw_mul_right_half F hij hy)) + +/-- **`V_{ij} ∘ V_{ij} ⊆ V_{ii} ⊔ V_{jj}`**, the coefficient-free form. -/ +theorem frameBlockRaw_mul_self_mem_sup (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) + {x y : J} (hx : x ∈ frameBlockRaw F i j) (hy : y ∈ frameBlockRaw F i j) : + x * y ∈ frameBlockRaw F i i ⊔ frameBlockRaw F j j := by + rw [frameBlockRaw_mul_self_split F hij hx hy] + exact Submodule.add_mem _ + (Submodule.mem_sup_left (frameBlockRaw_mul_self_left_mem F hij hx hy)) + (Submodule.mem_sup_right (frameBlockRaw_mul_self_right_mem F hij hx hy)) + +/-! ## Primitivity: the diagonal block is a line + +★ The `JordanFrame.primitive` field is consumed here and nowhere else in the frame Peirce +layer. -/ + +section Primitive + +variable [FiniteDimensional ℝ J] + +/-- **`J₂(c) = ℝ ∙ c` for a primitive idempotent `c`.** + +`EuclideanJordan/PeirceSubalgebra.lean` makes `J₂(c)` a Euclidean Jordan algebra with unit `c`, so +`EuclideanJordan/Class.lean`'s `spectral_resolution_complete'` applies *inside* it: every element is +`∑ₖ λ_k • e_k` with each `e_k` idempotent in `J₂(c)`. `EuclideanJordan/Rank.lean`'s +`isPrimitive_iff_of_idem` says each `e_k` is `0` or `1`, and `1 = c`; so every term is a real +multiple of `c`. + +Orthogonality of the `e_k` is not used — only that each is `0` or `1`. -/ +theorem peirceOneSub_eq_span_of_isPrimitive {c : J} (hp : IsPrimitive c) : + peirceOneSub hp.idem = Submodule.span ℝ {c} := by + classical + refine le_antisymm ?_ ?_ + · intro x hx + obtain ⟨m, e, lam, hfam, -, hrep⟩ := + spectral_resolution_complete' (J := ↥(peirceOneSub hp.idem)) ⟨x, hx⟩ + have hcoe : x = ∑ k, lam k • ((e k : ↥(peirceOneSub hp.idem)) : J) := by + have := congrArg (fun z : ↥(peirceOneSub hp.idem) => (z : J)) hrep + simpa using this + rw [hcoe] + refine Submodule.sum_mem _ fun k _ => Submodule.smul_mem _ _ ?_ + rcases (isPrimitive_iff_of_idem hp.idem hp.ne_zero).mp hp (e k) (hfam.idem k) with h | h + · rw [h]; simp + · rw [h]; exact Submodule.mem_span_singleton_self c + · rw [Submodule.span_le, Set.singleton_subset_iff] + exact hp.idem + +/-- `dim J₂(c) = 1` for a primitive idempotent — the definition of primitivity, cashed out as a +dimension. -/ +theorem finrank_peirceOneSub_of_isPrimitive {c : J} (hp : IsPrimitive c) : + Module.finrank ℝ ↥(peirceOneSub hp.idem) = 1 := by + rw [peirceOneSub_eq_span_of_isPrimitive hp] + exact finrank_span_singleton hp.ne_zero + +/-- **`V_{ii} = ℝ ∙ pᵢ`.** -/ +theorem frameBlockRaw_self_eq_span (F : JordanFrame J n) (i : Fin n) : + frameBlockRaw F i i = Submodule.span ℝ {F.p i} := by + rw [frameBlockRaw_self F i] + exact peirceOneSub_eq_span_of_isPrimitive (F.primitive i) + +/-- **`dim V_{ii} = 1`.** The statement the coordinatization rests on. -/ +theorem finrank_frameBlockRaw_self (F : JordanFrame J n) (i : Fin n) : + Module.finrank ℝ ↥(frameBlockRaw F i i) = 1 := by + rw [frameBlockRaw_self_eq_span F i] + exact finrank_span_singleton (F.p_ne_zero i) + +/-- `dim V_{ii} = 1`, in the `Sym2`-indexed vocabulary `frameBlock` uses. -/ +theorem finrank_frameBlock_diag (F : JordanFrame J n) (i : Fin n) : + Module.finrank ℝ ↥(frameBlock F s(i, i)) = 1 := + finrank_frameBlockRaw_self F i + +/-- An element of `V_{ii}` is a real multiple of `pᵢ` — `frameBlockRaw_self_eq_span` read +pointwise. -/ +theorem exists_smul_of_mem_frameBlockRaw_self (F : JordanFrame J n) {i : Fin n} {x : J} + (hx : x ∈ frameBlockRaw F i i) : ∃ a : ℝ, x = a • F.p i := by + rw [frameBlockRaw_self_eq_span F i, Submodule.mem_span_singleton] at hx + obtain ⟨a, ha⟩ := hx + exact ⟨a, ha.symm⟩ + +/-- **`V_{ij} ∘ V_{ij} ⊆ ℝ∙pᵢ + ℝ∙pⱼ`**, with the coefficients exhibited. + +`frameBlockRaw_mul_self_split` puts the product in `V_{ii} ⊕ V_{jj}`; `dim V_{ii} = 1` turns each +summand into a scalar multiple of a frame member. -/ +theorem frameBlockRaw_mul_self_eq (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x y : J} + (hx : x ∈ frameBlockRaw F i j) (hy : y ∈ frameBlockRaw F i j) : + ∃ a b : ℝ, x * y = a • F.p i + b • F.p j := by + obtain ⟨a, ha⟩ := + exists_smul_of_mem_frameBlockRaw_self F (frameBlockRaw_mul_self_left_mem F hij hx hy) + obtain ⟨b, hb⟩ := + exists_smul_of_mem_frameBlockRaw_self F (frameBlockRaw_mul_self_right_mem F hij hx hy) + exact ⟨a, b, by rw [frameBlockRaw_mul_self_split F hij hx hy, ha, hb]⟩ + +end Primitive + +/-! ## The table as submodule inclusions + +`Submodule.map₂ (jmulₗ J) P Q` is the submodule generated by the products, so these are the +literal `⊆` statements of the module docstring. `EuclideanJordan/Class.lean`'s `jmulₗ` is the bundled form of +the same product (`jmulₗ_apply` is `rfl`). -/ + +section Map₂ + +/-- `V_{ij} ∘ V_{jk} ⊆ V_{ik}`. -/ +theorem map₂_frameBlockRaw_middle_le (F : JordanFrame J n) {i j k : Fin n} (hij : i ≠ j) + (hjk : j ≠ k) (hik : i ≠ k) : + Submodule.map₂ (jmulₗ J) (frameBlockRaw F i j) (frameBlockRaw F j k) + ≤ frameBlockRaw F i k := + Submodule.map₂_le.mpr fun _ hx _ hy => frameBlockRaw_mul_middle F hij hjk hik hx hy + +/-- `V_{ij} ∘ V_{kl} = 0` for disjoint index pairs. -/ +theorem map₂_frameBlockRaw_disjoint (F : JordanFrame J n) {i j k l : Fin n} (hik : i ≠ k) + (hil : i ≠ l) (hjk : j ≠ k) (hjl : j ≠ l) : + Submodule.map₂ (jmulₗ J) (frameBlockRaw F i j) (frameBlockRaw F k l) = ⊥ := + le_bot_iff.mp <| Submodule.map₂_le.mpr fun _ hx _ hy => + (Submodule.mem_bot ℝ).mpr (frameBlockRaw_mul_disjoint F hik hil hjk hjl hx hy) + +/-- `V_{ij} ∘ V_{ij} ⊆ V_{ii} ⊔ V_{jj}`. -/ +theorem map₂_frameBlockRaw_self_le (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) : + Submodule.map₂ (jmulₗ J) (frameBlockRaw F i j) (frameBlockRaw F i j) + ≤ frameBlockRaw F i i ⊔ frameBlockRaw F j j := + Submodule.map₂_le.mpr fun _ hx _ hy => frameBlockRaw_mul_self_mem_sup F hij hx hy + +/-- `V_{ij} ∘ V_{ij} ⊆ ℝ∙pᵢ + ℝ∙pⱼ`. -/ +theorem map₂_frameBlockRaw_self_le_span [FiniteDimensional ℝ J] (F : JordanFrame J n) + {i j : Fin n} (hij : i ≠ j) : + Submodule.map₂ (jmulₗ J) (frameBlockRaw F i j) (frameBlockRaw F i j) + ≤ Submodule.span ℝ {F.p i} ⊔ Submodule.span ℝ {F.p j} := by + rw [← frameBlockRaw_self_eq_span F i, ← frameBlockRaw_self_eq_span F j] + exact map₂_frameBlockRaw_self_le F hij + +end Map₂ + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean new file mode 100644 index 0000000000..316991dace --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean @@ -0,0 +1,73 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner + + +/-! +# The Jordan product on `H_n(𝕜)` as a bundled bilinear map + +`EuclideanJordan/Vendor/HermitianMat/Jordan.lean` supplies `HermitianMat.symmMul`, the +Jordan product `A ∘ B = ½(AB + BA)`, together with the scoped `HermMul` instances that make +it the `*` of a `NonUnitalNonAssocCommRing`. Several results in this library are stated over +the *unbundled* form of the product instead — an ℝ-bilinear map `m : J →ₗ[ℝ] J →ₗ[ℝ] J`, see +`EuclideanJordan/Bridge.lean` for why. This file supplies that form for the concrete carrier, +so those results can be instantiated on `H_n(𝕜)`. + +The bilinearity proofs go through `symmMul_toMat` and `symmMul_comm`; the ℝ-linearity (rather +than 𝕜-linearity) is the right statement because `HermitianMat n 𝕜` is only an ℝ-module — +a 𝕜-multiple of a Hermitian matrix need not be Hermitian. +-/ + +noncomputable section + +open scoped Matrix + +namespace EuclideanJordan + +variable {n : Type*} [Fintype n] [DecidableEq n] +variable {𝕜 : Type*} [RCLike 𝕜] + +theorem symmMul_add_rightG (a b c : HermitianMat n 𝕜) : + a.symmMul (b + c) = a.symmMul b + a.symmMul c := by + ext1 + simp only [HermitianMat.symmMul_toMat, HermitianMat.mat_add] + rw [Matrix.mul_add, Matrix.add_mul, ← smul_add] + congr 1 + abel + +theorem symmMul_smul_rightG (t : ℝ) (a b : HermitianMat n 𝕜) : + a.symmMul (t • b) = t • a.symmMul b := by + ext1 + simp only [HermitianMat.symmMul_toMat, HermitianMat.mat_smul] + rw [Matrix.mul_smul, Matrix.smul_mul, ← smul_add, smul_comm] + +/-- The Euclidean Jordan product `x ∘ y = ½(xy + yx)` on `H_n(𝕜)` as an ℝ-bilinear map. -/ +def jordanBilinG (𝕜 : Type*) [RCLike 𝕜] : + HermitianMat n 𝕜 →ₗ[ℝ] HermitianMat n 𝕜 →ₗ[ℝ] HermitianMat n 𝕜 := + LinearMap.mk₂ ℝ (fun a b => a.symmMul b) + (fun a a' b => by + show (a + a').symmMul b = a.symmMul b + a'.symmMul b + rw [HermitianMat.symmMul_comm, symmMul_add_rightG] + rw [HermitianMat.symmMul_comm (A := b) (B := a), + HermitianMat.symmMul_comm (A := b) (B := a')]) + (fun t a b => by + show (t • a).symmMul b = t • a.symmMul b + rw [HermitianMat.symmMul_comm, symmMul_smul_rightG, + HermitianMat.symmMul_comm (A := b) (B := a)]) + (fun a b b' => symmMul_add_rightG a b b') + (fun t a b => symmMul_smul_rightG t a b) + +@[simp] +theorem jordanBilin_applyG (a b : HermitianMat n 𝕜) : + jordanBilinG 𝕜 a b = a.symmMul b := rfl + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean new file mode 100644 index 0000000000..1200fad32e --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean @@ -0,0 +1,349 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce +import LeanPool.EuclideanJordan.EuclideanJordan.Witness +import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin + + +/-! +# A carrier for `EuclideanJordanAlgebra` + +`EuclideanJordan/Class.lean` names the Euclidean Jordan algebra hypothesis as a class, and `EuclideanJordan/Rank.lean`, +`EuclideanJordan/FrameExists.lean` and `EuclideanJordan/FramePeirce.lean` state their results over it. Until this file +the **only** instances of that class in the tree were `EuclideanJordan/PeirceSubalgebra.lean`'s two, and both +are conditional on an ambient `[EuclideanJordanAlgebra J]`, so nothing exhibited a base model: +`exists_jordanFrame` and `frameBlock_isInternal` were universally quantified over a class no +object was known to inhabit. + +This file supplies the base model. `H_n(𝕜)` — Hermitian matrices over an `RCLike` field, under +the symmetrized product `A ∘ B = ½(AB + BA)` and the real trace form — is a +`EuclideanJordanAlgebra`. + +★ **This is the same exposure the layer has had to close for every other hypothesis it carries.** +An abstract theorem whose hypothesis no carrier is known to satisfy is worth nothing; +`EuclideanJordan/Witness.lean`'s `instIsFormallyReal` and `diagFrame_orthIdem` close it for +`IsFormallyReal` and `IsOrthIdemFamily`, and this file closes it for `EuclideanJordanAlgebra`. + +★ The class was **not** the only unwitnessed hypothesis left in the layer, and an earlier draft +of this docstring said it was. `EuclideanJordan/Rank.lean`'s `IsPrimitive` and `JordanFrame` had no witness +either — `EuclideanJordan/Witness.lean` builds the diagonal matrix units but never proves one primitive, so +no `JordanFrame` had ever been constructed. Both are witnessed at the end of this file, but by a +separate argument, and the class instance alone would not have supplied them. + +## No field needed new mathematics + +Every one of the class's six field obligations was already proved in the tree, in +`jordanBilinG` or `symmMul` vocabulary: + +| class field | discharged by | +| --- | --- | +| `mul_comm` | `HermitianMat.symmMul_comm` | +| `add_mul` | bilinearity of `EuclideanJordan/HermitianBilin.lean`'s `jordanBilinG` | +| `smul_mul` | bilinearity of `EuclideanJordan/HermitianBilin.lean`'s `jordanBilinG` | +| `one_mul` | `HermitianMat.one_symmMul` | +| `jordan` | `EuclideanJordan/Order.lean`'s `hermitian_jordan_id` (the vendored `IsCommJordan`) | +| `inner_assoc` | `EuclideanJordan/Order.lean`'s `hermitian_jordan_assoc` | + +The three lemmas before the instance are re-orientations, not new results: two unbundle +`jordanBilinG`'s `LinearMap` structure, and `hermitian_symmMul_jordan` turns Mathlib's +`lmul_comm_rmul_rmul` orientation into the class's by commuting the product twice. + +## The diamond `EuclideanJordan/Class.lean` warned about, measured + +`EuclideanJordan/Class.lean` records that `instNonUnitalNonAssocCommRing` fires on any type carrying the +class, that `HermitianMat d 𝕜` already carries a `Mul` from +`EuclideanJordan/Vendor/HermitianMat/Jordan.lean`'s `scoped instance : CommMagma`, and that +"if one is ever declared, that scoped instance and this class's `toMul` will both be in scope +inside `open HermMul` sections and one of them has to give way." + +That is now declared, and the collision is **definitional**: `hermMul_toMul_eq` below is `rfl`. +Both `Mul` structures have `HermitianMat.symmMul` as their multiplication, so which one instance +search returns does not change any statement. Concretely, inside an `open HermMul` section with +this instance in scope, `#synth Mul (HermitianMat (Fin 2) ℂ)` returns *this* instance's `toMul` +(the global instance outranks the opened scoped one), `HermMul.mul_eq_symmMul` still typechecks, +and `EuclideanJordan/Order.lean`'s two `open HermMul` proofs — `hermitian_jordan_id` and +`hermitian_formallyReal` — still go through unaltered. + +★ Two things this does *not* claim. It is not claimed that the collision is invisible: instance +search does return a different term inside `open HermMul` once this module is imported, and a +proof that pins the instance by name rather than by its product would notice. And it is not +claimed that anything currently pays that cost. ★ Every `open HermMul` site in this library — +`EuclideanJordan/Order.lean`, `EuclideanJordan/Spectral.lean` and `EuclideanJordan/Witness.lean` — +is strictly *upstream* of this file: this file's import closure contains all three, and imports +are directional, so none of them can see this instance. ★ A **downstream** module that both +imports this file and opens `HermMul` is a different matter: it would see the collision, and would +have to navigate it rather than avoid it, bridging with `hermitian_mul_eq_symmMul`. That does not +change what the three sites above see. + +★ One elaboration trap, recorded because it cost a probe round. Inside the structure-instance +notation, `simpa only [...] using e` **fails** on fields whose statement is in `*` form while `e` +is in `symmMul` form, reporting a type mismatch between `(A + B) * C` and `(A + B).symmMul C` +even though the two are `rfl`-equal (`hermitian_mul_eq_symmMul` below). The named-lemma route +(`add_mul A B C := hermitian_symmMul_add_left A B C`) succeeds on the same goal. So the +mismatch is `simpa`'s reducibility setting, not a real failure of the defeq — do not conclude +from such a message that the product is wrong. + +## Scope + +This file is substrate: it proves no new theorem about Jordan algebras. What changes is that the +M3/M4 results are now known to be about a nonempty class: `hermitian_exists_jordanFrame` and +`hermitian_frameBlock_isInternal` are `exists_jordanFrame` and `frameBlock_isInternal` read on +`H_n(𝕜)`. + +★ The last section goes past existence and **names a frame** on `H_n(ℂ)`: `diagJordanFrame`, the +diagonal matrix units of `EuclideanJordan/Witness.lean` reindexed along `Fintype.equivFin`. Unlike the class +instance, that is not repackaging: `diagFrame_isPrimitive` is the one obligation of `JordanFrame` +that no lemma in the tree discharges — `diagFrame_orthIdem` gives `orthIdem` and `diagFrame_sum` +gives `complete`, and `p` is data — and its proof and the four entrywise lemmas under it are +written here. (`hermitian_one_ne_zero` is new too, but it is three lines and reads off one +matrix entry.) ★ Note what primitivity is **not**: it is not +`dim V_ii = 1`, and this file does not prove that. Two scope limits on the frame: it is over +`ℂ` only, because `EuclideanJordan/Witness.lean`'s `diagFrame` is, and its cardinality is `Fintype.card n`, +which is **not** proved to be the rank — `EuclideanJordan/Rank.lean` proves only `card ≤ rank` and +`card ≤ finrank`. +-/ + +noncomputable section + +namespace EuclideanJordan + +variable {n : Type*} [Fintype n] [DecidableEq n] {𝕜 : Type*} [RCLike 𝕜] + +/-! ## The three re-orientations the class fields need -/ + +/-- Additivity of `∘` in its left argument, from `jordanBilinG`'s `LinearMap` structure. -/ +theorem hermitian_symmMul_add_left (A B C : HermitianMat n 𝕜) : + (A + B).symmMul C = A.symmMul C + B.symmMul C := by + simpa only [jordanBilin_applyG] using (jordanBilinG (n := n) 𝕜).map_add₂ A B C + +/-- Real homogeneity of `∘` in its left argument, from `jordanBilinG`'s `LinearMap` structure. -/ +theorem hermitian_symmMul_smul_left (r : ℝ) (A B : HermitianMat n 𝕜) : + (r • A).symmMul B = r • (A.symmMul B) := by + simpa only [jordanBilin_applyG] using (jordanBilinG (n := n) 𝕜).map_smul₂ r A B + +/-- The Jordan identity in the class's orientation, `x ∘ (x² ∘ y) = x² ∘ (x ∘ y)`. +`EuclideanJordan/Order.lean`'s `hermitian_jordan_id` carries Mathlib's orientation +`(x ∘ y) ∘ x² = x ∘ (y ∘ x²)`; the two differ by commuting the product twice. -/ +theorem hermitian_symmMul_jordan (A B : HermitianMat n 𝕜) : + A.symmMul ((A.symmMul A).symmMul B) = (A.symmMul A).symmMul (A.symmMul B) := by + have h := hermitian_jordan_id (n := n) (𝕜 := 𝕜) A B + simp only [jordanBilin_applyG] at h + rw [HermitianMat.symmMul_comm (A.symmMul A) B, ← h] + exact HermitianMat.symmMul_comm _ _ + +/-! ## The instance -/ + +/-- ★★★ **`H_n(𝕜)` is a Euclidean Jordan algebra.** The class of `EuclideanJordan/Class.lean`, on the +Hermitian-matrix carrier, at the generality `EuclideanJordan/Order.lean` uses: any `RCLike` scalar +field and any finite decidable index type. -/ +instance instEuclideanJordanAlgebraHermitianMat : + EuclideanJordanAlgebra (HermitianMat n 𝕜) where + mul := HermitianMat.symmMul + one := 1 + mul_comm A B := HermitianMat.symmMul_comm A B + add_mul A B C := hermitian_symmMul_add_left A B C + smul_mul r A B := hermitian_symmMul_smul_left r A B + one_mul A := HermitianMat.one_symmMul A + jordan A B := hermitian_symmMul_jordan A B + inner_assoc A B C := hermitian_jordan_assoc A B C + +/-! ### Fidelity: the class's product is the intended one + +Inhabitedness alone would be satisfied by any product making the fields true. These three +`rfl`s pin the instance's `*` to the three names the rest of the tree uses for `A ∘ B`. -/ + +/-- The class's product on `H_n(𝕜)` is `HermitianMat.symmMul`. -/ +theorem hermitian_mul_eq_symmMul (A B : HermitianMat n 𝕜) : A * B = A.symmMul B := rfl + +/-- The class's product on `H_n(𝕜)` is `jordanBilinG`, the bundled bilinear form of the same +product. -/ +theorem hermitian_mul_eq_jordanBilinG (A B : HermitianMat n 𝕜) : + A * B = jordanBilinG 𝕜 A B := rfl + +/-- ★ **The `HermMul` collision is definitional.** The `Mul` this instance supplies and the one +`EuclideanJordan/Vendor/HermitianMat/Jordan.lean`'s scoped `CommMagma` supplies are the same +structure, so which one instance search returns inside an `open HermMul` section cannot change a +statement. See the module docstring for what this does and does not claim. -/ +theorem hermMul_toMul_eq : + (HermMul.instCommMagmaHermitianMat (d := n) (𝕜 := 𝕜)).toMul + = (instEuclideanJordanAlgebraHermitianMat (n := n) (𝕜 := 𝕜)).toMul := rfl + +/-! ## Nontriviality -/ + +/-- `1 ≠ 0` in `H_n(𝕜)` whenever the index type is nonempty: read off the `(i, i)` entry. -/ +theorem hermitian_one_ne_zero [Nonempty n] : (1 : HermitianMat n 𝕜) ≠ 0 := by + intro h + have h2 := congrArg + (fun A : HermitianMat n 𝕜 => A.mat (Classical.arbitrary n) (Classical.arbitrary n)) h + simp only [HermitianMat.mat_one, HermitianMat.mat_zero, Matrix.one_apply_eq, + Matrix.zero_apply] at h2 + exact one_ne_zero h2 + +instance instNontrivialHermitianMat [Nonempty n] : Nontrivial (HermitianMat n 𝕜) := + ⟨⟨1, 0, hermitian_one_ne_zero⟩⟩ + +/-! ## M3 and M4 on the carrier + +The two results now read on a live object rather than on a class with no known inhabitant. +Neither proof is new: each is the abstract theorem with `J := H_n(𝕜)`. -/ + +/-- **(M3) on `H_n(𝕜)`.** `EuclideanJordan/FrameExists.lean`'s `exists_jordanFrame`, instantiated. The +finite-dimensionality it needs is `HermitianMat.FiniteDimensional`; the nontriviality is +`hermitian_one_ne_zero`. -/ +theorem hermitian_exists_jordanFrame [Nonempty n] : + ∃ k, Nonempty (JordanFrame (HermitianMat n 𝕜) k) := + exists_jordanFrame (HermitianMat n 𝕜) hermitian_one_ne_zero + +/-- **(M4) on `H_n(𝕜)`.** `EuclideanJordan/FramePeirce.lean`'s `frameBlock_isInternal`, instantiated: a +Jordan frame of `H_n(𝕜)` splits it as the internal direct sum of its diagonal and coherence +blocks. -/ +theorem hermitian_frameBlock_isInternal {k : ℕ} (F : JordanFrame (HermitianMat n 𝕜) k) : + DirectSum.IsInternal (frameBlock F) := + frameBlock_isInternal F + +/-- **(M3) and (M4) together on `H_n(𝕜)`**: the algebra has a Jordan frame, and that frame +decomposes it. This is the statement that was universally quantified over an unwitnessed class +before this file. -/ +theorem hermitian_exists_frame_isInternal [Nonempty n] : + ∃ (k : ℕ) (F : JordanFrame (HermitianMat n 𝕜) k), DirectSum.IsInternal (frameBlock F) := by + obtain ⟨k, ⟨F⟩⟩ := hermitian_exists_jordanFrame (n := n) (𝕜 := 𝕜) + exact ⟨k, F, frameBlock_isInternal F⟩ + +/-! ## A named frame on `H_n(ℂ)` + +`EuclideanJordan/Witness.lean` builds the diagonal matrix units `E_ii` and proves them orthogonal +idempotents summing to the unit. That is three of `JordanFrame`'s four fields. The missing one +is **primitivity**, which is proved here. + +The argument is entrywise and short. If `d` is fixed by `E_ii ∘ -` then reading the `(a, b)` +entry of `½(E_ii D + D E_ii) = D` gives `D_ab = 0` off `(i, i)` — the three off-diagonal cases +reduce to `½ D_ab = D_ab` or to `0 = D_ab` — so `D = D_ii · E_ii`. Idempotency then forces +`D_ii² = D_ii` at the surviving entry, so `D_ii ∈ {0, 1}` and `d` is `0` or `E_ii`. -/ + +section Frame + +open Witness + +variable {n : Type*} [Fintype n] [DecidableEq n] + +omit [Fintype n] in +/-- The diagonal matrix unit entrywise: `1` at `(i, i)` and `0` elsewhere. -/ +theorem diagFrame_mat_apply (i a b : n) : + (diagFrame i).mat a b = if a = b then (if a = i then (1 : ℂ) else 0) else 0 := by + rw [diagFrame_mat, Matrix.diagonal_apply] + split_ifs <;> simp_all + +/-- The entries of `E_ii ∘ D`. Left multiplication by `E_ii` keeps row `i`, right +multiplication keeps column `i`, so the symmetrized product halves everything meeting the cross +at `i` exactly once and kills everything off it. -/ +theorem diagFrame_symmMul_mat_apply (i : n) (D : HermitianMat n ℂ) (a b : n) : + ((diagFrame i).symmMul D).mat a b + = (2 : ℂ)⁻¹ * ((if a = i then D.mat a b else 0) + (if b = i then D.mat a b else 0)) := by + rw [HermitianMat.symmMul_toMat] + simp only [Matrix.smul_apply, Matrix.add_apply, Matrix.mul_apply, smul_eq_mul] + congr 1 + congr 1 + · rw [Finset.sum_eq_single a] + · rw [diagFrame_mat_apply]; simp + · intro k _ hk; rw [diagFrame_mat_apply]; simp [Ne.symm hk] + · intro h; exact absurd (Finset.mem_univ a) h + · rw [Finset.sum_eq_single b] + · rw [diagFrame_mat_apply]; simp [eq_comm] + · intro k _ hk; rw [diagFrame_mat_apply]; simp [hk] + · intro h; exact absurd (Finset.mem_univ b) h + +/-- **An element fixed by `E_ii ∘ -` lives at the single entry `(i, i)`.** Note this uses only +the fixed-point hypothesis, not idempotency of `D`. -/ +theorem mat_eq_zero_of_diagFrame_fixed {i : n} {D : HermitianMat n ℂ} + (h : (diagFrame i).symmMul D = D) {a b : n} (hab : ¬(a = i ∧ b = i)) : + D.mat a b = 0 := by + have he := congrArg (fun M : HermitianMat n ℂ => M.mat a b) h + simp only [diagFrame_symmMul_mat_apply] at he + by_cases ha : a = i <;> by_cases hb : b = i + · exact absurd ⟨ha, hb⟩ hab + · rw [if_pos ha, if_neg hb, add_zero] at he + linear_combination -2 * he + · rw [if_neg ha, if_pos hb, zero_add] at he + linear_combination -2 * he + · rw [if_neg ha, if_neg hb, add_zero, mul_zero] at he + exact he.symm + +/-- So such an element is a scalar multiple of `E_ii`. -/ +theorem mat_eq_smul_of_diagFrame_fixed {i : n} {D : HermitianMat n ℂ} + (h : (diagFrame i).symmMul D = D) : + D.mat = D.mat i i • (diagFrame i).mat := by + ext a b + rw [Matrix.smul_apply, diagFrame_mat_apply, smul_eq_mul] + by_cases hab : a = b + · subst hab + by_cases ha : a = i + · subst ha; simp + · rw [mat_eq_zero_of_diagFrame_fixed h (fun hc => ha hc.1)]; simp [ha] + · rw [mat_eq_zero_of_diagFrame_fixed h (fun hc => hab (hc.1.trans hc.2.symm))]; simp [hab] + +/-- ★★ **The diagonal matrix units are primitive.** The one obligation of `JordanFrame` that +`EuclideanJordan/Witness.lean` leaves open, and the only new mathematics in this file. -/ +theorem diagFrame_isPrimitive (i : n) : IsPrimitive (diagFrame (d := n) i) := by + refine ⟨(diagFrame_orthIdem (d := n)).idem i, ?_, ?_⟩ + · intro h + have hii := congrArg (fun M : HermitianMat n ℂ => M.mat i i) h + rw [diagFrame_mat_apply] at hii + simp at hii + · intro D hD hcD + have hsupp : D.mat = D.mat i i • (diagFrame i).mat := mat_eq_smul_of_diagFrame_fixed hcD + -- the Jordan square of `D` is its matrix square, since `D` commutes with itself + have hD' : D.symmMul D = D := hD + have hsq : D.mat * D.mat = D.mat := by + have hm := congrArg HermitianMat.mat hD' + rwa [HermitianMat.symmMul_self] at hm + have hii : D.mat i i * D.mat i i = D.mat i i := by + have hentry := congrFun (congrFun hsq i) i + rw [Matrix.mul_apply, Finset.sum_eq_single i] at hentry + · exact hentry + · intro k _ hk + rw [mat_eq_zero_of_diagFrame_fixed hcD (fun hc => hk hc.2), zero_mul] + · intro h; exact absurd (Finset.mem_univ i) h + have hfac : D.mat i i * (D.mat i i - 1) = 0 := by linear_combination hii + rcases mul_eq_zero.mp hfac with h0 | h1 + · left + apply HermitianMat.ext + rw [hsupp, h0, zero_smul, HermitianMat.mat_zero] + · right + apply HermitianMat.ext + rw [hsupp, sub_eq_zero.mp h1, one_smul] + +/-- ★★★ **A Jordan frame on `H_n(ℂ)`, named.** The diagonal matrix units, reindexed along +`Fintype.equivFin` because `JordanFrame` is `Fin`-indexed. ★ Its cardinality `Fintype.card n` +is **not** claimed to be the rank of the algebra — `EuclideanJordan/Rank.lean` proves only that a frame's +cardinality is bounded by the rank and by the dimension. -/ +noncomputable def diagJordanFrame : JordanFrame (HermitianMat n ℂ) (Fintype.card n) where + p k := diagFrame ((Fintype.equivFin n).symm k) + orthIdem := + ⟨fun _ => (diagFrame_orthIdem (d := n)).idem _, + fun _ _ hkl => (diagFrame_orthIdem (d := n)).orth _ _ + ((Fintype.equivFin n).symm.injective.ne hkl)⟩ + primitive _ := diagFrame_isPrimitive _ + complete := by + rw [Equiv.sum_comp (Fintype.equivFin n).symm diagFrame] + exact diagFrame_sum + +@[simp] theorem diagJordanFrame_p (k : Fin (Fintype.card n)) : + (diagJordanFrame (n := n)).p k = diagFrame ((Fintype.equivFin n).symm k) := rfl + +/-- **(M4) on a named frame.** `H_n(ℂ)` is the internal direct sum of the Peirce blocks of the +diagonal frame — the frame Peirce decomposition with nothing left quantified. -/ +theorem diagJordanFrame_isInternal : + DirectSum.IsInternal (frameBlock (diagJordanFrame (n := n))) := + frameBlock_isInternal _ + +end Frame + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean new file mode 100644 index 0000000000..74e575b790 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean @@ -0,0 +1,675 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace +import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin + + +/-! +# The order structure on a Euclidean Jordan algebra + +The rest of this library runs on `[NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J]`, +which carries no order at all. This file supplies one: `orderUnitSpaceOfBilinear` produces an +`OrderUnitSpace J` for `J` a finite-dimensional formally real Jordan algebra, with the **cone +of sums of squares** as the positive cone and the Jordan unit as the order unit. + +## The cone, and why it is sums of squares rather than squares + +`0 ≤ x` is defined here as "`x` is a finite sum of Jordan squares" (`IsSoS`). The alternative — +`x` is a *single* square — is the right reading, but it is not usable as a *definition*: +closure of the single-square set under addition is not available before the spectral theorem, +whereas closure of the sums-of-squares set under addition is a concatenation of index sets. The +two readings are then **proved equal** in `isSoS_iff_exists_sq` — under the Euclidean +hypothesis of the third section, and not before. + +Each field of `OrderUnitSpace` is paid for by exactly one thing: + +| field | what pays for it | +| --- | --- | +| `le_refl`, `le_trans` | the cone contains `0` and is closed under `+` | +| `le_antisymm` | **formal reality** (`eq_zero_of_isSoS_of_isSoS_neg`) | +| `add_le_add_left` | the order is a difference condition | +| `smul_nonneg_mono` | `r • (y ∘ y) = (√r • y) ∘ (√r • y)` for `r ≥ 0` | +| `ousUnit_nonneg` | the unit is idempotent, hence a square | +| `archimedean` (order-unit boundedness) | **the spectral theorem** | + +★ **The spectral theorem is what buys order-unit boundedness.** +`spectral_resolution_bilinear` writes `x = ∑ lam i • q i` over an orthogonal idempotent family +summing to `e`, so `r • e - x = ∑ (r - lam i) • q i` is a sum of nonnegative multiples of +idempotents — a sum of squares — for any `r` dominating every `lam i`. The bound taken here is +`∑ i, |lam i|` rather than `max lam`: it dominates every coefficient, is manifestly +nonnegative, and needs no nonemptiness side condition when the resolution is empty. + +## Shape: a hypothesis-carrying `def` in bilinear-map vocabulary, not an instance + +Two deliberate choices, both forced by diamonds. + +1. **A `def`, never an `instance`.** A global `OrderUnitSpace` instance keyed on the Jordan + typeclasses would fire on `HermitianMat d 𝕜`, which already carries a `PartialOrder` and a + `Norm` from the vendored Loewner structure, putting two of each on the concrete carrier. + Consumers write `letI := orderUnitSpaceOfBilinear …`, exactly as + `EuclideanJordan/Bridge.lean`'s `ringOfBilinear` is used. +2. **Bilinear-map vocabulary.** Every statement takes the Jordan product as + `m : J →ₗ[ℝ] J →ₗ[ℝ] J` over `[NormedAddCommGroup J] [InnerProductSpace ℝ J]`, and reaches + the ring vocabulary only *inside* proofs, via `ringOfBilinear`. Assuming + `[NonUnitalNonAssocCommRing J]` and `[NormedAddCommGroup J]` together would give two + `AddCommGroup J` instances, which is the diamond `EuclideanJordan/Bridge.lean` was written + to dodge. Only one `AddCommGroup` is ever in play here, and the produced structure's + `toNormedAddCommGroup` is the ambient instance on the nose + (`normedAddCommGroup_ofBilinear`, proved by `rfl`). + +## Scope — what is and is not proved here + +* **The Euclidean hypothesis `hassoc` is carried, not derived.** Six declarations in the third + section — `inner_mul_self_nonneg_of_idem`, `inner_left_coeff`, `nonneg_coeff_of_inner_nonneg`, + `nonneg_coeff_of_isSoS`, `isArchimedean_ofBilinear`, `isSoS_iff_exists_sq` — assume an + associative inner product, `⟪x ∘ y, z⟫ = ⟪y, x ∘ z⟫`. That is Faraut–Korányi's definition of + *Euclidean* Jordan algebra (FK III.1), and over ℝ in finite dimension it is equivalent to + formal reality — but that equivalence needs the trace form and **is not formalized here**. + Both directions of the dependency are therefore hypotheses, and `hermitian_jordan_assoc` + supplies a live carrier for the new one so that no theorem is conditional on an uninhabited + premise. +* **The constructed order is the Loewner order on `H_n(𝕜)`.** Both containments are proved — + `hermitian_isSoS_iff_nonneg`, `hermitian_le_ofEJA_iff`. No square root on the carrier is + needed for this: the spectral idempotents are themselves positive semidefinite, so + `HermitianMat.inner_ge_zero` makes `⟪q i, A⟫ ≥ 0` and `inner_left_coeff` reads the + coefficient off. +-/ + +noncomputable section + +namespace EuclideanJordan + +open Finset + +/-! ## The cone of sums of squares + +Nothing in this section mentions a norm or an inner product; the ambient structure is the +additive group and the `ℝ`-module, which is all the cone algebra needs. -/ + +section Cone + +variable {J : Type*} [AddCommGroup J] [Module ℝ J] + +/-- **The positive cone**: `z` is a finite sum of squares of the bilinear product `m`. + +The empty sum is allowed, so `0` is in the cone by `k = 0`. -/ +def IsSoS (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (z : J) : Prop := + ∃ (k : ℕ) (f : Fin k → J), z = ∑ i, m (f i) (f i) + +variable {m : J →ₗ[ℝ] J →ₗ[ℝ] J} + +theorem isSoS_zero : IsSoS m 0 := ⟨0, fun i => i.elim0, by simp⟩ + +/-- The cone is closed under addition — the whole reason it is stated with sums of squares +rather than squares: the witness is a concatenation of index sets. -/ +theorem IsSoS.add {a b : J} (ha : IsSoS m a) (hb : IsSoS m b) : IsSoS m (a + b) := by + obtain ⟨k, f, hf⟩ := ha + obtain ⟨l, g, hg⟩ := hb + refine ⟨k + l, Fin.append f g, ?_⟩ + rw [Fin.sum_univ_add] + simp only [Fin.append_left, Fin.append_right] + rw [← hf, ← hg] + +theorem isSoS_sum {ι : Type*} (s : Finset ι) (g : ι → J) (h : ∀ i ∈ s, IsSoS m (g i)) : + IsSoS m (∑ i ∈ s, g i) := by + classical + induction s using Finset.induction with + | empty => simpa using isSoS_zero + | insert a s ha ih => + rw [Finset.sum_insert ha] + exact (h a (Finset.mem_insert_self a s)).add + (ih fun i hi => h i (Finset.mem_insert_of_mem hi)) + +/-- The cone is closed under nonnegative scalars: `r • (y ∘ y) = (√r • y) ∘ (√r • y)`. -/ +theorem IsSoS.smul {r : ℝ} (hr : 0 ≤ r) {a : J} (ha : IsSoS m a) : IsSoS m (r • a) := by + obtain ⟨k, f, hf⟩ := ha + refine ⟨k, fun i => Real.sqrt r • f i, ?_⟩ + rw [hf, Finset.smul_sum] + refine Finset.sum_congr rfl fun i _ => ?_ + simp only [map_smul, LinearMap.smul_apply, smul_smul] + rw [Real.mul_self_sqrt hr] + +theorem isSoS_of_idem {c : J} (hc : m c c = c) : IsSoS m c := + ⟨1, fun _ => c, by simp [hc]⟩ + +theorem isSoS_smul_idem {r : ℝ} (hr : 0 ≤ r) {c : J} (hc : m c c = c) : IsSoS m (r • c) := + (isSoS_of_idem hc).smul hr + +/-- **Antisymmetry of the cone, and the only place formal reality is used in this file.** + +If `a` and `-a` are both sums of squares then the concatenated family has vanishing sum of +squares, so formal reality kills every member of it — including every member of `a`'s own +family. -/ +theorem eq_zero_of_isSoS_of_isSoS_neg + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + {a : J} (ha : IsSoS m a) (hna : IsSoS m (-a)) : a = 0 := by + obtain ⟨k, f, hf⟩ := ha + obtain ⟨l, g, hg⟩ := hna + have hsum : (∑ i, m (Fin.append f g i) (Fin.append f g i)) = 0 := by + rw [Fin.sum_univ_add] + simp only [Fin.append_left, Fin.append_right] + rw [← hf, ← hg, add_neg_cancel] + have hz := hfr (k + l) (Fin.append f g) hsum + rw [hf, Finset.sum_eq_zero] + intro i _ + have hi := hz (Fin.castAdd l i) + rw [Fin.append_left] at hi + simp [hi] + +/-- The partial order induced by the cone: `x ≤ y` iff `y - x` is a sum of squares. -/ +@[instance_reducible] +def partialOrderOfSoS (m : J →ₗ[ℝ] J →ₗ[ℝ] J) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) : + PartialOrder J where + le x y := IsSoS m (y - x) + le_refl x := by simpa using isSoS_zero + le_trans x y z hxy hyz := by + show IsSoS m (z - x) + rw [← sub_add_sub_cancel z y x] + exact hyz.add hxy + le_antisymm x y hxy hyx := by + have h : y - x = 0 := + eq_zero_of_isSoS_of_isSoS_neg hfr hxy (by rw [neg_sub]; exact hyx) + exact (sub_eq_zero.mp h).symm + +/-- The rearrangement both the order-unit bound and the Archimedean squeeze run on: against a +complete orthogonal idempotent family, `r • e - x` is again diagonal, with coefficients +`r - lam i`. -/ +theorem smul_unit_sub_eq {n : ℕ} {q : Fin n → J} {lam : Fin n → ℝ} {e x : J} + (hsum : (∑ i, q i) = e) (hx : x = ∑ i, lam i • q i) (r : ℝ) : + r • e - x = ∑ i, (r - lam i) • q i := by + rw [← hsum, hx, Finset.smul_sum, ← Finset.sum_sub_distrib] + exact Finset.sum_congr rfl fun i _ => (sub_smul _ _ _).symm + +end Cone + +/-! ## The order unit space + +From here the ambient structure is a finite-dimensional real inner product space carrying the +Jordan product as a bundled bilinear map. -/ + +section OrderUnit + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [FiniteDimensional ℝ J] +variable {m : J →ₗ[ℝ] J →ₗ[ℝ] J} + +/-- **Order-unit boundedness, read off the spectral resolution.** This is the field the EJA +layer had no way to supply before `EuclideanJordan/Spectral.lean`. -/ +theorem exists_isSoS_smul_unit_sub + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (e : J) (he : ∀ y : J, m e y = y) (x : J) : + ∃ r : ℝ, 0 ≤ r ∧ IsSoS m (r • e - x) := by + obtain ⟨n, q, lam, hidem, _horth, hsum, hx⟩ := + spectral_resolution_bilinear m hcomm hjordan hfr e he x + refine ⟨∑ i, |lam i|, Finset.sum_nonneg fun i _ => abs_nonneg _, ?_⟩ + have hle : ∀ i, lam i ≤ ∑ j, |lam j| := fun i => + le_trans (le_abs_self _) + (Finset.single_le_sum (f := fun j => |lam j|) (fun j _ => abs_nonneg _) (Finset.mem_univ i)) + rw [smul_unit_sub_eq hsum hx] + exact isSoS_sum _ _ fun i _ => isSoS_smul_idem (by linarith [hle i]) (hidem i) + +/-- **A Euclidean Jordan algebra is an order unit space**, with the cone of sums of squares +as the positive cone and the Jordan unit as the order unit. + +A `def`, not an `instance` — see the module docstring. The `NormedAddCommGroup` and +`NormedSpace` parents are filled from the ambient instances, so no second normed structure is +created. -/ +@[instance_reducible] +def orderUnitSpaceOfBilinear (m : J →ₗ[ℝ] J →ₗ[ℝ] J) + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (e : J) (he : ∀ y : J, m e y = y) : + OrderUnitSpace J := + { (inferInstance : NormedAddCommGroup J), (inferInstance : NormedSpace ℝ J), + partialOrderOfSoS m hfr with + add_le_add_left := fun a b h c => by + show IsSoS m (c + b - (c + a)) + rw [show c + b - (c + a) = b - a by abel] + exact h + ousUnit := e + smul_nonneg_mono := fun r hr {a b} h => by + show IsSoS m (r • b - r • a) + rw [← smul_sub] + exact IsSoS.smul hr h + ousUnit_nonneg := by + show IsSoS m (e - 0) + rw [sub_zero] + exact isSoS_of_idem (he e) + archimedean := fun a => exists_isSoS_smul_unit_sub hcomm hjordan hfr e he a } + +section Characterization + +variable (m) +variable (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (e : J) (he : ∀ y : J, m e y = y) + +/-- **No second normed structure.** The produced order unit space's normed group is the +ambient one on the nose — the check that the `ringOfBilinear` diamond stays shut. -/ +theorem normedAddCommGroup_ofBilinear : + (orderUnitSpaceOfBilinear m hcomm hjordan hfr e he).toNormedAddCommGroup + = (inferInstance : NormedAddCommGroup J) := rfl + +theorem le_ofBilinear (x y : J) : + letI := orderUnitSpaceOfBilinear m hcomm hjordan hfr e he + x ≤ y ↔ IsSoS m (y - x) := Iff.rfl + +theorem ousUnit_ofBilinear : + @OrderUnitSpace.ousUnit J (orderUnitSpaceOfBilinear m hcomm hjordan hfr e he) = e := rfl + +/-- The effect space at EJA generality: the interval `[0, e]`, unfolded to the two cone +conditions that define it. -/ +theorem isEffect_ofBilinear (a : J) : + @OrderUnitSpace.IsEffect J (orderUnitSpaceOfBilinear m hcomm hjordan hfr e he) a + ↔ IsSoS m a ∧ IsSoS m (e - a) := by + constructor + · rintro ⟨h0, h1⟩ + refine ⟨?_, h1⟩ + have h0' : IsSoS m (a - 0) := h0 + rwa [sub_zero] at h0' + · rintro ⟨h0, h1⟩ + refine ⟨?_, h1⟩ + show IsSoS m (a - 0) + rwa [sub_zero] + +/-- `EuclideanJordan/OrderUnitSpace.lean`'s spanning theorem, live at EJA generality. It is +here as evidence that the abstract effect API genuinely applies to the constructed structure, +not merely that the structure typechecks. -/ +theorem span_isEffect_eq_top_ofBilinear : + letI := orderUnitSpaceOfBilinear m hcomm hjordan hfr e he + Submodule.span ℝ {a : J | OrderUnitSpace.IsEffect a} = ⊤ := by + letI := orderUnitSpaceOfBilinear m hcomm hjordan hfr e he + exact OrderUnitSpace.span_isEffect_eq_top + +end Characterization + +end OrderUnit + +/-! ## The Euclidean hypothesis: the Archimedean squeeze and the cone of squares + +The two results below need more than the order: they need the coefficients of a spectral +resolution of a *positive* element to be nonnegative, which no amount of cone algebra +supplies. What supplies it is the associative inner product — the "Euclidean" in Euclidean +Jordan algebra. -/ + +section Euclidean + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] +variable {m : J →ₗ[ℝ] J →ₗ[ℝ] J} + +/-- **`L_c` is a positive operator for an idempotent `c`.** + +`L_c = P₁(c) + ½ P_{1/2}(c)` on the nose, and both Peirce projections are idempotent +(`EuclideanJordan/Peirce.lean`'s `mul_peirceOne` feeding `peirceOne_of_eigen`) and self-adjoint (from +self-adjointness of `L_c`, which is `hassoc` at `x := c`). A self-adjoint idempotent `P` +satisfies `⟪P y, y⟫ = ⟪P y, P y⟫ ≥ 0`, so the sum is nonnegative. + +★ The eigenvalue trichotomy is never invoked, and no functional calculus is needed: the two +projections are polynomials in `L_c` that the tree already carries as linear maps. -/ +theorem inner_mul_self_nonneg_of_idem + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hassoc : ∀ x y z : J, inner ℝ (m x y) z = inner ℝ y (m x z)) + {c : J} (hc : m c c = c) (y : J) : + 0 ≤ inner ℝ (m c y) y := by + letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + letI : IsCommJordan J := ⟨hjordan⟩ + letI : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ + have hc' : c * c = c := hc + have hsa : ∀ u v : J, inner ℝ (c * u) v = inner ℝ u (c * v) := fun u v => hassoc c u v + have hsa1 : ∀ u v : J, inner ℝ (peirceOne c u) v = inner ℝ u (peirceOne c v) := by + intro u v + simp only [peirceOne_apply, inner_sub_left, inner_sub_right, real_inner_smul_left, + real_inner_smul_right] + rw [hsa (c * u) v, hsa u (c * v), hsa u v] + have hsah : ∀ u v : J, inner ℝ (peirceHalf c u) v = inner ℝ u (peirceHalf c v) := by + intro u v + simp only [peirceHalf_apply, inner_sub_left, inner_sub_right, real_inner_smul_left, + real_inner_smul_right] + rw [hsa (c * u) v, hsa u (c * v), hsa u v] + have hid1 : peirceOne c (peirceOne c y) = peirceOne c y := + peirceOne_of_eigen (mul_peirceOne hc' y) + have hidh : peirceHalf c (peirceHalf c y) = peirceHalf c y := + peirceHalf_of_eigen_half (mul_peirceHalf hc' y) + have hsplit : (m c y : J) = peirceOne c y + (2 : ℝ)⁻¹ • peirceHalf c y := by + show c * y = _ + simp only [peirceOne_apply, peirceHalf_apply] + module + have key1 : inner ℝ (peirceOne c y) (peirceOne c y) = inner ℝ (peirceOne c y) y := by + rw [hsa1 y (peirceOne c y), hid1, real_inner_comm] + have keyh : inner ℝ (peirceHalf c y) (peirceHalf c y) = inner ℝ (peirceHalf c y) y := by + rw [hsah y (peirceHalf c y), hidh, real_inner_comm] + have h1 : (0 : ℝ) ≤ inner ℝ (peirceOne c y) y := key1 ▸ real_inner_self_nonneg + have hh : (0 : ℝ) ≤ inner ℝ (peirceHalf c y) y := keyh ▸ real_inner_self_nonneg + rw [hsplit, inner_add_left, real_inner_smul_left] + linarith + +/-- **A sum of squares has nonnegative spectral coefficients.** + +Pairing against `q k` reads the coefficient off — the idempotents are pairwise orthogonal for +the inner product because `hassoc` turns `⟪q k, q i⟫` into `⟪q k, q k ∘ q i⟫` — while pairing +against the sum-of-squares presentation is nonnegative term by term, each term being +`⟪L_{q k} f j, f j⟫`. + +This is the fact that both `isArchimedean_ofBilinear` and `isSoS_iff_exists_sq` reduce to, and +it is the only content in this file that the order axioms themselves do not supply. -/ +theorem inner_left_coeff + (hassoc : ∀ x y z : J, inner ℝ (m x y) z = inner ℝ y (m x z)) + {n : ℕ} {q : Fin n → J} {lam : Fin n → ℝ} + (hidem : ∀ i, m (q i) (q i) = q i) + (horth : ∀ i j, i ≠ j → m (q i) (q j) = 0) + {x : J} (hx : x = ∑ i, lam i • q i) (k : Fin n) : + inner ℝ (q k) x = lam k * inner ℝ (q k) (q k) := by + have horthinner : ∀ i, i ≠ k → inner ℝ (q k) (q i) = (0 : ℝ) := by + intro i hi + calc inner ℝ (q k) (q i) = inner ℝ (m (q k) (q k)) (q i) := by rw [hidem k] + _ = inner ℝ (q k) (m (q k) (q i)) := hassoc (q k) (q k) (q i) + _ = 0 := by rw [horth k i (Ne.symm hi), inner_zero_right] + rw [hx, inner_sum, Finset.sum_eq_single k] + · rw [real_inner_smul_right] + · intro i _ hi + rw [real_inner_smul_right, horthinner i hi, mul_zero] + · intro h + exact absurd (Finset.mem_univ k) h + +/-- A coefficient is nonnegative as soon as its idempotent pairs nonnegatively with the +element — the shape shared by `nonneg_coeff_of_isSoS` (where the pairing is nonnegative +because `x` is a sum of squares) and by `hermitian_nonneg_le_isSoS` (where it is nonnegative +because `x` is positive semidefinite). -/ +theorem nonneg_coeff_of_inner_nonneg + (hassoc : ∀ x y z : J, inner ℝ (m x y) z = inner ℝ y (m x z)) + {n : ℕ} {q : Fin n → J} {lam : Fin n → ℝ} + (hidem : ∀ i, m (q i) (q i) = q i) + (horth : ∀ i j, i ≠ j → m (q i) (q j) = 0) + {x : J} (hx : x = ∑ i, lam i • q i) + {k : Fin n} (hk : q k ≠ 0) (hnn : (0 : ℝ) ≤ inner ℝ (q k) x) : 0 ≤ lam k := by + have hxk := inner_left_coeff hassoc hidem horth hx k + have hpos : (0 : ℝ) < inner ℝ (q k) (q k) := real_inner_self_pos.mpr hk + nlinarith [hxk, hnn, hpos] + +theorem nonneg_coeff_of_isSoS + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hassoc : ∀ x y z : J, inner ℝ (m x y) z = inner ℝ y (m x z)) + {n : ℕ} {q : Fin n → J} {lam : Fin n → ℝ} + (hidem : ∀ i, m (q i) (q i) = q i) + (horth : ∀ i j, i ≠ j → m (q i) (q j) = 0) + {x : J} (hx : x = ∑ i, lam i • q i) (hsos : IsSoS m x) + {k : Fin n} (hk : q k ≠ 0) : 0 ≤ lam k := by + obtain ⟨j, f, hf⟩ := hsos + have hnn : (0 : ℝ) ≤ inner ℝ (q k) x := by + rw [hf, inner_sum] + refine Finset.sum_nonneg fun i _ => ?_ + have hstep : inner ℝ (q k) (m (f i) (f i)) = inner ℝ (m (q k) (f i)) (f i) := by + calc inner ℝ (q k) (m (f i) (f i)) + = inner ℝ (m (f i) (f i)) (q k) := real_inner_comm _ _ + _ = inner ℝ (f i) (m (f i) (q k)) := hassoc (f i) (f i) (q k) + _ = inner ℝ (f i) (m (q k) (f i)) := by rw [hcomm (f i) (q k)] + _ = inner ℝ (m (q k) (f i)) (f i) := real_inner_comm _ _ + rw [hstep] + exact inner_mul_self_nonneg_of_idem hcomm hjordan hassoc (hidem k) (f i) + exact nonneg_coeff_of_inner_nonneg hassoc hidem horth hx hk hnn + +variable [FiniteDimensional ℝ J] + +/-- **The genuine Archimedean property**, in the sense +`EuclideanJordan/OrderUnitSpace.lean`'s `IsArchimedean` carries: an element under *every* +positive multiple of the unit is nonpositive. + +This is strictly stronger than the class's `archimedean` field, which is order-unit +boundedness only. ★ It is proved here at EJA generality; before this, `H_n(𝕜)` was the only +carrier known to satisfy it, so results assuming `IsArchimedean` had a single model. -/ +theorem isArchimedean_ofBilinear + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (hassoc : ∀ x y z : J, inner ℝ (m x y) z = inner ℝ y (m x z)) + (e : J) (he : ∀ y : J, m e y = y) : + @OrderUnitSpace.IsArchimedean J (orderUnitSpaceOfBilinear m hcomm hjordan hfr e he) := by + intro x hx + show IsSoS m (0 - x) + obtain ⟨n, q, lam, hidem, horth, hsum, hxe⟩ := + spectral_resolution_bilinear m hcomm hjordan hfr e he x + have hlam : ∀ i, q i ≠ 0 → lam i ≤ 0 := by + intro i hi + by_contra hcon + have hpos : 0 < lam i := not_le.mp hcon + have hsos : IsSoS m ((lam i / 2) • e - x) := hx (lam i / 2) (by linarith) + have hrw := smul_unit_sub_eq hsum hxe (lam i / 2) + have := nonneg_coeff_of_isSoS hcomm hjordan hassoc hidem horth hrw (hrw ▸ hsos) hi + linarith + have h0 := smul_unit_sub_eq hsum hxe 0 + rw [zero_smul] at h0 + rw [h0] + refine isSoS_sum _ _ fun i _ => ?_ + by_cases hi : q i = 0 + · rw [hi, smul_zero] + exact isSoS_zero + · exact isSoS_smul_idem (by linarith [hlam i hi]) (hidem i) + +/-- **The cone of the order is the cone of squares.** The two readings of `0 ≤ x` — a sum of +squares, and a single square — coincide, so a development that *defines* positivity as "is a +square" agrees with the one built here; that agreement is a theorem rather than a stipulation. + +The forward direction is the whole content: a sum of squares has nonnegative coefficients, so +`∑ √(lam i) • q i` squares back to it by orthogonality. -/ +theorem isSoS_iff_exists_sq + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (hassoc : ∀ x y z : J, inner ℝ (m x y) z = inner ℝ y (m x z)) + (e : J) (he : ∀ y : J, m e y = y) (x : J) : + IsSoS m x ↔ ∃ y : J, x = m y y := by + constructor + · intro hsos + obtain ⟨n, q, lam, hidem, horth, hsum, hxe⟩ := + spectral_resolution_bilinear m hcomm hjordan hfr e he x + set s : Fin n → ℝ := fun i => Real.sqrt (max (lam i) 0) with hs + refine ⟨∑ i, s i • q i, ?_⟩ + have step : ∀ v : J, m (∑ i, s i • q i) v = ∑ i, s i • m (q i) v := by + intro v + rw [map_sum, LinearMap.sum_apply] + exact Finset.sum_congr rfl fun i _ => by rw [map_smul, LinearMap.smul_apply] + have step2 : ∀ i, m (q i) (∑ j, s j • q j) = s i • q i := by + intro i + rw [map_sum, Finset.sum_eq_single i] + · rw [map_smul, hidem i] + · intro j _ hj + rw [map_smul, horth i j (Ne.symm hj), smul_zero] + · intro h + exact absurd (Finset.mem_univ i) h + have hexp : m (∑ i, s i • q i) (∑ i, s i • q i) = ∑ i, (s i * s i) • q i := by + rw [step] + simp only [step2, smul_smul] + rw [hexp, hxe] + refine Finset.sum_congr rfl fun i _ => ?_ + by_cases hi : q i = 0 + · rw [hi, smul_zero, smul_zero] + · have hnn : 0 ≤ lam i := + nonneg_coeff_of_isSoS hcomm hjordan hassoc hidem horth hxe hsos hi + rw [hs] + simp only + rw [max_eq_left hnn, Real.mul_self_sqrt hnn] + · rintro ⟨y, rfl⟩ + exact ⟨1, fun _ => y, by simp⟩ + +end Euclidean + +/-! ## A live carrier for the hypothesis bundle + +`H_n(𝕜)` satisfies every hypothesis above, including the associative-inner-product hypothesis +this file introduces, so nothing in the two previous sections is conditional on a premise with +no carrier. The construction is applied to it at the end; the resulting order unit space is a +*second* one on `H_n(𝕜)`, kept as a `def` and never an instance, and it is **not** proved +equal to the vendored Loewner structure (see the module docstring). -/ + +section Carrier + +open ComplexOrder + +variable {n : Type*} [Fintype n] [DecidableEq n] {𝕜 : Type*} [RCLike 𝕜] + +/-- **The Euclidean hypothesis, live on `H_n(𝕜)`.** Both sides are +`½(Tr[ABC] + Tr[BAC])` after `Matrix.trace_mul_cycle`. -/ +theorem hermitian_jordan_assoc (A B C : HermitianMat n 𝕜) : + inner ℝ (jordanBilinG 𝕜 A B) C = inner ℝ B (jordanBilinG 𝕜 A C) := by + have htr : (((2 : 𝕜)⁻¹ • (A.mat * B.mat + B.mat * A.mat)) * C.mat).trace + = (B.mat * ((2 : 𝕜)⁻¹ • (A.mat * C.mat + C.mat * A.mat))).trace := by + rw [Matrix.smul_mul, Matrix.mul_smul, Matrix.trace_smul, Matrix.trace_smul] + congr 1 + rw [Matrix.add_mul, Matrix.mul_add, Matrix.trace_add, Matrix.trace_add, add_comm] + congr 1 + · rw [mul_assoc] + · rw [Matrix.trace_mul_cycle, Matrix.trace_mul_cycle, mul_assoc] + rw [HermitianMat.inner_def, HermitianMat.inner_def, jordanBilin_applyG, jordanBilin_applyG, + HermitianMat.symmMul_toMat, HermitianMat.symmMul_toMat, htr] + +theorem hermitian_jordan_comm (A B : HermitianMat n 𝕜) : + jordanBilinG 𝕜 A B = jordanBilinG 𝕜 B A := + HermitianMat.symmMul_comm A B + +/-! The two facts below are the only ones that need `HermMul`'s *scoped* multiplicative +instances (`CommMagma`, `MulZeroClass`, `IsCommJordan`), so the `open` is confined to this +block rather than covering the whole section. + +★ **That confinement is hygiene, not a fix, and the record should say so.** It was made while +chasing an elaboration blow-up in `hermitian_isArchimedean_ofEJA` and +`hermitian_isSoS_iff_exists_sq` on the theory that a second multiplicative structure in scope +was making unification search a diamond. **It changed nothing** — the timeout survived it +unaltered, as did a second theory that the mismatch between `HermitianMat.instAddCommGroup` +and `NormedAddCommGroup.toAddCommGroup` was being paid for (that defeq costs about a second in +isolation). The real cause is recorded at the two call sites below. -/ + +section ScopedMul +open HermMul + +theorem hermitian_jordan_id (A B : HermitianMat n 𝕜) : + jordanBilinG 𝕜 (jordanBilinG 𝕜 A B) (jordanBilinG 𝕜 A A) + = jordanBilinG 𝕜 A (jordanBilinG 𝕜 B (jordanBilinG 𝕜 A A)) := by + simpa only [jordanBilin_applyG, ← HermMul.mul_eq_symmMul] using + IsCommJordan.lmul_comm_rmul_rmul A B + +theorem hermitian_formallyReal (k : ℕ) (f : Fin k → HermitianMat n 𝕜) + (h : (∑ i, jordanBilinG 𝕜 (f i) (f i)) = 0) (i : Fin k) : f i = 0 := + IsFormallyReal.eq_zero_of_sum_mul_self Finset.univ f + (by simpa only [jordanBilin_applyG, ← HermMul.mul_eq_symmMul] using h) + i (Finset.mem_univ i) + +end ScopedMul + +theorem hermitian_jordan_unit (A : HermitianMat n 𝕜) : jordanBilinG 𝕜 1 A = A := + HermitianMat.one_symmMul A + +/-! ★★ **Why every application below pins `(J := HermitianMat n 𝕜)` explicitly.** + +Without it these three declarations exhausted 200 000 heartbeats, and raising the budget was +the wrong move: with `maxHeartbeats 0` the elaboration ran for two minutes and then *failed*, +having defaulted `J := ℕ` off the bare numeral `1` supplied for the explicit `e : J`. The +profiler names the cost exactly. With `J` unsolved, the `m` argument stays a metavariable +through the argument list, so checking `hermitian_formallyReal` becomes the higher-order +problem + + `∑ i, (?m (f i)) (f i) =?= ∑ i, ((jordanBilinG ?n) (f i)) (f i)`, + +and `isDefEq` unfolds `Finset.sum` through `Multiset.foldr`, `Quot.liftOn` and `List.map` +hunting for a match — thirteen seconds, and it fails. Pinning `J` makes the same unification +first-order and the whole file elaborates in about six seconds. + +The transferable rule: **an `isDefEq` timeout under a `Finset.sum` usually means a +metavariable in the function position, not a budget that is too small.** Ascribing the +numeral (`(1 : HermitianMat n 𝕜)`) is necessary too but not sufficient — it removes the wrong +`ℕ` answer without removing the search. -/ + +/-- **The construction, applied to `H_n(𝕜)`.** Its only job is to witness that the +hypothesis bundle of `orderUnitSpaceOfBilinear` is inhabited. -/ +@[instance_reducible] +def hermitianOrderUnitOfEJA : OrderUnitSpace (HermitianMat n 𝕜) := + orderUnitSpaceOfBilinear (J := HermitianMat n 𝕜) (jordanBilinG 𝕜) hermitian_jordan_comm + hermitian_jordan_id hermitian_formallyReal (1 : HermitianMat n 𝕜) hermitian_jordan_unit + +/-- The Archimedean squeeze holds for the constructed structure on `H_n(𝕜)`, so +`isArchimedean_ofBilinear` is not vacuous either. -/ +theorem hermitian_isArchimedean_ofEJA : + @OrderUnitSpace.IsArchimedean (HermitianMat n 𝕜) hermitianOrderUnitOfEJA := + isArchimedean_ofBilinear (J := HermitianMat n 𝕜) (m := jordanBilinG 𝕜) hermitian_jordan_comm + hermitian_jordan_id hermitian_formallyReal hermitian_jordan_assoc (1 : HermitianMat n 𝕜) + hermitian_jordan_unit + +/-- And the cone of the constructed order is the cone of squares on `H_n(𝕜)`. -/ +theorem hermitian_isSoS_iff_exists_sq (A : HermitianMat n 𝕜) : + IsSoS (jordanBilinG 𝕜) A ↔ ∃ B : HermitianMat n 𝕜, A = jordanBilinG 𝕜 B B := + isSoS_iff_exists_sq (J := HermitianMat n 𝕜) (m := jordanBilinG 𝕜) hermitian_jordan_comm + hermitian_jordan_id hermitian_formallyReal hermitian_jordan_assoc (1 : HermitianMat n 𝕜) + hermitian_jordan_unit A + +/-! ### The constructed order *is* the Loewner order on `H_n(𝕜)` + +Fidelity, not inhabitedness: the abstract cone could have been inhabited and still been the +wrong cone. Both containments are below. -/ + +/-- A Jordan square in `H_n(𝕜)` is positive semidefinite: the Jordan square is the matrix +square, and `M M = Mᴴ M` for `M` Hermitian. -/ +theorem hermitian_sq_nonneg (B : HermitianMat n 𝕜) : 0 ≤ jordanBilinG 𝕜 B B := by + rw [HermitianMat.zero_le_iff, jordanBilin_applyG, HermitianMat.symmMul_self] + simpa only [B.H.eq] using Matrix.posSemidef_conjTranspose_mul_self B.mat + +/-- A Jordan idempotent is therefore positive semidefinite: it *is* its own square. -/ +theorem hermitian_idem_nonneg {C : HermitianMat n 𝕜} (hC : jordanBilinG 𝕜 C C = C) : 0 ≤ C := + hC ▸ hermitian_sq_nonneg C + +/-- Sums of squares are positive semidefinite. -/ +theorem hermitian_isSoS_le_nonneg {A : HermitianMat n 𝕜} (h : IsSoS (jordanBilinG 𝕜) A) : + 0 ≤ A := by + obtain ⟨k, f, hf⟩ := h + rw [hf] + exact Finset.sum_nonneg fun i _ => hermitian_sq_nonneg (f i) + +/-- **The containment that needed the Euclidean hypothesis**: a positive semidefinite matrix is +a sum of squares. Its spectral idempotents are themselves positive semidefinite, so +`⟪q i, A⟫ ≥ 0` by `HermitianMat.inner_ge_zero`, and `inner_left_coeff` reads that off as +`lam i ≥ 0`. -/ +theorem hermitian_nonneg_le_isSoS {A : HermitianMat n 𝕜} (hA : 0 ≤ A) : + IsSoS (jordanBilinG 𝕜) A := by + obtain ⟨N, q, lam, hidem, horth, hsum, hxe⟩ := + spectral_resolution_bilinear (J := HermitianMat n 𝕜) (jordanBilinG 𝕜) hermitian_jordan_comm + hermitian_jordan_id hermitian_formallyReal (1 : HermitianMat n 𝕜) hermitian_jordan_unit A + rw [hxe] + refine isSoS_sum _ _ fun i _ => ?_ + by_cases hi : q i = 0 + · rw [hi, smul_zero] + exact isSoS_zero + · refine isSoS_smul_idem ?_ (hidem i) + refine nonneg_coeff_of_inner_nonneg (J := HermitianMat n 𝕜) (m := jordanBilinG 𝕜) + hermitian_jordan_assoc hidem horth hxe hi ?_ + exact HermitianMat.inner_ge_zero (hermitian_idem_nonneg (hidem i)) hA + +/-- **The abstract cone is the Loewner cone on `H_n(𝕜)`.** -/ +theorem hermitian_isSoS_iff_nonneg (A : HermitianMat n 𝕜) : + IsSoS (jordanBilinG 𝕜) A ↔ 0 ≤ A := + ⟨hermitian_isSoS_le_nonneg, hermitian_nonneg_le_isSoS⟩ + +/-- **The constructed order relation is the Loewner order relation.** -/ +theorem hermitian_le_ofEJA_iff (A B : HermitianMat n 𝕜) : + @LE.le (HermitianMat n 𝕜) (@Preorder.toLE _ (@PartialOrder.toPreorder _ + (@OrderUnitSpace.toPartialOrder _ hermitianOrderUnitOfEJA))) A B ↔ A ≤ B := by + rw [show (@LE.le (HermitianMat n 𝕜) (@Preorder.toLE _ (@PartialOrder.toPreorder _ + (@OrderUnitSpace.toPartialOrder _ hermitianOrderUnitOfEJA))) A B) + = IsSoS (jordanBilinG 𝕜) (B - A) from rfl, + hermitian_isSoS_iff_nonneg, sub_nonneg] + +end Carrier + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean b/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean new file mode 100644 index 0000000000..7c333edd01 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean @@ -0,0 +1,493 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +import LeanPool.EuclideanJordan.EuclideanJordan.Order + + +/-! +# Koecher / Alfsen–Shultz: a unital linear order isomorphism is a Jordan automorphism + +On a finite-dimensional formally real Jordan algebra, a linear bijection `Φ` that fixes the unit +and preserves the cone of sums of squares **in both directions** preserves the Jordan product. + +This is the classical theorem — Koecher; Alfsen–Shultz, *Geometry of State Spaces*, Thm 2.80. It is +**not** van Imhoff–Roelands' JB-generality version (arXiv:1904.09278), which concludes linearity +rather than assuming it. Here `Φ : J ≃ₗ[ℝ] J` is linear **by type**, and that is the whole +difference. + +## The route + +Jordan-multiplicativity reduces to preservation of squares, and squares to preservation of +idempotents, provided idempotents can be recognised *order-theoretically*. They can: + +* `c` is idempotent **iff** `c` is **sharp** — `0 ≤ c ≤ e` and no nonzero element of the cone lies + below both `c` and `e − c` (`isSharp_iff_idem`). + +Sharpness is visibly transported by `Φ`, since every clause is a statement about the cone and the +unit; so `Φ` maps idempotents to idempotents. A complete orthogonal family `∑ qᵢ = e` goes to a +family with `∑ Φqᵢ = e`, and orthogonality is then *recovered* rather than transported +(`orth_of_sum_eq_unit`). Finally `x = ∑ λᵢ qᵢ ⟹ x ∘ x = ∑ λᵢ² qᵢ` gives `Φ(x ∘ x) = Φx ∘ Φx`, and +polarisation finishes it. + +★ **The reflecting half of the order hypothesis is load-bearing.** `horder` is a *biconditional*. +A one-directional `IsSoS x → IsSoS (Φ x)` would not do: the sharpness clause is a `∀` over the cone, +and transporting it along `Φ.symm` needs the converse. This is checked, not assumed — see +`map_idem_of_orderIso`, where `(horder _).mpr` appears three times. + +## What the argument runs on + +Two facts about the cone, both of which need an **associative positive definite form**: + +* a sum of squares has nonnegative spectral coefficients (`nonneg_coeff`); +* the **face lemma**: `0 ≤ x ≤ c` with `c` idempotent forces `c ∘ x = x` (`face_lemma`). + +`EuclideanJordan/Order.lean` proves the first from the *ambient* inner product, under the hypothesis that the +inner product is associative for the Jordan product. A consumer that carries an inner product +and a Jordan product as unrelated data has no such hypothesis available, which is why this file +pairs against `EuclideanJordan/TraceForm.lean`'s `traceForm` instead: a form built from the algebra, so nothing has to +be assumed about an ambient one. Both facts are proved here from scratch in that vocabulary; the +`inner`-shaped originals in `EuclideanJordan/Order.lean` are untouched and are not used. + +## Extreme points are not needed + +The natural-looking characterisation — `c` is idempotent iff it is an extreme point of `[0, e]` — +is **avoided deliberately**. ★ The reason is a price judgment and is recorded as one, not as a +theorem: on the classical proof both of its directions run through the same face lemma that +sharpness needs, and it additionally drags in Mathlib's `Set.extremePoints` and convexity API and +forces `[0, e]` to be stated as a `Set`. Nothing below establishes that, because the +extreme-points route was never built. + +★ `EuclideanJordan/OrderUnitSpace.lean` carries an `IsSharp` of its own, and this file does +**not** use it or bridge to it. That one is stated over an `OrderUnitSpace` instance, and getting +one here would mean instantiating `EuclideanJordan/Order.lean`'s `orderUnitSpaceOfBilinear` — a `def`, whose +own docstring warns that instantiating it puts a second `PartialOrder J` in scope. +★★ **The two are not literally the same formula, and the difference is worth stating rather than +glossing.** `OrderUnitSpace.IsSharp` quantifies its witness over the *effects* (`IsEffect a`, i.e. +`0 ≤ a ≤ 𝟙`); `IsSharp` below quantifies over the whole cone (`IsSoS mulLₗ x`, with no upper bound +on `x`). The extra clause is redundant — `x ≤ c` and `c ≤ e` give `x ≤ e` by transitivity of the +cone order — so the two conditions are equivalent; but **that equivalence is proved nowhere below +and nothing needs it**, and the version used here is the one whose witness hypothesis is weaker, +hence the harder condition to satisfy and the safer one to transport. + +## What this closes, and what it does not + +★★ **This is one clause of a larger statement, and which clause it is matters.** The route that +builds an order automorphism `Θ_a` out of a sequential product and then reads it as a Jordan +automorphism needs three separate things: the construction of `Θ_a` (van de Wetering, +arXiv:1803.11139, Prop 5.3), the upgrade of a unital linear order isomorphism to a Jordan +automorphism, and the fixing and cocycle properties (vdW Props 5.5, 5.7). **Only the upgrade is +proved here.** The other two are cited, not formalized, and nothing below establishes them. +-/ + +noncomputable section + +namespace EuclideanJordan + +open Finset + +/-! ## Pairing against the trace form -/ + +section Abstract + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] [IsFormallyReal J] [Module.Finite ℝ J] + +omit [IsFormallyReal J] [Module.Finite ℝ J] in +/-- **`L_c` is a positive operator for the trace form**, in the sharp form that also reads off when +the pairing vanishes: `τ(c ∘ y, y)` is the sum of two squares of the form, one for each nonzero +Peirce eigenvalue of `c`. + +`L_c = P₁(c) + ½ P_{1/2}(c)`; both Peirce projections are self-adjoint for `τ` (associativity, three +times) and idempotent, so `τ(P y, y) = τ(P y, P y)`. + +★ Stated as an equation rather than as an inequality because both consumers need it: nonnegativity +falls out, and so does `c ∘ y = 0` from a vanishing pairing — which an inequality would have +lost. -/ +theorem traceForm_mulL_split {c : J} (hc : c * c = c) (y : J) : + traceForm (c * y) y + = traceForm (peirceOne c y) (peirceOne c y) + + (2 : ℝ)⁻¹ * traceForm (peirceHalf c y) (peirceHalf c y) := by + have hsa1 : ∀ u v : J, traceForm (peirceOne c u) v = traceForm u (peirceOne c v) := by + intro u v + simp only [peirceOne_apply, map_sub, map_smul, LinearMap.sub_apply, LinearMap.smul_apply, + smul_eq_mul] + rw [traceForm_assoc c (c * u) v, traceForm_assoc c u (c * v), traceForm_assoc c u v] + have hsah : ∀ u v : J, traceForm (peirceHalf c u) v = traceForm u (peirceHalf c v) := by + intro u v + simp only [peirceHalf_apply, map_sub, map_smul, LinearMap.sub_apply, LinearMap.smul_apply, + smul_eq_mul] + rw [traceForm_assoc c (c * u) v, traceForm_assoc c u (c * v), traceForm_assoc c u v] + have hid1 : peirceOne c (peirceOne c y) = peirceOne c y := + peirceOne_of_eigen (mul_peirceOne hc y) + have hidh : peirceHalf c (peirceHalf c y) = peirceHalf c y := + peirceHalf_of_eigen_half (mul_peirceHalf hc y) + have key1 : traceForm (peirceOne c y) (peirceOne c y) = traceForm (peirceOne c y) y := by + rw [hsa1 y (peirceOne c y), hid1, traceForm_comm] + have keyh : traceForm (peirceHalf c y) (peirceHalf c y) = traceForm (peirceHalf c y) y := by + rw [hsah y (peirceHalf c y), hidh, traceForm_comm] + have hsplit : (c * y : J) = peirceOne c y + (2 : ℝ)⁻¹ • peirceHalf c y := by + simp only [peirceOne_apply, peirceHalf_apply] + module + rw [key1, keyh, hsplit, map_add, map_smul, LinearMap.add_apply, LinearMap.smul_apply, + smul_eq_mul] + +theorem traceForm_mulL_nonneg_of_idem {c : J} (hc : c * c = c) (y : J) : + 0 ≤ traceForm (c * y) y := by + rw [traceForm_mulL_split hc y] + have h1 := traceForm_self_nonneg (peirceOne c y) + have h2 := traceForm_self_nonneg (peirceHalf c y) + linarith + +/-- A vanishing pairing kills the product outright: both Peirce components of `y` at `c` vanish, so +`c ∘ y = 0`. This is where definiteness of `traceForm` is spent. -/ +theorem mul_eq_zero_of_traceForm_mulL_eq_zero {c : J} (hc : c * c = c) {y : J} + (h : traceForm (c * y) y = 0) : c * y = 0 := by + rw [traceForm_mulL_split hc y] at h + have h1 := traceForm_self_nonneg (peirceOne c y) + have h2 := traceForm_self_nonneg (peirceHalf c y) + have e1 : peirceOne c y = 0 := eq_zero_of_traceForm_self_eq_zero (by linarith) + have e2 : peirceHalf c y = 0 := eq_zero_of_traceForm_self_eq_zero (by linarith) + have hsplit : (c * y : J) = peirceOne c y + (2 : ℝ)⁻¹ • peirceHalf c y := by + simp only [peirceOne_apply, peirceHalf_apply] + module + rw [hsplit, e1, e2, smul_zero, add_zero] + +/-- **An idempotent pairs nonnegatively with the cone.** Associativity moves `c` onto one factor of +each square, and the previous lemma does the rest. -/ +theorem traceForm_nonneg_of_idem_of_isSoS {c x : J} (hc : c * c = c) + (hx : IsSoS mulLₗ x) : 0 ≤ traceForm c x := by + obtain ⟨k, f, hf⟩ := hx + simp only [mulLₗ_apply, mulL_apply] at hf + rw [hf, map_sum] + refine Finset.sum_nonneg fun i _ => ?_ + have hmove : traceForm c (f i * f i) = traceForm (c * f i) (f i) := by + simp only [traceForm_apply] + exact (jtr_assoc c (f i) (f i)).symm + rw [hmove] + exact traceForm_mulL_nonneg_of_idem hc (f i) + +/-- **A vanishing pairing against the cone annihilates.** Every square in the presentation is +killed factor by factor, and `J₀(c)` is a subalgebra, so the whole element is killed. -/ +theorem mul_eq_zero_of_traceForm_eq_zero {c x : J} (hc : c * c = c) + (hx : IsSoS mulLₗ x) (h : traceForm c x = 0) : c * x = 0 := by + obtain ⟨k, f, hf⟩ := hx + simp only [mulLₗ_apply, mulL_apply] at hf + have hterm : ∀ i : Fin k, 0 ≤ traceForm c (f i * f i) := by + intro i + have hmove : traceForm c (f i * f i) = traceForm (c * f i) (f i) := by + simp only [traceForm_apply] + exact (jtr_assoc c (f i) (f i)).symm + rw [hmove] + exact traceForm_mulL_nonneg_of_idem hc (f i) + have hsum : (∑ i, traceForm c (f i * f i)) = 0 := by rw [← map_sum, ← hf]; exact h + have hzero : ∀ i : Fin k, traceForm c (f i * f i) = 0 := fun i => + (Finset.sum_eq_zero_iff_of_nonneg fun j _ => hterm j).mp hsum i (Finset.mem_univ i) + have hkill : ∀ i : Fin k, c * f i = 0 := by + intro i + refine mul_eq_zero_of_traceForm_mulL_eq_zero hc ?_ + have hmove : traceForm c (f i * f i) = traceForm (c * f i) (f i) := by + simp only [traceForm_apply] + exact (jtr_assoc c (f i) (f i)).symm + rw [← hmove] + exact hzero i + rw [hf, Finset.mul_sum] + exact Finset.sum_eq_zero fun i _ => eigen_zero_mul_zero hc (hkill i) (hkill i) + +/-! ## The face lemma -/ + +omit [IsCommJordan J] [Module ℝ J] [IsScalarTower ℝ J J] [IsFormallyReal J] [Module.Finite ℝ J] in +/-- The orthocomplement of an idempotent is an idempotent. -/ +theorem sub_idem {e c : J} (he : ∀ y : J, e * y = y) (hc : c * c = c) : + (e - c) * (e - c) = e - c := by + have hce : c * e = c := by rw [mul_comm]; exact he c + rw [sub_mul, mul_sub, mul_sub, he, he, hc, hce, sub_self, sub_zero] + +/-- **The face lemma.** If `0 ≤ x ≤ c` with `c` idempotent, then `c ∘ x = x`. + +Pair against the complementary idempotent `e − c`: the pairing with `x` is nonnegative, and bounded +above by the pairing with `c`, which is `τ((e − c) ∘ c) = τ(0) = 0`. So it vanishes, and the +previous lemma turns that into `(e − c) ∘ x = 0`. -/ +theorem face_lemma {e c x : J} (he : ∀ y : J, e * y = y) (hc : c * c = c) + (hx : IsSoS mulLₗ x) (hcx : IsSoS mulLₗ (c - x)) : c * x = x := by + have hd : (e - c) * (e - c) = e - c := sub_idem he hc + have h1 : 0 ≤ traceForm (e - c) x := traceForm_nonneg_of_idem_of_isSoS hd hx + have h2 : 0 ≤ traceForm (e - c) (c - x) := traceForm_nonneg_of_idem_of_isSoS hd hcx + have hdc : traceForm (e - c) c = 0 := by + have hz : (e - c) * c = 0 := by rw [sub_mul, he, hc, sub_self] + simp only [traceForm_apply, hz, map_zero] + rw [map_sub, hdc] at h2 + have hzero : traceForm (e - c) x = 0 := le_antisymm (by linarith) h1 + have hdx : (e - c) * x = 0 := mul_eq_zero_of_traceForm_eq_zero hd hx hzero + rw [sub_mul, he, sub_eq_zero] at hdx + exact hdx.symm + +/-- **Idempotents summing to the unit are pairwise orthogonal** — orthogonality is *recovered*, not +transported. `qⱼ` lies under the idempotent `e − qᵢ`, because the difference is the sum of the +remaining members of the family; the face lemma then gives `(e − qᵢ) ∘ qⱼ = qⱼ`. -/ +theorem orth_of_sum_eq_unit {n : ℕ} {q : Fin n → J} {e : J} (he : ∀ y : J, e * y = y) + (hidem : ∀ i, q i * q i = q i) (hsum : (∑ i, q i) = e) (i j : Fin n) (hij : i ≠ j) : + q i * q j = 0 := by + classical + have hjm : j ∈ Finset.univ.erase i := Finset.mem_erase.mpr ⟨Ne.symm hij, Finset.mem_univ j⟩ + have h1 : (∑ k, q k) = q i + ∑ k ∈ Finset.univ.erase i, q k := + (Finset.add_sum_erase _ q (Finset.mem_univ i)).symm + have h2 : (∑ k ∈ Finset.univ.erase i, q k) + = q j + ∑ k ∈ (Finset.univ.erase i).erase j, q k := + (Finset.add_sum_erase _ q hjm).symm + have hrest : e - q i - q j = ∑ k ∈ (Finset.univ.erase i).erase j, q k := by + rw [← hsum, h1, h2]; abel + have hsos : IsSoS mulLₗ (e - q i - q j) := by + rw [hrest] + exact isSoS_sum _ _ fun k _ => isSoS_of_idem (hidem k) + have hfl : (e - q i) * q j = q j := + face_lemma he (sub_idem he (hidem i)) (isSoS_of_idem (hidem j)) hsos + rw [sub_mul, he, sub_eq_self] at hfl + exact hfl + +/-! ## Reading the spectral coefficients -/ + +omit [IsCommJordan J] [IsFormallyReal J] [Module.Finite ℝ J] in +/-- A combination of orthogonal idempotents with nonnegative coefficients is in the cone. The +coefficient condition is only imposed where the idempotent is nonzero, matching what +`nonneg_coeff` can supply: `EuclideanJordan/Spectral.lean`'s resolution pads with a possibly-zero idempotent, +whose coefficient is unconstrained. -/ +theorem isSoS_sum_smul_idem {n : ℕ} {q : Fin n → J} (hidem : ∀ i, q i * q i = q i) + {g : Fin n → ℝ} (hg : ∀ i, q i ≠ 0 → 0 ≤ g i) : + IsSoS mulLₗ (∑ i, g i • q i) := by + refine isSoS_sum _ _ fun i _ => ?_ + by_cases h : q i = 0 + · rw [h, smul_zero]; exact isSoS_zero + · exact isSoS_smul_idem (hg i h) (hidem i) + +/-- **A sum of squares has nonnegative spectral coefficients.** Pairing against `qₖ` reads the +coefficient off directly — `τ(qₖ, qᵢ) = tr(L_{qₖ ∘ qᵢ})` is `0` off the diagonal by orthogonality — +and the pairing is nonnegative because `x` is in the cone; the diagonal value `tr(L_{qₖ})` is at +least `1`. -/ +theorem nonneg_coeff {n : ℕ} {q : Fin n → J} {lam : Fin n → ℝ} + (hidem : ∀ i, q i * q i = q i) (horth : ∀ i j, i ≠ j → q i * q j = 0) + {x : J} (hx : x = ∑ i, lam i • q i) (hsos : IsSoS mulLₗ x) + {k : Fin n} (hk : q k ≠ 0) : 0 ≤ lam k := by + have hpair : traceForm (q k) x = lam k * jtr (q k) := by + rw [hx, map_sum, Finset.sum_eq_single k] + · rw [map_smul, smul_eq_mul, traceForm_apply, hidem k] + · intro i _ hik + rw [map_smul, smul_eq_mul, traceForm_apply, horth k i (Ne.symm hik), map_zero, mul_zero] + · intro h; exact absurd (Finset.mem_univ k) h + have hnn : 0 ≤ traceForm (q k) x := traceForm_nonneg_of_idem_of_isSoS (hidem k) hsos + have hpos : (0 : ℝ) < jtr (q k) := + lt_of_lt_of_le zero_lt_one (one_le_jtr_of_idem (hidem k) hk) + nlinarith [hpair, hnn, hpos] + +omit [IsCommJordan J] [IsScalarTower ℝ J J] [IsFormallyReal J] [Module.Finite ℝ J] in +/-- Subtracting a multiple of one member of a family shifts exactly that coefficient. The +bookkeeping step behind both witnesses in `idem_of_isSharp`. -/ +theorem sum_smul_sub_smul {n : ℕ} (q : Fin n → J) (g : Fin n → ℝ) (i : Fin n) (mu : ℝ) : + (∑ j, g j • q j) - mu • q i = ∑ j, (if j = i then g i - mu else g j) • q j := by + classical + have hpt : ∀ j : Fin n, (if j = i then g i - mu else g j) • q j + = g j • q j - (if j = i then mu • q i else 0) := by + intro j + by_cases hj : j = i + · subst hj; simp [sub_smul] + · simp [hj] + calc (∑ j, g j • q j) - mu • q i + = (∑ j, g j • q j) - ∑ j, (if j = i then mu • q i else 0) := by + simp [Finset.sum_ite_eq'] + _ = ∑ j, (g j • q j - (if j = i then mu • q i else 0)) := by + rw [Finset.sum_sub_distrib] + _ = ∑ j, (if j = i then g i - mu else g j) • q j := + (Finset.sum_congr rfl fun j _ => hpt j).symm + +/-! ## Sharpness -/ + +/-- **A sharp element**: in the cone, below the unit, and with no nonzero element of the cone below +both it and its complement. + +This is `EuclideanJordan/OrderUnitSpace.lean`'s `IsSharp` written in the sums-of-squares +vocabulary, with `0 ≤ z` spelled `IsSoS mulLₗ z` and `a ≤ b` spelled `IsSoS mulLₗ (b - a)`. No +lemma below relates the two; see the module docstring for why not. -/ +def IsSharp (e c : J) : Prop := + IsSoS mulLₗ c ∧ IsSoS mulLₗ (e - c) ∧ + ∀ x : J, IsSoS mulLₗ x → IsSoS mulLₗ (c - x) → IsSoS mulLₗ (e - c - x) → x = 0 + +/-- An idempotent is sharp. The face lemma applies at `c` and at `e − c`, giving `x = c ∘ x` and +`x = (e − c) ∘ x = x − c ∘ x`, so `x = 0`. -/ +theorem isSharp_of_idem {e c : J} (he : ∀ y : J, e * y = y) (hc : c * c = c) : IsSharp e c := by + refine ⟨isSoS_of_idem hc, isSoS_of_idem (sub_idem he hc), fun x hx hcx hecx => ?_⟩ + have h1 : c * x = x := face_lemma he hc hx hcx + have h2 : (e - c) * x = x := face_lemma he (sub_idem he hc) hx hecx + rw [sub_mul, he, h1, sub_self] at h2 + exact h2.symm + +/-- **A sharp element is an idempotent** — the half that spends the spectral theorem. + +Resolve `c = ∑ λᵢ qᵢ`. Being in the cone forces `λᵢ ≥ 0` and being below the unit forces +`λᵢ ≤ 1`, both by `nonneg_coeff`. A coefficient strictly inside `(0, 1)` would make +`min(λₖ, 1 − λₖ) • qₖ` a nonzero witness against sharpness, so every coefficient at a nonzero +idempotent is `0` or `1` — and then `λᵢ² = λᵢ` termwise. -/ +theorem idem_of_isSharp {e c : J} (he : ∀ y : J, e * y = y) (h : IsSharp e c) : c * c = c := by + obtain ⟨hc, hec, hsharp⟩ := h + obtain ⟨n, q, lam, hfam, hsum, hcq⟩ := spectral_resolution_complete e he c + have hec' : e - c = ∑ i, (1 - lam i) • q i := by + have hr := smul_unit_sub_eq hsum hcq 1 + rwa [one_smul] at hr + have hlo : ∀ i, q i ≠ 0 → 0 ≤ lam i := fun i hi => + nonneg_coeff hfam.idem hfam.orth hcq hc hi + have hhi : ∀ i, q i ≠ 0 → lam i ≤ 1 := by + intro i hi + have := nonneg_coeff hfam.idem hfam.orth hec' hec hi + linarith + have hbin : ∀ i, q i ≠ 0 → lam i = 0 ∨ lam i = 1 := by + intro i hi + rcases eq_or_lt_of_le (hlo i hi) with h0 | hlt0 + · exact Or.inl h0.symm + rcases eq_or_lt_of_le (hhi i hi) with h1 | hlt1 + · exact Or.inr h1 + exfalso + have hmupos : 0 < min (lam i) (1 - lam i) := lt_min hlt0 (by linarith) + set mu := min (lam i) (1 - lam i) with hmudef + have hmu1 : mu ≤ lam i := min_le_left _ _ + have hmu2 : mu ≤ 1 - lam i := min_le_right _ _ + have hwx : IsSoS mulLₗ (mu • q i) := isSoS_smul_idem (le_of_lt hmupos) (hfam.idem i) + have hwc : IsSoS mulLₗ (c - mu • q i) := by + rw [hcq, sum_smul_sub_smul q lam i mu] + refine isSoS_sum_smul_idem hfam.idem fun j hj => ?_ + by_cases hji : j = i + · rw [if_pos hji]; linarith + · rw [if_neg hji]; exact hlo j hj + have hwe : IsSoS mulLₗ (e - c - mu • q i) := by + rw [hec', sum_smul_sub_smul q (fun j => 1 - lam j) i mu] + refine isSoS_sum_smul_idem hfam.idem fun j hj => ?_ + by_cases hji : j = i + · rw [if_pos hji] + show (0 : ℝ) ≤ 1 - lam i - mu + linarith + · rw [if_neg hji] + show (0 : ℝ) ≤ 1 - lam j + linarith [hhi j hj] + have hzero := hsharp (mu • q i) hwx hwc hwe + rcases smul_eq_zero.mp hzero with hz | hz + · exact absurd hz (ne_of_gt hmupos) + · exact hi hz + have hsq : c * c = ∑ i, (lam i * lam i) • q i := sq_of_orthIdem hfam hcq + rw [hsq, hcq] + refine Finset.sum_congr rfl fun i _ => ?_ + by_cases hi : q i = 0 + · rw [hi, smul_zero, smul_zero] + · rcases hbin i hi with hb | hb <;> rw [hb] <;> norm_num + +/-- **Idempotents are exactly the sharp elements.** -/ +theorem isSharp_iff_idem {e c : J} (he : ∀ y : J, e * y = y) : IsSharp e c ↔ c * c = c := + ⟨idem_of_isSharp he, isSharp_of_idem he⟩ + +/-! ## The automorphism -/ + +variable {e : J} + +/-- **A unital linear order isomorphism preserves idempotents.** + +Sharpness is a statement about the cone and the unit, both of which `Φ` preserves; the `∀` clause is +transported through `Φ.symm`, which is where the *reflecting* half of `horder` is spent. -/ +theorem map_idem_of_orderIso (he : ∀ y : J, e * y = y) (Φ : J ≃ₗ[ℝ] J) (hunital : Φ e = e) + (horder : ∀ x : J, IsSoS mulLₗ x ↔ IsSoS mulLₗ (Φ x)) {c : J} (hc : c * c = c) : + Φ c * Φ c = Φ c := by + refine idem_of_isSharp he ?_ + obtain ⟨h1, h2, h3⟩ := isSharp_of_idem he hc + refine ⟨(horder c).mp h1, ?_, ?_⟩ + · have hstep := (horder (e - c)).mp h2 + rwa [map_sub, hunital] at hstep + · intro x hx hcx hecx + have hw : Φ.symm x = 0 := by + refine h3 (Φ.symm x) ((horder _).mpr ?_) ((horder _).mpr ?_) ((horder _).mpr ?_) + · rwa [Φ.apply_symm_apply] + · rwa [map_sub, Φ.apply_symm_apply] + · rwa [map_sub, map_sub, hunital, Φ.apply_symm_apply] + rw [← Φ.apply_symm_apply x, hw, map_zero] + +/-- **A unital linear order isomorphism preserves squares.** + +The image of a complete orthogonal family is a family of idempotents summing to the unit, hence +pairwise orthogonal by `orth_of_sum_eq_unit`; squaring is then coefficientwise on both sides. -/ +theorem map_sq_of_orderIso (he : ∀ y : J, e * y = y) (Φ : J ≃ₗ[ℝ] J) (hunital : Φ e = e) + (horder : ∀ x : J, IsSoS mulLₗ x ↔ IsSoS mulLₗ (Φ x)) (x : J) : + Φ (x * x) = Φ x * Φ x := by + obtain ⟨n, q, lam, hfam, hsum, hx⟩ := spectral_resolution_complete e he x + have hidem' : ∀ i, Φ (q i) * Φ (q i) = Φ (q i) := fun i => + map_idem_of_orderIso he Φ hunital horder (hfam.idem i) + have hsum' : (∑ i, Φ (q i)) = e := by rw [← map_sum, hsum, hunital] + have hfam' : IsOrthIdemFamily (fun i => Φ (q i)) := + ⟨hidem', orth_of_sum_eq_unit he hidem' hsum'⟩ + have hΦx : Φ x = ∑ i, lam i • Φ (q i) := by + rw [hx, map_sum]; simp only [map_smul] + rw [sq_of_orthIdem hfam' hΦx, sq_of_orthIdem hfam hx, map_sum] + simp only [map_smul] + +/-- **Koecher / Alfsen–Shultz.** A unital linear order isomorphism of a finite-dimensional formally +real Jordan algebra is a Jordan automorphism. + +Polarisation: `2 (x ∘ y) = (x + y)² − x² − y²`, and `Φ` is linear, so preservation of squares is +preservation of the product once the `2` is cancelled. -/ +theorem map_jordan_of_orderIso (he : ∀ y : J, e * y = y) (Φ : J ≃ₗ[ℝ] J) (hunital : Φ e = e) + (horder : ∀ z : J, IsSoS mulLₗ z ↔ IsSoS mulLₗ (Φ z)) (x y : J) : + Φ (x * y) = Φ x * Φ y := by + have hxy := map_sq_of_orderIso he Φ hunital horder (x + y) + have hxx := map_sq_of_orderIso he Φ hunital horder x + have hyy := map_sq_of_orderIso he Φ hunital horder y + have hl : (x + y) * (x + y) = x * x + (x * y + x * y) + y * y := by + rw [add_mul, mul_add, mul_add, mul_comm y x]; abel + have hr : (Φ x + Φ y) * (Φ x + Φ y) + = Φ x * Φ x + (Φ x * Φ y + Φ x * Φ y) + Φ y * Φ y := by + rw [add_mul, mul_add, mul_add, mul_comm (Φ y) (Φ x)]; abel + rw [hl, map_add Φ x y, hr, map_add, map_add, map_add, hxx, hyy] at hxy + have h4 : Φ (x * y) + Φ (x * y) = Φ x * Φ y + Φ x * Φ y := + add_left_cancel (add_right_cancel hxy) + have h5 : (2 : ℕ) • (Φ (x * y) - Φ x * Φ y) = 0 := by + rw [two_nsmul] + linear_combination (norm := abel) h4 + exact sub_eq_zero.mp (nsmul_eq_zero_iff' (by norm_num) h5) + +end Abstract + +/-! ## The theorem in bilinear-map vocabulary + +The crossing `EuclideanJordan/Bridge.lean` was built for: the *statement* mentions only the bundled bilinear +map, so no ring instance has to exist before it elaborates; only the proof needs one. -/ + +section Interface + +variable {J : Type*} [NormedAddCommGroup J] [Module ℝ J] [Module.Finite ℝ J] + +/-- **Koecher / Alfsen–Shultz, in bilinear-map vocabulary.** The Jordan product as a bundled +bilinear map, the Jordan identity and formal reality as hypotheses in that vocabulary, and the cone +as `EuclideanJordan/Order.lean`'s `IsSoS`. + +★ No inner product appears. The ambient structure is a normed additive group carrying an +`ℝ`-module structure — `NormedAddCommGroup` only because `EuclideanJordan/Bridge.lean`'s `ringOfBilinear` is +stated over one — and the norm is never used. -/ +theorem orderIso_preservesJordan (m : J →ₗ[ℝ] J →ₗ[ℝ] J) + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (e : J) (he : ∀ y : J, m e y = y) + (Φ : J ≃ₗ[ℝ] J) (hunital : Φ e = e) + (horder : ∀ x : J, IsSoS m x ↔ IsSoS m (Φ x)) (x y : J) : + Φ (m x y) = m (Φ x) (Φ y) := by + letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + letI : IsCommJordan J := ⟨hjordan⟩ + letI : IsScalarTower ℝ J J := ⟨fun r a b => smul_bilinear m r a b⟩ + letI : IsFormallyReal J := isFormallyReal_of_fin m hcomm hfr + have hm : (mulLₗ : J →ₗ[ℝ] J →ₗ[ℝ] J) = m := by ext a b; rfl + rw [hm] at * + exact map_jordan_of_orderIso he Φ hunital horder x y + +end Interface + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean b/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean new file mode 100644 index 0000000000..cf18f00241 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean @@ -0,0 +1,309 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import Mathlib.Data.Real.Basic +import Mathlib.Analysis.Normed.Module.Basic +import Mathlib.Algebra.Order.Group.Defs +import Mathlib.Tactic.Linarith + + +/-! +# Order Unit Spaces + +An **order unit space** (V, V⁺, 1) is a real ordered vector space with a distinguished +order unit. Two honesty notes on this encoding: the domination field below is the +*order-unit* boundedness property (`a ≤ r • 1`), not the full Archimedean condition; +and the carried `NormedAddCommGroup`/`NormedSpace` structure is an independent normed +structure, not derived here as the order-unit norm. + +## Main definitions + +* `OrderUnitSpace` — typeclass for ordered real vector spaces with an order unit + (order-unit boundedness) +* `OrderUnitSpace.IsEffect` — predicate for the effect space [0, 1]_V + +## References + +* Alfsen-Shultz, Geometry of State Spaces of Operator Algebras +* van de Wetering, arXiv:1803.11139 +-/ + +noncomputable section + +/-- An order unit space is a real vector space with a partial order compatible + with addition and a distinguished order unit (order-unit boundedness; see the + `archimedean` field note). -/ +class OrderUnitSpace (V : Type*) extends + NormedAddCommGroup V, + NormedSpace ℝ V, + PartialOrder V where + /-- Addition respects order on the left. -/ + add_le_add_left : ∀ (a b : V), a ≤ b → ∀ c, c + a ≤ c + b + /-- The order unit. -/ + ousUnit : V + /-- Nonneg scalar mult is monotone. -/ + smul_nonneg_mono : ∀ (r : ℝ), 0 ≤ r → ∀ {a b : V}, a ≤ b → r • a ≤ r • b + /-- The order unit is positive. -/ + ousUnit_nonneg : (0 : V) ≤ ousUnit + /-- **Order-unit boundedness** (the order-unit axiom): every element is dominated + by some nonnegative scalar multiple of the unit, `a ≤ r • 1`. This is the + order-unit property, weaker than the full Archimedean condition; the field name + is retained for continuity. -/ + archimedean : ∀ a : V, ∃ r : ℝ, 0 ≤ r ∧ a ≤ r • ousUnit + +namespace OrderUnitSpace + +variable {V : Type*} [OrderUnitSpace V] + +scoped notation "𝟙" => OrderUnitSpace.ousUnit (V := _) + +-- Ordered group lemmas derived from add_le_add_left + +theorem add_le_add_right' {a b : V} (h : a ≤ b) (c : V) : a + c ≤ b + c := by + rw [add_comm a c, add_comm b c] + exact add_le_add_left a b h c + +theorem neg_le_neg {a b : V} (h : a ≤ b) : -b ≤ -a := by + have h1 := add_le_add_right' h (-b) + rw [add_neg_cancel] at h1 + have h2 := add_le_add_right' h1 (-a) + simp [add_assoc, add_neg_cancel, zero_add, add_zero] at h2 + exact h2 + +theorem neg_nonneg_of_nonpos {a : V} (h : a ≤ 0) : (0 : V) ≤ -a := by + have := neg_le_neg h + simp at this + exact this + +theorem neg_nonpos_of_nonneg {a : V} (h : (0 : V) ≤ a) : -a ≤ 0 := by + have := neg_le_neg h + simp at this + exact this + +theorem sub_nonneg_of_le {a b : V} (h : a ≤ b) : (0 : V) ≤ b - a := by + have h1 := add_le_add_right' h (-a) + rw [add_neg_cancel] at h1 + rwa [sub_eq_add_neg] + +theorem sub_le_self_of_nonneg {b : V} {a : V} (h : (0 : V) ≤ a) : b - a ≤ b := by + rw [sub_eq_add_neg] + have h1 : -a ≤ 0 := neg_nonpos_of_nonneg h + have h2 := add_le_add_left (-a) (0 : V) h1 b + rwa [add_zero] at h2 + +theorem le_add_of_nonneg_right {a b : V} (h : (0 : V) ≤ b) : a ≤ a + b := by + have h1 := add_le_add_left (0 : V) b h a + rw [add_zero] at h1 + exact h1 + +-- Effect space + +/-- An effect is an element `a` with `0 ≤ a ≤ 𝟙`. -/ +def IsEffect (a : V) : Prop := (0 : V) ≤ a ∧ a ≤ 𝟙 + +/-- The converse of `sub_nonneg_of_le`: a nonnegative difference gives an inequality. -/ +theorem le_of_sub_nonneg {a b : V} (h : (0 : V) ≤ b - a) : a ≤ b := by + have h1 := add_le_add_left 0 (b - a) h a + rw [add_zero, add_sub_cancel] at h1 + exact h1 + +/-- Subtracting a fixed element preserves order. -/ +theorem sub_le_sub_right' {a b : V} (h : a ≤ b) (c : V) : a - c ≤ b - c := by + refine le_of_sub_nonneg ?_ + have hrw : (b - c) - (a - c) = b - a := by abel + rw [hrw] + exact sub_nonneg_of_le h + +/-- Subtracting from a fixed element reverses order. -/ +theorem sub_le_sub_left' {a b : V} (h : a ≤ b) (c : V) : c - b ≤ c - a := by + refine le_of_sub_nonneg ?_ + have hrw : (c - a) - (c - b) = b - a := by abel + rw [hrw] + exact sub_nonneg_of_le h + +/-- A nonpositive difference gives an inequality. -/ +theorem le_of_sub_nonpos {a b : V} (h : a - b ≤ 0) : a ≤ b := by + refine le_of_sub_nonneg ?_ + have h1 : (0 : V) ≤ -(a - b) := neg_nonneg_of_nonpos h + have hrw : -(a - b) = b - a := by abel + rwa [hrw] at h1 + +/-- **Monotonicity in the SCALAR**: on a nonnegative element, a larger scalar gives a +larger multiple. (`smul_nonneg_mono` is monotonicity in the *element*; this is the +companion the class was missing, and the direct-sum carrier's order-unit bound needs it +to compare against `max r₁ r₂`.) -/ +theorem smul_le_smul_of_le_of_nonneg {r s : ℝ} (hrs : r ≤ s) {a : V} + (ha : (0 : V) ≤ a) : r • a ≤ s • a := by + apply le_of_sub_nonneg + rw [← sub_smul] + have h := smul_nonneg_mono (s - r) (by linarith) ha + rwa [smul_zero] at h + +theorem isEffect_zero : IsEffect (0 : V) := + ⟨le_refl 0, ousUnit_nonneg⟩ + +theorem isEffect_unit : IsEffect (𝟙 : V) := + ⟨ousUnit_nonneg, le_refl 𝟙⟩ + +/-- The orthocomplement `𝟙 - a` of an effect is an effect. -/ +theorem IsEffect.ortho {a : V} (h : IsEffect a) : + IsEffect (𝟙 - a) := + ⟨sub_nonneg_of_le h.2, sub_le_self_of_nonneg h.1⟩ + +/-- Two effects are orthogonal if `a + b ≤ 𝟙`. -/ +def AreOrthogonal (a b : V) : Prop := a + b ≤ 𝟙 + +/-- A sharp effect (projective unit) is an effect `p` with no nonzero + effect below both `p` and `𝟙 - p`. -/ +def IsSharp (p : V) : Prop := + IsEffect p ∧ ∀ (a : V), IsEffect a → a ≤ p → a ≤ 𝟙 - p → a = 0 + +/-- Sum of nonneg elements is nonneg. -/ +theorem add_nonneg {a b : V} (ha : (0 : V) ≤ a) (hb : (0 : V) ≤ b) : + (0 : V) ≤ a + b := + le_trans ha (le_add_of_nonneg_right hb) + +/-- Addition of effects whose sum is bounded. -/ +theorem IsEffect.add_of_le_unit {a b : V} (ha : IsEffect a) (hb : IsEffect b) + (hab : a + b ≤ 𝟙) : IsEffect (a + b) := + ⟨add_nonneg ha.1 hb.1, hab⟩ + +/-! ## The effects span, and linear maps are determined on them + +The textbook route proves the spanning property through the order-unit *norm* — if `‖v‖ ≤ ½` +then `0 ≤ ½𝟙 + v ≤ 𝟙`, so the effects contain a ball about `½𝟙`. That route needs the +carried norm to *be* the order-unit norm, which this class deliberately does not +assert (see the class docstring), and needs the Archimedean property proper rather than +the order-unit boundedness the `archimedean` field carries. + +The two conclusions — that the effects span and that linear maps agreeing on effects are +equal — do not need either. They follow from order-unit boundedness alone, which is what is +proved here: strictly more general than the norm route, and available at exactly this class's +own strength. The ball clause itself is *not* formalized; it is the textbook route, not its +content. -/ + +/-- Nonnegative scalars preserve nonnegativity. -/ +theorem smul_nonneg' {r : ℝ} (hr : 0 ≤ r) {a : V} (ha : (0 : V) ≤ a) : + (0 : V) ≤ r • a := by + have h := smul_nonneg_mono r hr ha + rwa [smul_zero] at h + +/-- A scalar in `[0,1]` times an effect is an effect. -/ +theorem isEffect_smul {r : ℝ} (hr0 : 0 ≤ r) (hr1 : r ≤ 1) {a : V} (ha : IsEffect a) : + IsEffect (r • a) := by + refine ⟨smul_nonneg' hr0 ha.1, ?_⟩ + calc r • a ≤ (1 : ℝ) • a := smul_le_smul_of_le_of_nonneg hr1 ha.1 + _ = a := one_smul ℝ a + _ ≤ 𝟙 := ha.2 + +/-- A scalar in `[0,1]` times the unit is an effect. -/ +theorem isEffect_smul_unit {r : ℝ} (hr0 : 0 ≤ r) (hr1 : r ≤ 1) : + IsEffect (r • (𝟙 : V)) := + isEffect_smul hr0 hr1 isEffect_unit + +/-- **The spanning clause.** The effects span the whole space. Proof from +order-unit boundedness only: bound `x` above by `r • 𝟙` and `-x` above by `s • 𝟙`, so +that `x + s • 𝟙` is nonnegative and below `(r+s) • 𝟙`; rescaling by +`c = r + s + 1 > 0` lands it in the effects, and `x = c • (that) - s • 𝟙` with `𝟙` +itself an effect. -/ +theorem span_isEffect_eq_top : + Submodule.span ℝ {a : V | IsEffect a} = ⊤ := by + rw [eq_top_iff] + intro x _ + obtain ⟨r, hr0, hr⟩ := archimedean x + obtain ⟨s, hs0, hs⟩ := archimedean (-x) + set c : ℝ := r + s + 1 with hc + have hcpos : (0 : ℝ) < c := by positivity + -- `x + s • 𝟙` is nonnegative + have hxs_nonneg : (0 : V) ≤ x + s • 𝟙 := by + have h := add_le_add_left (-x) (s • 𝟙) hs x + rwa [add_neg_cancel] at h + -- and bounded by `c • 𝟙` + have hxs_le : x + s • 𝟙 ≤ c • (𝟙 : V) := by + calc x + s • 𝟙 ≤ r • 𝟙 + s • 𝟙 := add_le_add_right' hr _ + _ = (r + s) • (𝟙 : V) := (add_smul r s 𝟙).symm + _ ≤ c • (𝟙 : V) := + smul_le_smul_of_le_of_nonneg (by rw [hc]; linarith) ousUnit_nonneg + -- so its `c`-rescaling is an effect + have hy : IsEffect (c⁻¹ • (x + s • 𝟙)) := by + refine ⟨smul_nonneg' (le_of_lt (inv_pos.mpr hcpos)) hxs_nonneg, ?_⟩ + have h := smul_nonneg_mono c⁻¹ (le_of_lt (inv_pos.mpr hcpos)) hxs_le + rwa [smul_smul, inv_mul_cancel₀ (ne_of_gt hcpos), one_smul] at h + -- and `x` is a combination of it and the unit + have hx : x = c • (c⁻¹ • (x + s • 𝟙)) - s • 𝟙 := by + rw [smul_smul, mul_inv_cancel₀ (ne_of_gt hcpos), one_smul] + abel + rw [hx] + exact Submodule.sub_mem _ + (Submodule.smul_mem _ _ (Submodule.subset_span hy)) + (Submodule.smul_mem _ _ (Submodule.subset_span isEffect_unit)) + +/-- **The extensionality clause.** Two linear maps agreeing on the effects are +equal. This is what the spanning property is normally put to use for. -/ +theorem linearMap_eq_of_eq_on_effects {W : Type*} [AddCommGroup W] [Module ℝ W] + (f g : V →ₗ[ℝ] W) (h : ∀ a : V, IsEffect a → f a = g a) : f = g := + LinearMap.ext_on span_isEffect_eq_top (fun a ha => h a ha) + +/-- **The Archimedean property**, in the sense the standard definition of an order unit space +carries and this class does not. + +The class's `archimedean` field is order-unit *boundedness* only — every element is under +*some* multiple of the unit. This is the genuine Archimedean squeeze: an element under +*every* positive multiple of the unit is nonpositive. The two are different, and the +difference is what a homogeneity argument consumes at its last step. + +★ It is supplied as an explicit `Prop` rather than added to the class, so that any theorem +needing it says so in its own statement. Nothing is given up by that: the Archimedean property +*is* part of the standard definition of an order unit space — not a stand-in for a cited +result — so a theorem proved under it is proved at the standard generality. -/ +def IsArchimedean (V : Type*) [OrderUnitSpace V] : Prop := + ∀ x : V, (∀ ε : ℝ, 0 < ε → x ≤ ε • ousUnit) → x ≤ 0 + +/-- The textbook Archimedean order-unit condition in its `ℕ` form (Alfsen–Shultz, +Paulsen–Tomforde): `n • x ≤ 𝟙` for every positive `n` forces `x ≤ 0`. -/ +def IsArchNat (V : Type*) [OrderUnitSpace V] : Prop := + ∀ x : V, (∀ n : ℕ, 0 < n → (n : ℝ) • x ≤ (𝟙 : V)) → x ≤ 0 + +theorem archNat_of_arch {V : Type*} [OrderUnitSpace V] (h : IsArchimedean V) : + IsArchNat V := by + intro x hx + refine h x ?_ + intro ε hε + obtain ⟨n, hn⟩ := exists_nat_gt (1 / ε) + have hnpos : (0 : ℝ) < (n : ℝ) := lt_trans (by positivity) hn + have hn0 : 0 < n := by exact_mod_cast hnpos + have h2 := smul_nonneg_mono ((n : ℝ))⁻¹ (by positivity) (hx n hn0) + rw [smul_smul, inv_mul_cancel₀ (ne_of_gt hnpos), one_smul] at h2 + have h3 : ((n : ℝ))⁻¹ ≤ ε := by + rw [div_lt_iff₀ hε] at hn + rw [inv_eq_one_div, div_le_iff₀ hnpos] + nlinarith + exact le_trans h2 (smul_le_smul_of_le_of_nonneg h3 ousUnit_nonneg) + +theorem arch_of_archNat {V : Type*} [OrderUnitSpace V] (h : IsArchNat V) : + IsArchimedean V := by + intro x hx + refine h x ?_ + intro n hn0 + have hnpos : (0 : ℝ) < (n : ℝ) := by exact_mod_cast hn0 + have h2 := smul_nonneg_mono ((n : ℝ)) (le_of_lt hnpos) (hx ((n : ℝ))⁻¹ (by positivity)) + rwa [smul_smul, mul_inv_cancel₀ (ne_of_gt hnpos), one_smul] at h2 + +/-- **`IsArchimedean` is exactly the textbook notion — neither stronger nor weaker.** + +This matters for honesty, not for any proof. A theorem proved under `IsArchimedean` is claimed +to hold at the standard generality of an order unit space, on the grounds that the Archimedean +property is *part of the standard definition* rather than a stand-in for a cited result. That +defence is only as good as the claim that this `Prop` is the standard condition — so here it +is, machine-checked in both directions against the `ℕ` form. -/ +theorem arch_iff {V : Type*} [OrderUnitSpace V] : IsArchimedean V ↔ IsArchNat V := + ⟨archNat_of_arch, arch_of_archNat⟩ + +end OrderUnitSpace diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean new file mode 100644 index 0000000000..23b4938c4e --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean @@ -0,0 +1,95 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul + + +/-! +# Orthogonal idempotents, and the simultaneous-diagonalisation field + +This file derives, at the generality of an arbitrary idempotent, the Faraut–Korányi +simultaneous-diagonalisation fact: + +> an element **scalar on `range q`** and an element of **`J₂(q)`** operator-commute. + +Here `q` is any idempotent `c`, "scalar on `range c`" is `a = μ • c + a₀` with `a₀ ∈ J₀(c)`, +and `J₂(c)` is `J₁(c)` in the eigenvalue naming of `EuclideanJordan/Peirce.lean`. The proof is four +lines, because `EuclideanJordan/PeirceMul.lean` already did the work: `L_c` commutes with `L_b` for +`b ∈ J₁(c)`, and `L_{a₀}` commutes with `L_b` for `a₀ ∈ J₀(c)`, so `L_a = μ L_c + L_{a₀}` +commutes with `L_b` by linearity. + +The interface's `q` is a *rank-two* idempotent `pᵢ + pⱼ` built from a Jordan frame, so +`add_idem_of_orthogonal` below supplies the shape: a sum of two orthogonal idempotents is an +idempotent, and then the general result applies. + +## Scope + +The frame-level version of the statement quantifies over a rank-two `q = pᵢ + pⱼ` built from a +Jordan frame; `add_idem_of_orthogonal` below supplies the shape that reduces it to the general +result — a sum of two orthogonal idempotents is an idempotent — +and `EuclideanJordan/Frame.lean` assembles it (`opCommute_scalarOn_frame`). + +## References + +* Faraut and Korányi, *Analysis on Symmetric Cones*, Ch. IV. +-/ + +namespace EuclideanJordan + +section Orthogonal + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + +omit [IsCommJordan J] [Module ℝ J] in +/-- **A sum of two orthogonal idempotents is an idempotent.** Pure expansion — this needs +only commutativity and distributivity, not the Jordan identity. -/ +theorem add_idem_of_orthogonal {p q : J} (hp : p * p = p) (hq : q * q = q) (hpq : p * q = 0) : + (p + q) * (p + q) = p + q := by + have hqp : q * p = 0 := by rw [mul_comm]; exact hpq + rw [add_mul, mul_add, mul_add, hp, hq, hpq, hqp] + abel + +/-- **Orthogonal idempotents operator-commute.** Not merely `p ∘ q = 0`: the multiplication +operators themselves commute, which is what every simultaneous-diagonalisation argument +needs. Immediate from `opCommute_eigen_one_zero`, since `p ∈ J₁(p)` and `q ∈ J₀(p)`. -/ +theorem opCommute_of_orthogonal {p q : J} (hp : p * p = p) (hpq : p * q = 0) (w : J) : + p * (q * w) = q * (p * w) := + opCommute_eigen_one_zero hp hp hpq w + +section ScalarTower + +variable [IsScalarTower ℝ J J] + +/-- **Faraut–Korányi simultaneous diagonalisation, at single-idempotent generality.** + +If `a` is scalar on `range c` — that is, `a = μ • c + a₀` with `a₀` in the `0`-Peirce +component — and `b` lies in the `1`-Peirce component `J₂(c)`, then `L_a` and `L_b` commute. + +It is a consequence of the Jordan identity alone. -/ +theorem opCommute_scalarOn {c a a₀ b : J} {μ : ℝ} (hc : c * c = c) + (ha : a = μ • c + a₀) (ha₀ : c * a₀ = 0) (hb : c * b = b) (w : J) : + a * (b * w) = b * (a * w) := by + have hcb : c * (b * w) = b * (c * w) := mul_comm_of_eigen_one hc hb w + have h0 : a₀ * (b * w) = b * (a₀ * w) := (opCommute_eigen_one_zero hc hb ha₀ w).symm + subst ha + rw [add_mul, add_mul, mul_add, smul_mul_assoc, smul_mul_assoc, hcb, h0, mul_smul_comm'] + +/-- The interface's actual shape: `c` is the rank-two idempotent `p + q` built from two +orthogonal idempotents of a Jordan frame. -/ +theorem opCommute_scalarOn_pair {p q a a₀ b : J} {μ : ℝ} (hp : p * p = p) (hq : q * q = q) + (hpq : p * q = 0) (ha : a = μ • (p + q) + a₀) (ha₀ : (p + q) * a₀ = 0) + (hb : (p + q) * b = b) (w : J) : a * (b * w) = b * (a * w) := + opCommute_scalarOn (add_idem_of_orthogonal hp hq hpq) ha ha₀ hb w + +end ScalarTower + +end Orthogonal + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean new file mode 100644 index 0000000000..6d62edb06f --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Block + + +/-! +# The Faraut–Korányi block pattern, forced by three constraints + +`EuclideanJordan/Block.lean` characterises the rank-two block `J₁(p+q)`. This file does the frame-level +analogue, but as a **constraint on eigenvalue patterns** rather than as an assembled direct +sum — which is both what is provable here and what the FK block structure actually *is*. + +Let `p` be an orthogonal idempotent family and `x ≠ 0` a joint eigenvector, +`pᵢ ∘ x = μᵢ • x`, lying in `J₁(∑ pᵢ)`. Three facts: + +* `sum_eigen_eq_one` — **`∑ μᵢ = 1`**; +* `eigen_pattern_mem` — **each `μᵢ ∈ {0, 1/2, 1}`** (the trichotomy, applied at each `pᵢ`); +* `eigen_pattern_card_le_two` — **at most two `μᵢ` are nonzero**, since every nonzero one is + at least `1/2` and they sum to `1`. + +Together these force the Faraut–Korányi pattern: **either exactly one `μᵢ` is `1` — the +diagonal block `J_ii` — or exactly two are `1/2` — the coherence block `J_ij`.** The +arithmetic is immediate from the three: zero nonzero entries give sum `0 ≠ 1`; one gives a +single entry equal to `1`, so it is `1` and not `1/2`; two give `a + b = 1` with +`a, b ∈ {1/2, 1}`, whose only solution is `1/2 + 1/2`. + +★ **That packaged statement is deliberately NOT a theorem here.** Formalising it means +extracting the elements of a `Finset` of cardinality `≤ 2` and case-splitting, which is +bookkeeping with no mathematical content, and stating it without proving it is exactly the +defect this arc spent four audit rounds removing. The three constraints are proved; the +one-line consequence is written out above so a reader can check it rather than trust it. + +★ **What is still not built:** the assembled direct sum `J = ⊕_{i ≤ j} J_{ij}`. These +constraints say what the summands can be, not that every element decomposes into them. +-/ + +namespace EuclideanJordan + +open Finset + +section Pattern + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] +variable {ι : Type*} [Fintype ι] [DecidableEq ι] {p : ι → J} {x : J} {mu : ι → ℝ} + +omit [IsCommJordan J] [IsScalarTower ℝ J J] [DecidableEq ι] in +/-- **The eigenvalue pattern of a joint eigenvector in the block sums to 1.** -/ +theorem sum_eigen_eq_one (hx : x ≠ 0) (hs : (∑ i, p i) * x = x) + (hmu : ∀ i, p i * x = mu i • x) : ∑ i, mu i = 1 := by + have hexp : (∑ i, p i) * x = (∑ i, mu i) • x := by + rw [Finset.sum_mul, Finset.sum_smul] + exact Finset.sum_congr rfl fun i _ => hmu i + have hcalc : (∑ i, mu i) • x = x := by rw [← hexp, hs] + have hz : ((∑ i, mu i) - 1) • x = 0 := by rw [sub_smul, hcalc, one_smul, sub_self] + rcases smul_eq_zero.mp hz with h | h + · linarith [sub_eq_zero.mp h] + · exact absurd h hx + +omit [Fintype ι] [DecidableEq ι] in +/-- **Each entry of the pattern is `0`, `1/2` or `1`.** -/ +theorem eigen_pattern_mem (hp : IsOrthIdemFamily p) (hx : x ≠ 0) + (hmu : ∀ i, p i * x = mu i • x) (i : ι) : mu i = 0 ∨ mu i = (2 : ℝ)⁻¹ ∨ mu i = 1 := + eigenvalue_trichotomy (hp.idem i) hx (hmu i) + +/-- **At most two frame idempotents see a given joint eigenvector.** -/ +theorem eigen_pattern_card_le_two (hp : IsOrthIdemFamily p) (hx : x ≠ 0) + (hs : (∑ i, p i) * x = x) (hmu : ∀ i, p i * x = mu i • x) : + ({i | mu i ≠ 0} : Finset ι).card ≤ 2 := by + classical + set S : Finset ι := {i | mu i ≠ 0} with hSdef + have hlb : ∀ i ∈ S, (2 : ℝ)⁻¹ ≤ mu i := by + intro i hi + have hne : mu i ≠ 0 := by simpa [hSdef] using hi + rcases eigen_pattern_mem hp hx hmu i with h | h | h + · exact absurd h hne + · rw [h] + · rw [h]; norm_num + have hsumS : ∑ i ∈ S, mu i = 1 := by + have hsub : ∑ i ∈ S, mu i = ∑ i, mu i := by + refine Finset.sum_subset (Finset.subset_univ S) ?_ + intro i _ hi + simpa [hSdef] using hi + rw [hsub, sum_eigen_eq_one hx hs hmu] + have hcard : (S.card : ℝ) * (2 : ℝ)⁻¹ ≤ ∑ i ∈ S, mu i := by + have h1 : ∑ _i ∈ S, (2 : ℝ)⁻¹ ≤ ∑ i ∈ S, mu i := Finset.sum_le_sum hlb + simpa [Finset.sum_const, nsmul_eq_mul] using h1 + rw [hsumS] at hcard + have : (S.card : ℝ) ≤ 2 := by linarith + exact_mod_cast this + +end Pattern + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean new file mode 100644 index 0000000000..95a7e9383e --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean @@ -0,0 +1,345 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import Mathlib.Algebra.Jordan.Basic +import Mathlib.Analysis.Normed.Module.Basic +import Mathlib.Data.Real.Basic +import Mathlib.Tactic.LinearCombination + + +/-! +# The Peirce decomposition at a single idempotent + +It is natural to expect the Peirce decomposition to depend on the spectral theorem. **It does +not, in the direction that matters, and this file is the evidence.** +The Peirce decomposition at a *given* idempotent needs the Jordan identity and the +invertibility of `2`, and nothing else: no spectral theorem, no formal reality, no finite +dimension, no inner product, not even a unit. +★ The first draft of this sentence said "the Jordan identity and nothing else", which is +wrong — `peirce_poly` divides by `2` (`two_smul_eq_zero'`), which is why every statement below +it carries `Module ℝ J`. Only the *linearised identities* `two_lin1_raw`/`two_lin1_apply` are +genuinely torsion-free, and they are, deliberately: their factor of `2` is carried in the +statement rather than cancelled. Caught 2026-08-12 by reading the `omit` lines against this +paragraph. What the spectral theorem is needed for is *producing* idempotents — a Jordan frame — not for +decomposing at one that is already in hand. `EuclideanJordan/FrameExists.lean` does the +producing; this file does the decomposing. + +## The mathematics + +For an idempotent `c`, the multiplication operator `L_c : y ↦ c ∘ y` satisfies + + `2·L_c³ − 3·L_c² + L_c = 0`, i.e. `L_c (L_c − 1) (2L_c − 1) = 0`, + +so its only possible eigenvalues are `0`, `1/2`, `1`, and the three Lagrange interpolants +at those roots are projections summing to the identity. That is the Peirce decomposition +`J = J₁(c) ⊕ J_{1/2}(c) ⊕ J₀(c)`. + +The polynomial identity comes from **one** substitution into the linearised Jordan identity. +Writing `⁅·,·⁆` for the commutator of multiplication operators, polarising the Jordan +identity `⁅L_x, L_{x²}⁆ = 0` at `x = a ± b` and subtracting gives + + `⁅L_{a²}, L_b⁆ + 2⁅L_{ab}, L_a⁆ = 0` (`two_lin1_raw`, up to a factor of 2), + +and evaluating that at `a := c`, `b := y`, argument `:= c` collapses immediately to the +Peirce polynomial. Mathlib proves only the `a ↔ b` *symmetrised* consequence +(`two_nsmul_lie_lmul_lmul_add_eq_lie_lmul_lmul_add`), which is strictly weaker; the `a − b` +substitution is what separates the two halves, and it costs one extra line. + +## What is here, and what is not + +Here: the polynomial identity, the three projections, the resolution of the identity, the +three eigenvalue equations, existence and uniqueness of the decomposition, and the +trichotomy (`L_c` has no eigenvalue outside `{0, 1/2, 1}`). + +**Not here:** the Faraut–Korányi *multiplication rules* between Peirce components +(`J_i ∘ J_j ⊆ …`), which are `EuclideanJordan/PeirceMul.lean`, and the decomposition relative +to a whole Jordan frame, which is `EuclideanJordan/FramePeirce.lean`. Nothing in this file +should be read as covering them. + +## References + +Mathlib's Jordan support (`Mathlib/Algebra/Jordan/Basic.lean`, 237 lines) is the classes +`IsJordan` / `IsCommJordan`, five operator-commutation lemmas and two linearised +identities. There is no idempotent theory, no Peirce decomposition and no spectral theory +in Mathlib; we are aware of none in any other proof assistant either, though we have not +searched them systematically. + +* Faraut and Korányi, *Analysis on Symmetric Cones*, Prop. IV.1.1. +* McCrimmon, *A Taste of Jordan Algebras*, §II.8. +-/ + +namespace EuclideanJordan + +local notation "L" => AddMonoid.End.mulLeft + +/-! Applying an `AddMonoid.End` expression to an element is definitional in every constructor +we use, but Mathlib's corresponding lemmas are phrased for the `AddMonoidHom` coercion and do not +match the `AddMonoid.End` one. Lean 4.28's simp set bridged this on its own; 4.30's does not, so +the four `rfl`s are stated here and passed to `simpa` explicitly. -/ + +private theorem L_apply {J : Type*} [NonUnitalNonAssocSemiring J] (a w : J) : + (AddMonoid.End.mulLeft a) w = a * w := rfl + +private theorem End_add_apply {J : Type*} [NonUnitalNonAssocCommRing J] + (f g : AddMonoid.End J) (w : J) : (f + g) w = f w + g w := rfl + +private theorem End_mul_apply {J : Type*} [NonUnitalNonAssocCommRing J] + (f g : AddMonoid.End J) (w : J) : (f * g) w = f (g w) := rfl + +private theorem End_neg_apply {J : Type*} [NonUnitalNonAssocCommRing J] + (f : AddMonoid.End J) (w : J) : (-f) w = -(f w) := rfl + +section Linearisation + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] + +/-- **Twice the linearised Jordan identity.** Polarising `⁅L_x, L_{x²}⁆ = 0` at `x = a + b` +and at `x = a − b` and subtracting isolates the half that the symmetrised Mathlib version +(`two_nsmul_lie_lmul_lmul_add_eq_lie_lmul_lmul_add`) leaves fused. + +Stated with the factor `2` carried rather than cancelled, so that this lemma needs no +torsion hypothesis and holds over any `NonUnitalNonAssocCommRing`. -/ +theorem two_lin1_raw (a b : J) : + (2 : ℕ) • ⁅L b, L (a * a)⁆ + (4 : ℕ) • ⁅L a, L (a * b)⁆ = 0 := by + -- Mathlib's `LieRing.ofAssociativeRing` is only a `local instance` of its own file, so a ring's + -- commutator carries `Ring.instBracket` but no `LieRing`, and `lie_add`/`add_lie`/`lie_sub`/ + -- `sub_lie` cannot fire on `AddMonoid.End J`. Reinstating it here is what makes the `simp only` + -- below distribute the bracket over the polarised sums. + let _ : LieRing (AddMonoid.End J) := LieRing.ofAssociativeRing + have hs := (commute_lmul_lmul_sq (a + b)).lie_eq + have hd := (commute_lmul_lmul_sq (a - b)).lie_eq + have ha := (commute_lmul_lmul_sq a).lie_eq + have hb := (commute_lmul_lmul_sq b).lie_eq + simp only [add_mul, mul_add, sub_mul, mul_sub, map_add, map_sub, lie_add, add_lie, + lie_sub, sub_lie, mul_comm b a] at hs hd + rw [ha, hb] at hs hd + have h := sub_eq_zero_of_eq (hs.trans hd.symm) + rw [← h] + abel + +/-- `two_lin1_raw` evaluated at an element. -/ +theorem two_lin1_apply (a b w : J) : + (2 : ℕ) • (b * (a * a * w) - a * a * (b * w)) + + (4 : ℕ) • (a * (a * b * w) - a * b * (a * w)) = 0 := by + have h := congrArg (fun f : AddMonoid.End J => f w) (two_lin1_raw a b) + simpa [Ring.lie_def, sub_eq_add_neg, L_apply, End_add_apply, End_mul_apply, + End_neg_apply] using h + +end Linearisation + +section Projections + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [Module ℝ J] [IsScalarTower ℝ J J] + +/-- In a *commutative* algebra the scalar-tower rule already gives the `SMulCommClass` +rule, so only `IsScalarTower` has to be assumed — which is what the concrete carrier +`HermitianMat` supplies (`EuclideanJordan/Vendor/HermitianMat/Jordan.lean`). -/ +theorem mul_smul_comm' (r : ℝ) (a b : J) : a * (r • b) = r • (a * b) := by + rw [mul_comm, smul_mul_assoc, mul_comm] + +/-- The Jordan multiplication operator `L_c : y ↦ c ∘ y`, as an `ℝ`-linear map. -/ +def mulL (c : J) : J →ₗ[ℝ] J where + toFun y := c * y + map_add' := mul_add c + map_smul' r y := mul_smul_comm' r c y + +@[simp] theorem mulL_apply (c y : J) : mulL c y = c * y := rfl + +/-- The Peirce projection onto the `1`-eigenspace of `L_c`: the Lagrange interpolant +`2L² − L`, which is `1` at `1` and `0` at `0` and `1/2`. -/ +def peirceOne (c : J) : J →ₗ[ℝ] J := (2 : ℝ) • (mulL c ∘ₗ mulL c) - mulL c + +/-- The Peirce projection onto the `1/2`-eigenspace of `L_c`: the Lagrange interpolant +`4L − 4L²`. -/ +def peirceHalf (c : J) : J →ₗ[ℝ] J := (4 : ℝ) • mulL c - (4 : ℝ) • (mulL c ∘ₗ mulL c) + +/-- The Peirce projection onto the `0`-eigenspace of `L_c`: the Lagrange interpolant +`1 − 3L + 2L²`. -/ +def peirceZero (c : J) : J →ₗ[ℝ] J := + LinearMap.id - (3 : ℝ) • mulL c + (2 : ℝ) • (mulL c ∘ₗ mulL c) + +@[simp] theorem peirceOne_apply (c y : J) : + peirceOne c y = (2 : ℝ) • (c * (c * y)) - c * y := rfl + +@[simp] theorem peirceHalf_apply (c y : J) : + peirceHalf c y = (4 : ℝ) • (c * y) - (4 : ℝ) • (c * (c * y)) := rfl + +@[simp] theorem peirceZero_apply (c y : J) : + peirceZero c y = y - (3 : ℝ) • (c * y) + (2 : ℝ) • (c * (c * y)) := rfl + +/-- **The resolution of the identity.** The three Lagrange interpolants sum to `1`. + +★ This is pure polynomial arithmetic and holds for **every** `c`, idempotent or not — it is +the Jordan identity that makes the three summands land in the eigenspaces, not the +resolution itself. Keeping the two facts separate is what makes the failure mode visible: +a decomposition into three pieces is worthless without knowing what the pieces are. -/ +theorem peirce_add_add (c y : J) : peirceOne c y + peirceHalf c y + peirceZero c y = y := by + simp only [peirceOne_apply, peirceHalf_apply, peirceZero_apply] + module + +/-! ### How the projections act on the eigenspaces + +These six lemmas are the "already an eigenvector" direction, and — like `peirce_add_add` — +they are polynomial arithmetic that needs no Jordan identity: they say what the Lagrange +interpolants do to something already known to satisfy `c ∘ y = μ • y`. -/ + +/-- On the `1`-eigenspace, `peirceOne` is the identity. -/ +theorem peirceOne_of_eigen {c y : J} (h : c * y = y) : peirceOne c y = y := by + simp only [peirceOne_apply, h] + module + +/-- `peirceOne` kills the `1/2`-eigenspace. -/ +theorem peirceOne_of_eigen_half {c y : J} (h : c * y = (2 : ℝ)⁻¹ • y) : + peirceOne c y = 0 := by + simp only [peirceOne_apply, h, mul_smul_comm'] + module + +/-- `peirceOne` kills the `0`-eigenspace. -/ +theorem peirceOne_of_eigen_zero {c y : J} (h : c * y = 0) : peirceOne c y = 0 := by + simp only [peirceOne_apply, h, mul_zero] + module + +/-- `peirceHalf` kills the `1`-eigenspace. -/ +theorem peirceHalf_of_eigen {c y : J} (h : c * y = y) : peirceHalf c y = 0 := by + simp only [peirceHalf_apply, h] + module + +/-- On the `1/2`-eigenspace, `peirceHalf` is the identity. -/ +theorem peirceHalf_of_eigen_half {c y : J} (h : c * y = (2 : ℝ)⁻¹ • y) : + peirceHalf c y = y := by + simp only [peirceHalf_apply, h, mul_smul_comm'] + module + +/-- `peirceHalf` kills the `0`-eigenspace. -/ +theorem peirceHalf_of_eigen_zero {c y : J} (h : c * y = 0) : peirceHalf c y = 0 := by + simp only [peirceHalf_apply, h, mul_zero] + module + +end Projections + +section Peirce + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +private theorem two_smul_eq_zero' {x : J} (h : (2 : ℕ) • x = 0) : x = 0 := by + have h2 : (2 : ℝ) • x = 0 := by + rw [show ((2 : ℝ)) = ((2 : ℕ) : ℝ) by norm_num, Nat.cast_smul_eq_nsmul] + exact h + simpa using h2 + +omit [IsScalarTower ℝ J J] in +/-- **The Peirce polynomial identity.** For an idempotent `c`, + + `2·L_c³ − 3·L_c² + L_c = 0`, i.e. `L_c (L_c − 1) (2L_c − 1) = 0`. + +Everything else in this file is a consequence. The proof is `two_lin1_apply` at +`a := c`, `b := y`, argument `:= c`, and nothing more: the hypotheses are the Jordan +identity and `c ∘ c = c`. -/ +theorem peirce_poly {c : J} (hc : c * c = c) (y : J) : + (2 : ℕ) • (c * (c * (c * y))) + c * y = (3 : ℕ) • (c * (c * y)) := by + have h := two_lin1_apply c y c + simp only [hc, mul_comm y c, mul_comm (c * y) c] at h + refine sub_eq_zero.mp (two_smul_eq_zero' ?_) + rw [← h] + abel + +omit [IsScalarTower ℝ J J] in +/-- `peirce_poly` solved for the cube, over `ℝ` — the form every consumer below uses. -/ +theorem peirce_cube {c : J} (hc : c * c = c) (y : J) : + c * (c * (c * y)) = (3 / 2 : ℝ) • (c * (c * y)) - (2 : ℝ)⁻¹ • (c * y) := by + have h := peirce_poly hc y + have h2 : (2 : ℝ) • (c * (c * (c * y))) + c * y = (3 : ℝ) • (c * (c * y)) := by + rw [show (2 : ℝ) = ((2 : ℕ) : ℝ) by norm_num, show (3 : ℝ) = ((3 : ℕ) : ℝ) by norm_num, + Nat.cast_smul_eq_nsmul, Nat.cast_smul_eq_nsmul] + exact h + have h3 : (2 : ℝ) • (c * (c * (c * y))) = (3 : ℝ) • (c * (c * y)) - c * y := + eq_sub_of_add_eq h2 + calc c * (c * (c * y)) = (2 : ℝ)⁻¹ • ((2 : ℝ) • (c * (c * (c * y)))) := by module + _ = (2 : ℝ)⁻¹ • ((3 : ℝ) • (c * (c * y)) - c * y) := by rw [h3] + _ = (3 / 2 : ℝ) • (c * (c * y)) - (2 : ℝ)⁻¹ • (c * y) := by module + +/-- The image of `peirceOne c` lies in the `1`-eigenspace of `L_c`. -/ +theorem mul_peirceOne {c : J} (hc : c * c = c) (y : J) : + c * peirceOne c y = peirceOne c y := by + simp only [peirceOne_apply, mul_sub, mul_smul_comm', peirce_cube hc y] + module + +/-- The image of `peirceHalf c` lies in the `1/2`-eigenspace of `L_c`. -/ +theorem mul_peirceHalf {c : J} (hc : c * c = c) (y : J) : + c * peirceHalf c y = (2 : ℝ)⁻¹ • peirceHalf c y := by + simp only [peirceHalf_apply, mul_sub, mul_smul_comm', peirce_cube hc y] + module + +/-- The image of `peirceZero c` lies in the `0`-eigenspace of `L_c`. -/ +theorem mul_peirceZero {c : J} (hc : c * c = c) (y : J) : c * peirceZero c y = 0 := by + simp only [peirceZero_apply, mul_add, mul_sub, mul_smul_comm', peirce_cube hc y] + module + +/-- **The Peirce decomposition, existence half.** Every element of a real commutative +Jordan algebra splits, relative to any idempotent `c`, into a part fixed by `L_c`, a part +halved by it, and a part killed by it. -/ +theorem exists_peirce_decomposition {c : J} (hc : c * c = c) (y : J) : + ∃ y₁ yₕ y₀ : J, c * y₁ = y₁ ∧ c * yₕ = (2 : ℝ)⁻¹ • yₕ ∧ c * y₀ = 0 + ∧ y = y₁ + yₕ + y₀ := + ⟨peirceOne c y, peirceHalf c y, peirceZero c y, mul_peirceOne hc y, mul_peirceHalf hc y, + mul_peirceZero hc y, (peirce_add_add c y).symm⟩ + +omit [IsCommJordan J] in +/-- **The Peirce decomposition, uniqueness half.** A vanishing sum of Peirce components is +componentwise zero — so the decomposition of `exists_peirce_decomposition` is unique, and +`J = J₁(c) ⊕ J_{1/2}(c) ⊕ J₀(c)` is a genuine direct sum. + +The proof needs no independence-of-eigenspaces import: applying the two projections +`peirceOne` and `peirceHalf` to the relation reads off two of the three components, and +the third follows by subtraction. -/ +theorem peirce_eq_zero_of_add_eq_zero {c y₁ yₕ y₀ : J} (h₁ : c * y₁ = y₁) + (hₕ : c * yₕ = (2 : ℝ)⁻¹ • yₕ) (h₀ : c * y₀ = 0) (h : y₁ + yₕ + y₀ = 0) : + y₁ = 0 ∧ yₕ = 0 ∧ y₀ = 0 := by + have e1 : y₁ = 0 := by + have := congrArg (peirceOne c) h + rwa [map_add, map_add, peirceOne_of_eigen h₁, peirceOne_of_eigen_half hₕ, + peirceOne_of_eigen_zero h₀, add_zero, add_zero, map_zero] at this + have eh : yₕ = 0 := by + have := congrArg (peirceHalf c) h + rwa [map_add, map_add, peirceHalf_of_eigen h₁, peirceHalf_of_eigen_half hₕ, + peirceHalf_of_eigen_zero h₀, add_zero, zero_add, map_zero] at this + refine ⟨e1, eh, ?_⟩ + rw [e1, eh, zero_add, zero_add] at h + exact h + +/-- **The eigenvalue trichotomy.** `L_c` has no eigenvalue outside `{0, 1/2, 1}`: the +Peirce polynomial annihilates it, and its roots are exactly those three. + +★ Stated for a *nonzero* eigenvector, which is the whole content — the equation +`c ∘ y = μ • y` is satisfied by `y = 0` for every `μ`. -/ +theorem eigenvalue_trichotomy {c : J} (hc : c * c = c) {y : J} (hy : y ≠ 0) {μ : ℝ} + (h : c * y = μ • y) : μ = 0 ∨ μ = (2 : ℝ)⁻¹ ∨ μ = 1 := by + have hp : peirceOne c y = (2 * μ * μ - μ) • y := by + simp only [peirceOne_apply, h, mul_smul_comm', smul_smul] + module + have key := mul_peirceOne hc y + rw [hp, mul_smul_comm', h, smul_smul] at key + have hs : ((2 * μ * μ - μ) * μ - (2 * μ * μ - μ)) • y = 0 := by + rw [sub_smul, key] + exact sub_self _ + have hz : (2 * μ * μ - μ) * μ - (2 * μ * μ - μ) = 0 := + (smul_eq_zero.mp hs).resolve_right hy + have hfac : μ * (2 * μ - 1) * (μ - 1) = 0 := by linear_combination hz + rcases mul_eq_zero.mp hfac with h' | h' + · rcases mul_eq_zero.mp h' with h'' | h'' + · exact Or.inl h'' + · exact Or.inr (Or.inl (by linarith)) + · exact Or.inr (Or.inr (by linarith)) + +end Peirce + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean new file mode 100644 index 0000000000..ffbbcbb7a2 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean @@ -0,0 +1,240 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Peirce + + +/-! +# The Peirce multiplication rules + +`EuclideanJordan/Peirce.lean` decomposes `J = J₁(c) ⊕ J_{1/2}(c) ⊕ J₀(c)` for an idempotent `c`. This +file proves how the three components multiply — the Faraut–Korányi relations: + +| | `J₁` | `J_{1/2}` | `J₀` | +| --- | --- | --- | --- | +| **`J₁`** | `⊆ J₁` | `⊆ J_{1/2}` | `= 0` | +| **`J_{1/2}`** | `⊆ J_{1/2}` | `⊆ J₁ ⊕ J₀` | `⊆ J_{1/2}` | +| **`J₀`** | `= 0` | `⊆ J_{1/2}` | `⊆ J₀` | + +As in `EuclideanJordan/Peirce.lean`, the hypotheses are the Jordan identity and the invertibility of the +integers used (`2` for the commuting rules, `4` for the half-half rule): no spectral theorem, +no formal reality, no finite dimension, no unit. +## The two ingredients + +`EuclideanJordan/Peirce.lean` needed only the *once*-linearised Jordan identity `two_lin1_raw`. Five of +the six rules follow from a single consequence of it — **`L_x` commutes with `L_c` whenever +`x` lies in `J₁(c)` or `J₀(c)`** (`mul_comm_of_eigen_one`, `mul_comm_of_eigen_zero`) — after +which each rule is one rewrite. + +The sixth, `J_{1/2} ∘ J_{1/2} ⊆ J₁ ⊕ J₀`, is genuinely deeper and needs the *fully* +linearised identity `four_lin2_raw`, obtained here by polarising `two_lin1_raw` a second +time. Evaluated at the right point it collapses to `L_c² = L_c` on the product, which is +exactly "the `1/2`-component vanishes". + +## Why `opCommute_eigen_one_zero` is the one to look at + +The Faraut–Korányi simultaneous-diagonalisation fact — *an element scalar on `range q` and an +element of `J₂(q)` operator-commute* — is the load-bearing hypothesis in the coalescence +arguments that run over a Jordan frame. Its single-idempotent case is +`opCommute_eigen_one_zero` below, and it is three lines from `four_lin2_raw`. + +★ **That is a case, not the frame-level statement.** The frame-level version quantifies over a +rank-two `q = pᵢ + pⱼ` drawn from a Jordan frame, and this file has no frame. +`EuclideanJordan/Frame.lean` puts it in frame shape (`opCommute_scalarOn_frame`) once +orthogonal idempotent families are available, and `EuclideanJordan/Block.lean` supplies the +projection commutation (`peirceOne_comm_peirceOne` and siblings) and the exact +characterisation of the rank-two block. + +## References + +* Faraut and Korányi, *Analysis on Symmetric Cones*, Prop. IV.1.1 and Lemma IV.1.3. +* McCrimmon, *A Taste of Jordan Algebras*, §II.8. +-/ + +namespace EuclideanJordan + +local notation "L" => AddMonoid.End.mulLeft + +/-! Applying an `AddMonoid.End` expression to an element is definitional in every constructor +we use, but Mathlib's corresponding lemmas are phrased for the `AddMonoidHom` coercion and do not +match the `AddMonoid.End` one. Lean 4.28's simp set bridged this on its own; 4.30's does not, so +the four `rfl`s are stated here and passed to `simpa` explicitly. -/ + +private theorem L_apply {J : Type*} [NonUnitalNonAssocSemiring J] (a w : J) : + (AddMonoid.End.mulLeft a) w = a * w := rfl + +private theorem End_add_apply {J : Type*} [NonUnitalNonAssocCommRing J] + (f g : AddMonoid.End J) (w : J) : (f + g) w = f w + g w := rfl + +private theorem End_mul_apply {J : Type*} [NonUnitalNonAssocCommRing J] + (f g : AddMonoid.End J) (w : J) : (f * g) w = f (g w) := rfl + +private theorem End_neg_apply {J : Type*} [NonUnitalNonAssocCommRing J] + (f : AddMonoid.End J) (w : J) : (-f) w = -(f w) := rfl + +section Lin2 + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] + +/-- **Four times the fully linearised Jordan identity.** Polarising `two_lin1_raw` a second +time, in `a := p + q`, and subtracting the two pure terms leaves the cyclic sum + + `⁅L_b, L_{pq}⁆ + ⁅L_p, L_{qb}⁆ + ⁅L_q, L_{pb}⁆ = 0`, + +which is the identity every Peirce multiplication rule beyond the commuting ones needs. +The factor `4` is carried rather than cancelled so that this holds with no torsion +hypothesis. -/ +theorem four_lin2_raw (p q b : J) : + (4 : ℕ) • (⁅L b, L (p * q)⁆ + ⁅L p, L (q * b)⁆ + ⁅L q, L (p * b)⁆) = 0 := by + -- Same instance gap as in `two_lin1_raw`: `LieRing.ofAssociativeRing` is a `local instance` + -- of its own Mathlib file, so a ring's commutator carries `Ring.instBracket` but no `LieRing`, + -- and `lie_add`/`add_lie` cannot fire on `AddMonoid.End J`. Reinstating it inside the proof — + -- never at section scope, which would elaborate this theorem's own `⁅·,·⁆` against a different + -- instance — is what makes the `simp only` below distribute the bracket. + let _ : LieRing (AddMonoid.End J) := LieRing.ofAssociativeRing + have h1 := two_lin1_raw (p + q) b + have hp := two_lin1_raw p b + have hq := two_lin1_raw q b + simp only [add_mul, mul_add, map_add, lie_add, add_lie, mul_comm q p] at h1 + have hpq : (2 : ℕ) • ⁅L b, L (p * p)⁆ + (4 : ℕ) • ⁅L p, L (p * b)⁆ + + ((2 : ℕ) • ⁅L b, L (q * q)⁆ + (4 : ℕ) • ⁅L q, L (q * b)⁆) = 0 := by + rw [hp, hq, add_zero] + have h := sub_eq_zero_of_eq (h1.trans hpq.symm) + rw [← h] + abel + +/-- `four_lin2_raw` evaluated at an element. -/ +theorem four_lin2_apply (p q b w : J) : + (4 : ℕ) • (b * (p * q * w) - p * q * (b * w) + + (p * (q * b * w) - q * b * (p * w)) + + (q * (p * b * w) - p * b * (q * w))) = 0 := by + have h := congrArg (fun f : AddMonoid.End J => f w) (four_lin2_raw p q b) + simpa [Ring.lie_def, sub_eq_add_neg, L_apply, End_add_apply, End_mul_apply, + End_neg_apply] using h + +end Lin2 + +section Commuting + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + +omit [IsCommJordan J] in +/-- Cancel a nonzero natural multiple in a real vector space. -/ +theorem nsmul_eq_zero_iff' {n : ℕ} (hn : n ≠ 0) {x : J} (h : n • x = 0) : x = 0 := by + have h2 : (n : ℝ) • x = 0 := by rw [Nat.cast_smul_eq_nsmul]; exact h + rcases smul_eq_zero.mp h2 with h' | h' + · exact absurd (Nat.cast_eq_zero.mp h') hn + · exact h' + +/-- **`L_x` commutes with `L_c` when `c ∘ x = x`.** The `1`-eigenvectors of `L_c` are +operator-compatible with `c`. Five of the six multiplication rules come from this and its +`0`-eigenvalue twin. -/ +theorem mul_comm_of_eigen_one {c x : J} (hc : c * c = c) (hx : c * x = x) (w : J) : + c * (x * w) = x * (c * w) := by + have h := two_lin1_apply c x w + simp only [hc, hx] at h + refine sub_eq_zero.mp (nsmul_eq_zero_iff' (n := 2) (by norm_num) ?_) + rw [← h] + abel + +/-- **`L_x` commutes with `L_c` when `c ∘ x = 0`.** -/ +theorem mul_comm_of_eigen_zero {c x : J} (hc : c * c = c) (hx : c * x = 0) (w : J) : + c * (x * w) = x * (c * w) := by + have h := two_lin1_apply c x w + simp only [hc, hx, zero_mul, mul_zero, sub_self] at h + refine (sub_eq_zero.mp (nsmul_eq_zero_iff' (n := 2) (by norm_num) ?_)).symm + rw [← h] + abel + +/-- **`J₁(c) ∘ J₁(c) ⊆ J₁(c)`.** -/ +theorem eigen_one_mul_one {c x y : J} (hc : c * c = c) (hx : c * x = x) (hy : c * y = y) : + c * (x * y) = x * y := by + rw [mul_comm_of_eigen_one hc hx y, hy] + +/-- **`J₀(c) ∘ J₀(c) ⊆ J₀(c)`.** -/ +theorem eigen_zero_mul_zero {c x y : J} (hc : c * c = c) (hx : c * x = 0) (hy : c * y = 0) : + c * (x * y) = 0 := by + rw [mul_comm_of_eigen_zero hc hx y, hy, mul_zero] + +/-- **`J₁(c) ∘ J₀(c) = 0`** — the two extreme Peirce components annihilate each other, and +not merely land in a common component. + +The proof reads the same product twice: `L_c` fixes it because `x ∈ J₁`, and `L_c` kills it +because `y ∈ J₀`. -/ +theorem eigen_one_mul_zero {c x y : J} (hc : c * c = c) (hx : c * x = x) (hy : c * y = 0) : + x * y = 0 := by + have h1 : c * (x * y) = 0 := by rw [mul_comm_of_eigen_one hc hx y, hy, mul_zero] + have h2 : c * (x * y) = x * y := by + rw [mul_comm x y, mul_comm_of_eigen_zero hc hy x, hx, mul_comm] + rw [← h2, h1] + +/-- **The single-idempotent case of Faraut–Korányi simultaneous diagonalisation.** + +An element of `J₁(c)` and an element of `J₀(c)` **operator**-commute: `L_x L_y = L_y L_x`, +which is strictly more than the product `x ∘ y` vanishing. It falls straight out of the +cyclic identity, because two of its three brackets vanish. + +★ This is the *case* `q = c`, not the field: the field ranges over a rank-two `q = pᵢ + pⱼ` +inside a Jordan frame, and there is no frame in this file. -/ +theorem opCommute_eigen_one_zero {c x y : J} (hc : c * c = c) (hx : c * x = x) + (hy : c * y = 0) (w : J) : x * (y * w) = y * (x * w) := by + have hxy : x * y = 0 := eigen_one_mul_zero hc hx hy + have h := four_lin2_apply x y c w + rw [hxy] at h + simp only [mul_comm y c, mul_comm x c, hx, hy, zero_mul, mul_zero, + sub_self, zero_add, add_zero] at h + refine (sub_eq_zero.mp (nsmul_eq_zero_iff' (n := 4) (by norm_num) ?_)).symm + rw [← h] + +end Commuting + +section HalfRules + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] + +/-- **`J₁(c) ∘ J_{1/2}(c) ⊆ J_{1/2}(c)`.** -/ +theorem eigen_one_mul_half {c x y : J} (hc : c * c = c) (hx : c * x = x) + (hy : c * y = (2 : ℝ)⁻¹ • y) : c * (x * y) = (2 : ℝ)⁻¹ • (x * y) := by + rw [mul_comm_of_eigen_one hc hx y, hy, mul_smul_comm'] + +/-- **`J₀(c) ∘ J_{1/2}(c) ⊆ J_{1/2}(c)`.** -/ +theorem eigen_zero_mul_half {c x y : J} (hc : c * c = c) (hx : c * x = 0) + (hy : c * y = (2 : ℝ)⁻¹ • y) : c * (x * y) = (2 : ℝ)⁻¹ • (x * y) := by + rw [mul_comm_of_eigen_zero hc hx y, hy, mul_smul_comm'] + +/-- **`J_{1/2}(c) ∘ J_{1/2}(c) ⊆ J₁(c) ⊕ J₀(c)`**, stated as the polynomial relation +`L_c² = L_c` on the product — which is exactly "no `1/2`-component", since +`peirceHalf c z = 4•(c ∘ z) − 4•(c ∘ (c ∘ z))` collapses to `0` under it (`peirceHalf_mul_half_eq_zero`). +★ An earlier draft attributed this to the eigenvalue trichotomy. It does not use the +trichotomy — it is the projection formula directly. + +This is the one rule that needs the fully linearised identity: evaluating `four_lin2_raw` +at `(c, y, x)` and argument `c`, the four `1/4`-terms cancel in pairs and what survives is +`L_c²(xy) − L_c(xy) = 0`. -/ +theorem eigen_half_mul_half {c x y : J} (hc : c * c = c) (hx : c * x = (2 : ℝ)⁻¹ • x) + (hy : c * y = (2 : ℝ)⁻¹ • y) : c * (c * (x * y)) = c * (x * y) := by + have h := four_lin2_apply c y x c + simp only [hc, hx, hy, smul_mul_assoc, mul_smul_comm', smul_smul, + mul_comm y c, mul_comm x c, mul_comm (x * y) c, mul_comm y x] at h + refine sub_eq_zero.mp (nsmul_eq_zero_iff' (n := 4) (by norm_num) ?_) + rw [← h] + abel + +/-- The `1/2`-component of a product of two `1/2`-elements vanishes — `eigen_half_mul_half` +read through the projection of `EuclideanJordan/Peirce.lean`. -/ +theorem peirceHalf_mul_half_eq_zero {c x y : J} (hc : c * c = c) + (hx : c * x = (2 : ℝ)⁻¹ • x) (hy : c * y = (2 : ℝ)⁻¹ • y) : + peirceHalf c (x * y) = 0 := by + rw [peirceHalf_apply, eigen_half_mul_half hc hx hy, sub_self] + +end HalfRules + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean new file mode 100644 index 0000000000..6ce96707ad --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean @@ -0,0 +1,243 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Class + + +/-! +# Peirce subalgebras: `J₂(c)` and `J₀(c)` as Euclidean Jordan algebras in their own right + +`EuclideanJordan/Peirce.lean` carries the Peirce decomposition at a single idempotent as three *linear +maps* `peirceOne c`, `peirceHalf c`, `peirceZero c`, and `EuclideanJordan/PeirceMul.lean` carries the six +Faraut–Korányi rules governing how their images multiply. Two of those rules — +`eigen_one_mul_one` and `eigen_zero_mul_zero` — say that the eigenvalue-`1` and eigenvalue-`0` +eigenspaces are closed under the product. This file turns that closure into structure: each +of the two eigenspaces is itself a Euclidean Jordan algebra, with its own unit. + +Everything that recurses into a Peirce subalgebra sits on this file. + +## The subobject vehicle: `Submodule`, not `NonUnitalSubalgebra` + +★ **This is a deviation from the build plan, and it is what made the module cheap.** The plan +routed the two eigenspaces through `NonUnitalSubalgebra ℝ J` and priced the module at its +risk #3, on a probe showing that `IsCommJordan ↥S` does not synthesise for a +`NonUnitalSubalgebra` subtype: Mathlib has no such transfer instance, so one would have to be +written, and `IsFormallyReal` with it. + +That probe reproduces here, re-run against this tree on 2026-08-22: with +`[EuclideanJordanAlgebra J]` in context, +`example (S : NonUnitalSubalgebra ℝ J) : IsCommJordan ↥S := inferInstance` fails to synthesise, +while the same `example` for `NonUnitalNonAssocCommRing ↥S` succeeds. The conclusion drawn from +it still does not apply, because under `EuclideanJordan/Class.lean`'s design **no transfer is needed**. +The class puts `Mul` and `One` on top of an inner-product space rather than alongside a ring +structure, so the natural subobject is the same shape one level down: the ambient +`Submodule ℝ J`, whose subtype already carries +`NormedAddCommGroup`, `InnerProductSpace ℝ` and `Module ℝ` from Mathlib, with `Mul` and `One` +added on top. Then `EuclideanJordanAlgebra ↥(peirceOneSub hc)` is *constructed* from six +field proofs, every one of which is the ambient identity read through `Subtype.ext`, and +`IsCommJordan`, `IsScalarTower`, `SMulCommClass` and the ring structure then arrive on the +subtype the same way they arrive on `J`: as `EuclideanJordan/Class.lean`'s derived instances. Nothing is +transferred because nothing has to be. + +★ **One rough edge, measured rather than predicted.** `IsFormallyReal ↥(peirceOneSub hc)` does +*not* come back from a bare `inferInstance`, though `IsCommJordan ↥(peirceOneSub hc)`, +`IsScalarTower`, `SMulCommClass` and `NonUnitalNonAssocCommRing` all do. The cause is +`IsFormallyReal`'s keying: it is indexed on `[Mul J]` **and** `[AddCommMonoid J]` as two +independent arguments, and on a `Submodule` subtype a bare goal picks +`(peirceOneSub hc).addCommMonoid` while `EuclideanJordan/Class.lean`'s instance carries the +ring-derived `AddCommMonoid`. The two are **definitionally equal** — both `rfl`-checked, as are +the two `Mul` paths — so this is an elaboration-order artifact and not a diamond, and it costs +nothing where it matters: every consumer in the tree demands `IsFormallyReal` downstream of a +`NonUnitalNonAssocCommRing` argument, so the ring-derived path is the one in the goal and the +instance matches. `spectral_resolution_complete'` therefore runs inside `J₂(c)` unaided. +Should a bare goal ever be wanted, `EuclideanJordanAlgebra.instIsFormallyReal +(J := ↥(peirceOneSub hc))` supplies it — pinning the type is what fixes it, since without it the +instance arguments are synthesised against a metavariable. No such instance is declared here, +because nothing needs one. + +Three facts make this work. None is asserted here on the strength of a past probe: each is +re-checked every time the file compiles, at the declaration named after it. + +* `NormedAddCommGroup ↥W` and `InnerProductSpace ℝ ↥W` synthesise for any `W : Submodule ℝ J` — + without which `instEJAPeirceOneSub` could not even be stated, since the class is indexed by + them; +* the inner product on the subtype is the ambient one **by `rfl`**, which is why + `instEJAPeirceOneSub`'s `inner_assoc` field is the ambient `inner_assoc` applied to the + coercions with no rewriting at all; +* membership in the two carriers is *definitionally* the eigenvalue equation, which is why + `instMulPeirceOneSub` passes `eigen_one_mul_one hc x.2 y.2` straight in as the closure proof + and `instEJAPeirceOneSub`'s `one_mul` field is `Subtype.ext x.2`. + +The plan's *specific* warning — that the class carries `One`, so the instance on `J₂(q)` is +constructed with `1 = q` rather than derived, and `simp`-normal-form care is owed around +`(1 : ↥S)` — is exactly right and is paid here by `coe_one_peirceOneSub` and +`coe_one_peirceZeroSub`, two `@[simp]` lemmas pinning `((1 : ↥(peirceOneSub hc)) : J) = c` and +`((1 : ↥(peirceZeroSub hc)) : J) = 1 - c`. + +The `NonUnitalSubalgebra` versions are **not** built. Nothing downstream needs the subalgebra +lattice, and `Submodule.finrank_lt` — which the dimension-drop lemmas below consume — wants a +`Submodule` anyway. + +## Naming: eigenvalues, not Jacobson indices + +The literature writes the eigenvalue-`1` and eigenvalue-`0` Peirce spaces as `J₂(c)` and +`J₀(c)`, indexing by twice the eigenvalue. The tree's existing projections are named by the +eigenvalue itself (`peirceOne`, `peirceHalf`, `peirceZero`), and consistency inside the tree +wins: `peirceOneSub hc` **is** `J₂(c)` and `peirceZeroSub hc` **is** `J₀(c)`. Reading +`peirceOneSub` as `J₁(c)` — the half-eigenspace — would be a mistake. The half-eigenspace gets +no subalgebra here, and cannot get one on the same terms: `EuclideanJordan/PeirceMul.lean`'s +`peirceHalf_mul_half_eq_zero` puts the product of two half-eigenvectors in `J₂(c) ⊕ J₀(c)`, so +the half-eigenspace is closed under the product only in the degenerate case where it squares to +zero. + +## Scope + +-/ + +noncomputable section + +namespace EuclideanJordan + +open EuclideanJordanAlgebra (smul_mul jordan inner_assoc mul_one') + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] + +/-! ## The two carriers -/ + +/-- **`J₂(c)`**, the eigenvalue-`1` Peirce space `{x | c ∘ x = x}`, as a submodule. + +The idempotency hypothesis is carried but unused: the eigenspace of any element is a submodule. +It is present so that the *type* `↥(peirceOneSub hc)` records it, which is what lets the +`Mul`, `One` and `EuclideanJordanAlgebra` instances below be found by synthesis. -/ +def peirceOneSub {c : J} (_hc : c * c = c) : Submodule ℝ J where + carrier := {x : J | c * x = x} + add_mem' := fun {a b} ha hb => by + change c * (a + b) = a + b + rw [mul_add, ha, hb] + zero_mem' := mul_zero c + smul_mem' := fun r x hx => by + change c * (r • x) = r • x + rw [mul_smul_comm, hx] + +@[simp] theorem mem_peirceOneSub {c : J} (hc : c * c = c) {x : J} : + x ∈ peirceOneSub hc ↔ c * x = x := Iff.rfl + +/-- **`J₀(c)`**, the eigenvalue-`0` Peirce space `{x | c ∘ x = 0}`, as a submodule. -/ +def peirceZeroSub {c : J} (_hc : c * c = c) : Submodule ℝ J where + carrier := {x : J | c * x = 0} + add_mem' := fun {a b} ha hb => by + change c * (a + b) = 0 + rw [mul_add, ha, hb, add_zero] + zero_mem' := mul_zero c + smul_mem' := fun r x hx => by + change c * (r • x) = 0 + rw [mul_smul_comm, hx, smul_zero] + +@[simp] theorem mem_peirceZeroSub {c : J} (hc : c * c = c) {x : J} : + x ∈ peirceZeroSub hc ↔ c * x = 0 := Iff.rfl + +/-! ## `J₂(c)` is a Euclidean Jordan algebra with unit `c` -/ + +section Two + +variable {c : J} (hc : c * c = c) + +/-- Closure under the product is `EuclideanJordan/PeirceMul.lean`'s `eigen_one_mul_one`, applied to the +membership proofs directly: membership unfolds to the eigenvalue equation. -/ +instance instMulPeirceOneSub : Mul ↥(peirceOneSub hc) := + ⟨fun x y => ⟨(x : J) * (y : J), eigen_one_mul_one hc x.2 y.2⟩⟩ + +/-- ★ **The unit of `J₂(c)` is `c`, not `1`.** -/ +instance instOnePeirceOneSub : One ↥(peirceOneSub hc) := ⟨⟨c, hc⟩⟩ + +@[simp] theorem coe_mul_peirceOneSub (x y : ↥(peirceOneSub hc)) : + ((x * y : ↥(peirceOneSub hc)) : J) = (x : J) * (y : J) := rfl + +@[simp] theorem coe_one_peirceOneSub : ((1 : ↥(peirceOneSub hc)) : J) = c := rfl + +/-- **`J₂(c)` is a Euclidean Jordan algebra.** Every field is the ambient identity at the +coercions; `one_mul` is the membership proof itself. -/ +instance instEJAPeirceOneSub : EuclideanJordanAlgebra ↥(peirceOneSub hc) := + { (inferInstance : Mul ↥(peirceOneSub hc)), (inferInstance : One ↥(peirceOneSub hc)) with + mul_comm := fun x y => Subtype.ext (_root_.mul_comm (x : J) (y : J)) + add_mul := fun x y z => Subtype.ext (_root_.add_mul (x : J) (y : J) (z : J)) + smul_mul := fun r x y => Subtype.ext (smul_mul r (x : J) (y : J)) + one_mul := fun x => Subtype.ext x.2 + jordan := fun x y => Subtype.ext (jordan (x : J) (y : J)) + inner_assoc := fun x y z => inner_assoc (x : J) (y : J) (z : J) } + +end Two + +/-! ## `J₀(c)` is a Euclidean Jordan algebra with unit `1 - c` -/ + +section Zero + +variable {c : J} (hc : c * c = c) + +theorem one_sub_mem_peirceZeroSub : (1 : J) - c ∈ peirceZeroSub hc := by + change c * (1 - c) = 0 + rw [mul_sub, mul_one', hc, sub_self] + +instance instMulPeirceZeroSub : Mul ↥(peirceZeroSub hc) := + ⟨fun x y => ⟨(x : J) * (y : J), eigen_zero_mul_zero hc x.2 y.2⟩⟩ + +/-- ★ **The unit of `J₀(c)` is `1 - c`.** -/ +instance instOnePeirceZeroSub : One ↥(peirceZeroSub hc) := + ⟨⟨(1 : J) - c, one_sub_mem_peirceZeroSub hc⟩⟩ + +@[simp] theorem coe_mul_peirceZeroSub (x y : ↥(peirceZeroSub hc)) : + ((x * y : ↥(peirceZeroSub hc)) : J) = (x : J) * (y : J) := rfl + +@[simp] theorem coe_one_peirceZeroSub : ((1 : ↥(peirceZeroSub hc)) : J) = 1 - c := rfl + +/-- **`J₀(c)` is a Euclidean Jordan algebra.** -/ +instance instEJAPeirceZeroSub : EuclideanJordanAlgebra ↥(peirceZeroSub hc) := + { (inferInstance : Mul ↥(peirceZeroSub hc)), (inferInstance : One ↥(peirceZeroSub hc)) with + mul_comm := fun x y => Subtype.ext (_root_.mul_comm (x : J) (y : J)) + add_mul := fun x y z => Subtype.ext (_root_.add_mul (x : J) (y : J) (z : J)) + smul_mul := fun r x y => Subtype.ext (smul_mul r (x : J) (y : J)) + one_mul := fun x => Subtype.ext (by + change ((1 : J) - c) * (x : J) = (x : J) + rw [sub_mul, EuclideanJordanAlgebra.one_mul, x.2, sub_zero]) + jordan := fun x y => Subtype.ext (jordan (x : J) (y : J)) + inner_assoc := fun x y z => inner_assoc (x : J) (y : J) (z : J) } + +end Zero + +/-! ## The dimension drop + +The two lemmas that make an induction on `finrank ℝ J` down the Peirce decomposition +terminate. Each is a one-element argument: if the subalgebra were everything it would contain +the ambient unit (resp. `c` itself), and its defining equation would then force `c = 1` +(resp. `c = 0`). -/ + +section Finrank + +variable [FiniteDimensional ℝ J] {c : J} (hc : c * c = c) + +theorem finrank_peirceOneSub_lt (hne : c ≠ 1) : + Module.finrank ℝ ↥(peirceOneSub hc) < Module.finrank ℝ J := by + refine Submodule.finrank_lt ?_ + intro htop + have h1 : (1 : J) ∈ peirceOneSub hc := by rw [htop]; exact Submodule.mem_top + have h2 : c * (1 : J) = 1 := h1 + exact hne (by rwa [mul_one'] at h2) + +theorem finrank_peirceZeroSub_lt (hne : c ≠ 0) : + Module.finrank ℝ ↥(peirceZeroSub hc) < Module.finrank ℝ J := by + refine Submodule.finrank_lt ?_ + intro htop + have h1 : c ∈ peirceZeroSub hc := by rw [htop]; exact Submodule.mem_top + have h2 : c * c = 0 := h1 + exact hne (by rwa [hc] at h2) + +end Finrank + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean new file mode 100644 index 0000000000..8b7ed21f91 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean @@ -0,0 +1,153 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Frame + + +/-! +# Powers of a single element, and how far power associativity gets + +Power associativity — Albert's theorem, that the subalgebra generated by one element of a +Jordan algebra is associative — is the gateway to the Jordan spectral theorem: the spectral +resolution of `x` lives inside `ℝ[x]`, and that argument needs `ℝ[x]` associative before it can +start. + +★ **`EuclideanJordan/PowerAssoc.lean` proves the theorem itself** (`jpow_mul_jpow`, +unconditional). This file is the *reduction* that made it reachable: it cuts power +associativity down to a single family of commutator statements and discharges the first three +members. It is kept because the reduction, not the induction, is where the content is. + +## The reduction + +Write `CommuteAt x m` for *`L_{x^{m+1}}` commutes with `L_x`*: + + `∀ w, x ∘ (x^{m+1} ∘ w) = x^{m+1} ∘ (x ∘ w)`. + +Then `jpow_mul_jpow_of_commuteAt` says: **`CommuteAt x m` alone gives +`x^{m+1} ∘ x^{n+1} = x^{m+n+2}` for every `n`.** So full power associativity is exactly +`∀ m, CommuteAt x m`, and nothing else is missing. + +★ The reduction itself does **not** use the Jordan identity — its `omit` line says so. Every +bit of Jordan content in power associativity is concentrated in `CommuteAt`. **This is the +part that held up**: it is what let `EuclideanJordan/PowerAssoc.lean` attack a single commutator family +instead of the product law, and the proof there is exactly that attack. + +## What is discharged here + +| `m` | statement | source | +| --- | --- | --- | +| `0` | `L_x` commutes with `L_x` | trivial | +| `1` | `L_x` commutes with `L_{x²}` | **the Jordan identity itself** | +| `2` | `L_x` commutes with `L_{x³}` | one instance of `two_lin1_apply` | + +★ All three are **subsumed** by `commuteAt_all` in `EuclideanJordan/PowerAssoc.lean`. They are +kept because they are the elementary route, because `commuteAt_one` *is* the Jordan identity in +this vocabulary, and because `commuteAt_two` is what made the general pattern visible. + +★ **Do not read `sq_mul_jpow` as power associativity.** It is the `m = 1` row of the table +above and nothing more. +-/ + +namespace EuclideanJordan + +section Pow + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] + +/-- `jpow x n = x^{n+1}`. Indexed from `1` rather than `0` so that no unit is assumed — +the Peirce layer of this development is unit-free and this stays so. -/ +def jpow (x : J) : ℕ → J + | 0 => x + | (n + 1) => x * jpow x n + +omit [IsCommJordan J] in +@[simp] theorem jpow_zero (x : J) : jpow x 0 = x := rfl + +omit [IsCommJordan J] in +@[simp] theorem jpow_succ (x : J) (n : ℕ) : jpow x (n + 1) = x * jpow x n := rfl + +omit [IsCommJordan J] in +theorem jpow_succ' (x : J) (n : ℕ) : jpow x (n + 1) = jpow x n * x := by + rw [jpow_succ, mul_comm] + +/-- `L_{x^{m+1}}` commutes with `L_x`. Power associativity is exactly `∀ m, CommuteAt x m` +(see `jpow_mul_jpow_of_commuteAt`). -/ +def CommuteAt (x : J) (m : ℕ) : Prop := ∀ w : J, x * (jpow x m * w) = jpow x m * (x * w) + +omit [IsCommJordan J] in +/-- **The reduction.** A single commutation hypothesis at `m` gives the whole `m`-th row of +the power table. + +★ Note the `omit`: this holds in *any* commutative non-associative ring. The Jordan identity +is not used, which localises all of Albert's difficulty in `CommuteAt`. -/ +theorem jpow_mul_jpow_of_commuteAt {x : J} {m : ℕ} (h : CommuteAt x m) (n : ℕ) : + jpow x m * jpow x n = jpow x (m + n + 1) := by + induction n with + | zero => rw [jpow_zero, ← jpow_succ', Nat.add_zero] + | succ n ih => + rw [jpow_succ, ← h (jpow x n), ih, + show m + (n + 1) + 1 = (m + n + 1) + 1 from by omega] + exact (jpow_succ x (m + n + 1)).symm + +omit [IsCommJordan J] in +/-- `m = 0`: `L_x` commutes with itself. -/ +theorem commuteAt_zero (x : J) : CommuteAt x 0 := fun _ => rfl + +/-- `m = 1`: `L_x` commutes with `L_{x²}`. **This is the Jordan identity**, in the operator +form Mathlib states it (`commute_lmul_lmul_sq`). -/ +theorem commuteAt_one (x : J) : CommuteAt x 1 := by + intro w + have h := IsCommJordan.lmul_comm_rmul_rmul x w + -- `(x * w) * (x * x) = x * (w * (x * x))` + calc x * (jpow x 1 * w) = x * ((x * x) * w) := rfl + _ = x * (w * (x * x)) := by rw [mul_comm (x * x) w] + _ = (x * w) * (x * x) := h.symm + _ = (x * x) * (x * w) := mul_comm _ _ + _ = jpow x 1 * (x * w) := rfl + +/-- **`x² ∘ x^{n+1} = x^{n+3}`.** The `m = 1` row. -/ +theorem sq_mul_jpow (x : J) (n : ℕ) : (x * x) * jpow x n = jpow x (n + 2) := by + have := jpow_mul_jpow_of_commuteAt (commuteAt_one x) n + rw [show (1 : ℕ) + n + 1 = n + 2 from by omega] at this + exact this + +end Pow + +section PowTwo + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + +/-- `m = 2`: **`L_x` commutes with `L_{x³}`.** One instance of the linearised Jordan +identity: at `a := x`, `b := x²` the first bracket of `two_lin1_apply` cancels against +itself, leaving exactly this. -/ +theorem commuteAt_two (x : J) : CommuteAt x 2 := by + intro w + have h := two_lin1_apply x (jpow x 1) w + simp only [jpow_succ, jpow_zero, sub_self, smul_zero, zero_add] at h + refine sub_eq_zero.mp (nsmul_eq_zero_iff' (n := 4) (by norm_num) ?_) + rw [← h] + abel + +/-- **`x³ ∘ x^{n+1} = x^{n+4}`.** The `m = 2` row. -/ +theorem cube_mul_jpow (x : J) (n : ℕ) : jpow x 2 * jpow x n = jpow x (n + 3) := by + have := jpow_mul_jpow_of_commuteAt (commuteAt_two x) n + rw [show (2 : ℕ) + n + 1 = n + 3 from by omega] at this + exact this + +omit [IsCommJordan J] [Module ℝ J] in +/-- **Power associativity, as the single statement that remains.** Everything else is +`jpow_mul_jpow_of_commuteAt`. -/ +theorem jpow_mul_jpow_of_forall_commuteAt {x : J} (h : ∀ m, CommuteAt x m) (m n : ℕ) : + jpow x m * jpow x n = jpow x (m + n + 1) := + jpow_mul_jpow_of_commuteAt (h m) n + +end PowTwo + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean new file mode 100644 index 0000000000..15ddb0652c --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Power + + +/-! +# Albert's theorem: power associativity + +**`jpow_mul_jpow`: `x^{m+1} ∘ x^{n+1} = x^{m+n+2}` in any real commutative Jordan algebra.** +Unconditional; closure is Lean's three core axioms. + +This is the gateway to the Jordan spectral theorem: the spectral resolution of `x` lives in +the subalgebra `ℝ[x]`, and that argument cannot begin until `ℝ[x]` is associative. We are aware +of no formalization of Albert's theorem in any proof assistant, though we have not searched +them systematically, and Mathlib has nothing between `IsCommJordan` and this. + +## The proof + +Write `cm x i j w` for the commutator `[L_{x^{i+1}}, L_{x^{j+1}}]` applied to `w`. The whole +theorem is `cm x i j w = 0`, since `EuclideanJordan/Power.lean`'s `jpow_mul_jpow_of_commuteAt` already +turns commutation into the product law. + +The engine is `cm_cyclic`: for `i + j = N` (and given the product law at level `N`), the +fully linearised Jordan identity `four_lin2_apply` collapses to + + `cm x j (i+1) w + cm x 0 (N+1) w + cm x i (j+1) w = 0`. + +Now fix `N` and `w` and look at the "antidiagonal" `d k := cm x k (N+1-k) w`. The identity +says exactly `d j + d 0 + d i = 0` whenever `i + j = N`, and antisymmetry says +`d k = − d (N+1−k)`. Together these force + + `d (k+1) = d k + d 0`, hence `d k = (k+1) · d 0`, + +and the wrap-around `d (N+1) = − d 0` then gives `(N+3) · d 0 = 0`. In a real vector space +that means `d 0 = 0`, so the whole antidiagonal vanishes and the induction advances. + +★ **`(N+3)` is where the characteristic hypothesis bites *hardest*.** Albert's theorem needs +**every** positive integer invertible — it would fail in characteristic `p` for any `p ≤ N+3` +— whereas the Peirce layer needs only `2`. So `Module ℝ J` is load-bearing everywhere in this +development, and unboundedly so here. +## Relation to Mathlib + +The Mathlib-facing shape is `instance : PNatPowAssoc J` — the class +(`Mathlib/Algebra/Group/PNatPowAssoc.lean`, `ppow_add : x ^ (k + n) = x ^ k * x ^ n`) already +exists with two users. Supplying the `Pow J ℕ+` instance and transporting `jpow_mul_jpow` +across it is the upstream contribution. **Not done here** — that translation is unwritten, and +this file should not be read as providing it. +-/ + +namespace EuclideanJordan + +section Albert + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + +/-- The commutator `[L_{x^{i+1}}, L_{x^{j+1}}]` applied to `w`. Albert's theorem is that this +vanishes identically. -/ +def cm (x : J) (i j : ℕ) (w : J) : J := + jpow x i * (jpow x j * w) - jpow x j * (jpow x i * w) + +omit [IsCommJordan J] [Module ℝ J] in +theorem cm_antisymm (x : J) (i j : ℕ) (w : J) : cm x i j w = - cm x j i w := by simp [cm] + +omit [IsCommJordan J] [Module ℝ J] in +theorem cm_self (x : J) (i : ℕ) (w : J) : cm x i i w = 0 := by simp [cm] + +omit [IsCommJordan J] [Module ℝ J] in +/-- `cm x 0 i ≡ 0` is precisely `CommuteAt x i`. -/ +theorem commuteAt_of_cm {x : J} {i : ℕ} (h : ∀ w, cm x 0 i w = 0) : CommuteAt x i := by + intro w + have hw := h w + rw [cm, jpow_zero] at hw + exact sub_eq_zero.mp hw + +/-- **The engine.** The fully linearised Jordan identity, instantiated at `(x, x^{i+1}, +x^{j+1})` and evaluated at `w`, with the middle product already reduced by the level-`N` +product law. This is the relation that ties the antidiagonal together. -/ +theorem cm_cyclic (x : J) (i j : ℕ) (hij : jpow x i * jpow x j = jpow x (i + j + 1)) (w : J) : + cm x j (i + 1) w + cm x 0 (i + j + 1) w + cm x i (j + 1) w = 0 := by + have h := four_lin2_apply (jpow x 0) (jpow x i) (jpow x j) w + simp only [jpow_zero] at h + rw [show x * jpow x i = jpow x (i + 1) from rfl] at h + rw [show x * jpow x j = jpow x (j + 1) from rfl] at h + rw [hij] at h + refine nsmul_eq_zero_iff' (n := 4) (by norm_num) ?_ + rw [← h] + simp only [cm, jpow_zero] + +/-- **Albert's theorem, commutator form.** All multiplication operators of powers of a single +element commute. The induction is on the total degree `N`; at each level the antidiagonal +argument of the module docstring forces the new commutators to vanish. -/ +theorem cm_eq_zero (x : J) (N : ℕ) : ∀ i j : ℕ, i + j ≤ N → ∀ w : J, cm x i j w = 0 := by + induction N with + | zero => + intro i j hij w + obtain ⟨rfl, rfl⟩ : i = 0 ∧ j = 0 := ⟨by omega, by omega⟩ + exact cm_self x 0 w + | succ N ih => + have hA : ∀ i j : ℕ, i + j ≤ N → jpow x i * jpow x j = jpow x (i + j + 1) := fun i j hij => + jpow_mul_jpow_of_commuteAt (commuteAt_of_cm (fun w => ih 0 i (by omega) w)) j + intro i j hij w + rcases Nat.lt_or_ge (i + j) (N + 1) with hlt | hge + · exact ih i j (by omega) w + · have hsum : i + j = N + 1 := by omega + set c : J := cm x 0 (N + 1) w with hc + have key : ∀ k : ℕ, k ≤ N + 1 → ∀ l : ℕ, k + l = N + 1 → cm x k l w = (k + 1) • c := by + intro k + induction k with + | zero => + intro _ l hl + obtain rfl : l = N + 1 := by omega + simp [hc] + | succ k ihk => + intro hk l hl + have hcyc := cm_cyclic x l k (hA l k (by omega)) w + rw [show l + k + 1 = N + 1 from by omega] at hcyc + rw [ihk (by omega) (l + 1) (by omega), cm_antisymm x l (k + 1) w, ← hc] at hcyc + rw [(add_neg_eq_zero.mp hcyc).symm, ← succ_nsmul] + have hlast : cm x (N + 1) 0 w = (N + 2) • c := key (N + 1) le_rfl 0 (by omega) + have hneg : cm x (N + 1) 0 w = - c := by rw [cm_antisymm x (N + 1) 0 w, ← hc] + have hzero : (N + 3) • c = 0 := by + have h2 : (N + 2) • c = -c := by rw [← hlast, hneg] + rw [show N + 3 = (N + 2) + 1 from rfl, succ_nsmul, h2] + exact neg_add_cancel c + rw [key i (by omega) j (by omega), nsmul_eq_zero_iff' (n := N + 3) (by omega) hzero, + smul_zero] + +/-- `L_{x^{m+1}}` commutes with `L_x`, for every `m` — the gap this arc's wall certificate +priced as open. -/ +theorem commuteAt_all (x : J) (m : ℕ) : CommuteAt x m := + commuteAt_of_cm (fun w => cm_eq_zero x m 0 m (by omega) w) + +/-- **POWER ASSOCIATIVITY (Albert's theorem).** `x^{m+1} ∘ x^{n+1} = x^{m+n+2}` in any real +commutative Jordan algebra. No formal reality, no finite dimension, no unit, no inner +product — only the Jordan identity and the real scalars. -/ +theorem jpow_mul_jpow (x : J) (m n : ℕ) : jpow x m * jpow x n = jpow x (m + n + 1) := + jpow_mul_jpow_of_commuteAt (commuteAt_all x m) n + +/-- The operator form: the multiplication operators of powers of one element mutually +commute. This, not the product law, is what a spectral argument consumes. -/ +theorem opCommute_jpow (x : J) (i j : ℕ) (w : J) : + jpow x i * (jpow x j * w) = jpow x j * (jpow x i * w) := + sub_eq_zero.mp (cm_eq_zero x (i + j) i j le_rfl w) + +end Albert + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean new file mode 100644 index 0000000000..0a10479b60 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean @@ -0,0 +1,191 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra + + +/-! +# Primitive idempotents, Jordan frames, and rank + +`EuclideanJordan/Frame.lean` carries `IsOrthIdemFamily`: a family of pairwise-orthogonal idempotents, with +completeness deliberately left out. This file adds the two conditions that turn such a family +into a *Jordan frame* — every member primitive, and the family complete — and defines the rank. + +## What the rank is here, and the chapter that is not needed + +★ **Frame conjugacy is not proved and is not needed.** "All Jordan frames have the same +cardinality" looks like a prerequisite — "rank `n ≥ 3`" seems meaningless before it — and its +classical proof (Faraut–Korányi Thm IV.2.5, conjugacy of frames under `Aut(J)`) is a chapter of +its own. The direction saves it. The rank is *defined* as the size of a Jordan frame — a complete +system of orthogonal primitive idempotents `F = {p₁, …, pₙ}` with `∑ᵢ pᵢ = e` (FK IV.2) — so a +hypothesis "rank `= n`" **implies** "there exists a Jordan frame of cardinality `n`". Taking +the latter as the Lean hypothesis makes the Lean theorem weaker or equal, which is the correct +direction for an import; and well-definedness falls out downstream, since once `J ≅ H_n(K)` the +dimension pins `n`. + +★ So this file's `rank` is deliberately **not** the load-bearing object, and downstream modules +should carry a `JordanFrame J n` as *data* rather than reason about the number `rank J`. What +is proved about `rank J` is exactly two inequalities: `JordanFrame.card_le_rank` (a frame's +cardinality is at most the rank) and `rank_le_finrank`. The reverse of the first — +`rank J = n` for a frame of cardinality `n` — is **not** proved here, and nothing in this file +is a step towards it: `rank J` is a supremum over *all* orthogonal families of nonzero +idempotents, primitive or not, and bounding such a family by `n` needs either the frame Peirce +decomposition or frame conjugacy, neither of which is available yet. Do not quote `rank J = n` +off this file. + +## The linear-independence argument is already in the tree + +Orthogonal nonzero idempotents are linearly independent, and the argument is +`EuclideanJordan/Order.lean`'s `inner_left_coeff` verbatim: idempotency and then associativity of the inner +product give `⟪pₖ, pᵢ⟫ = ⟪pₖ ∘ pₖ, pᵢ⟫ = ⟪pₖ, pₖ ∘ pᵢ⟫ = 0` for `i ≠ k`, so pairing a vanishing +combination against `pₖ` reads off `gₖ ‖pₖ‖² = 0`. That lemma is stated in `EuclideanJordan/Order.lean`'s bilinear-map vocabulary; +`EuclideanJordan/Class.lean`'s `jmulₗ` and `jmulₗ_inner_assoc` are what let it be applied at the class +without restating it. + +## Scope + +-/ + +noncomputable section + +namespace EuclideanJordan + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] + +/-! ## Primitivity -/ + +/-- A **primitive idempotent**: a nonzero idempotent that cannot be split, i.e. the only +idempotents of the Peirce subalgebra `J₂(c)` are `0` and `c` itself. + +The third clause is stated in the ambient algebra — `d` idempotent with `c ∘ d = d`, which is +membership in `peirceOneSub` — rather than over the subtype, so that it can be checked without +first producing the subalgebra. `isPrimitive_iff_of_idem` below is the two readings' +equivalence. -/ +def IsPrimitive (c : J) : Prop := + c * c = c ∧ c ≠ 0 ∧ ∀ d : J, d * d = d → c * d = d → d = 0 ∨ d = c + +theorem IsPrimitive.idem {c : J} (h : IsPrimitive c) : c * c = c := h.1 + +theorem IsPrimitive.ne_zero {c : J} (h : IsPrimitive c) : c ≠ 0 := h.2.1 + +theorem IsPrimitive.eq_zero_or_eq {c : J} (h : IsPrimitive c) {d : J} (hd : d * d = d) + (hcd : c * d = d) : d = 0 ∨ d = c := h.2.2 d hd hcd + +/-- Primitivity of `c`, read *inside* `J₂(c)`: `c` is primitive exactly when the only +idempotents of the Peirce subalgebra are its zero and its unit. The intended consumer is the +`dim V_ii = 1` step of the frame Peirce decomposition, which runs the spectral theorem inside +`J₂(pᵢ)`; that step is `EuclideanJordan/FramePeirceMul.lean`'s `peirceOneSub_eq_span_of_isPrimitive`, and it +consumes this lemma in the `→` direction. -/ +theorem isPrimitive_iff_of_idem {c : J} (hc : c * c = c) (hc0 : c ≠ 0) : + IsPrimitive c ↔ ∀ d : ↥(peirceOneSub hc), d * d = d → d = 0 ∨ d = 1 := by + constructor + · rintro ⟨-, -, h⟩ d hd + rcases h (d : J) (congrArg Subtype.val hd) d.2 with h0 | h1 + · exact Or.inl (Subtype.ext h0) + · exact Or.inr (Subtype.ext h1) + · intro h + refine ⟨hc, hc0, fun d hd hcd => ?_⟩ + rcases h ⟨d, hcd⟩ (Subtype.ext hd) with h0 | h1 + · exact Or.inl (congrArg Subtype.val h0) + · exact Or.inr (congrArg Subtype.val h1) + +/-! ## Jordan frames -/ + +/-- A **Jordan frame**: a complete family of pairwise-orthogonal primitive idempotents. +Carried as data, indexed by `Fin n`, so that its cardinality is available without any +well-definedness theorem — see the module docstring. -/ +structure JordanFrame (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + [EuclideanJordanAlgebra J] (n : ℕ) where + /-- The idempotents. -/ + p : Fin n → J + /-- They are idempotent and pairwise orthogonal. -/ + orthIdem : IsOrthIdemFamily p + /-- Each is primitive. -/ + primitive : ∀ i, IsPrimitive (p i) + /-- They sum to the unit. -/ + complete : ∑ i, p i = 1 + +namespace JordanFrame + +variable {n : ℕ} (F : JordanFrame J n) + +theorem p_ne_zero (i : Fin n) : F.p i ≠ 0 := (F.primitive i).ne_zero + +/-- **A Jordan frame cannot be extended.** Anything orthogonal to every member of a complete +family is annihilated by the unit, hence zero — so there is no nonzero idempotent to adjoin. +Idempotency of `q` is not used. -/ +theorem eq_zero_of_orth {q : J} (hq : ∀ i, F.p i * q = 0) : q = 0 := by + have h : (1 : J) * q = 0 := by + rw [← F.complete, Finset.sum_mul, Finset.sum_eq_zero (fun i _ => hq i)] + rwa [EuclideanJordanAlgebra.one_mul] at h + +include F in +/-- A frame of a nontrivial algebra is nonempty. -/ +theorem card_pos (h1 : (1 : J) ≠ 0) : 0 < n := by + rcases Nat.eq_zero_or_pos n with rfl | h + · exact absurd (by simpa using F.complete.symm) h1 + · exact h + +end JordanFrame + +/-! ## Linear independence and the dimension bound -/ + +/-- **Orthogonal nonzero idempotents are linearly independent.** `EuclideanJordan/Order.lean`'s +`inner_left_coeff`, applied through `EuclideanJordan/Class.lean`'s `jmulₗ`. -/ +theorem linearIndependent_of_orthIdem {n : ℕ} {p : Fin n → J} (hp : IsOrthIdemFamily p) + (hne : ∀ i, p i ≠ 0) : LinearIndependent ℝ p := by + rw [Fintype.linearIndependent_iff] + intro g hg k + have key := inner_left_coeff (m := jmulₗ J) (q := p) (lam := g) jmulₗ_inner_assoc + (fun i => hp.idem i) (fun i j hij => hp.orth i j hij) (x := 0) hg.symm k + rw [inner_zero_right] at key + have hpos : (0 : ℝ) < inner ℝ (p k) (p k) := real_inner_self_pos.mpr (hne k) + nlinarith [key, hpos] + +theorem card_le_finrank_of_orthIdem [FiniteDimensional ℝ J] {n : ℕ} {p : Fin n → J} + (hp : IsOrthIdemFamily p) (hne : ∀ i, p i ≠ 0) : n ≤ Module.finrank ℝ J := by + simpa using (linearIndependent_of_orthIdem hp hne).fintype_card_le_finrank + +/-! ## Rank -/ + +/-- The cardinalities realised by orthogonal families of **nonzero** idempotents. Primitivity +is not required — see the module docstring on what that costs. -/ +def orthIdemCards (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + [EuclideanJordanAlgebra J] : Set ℕ := + {n | ∃ p : Fin n → J, IsOrthIdemFamily p ∧ ∀ i, p i ≠ 0} + +theorem zero_mem_orthIdemCards : 0 ∈ orthIdemCards J := + ⟨Fin.elim0, ⟨fun i => i.elim0, fun i => i.elim0⟩, fun i => i.elim0⟩ + +theorem bddAbove_orthIdemCards [FiniteDimensional ℝ J] : BddAbove (orthIdemCards J) := + ⟨Module.finrank ℝ J, fun _ ⟨_p, hp, hne⟩ => card_le_finrank_of_orthIdem hp hne⟩ + +/-- The **rank** of a Euclidean Jordan algebra: the greatest cardinality of a family of +pairwise-orthogonal nonzero idempotents. -/ +def rank (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + [EuclideanJordanAlgebra J] : ℕ := sSup (orthIdemCards J) + +theorem le_rank [FiniteDimensional ℝ J] {n : ℕ} (hn : n ∈ orthIdemCards J) : n ≤ rank J := + le_csSup bddAbove_orthIdemCards hn + +theorem rank_le_finrank [FiniteDimensional ℝ J] : rank J ≤ Module.finrank ℝ J := + csSup_le ⟨0, zero_mem_orthIdemCards⟩ (fun _ ⟨_p, hp, hne⟩ => card_le_finrank_of_orthIdem hp hne) + +theorem JordanFrame.mem_orthIdemCards {n : ℕ} (F : JordanFrame J n) : n ∈ orthIdemCards J := + ⟨F.p, F.orthIdem, F.p_ne_zero⟩ + +theorem JordanFrame.card_le_rank [FiniteDimensional ℝ J] {n : ℕ} (F : JordanFrame J n) : + n ≤ rank J := le_rank F.mem_orthIdemCards + +theorem JordanFrame.card_le_finrank [FiniteDimensional ℝ J] {n : ℕ} (F : JordanFrame J n) : + n ≤ Module.finrank ℝ J := + card_le_finrank_of_orthIdem F.orthIdem F.p_ne_zero + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean new file mode 100644 index 0000000000..e8d5f3bf58 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean @@ -0,0 +1,599 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra +import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +import LeanPool.EuclideanJordan.EuclideanJordan.Witness +import LeanPool.EuclideanJordan.EuclideanJordan.Bridge +import Mathlib.LinearAlgebra.Lagrange +import Mathlib.Analysis.Complex.Polynomial.Basic +import Mathlib.RingTheory.PrincipalIdealDomain + + +/-! +# The single-element spectral theorem for a formally real Jordan algebra + +Every element of a finite-dimensional formally real Jordan algebra is a real combination of +pairwise-orthogonal idempotents drawn from the subalgebra it generates +(`spectral_resolution`), and, when a unit is available, of a family summing to it +(`spectral_resolution_complete`). Both are instantiated on `HermitianMat d 𝕜` at the end of +the file, so the abstract statements have a live carrier. + +## The route, and why it carries no ring structure on `jspan x` + +The classical treatment works inside the unital algebra `ℝ[x] = span{1, x, x², …}` and needs +that algebra to be a *ring* — which forces either a unit on `J` or a `Unitization` of a +subtype, and with it a `NonUnitalCommRing ↥(jspan x)` instance and its scalar towers. **None +of that appears here.** The polynomial bookkeeping is carried by a linear map + + `jeval x : ℝ[X] →ₗ[ℝ] J`, `jeval x p = ∑ₙ p.coeff n • x^{n+1}`, + +which is "`x·p(x)`" — the shape available with no unit at all, since every monomial carries +at least one factor of `x`. Its whole content is one identity, + + `jeval_mul : jeval x p * jeval x q = jeval x (X * p * q)`, + +which is `EuclideanJordan/PowerAssoc.lean`'s `jpow_mul_jpow` transported along bilinearity. Everything +downstream is ideal theory in `ℝ[X]`, where Mathlib already has what is needed, and the +values live in the ambient `J`, where `EuclideanJordan/FormallyReal.lean` already applies. + +The steps, each a named declaration below: + +| step | statement | declaration | +| --- | --- | --- | +| 1 | the annihilator `{p | x·p(x) = 0}` is an ideal of `ℝ[X]`, nonzero in finite dimension | `jann`, `exists_annihilator_generator` | +| 2 | its generator `m` is radical, hence squarefree | `isRadical_of_annihilator` | +| 3 | `m` has a nonzero constant term | `coeff_zero_ne_zero_of_annihilator` | +| 4 | `m` has no non-real complex root | `annihilator_aeval_ne_zero_of_im_ne_zero` | +| 5 | so some product of distinct linear factors, none of them `X`, annihilates `x` | `exists_split_annihilator` | +| 6 | Lagrange interpolation at those roots together with `0` gives the idempotents | `exists_orthIdem_finset` | + +★ **Step 2 is where formal reality enters the polynomial algebra**, through the identity +`jpow (jeval x f) n = jeval x (Xⁿ f^{n+1})` (`jpow_jeval`): a Jordan power of a value of +`jeval` is again a value of `jeval`, so `EuclideanJordan/FormallyReal.lean`'s no-nilpotents theorem — +which is stated about *ambient* elements of `J` — applies with no repackaging. This is why +no `IsReduced` instance on a ring structure over `jspan x` is needed anywhere. + +★ **Step 4 uses no idempotent.** The textbook argument builds `e ≡ e² (mod m)` from a Bézout +splitting and applies formal reality to `((x−Re z)e(x))² + (Im z · e(x))²`. Here the +idempotence is never used: with `q ∣ m` the real quadratic through a non-real root `z`, and +`e := βu` from `α q + β u = 1`, the *only* facts consumed are `q·e ≡ 0 (mod m)` and, for the +contradiction, `e ≢ 0 (mod m)`. What formal reality kills is the value `jeval x g` directly, +where `X * g` is `e` corrected to have zero constant term — a correction available exactly +because of step 3. Multiplicities never enter, and neither does `Mathlib`'s `radical` API. + +## Scope — what these theorems are NOT + +* They are stated at the **typeclass** generality of the rest of the EJA layer + (`NonUnitalNonAssocCommRing` + `IsCommJordan` + `Module ℝ` + `IsScalarTower` + finite + dimension + `IsFormallyReal`). A consumer carrying its Jordan product as a *bundled bilinear + map* `J →ₗ[ℝ] J →ₗ[ℝ] J` reaches them only through `EuclideanJordan/Bridge.lean`'s + `ringOfBilinear`, for the reason recorded in `EuclideanJordan/Frame.lean`. The interface + section at the end of this file does exactly that crossing, for this theorem and no other. +* `spectral_resolution` is unit-free and its idempotents therefore sum to the *support* of + `x`, not to a unit. `spectral_resolution_complete` adds completeness by appending `e − s` + with coefficient `0`; that member is not in `jspan x`, so the `jspan` clause is the price + of completeness and is dropped there rather than weakened. +* Nothing here is a functional calculus: the resolution is produced, but no continuous or + polynomial calculus is built on it. +-/ + +namespace EuclideanJordan + +open Polynomial + +section Eval + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] + +/-- `jeval x p = ∑ n, p.coeff n • x^{n+1}`, morally `x * p(x)`. -/ +noncomputable def jeval (x : J) : Polynomial ℝ →ₗ[ℝ] J where + toFun p := p.sum fun n a => a • jpow x n + map_add' p q := + Polynomial.sum_add_index p q _ (fun _ => zero_smul _ _) (fun _ b₁ b₂ => add_smul b₁ b₂ _) + map_smul' r p := by + simp only [RingHom.id_apply] + rw [Polynomial.sum_smul_index p r (fun n a => a • jpow x n) (fun _ => zero_smul ℝ _), + Polynomial.smul_sum] + exact Finset.sum_congr rfl fun n _ => (mul_smul r _ _) + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +@[simp] theorem jeval_monomial (x : J) (n : ℕ) (a : ℝ) : + jeval x (monomial n a) = a • jpow x n := + Polynomial.sum_monomial_index a (fun n a => a • jpow x n) (zero_smul _ _) + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +@[simp] theorem jeval_one (x : J) : jeval x 1 = x := by + rw [← C_1, ← monomial_zero_left, jeval_monomial, one_smul, jpow_zero] + +omit [IsCommJordan J] in +/-- Multiplying the argument by `X` multiplies the value by `x`. -/ +theorem jeval_X_mul (x : J) (p : Polynomial ℝ) : jeval x (X * p) = x * jeval x p := by + induction p using Polynomial.induction_on' with + | add p q hp hq => rw [mul_add, map_add, map_add, hp, hq, mul_add] + | monomial n a => + rw [X_mul_monomial, jeval_monomial, jeval_monomial, jpow_succ, + mul_smul_comm'] + +/-- **The multiplication rule.** `jeval` is "`x·p(x)`", so the product of two values is +`x·p(x)·x·q(x) = x·(X p q)(x)`. -/ +theorem jeval_mul (x : J) (p q : Polynomial ℝ) : + jeval x p * jeval x q = jeval x (X * p * q) := by + induction p using Polynomial.induction_on' with + | add p₁ p₂ hp₁ hp₂ => rw [map_add, add_mul, hp₁, hp₂, mul_add, add_mul, map_add] + | monomial m a => + induction q using Polynomial.induction_on' with + | add q₁ q₂ hq₁ hq₂ => rw [map_add, mul_add, hq₁, hq₂, mul_add, map_add] + | monomial n b => + rw [jeval_monomial, jeval_monomial, smul_mul_assoc, mul_smul_comm', jpow_mul_jpow, + smul_smul, X_mul_monomial, monomial_mul_monomial, jeval_monomial] + congr 2 + omega + +end Eval + +section Annihilator + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] + +omit [IsCommJordan J] in +theorem jeval_X_pow_mul {x : J} {p : Polynomial ℝ} (hp : jeval x p = 0) (k : ℕ) : + jeval x (X ^ k * p) = 0 := by + induction k with + | zero => simpa using hp + | succ k ih => rw [pow_succ', mul_assoc, jeval_X_mul, ih, mul_zero] + +omit [IsCommJordan J] in +theorem jeval_mul_eq_zero {x : J} {p : Polynomial ℝ} (hp : jeval x p = 0) (q : Polynomial ℝ) : + jeval x (q * p) = 0 := by + induction q using Polynomial.induction_on' with + | add q₁ q₂ h₁ h₂ => rw [add_mul, map_add, h₁, h₂, add_zero] + | monomial n a => + have hmul : (monomial n a : Polynomial ℝ) * p = a • (X ^ n * p) := by + rw [← C_mul_X_pow_eq_monomial, smul_eq_C_mul, mul_assoc] + rw [hmul, map_smul, jeval_X_pow_mul hp n, smul_zero] + +/-- The annihilator of `x`: the polynomials `p` with `x·p(x) = 0`. -/ +noncomputable def jann (x : J) : Ideal (Polynomial ℝ) where + carrier := {p | jeval x p = 0} + add_mem' := fun {p q} hp hq => by + simp only [Set.mem_ofPred_eq] at * + rw [map_add, hp, hq, add_zero] + zero_mem' := by simp only [Set.mem_ofPred_eq, map_zero] + smul_mem' := fun q p hp => by + simp only [Set.mem_ofPred_eq, smul_eq_mul] at * + exact jeval_mul_eq_zero hp q + +omit [IsCommJordan J] in +theorem mem_jann {x : J} {p : Polynomial ℝ} : p ∈ jann x ↔ jeval x p = 0 := Iff.rfl + +omit [IsCommJordan J] in +/-- **The annihilator has a nonzero generator.** Finite dimension makes the powers of `x` +dependent, so the annihilator is a nonzero ideal of the principal ideal ring `ℝ[X]`. -/ +theorem exists_annihilator_generator [Module.Finite ℝ J] (x : J) : + ∃ m : Polynomial ℝ, m ≠ 0 ∧ ∀ f, jeval x f = 0 ↔ m ∣ f := by + obtain ⟨n, c, ⟨i₀, hi₀⟩, hsum⟩ := exists_jpow_relation x + have hp0 : jeval x (∑ i : Fin n, monomial (i : ℕ) (c i)) = 0 := by + rw [map_sum] + simpa using hsum + have hpne : (∑ i : Fin n, monomial (i : ℕ) (c i)) ≠ 0 := by + intro h + have hco : (∑ i : Fin n, monomial (i : ℕ) (c i)).coeff (i₀ : ℕ) = c i₀ := by + rw [Polynomial.finsetSum_coeff, Finset.sum_eq_single i₀] + · simp + · intro b _ hb + simp [Polynomial.coeff_monomial, Fin.val_ne_of_ne hb] + · simp + rw [h, Polynomial.coeff_zero] at hco + exact hi₀ hco.symm + refine ⟨Submodule.IsPrincipal.generator (jann x), ?_, fun f => ?_⟩ + · intro hgen + have := (Submodule.IsPrincipal.mem_iff_generator_dvd (jann x)).mp (mem_jann.mpr hp0) + rw [hgen, zero_dvd_iff] at this + exact hpne this + · exact (mem_jann (x := x) (p := f)).symm.trans + (Submodule.IsPrincipal.mem_iff_generator_dvd (jann x)) + +/-- `x·f(x)` raised to the `n`-th Jordan power is again a value of `jeval`. -/ +theorem jpow_jeval (x : J) (f : Polynomial ℝ) (n : ℕ) : + jpow (jeval x f) n = jeval x (X ^ n * f ^ (n + 1)) := by + induction n with + | zero => simp + | succ n ih => + rw [jpow_succ, ih, jeval_mul] + congr 1 + ring + +variable [IsFormallyReal J] + +/-- **The generator is radical.** A power of `f` annihilating `x` forces `f` to, because the +Jordan powers of `x·f(x)` are exactly the values of `jeval` on `Xⁿ f^{n+1}` and a formally +real Jordan algebra has no nilpotents. -/ +theorem isRadical_of_annihilator {x : J} {m : Polynomial ℝ} + (hm : ∀ f, jeval x f = 0 ↔ m ∣ f) : IsRadical m := by + intro n f hdvd + match n with + | 0 => exact (isUnit_of_dvd_one (by simpa using hdvd)).dvd + | (k + 1) => + refine (hm f).mp ?_ + have h1 : jeval x (X ^ k * f ^ (k + 1)) = 0 := + jeval_X_pow_mul ((hm _).mpr hdvd) k + rw [← jpow_jeval] at h1 + exact eq_zero_of_jpow_eq_zero k h1 + +/-- **The generator has a nonzero constant term.** If `X ∣ m` then the cofactor's value +squares to zero, so it too annihilates `x`, and it has smaller degree than the generator. -/ +theorem coeff_zero_ne_zero_of_annihilator {x : J} {m : Polynomial ℝ} + (hm : ∀ f, jeval x f = 0 ↔ m ∣ f) (hm0 : m ≠ 0) : m.coeff 0 ≠ 0 := by + intro hc + obtain ⟨m₁, hm₁⟩ := Polynomial.X_dvd_iff.mpr hc + have hm₁0 : m₁ ≠ 0 := by + rintro rfl + rw [mul_zero] at hm₁ + exact hm0 hm₁ + have hsq : jeval x m₁ * jeval x m₁ = 0 := by + rw [jeval_mul, ← hm₁] + exact (hm _).mpr ⟨m₁, rfl⟩ + have hdvd : m ∣ m₁ := (hm m₁).mp (eq_zero_of_mul_self_eq_zero hsq) + have hle := Polynomial.natDegree_le_of_dvd hdvd hm₁0 + rw [hm₁, Polynomial.natDegree_mul Polynomial.X_ne_zero hm₁0, Polynomial.natDegree_X] at hle + omega + +end Annihilator + +section Kill + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] [IsFormallyReal J] + +open Complex in +/-- **The kill.** The generator of the annihilator has no non-real complex root. -/ +theorem annihilator_aeval_ne_zero_of_im_ne_zero {x : J} {m : Polynomial ℝ} + (hm : ∀ f, jeval x f = 0 ↔ m ∣ f) (hsq : Squarefree m) (h0 : m.coeff 0 ≠ 0) + {z : ℂ} (hz : z.im ≠ 0) : aeval z m ≠ 0 := by + intro hroot + obtain ⟨u, hu⟩ := m.quadratic_dvd_of_aeval_eq_zero_im_ne_zero hroot hz + set q : Polynomial ℝ := X ^ 2 - C (2 * z.re) * X + C (‖z‖ ^ 2) with hqdef + have hqnu : ¬ IsUnit q := by + intro h + have h2 : q.natDegree = 2 := by rw [hqdef]; compute_degree! + rw [Polynomial.natDegree_eq_zero_of_isUnit h] at h2 + exact absurd h2 (by norm_num) + have hcop : IsCoprime q u := by + rw [hu] at hsq + exact (IsRelPrime.of_squarefree_mul hsq).isCoprime + obtain ⟨α, β, hbez⟩ := hcop + set c : ℝ := (β * u).coeff 0 / m.coeff 0 with hcdef + set g : Polynomial ℝ := (β * u - C c * m).divX with hgdef + have hXg : X * g = β * u - C c * m := by + have hc0 : (β * u - C c * m).coeff 0 = 0 := by + simp [hcdef, div_mul_cancel₀ _ h0] + rw [hgdef] + conv_rhs => rw [← Polynomial.X_mul_divX_add (β * u - C c * m)] + rw [hc0, map_zero, add_zero] + -- `q` annihilates the class of `X * g` + have h1 : m ∣ q * (X * g) := by + rw [hXg] + exact ⟨β - C c * q, by rw [hu]; ring⟩ + -- `q` is a sum of two squares + have hqsq : q = (X - C z.re) ^ 2 + C (z.im ^ 2) := by + have hnorm : ‖z‖ ^ 2 = z.re ^ 2 + z.im ^ 2 := by + rw [← Complex.normSq_eq_norm_sq, Complex.normSq_apply]; ring + rw [hqdef, hnorm] + simp only [Polynomial.C_add, Polynomial.C_mul, Polynomial.C_pow, map_ofNat] + ring + -- the sum of squares vanishes + set a : J := jeval x ((X - C z.re) * g) with hadef + set b : J := z.im • jeval x g with hbdef + have hsum : a * a + b * b = 0 := by + have hb2 : b * b = jeval x (C (z.im ^ 2) * (X * g * g)) := by + rw [hbdef, smul_mul_assoc, mul_smul_comm', smul_smul, jeval_mul, ← Polynomial.smul_eq_C_mul, + map_smul] + ring_nf + rw [hadef, jeval_mul, hb2, ← map_add] + refine (hm _).mpr (dvd_trans (h1.mul_right g) ?_) + exact ⟨1, by rw [hqsq]; ring⟩ + have hb0 : b = 0 := by + have := IsFormallyReal.eq_zero_of_sum_mul_self (Finset.univ : Finset (Fin 2)) ![a, b] + (by simpa [Fin.sum_univ_two] using hsum) + simpa using this 1 (Finset.mem_univ 1) + have hg0 : jeval x g = 0 := by + rcases smul_eq_zero.mp (hbdef ▸ hb0) with h | h + · exact absurd h hz + · exact h + -- and that forces `q` to be a unit + have hmg : m ∣ β * u := by + have h4 : m ∣ β * u - C c * m := by + rw [← hXg]; exact ((hm g).mp hg0).mul_left X + simpa using dvd_add h4 (⟨C c, by ring⟩ : m ∣ C c * m) + have hqm : q ∣ m := ⟨u, hu⟩ + exact hqnu (isUnit_of_dvd_one (hbez ▸ dvd_add (dvd_mul_left q α) (hqm.trans hmg))) + +end Kill + + +section Split + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] [IsFormallyReal J] [Module.Finite ℝ J] + +/-- **The split annihilator.** Some product of *distinct* linear factors, none of them `X`, +annihilates `x`. -/ +theorem exists_split_annihilator (x : J) : + ∃ S : Finset ℝ, (0 : ℝ) ∉ S ∧ jeval x (∏ a ∈ S, (X - C a)) = 0 := by + obtain ⟨m, hm0, hm⟩ := exists_annihilator_generator x + have hsq : Squarefree m := (isRadical_of_annihilator hm).squarefree hm0 + have hc0 : m.coeff 0 ≠ 0 := coeff_zero_ne_zero_of_annihilator hm hm0 + refine ⟨m.roots.toFinset, ?_, ?_⟩ + · rw [Polynomial.coeff_zero_eq_eval_zero] at hc0 + simpa [Polynomial.mem_roots', Polynomial.IsRoot.def] using fun _ => hc0 + · have hPm : (∏ a ∈ m.roots.toFinset, (X - C a)) ∣ m := + Finset.prod_dvd_of_coprime + ((Polynomial.pairwise_coprime_X_sub_C (Function.injective_id (α := ℝ))).set_pairwise _) + (fun a ha => Polynomial.dvd_iff_isRoot.mpr + (Polynomial.isRoot_of_mem_roots (Multiset.mem_toFinset.mp ha))) + obtain ⟨W, hW⟩ := hPm + have hWnr : ∀ r : ℝ, W.eval r ≠ 0 := by + intro r hr + have hmr : m.IsRoot r := by rw [hW]; simp [Polynomial.IsRoot.def, hr] + have hrS : r ∈ m.roots.toFinset := + Multiset.mem_toFinset.mpr (Polynomial.mem_roots'.mpr ⟨hm0, hmr⟩) + have hdd : (X - C r) * (X - C r) ∣ m := by + rw [hW] + exact mul_dvd_mul (Finset.dvd_prod_of_mem _ hrS) (Polynomial.dvd_iff_isRoot.mpr hr) + have hu := hsq _ hdd + rw [Polynomial.isUnit_iff_degree_eq_zero, Polynomial.degree_X_sub_C] at hu + exact one_ne_zero hu + have hWu : IsUnit W := by + by_contra hWnu + have hW0 : W ≠ 0 := by + rintro rfl + rw [mul_zero] at hW + exact hm0 hW + have hdeg : W.degree ≠ 0 := fun h => hWnu (Polynomial.isUnit_iff_degree_eq_zero.mpr h) + obtain ⟨z, hz⟩ := IsAlgClosed.exists_aeval_eq_zero ℂ W hdeg + have him : z.im ≠ 0 := by + intro h + refine hWnr z.re ?_ + have hzr : z = algebraMap ℝ ℂ z.re := by + apply Complex.ext <;> simp [h] + rw [hzr, Polynomial.aeval_algebraMap_apply_eq_algebraMap_eval] at hz + simpa using hz + exact annihilator_aeval_ne_zero_of_im_ne_zero hm hsq hc0 him (by rw [hW, map_mul, hz, mul_zero]) + obtain ⟨v, hv⟩ := hWu.exists_right_inv + refine (hm _).mpr ⟨v, ?_⟩ + calc (∏ a ∈ m.roots.toFinset, (X - C a)) + = (∏ a ∈ m.roots.toFinset, (X - C a)) * (W * v) := by rw [hv, mul_one] + _ = m * v := by rw [← mul_assoc, ← hW] + +omit [IsCommJordan J] [IsScalarTower ℝ J J] [IsFormallyReal J] [Module.Finite ℝ J] in +/-- Every value of `jeval x` lies in the subalgebra generated by `x`. -/ +theorem jeval_mem_jspan (x : J) (p : Polynomial ℝ) : jeval x p ∈ jspan x := by + change p.sum (fun n a => a • jpow x n) ∈ jspan x + rw [Polynomial.sum_def] + exact Submodule.sum_mem _ fun n _ => Submodule.smul_mem _ _ (jpow_mem_jspan x n) + +omit [IsCommJordan J] [IsFormallyReal J] [Module.Finite ℝ J] in +/-- **The vehicle.** A polynomial vanishing at every node of `S ∪ {0}` is divisible by the +nodal polynomial, and the annihilator absorbs the quotient. -/ +theorem jeval_eq_zero_of_eval_eq_zero {x : J} {S : Finset ℝ} (h0S : (0 : ℝ) ∉ S) + (hA : jeval x (∏ a ∈ S, (X - C a)) = 0) {p f : Polynomial ℝ} (hpf : X * p = f) + (hf : ∀ ν ∈ insert (0 : ℝ) S, f.eval ν = 0) : jeval x p = 0 := by + have hnodal : (∏ ν ∈ insert (0 : ℝ) S, (X - C ν)) = X * ∏ a ∈ S, (X - C a) := by + rw [Finset.prod_insert h0S, map_zero, sub_zero] + have hdvd : (∏ ν ∈ insert (0 : ℝ) S, (X - C ν)) ∣ f := + Finset.prod_dvd_of_coprime + ((Polynomial.pairwise_coprime_X_sub_C (Function.injective_id (α := ℝ))).set_pairwise _) + (fun ν hν => Polynomial.dvd_iff_isRoot.mpr (hf ν hν)) + rw [hnodal] at hdvd + obtain ⟨h, hh⟩ := hdvd + have hp : p = (∏ a ∈ S, (X - C a)) * h := by + apply mul_left_cancel₀ (Polynomial.X_ne_zero (R := ℝ)) + rw [hpf, hh, mul_assoc] + rw [hp, mul_comm] + exact jeval_mul_eq_zero hA h + +/-- **The spectral resolution, indexed by the eigenvalues.** -/ +theorem exists_orthIdem_finset (x : J) : + ∃ (S : Finset ℝ) (c : ℝ → J), (∀ a ∈ S, c a * c a = c a) ∧ + (∀ a ∈ S, ∀ a' ∈ S, a ≠ a' → c a * c a' = 0) ∧ + (∀ a, c a ∈ jspan x) ∧ x = ∑ a ∈ S, a • c a := by + classical + obtain ⟨S, h0S, hA⟩ := exists_split_annihilator x + set b : ℝ → Polynomial ℝ := fun a => Lagrange.basis (insert (0 : ℝ) S) id a with hbdef + set g : ℝ → Polynomial ℝ := fun a => (b a).divX with hgdef + have h0T : (0 : ℝ) ∈ insert (0 : ℝ) S := Finset.mem_insert_self 0 S + have hne : ∀ a ∈ S, a ≠ 0 := fun a ha h => h0S (h ▸ ha) + have hinj : Set.InjOn (id : ℝ → ℝ) (↑(insert (0 : ℝ) S) : Set ℝ) := fun _ _ _ _ h => h + have heval : ∀ a ∈ S, ∀ ν ∈ insert (0 : ℝ) S, (b a).eval ν = if ν = a then 1 else 0 := by + intro a ha ν hν + by_cases h : ν = a + · subst h + simpa [hbdef] using Lagrange.eval_basis_self hinj (Finset.mem_insert_of_mem ha) + · simpa [hbdef, h] using Lagrange.eval_basis_of_ne (v := (id : ℝ → ℝ)) (Ne.symm h) hν + have hXg : ∀ a ∈ S, b a = X * g a := by + intro a ha + have h0 : (b a).coeff 0 = 0 := by + rw [Polynomial.coeff_zero_eq_eval_zero, heval a ha 0 h0T, if_neg (Ne.symm (hne a ha))] + conv_lhs => rw [← Polynomial.X_mul_divX_add (b a)] + rw [h0, map_zero, add_zero, hgdef] + refine ⟨S, fun a => jeval x (g a), ?_, ?_, fun a => jeval_mem_jspan x (g a), ?_⟩ + · intro a ha + have key : jeval x (X * g a * g a - g a) = 0 := by + refine jeval_eq_zero_of_eval_eq_zero h0S hA (f := b a ^ 2 - b a) ?_ ?_ + · rw [hXg a ha]; ring + · intro ν hν + simp only [Polynomial.eval_sub, Polynomial.eval_pow, heval a ha ν hν] + by_cases h : ν = a <;> simp [h] + rw [map_sub, sub_eq_zero, ← jeval_mul] at key + change jeval x (g a) * jeval x (g a) = jeval x (g a) + exact key + · intro a ha a' ha' haa' + have key : jeval x (X * g a * g a') = 0 := by + refine jeval_eq_zero_of_eval_eq_zero h0S hA (f := b a * b a') ?_ ?_ + · rw [hXg a ha, hXg a' ha']; ring + · intro ν hν + simp only [Polynomial.eval_mul, heval a ha ν hν, heval a' ha' ν hν] + by_cases h : ν = a + · rw [if_pos h, if_neg (by rw [h]; exact haa'), mul_zero] + · rw [if_neg h, zero_mul] + rw [← jeval_mul] at key + change jeval x (g a) * jeval x (g a') = 0 + exact key + · have key : jeval x (1 - ∑ a ∈ S, a • g a) = 0 := by + refine jeval_eq_zero_of_eval_eq_zero h0S hA (f := X - ∑ a ∈ S, a • b a) ?_ ?_ + · rw [mul_sub, mul_one, Finset.mul_sum] + congr 1 + exact Finset.sum_congr rfl fun a ha => by rw [mul_smul_comm, ← hXg a ha] + · intro ν hν + simp only [Polynomial.eval_sub, Polynomial.eval_X, Polynomial.eval_finsetSum, + Polynomial.eval_smul, smul_eq_mul] + rcases Finset.mem_insert.mp hν with rfl | hνS + · rw [Finset.sum_eq_zero fun a ha => by + rw [heval a ha 0 hν, if_neg (Ne.symm (hne a ha)), mul_zero], sub_zero] + · rw [Finset.sum_eq_single ν + (fun a ha hane => by rw [heval a ha ν hν, if_neg (Ne.symm hane), mul_zero]) + (fun h => absurd hνS h), + heval ν hνS ν hν, if_pos rfl, mul_one, sub_self] + rw [map_sub, map_sum, jeval_one, sub_eq_zero] at key + simpa using key + +/-- **The single-element spectral theorem.** -/ +theorem spectral_resolution (x : J) : + ∃ (n : ℕ) (c : Fin n → J) (lam : Fin n → ℝ), + IsOrthIdemFamily c ∧ (∀ i, c i ∈ jspan x) ∧ x = ∑ i, lam i • c i := by + classical + obtain ⟨S, c, hidem, horth, hmem, hx⟩ := exists_orthIdem_finset x + refine ⟨S.card, fun i => c (S.equivFin.symm i), fun i => ((S.equivFin.symm i : ℝ)), + ⟨fun i => hidem _ (S.equivFin.symm i).2, fun i j hij => ?_⟩, fun i => hmem _, ?_⟩ + · refine horth _ (S.equivFin.symm i).2 _ (S.equivFin.symm j).2 fun h => hij ?_ + exact S.equivFin.symm.injective (Subtype.ext h) + · rw [hx, ← Finset.sum_coe_sort S (fun a => a • c a)] + exact (Equiv.sum_comp S.equivFin.symm (fun a : {y // y ∈ S} => (a : ℝ) • c a)).symm + +/-- **The spectral theorem with completeness**, the unit carried as an ordinary hypothesis. -/ +theorem spectral_resolution_complete (e : J) (he : ∀ y : J, e * y = y) (x : J) : + ∃ (n : ℕ) (c : Fin n → J) (lam : Fin n → ℝ), + IsOrthIdemFamily c ∧ (∑ i, c i) = e ∧ x = ∑ i, lam i • c i := by + obtain ⟨n, c, lam, hfam, _, hx⟩ := spectral_resolution x + have hss : (∑ i, c i) * (∑ i, c i) = ∑ i, c i := hfam.sum_idem Finset.univ + have hsc : ∀ k, (∑ i, c i) * c k = c k := by + intro k + rw [Finset.sum_mul, Finset.sum_eq_single k (fun j _ hjk => hfam.orth j k hjk) + (fun h => absurd (Finset.mem_univ k) h)] + exact hfam.idem k + have hes : (∑ i, c i) * e = ∑ i, c i := by rw [mul_comm, he] + have hlast : (e - ∑ i, c i) * (e - ∑ i, c i) = e - ∑ i, c i := by + rw [sub_mul, mul_sub, mul_sub, he, he, hss, hes, sub_self, sub_zero] + have horthlast : ∀ k, (e - ∑ i, c i) * c k = 0 := by + intro k + rw [sub_mul, he, hsc, sub_self] + refine ⟨n + 1, Fin.snoc c (e - ∑ i, c i), Fin.snoc lam 0, ⟨?_, ?_⟩, ?_, ?_⟩ + · intro i + induction i using Fin.lastCases with + | last => simpa using hlast + | cast i => simpa using hfam.idem i + · intro i j hij + induction i using Fin.lastCases with + | last => + induction j using Fin.lastCases with + | last => exact absurd rfl hij + | cast j => simpa using horthlast j + | cast i => + induction j using Fin.lastCases with + | last => simpa [mul_comm] using horthlast i + | cast j => + simpa using hfam.orth i j fun h => hij (by rw [h]) + · rw [Fin.sum_univ_castSucc] + simp + · rw [Fin.sum_univ_castSucc] + simpa using hx + +end Split + + +section Concrete + +open HermMul + +variable {d : Type*} [Fintype d] [DecidableEq d] {𝕜 : Type*} [RCLike 𝕜] + +/-- **The spectral theorem live on `H_d(𝕜)`.** -/ +theorem hermitian_spectral_resolution (A : HermitianMat d 𝕜) : + ∃ (n : ℕ) (c : Fin n → HermitianMat d 𝕜) (lam : Fin n → ℝ), + IsOrthIdemFamily c ∧ (∀ i, c i ∈ jspan A) ∧ A = ∑ i, lam i • c i := + spectral_resolution A + +/-- **The spectral theorem with completeness, live on `H_d(𝕜)`**: the unit is `1`. -/ +theorem hermitian_spectral_resolution_complete (A : HermitianMat d 𝕜) : + ∃ (n : ℕ) (c : Fin n → HermitianMat d 𝕜) (lam : Fin n → ℝ), + IsOrthIdemFamily c ∧ (∑ i, c i) = 1 ∧ A = ∑ i, lam i • c i := + spectral_resolution_complete 1 (fun y => by rw [mul_eq_symmMul, HermitianMat.symmMul_comm, + HermitianMat.symmMul_one]) A + +end Concrete + + +section Interface + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] + +omit [InnerProductSpace ℝ J] in +/-- Formal reality over an arbitrary `Finset`, from the `Fin k` form. The two differ only by +reindexing. -/ +theorem isFormallyReal_of_fin [Module ℝ J] (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (hcomm : ∀ x y : J, m x y = m y x) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) : + letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + IsFormallyReal J := by + letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + refine ⟨fun {ι} s f hsum i hi => ?_⟩ + classical + have key : (∑ k : Fin s.card, m (f (s.equivFin.symm k)) (f (s.equivFin.symm k))) = 0 := by + rw [show (∑ k : Fin s.card, m (f (s.equivFin.symm k)) (f (s.equivFin.symm k))) + = ∑ a : {y // y ∈ s}, m (f a) (f a) from + Equiv.sum_comp s.equivFin.symm (fun a : {y // y ∈ s} => m (f a) (f a)), + Finset.sum_coe_sort s (fun a => m (f a) (f a))] + exact hsum + simpa using hfr s.card (fun k => f (s.equivFin.symm k)) key (s.equivFin ⟨i, hi⟩) + +/-- **The spectral theorem in bilinear-map vocabulary**: the Jordan product as a bundled +bilinear map, the Jordan identity and formal reality as hypotheses in that vocabulary, and the +conclusion stated without mentioning any ring instance. + +★ This is the crossing `EuclideanJordan/Bridge.lean` was built for, and it works here for the reason that file +gives: the *statement* is expressible with `m` alone, so no ring instance has to exist before it +elaborates. Only the proof needs one, and `ringOfBilinear` supplies it on the ambient additive +group. + +★ Finite-dimensionality is not decoration. Without it the statement is false: `ℝ[X]` with +polynomial multiplication satisfies every hypothesis below and has no nonconstant spectral +resolution, its only idempotents being `0` and `1`. -/ +theorem spectral_resolution_bilinear [FiniteDimensional ℝ J] (m : J →ₗ[ℝ] J →ₗ[ℝ] J) + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (e : J) (he : ∀ y : J, m e y = y) (x : J) : + ∃ (n : ℕ) (q : Fin n → J) (lam : Fin n → ℝ), + (∀ i, m (q i) (q i) = q i) ∧ + (∀ i j, i ≠ j → m (q i) (q j) = 0) ∧ + (∑ i, q i) = e ∧ + x = ∑ i, lam i • q i := by + letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + letI : IsCommJordan J := ⟨hjordan⟩ + letI : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ + letI : IsFormallyReal J := isFormallyReal_of_fin m hcomm hfr + obtain ⟨n, q, lam, hfam, hsum, hx⟩ := spectral_resolution_complete e he x + exact ⟨n, q, lam, hfam.idem, hfam.orth, hsum, hx⟩ + +end Interface + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean new file mode 100644 index 0000000000..9724d46c78 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean @@ -0,0 +1,158 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc +import Mathlib.LinearAlgebra.Dimension.Finite + + +/-! +# The subalgebra generated by one element is associative + +This is Albert's theorem in the form it is usually *stated* — `EuclideanJordan/PowerAssoc.lean` proves the +form it is usually *proved* in, the power law `x^{m+1} ∘ x^{n+1} = x^{m+n+2}`. The two are not +the same statement: the power law is about products of powers, and this is about products of +arbitrary `ℝ`-combinations of powers. + +`jspan x` is the `ℝ`-span of `{x, x², x³, …}`. The results: + +* `mul_mem_jspan` — it is closed under the Jordan product, so it is a subalgebra; +* `jspan_assoc` — **the product is associative on it**. + +Both come from `jpow_mul_jpow` by span induction and nothing else. `jspan_assoc` needs three +nested inductions because associativity is trilinear, which is the only reason this file is +longer than its content. + +## Why it is worth stating separately + +The textbook route to the Jordan spectral theorem runs through `ℝ[x]`: one shows it is a +finite-dimensional associative commutative real algebra, that formal reality makes it reduced +(`EuclideanJordan/FormallyReal.lean`), and hence that it is `ℝ^k` — which produces the orthogonal +idempotents that `EuclideanJordan/Frame.lean` then consumes. **`jspan_assoc` is the first of those four +steps.** The remaining three — finite-dimensionality of `jspan x`, reducedness in the form the +classification wants, and the classification itself — are not built. + +★★★ **The spectral theorem is proved in `EuclideanJordan/Spectral.lean`, and the route above is +not the one it took.** The paragraph is left as written because the mis-shaping is the record. +`jspan x` is never made a ring, never reduced, never classified; the proof evaluates polynomials +into the *ambient* `J` by a linear map and does its ideal theory in `ℝ[X]`. What this file +actually supplies to that proof is `exists_jpow_relation` (the nonzero annihilator) and +`mul_mem_jspan` / `jspan_assoc` — the latter two only to place the resulting idempotents in +`jspan x`, not to build any algebra structure on it. + +★ `jspan x` is a `Submodule`, not a `NonUnitalSubalgebra`: bundling it would need the ambient +`NonUnitalNonAssocCommRing` to carry an algebra structure over `ℝ`, which this unit-free +development deliberately does not assume. The two theorems below say exactly what a bundled +version would, without the bundling. +-/ + +namespace EuclideanJordan + +section Subalgebra + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] + +/-- The `ℝ`-span of the powers of `x`: the subalgebra generated by `x`. -/ +def jspan (x : J) : Submodule ℝ J := Submodule.span ℝ (Set.range (jpow x)) + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +theorem jpow_mem_jspan (x : J) (n : ℕ) : jpow x n ∈ jspan x := + Submodule.subset_span ⟨n, rfl⟩ + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +theorem self_mem_jspan (x : J) : x ∈ jspan x := by + simpa using jpow_mem_jspan x 0 + +/-- `jspan x` is closed under the Jordan product. -/ +theorem mul_mem_jspan {x a b : J} (ha : a ∈ jspan x) (hb : b ∈ jspan x) : a * b ∈ jspan x := by + have key : ∀ n : ℕ, ∀ c ∈ jspan x, jpow x n * c ∈ jspan x := by + intro n c hc + induction hc using Submodule.span_induction with + | mem y hy => + obtain ⟨m, rfl⟩ := hy + rw [jpow_mul_jpow] + exact jpow_mem_jspan x _ + | zero => rw [mul_zero]; exact Submodule.zero_mem _ + | add u v _ _ hu hv => rw [mul_add]; exact Submodule.add_mem _ hu hv + | smul r u _ hu => rw [mul_smul_comm']; exact Submodule.smul_mem _ r hu + induction ha using Submodule.span_induction with + | mem y hy => obtain ⟨n, rfl⟩ := hy; exact key n b hb + | zero => rw [zero_mul]; exact Submodule.zero_mem _ + | add u v _ _ hu hv => rw [add_mul]; exact Submodule.add_mem _ hu hv + | smul r u _ hu => rw [smul_mul_assoc]; exact Submodule.smul_mem _ r hu + +/-- **Albert's theorem, classical form: the subalgebra generated by one element is +associative.** -/ +theorem jspan_assoc {x a b c : J} (ha : a ∈ jspan x) (hb : b ∈ jspan x) (hc : c ∈ jspan x) : + (a * b) * c = a * (b * c) := by + have h3 : ∀ i j : ℕ, ∀ z ∈ jspan x, (jpow x i * jpow x j) * z = jpow x i * (jpow x j * z) := by + intro i j z hz + induction hz using Submodule.span_induction with + | mem y hy => + obtain ⟨k, rfl⟩ := hy + rw [jpow_mul_jpow, jpow_mul_jpow, jpow_mul_jpow, jpow_mul_jpow] + congr 1 + omega + | zero => rw [mul_zero, mul_zero, mul_zero] + | add u v _ _ hu hv => rw [mul_add, mul_add, mul_add, hu, hv] + | smul r u _ hu => rw [mul_smul_comm', mul_smul_comm', mul_smul_comm', hu] + have h2 : ∀ i : ℕ, ∀ y ∈ jspan x, ∀ z ∈ jspan x, + (jpow x i * y) * z = jpow x i * (y * z) := by + intro i y hy z hz + induction hy using Submodule.span_induction with + | mem w hw => obtain ⟨j, rfl⟩ := hw; exact h3 i j z hz + | zero => rw [mul_zero, zero_mul, mul_zero] + | add u v _ _ hu hv => rw [mul_add, add_mul, hu, hv, ← mul_add, ← add_mul] + | smul r u _ hu => simp only [mul_smul_comm', smul_mul_assoc]; rw [hu] + induction ha using Submodule.span_induction with + | mem w hw => obtain ⟨i, rfl⟩ := hw; exact h2 i b hb c hc + | zero => rw [zero_mul, zero_mul, zero_mul] + | add u v _ _ hu hv => rw [add_mul, add_mul, add_mul, hu, hv] + | smul r u _ hu => rw [smul_mul_assoc, smul_mul_assoc, smul_mul_assoc, hu] + +/-! ### The finite-dimensional entry point + +★ **Both results below are linear algebra, not Jordan theory**, and their `omit` lines prove +it: neither uses `IsCommJordan`. They are recorded here because they are step 1 of the route +to the spectral theorem and because it is worth being explicit that step 1 is free — all the +difficulty is downstream, in turning an annihilating relation into idempotents. -/ + +section Finite + +variable [Module.Finite ℝ J] + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +/-- **A nontrivial annihilating relation among the powers.** In finite dimension the powers of +`x` cannot be independent, so some nonzero real combination of `x, x², …, x^{N+1}` vanishes — +the existence half of a minimal polynomial *with zero constant term*, which is the right shape +for a unit-free setting. + +★ No Jordan input: this is "an over-long family in a finite-dimensional space is dependent". -/ +theorem exists_jpow_relation (x : J) : + ∃ (n : ℕ) (c : Fin n → ℝ), (∃ i, c i ≠ 0) ∧ ∑ i, c i • jpow x i = 0 := by + have hnot : ¬ LinearIndependent ℝ (fun i : Fin (Module.finrank ℝ J + 1) => jpow x i) := by + intro h + have hle := h.fintype_card_le_finrank + simp only [Fintype.card_fin] at hle + omega + obtain ⟨c, hsum, i, hi⟩ := Fintype.not_linearIndependent_iff.mp hnot + exact ⟨_, c, ⟨i, hi⟩, hsum⟩ + +omit [IsCommJordan J] [IsScalarTower ℝ J J] in +/-- `jspan x` is finite-dimensional. Immediate — it is a submodule of a finite-dimensional +space — and recorded only so that the spectral-theorem route can cite it. -/ +instance jspan_finite (x : J) : Module.Finite ℝ (jspan x) := + Module.Finite.of_injective (jspan x).subtype (jspan x).injective_subtype + +end Finite + +end Subalgebra + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean b/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean new file mode 100644 index 0000000000..e5946ee64f --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean @@ -0,0 +1,333 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +import Mathlib.LinearAlgebra.Trace +import Mathlib.LinearAlgebra.Projection + + +/-! +# The Jordan trace form + +`τ(x, y) := tr(L_{x ∘ y})`, where `L_c` is the Jordan multiplication operator `EuclideanJordan/Peirce.lean` +carries as `mulL`. On a finite-dimensional formally real Jordan algebra this form is symmetric, +**associative** (`τ(x ∘ y, z) = τ(y, x ∘ z)`) and **positive definite** — which is to say, it makes +such an algebra Euclidean without any inner product having been supplied. + +## Why this file exists + +`EuclideanJordan/Order.lean`'s Euclidean section — `inner_mul_self_nonneg_of_idem`, `inner_left_coeff`, +`nonneg_coeff_of_isSoS`, `isArchimedean_ofBilinear`, `isSoS_iff_exists_sq` — takes the associativity +of the **ambient** inner product as a hypothesis, `hassoc : ∀ x y z, ⟪m x y, z⟫ = ⟪y, m x z⟫`. The +concrete carrier `H_n(𝕜)` discharges it by hand, through `hermitian_jordan_assoc` +(`EuclideanJordan/HermitianCarrier.lean`). ★ An *abstract* consumer generally cannot. A +structure that carries a `NormedAddCommGroup`/`InnerProductSpace` pair on `J` and a Jordan +product as an unrelated bilinear field has **no** compatibility between the two, and adding the +Jordan identity, formal reality and a cone-of-squares reading of positivity does not supply one: +associativity of the inner product is an independent assumption. So a proof that has to run +inside those hypotheses has no associative form to pair against, and every one of the six results +above is inapplicable as stated. + +This file builds the missing form *from the algebra*. Nothing here mentions a norm or an inner +product; the ambient structure is `EuclideanJordan/Peirce.lean`'s (a commutative Jordan ring that is an +`ℝ`-module), plus finite-dimensionality and formal reality where the spectral theorem is used. +A consumer working in bilinear-map vocabulary installs `EuclideanJordan/Bridge.lean`'s +`ringOfBilinear` and gets `traceForm` on the nose. + +## The associativity argument + +`tr(L_·)` is associative because the **quadrilinear** form of the Jordan identity, + +`L_{(a∘c)∘y} + L_a L_y L_c + L_c L_y L_a = L_a L_{c∘y} + L_c L_{a∘y} + L_y L_{a∘c}`, + +has a right-hand side that is symmetric under permuting `(a, c, y)` — the three pairs +`{(a, c∘y), (c, a∘y), (y, a∘c)}` are permuted among themselves and `tr(fg) = tr(gf)` — while +`tr(L_a L_y L_c) + tr(L_c L_y L_a)` is symmetric by cyclicity of the trace. Hence +`tr(L_{(a∘c)∘y})` is symmetric in `(a, c, y)`, and that *is* associativity. + +★ The quadrilinear identity itself is cheap and this was not expected. It is the difference of two +instances of Mathlib's own linearized Jordan identity +(`two_nsmul_lie_lmul_lmul_add_add_eq_zero`) with the last two arguments swapped; `jordan_linearized` +and `jordan_quadrilinear` below are the whole of it. + +## Positive definiteness + +`τ(x, x) = tr(L_{x∘x})`, and the spectral resolution `x = ∑ᵢ λᵢ qᵢ` into orthogonal idempotents +gives `x ∘ x = ∑ᵢ λᵢ² qᵢ`, so `τ(x, x) = ∑ᵢ λᵢ² tr(L_{qᵢ})`. For an idempotent `c` the split +`L_c = P₁(c) + ½ P_{1/2}(c)` writes `tr(L_c)` as a nonnegative combination of traces of *idempotent +endomorphisms*, which are ranks of their ranges; and `P₁(c) c = c`, so a nonzero `c` forces +`tr(L_c) ≥ 1`. That inequality is the whole of definiteness: a vanishing `τ(x, x)` kills every +coefficient whose idempotent is nonzero. + +★ **What this file does NOT claim.** `τ` is not shown to be *the* Jordan trace form in the sense of +Faraut–Korányi (the trace of the quadratic representation, or the sum of the eigenvalues of `x`); +for a unit `e` one has `L_e = id` and so `tr(L_e) = finrank ℝ J` rather than the rank of `J` — **a +remark, not a lemma; it is not proved below and nothing uses it.** Nothing downstream needs the +normalisation, and +`EuclideanJordan/Class.lean`'s `EuclideanJordanAlgebra` deliberately takes an *arbitrary* associative positive +definite form rather than a normalised one, so `traceForm` is admissible there as it stands. +-/ + +noncomputable section + +namespace EuclideanJordan + +open Finset LinearMap + +/-! ## The linearized and quadrilinear Jordan identities -/ + +section Linearised + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + +/-- **The multilinearized Jordan identity, elementwise.** + +Mathlib states it as `2 • ([L_{ab}, L_c] + [L_{bc}, L_a] + [L_{ca}, L_b]) = 0` in +`AddMonoid.End J`; applying that at `v`, unfolding the commutators and cancelling the `2` gives the +form below. The `have h3 : … := h2` step works because the `AddMonoid.End` ring operations are +definitionally pointwise, which saves fighting for application `simp` lemmas that do not exist +under the names one would guess. -/ +theorem jordan_linearized (a b c v : J) : + (a * b) * (c * v) + (b * c) * (a * v) + (c * a) * (b * v) + = c * ((a * b) * v) + a * ((b * c) * v) + b * ((c * a) * v) := by + have h := congrArg (fun f : AddMonoid.End J => f v) + (two_nsmul_lie_lmul_lmul_add_add_eq_zero (A := J) a b c) + simp only [Ring.lie_def] at h + have h2 := nsmul_eq_zero_iff' (J := J) (n := 2) (by norm_num) h + have h3 : (a * ((b * c) * v) - (b * c) * (a * v)) + + (b * ((c * a) * v) - (c * a) * (b * v)) + + (c * ((a * b) * v) - (a * b) * (c * v)) = 0 := h2 + linear_combination (norm := abel) -h3 + +/-- **The quadrilinear Jordan identity**, the operator identity of this file read elementwise. + +It is `jordan_linearized a c y v - jordan_linearized a c v y`: the *same* identity with its last two +arguments swapped. The `rw`s only put the two instances into a common shape by commuting +products. -/ +theorem jordan_quadrilinear (a c y v : J) : + ((a * c) * y) * v + a * (y * (c * v)) + c * (y * (a * v)) + = a * ((c * y) * v) + c * ((a * y) * v) + y * ((a * c) * v) := by + have h1 := jordan_linearized a c y v + have h2 := jordan_linearized a c v y + rw [show ((a * c) * y) * v = v * ((a * c) * y) from mul_comm _ _, + show a * (y * (c * v)) = a * ((c * v) * y) by rw [mul_comm y (c * v)], + show c * (y * (a * v)) = c * ((v * a) * y) by rw [mul_comm y (a * v), mul_comm a v], + show c * ((a * y) * v) = c * ((y * a) * v) by rw [mul_comm a y]] + rw [show (a * c) * (y * v) = (a * c) * (v * y) by rw [mul_comm y v]] at h1 + rw [show (c * y) * (a * v) = (v * a) * (c * y) by rw [mul_comm a v, mul_comm]] at h1 + rw [show (y * a) * (c * v) = (c * v) * (a * y) by rw [mul_comm y a, mul_comm]] at h1 + linear_combination (norm := abel) h1 - h2 + +end Linearised + +/-! ## `tr ∘ L` and its associativity -/ + +section Trace + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] + +/-- `EuclideanJordan/Peirce.lean`'s `mulL`, bundled as a linear map in the multiplier — which is what makes +`jtr` linear. -/ +def mulLₗ : J →ₗ[ℝ] J →ₗ[ℝ] J where + toFun := mulL + map_add' a b := by ext y; simp only [mulL_apply, LinearMap.add_apply, add_mul] + map_smul' r a := by + ext y + simp only [mulL_apply, LinearMap.smul_apply, RingHom.id_apply, smul_mul_assoc] + +omit [IsCommJordan J] in +@[simp] theorem mulLₗ_apply (a : J) : mulLₗ a = mulL a := rfl + +/-- **The operator form of the quadrilinear identity**, in `Module.End ℝ J`. + +`L_{(a∘c)∘y} + L_a L_y L_c + L_c L_y L_a = L_a L_{c∘y} + L_c L_{a∘y} + L_y L_{a∘c}`. -/ +theorem mulL_quad_op (a c y : J) : + mulL ((a * c) * y) + mulL a * mulL y * mulL c + mulL c * mulL y * mulL a + = mulL a * mulL (c * y) + mulL c * mulL (a * y) + mulL y * mulL (a * c) := by + ext v + simp only [LinearMap.add_apply, Module.End.mul_apply, mulL_apply] + exact jordan_quadrilinear a c y v + +/-- **The Jordan trace functional** `x ↦ tr(L_x)`, as an `ℝ`-linear form. + +It is not normalised — see the module docstring. ★ The carrier here has no `1` +(`NonUnitalNonAssocCommRing`), so the normalisation cannot even be *stated* at this generality; +where a unit `e` is available it is `L_e = id`, so `jtr e` would be `finrank ℝ J` rather than the +rank. That is a remark, not a lemma: nothing below proves it and nothing needs it. -/ +def jtr : J →ₗ[ℝ] ℝ := (LinearMap.trace ℝ J).comp mulLₗ + +omit [IsCommJordan J] in +@[simp] theorem jtr_apply (x : J) : jtr x = LinearMap.trace ℝ J (mulL x) := rfl + +/-- **`tr(L_·)` is associative.** Trace the operator identity twice, at `(a, c, y)` and at +`(c, y, a)`; the two right-hand sides agree term by term after commuting products, and the two +triple-trace sums agree by cyclicity, so the two remaining terms agree. + +★ Finite-dimensionality is not needed: `LinearMap.trace` is total (it is `0` on a module with no +finite basis) and `trace_mul_cycle` is unconditional. It enters two theorems below, where the +trace of an idempotent endomorphism has to be its rank. -/ +theorem jtr_assoc (a c y : J) : jtr ((a * c) * y) = jtr (a * (c * y)) := by + have h1 := congrArg (LinearMap.trace ℝ J) (mulL_quad_op a c y) + have h2 := congrArg (LinearMap.trace ℝ J) (mulL_quad_op c y a) + simp only [map_add] at h1 h2 + rw [show a * (c * y) = (c * y) * a from mul_comm _ _] + rw [show y * a = a * y from mul_comm _ _, show c * a = a * c from mul_comm _ _] at h2 + have e1 : trace ℝ J (mulL c * mulL a * mulL y) = trace ℝ J (mulL a * mulL y * mulL c) := + (trace_mul_cycle ℝ (mulL a) (mulL y) (mulL c)).symm + have e2 : trace ℝ J (mulL y * mulL a * mulL c) = trace ℝ J (mulL c * mulL y * mulL a) := + trace_mul_cycle ℝ (mulL y) (mulL a) (mulL c) + simp only [jtr_apply] at * + linarith [h1, h2, e1, e2] + +variable [Module.Finite ℝ J] + +/-- **The trace of `L_c` is nonnegative for an idempotent `c`.** + +`L_c = P₁(c) + ½ P_{1/2}(c)`, both Peirce projections are idempotent *endomorphisms*, and the trace +of an idempotent endomorphism is the rank of its range. -/ +theorem jtr_nonneg_of_idem {c : J} (hc : c * c = c) : 0 ≤ jtr c := by + have hid1 : IsIdempotentElem (peirceOne c) := by + ext y; exact peirceOne_of_eigen (mul_peirceOne hc y) + have hidh : IsIdempotentElem (peirceHalf c) := by + ext y; exact peirceHalf_of_eigen_half (mul_peirceHalf hc y) + have hsplit : mulL c = peirceOne c + (2 : ℝ)⁻¹ • peirceHalf c := by + ext y + simp only [mulL_apply, LinearMap.add_apply, LinearMap.smul_apply, peirceOne_apply, + peirceHalf_apply] + module + rw [jtr_apply, hsplit, map_add, LinearMap.map_smul, hid1.isProj_range.trace, + hidh.isProj_range.trace] + have h1 : (0 : ℝ) ≤ (Module.finrank ℝ (LinearMap.range (peirceOne c)) : ℝ) := Nat.cast_nonneg _ + have h2 : (0 : ℝ) ≤ (Module.finrank ℝ (LinearMap.range (peirceHalf c)) : ℝ) := Nat.cast_nonneg _ + simp only [smul_eq_mul] + linarith + +/-- **The trace of `L_c` is at least one for a nonzero idempotent `c`** — the whole of positive +definiteness. `P₁(c) c = c`, so `c` lies in the range of `P₁(c)`; a nonzero `c` makes that range +nonzero, hence of rank at least one. -/ +theorem one_le_jtr_of_idem {c : J} (hc : c * c = c) (hc0 : c ≠ 0) : 1 ≤ jtr c := by + have hid1 : IsIdempotentElem (peirceOne c) := by + ext y; exact peirceOne_of_eigen (mul_peirceOne hc y) + have hidh : IsIdempotentElem (peirceHalf c) := by + ext y; exact peirceHalf_of_eigen_half (mul_peirceHalf hc y) + have hsplit : mulL c = peirceOne c + (2 : ℝ)⁻¹ • peirceHalf c := by + ext y + simp only [mulL_apply, LinearMap.add_apply, LinearMap.smul_apply, peirceOne_apply, + peirceHalf_apply] + module + have hmem : c ∈ LinearMap.range (peirceOne c) := ⟨c, peirceOne_of_eigen (by rw [hc])⟩ + have hne : LinearMap.range (peirceOne c) ≠ ⊥ := fun h => hc0 (by simpa [h] using hmem) + have hpos : 0 < Module.finrank ℝ (LinearMap.range (peirceOne c)) := + Module.finrank_pos_iff.mpr (by rw [Submodule.nontrivial_iff_ne_bot]; exact hne) + rw [jtr_apply, hsplit, map_add, LinearMap.map_smul, hid1.isProj_range.trace, + hidh.isProj_range.trace] + have h1 : (1 : ℝ) ≤ (Module.finrank ℝ (LinearMap.range (peirceOne c)) : ℝ) := by + exact_mod_cast hpos + have h2 : (0 : ℝ) ≤ (Module.finrank ℝ (LinearMap.range (peirceHalf c)) : ℝ) := Nat.cast_nonneg _ + simp only [smul_eq_mul] + linarith + +end Trace + +/-! ## The form itself -/ + +section Form + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] + +/-- **The Jordan trace form** `τ(x, y) = tr(L_{x ∘ y})`. -/ +def traceForm : J →ₗ[ℝ] J →ₗ[ℝ] ℝ := + LinearMap.mk₂ ℝ (fun x y => jtr (x * y)) + (fun x x' y => by rw [add_mul, map_add]) + (fun r x y => by rw [smul_mul_assoc, map_smul, smul_eq_mul]) + (fun x y y' => by rw [mul_add, map_add]) + (fun r x y => by rw [mul_smul_comm' r x y, map_smul, smul_eq_mul]) + +omit [IsCommJordan J] in +@[simp] theorem traceForm_apply (x y : J) : traceForm x y = jtr (x * y) := rfl + +omit [IsCommJordan J] in +theorem traceForm_comm (x y : J) : traceForm x y = traceForm y x := by + simp only [traceForm_apply, mul_comm] + +/-- **The trace form is associative**: `τ(x ∘ y, z) = τ(y, x ∘ z)`. This is the hypothesis +`hassoc` that `EuclideanJordan/Order.lean`'s Euclidean section and `EuclideanJordan/Class.lean`'s class both take, now a +theorem about a form built from the algebra alone. -/ +theorem traceForm_assoc (x y z : J) : traceForm (x * y) z = traceForm y (x * z) := by + simp only [traceForm_apply] + rw [show x * y = y * x from mul_comm _ _] + exact jtr_assoc y x z + +end Form + +/-! ## Positive definiteness -/ + +section PosDef + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] + [IsScalarTower ℝ J J] [IsFormallyReal J] [Module.Finite ℝ J] + +omit [IsCommJordan J] [IsFormallyReal J] [Module.Finite ℝ J] in +/-- The square of a diagonal element is diagonal with squared coefficients. Orthogonality kills +every cross term. -/ +theorem sq_of_orthIdem {n : ℕ} {q : Fin n → J} (hq : IsOrthIdemFamily q) {lam : Fin n → ℝ} + {x : J} (hx : x = ∑ i, lam i • q i) : + x * x = ∑ i, (lam i * lam i) • q i := by + classical + rw [hx, Finset.sum_mul] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [Finset.mul_sum, Finset.sum_eq_single i] + · rw [smul_mul_assoc, mul_smul_comm', smul_smul, hq.idem i] + · intro j _ hji + rw [smul_mul_assoc, mul_smul_comm', smul_smul, hq.orth i j (Ne.symm hji), smul_zero] + · intro h + exact absurd (Finset.mem_univ i) h + +/-- **`τ(x, x) ≥ 0`.** The spectral resolution turns `x ∘ x` into a nonnegative combination of +idempotents, and `jtr` is nonnegative on each of them. -/ +theorem traceForm_self_nonneg (x : J) : 0 ≤ traceForm x x := by + obtain ⟨n, q, lam, hfam, _, hx⟩ := spectral_resolution x + rw [traceForm_apply, sq_of_orthIdem hfam hx, map_sum] + refine Finset.sum_nonneg fun i _ => ?_ + rw [map_smul, smul_eq_mul] + exact mul_nonneg (mul_self_nonneg _) (jtr_nonneg_of_idem (hfam.idem i)) + +/-- **`τ` is definite.** A vanishing `τ(x, x) = ∑ᵢ λᵢ² tr(L_{qᵢ})` forces `λᵢ = 0` for every +`qᵢ ≠ 0`, because `tr(L_{qᵢ}) ≥ 1` there; and the terms with `qᵢ = 0` vanish anyway. -/ +theorem eq_zero_of_traceForm_self_eq_zero {x : J} (h : traceForm x x = 0) : x = 0 := by + classical + obtain ⟨n, q, lam, hfam, _, hx⟩ := spectral_resolution x + have hterm : ∀ i : Fin n, 0 ≤ (lam i * lam i) * jtr (q i) := fun i => + mul_nonneg (mul_self_nonneg _) (jtr_nonneg_of_idem (hfam.idem i)) + have hsum : (∑ i, (lam i * lam i) * jtr (q i)) = 0 := by + rw [traceForm_apply, sq_of_orthIdem hfam hx, map_sum] at h + simpa only [map_smul, smul_eq_mul] using h + have hzero : ∀ i : Fin n, (lam i * lam i) * jtr (q i) = 0 := fun i => + (Finset.sum_eq_zero_iff_of_nonneg fun j _ => hterm j).mp hsum i (Finset.mem_univ i) + rw [hx] + refine Finset.sum_eq_zero fun i _ => ?_ + by_cases hq0 : q i = 0 + · rw [hq0, smul_zero] + · have hpos : (0 : ℝ) < jtr (q i) := lt_of_lt_of_le zero_lt_one (one_le_jtr_of_idem (hfam.idem i) hq0) + have : lam i * lam i = 0 := by + rcases mul_eq_zero.mp (hzero i) with h' | h' + · exact h' + · exact absurd h' (ne_of_gt hpos) + rw [mul_self_eq_zero.mp this, zero_smul] + +/-- `τ(x, x) = 0 ↔ x = 0`. -/ +theorem traceForm_self_eq_zero_iff (x : J) : traceForm x x = 0 ↔ x = 0 := + ⟨eq_zero_of_traceForm_self_eq_zero, fun h => by rw [h]; simp⟩ + +end PosDef + +end EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean new file mode 100644 index 0000000000..05c8ec30d9 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean @@ -0,0 +1,20 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ + +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic + +/-! +# Vendor + +Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. +-/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/ContinuousLinearMap.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/ContinuousLinearMap.lean new file mode 100644 index 0000000000..e1e96b3d02 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/ContinuousLinearMap.lean @@ -0,0 +1,73 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Analysis.InnerProductSpace.Spectrum +public import Mathlib.Order.CompletePartialOrder + +/-! +# Continuous linear maps + +This file collects auxiliary results about `ContinuousLinearMap`s that are not (yet) available in +Mathlib. + +## Main results + +* `ContinuousLinearMap.ker_mk`: the kernel of the continuous linear map built from a continuous + semilinear map `f` agrees with the kernel of `f`. +* `ContinuousLinearMap.support_eq_sup_eigenspace_nonzero`: the range of a symmetric continuous + linear map on a finite-dimensional Euclidean space is the supremum of its eigenspaces for + nonzero eigenvalues. +-/ + +@[expose] public section + +namespace ContinuousLinearMap + +variable {R S : Type*} [Semiring R] [Semiring S] (σ : R →+* S) (M M₂ : Type*) +variable [TopologicalSpace M] [AddCommMonoid M] [TopologicalSpace M₂] [AddCommMonoid M₂] +variable [Module R M] [Module S M₂] + +theorem ker_mk (f : M →ₛₗ[σ] M₂) (hf : Continuous f.toFun) : + (ContinuousLinearMap.mk f hf).ker = LinearMap.ker f := by + rfl + +end ContinuousLinearMap + +namespace ContinuousLinearMap + +variable {n 𝕜 : Type*} [Fintype n] [RCLike 𝕜] + +/-- The support of a Hermitian matrix is the sum of its nonzero eigenspaces. -/ +theorem support_eq_sup_eigenspace_nonzero (A : EuclideanSpace 𝕜 n →L[𝕜] EuclideanSpace 𝕜 n) + (hA : A.IsSymmetric) : A.range = ⨆ μ ≠ 0, Module.End.eigenspace A μ := by + apply le_antisymm + · rintro x ⟨y, hy⟩ + have h_decomp : y ∈ ⨆ (μ : 𝕜), Module.End.eigenspace A.toLinearMap μ := by + have h_orth := hA.orthogonalComplement_iSup_eigenspaces_eq_bot + rw [Submodule.orthogonal_eq_bot_iff] at h_orth + rw [h_orth] + exact Submodule.mem_top; + rw [Submodule.mem_iSup_iff_exists_finsupp] at h_decomp + rcases h_decomp with ⟨f, hf₁, hf₂⟩ + have h_apply_A : A y = ∑ i ∈ f.support, A (f i) := by + rw [← hf₂, map_finsuppSum] + exact rfl + have h_eigen (i) : A (f i) = (i : 𝕜) • f i := + Module.End.mem_eigenspace_iff.mp (hf₁ i) + rw [← hy, coe_coe, h_apply_A, Finset.sum_congr rfl (fun i _ ↦ h_eigen i)] + refine Submodule.sum_mem _ fun i _ ↦ ?_ + by_cases hi0 : i = 0 + · simp [hi0] + · apply Submodule.smul_mem + apply Submodule.mem_iSup_of_mem i + exact Submodule.mem_iSup_of_mem hi0 (hf₁ i) + · simp only [iSup_le_iff] + intro μ hμ x hx + use μ⁻¹ • x + simp_all + +end ContinuousLinearMap diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean new file mode 100644 index 0000000000..dac99afcfc --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean @@ -0,0 +1,21 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ + +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace + +/-! +# HermitianMat + +Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. +-/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean new file mode 100644 index 0000000000..b3d4727977 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean @@ -0,0 +1,771 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes + + +@[expose] public section + +/-- The type of Hermitian matrices, as a `Subtype`. Equivalent to a `Matrix n n α` bundled +with the fact that `Matrix.IsHermitian`. -/ +def HermitianMat (n : Type*) (α : Type*) [AddGroup α] [StarAddMonoid α] := + (selfAdjoint (Matrix n n α) : Type (max u_1 u_2)) + +namespace HermitianMat + +variable {α R 𝕜 : Type*} {m n : Type*} +variable [Star R] [TrivialStar R] +variable [RCLike 𝕜] + +section addgroup + +variable [AddGroup α] [StarAddMonoid α] + +theorem eq_IsHermitian : HermitianMat n α = { m : Matrix n n α // m.IsHermitian} := by + rfl + +@[coe] def mat : HermitianMat n α → Matrix n n α := + Subtype.val + +instance : Coe (HermitianMat n α) (Matrix n n α) := ⟨mat⟩ + +@[simp] +theorem val_eq_coe (A : HermitianMat n α) : A.val = A := by + rfl + +@[simp] +theorem mat_mk (x : Matrix n n α) (h) : mat ⟨x, h⟩ = x := by + rfl + +@[simp] +theorem mk_mat {A : HermitianMat n α} (h : A.mat.IsHermitian) : ⟨A.mat, h⟩ = A := by + rfl + +/-- Alias for HermitianMat.property or HermitianMat.2, this gets the fact that the value + is actually `IsHermitian`.-/ +theorem H (A : HermitianMat n α) : A.mat.IsHermitian := + A.2 + +@[ext] protected theorem ext {A B : HermitianMat n α} : A.mat = B.mat → A = B := + Subtype.ext + +instance instFun : FunLike (HermitianMat n α) n (n → α) where + coe M := (M : Matrix n n α) + coe_injective _ _ h := HermitianMat.ext h + +@[simp] +theorem mat_apply {A : HermitianMat n α} {i j : n} : A.mat i j = A i j := by + rfl + +@[simp] +theorem conjTranspose_mat (A : HermitianMat n α) : + A.mat.conjTranspose = A.mat := + A.H + +instance : AddGroup (HermitianMat n α) := + AddSubgroup.toAddGroup _ + +instance [IsEmpty n] : Unique (HermitianMat n α) where + default := 0 + uniq a := by ext; exact (IsEmpty.false ‹_›).elim + +@[simp, norm_cast] +theorem mat_zero : (0 : HermitianMat n α).mat = 0 := by + rfl + +@[simp] +theorem mk_zero (h : (0 : Matrix n n α).IsHermitian) : ⟨0, h⟩ = (0 : HermitianMat n α) := by + rfl + +@[simp] +theorem zero_apply (i j : n) : (0 : HermitianMat n 𝕜) i j = 0 := by + rfl + +@[simp, norm_cast] +theorem mat_add (A B : HermitianMat n α) : + (A + B).mat = A.mat + B.mat := by + rfl + +@[simp, norm_cast] +theorem mat_sub (A B : HermitianMat n α) : + (A - B).mat = A.mat - B.mat := by + rfl + +@[simp, norm_cast] +theorem mat_neg (A : HermitianMat n α) : + (-A).mat = -A.mat := by + rfl + +section smul +variable [SMul R α] [StarModule R α] + +instance : SMul R (HermitianMat n α) := + ⟨fun c A ↦ ⟨c • A.mat, (IsSelfAdjoint.all _).smul A.H⟩⟩ + +@[simp, norm_cast] +theorem mat_smul (c : R) (A : HermitianMat n α) : + (c • A).mat = c • A.mat := by + rfl + +@[simp] +theorem smul_apply (c : R) (A : HermitianMat n α) (i j : n) : + (c • A) i j = c • A i j := by + rfl +end smul +section topology + +variable [TopologicalSpace α] + +instance : TopologicalSpace (HermitianMat n α) := + inferInstanceAs (TopologicalSpace (selfAdjoint _)) + +/- Amusingly, if we don't tag this fun_prop, then fun_prop fails to prove other things! Because +it will look through and see that `HermitianMat.mat` is `Subtype.val` *here*, but not in downstream +applications of the tactic. -/ +@[fun_prop] +theorem continuous_mat : Continuous (HermitianMat.mat : HermitianMat n α → Matrix n n α) := by + fun_prop + +lemma continuousOn_iff_coe {X : Type*} [TopologicalSpace X] {s : Set X} + (f : X → HermitianMat n α) : + ContinuousOn f s ↔ ContinuousOn (fun x => (f x).mat) s := by + constructor + · intro; fun_prop + · intro h + rw [continuousOn_iff_continuous_domRestrict] at * + apply Continuous.subtype_mk h + +variable [IsTopologicalAddGroup α] + +--In principle, ContinuousAdd and ContinuousNeg just need corresponding instances for α, +-- not all of IsTopologicalAddGroup. + +instance : ContinuousAdd (HermitianMat n α) := + inferInstanceAs (ContinuousAdd (selfAdjoint _)) + +instance : ContinuousNeg (HermitianMat n α) := + inferInstanceAs (ContinuousNeg (selfAdjoint _)) + +instance : IsTopologicalAddGroup (HermitianMat n α) where + +variable [TopologicalSpace R] [SMul R α] [ContinuousSMul R α] [StarModule R α] + +instance : ContinuousSMul R (HermitianMat n α) where + continuous_smul := by + rw [continuous_induced_rng] + fun_prop + +--Shorcut instances: +instance : IsTopologicalAddGroup (HermitianMat n 𝕜) := inferInstance + +instance : ContinuousSMul ℝ (HermitianMat n ℂ) := inferInstance + +--TODO: Would be good to figure out the general (not just RCLike) version of this. +instance : T3Space (HermitianMat n 𝕜) := + inferInstanceAs (T3Space (selfAdjoint _)) + +end topology + +section mulAction +variable [Monoid R] [MulAction R α] [StarModule R α] + +instance : MulAction R (HermitianMat n α) := + Function.Injective.mulAction Subtype.val Subtype.coe_injective mat_smul + +end mulAction +end addgroup +section addcommgroup + +variable [AddCommGroup α] [StarAddMonoid α] + +instance : AddCommGroup (HermitianMat n α) := + AddSubgroup.toAddCommGroup _ + +@[simp, norm_cast] +theorem mat_finset_sum (f : ι → HermitianMat n α) (s : Finset ι) : + (∑ i ∈ s, f i).mat = ∑ i ∈ s, (f i).mat := by + apply AddSubgroup.val_finsetSum + +section module + +variable [Semiring R] [Module R α] [StarModule R α] + +instance : Module R (HermitianMat n α) := + inferInstanceAs (Module R (selfAdjoint (Matrix n n α))) + +variable [TopologicalSpace α] + +/-- The projection from HermitianMat to Matrix, as a continuous linear map. -/ +@[simps] +def matₗ : HermitianMat n α →L[R] Matrix n n α where + toFun := mat + cont := by fun_prop + map_add' := by simp + map_smul' := by simp + +end module +end addcommgroup +section ring + +variable [NonAssocRing α] [StarRing α] [DecidableEq n] + +instance : One (HermitianMat n α) := + ⟨1, by + simp [selfAdjoint.mem_iff, ← Matrix.ext_iff, + Matrix.one_apply, apply_ite (β := α), eq_comm]⟩ + +@[simp, norm_cast] +theorem mat_one : (1 : HermitianMat n α).mat = 1 := by + rfl + +@[simp] +theorem mk_one (h : (1 : Matrix n n α).IsHermitian) : ⟨1, h⟩ = (1 : HermitianMat n α) := by + rfl + +@[simp] +theorem one_apply (i j : n) : (1 : HermitianMat n α) i j = (1 : Matrix n n α) i j := by + rfl + +noncomputable instance : AddCommMonoidWithOne (HermitianMat n 𝕜) where + +instance [i : Nonempty n] : NeZero (1 : HermitianMat n 𝕜) := by + constructor + intro h + simpa using congr($h i.some i.some) + +end ring +section commring + +variable [CommRing α] [StarRing α] [DecidableEq m] [Fintype m] +variable (A : HermitianMat m α) (n : ℕ) (z : ℤ) + +noncomputable instance instInv : Inv (HermitianMat m α) := + ⟨fun x ↦ ⟨x⁻¹, x.H.inv⟩⟩ + +@[simp, norm_cast] +theorem mat_inv : (A⁻¹).mat = A.mat⁻¹ := by + rfl + +@[simp] +theorem zero_inv : ((0 : HermitianMat m α)⁻¹) = 0 := by + ext1; simp + +@[simp] +theorem one_inv : ((1 : HermitianMat m α)⁻¹) = 1 := by + ext1; simp + +noncomputable instance instPow : Pow (HermitianMat m α) ℕ := + ⟨fun x n ↦ ⟨x ^ n, x.H.pow n⟩⟩ + +@[simp, norm_cast] +theorem mat_pow (n : ℕ) : (A ^ n).mat = A.mat ^ n := by + rfl + +@[simp] +theorem pow_zero : A ^ 0 = 1 := by + ext1; simp + +@[simp] +theorem zero_pow (hn : n ≠ 0): (0 : HermitianMat m α) ^ n = 0 := by + ext1; simp [hn] + +@[simp] +theorem one_pow : ((1 : HermitianMat m α) ^ n) = 1 := by + ext1; simp + +noncomputable instance instZPow : Pow (HermitianMat m α) ℤ := + ⟨fun x z ↦ ⟨x ^ z, x.H.zpow z⟩⟩ + +@[simp] +theorem mat_zpow (z : ℤ) : (A ^ z).mat = A.mat ^ z := by + rfl + +@[simp, norm_cast] +theorem zpow_natCast : A ^ (n : ℤ) = A ^ n := by + rfl + +@[simp] +theorem zpow_zero : A ^ (0 : ℤ) = 1 := by + ext1; simp + +@[simp] +theorem zpow_one : A ^ (1 : ℤ) = A := by + ext1; simp + +@[simp] +theorem one_zpow : ((1 : HermitianMat m α) ^ z) = 1 := by + ext1; simp + +@[simp] +theorem zpow_neg_one : A ^ (-1 : ℤ) = A⁻¹ := by + ext1; exact A.mat.zpow_neg_one + +@[simp] +theorem inv_zpow : A⁻¹ ^ z = (A ^ z)⁻¹ := by + ext1; exact A.mat.inv_zpow z + +add_aesop_rules safe norm (rule_sets := [Commutes]) + [mat_zero, mat_one, mat_smul, mat_add, mat_sub, mat_neg, mat_pow, mat_zpow, mat_inv] + +@[aesop safe apply (rule_sets := [Commutes])] +theorem _root_.Matrix.inv_commute {α : Type*} {A : Matrix m m α} [CommRing α] : Commute A⁻¹ A := by + rcases A.nonsing_inv_cancel_or_zero with h | h + · simp [Commute, SemiconjBy, h] + . simp [h] + +@[aesop safe apply (rule_sets := [Commutes])] +theorem commute_inv_self : Commute A⁻¹.mat A.mat := by + commutes + +@[aesop safe apply (rule_sets := [Commutes])] +theorem commute_self_inv : Commute A.mat A⁻¹.mat := by + commutes + +end commring +section rclike + +variable [Finite n] in +instance FiniteDimensional : FiniteDimensional ℝ (HermitianMat n 𝕜) := + FiniteDimensional.finiteDimensional_submodule (selfAdjoint.submodule ℝ (Matrix n n 𝕜)) + +@[simp] +theorem im_diag_eq_zero (A : HermitianMat n 𝕜) (x : n) : + RCLike.im (A x x) = 0 := by + simpa [CharZero.eq_neg_self_iff] using congrArg (RCLike.im <| · x x) A.H.symm + +--Repeat it explicitly for Complex.im so that simp can find it +@[simp] +theorem complex_im_eq_zero (A : HermitianMat n ℂ) (x : n) : + (A x x).im = 0 := + A.im_diag_eq_zero x + +end rclike + +section conj + +variable [CommRing α] [StarRing α] [Fintype n] +variable (A : HermitianMat n α) + +/-- The Hermitian matrix given by conjugating by a (possibly rectangular) Matrix. If we required `B` to be +square, this would apply to any `Semigroup`+`StarMul` (as proved by `IsSelfAdjoint.conjugate`). But this lets +us conjugate to other sizes too, as is done in e.g. Kraus operators. That is, it's a _heterogeneous_ conjguation. +-/ +def conj {m} (B : Matrix m n α) : HermitianMat n α →+ HermitianMat m α where + toFun A := + ⟨B * A.mat * B.conjTranspose, by + ext + simp only [Matrix.star_apply, Matrix.mul_apply, Matrix.conjTranspose_apply, Finset.sum_mul, + star_sum, star_mul', star_star, show ∀ (a b : n), star (A.mat b a) = A.mat a b from congrFun₂ A.property] + rw [Finset.sum_comm] + congr! 2 + ring⟩ + map_add' _ _ := by ext1; simp [Matrix.mul_add, Matrix.add_mul] + map_zero' := by simp + +theorem conj_apply (B : Matrix m n α) (A : HermitianMat n α) : + conj B A = ⟨B * A.mat * B.conjTranspose, (conj B A).2⟩ := by + rfl + +@[simp] +theorem conj_apply_mat (B : Matrix m n α) (A : HermitianMat n α) : + (A.conj B).mat = B * A.mat * B.conjTranspose := by + rfl + +theorem conj_conj {m l} [Fintype m] (B : Matrix m n α) (C : Matrix l m α) : + (A.conj B).conj C = A.conj (C * B) := by + ext1 + simp [Matrix.conjTranspose_mul, Matrix.mul_assoc] + +variable (B : HermitianMat n α) + +@[simp] +theorem conj_zero [DecidableEq n] : A.conj (0 : Matrix m n α) = 0 := by + simp [conj_apply] + +@[simp] +theorem conj_one [DecidableEq n] : A.conj 1 = A := by + simp [conj_apply] + +@[simp] +lemma conj_one_unitary [DecidableEq n] (U : Matrix.unitaryGroup n α) : + conj U.val 1 = 1 := by + ext1 + have h : U * U.val.conjTranspose = 1 := U.prop.2 + simp [h] + +variable (R : Type*) [Star R] [TrivialStar R] [CommSemiring R] [Algebra R α] [StarModule R α] + +/-- `HermitianMat.conj` as an `R`-linear map, where `R` is the ring of relevant reals. -/ +def conjLinear {m} (B : Matrix m n α) : HermitianMat n α →ₗ[R] HermitianMat m α where + toAddHom := conj B + map_smul' _ _ := by + ext1 + simp + +@[simp] +theorem conjLinear_apply (B : Matrix m n α) : conjLinear R B A = conj B A := by + rfl + +@[fun_prop] +lemma continuous_conj (ρ : HermitianMat n 𝕜) : Continuous (ρ.conj (m := m) ·) := by + simp only [HermitianMat.conj, AddMonoidHom.coe_mk, ZeroHom.coe_mk] + fun_prop + +end conj + +section eigenspace + +variable [Fintype n] [DecidableEq n] (A : HermitianMat n 𝕜) + +instance [i : Nonempty n] : FaithfulSMul ℝ (HermitianMat n 𝕜) where + eq_of_smul_eq_smul h := by + simpa [RCLike.smul_re, -mat_apply] using congr(RCLike.re ($(h 1).val i.some i.some)) + +/-- The continuous linear map associated with a Hermitian matrix. -/ +noncomputable def lin : EuclideanSpace 𝕜 n →L[𝕜] EuclideanSpace 𝕜 n where + toLinearMap := A.mat.toEuclideanLin + cont := LinearMap.continuous_of_finiteDimensional _ + +@[simp] +theorem isSymmetric : A.lin.IsSymmetric := + Matrix.isSymmetric_toEuclideanLin_iff.symm.mp A.H + +@[simp] +theorem lin_zero : (0 : HermitianMat n 𝕜).lin = 0 := by + simp [lin]; rfl + +@[simp] +theorem lin_one : (1 : HermitianMat n 𝕜).lin = 1 := by + simp [lin]; rfl + +noncomputable def eigenspace (μ : 𝕜) : Submodule 𝕜 (EuclideanSpace 𝕜 n) := + Module.End.eigenspace A.lin μ + +/-- The kernel of a Hermitian matrix `A` as a submodule of Euclidean space, defined by +`LinearMap.ker A.toMat.toEuclideanLin`. Equivalently, the zero-eigenspace. -/ +noncomputable def ker : Submodule 𝕜 (EuclideanSpace 𝕜 n) := + LinearMap.ker A.lin.toLinearMap + +theorem mem_ker_iff_mulVec_zero (x : EuclideanSpace 𝕜 n) : x ∈ A.ker ↔ A.mat.mulVec x = 0 := by + simp [ker, LinearMap.mem_ker, lin, Matrix.toLpLin_apply] + +/-- The kernel of a Hermitian matrix is its zero eigenspace. -/ +theorem ker_eq_eigenspace_zero : A.ker = A.eigenspace 0 := by + ext + simp [ker, eigenspace] + +@[simp] +theorem ker_zero : (0 : HermitianMat n 𝕜).ker = ⊤ := by + simp [ker] + +@[simp] +theorem ker_one : (1 : HermitianMat n 𝕜).ker = ⊥ := by + simp [ker]; rfl + +theorem ker_pos_smul {c : ℝ} (hc : c ≠ 0) : (c • A).ker = A.ker := by + ext x + simp [mem_ker_iff_mulVec_zero, Matrix.smul_mulVec, hc] + +/-- The support of a Hermitian matrix `A` as a submodule of Euclidean space, defined by +`LinearMap.range A.toMat.toEuclideanLin`. Equivalently, the sum of all nonzero eigenspaces. -/ +noncomputable def support : Submodule 𝕜 (EuclideanSpace 𝕜 n) := + LinearMap.range A.lin.toLinearMap + +/-- The support of a Hermitian matrix is the sum of its nonzero eigenspaces. -/ +theorem support_eq_sup_eigenspace_nonzero : A.support = ⨆ μ ≠ 0, A.eigenspace μ := by + exact A.lin.support_eq_sup_eigenspace_nonzero A.isSymmetric + +@[simp] +theorem support_zero : (0 : HermitianMat n 𝕜).support = ⊥ := by + simp [support] + +@[simp] +theorem support_one : (1 : HermitianMat n 𝕜).support = ⊤ := by + simpa [support] using LinearMap.ker_eq_bot_iff_range_eq_top.mp rfl + +@[simp] +theorem ker_orthogonal_eq_support : A.kerᗮ = A.support := by + rw [ker, support] + convert ContinuousLinearMap.orthogonal_ker A.lin + simp + +@[simp] +theorem support_orthogonal_eq_range : A.supportᗮ = A.ker := by + rw [ker, support] + convert! ContinuousLinearMap.orthogonal_range A.lin + simp + +end eigenspace + +section diagonal + +variable {𝕜 : Type*} [RCLike 𝕜] [DecidableEq n] + +variable (𝕜) in +def diagonal (f : n → ℝ) : HermitianMat n 𝕜 := + ⟨Matrix.diagonal (f ·), + by simp [selfAdjoint.mem_iff, Matrix.star_eq_conjTranspose, Matrix.diagonal_conjTranspose]⟩ + +variable (f g : n → ℝ) + +@[simp] +theorem diagonal_mat : (diagonal 𝕜 f).mat = Matrix.diagonal (f · : n → 𝕜) := by + rfl + +@[simp] +theorem diagonal_zero : (diagonal 𝕜 0) = (0 : HermitianMat n 𝕜) := by + ext1; simp + +@[simp] +theorem diagonal_one : (diagonal 𝕜 1) = (1 : HermitianMat n 𝕜) := by + ext; rw [diagonal_mat]; simp + +lemma diagonal_add : diagonal 𝕜 (f + g) = diagonal 𝕜 f + diagonal 𝕜 g := by + ext1; simp + +lemma diagonal_add_apply : diagonal 𝕜 (fun x ↦ f x + g x) = diagonal 𝕜 f + diagonal 𝕜 g := by + ext1; simp + +lemma diagonal_sub : diagonal 𝕜 (f - g) = diagonal 𝕜 f - diagonal 𝕜 g := by + ext1; simp + +theorem diagonal_mul (c : ℝ) : diagonal 𝕜 (fun x ↦ c * f x) = c • diagonal 𝕜 f := by + ext1; simp [← Matrix.diagonal_smul] + +theorem diagonal_conj_diagonal [Fintype n] : + (diagonal 𝕜 f).conj (diagonal 𝕜 g) = diagonal 𝕜 (fun i ↦ f i * (g i)^2) := by + ext1 + simp [diagonal, conj] + intro + ring + +/-- +A Hermitian matrix is equal to its diagonalization conjugated by its eigenvector unitary matrix. +-/ +lemma eq_conj_diagonal [Fintype n] (A : HermitianMat n 𝕜) : + A = (diagonal 𝕜 A.H.eigenvalues).conj A.H.eigenvectorUnitary := by + ext1 + exact Matrix.IsHermitian.spectral_theorem A.2 + +end diagonal + +section kronecker +open Kronecker + +variable {p q : Type*} +variable [CommRing α] [StarRing α] + +/-- The kronecker product of two HermitianMats, see `Matrix.kroneckerMap`. -/ +def kronecker (A : HermitianMat m α) (B : HermitianMat n α) : HermitianMat (m × n) α where + val := A.mat ⊗ₖ B.mat + property := Matrix.kroneckerMap_IsHermitian A.H B.H + +@[inherit_doc HermitianMat.kronecker] +scoped[HermitianMat] infixl:100 " ⊗ₖ " => HermitianMat.kronecker + +@[simp, norm_cast] +theorem kronecker_mat (A : HermitianMat m α) (B : HermitianMat n α) : + (A ⊗ₖ B).mat = A.mat ⊗ₖ B.mat := by + rfl + +@[simp] +theorem zero_kronecker (A : HermitianMat m α) : (0 : HermitianMat n α) ⊗ₖ A = 0 := by + ext1; simp + +@[simp] +theorem kronecker_zero (A : HermitianMat m α) : A ⊗ₖ (0 : HermitianMat n α) = 0 := by + ext1; simp + +variable [DecidableEq m] [DecidableEq n] in +@[simp] +theorem kronecker_one_one : (1 : HermitianMat m α) ⊗ₖ (1 : HermitianMat n α) = 1 := by + ext1; simp + +variable (A B : HermitianMat m α) (C : HermitianMat n α) in +theorem add_kronecker : (A + B) ⊗ₖ C = A ⊗ₖ C + B ⊗ₖ C := by + ext1; simp [Matrix.add_kronecker] + +variable (A : HermitianMat m α) (B C : HermitianMat n α) in +theorem kronecker_add : A ⊗ₖ (B + C) = A ⊗ₖ B + A ⊗ₖ C := by + ext1; simp [Matrix.kronecker_add] + +lemma kronecker_diagonal [DecidableEq m] [DecidableEq n] (d₁ : m → ℝ) (d₂ : n → ℝ) : + (diagonal 𝕜 d₁ ⊗ₖ diagonal 𝕜 d₂) = diagonal 𝕜 (fun (i : m × n) => d₁ i.1 * d₂ i.2) := by + ext1 + simp [Matrix.diagonal_kronecker_diagonal] + +/-- +A ⊗ₖ B always commutes with C ⊗ₖ D if the pairs commute. +-/ +--Apply safely. It will almost always work, but there are cases where it's not sound, +-- such as `A = 0`. But these can all get easily simp'ed away anyway. +@[aesop safe apply (rule_sets := [Commutes])] +theorem kron_commute [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n] + {A C : HermitianMat m α} {B D : HermitianMat n α} + (hAC : Commute A.mat C.mat) (hBD : Commute B.mat D.mat): + Commute (A ⊗ₖ B).mat (C ⊗ₖ D).mat := by + rw [commute_iff_eq] at hAC hBD ⊢ + simp only [kronecker_mat, ← Matrix.mul_kronecker_mul, hAC, hBD] + +/-- +A ⊗ₖ 1 always commutes with 1 ⊗ₖ B +-/ +@[aesop safe apply (rule_sets := [Commutes])] --redundant but important shortcut +theorem kron_id_commute_id_kro [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n] + (A : HermitianMat m α) (B : HermitianMat n α) : + Commute (A ⊗ₖ (1 : HermitianMat n α)).mat ((1 : HermitianMat m α) ⊗ₖ B).mat := by + commutes + +/- +The conjugate of a Kronecker product by a Kronecker product is the Kronecker product of the conjugates. +-/ +lemma kronecker_conj [Fintype m] [Fintype n] + (A : HermitianMat m α) (B : HermitianMat n α) (C : Matrix p m α) (D : Matrix q n α) : + (A ⊗ₖ B).conj (C ⊗ₖ D) = (A.conj C) ⊗ₖ (B.conj D) := by + ext1 + exact Matrix.kronecker_conj_eq A.mat B.mat C D + +end kronecker + +section more_range_stuff + +variable {d d₂ : Type*} [Fintype d] [DecidableEq d] [Fintype d₂] [DecidableEq d₂] + +/- +If the range of a Hermitian matrix is contained in its kernel, the matrix is zero. +-/ +theorem range_le_ker_imp_zero {A : HermitianMat d 𝕜} + (h : LinearMap.range A.mat.toEuclideanLin ≤ LinearMap.ker A.mat.toEuclideanLin) : A = 0 := by + rw [HermitianMat.ext_iff, mat_zero] + ext i j + have hA_sq : (A.mat * A.mat) = 0 := by + simp_all only [SetLike.le_def, LinearMap.mem_range, LinearMap.mem_ker, forall_exists_index, + forall_apply_eq_imp_iff] + simp_all only [← Matrix.ext_iff, Matrix.mul_apply, mat_apply, Matrix.zero_apply] + intro i j + specialize h ( EuclideanSpace.single j 1 ) + simpa [ Matrix.mulVec, dotProduct ] using congr(WithLp.ofLp $(h) i) + simp_all only [mat_apply, Matrix.zero_apply] + replace hA_sq := congr_fun ( congr_fun hA_sq i ) i + simp_all only [Matrix.mul_apply, mat_apply, Matrix.zero_apply] ; + -- Since $A$ is Hermitian, we have $A i x * A x i = |A i x|^2$. + have h_abs : ∀ x, (A i x) * (A x i) = ‖A i x‖ ^ 2 := by + intro x; have := A.2 + simp_all only [val_eq_coe, sq] ; + have := congr_fun ( congr_fun this i ) x + simp_all only [Matrix.star_apply, mat_apply, RCLike.star_def] ; + simp only [← this, mul_comm, RCLike.norm_conj]; + simp [ ← sq, RCLike.mul_conj ]; + simp_rw [h_abs] at hA_sq + norm_cast at hA_sq + simp_all [Finset.sum_eq_zero_iff_of_nonneg] + +/-- +If ker M ⊆ ker A, then range (A Mᴴ) = range A. +-/ +theorem _root_.Matrix.range_mul_conjTranspose_of_ker_le_ker {A : Matrix d d 𝕜} {M : Matrix d₂ d 𝕜} + (h : LinearMap.ker M.toEuclideanLin ≤ LinearMap.ker A.toEuclideanLin) : + LinearMap.range (A * M.conjTranspose).toEuclideanLin = LinearMap.range A.toEuclideanLin := by + apply le_antisymm + · rintro x ⟨y, rfl⟩ + use (M.conjTranspose.toEuclideanLin) y; + simp [Matrix.toEuclideanLin] + · intro x hx; + -- Since $x \in \text{range}(A)$, there exists $y \in \text{range}(Mᴴ)$ such that $A y = x$. + obtain ⟨y, hy⟩ : ∃ y ∈ LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)), A.toEuclideanLin y = x := by + have h_range_MH : LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)) = (LinearMap.ker (Matrix.toEuclideanLin M))ᗮ := by + have h_orthogonal : (LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)))ᗮ = LinearMap.ker (Matrix.toEuclideanLin M) := by + ext x + rw [Matrix.toEuclideanLin_conjTranspose_eq_adjoint] + simp only [Submodule.mem_orthogonal, LinearMap.mem_ker, LinearMap.mem_range] + constructor + · intro h + rw [← inner_self_eq_zero (𝕜 := 𝕜)] + have : ∀ y, @inner 𝕜 _ _ y (Matrix.toEuclideanLin M x) = 0 := by + intro y + rw [← LinearMap.adjoint_inner_left] + exact h _ ⟨y, rfl⟩ + exact this _ + · intro h y ⟨z, hz⟩ + rw [← hz, LinearMap.adjoint_inner_left, h, inner_zero_right] + rw [← h_orthogonal, Submodule.orthogonal_orthogonal] + obtain ⟨ y, rfl ⟩ := hx; + -- Since $y$ is in the range of $Mᴴ$, we can write $y$ as $y = y_1 + y_2$ where $y_1 \in \text{range}(Mᴴ)$ and $y_2 \in \text{ker}(M)$. + obtain ⟨y1, y2, hy1, hy2, hy⟩ : ∃ y1 y2 : EuclideanSpace 𝕜 d, y1 ∈ LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)) ∧ y2 ∈ LinearMap.ker (Matrix.toEuclideanLin M) ∧ y = y1 + y2 := by + have h_decomp : ∀ y : EuclideanSpace 𝕜 d, ∃ y1 ∈ LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)), ∃ y2 ∈ LinearMap.ker (Matrix.toEuclideanLin M), y = y1 + y2 := by + intro y + have h_decomp : y ∈ (LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose))) ⊔ (LinearMap.ker (Matrix.toEuclideanLin M)) := by + rw [ h_range_MH ]; + rw [ sup_comm, Submodule.sup_orthogonal_of_hasOrthogonalProjection ]; + exact Submodule.mem_top; + rw [ Submodule.mem_sup ] at h_decomp ; tauto; + exact ⟨ _, _, h_decomp y |> Classical.choose_spec |> And.left, h_decomp y |> Classical.choose_spec |> And.right |> Classical.choose_spec |> And.left, h_decomp y |> Classical.choose_spec |> And.right |> Classical.choose_spec |> And.right ⟩; + exact ⟨ y1, hy1, by rw [ hy, map_add, LinearMap.mem_ker.mp ( h hy2 ) ] ; simp ⟩; + obtain ⟨ z, rfl ⟩ := hy.1; + exact ⟨ z, by simpa [ Matrix.toEuclideanLin ] using hy.2 ⟩ + +theorem conj_ne_zero {A : HermitianMat d 𝕜} {M : Matrix d₂ d 𝕜} (hA : A ≠ 0) + (h : LinearMap.ker M.toEuclideanLin ≤ A.ker) : A.conj M ≠ 0 := by + by_contra h_contra + have h_range : LinearMap.range A.mat.toEuclideanLin ≤ LinearMap.ker A.mat.toEuclideanLin := by + have h_range : LinearMap.range (A.mat * M.conjTranspose).toEuclideanLin ≤ LinearMap.ker M.toEuclideanLin := by + rintro x ⟨y, rfl⟩ + replace h_contra := congr($(h_contra).mat) + simp_all [Matrix.toLpLin_apply, Matrix.mul_assoc] + rw [← Matrix.range_mul_conjTranspose_of_ker_le_ker h] + exact h_range.trans h + exact hA (range_le_ker_imp_zero h_range) + +theorem conj_ne_zero_iff {A : HermitianMat d 𝕜} {M : Matrix d₂ d 𝕜} + (h : LinearMap.ker M.toEuclideanLin ≤ A.ker) : A.conj M ≠ 0 ↔ A ≠ 0 := by + refine ⟨?_, (conj_ne_zero · h)⟩ + intro h rfl; grind + +section spectrum + +variable [Fintype n] [DecidableEq n] [Fintype m] [DecidableEq m] + +theorem _root_.Matrix.IsHermitian.spectrum_rcLike {A : Matrix n n 𝕜} (hA : A.IsHermitian) : + RCLike.ofReal '' spectrum ℝ A = spectrum 𝕜 A := by + rw [hA.spectrum_eq_image_range, hA.spectrum_real_eq_range_eigenvalues] + +/-- We fix a simp-normal form that, for HermitianMat, we always work in terms +of the real spectrum. -/ +@[simp] +theorem spectrum_rcLike (A : HermitianMat n 𝕜) : + spectrum 𝕜 A.mat = RCLike.ofReal '' spectrum ℝ A.mat := by + exact A.H.spectrum_rcLike.symm + +theorem ne_zero_iff_ne_zero_spectrum (A : HermitianMat n 𝕜) : + A ≠ 0 ↔ ∃ x ∈ spectrum ℝ A.mat, x ≠ 0 := by + constructor; + · intro h_nonzero + contrapose! h_nonzero + simp only [HermitianMat.ext_iff, mat_zero] + rw [A.H.spectral_theorem] + ext i j + simp [Matrix.mul_apply, Matrix.diagonal] + refine Finset.sum_eq_zero fun x _ ↦ ?_ + simp [h_nonzero _ <| A.H.spectrum_real_eq_range_eigenvalues.symm ▸ Set.mem_range_self _] + · rintro ⟨x, hx, hx'⟩ h + simp [h, spectrum, resolventSet, Algebra.algebraMap_eq_smul_one, + hx', Matrix.isUnit_iff_isUnit_det] at hx + +open scoped Pointwise in +theorem spectrum_prod + {A : HermitianMat m 𝕜} {B : HermitianMat n 𝕜} : + spectrum ℝ (A ⊗ₖ B).mat = spectrum ℝ A.mat * spectrum ℝ B.mat := + Matrix.spectrum_prod A.H B.H + +end spectrum + +--Shortcut instance +noncomputable instance : AddCommMonoid (HermitianMat d ℂ) := + inferInstance diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean new file mode 100644 index 0000000000..d3f7863aa5 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean @@ -0,0 +1,1427 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry + +public import Mathlib.Analysis.CStarAlgebra.ContinuousFunctionalCalculus.Continuity +public import Mathlib.Analysis.CStarAlgebra.ContinuousFunctionalCalculus.Commute +public import Mathlib.Analysis.CStarAlgebra.CStarMatrix +public import Mathlib.Topology.TietzeExtension +public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic + +/-! Matrix operations on HermitianMats with the CFC -/ + +@[expose] public section +namespace HermitianMat + +noncomputable section CFC + +variable {d d₂ 𝕜 : Type*} [Fintype d] [DecidableEq d] [Fintype d₂] [DecidableEq d₂] [RCLike 𝕜] +variable {X : Type*} [TopologicalSpace X] +variable (A : HermitianMat d 𝕜) (f : ℝ → ℝ) (g : ℝ → ℝ) (q r : ℝ) + +/- Adding this to the `CStarAlgebra` aesop set allows `cfc_tac` to use it. -/ +omit [Fintype d] [DecidableEq d] in +@[aesop safe apply (rule_sets := [CStarAlgebra])] +theorem isSelfAdjoint : IsSelfAdjoint A.mat := by + exact A.H + +/- Adding this to `fun_prop` allows `cfc_cont_tac` to use it. -/ +@[fun_prop] +theorem continuousOn_finite {α β : Type*} (f : α → β) (S : Set α) + [TopologicalSpace α] [TopologicalSpace β] [T1Space α] [Finite S] : ContinuousOn f S := by + rw [continuousOn_iff_continuous_domRestrict] + exact continuous_of_discreteTopology + +@[simp] +theorem conjTranspose_cfc : (cfc f A.mat).conjTranspose = cfc f A.mat := by + exact cfc_predicate f A.mat + +protected def cfc : HermitianMat d 𝕜 := + ⟨cfc f A.mat, cfc_predicate _ _⟩ + +theorem cfc_eq : A.cfc f = ⟨cfc f A.mat, cfc_predicate f A.mat⟩ := by + rfl + +@[simp] +theorem mat_cfc : (A.cfc f).mat = _root_.cfc f A.mat := by + rfl + +section congr + +variable {f g A} + +theorem cfc_eq_cfc_iff_eqOn (f g : ℝ → ℝ) : + A.cfc f = A.cfc g ↔ Set.EqOn f g (spectrum ℝ A.mat) := by + rw [HermitianMat.ext_iff, mat_cfc, mat_cfc] + exact _root_.cfc_eq_cfc_iff_eqOn A.H + +nonrec theorem cfc_congr (hfg : Set.EqOn f g (spectrum ℝ A.mat)) : + A.cfc f = A.cfc g := by + ext1 + exact cfc_congr hfg + +/-- Version of `cfc_congr` specialized to PSD matrices. -/ +nonrec theorem cfc_congr_of_nonneg (hA : 0 ≤ A) (hfg : Set.EqOn f g (Set.Ici 0)) : + A.cfc f = A.cfc g := by + refine cfc_congr (hfg.mono ?_) + open MatrixOrder in + exact spectrum_nonneg_of_nonneg (a := A.mat) hA + +open ComplexOrder in +/-- Version of `cfc_congr` specialized to positive definite matrices. -/ +nonrec theorem cfc_congr_of_posDef (hA : A.mat.PosDef) (hfg : Set.EqOn f g (Set.Ioi 0)) : + A.cfc f = A.cfc g := by + refine cfc_congr (hfg.mono ?_) + rw [A.H.spectrum_real_eq_range_eigenvalues] + rintro _ ⟨i, rfl⟩ + exact hA.eigenvalues_pos i + +end congr +section commute +variable {A B : HermitianMat d 𝕜} + +@[aesop unsafe apply 50% (rule_sets := [Commutes])] +theorem _root_.Commute.cfc_left (hAB : Commute A.mat B.mat) : + Commute (A.cfc f).mat B.mat := by + exact hAB.cfc_real f + +@[aesop unsafe apply 50% (rule_sets := [Commutes])] +theorem _root_.Commute.cfc_right (hAB : Commute A.mat B.mat) : + Commute A.mat (B.cfc f).mat := + (hAB.symm.cfc_left f).symm + +theorem cfc_commute (f g : ℝ → ℝ) (hAB : Commute A.mat B.mat) : + Commute (A.cfc f).mat (B.cfc g).mat := by + exact (hAB.cfc_right g).cfc_left f + +@[aesop safe apply (rule_sets := [Commutes])] +theorem cfc_self_commute (A : HermitianMat d 𝕜) (f g : ℝ → ℝ) : + Commute (A.cfc f).mat (A.cfc g).mat := by + commutes + +end commute + +/-- Reindexing a matrix commutes with applying the CFC. -/ +@[simp] +theorem cfc_reindex (e : d ≃ d₂) : (A.reindex e).cfc f = (A.cfc f).reindex e := by + rw [HermitianMat.ext_iff] + simp only [mat_cfc, mat_reindex] + exact Matrix.cfc_reindex f e + +theorem spectrum_cfc_eq_image (A : HermitianMat d 𝕜) (f : ℝ → ℝ) : + spectrum ℝ (A.cfc f).mat = f '' (spectrum ℝ A.mat) := by + exact cfc_map_spectrum f A.mat + +/-- +Spectral decomposition of `A.cfc f` as a sum of scaled projections (matrix version). +-/ +theorem cfc_toMat_eq_sum_smul_proj : (A.cfc f).mat = + ∑ i, f (A.H.eigenvalues i) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + rw [A.mat_cfc, A.H.cfc_eq, Matrix.IsHermitian.cfc] + have h : ( Matrix.diagonal ( RCLike.ofReal ∘ f ∘ Matrix.IsHermitian.eigenvalues A.H ) : Matrix d d 𝕜 ) = ∑ i, f ( A.H.eigenvalues i ) • Matrix.single i i 1 := by + ext i j ; by_cases hij : i = j <;> simp [ hij ]; + · simp [ Matrix.sum_apply, Matrix.single ]; + simp [ Algebra.smul_def ]; + · rw [Finset.sum_apply, Finset.sum_apply] + simp_all + rw [h] + simp [Matrix.single, Matrix.mul_assoc] + congr! 1 + ext j k + simp [Matrix.mul_apply,Finset.mul_sum, Finset.smul_sum, smul_ite, smul_zero] + +--Ensure we get this instance: +/-- info: locallyCompact_of_proper -/ +#guard_msgs in + +#synth LocallyCompactSpace (HermitianMat d 𝕜) + +theorem cfc_eigenvalues (A : HermitianMat d 𝕜) : + ∃ (e : d ≃ d), (A.cfc f).H.eigenvalues = f ∘ A.H.eigenvalues ∘ e := + A.H.cfc_eigenvalues f + +/-! Here we give HermitianMat versions of many cfc theorems, like `cfc_id`, `cfc_sub`, `cfc_comp`, +etc. We need these because (as above) `HermitianMat.cfc` is different from `_root_.cfc`. -/ + +@[simp] +nonrec theorem cfc_id : A.cfc id = A := by + simpa [HermitianMat.ext_iff] using cfc_id ℝ A.mat + +@[simp] +nonrec theorem cfc_id' : A.cfc (·) = A := + cfc_id A + +nonrec theorem cfc_add : A.cfc (f + g) = A.cfc f + A.cfc g := by + ext1; exact cfc_add .. + +theorem cfc_add_apply : A.cfc (fun x ↦ f x + g x) = A.cfc f + A.cfc g := + cfc_add A f g + +nonrec theorem cfc_sub : A.cfc (f - g) = A.cfc f - A.cfc g := by + ext1; exact cfc_sub .. + +theorem cfc_sub_apply : A.cfc (fun x ↦ f x - g x) = A.cfc f - A.cfc g := + cfc_sub A f g + +nonrec theorem cfc_neg : A.cfc (-f) = -A.cfc f := by + ext1; exact cfc_neg .. + +theorem cfc_neg_apply : A.cfc (fun x ↦ -f x) = -A.cfc f := + cfc_neg A f + +/-- We don't have a direct analog of `cfc_mul`, since we can't generally multiply +to HermitianMat's to get another one, so the theorem statement wouldn't be well-typed. +But, we can say that the matrices are always equal. See `cfc_conj` for the coe-free +analog to multiplication. -/ +theorem mat_cfc_mul : (A.cfc (f * g)).mat = A.cfc f * A.cfc g := by + simp only [mat_cfc] + exact cfc_mul .. + +theorem mat_cfc_mul_apply : (A.cfc (fun x ↦ f x * g x)).mat = A.cfc f * A.cfc g := by + exact mat_cfc_mul .. + +nonrec theorem cfc_comp : A.cfc (g ∘ f) = (A.cfc f).cfc g := by + ext1; exact cfc_comp .. + +theorem cfc_comp_apply : A.cfc (fun x ↦ g (f x)) = (A.cfc f).cfc g := + cfc_comp A f g + +nonrec theorem cfc_conj : (A.cfc f).conj (A.cfc g) = A.cfc (f * g^2) := by + ext1 + simp only [conj_apply, mat_cfc, mat_mk, conjTranspose_cfc] + rw [← cfc_mul, ← cfc_mul, Pi.mul_def, Pi.pow_def] + grind only + +@[simp] +theorem cfc_diagonal (g : d → ℝ) : (diagonal 𝕜 g).cfc f = diagonal 𝕜 (f ∘ g) := by + ext1 + exact Matrix.cfc_diagonal g f + +theorem cfc_conj_unitary (U : Matrix.unitaryGroup d 𝕜) : + (A.conj U.val).cfc f = (A.cfc f).conj U := by + ext1 + exact Matrix.cfc_conj_unitary f U + +@[simp] +nonrec theorem cfc_const : (A.cfc (fun _ ↦ r)) = r • 1 := by + ext1 + simp only [mat_cfc, mat_smul, mat_one] + rw [cfc_const r A.mat] + exact Algebra.algebraMap_eq_smul_one r + +@[simp] +nonrec theorem cfc_const_mul_id : A.cfc (fun x ↦ r * x) = r • A := by + ext1 + rw [mat_cfc, mat_smul, cfc_const_mul_id r A.mat] + +@[simp] +nonrec theorem cfc_const_mul : A.cfc (fun x ↦ r * f x) = r • A.cfc f := by + rw [← cfc_const_mul_id, ← cfc_comp] + rfl + +@[simp] +nonrec theorem cfc_apply_zero : (0 : HermitianMat d 𝕜).cfc f = f 0 • 1 := by + simp [HermitianMat.ext_iff, Algebra.algebraMap_eq_smul_one] + +@[simp] +nonrec theorem cfc_apply_one : (1 : HermitianMat d 𝕜).cfc f = f 1 • 1 := by + simp [HermitianMat.ext_iff, Algebra.algebraMap_eq_smul_one] + +theorem cfc_pow {n : ℕ} : A.cfc (· ^ n) = A ^ n := by + ext1 + induction n + · simp + · simp_rw [pow_succ, mat_pow, mat_cfc_mul_apply, pow_succ, cfc_id'] + congr + +theorem cfc_nonneg_iff : 0 ≤ A.cfc f ↔ ∀ i, 0 ≤ f (A.H.eigenvalues i) := by + open MatrixOrder in + rw [cfc_eq, ← Subtype.coe_le_coe, ZeroMemClass.coe_zero] + rw [_root_.cfc_nonneg_iff f A.mat, A.H.spectrum_real_eq_range_eigenvalues] + grind + +open ComplexOrder in +theorem cfc_posDef : (A.cfc f).mat.PosDef ↔ ∀ i, 0 < f (A.H.eigenvalues i) := by + rw [(A.cfc f).H.posDef_iff_eigenvalues_pos] + obtain ⟨e, he⟩ := A.cfc_eigenvalues f + rw [he] + refine ⟨fun h i ↦ ?_, fun h i ↦ h (e i)⟩ + simpa using h (e.symm i) + +variable {A f} in +/-- If a rael function preserves nonnegativity, the CFC preserves PSDness. -/ +theorem cfc_nonneg_of_nonneg (hA : 0 ≤ A) (hf : ∀ i ≥ 0, 0 ≤ f i) : + 0 ≤ A.cfc f := by + rw [cfc_nonneg_iff] + rw [zero_le_iff, A.H.posSemidef_iff_eigenvalues_nonneg] at hA + exact fun i ↦ hf _ (hA i) + +theorem cfc_nonSingular (hf : ∀ i, f (A.H.eigenvalues i) ≠ 0) : NonSingular (A.cfc f) := by + rw [nonSingular_iff_eigenvalue_ne_zero] + obtain ⟨e, he⟩ := cfc_eigenvalues f A + simpa [he] using fun i ↦ hf (e i) + +theorem trace_mul_cfc (A : HermitianMat d 𝕜) (f : ℝ → ℝ) : + (A.mat * (A.cfc f).mat).trace = ∑ i, A.H.eigenvalues i * f (A.H.eigenvalues i) := by + conv_lhs => rw [A.eq_conj_diagonal] + rw [cfc_conj_unitary] + simp [conj, Matrix.mul_assoc, A.H.eigenvectorUnitary.val.trace_mul_comm] + simp [← Matrix.mul_assoc, Matrix.IsHermitian.eigenvectorUnitary ] + +theorem norm_eq_sum_eigenvalues_sq (A : HermitianMat d 𝕜) : + ‖A‖ ^ 2 = ∑ i, (A.H.eigenvalues i)^2 := by + rw [← RCLike.ofReal_inj (K := 𝕜), RCLike.ofReal_pow, norm_eq_trace_sq] + conv_lhs => change (A ^ 2).mat.trace; rw [(A ^ 2).H.trace_eq_sum_eigenvalues] + simp only [map_sum, map_pow] + rw [← cfc_pow] + obtain ⟨e, he⟩ := cfc_eigenvalues (· ^ 2) A + simp only [he, Function.comp_apply, map_pow] + exact e.sum_comp (fun x ↦ (algebraMap ℝ 𝕜) (A.H.eigenvalues x) ^ 2) + +variable {A} in +theorem lt_smul_of_norm_lt {r : ℝ} (h : ‖A‖ ≤ r) : A ≤ r • 1 := by + rcases lt_or_ge r 0 with _ | hr + · have := norm_nonneg A + order + rcases isEmpty_or_nonempty d + · exact le_of_subsingleton + have h' := (sq_le_sq₀ (by positivity) (by positivity)).mpr h + rw [norm_eq_sum_eigenvalues_sq] at h' + nth_rw 1 [← cfc_const A, ← cfc_id A] + rw [le_iff, ← cfc_sub] + rw [(HermitianMat.H _).posSemidef_iff_eigenvalues_nonneg] + intro i; rw [Pi.zero_apply] + obtain ⟨e, he⟩ := cfc_eigenvalues ((fun x ↦ r) - id) A + rw [he]; clear he + dsimp only [Function.comp_apply, Pi.sub_apply, id_eq] + rw [sub_nonneg] + apply le_of_sq_le_sq _ hr + refine le_trans ?_ h' + exact Finset.single_le_sum (f := fun x ↦ (A.H.eigenvalues x)^2) (by intros; positivity) (Finset.mem_univ _) + +theorem ball_subset_Icc : Metric.ball A r ⊆ Set.Icc (A - r • 1) (A + r • 1) := by + intro x + simp only [Metric.mem_ball, dist_eq_norm, Set.mem_Icc, tsub_le_iff_right] + intro h + constructor + · rw [← norm_neg] at h + grw [← lt_smul_of_norm_lt h.le] + simp + · grw [← lt_smul_of_norm_lt h.le] + simp + +theorem spectrum_subset_of_mem_Icc (A B : HermitianMat d 𝕜) : + ∃ a b, ∀ x, A ≤ x ∧ x ≤ B → spectrum ℝ x.mat ⊆ Set.Icc a b := by + use ⨅ i, A.H.eigenvalues i, ⨆ i, B.H.eigenvalues i + rintro x ⟨hl, hr⟩ + exact A.H.spectrum_subset_of_mem_Icc B.H hl hr + +--TODO: Generalize this to real matrices (really, RCLike) too. The theorem below +-- gives it for complex matrices only. +-- @[fun_prop] +-- protected theorem cfc_continuous {f : ℝ → ℝ} (hf : Continuous f) : +-- Continuous (cfc · f : HermitianMat d 𝕜 → HermitianMat d 𝕜) := by +-- rcases isEmpty_or_nonempty d +-- · sorry +-- rw [Metric.continuous_iff] at hf ⊢ +-- intro x ε hε +-- have _ : Nonempty (spectrum ℝ x.toMat) := by +-- sorry +-- replace hf b := hf b ε hε +-- choose fc hfc₀ hfc using hf +-- let δ : ℝ := ⨆ e : spectrum ℝ x.toMat, fc e +-- refine ⟨δ, ?_, ?_⟩ +-- · --This whole block should just be `positivity`. TODO fix. +-- dsimp [δ] +-- --Why doesn't just `classical` make this happen automatically? +-- replace h_fin := Fintype.ofFinite (spectrum ℝ x.toMat) +-- rw [← Finset.sup'_univ_eq_ciSup, gt_iff_lt, Finset.lt_sup'_iff] +-- simp [hfc₀] +-- intro a ha +-- simp only [dist, AddSubgroupClass.subtype_apply, val_eq_coe, cfc_toMat] at ha ⊢ +-- sorry + +@[fun_prop] +protected theorem cfc_continuous {f : ℝ → ℝ} (hf : Continuous f) : + Continuous (HermitianMat.cfc · f : HermitianMat d ℂ → HermitianMat d ℂ) := by + unfold HermitianMat.cfc + suffices Continuous (fun A : HermitianMat d ℂ ↦ _root_.cfc f A.mat) by + fun_prop + have h_compact_cover := LocallyCompactSpace.local_compact_nhds (X := HermitianMat d ℂ) + apply continuous_of_continuousOn_iUnion_of_isOpen (ι := HermitianMat d ℂ × {x : ℝ // 0 < x}) + (s := fun ab ↦ Metric.ball ab.1 ab.2) + · rintro ⟨A, r, hr⟩ + apply ContinuousOn.mono ?_ (ball_subset_Icc A r) + obtain ⟨a, b, hab⟩ := spectrum_subset_of_mem_Icc (A - r • 1) (A + r • 1) + open ComplexOrder in + refine ContinuousOn.cfc (s := fun _ ↦ Set.Icc a b) (t := Set.Icc (A - r • 1) (A + r • 1)) (A := CStarMatrix d d ℂ) f ?_ (by fun_prop) ?_ (fun x _ ↦ x.H) + · intro _ _ + exact isCompact_Icc + · simp only [Set.mem_Icc] + exact fun _ _ ↦ eventually_nhdsWithin_of_forall hab + · simp + · ext x + simp only [Set.mem_iUnion, Set.mem_univ, iff_true] + use ⟨x, 1⟩ + simp + +open ComplexOrder in +theorem Matrix.PosDef.spectrum_subset_Ioi {d 𝕜 : Type*} [Fintype d] [DecidableEq d] [RCLike 𝕜] + {A : Matrix d d 𝕜} (hA : A.PosDef) : spectrum ℝ A ⊆ Set.Ioi 0 := by + intro x hx; + -- Since $A$ is positive definite, all its eigenvalues are positive. + have h_eigenvalues_pos : ∀ i : d, 0 < hA.1.eigenvalues i := by + exact hA.eigenvalues_pos; + have h_spectrum_eq_range : spectrum ℝ A = Set.range (hA.1.eigenvalues) := by + exact Matrix.IsHermitian.spectrum_real_eq_range_eigenvalues hA.left; + aesop + +/-- +If f is a continuous family of functions parameterized by x, then (fun x => A.cfc (f x)) is also continuous. +-/ +@[fun_prop] +theorem continuous_cfc_fun {f : X → ℝ → ℝ} (hf : ∀ i, Continuous (f · i)) : + Continuous (fun x ↦ A.cfc (f x)) := by + apply Continuous.subtype_mk + conv => enter [1, x]; apply A.cfc_toMat_eq_sum_smul_proj (f x) + fun_prop + +variable {f : X → ℝ → ℝ} {S : Set X} +/-- +ContinuousOn variant for when all the matrices (A x) have a spectrum in a set T, and f is continuous on a set S. +-/ +@[fun_prop] +theorem continuousOn_cfc_fun {T : Set ℝ} + (hf : ∀ i ∈ T, ContinuousOn (f · i) S) (hA : spectrum ℝ A.mat ⊆ T) : + ContinuousOn (fun x ↦ A.cfc (f x)) S := by + simp_rw [continuousOn_iff_continuous_domRestrict] at hf ⊢ + apply Continuous.subtype_mk + conv => enter [1, x]; apply A.cfc_toMat_eq_sum_smul_proj (f x) + unfold Set.domRestrict at hf + apply continuous_finsetSum _ + rw [A.H.spectrum_real_eq_range_eigenvalues] at hA + refine fun i _ ↦ Continuous.smul (hf _ (by grind)) (by fun_prop) + +section joint_continuity + +--TODO Cleanup + +/-- +Bound the Frobenius norm of a functional calculus application. +-/ +lemma norm_cfc_le_sqrt_card_mul_bound {A : HermitianMat d ℂ} {f : ℝ → ℝ} {C : ℝ} + (hC : 0 ≤ C) (hf : ∀ x ∈ spectrum ℝ A.mat, ‖f x‖ ≤ C) : + ‖A.cfc f‖ ≤ Real.sqrt (Fintype.card d) * C := by + rw [ ← Real.sqrt_sq ( norm_nonneg _ ) ]; + -- Recall that the Frobenius norm of a Hermitian matrix is the square root of the sum of the squares of its eigenvalues. + have h_frobenius_eigenvalues : ∀ (M : HermitianMat d ℂ), ‖M‖ ^ 2 = ∑ i ∈ Finset.univ, (M.H.eigenvalues i) ^ 2 := by + exact fun M => norm_eq_sum_eigenvalues_sq M; + -- Applying the bound on the eigenvalues to the Frobenius norm. + have h_bound : ∑ i ∈ Finset.univ, ((A.cfc f).H.eigenvalues i) ^ 2 ≤ (Fintype.card d) * C ^ 2 := by + have h_bound : ∀ i, ((A.cfc f).H.eigenvalues i) ^ 2 ≤ C ^ 2 := by + intro i + have h_eigenvalue_bound : |(A.cfc f).H.eigenvalues i| ≤ C := by + obtain ⟨ x, hx, hx' ⟩ : (A.cfc f).H.eigenvalues i ∈ f '' spectrum ℝ A.mat := by + have h_bound := (A.cfc f).H.eigenvalues_mem_spectrum_real i + rwa [spectrum_cfc_eq_image A f] at h_bound + specialize hf x hx + aesop; + nlinarith only [ abs_le.mp h_eigenvalue_bound ]; + exact le_trans ( Finset.sum_le_sum fun _ _ => h_bound _ ) ( by simp ); + rw [ h_frobenius_eigenvalues, Real.sqrt_le_left ] <;> nlinarith [ Real.sqrt_nonneg ( Fintype.card d : ℝ ), Real.mul_self_sqrt ( Nat.cast_nonneg ( Fintype.card d ) ) ] + +/- +The norm of the difference of two functional calculus applications is bounded by `sqrt(d)` times the sup norm of the difference of the functions. +-/ +lemma norm_cfc_sub_cfc_le_sqrt_card {A : HermitianMat d ℂ} {f g : ℝ → ℝ} : + ‖A.cfc f - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * ⨆ x ∈ spectrum ℝ A.mat, ‖f x - g x‖ := by + rw [ ← HermitianMat.cfc_sub ]; + refine' le_trans ( norm_cfc_le_sqrt_card_mul_bound _ _ ) _; + exact ⨆ x ∈ spectrum ℝ A.mat, ‖f x - g x‖; + · exact Real.iSup_nonneg fun _ => Real.iSup_nonneg fun _ => norm_nonneg _; + · intro x hx + apply le_csSup; + · -- The supremum of a finite set of real numbers is finite. + have h_finite : Set.Finite (spectrum ℝ A.mat) := by + exact Set.toFinite _; + obtain ⟨ M, hM ⟩ := h_finite.exists_finset_coe; + refine' ⟨ ∑ x ∈ M, ‖f x - g x‖, Set.forall_mem_range.2 fun x => _ ⟩; + rw [ ← hM ]; + rw [ @ciSup_eq_ite ]; + split_ifs <;> [ exact Finset.single_le_sum ( fun x _ => norm_nonneg ( f x - g x ) ) ( by assumption ) ; exact le_trans ( by norm_num ) ( Finset.sum_nonneg fun x _ => norm_nonneg ( f x - g x ) ) ]; + · exact ⟨ x, by aesop ⟩; + · rfl + +/- +If f and g are close on T, and the spectrum of A is in T, then A.cfc f and A.cfc g are close. +-/ +lemma norm_cfc_sub_le_of_sup_le {A : HermitianMat d ℂ} {f g : ℝ → ℝ} {T : Set ℝ} {ε : ℝ} + (hT : spectrum ℝ A.mat ⊆ T) (hε : 0 ≤ ε) (h_sup : ∀ x ∈ T, ‖f x - g x‖ ≤ ε) : + ‖A.cfc f - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * ε := by + refine' le_trans ( norm_cfc_sub_cfc_le_sqrt_card ) _; + gcongr; + refine' ciSup_le fun x => _; + exact Real.iSup_le (fun i => h_sup x (hT i)) hε + +/-- +If $f$ is jointly continuous on $S \times T$ and $T$ is compact, then $x \mapsto f(x, \cdot)$ is continuous into the space of bounded functions on $T$ with the uniform norm. +-/ +lemma dist_lt_of_continuous' {X : Type*} [TopologicalSpace X] + {f : X → ℝ → ℝ} {S : Set X} {T : Set ℝ} + (hT : IsCompact T) + (hf : ContinuousOn (fun (p : X × ℝ) ↦ f p.1 p.2) (S ×ˢ T)) + {x₀ : X} (hx₀ : x₀ ∈ S) {ε : ℝ} (hε : 0 < ε) : + ∃ U ∈ nhds x₀, ∀ x ∈ U ∩ S, ∀ t ∈ T, ‖f x t - f x₀ t‖ < ε := by + by_contra h_contra; + -- For each $t \in T$, by continuity at $(x₀, t)$, there exist neighborhoods $U_t$ of $x₀$ and $V_t$ of $t$ such that for all $x \in U_t \cap S$ and $t' \in V_t \cap T$, $|f(x, t') - f(x₀, t)| < \epsilon/2$. + have h_cont : ∀ t ∈ T, ∃ U_t ∈ nhds x₀, ∃ V_t ∈ nhds t, ∀ x ∈ U_t ∩ S, ∀ t' ∈ V_t ∩ T, ‖f x t' - f x₀ t‖ < ε / 2 := by + intro t ht + have h_cont_t : ∀ᶠ (p : X × ℝ) in nhds (x₀, t), p ∈ S ×ˢ T → ‖f p.1 p.2 - f x₀ t‖ < ε / 2 := by + have := hf ( x₀, t ) ⟨ hx₀, ht ⟩; + have := this.eventually ( Metric.ball_mem_nhds _ ( half_pos hε ) ); + rw [ eventually_nhdsWithin_iff ] at this; aesop; + rcases mem_nhds_prod_iff.mp h_cont_t with ⟨ U, hU, V, hV, hUV ⟩; + exact ⟨ U, hU, V, hV, fun x hx t' ht' => hUV ( Set.mk_mem_prod hx.1 ht'.1 ) ⟨ hx.2, ht'.2 ⟩ ⟩; + choose! U hU V hV hUV using h_cont; + -- Since $T$ is compact, cover it by finitely many $V_{t_i}$. Let $U = \bigcap U_{t_i}$. + obtain ⟨t_fin, ht_fin⟩ : ∃ t_fin : Finset ℝ, (∀ t ∈ t_fin, t ∈ T) ∧ T ⊆ ⋃ t ∈ t_fin, V t := by + have := hT.elim_nhds_subcover V fun t ht => hV t ht; + tauto; + refine h_contra ⟨⋂ t ∈ t_fin, U t, ?_, ?_⟩ + · exact Filter.biInter_mem ( Finset.finite_toSet t_fin ) |>.2 fun t ht => hU t ( ht_fin.1 t ht ); + · intro x hx t ht + obtain ⟨t', ht'_fin, ht'_t⟩ : ∃ t' ∈ t_fin, t ∈ V t' := by + simpa using ht_fin.2 ht; + have := hUV t' ( ht_fin.1 t' ht'_fin ) x ⟨ Set.mem_iInter₂.1 hx.1 t' ht'_fin, hx.2 ⟩ t ⟨ ht'_t, ht ⟩; + have := hUV t' ( ht_fin.1 t' ht'_fin ) x₀ ⟨ mem_of_mem_nhds ( hU t' ( ht_fin.1 t' ht'_fin ) ), hx₀ ⟩ t ⟨ ht'_t, ht ⟩; + exact abs_lt.mpr ⟨ by linarith [ abs_lt.mp ‹‖f x t - f x₀ t'‖ < ε / 2›, abs_lt.mp ‹‖f x₀ t - f x₀ t'‖ < ε / 2› ], by linarith [ abs_lt.mp ‹‖f x t - f x₀ t'‖ < ε / 2›, abs_lt.mp ‹‖f x₀ t - f x₀ t'‖ < ε / 2› ] ⟩ + +/-- +The functional calculus is continuous on matrices with spectrum in a compact set. +-/ +lemma continuousOn_cfc_of_compact {K : Set ℝ} {g : ℝ → ℝ} (hK : IsCompact K) (hg : ContinuousOn g K) : + ContinuousOn (fun (A : HermitianMat d ℂ) ↦ A.cfc g) {A | spectrum ℝ A.mat ⊆ K} := by + by_contra! h_contra; + -- By Stone-Weierstrass, there exists a sequence of polynomials `p_n` converging uniformly to `g` on `K`. + obtain ⟨p_n, hp_n⟩ : ∃ p_n : ℕ → Polynomial ℝ, (∀ n, ∀ x ∈ K, |(p_n n).eval x - g x| ≤ 1 / (n + 1)) := by + have h_stone_weierstrass : ∀ ε > 0, ∃ p : Polynomial ℝ, ∀ x ∈ K, |p.eval x - g x| < ε := by + have := @exists_polynomial_near_of_continuousOn; + obtain ⟨a, b, hab⟩ : ∃ a b : ℝ, K ⊆ Set.Icc a b := by + exact ⟨ hK.bddBelow.some, hK.bddAbove.some, fun x hx => ⟨ hK.bddBelow.choose_spec hx, hK.bddAbove.choose_spec hx ⟩ ⟩; + -- Extend $g$ to a continuous function on $[a, b]$. + obtain ⟨f, hf⟩ : ∃ f : ℝ → ℝ, ContinuousOn f (Set.Icc a b) ∧ ∀ x ∈ K, f x = g x := by + have := @ContinuousMap.exists_restrict_eq; + specialize this ( show IsClosed K from hK.isClosed ) ( ContinuousMap.mk ( fun x => g x ) <| by exact continuousOn_iff_continuous_domRestrict.mp hg ); + exact ⟨ _, this.choose.continuous.continuousOn, fun x hx => by simpa using congr_arg ( fun f => f ⟨ x, hx ⟩ ) this.choose_spec ⟩; + exact fun ε εpos => by rcases this a b f hf.1 ε εpos with ⟨ p, hp ⟩ ; exact ⟨ p, fun x hx => by simpa only [ hf.2 x hx ] using hp x ( hab hx ) ⟩ ; + exact ⟨ fun n => Classical.choose ( h_stone_weierstrass ( 1 / ( n + 1 ) ) ( by positivity ) ), fun n x hx => le_of_lt ( Classical.choose_spec ( h_stone_weierstrass ( 1 / ( n + 1 ) ) ( by positivity ) ) x hx ) ⟩; + -- The sequence `A ↦ A.cfc (p_n)` converges uniformly to `A ↦ A.cfc g` on `{A | spectrum A ⊆ K}`. + have h_uniform : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, ∀ A : HermitianMat d ℂ, spectrum ℝ A.mat ⊆ K → ‖A.cfc (fun x => (p_n n).eval x) - A.cfc g‖ < ε := by + -- By the properties of the functional calculus, we have `‖A.cfc p_n - A.cfc g‖ ≤ sqrt(d) * ‖p_n - g‖_{∞, K}`. + have h_uniform_bound : ∀ n, ∀ A : HermitianMat d ℂ, spectrum ℝ A.mat ⊆ K → ‖A.cfc (fun x => (p_n n).eval x) - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * (1 / (n + 1)) := by + intro n A hA + have h_uniform_bound : ‖A.cfc (fun x => (p_n n).eval x) - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * ⨆ x ∈ spectrum ℝ A.mat, |(p_n n).eval x - g x| := by + exact norm_cfc_sub_cfc_le_sqrt_card; + refine' le_trans h_uniform_bound ( mul_le_mul_of_nonneg_left _ ( Real.sqrt_nonneg _ ) ); + refine' ciSup_le fun x => _; + field_simp; + by_cases hx : x ∈ spectrum ℝ A.mat <;> simp_all + exact le_trans ( mul_le_mul_of_nonneg_right ( hp_n n x ( hA hx ) ) ( by positivity ) ) ( by nlinarith [ mul_inv_cancel₀ ( by positivity : ( n : ℝ ) + 1 ≠ 0 ) ] ); + exact fun ε εpos => ⟨ Nat.ceil ( ε⁻¹ * Real.sqrt ( Fintype.card d ) ), fun n hn A hA => lt_of_le_of_lt ( h_uniform_bound n A hA ) ( by rw [ mul_one_div, div_lt_iff₀ ] <;> nlinarith [ Nat.ceil_le.mp hn, inv_pos.mpr εpos, mul_inv_cancel₀ εpos.ne', Real.sqrt_nonneg ( Fintype.card d ), Real.sq_sqrt ( Nat.cast_nonneg ( Fintype.card d ) ) ] ) ⟩; + -- The uniform limit of continuous functions is continuous. + have h_cont : ContinuousOn (fun A : HermitianMat d ℂ => A.cfc g) {A : HermitianMat d ℂ | spectrum ℝ A.mat ⊆ K} := by + have h_seq_cont : ∀ n, ContinuousOn (fun A : HermitianMat d ℂ => A.cfc (fun x => (p_n n).eval x)) {A : HermitianMat d ℂ | spectrum ℝ A.mat ⊆ K} := by + fun_prop + refine' Metric.continuousOn_iff.mpr _; + intro A hA ε εpos + obtain ⟨N, hN⟩ := h_uniform (ε / 3) (by linarith) + obtain ⟨δ, δpos, hδ⟩ : ∃ δ > 0, ∀ a ∈ {A : HermitianMat d ℂ | spectrum ℝ A.mat ⊆ K}, dist a A < δ → ‖a.cfc (fun x => (p_n N).eval x) - A.cfc (fun x => (p_n N).eval x)‖ < ε / 3 := by + have := Metric.continuousOn_iff.mp ( h_seq_cont N ) A hA ( ε / 3 ) ( by linarith ); + exact ⟨ this.choose, this.choose_spec.1, fun a ha ha' => by simpa only [ dist_eq_norm ] using this.choose_spec.2 a ha ha' ⟩; + refine' ⟨ δ, δpos, fun a ha ha' => _ ⟩; + have := hN N le_rfl a ha; + have := hN N le_rfl A hA; + rw [ dist_eq_norm ]; + rw [ show a.cfc g - A.cfc g = ( a.cfc g - a.cfc ( fun x => Polynomial.eval x ( p_n N ) ) ) + ( a.cfc ( fun x => Polynomial.eval x ( p_n N ) ) - A.cfc ( fun x => Polynomial.eval x ( p_n N ) ) ) + ( A.cfc ( fun x => Polynomial.eval x ( p_n N ) ) - A.cfc g ) by abel1 ]; + exact lt_of_le_of_lt ( norm_add₃_le .. ) ( by linarith [ norm_sub_rev ( a.cfc g ) ( a.cfc fun x => Polynomial.eval x ( p_n N ) ), norm_sub_rev ( A.cfc fun x => Polynomial.eval x ( p_n N ) ) ( A.cfc g ), hδ a ha ha' ] ); + contradiction + +end joint_continuity + +theorem continuous_cfc_joint_compact {X d : Type*} [TopologicalSpace X] [Fintype d] [DecidableEq d] + {f : X → ℝ → ℝ} {A : X → HermitianMat d ℂ} {S : Set X} {T : Set ℝ} + (hT : IsCompact T) + (hf : ContinuousOn (fun (p : X × ℝ) ↦ f p.1 p.2) (S ×ˢ T)) + (hA₁ : ∀ x ∈ S, spectrum ℝ (A x).mat ⊆ T) + (hA₂ : ContinuousOn (fun x ↦ A x) S) : + ContinuousOn (fun x ↦ (A x).cfc (f x)) S := by + intro x x_in_S + have h_eps_delta : ContinuousWithinAt (fun y => (A y).cfc (f x)) S x := by + refine ContinuousOn.continuousWithinAt ?_ x_in_S + exact (continuousOn_cfc_of_compact hT (hf.uncurry_left x x_in_S)).comp hA₂ hA₁ + rw [ ContinuousWithinAt ] at *; + rw [ Metric.tendsto_nhds ] at *; + intro ε ε_pos + obtain ⟨U, hU₁, hU₂⟩ : ∃ U ∈ nhds x, ∀ y ∈ U ∩ S, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ Real.sqrt (Fintype.card d) * (ε / (2 * Real.sqrt (Fintype.card d) + 1)) := by + have h_eps_delta₁ : ∀ ε > 0, ∃ U ∈ nhds x, ∀ y ∈ U ∩ S, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ Real.sqrt (Fintype.card d) * ε := by + intro ε ε_pos + obtain ⟨U, hU₁, hU₂⟩ := dist_lt_of_continuous' (f := f) hT hf x_in_S ε_pos + use U, hU₁ + intro y hy + apply norm_cfc_sub_le_of_sup_le (hA₁ y hy.right) ε_pos.le + intro t ht + exact le_of_lt (hU₂ y hy t ht) + exact h_eps_delta₁ _ (by positivity) + filter_upwards [ h_eps_delta ( ε / 2 ) ( half_pos ε_pos ), self_mem_nhdsWithin, mem_nhdsWithin_of_mem_nhds hU₁ ] with y hy₁ hy₂ hy₃ + apply lt_of_le_of_lt (dist_triangle _ ((A y).cfc (f x)) _) + nlinarith [hU₂ y ⟨ hy₃, hy₂ ⟩, + Real.sqrt_nonneg ( Fintype.card d : ℝ ), + mul_div_cancel₀ ε ( show ( 2 * Real.sqrt ( Fintype.card d : ℝ ) + 1 ) ≠ 0 by positivity ), + norm_nonneg ( ( A y ).cfc ( f y ) - ( A y ).cfc ( f x ) ), + norm_nonneg ( ( A y ).cfc ( f x ) - ( A x ).cfc ( f x ) ), + dist_eq_norm ( ( A y ).cfc ( f y ) ) ( ( A y ).cfc ( f x ) ), + dist_eq_norm ( ( A y ).cfc ( f x ) ) ( ( A x ).cfc ( f x ) )] + +open scoped Matrix.Norms.Frobenius +/- +PROBLEM +Eigenvalues of a `HermitianMat` are bounded by its (Frobenius) norm. +PROVIDED SOLUTION +Use `norm_eq_sum_eigenvalues_sq` which gives `‖A‖² = Σᵢ (A.H.eigenvalues i)²`. Since `(A.H.eigenvalues i)² ≤ Σⱼ (A.H.eigenvalues j)² = ‖A‖²`, we get `|A.H.eigenvalues i| ≤ ‖A‖`. +-/ +lemma eigenvalue_norm_le (A : HermitianMat d ℂ) (i : d) : + |A.H.eigenvalues i| ≤ ‖A‖ := by + have h_eigenvalue_bound : |A.H.eigenvalues i| ^ 2 ≤ ‖A‖ ^ 2 := by + rw [ norm_eq_sum_eigenvalues_sq A ]; + simp [pow_two]; + exact Finset.single_le_sum ( fun i _ => mul_self_nonneg ( A.H.eigenvalues i ) ) ( Finset.mem_univ i ); + nlinarith [ norm_nonneg A ] + +/-- +The spectrum of a `HermitianMat` is contained in the closed ball of radius `‖A‖` around 0. +-/ +lemma spectrum_subset_closedBall (A : HermitianMat d ℂ) : + spectrum ℝ A.mat ⊆ Metric.closedBall (0 : ℝ) ‖A‖ := by + rw [A.H.spectrum_real_eq_range_eigenvalues] + rintro _ ⟨i, rfl⟩ + simp [Metric.mem_closedBall, dist_zero_right, eigenvalue_norm_le] +/- +PROBLEM +Upper semicontinuity of the spectrum of a Hermitian matrix: if the spectrum of `A₀` is contained +in an open set `U`, then the spectrum of any Hermitian matrix sufficiently close to `A₀` is also +contained in `U`. This follows from the openness of the set of invertible matrices and compactness. +PROVIDED SOLUTION +The proof uses the resolvent approach and compactness. +1. Let M = ‖A₀‖ + 1. For B in a ball of radius 1 around A₀, ‖B‖ ≤ M, so spectrum ℝ B.mat ⊆ Metric.closedBall 0 M (by spectrum_subset_closedBall and the triangle inequality for norms). +2. Let K = Metric.closedBall (0 : ℝ) M \ U. Then K is compact (closed and bounded minus open = closed and bounded in ℝ). And K ∩ spectrum ℝ A₀.mat = ∅ (since spectrum ℝ A₀.mat ⊆ U). +3. For each t ∈ K: t ∉ spectrum ℝ A₀.mat. By the definition of spectrum, A₀.mat - algebraMap ℝ (Matrix d d ℂ) t is a unit. The set of units is open (Units.isOpen, since Matrix d d ℂ has HasSummableGeomSeries). The map B ↦ B.mat - algebraMap ℝ _ t is continuous. So there exist δ_t > 0 and ε_t > 0 such that for ‖B - A₀‖ < δ_t and |s - t| < ε_t, B.mat - algebraMap ℝ _ s is a unit, meaning s ∉ spectrum ℝ B.mat. +4. By compactness of K (it's compact since it's a closed subset of the compact ball): finitely many ε-balls B(t_j, ε_{t_j}) cover K. Let δ = min(1, min_j δ_{t_j}). +5. For B with ‖B - A₀‖ < δ: spectrum ℝ B.mat ⊆ Metric.closedBall 0 M (by step 1) and spectrum ℝ B.mat ∩ K = ∅ (by step 4). So spectrum ℝ B.mat ⊆ Metric.closedBall 0 M \ K ⊆ U. +Note: we need to connect spectrum ℝ B.mat (the real spectrum) to IsUnit in the complex matrix ring. Use that for self-adjoint elements, t ∈ spectrum ℝ A.mat iff algebraMap ℝ (Matrix d d ℂ) t ∈ spectrum ℂ A.mat, and the resolvent set is open. We can use spectrum.isOpen_resolventSet or the characterization via IsUnit. +-/ + + +lemma spectrum_subset_of_isOpen (A₀ : HermitianMat d ℂ) (U : Set ℝ) + (hU : IsOpen U) (hAU : spectrum ℝ A₀.mat ⊆ U) : + ∀ᶠ B in nhds A₀, spectrum ℝ B.mat ⊆ U := by + -- Let `M = ‖A₀‖ + 1`. For `B` with `‖B - A₀‖ < 1` we get `‖B‖ ≤ M`, so `σ(B) ⊆ closedBall 0 M`. + obtain ⟨M, hM⟩ : ∃ M : ℝ, ∀ B : HermitianMat d ℂ, ‖B - A₀‖ < 1 → + spectrum ℝ B.mat ⊆ Metric.closedBall 0 M := by + use ‖A₀‖ + 1 + intro B hB + have h_norm : ‖B‖ ≤ ‖A₀‖ + 1 := by + have := norm_sub_norm_le B A₀; linarith + exact (spectrum_subset_closedBall B).trans (Metric.closedBall_subset_closedBall h_norm) + -- `K = closedBall 0 M \ U` is compact and disjoint from `σ(A₀)`. + set K : Set ℝ := Metric.closedBall 0 M \ U + have hK_compact : IsCompact K := IsCompact.diff (ProperSpace.isCompact_closedBall _ _) hU + have hK_disjoint : K ∩ spectrum ℝ A₀.mat = ∅ := + Set.eq_empty_of_forall_notMem fun x hx => hx.1.2 <| hAU hx.2 + -- For each `t ∈ K` there are `δ_t, ε_t > 0` such that `‖B - A₀‖ < δ_t` and `|s - t| < ε_t` + -- make `B.mat - s` a unit: units are open and `A₀.mat - t` is one. + have h_unitary : ∀ t ∈ K, ∃ δ_t > 0, ∃ ε_t > 0, ∀ B : HermitianMat d ℂ, ‖B - A₀‖ < δ_t → + ∀ s : ℝ, |s - t| < ε_t → IsUnit (B.mat - algebraMap ℝ (Matrix d d ℂ) s) := by + intro t ht + have h_unitary : IsUnit (A₀.mat - algebraMap ℝ (Matrix d d ℂ) t) := by + simp_all [Set.ext_iff, spectrum.mem_iff] + simpa using hK_disjoint t ht |> IsUnit.neg |> IsUnit.mul <| isUnit_one + have h_unitary_open : IsOpen {B : Matrix d d ℂ | IsUnit B} := Units.isOpen + have h_unitary_cont : Continuous (fun p : HermitianMat d ℂ × ℝ => + p.1.mat - algebraMap ℝ (Matrix d d ℂ) p.2) := by + refine' Continuous.sub _ _ <;> fun_prop (disch := solve_by_elim) + obtain ⟨ε, ε_pos, hε⟩ := + Metric.isOpen_iff.mp (h_unitary_open.preimage h_unitary_cont) (A₀, t) h_unitary + exact ⟨ε, ε_pos, ε, ε_pos, fun B hB s hs => hε (show (B, s) ∈ Metric.ball (A₀, t) ε from by + simp only [Metric.mem_ball, Prod.dist_eq] + refine max_lt ?_ ?_ + · simpa [dist_eq_norm] using hB + · simpa [dist_eq_norm] using hs)⟩ + -- Compactness gives a finite subcover of `ε`-balls; set `δ = min 1 (min_j δ_j)`. + obtain ⟨δ, hδ_pos, hδ⟩ : ∃ δ > 0, ∀ t ∈ K, ∃ ε_t > 0, ∀ B : HermitianMat d ℂ, ‖B - A₀‖ < δ → + ∀ s : ℝ, |s - t| < ε_t → IsUnit (B.mat - algebraMap ℝ (Matrix d d ℂ) s) := by + choose! δ hδ ε hε h using h_unitary + have := hK_compact.elim_nhds_subcover (fun t => Metric.ball t (ε t)) + fun t ht => Metric.ball_mem_nhds t (hε t ht) + simp_all [Set.subset_def] + obtain ⟨t, ht₁, ht₂⟩ := this + obtain ⟨δ_min, hδ_min_pos, hδ_min⟩ : ∃ δ_min > 0, ∀ i ∈ t, δ_min ≤ δ i := by + by_cases ht : t.Nonempty <;> simp_all [Finset.Nonempty] + · exact ⟨Finset.min' (t.image δ) ⟨_, Finset.mem_image_of_mem δ ht.choose_spec⟩, + by have := Finset.min'_mem (t.image δ) ⟨_, Finset.mem_image_of_mem δ ht.choose_spec⟩; aesop, + fun i hi => Finset.min'_le _ _ (Finset.mem_image_of_mem δ hi)⟩ + · exact ⟨1, zero_lt_one⟩ + refine' ⟨Min.min δ_min 1, lt_min hδ_min_pos zero_lt_one, fun x hx => _⟩ + obtain ⟨i, hi, hi'⟩ := ht₂ x hx + exact ⟨ε i - |x - i|, sub_pos.mpr (by simp_all [abs_sub_comm]; exact hi'), + fun B hB s hs => h i (ht₁ i hi) B (lt_of_lt_of_le hB (min_le_of_left_le (hδ_min i hi))) s + (by rw [abs_lt] at *; constructor <;> linarith [abs_le.mp (show |x - i| ≤ |x - i| by rfl)])⟩ + -- For `B` with `‖B - A₀‖ < δ`, any `t ∈ σ(B)` lies outside `K`. + have h_not_in_K : ∀ B : HermitianMat d ℂ, ‖B - A₀‖ < δ → ∀ t ∈ spectrum ℝ B.mat, t ∉ K := by + intro B hB t ht htK + obtain ⟨ε_t, hε_t_pos, hε_t⟩ := hδ t htK + have h_unit : IsUnit (B.mat - algebraMap ℝ (Matrix d d ℂ) t) := + hε_t B hB t (by simpa using hε_t_pos) + exact ht (by have h1 := h_unit.neg; simp_all; exact h1) + filter_upwards [Metric.ball_mem_nhds A₀ (show 0 < Min.min δ 1 by positivity)] with B hB using + fun t ht => Classical.not_not.1 fun h => h_not_in_K B + (lt_of_lt_of_le (by simpa [dist_eq_norm] using hB) (min_le_left _ _)) t ht + ⟨hM B (lt_of_lt_of_le (by simpa [dist_eq_norm] using hB) (min_le_right _ _)) ht, h⟩ + +/- +PROBLEM +The CFC is continuous in the matrix argument when the function is continuous on a set containing +the spectra, even when that set is not compact. This generalizes `continuousOn_cfc_of_compact`. +The proof uses Tietze extension from the finite spectrum to a compact interval, applies the +compact version, and bounds the error using upper semicontinuity of the spectrum. +PROVIDED SOLUTION +The proof uses Tietze extension and the compact version `continuousOn_cfc_of_compact`. +Step 1: Extend g from the finite set spectrum(A₀) to all of ℝ via Tietze. +The spectrum `Λ := spectrum ℝ A₀.mat` is finite (it equals `Set.range A₀.H.eigenvalues`), hence closed. +The restriction of g to Λ is continuous (any function on a finite set is continuous in a T1 space, using `continuousOn_finite`). +By `ContinuousMap.exists_restrict_eq`, there exists a continuous function `h : C(ℝ, ℝ)` with `h = g` on Λ. +Step 2: (A₀).cfc g = (A₀).cfc h, since g = h on spectrum(A₀) (by `cfc_congr`). +Step 3: Show `B ↦ B.cfc h` is continuous at A₀. +Let M = ‖A₀‖ + 1 and K = Set.Icc (-M) M. Since h is continuous on ℝ and hence on K, and K is compact, by `continuousOn_cfc_of_compact`, the map `B ↦ B.cfc h` is continuous on `{B | spectrum ℝ B.mat ⊆ K}`. Since `{B | spectrum ℝ B.mat ⊆ T} ∩ (Metric.ball A₀ 1) ⊆ {B | spectrum ℝ B.mat ⊆ K}` (because for B near A₀, ‖B‖ ≤ M, so spectrum B ⊆ [-M, M] = K by spectrum_subset_closedBall), the map is ContinuousWithinAt at A₀. +Step 4: Show `‖B.cfc g - B.cfc h‖ → 0` as B → A₀ within `{B | σ(B) ⊆ T}`. +The function `|g - h|` is 0 on Λ = spectrum(A₀). For each eigenvalue λᵢ ∈ Λ ⊆ T: +- g is continuous on T at λᵢ +- h is continuous everywhere +So `|g(t) - h(t)| = |g(t) - g(λᵢ) + h(λᵢ) - h(t)|` is small for t near λᵢ with t ∈ T. +Define U_ε = {t ∈ ℝ | ∀ λ ∈ Λ, if |t - λ| < some δ then |g(t) - h(t)| < ε for t ∈ T} ∪ (complement of a ball around Λ). +Actually, more precisely: the set V_ε = {t : ℝ | t ∈ T → |g(t) - h(t)| < ε} is an open set containing Λ (since |g - h| = 0 on Λ and both are continuous at each point of Λ along T). +By `spectrum_subset_of_isOpen`, for B near A₀, spectrum(B) ⊆ V_ε. +Since spectrum(B) ⊆ T, for t ∈ spectrum(B), |g(t) - h(t)| < ε. +So ‖B.cfc g - B.cfc h‖ ≤ sqrt(d) * ε by `norm_cfc_sub_le_of_sup_le`. +Step 5: Combine. By the triangle inequality: +‖B.cfc g - A₀.cfc g‖ ≤ ‖B.cfc g - B.cfc h‖ + ‖B.cfc h - A₀.cfc h‖ +Both terms → 0, so the map is ContinuousWithinAt. +Use `Metric.continuousWithinAt_iff` and an ε/2 argument. +-/ + + +lemma continuousWithinAt_cfc_of_continuousOn {T : Set ℝ} {g : ℝ → ℝ} + {A₀ : HermitianMat d ℂ} + (hg : ContinuousOn g T) (hA₀ : spectrum ℝ A₀.mat ⊆ T) : + ContinuousWithinAt (fun B ↦ B.cfc g) {B | spectrum ℝ B.mat ⊆ T} A₀ := by + have h_ext : ∃ h : ℝ → ℝ, Continuous h ∧ ∀ x ∈ spectrum ℝ A₀.mat, h x = g x := by + have h_finite : Set.Finite (spectrum ℝ A₀.mat) := by + exact Set.toFinite _ + generalize_proofs at *; ( + have h_cont : ContinuousOn g (spectrum ℝ A₀.val) := by + exact hg.mono hA₀ + generalize_proofs at *; ( + have := @ContinuousMap.exists_restrict_eq ℝ; + specialize this ( show IsClosed ( spectrum ℝ A₀.val ) from h_finite.isClosed ) ( ContinuousMap.mk ( fun x => g x ) <| by exact continuousOn_iff_continuous_domRestrict.mp h_cont ) ; rcases this with ⟨ h, hh ⟩ ; exact ⟨ h, h.continuous, fun x hx => by simpa using congr_arg ( fun f => f ⟨ x, hx ⟩ ) hh ⟩ ;)); + obtain ⟨h, hh_cont, hh_eq⟩ := h_ext; + have h_cfc_cont : ContinuousWithinAt (fun B => B.cfc h) {B : HermitianMat d ℂ | spectrum ℝ B.mat ⊆ T} A₀ := by + exact Continuous.continuousWithinAt (HermitianMat.cfc_continuous hh_cont) + have h_diff_small : ∀ ε > 0, ∃ U ∈ nhds A₀, ∀ B ∈ U ∩ {B : HermitianMat d ℂ | spectrum ℝ B.mat ⊆ T}, ‖B.cfc g - B.cfc h‖ < ε := by + intro ε ε_pos + obtain ⟨δ, δ_pos, hδ⟩ : ∃ δ > 0, ∀ x ∈ T, ∀ y ∈ spectrum ℝ A₀.mat, |x - y| < δ → |g x - h x| < ε / (Real.sqrt (Fintype.card d) + 1) := by + have h_diff_small : ∀ y ∈ spectrum ℝ A₀.mat, ∃ δ > 0, ∀ x ∈ T, |x - y| < δ → |g x - h x| < ε / (Real.sqrt (Fintype.card d) + 1) := by + intro y hy + have h_diff_small : Filter.Tendsto (fun x => |g x - h x|) (nhdsWithin y T) (nhds 0) := by + have h_diff_small : Filter.Tendsto (fun x => g x - h x) (nhdsWithin y T) (nhds (g y - h y)) := by + exact Filter.Tendsto.sub ( hg.continuousWithinAt ( hA₀ hy ) ) ( hh_cont.continuousWithinAt ); + simpa [ hh_eq y hy ] using h_diff_small.abs; + have := Metric.tendsto_nhdsWithin_nhds.mp h_diff_small ( ε / ( Real.sqrt ( Fintype.card d ) + 1 ) ) ( div_pos ε_pos ( add_pos_of_nonneg_of_pos ( Real.sqrt_nonneg _ ) zero_lt_one ) ) ; aesop; + choose! δ hδ_pos hδ using h_diff_small; + have h_finite : Set.Finite (spectrum ℝ A₀.mat) := by + exact Set.toFinite _; + obtain ⟨δ_min, hδ_min_pos, hδ_min⟩ : ∃ δ_min > 0, ∀ y ∈ spectrum ℝ A₀.mat, δ_min ≤ δ y := by + by_cases h_empty : spectrum ℝ A₀.mat = ∅; + · exact ⟨ 1, zero_lt_one, by simp [ h_empty ] ⟩; + · have := h_finite.toFinset.exists_min_image δ; + exact Exists.elim ( this ( Finset.nonempty_of_ne_empty ( by simpa [ Set.ext_iff ] using h_empty ) ) ) fun x hx => ⟨ δ x, hδ_pos x ( by simpa using hx.1 ), fun y hy => hx.2 _ ( h_finite.mem_toFinset.mpr hy ) ⟩; + exact ⟨ δ_min, hδ_min_pos, fun x hx y hy hxy => hδ y hy x hx ( lt_of_lt_of_le hxy ( hδ_min y hy ) ) ⟩; + -- By the spectrum_subset_of_isOpen lemma, there exists a neighborhood U of A₀ such that the spectrum of B is within δ of the spectrum of A₀ for all B in U. + obtain ⟨U, hU⟩ : ∃ U ∈ nhds A₀, ∀ B ∈ U, spectrum ℝ B.mat ⊆ {x | ∃ y ∈ spectrum ℝ A₀.mat, |x - y| < δ} := by + have h_spectrum_subset : ∀ᶠ B in nhds A₀, spectrum ℝ B.mat ⊆ Metric.thickening δ (spectrum ℝ A₀.mat) := by + have h_open : IsOpen (Metric.thickening δ (spectrum ℝ A₀.mat)) := by + exact Metric.isOpen_thickening + have := spectrum_subset_of_isOpen A₀ ( Metric.thickening δ ( spectrum ℝ A₀.mat ) ) h_open ( Metric.self_subset_thickening δ_pos _ ) ; aesop; + generalize_proofs at *; ( + exact ⟨ _, h_spectrum_subset, fun B hB => fun x hx => by simpa [ dist_eq_norm ] using Metric.mem_thickening_iff.mp ( hB hx ) ⟩) + generalize_proofs at *; ( + refine' ⟨ U, hU.1, fun B hB => _ ⟩ + have h_diff_small : ∀ x ∈ spectrum ℝ B.mat, |g x - h x| ≤ ε / (Real.sqrt (Fintype.card d) + 1) := by + exact fun x hx => le_of_lt ( hδ x ( hB.2 hx ) _ ( hU.2 B hB.1 hx |> Classical.choose_spec |> And.left ) ( hU.2 B hB.1 hx |> Classical.choose_spec |> And.right ) ) |> le_trans <| by norm_num; + generalize_proofs at *; ( + have h_diff_small : ‖B.cfc g - B.cfc h‖ ≤ Real.sqrt (Fintype.card d) * (ε / (Real.sqrt (Fintype.card d) + 1)) := by + apply_rules [ norm_cfc_sub_le_of_sup_le ]; + · positivity; + · exact fun x hx => hx + generalize_proofs at *; ( + exact h_diff_small.trans_lt ( by rw [ mul_div, div_lt_iff₀ ] <;> nlinarith [ Real.sqrt_nonneg ( Fintype.card d : ℝ ), Real.sq_sqrt ( Nat.cast_nonneg ( Fintype.card d ) ) ] )))); + rw [ Metric.continuousWithinAt_iff ] at *; + intro ε hε + obtain ⟨δ, hδ_pos, hδ⟩ := h_cfc_cont (ε / 2) (half_pos hε) + obtain ⟨U, hU_nhds, hU⟩ := h_diff_small (ε / 2) (half_pos hε) + use Min.min δ (Metric.mem_nhds_iff.mp hU_nhds).choose + simp [hδ_pos]; + refine' ⟨ _, _ ⟩ + all_goals generalize_proofs at *; + · exact ‹∃ ε, 0 < ε ∧ Metric.ball A₀ ε ⊆ U›.choose_spec.1; + · intro x hx hx' hx'' + have hd1 := hδ hx hx' + have hd2 := hU x ⟨(Metric.mem_nhds_iff.mp hU_nhds).choose_spec.2 hx'', hx⟩ + have h_eq : A₀.cfc g = A₀.cfc h := by + exact cfc_congr (show Set.EqOn g h (spectrum ℝ A₀.mat) from fun x hx => hh_eq x hx ▸ rfl) ▸ rfl + rw [dist_eq_norm, h_eq] + calc ‖x.cfc g - A₀.cfc h‖ + = ‖(x.cfc g - x.cfc h) + (x.cfc h - A₀.cfc h)‖ := by congr 1; abel + _ ≤ ‖x.cfc g - x.cfc h‖ + ‖x.cfc h - A₀.cfc h‖ := norm_add_le _ _ + _ < ε / 2 + ε / 2 := by + apply add_lt_add hd2 + rwa [dist_eq_norm] at hd1 + _ = ε := add_halves ε + +/- +PROBLEM +For `f` jointly continuous on `S ×ˢ T` and the spectrum of `A y` contained in `T`, the difference +`f(y,t) - f(x₀,t)` can be made uniformly small on `spectrum (ℝ) (A y).mat` for `y` near `x₀`. +This is the non-compact replacement for `dist_lt_of_continuous'`: instead of uniform convergence +on a fixed compact set, we get uniform convergence on the (moving, finite) spectrum. +PROVIDED SOLUTION +Constructive proof. The key steps: +Step 1: For each eigenvalue λᵢ := (A x₀).H.eigenvalues i (which is in T by hA₁), the function f is continuous at (x₀, λᵢ) within S ×ˢ T. So there exist open neighborhoods U_i of x₀ and V_i of λᵢ such that for all (y, t) ∈ (U_i ∩ S) × (V_i ∩ T), we have ‖f y t - f x₀ t‖ < ε. Here's how to get this: +- hf (x₀, λᵢ) gives ContinuousWithinAt at (x₀, λᵢ) within S ×ˢ T +- Apply this to the ε-ball around f(x₀, λᵢ), use that f(x₀, λᵢ) - f(x₀, λᵢ) = 0 to get ‖f y t - f x₀ λᵢ‖ < ε/2 +- Similarly, hf at (x₀, λᵢ) restricted to {x₀} × T gives ‖f x₀ t - f x₀ λᵢ‖ < ε/2 +- Triangle inequality: ‖f y t - f x₀ t‖ < ε +- Use `mem_nhds_prod_iff` to extract U_i and V_i from the product neighborhood +Step 2: The open set W := ⋃ᵢ V_i contains spectrum(A x₀) (since each λᵢ ∈ V_i and spectrum = range of eigenvalues). W is open as a union of open sets. By `spectrum_subset_of_isOpen (A x₀) W`, we get ∀ᶠ B in nhds (A x₀), spectrum ℝ B.mat ⊆ W. +Step 3: By ContinuousWithinAt of A at x₀ (from hA₂), and the filter from step 2, we get: ∀ᶠ y in nhdsWithin x₀ S, spectrum ℝ (A y).mat ⊆ W. Convert this to ∃ U' ∈ nhds x₀, ∀ y ∈ U' ∩ S, spectrum(A y) ⊆ W. +Step 4: Take U = U' ∩ ⋂ᵢ U_i (finite intersection since d is Fintype). For y ∈ U ∩ S and t ∈ spectrum(A y): +- t ∈ T (by hA₁) +- spectrum(A y) ⊆ W (by step 3), so t ∈ V_i for some i +- y ∈ U_i ∩ S +- So ‖f y t - f x₀ t‖ < ε (by step 1) +Use `by_contra` and arrive at contradiction, or construct the neighborhood directly using `Filter.inter_mem` and `Filter.iInter_mem` (since d is Fintype, the index set is finite). +IMPORTANT: To get the open sets V_i, use `ContinuousWithinAt` of f at (x₀, λᵢ) which gives an eventually filter statement, then extract using `mem_nhdsWithin_iff_exists_mem_nhds_inter` and `mem_nhds_prod_iff`. +For the continuity of A composed with spectrum_subset_of_isOpen: use `ContinuousWithinAt.eventually` or compose the filter. Specifically: `(hA₂ x₀ hx₀).eventually (spectrum_subset_of_isOpen (A x₀) W hW_open hW_contains)` gives `∀ᶠ y in nhdsWithin x₀ S, spectrum(A y) ⊆ W`. Then use `Filter.Eventually.exists_mem` to get U'. +-/ + +lemma dist_lt_of_continuous_spectrum {X : Type*} [TopologicalSpace X] + {f : X → ℝ → ℝ} {A : X → HermitianMat d ℂ} {S : Set X} {T : Set ℝ} + (hf : ContinuousOn (fun (p : X × ℝ) ↦ f p.1 p.2) (S ×ˢ T)) + (hA₁ : ∀ x ∈ S, spectrum ℝ (A x).mat ⊆ T) + (hA₂ : ContinuousOn (fun x ↦ A x) S) + {x₀ : X} (hx₀ : x₀ ∈ S) {ε : ℝ} (hε : 0 < ε) : + ∃ U ∈ nhds x₀, ∀ y ∈ U ∩ S, ∀ t ∈ spectrum ℝ (A y).mat, ‖f y t - f x₀ t‖ < ε := by + by_contra h_contra; + -- For each eigenvalue λᵢ := (A x₀).H.eigenvalues i (which is in T by hA₁), the function f is continuous at (x₀, λᵢ) within S ×ˢ T. So there exist open neighborhoods U_i of x₀ and V_i of λᵢ such that for all (y, t) ∈ (U_i ∩ S) × (V_i ∩ T), we have ‖f y t - f x₀ t‖ < ε. + obtain ⟨U_i, V_i, hU_i, hV_i, h_cont⟩ : ∃ (U_i : d → Set X) (V_i : d → Set ℝ), (∀ i, IsOpen (U_i i)) ∧ (∀ i, IsOpen (V_i i)) ∧ (∀ i, x₀ ∈ U_i i) ∧ (∀ i, (A x₀).H.eigenvalues i ∈ V_i i) ∧ (∀ i, ∀ y ∈ U_i i ∩ S, ∀ t ∈ V_i i ∩ T, ‖f y t - f x₀ t‖ < ε) := by + have h_cont : ∀ i, ∃ (U_i : Set X) (V_i : Set ℝ), IsOpen U_i ∧ IsOpen V_i ∧ x₀ ∈ U_i ∧ (A x₀).H.eigenvalues i ∈ V_i ∧ ∀ y ∈ U_i ∩ S, ∀ t ∈ V_i ∩ T, ‖f y t - f x₀ t‖ < ε := by + intro i + generalize_proofs at *; ( + have h_cont : ContinuousWithinAt (fun p : X × ℝ => f p.1 p.2 - f x₀ p.2) (S ×ˢ T) (x₀, (A x₀).H.eigenvalues i) := by + have hmemT : (A x₀).H.eigenvalues i ∈ T := + hA₁ x₀ hx₀ ((A x₀).H.eigenvalues_mem_spectrum_real i) + have h1 : ContinuousWithinAt (fun p : X × ℝ => f p.1 p.2) (S ×ˢ T) + (x₀, (A x₀).H.eigenvalues i) := hf (x₀, (A x₀).H.eigenvalues i) ⟨hx₀, hmemT⟩ + have hg : ContinuousWithinAt (fun q : ℝ => f x₀ q) T ((A x₀).H.eigenvalues i) := + (hf (x₀, (A x₀).H.eigenvalues i) ⟨hx₀, hmemT⟩).comp + (continuousWithinAt_const.prodMk continuousWithinAt_id) (fun q hq => ⟨hx₀, hq⟩) + have h2 : ContinuousWithinAt (fun p : X × ℝ => f x₀ p.2) (S ×ˢ T) + (x₀, (A x₀).H.eigenvalues i) := + hg.comp (f := Prod.snd) (x := (x₀, (A x₀).H.eigenvalues i)) + continuousWithinAt_snd (fun p hp => hp.2) + exact h1.sub h2 ) + generalize_proofs at *; ( + have := h_cont.eventually ( Metric.ball_mem_nhds _ hε ) + simp_all [ dist_eq_norm ] + ( + rw [ eventually_nhdsWithin_iff ] at this + generalize_proofs at *; ( + rcases mem_nhds_prod_iff.mp this with ⟨ U, V, hU, hV, h ⟩ + generalize_proofs at *; ( + exact ⟨ interior U, isOpen_interior, interior hU, isOpen_interior, mem_interior_iff_mem_nhds.mpr V, mem_interior_iff_mem_nhds.mpr hV, fun y hy hyS t ht htT => h ( Set.mk_mem_prod ( interior_subset hy ) ( interior_subset ht ) ) ⟨ hyS, htT ⟩ ⟩)))) + generalize_proofs at *; ( + choose U_i V_i hU_i hV_i hx₀_i hV_i_i h_cont_i using h_cont; exact ⟨ U_i, V_i, hU_i, hV_i, hx₀_i, hV_i_i, h_cont_i ⟩ ;); + -- The open set W := ⋃ᵢ V_i contains spectrum(A x₀) (since each λᵢ ∈ V_i and spectrum = range of eigenvalues). W is open as a union of open sets. + set W := ⋃ i, V_i i with hW_def + have hW_open : IsOpen W := by + exact isOpen_iUnion hV_i + have hW_spectrum : spectrum ℝ (A x₀).mat ⊆ W := by + intro t ht + obtain ⟨i, hi⟩ : ∃ i, t = (A x₀).H.eigenvalues i := by + have h_eigenvalues : spectrum ℝ (A x₀).mat = Set.range (A x₀).H.eigenvalues := + (A x₀).H.spectrum_real_eq_range_eigenvalues + generalize_proofs at *; ( + exact h_eigenvalues.subset ht |> Exists.imp fun i hi => hi.symm) + aesop + have hW_subset : ∀ᶠ B in nhds (A x₀), spectrum ℝ B.mat ⊆ W := by + exact spectrum_subset_of_isOpen (A x₀) W hW_open hW_spectrum + have hW_subset_S : ∀ᶠ y in nhdsWithin x₀ S, spectrum ℝ (A y).mat ⊆ W := by + exact Filter.mem_of_superset ( hA₂.continuousWithinAt hx₀ |> fun h => h.eventually ( hW_subset ) ) fun y hy => hy + obtain ⟨U', hU'⟩ : ∃ U' ∈ nhds x₀, ∀ y ∈ U' ∩ S, spectrum ℝ (A y).mat ⊆ W := by + obtain ⟨ U', hU' ⟩ := mem_nhdsWithin_iff_exists_mem_nhds_inter.mp hW_subset_S; use U'; aesop; + obtain ⟨U'', hU''⟩ : ∃ U'' ∈ nhds x₀, ∀ i, U'' ⊆ U_i i := by + exact ⟨ ⋂ i, U_i i, Filter.mem_of_superset ( Filter.iInter_mem.mpr fun i => IsOpen.mem_nhds ( hU_i i ) ( h_cont.1 i ) ) fun x hx => by aesop, fun i => Set.iInter_subset _ i ⟩ + set U := U' ∩ U'' with hU_def + have hU_mem : U ∈ nhds x₀ := by + exact Filter.inter_mem hU'.1 hU''.1 + have hU_subset : ∀ y ∈ U ∩ S, spectrum ℝ (A y).mat ⊆ W := by + exact fun y hy => hU'.2 y ⟨ hy.1.1, hy.2 ⟩ |> Set.Subset.trans <| by simp [ hW_def ] ; + have hU_cont : ∀ y ∈ U ∩ S, ∀ t ∈ spectrum ℝ (A y).mat, ‖f y t - f x₀ t‖ < ε := by + intro y hy t ht + obtain ⟨i, hi⟩ : ∃ i, t ∈ V_i i := by + exact Set.mem_iUnion.mp ( hU_subset y hy ht ) |> Exists.imp fun i => by tauto; + have h_cont_i : ‖f y t - f x₀ t‖ < ε := by + exact h_cont.2.2 i y ⟨ hU''.2 i ( by aesop ), hy.2 ⟩ t ⟨ hi, hA₁ y hy.2 ht ⟩ |> fun h => by simpa using h; + exact h_cont_i + exact h_contra ⟨U, hU_mem, hU_cont⟩ + +/- +PROBLEM +Joint continuity of the functional calculus, without requiring compactness of `T`. +This generalizes `continuous_cfc_joint_compact` by removing the `IsCompact T` hypothesis. +The compactness is unnecessary because the spectrum of a `HermitianMat` (which is +finite-dimensional) is always finite and hence compact. The proof works by reducing to +the compact case locally: at each point `x₀ ∈ S`, the spectrum of `A x₀` is finite and +contained in a compact interval `K = [-M, M]`, and the compact version is applied with a +continuous extension of `f x₀` from the finite spectrum to `K`. +PROVIDED SOLUTION +The proof follows the same structure as `continuous_cfc_joint_compact` but uses the non-compact helpers `dist_lt_of_continuous_spectrum` and `continuousWithinAt_cfc_of_continuousOn` instead of `dist_lt_of_continuous'` and `continuousOn_cfc_of_compact`. +Fix x ∈ S. Show ContinuousWithinAt. +Step 1: Show `ContinuousWithinAt (fun y => (A y).cfc (f x)) S x`. +The function f x (i.e., f(x, ·)) is continuous on T: this follows from `hf.uncurry_left x x_in_S` which gives `ContinuousOn (f x) T`. +By `continuousWithinAt_cfc_of_continuousOn` with g = f x and T = T: + `ContinuousWithinAt (fun B ↦ B.cfc (f x)) {B | spectrum ℝ B.mat ⊆ T} (A x)`. +Compose with `hA₂.continuousWithinAt x_in_S` and `hA₁`: + `ContinuousWithinAt (fun y => (A y).cfc (f x)) S x`. +Note: We need that `fun y => A y` maps `S` into `{B | spectrum ℝ B.mat ⊆ T}`, which follows from `hA₁`. +Step 2: Use the triangle inequality, exactly as in `continuous_cfc_joint_compact`: +Decompose the goal using: + dist ((A y).cfc (f y)) ((A x).cfc (f x)) ≤ dist ((A y).cfc (f y)) ((A y).cfc (f x)) + dist ((A y).cfc (f x)) ((A x).cfc (f x)) +For the first term (f varies, A = A(y)): +Use `dist_lt_of_continuous_spectrum` (our new helper) to get: for any ε > 0, there exists U ∈ nhds x such that for y ∈ U ∩ S and t ∈ spectrum(A y), ‖f y t - f x t‖ < ε. +Then by `norm_cfc_sub_le_of_sup_le`, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ sqrt(d) * ε. +For the second term (A varies, f = f(x)): +Use step 1 directly (ContinuousWithinAt of B ↦ B.cfc (f x)). +Combine with the same nlinarith/ε-δ argument as in `continuous_cfc_joint_compact`. +In code, the proof structure should mirror continuous_cfc_joint_compact closely, just replacing: +- `dist_lt_of_continuous' hT hf x_in_S ε_pos` with `dist_lt_of_continuous_spectrum hf hA₁ hA₂ x_in_S ε_pos` +- `continuousOn_cfc_of_compact hT (hf.uncurry_left x x_in_S)` with `continuousWithinAt_cfc_of_continuousOn (hf.uncurry_left x x_in_S) (hA₁ x x_in_S)` composed with hA₂ and hA₁. +-/ + +@[fun_prop] +theorem continuous_cfc_joint {X d : Type*} [TopologicalSpace X] [Fintype d] [DecidableEq d] + {f : X → ℝ → ℝ} {A : X → HermitianMat d ℂ} {S : Set X} {T : Set ℝ} + (hf : ContinuousOn (fun (p : X × ℝ) ↦ f p.1 p.2) (S ×ˢ T)) + (hA₁ : ∀ x ∈ S, spectrum ℝ (A x).mat ⊆ T) + (hA₂ : ContinuousOn (fun x ↦ A x) S) : + ContinuousOn (fun x ↦ (A x).cfc (f x)) S := by + by_contra h_not_cont_at_x₀ + generalize_proofs at *; + have h_cont : ∀ x₀ ∈ S, ContinuousWithinAt (fun x => (A x).cfc (f x)) S x₀ := by + intro x₀ hx₀ + have h_cont : ContinuousWithinAt (fun x => (A x).cfc (f x₀)) S x₀ := by + have h_cont : ContinuousWithinAt (fun B => B.cfc (f x₀)) {B | spectrum ℝ B.mat ⊆ T} (A x₀) := by + apply_rules [ continuousWithinAt_cfc_of_continuousOn, hf.uncurry_left x₀ hx₀ ] + generalize_proofs at *; ( + exact h_cont.comp ( hA₂.continuousWithinAt hx₀ ) ( by aesop ) |> ContinuousWithinAt.mono <| by aesop;) + generalize_proofs at *; ( + -- By the triangle inequality, we can bound the distance between $(A x).cfc (f x)$ and $(A x₀).cfc (f x₀)$. + have h_triangle : ∀ᶠ x in nhdsWithin x₀ S, ‖(A x).cfc (f x) - (A x).cfc (f x₀)‖ ≤ Real.sqrt (Fintype.card d) * (⨆ t ∈ spectrum ℝ (A x).mat, ‖f x t - f x₀ t‖) := by + refine' Filter.Eventually.of_forall fun x => _; + exact norm_cfc_sub_cfc_le_sqrt_card + generalize_proofs at *; ( + -- By the properties of the supremum, we can bound the distance between $(A x).cfc (f x)$ and $(A x₀).cfc (f x₀)$. + have h_sup : Filter.Tendsto (fun x => ⨆ t ∈ spectrum ℝ (A x).mat, ‖f x t - f x₀ t‖) (nhdsWithin x₀ S) (nhds 0) := by + have h_sup : ∀ ε > 0, ∃ U ∈ nhdsWithin x₀ S, ∀ x ∈ U, ∀ t ∈ spectrum ℝ (A x).mat, ‖f x t - f x₀ t‖ < ε := by + intro ε ε_pos + generalize_proofs at *; ( + have := dist_lt_of_continuous_spectrum hf hA₁ hA₂ hx₀ ε_pos + generalize_proofs at *; ( + obtain ⟨ U, hU₁, hU₂ ⟩ := this; exact ⟨ U ∩ S, mem_nhdsWithin_iff_exists_mem_nhds_inter.mpr ⟨ U, hU₁, by simp ⟩, fun x hx t ht => hU₂ x ⟨ hx.1, hx.2 ⟩ t ht ⟩ ;)) + generalize_proofs at *; ( + refine' Metric.tendsto_nhds.mpr _; + intro ε ε_pos; rcases h_sup ( ε / 2 ) ( half_pos ε_pos ) with ⟨ U, hU₁, hU₂ ⟩ ; filter_upwards [ hU₁ ] with x hx; simp_all [ dist_eq_norm ] ; ( + rw [ abs_of_nonneg ( Real.iSup_nonneg fun _ => Real.iSup_nonneg fun _ => abs_nonneg _ ) ] ; refine' lt_of_le_of_lt ( ciSup_le fun t => _ ) ( half_lt_self ε_pos ) ; by_cases ht : t ∈ spectrum ℝ ( A x |> HermitianMat.mat ) <;> simp_all [ abs_lt ] ; + · exact abs_le.mpr ⟨ by linarith [ hU₂ x hx t ht ], by linarith [ hU₂ x hx t ht ] ⟩; + · linarith [ ε_pos ])) + generalize_proofs at *; ( + have h_final : Filter.Tendsto (fun x => ‖(A x).cfc (f x) - (A x).cfc (f x₀)‖) (nhdsWithin x₀ S) (nhds 0) := by + exact squeeze_zero_norm' ( by filter_upwards [ h_triangle ] with x hx; simpa using hx ) ( by simpa using h_sup.const_mul _ ) |> fun h => h.trans ( by simp ) ; + generalize_proofs at *; ( + convert h_cont.add ( show ContinuousWithinAt ( fun x => ( A x |> HermitianMat.cfc ) ( f x ) - ( A x |> HermitianMat.cfc ) ( f x₀ ) ) S x₀ from ?_ ) using 1 ; aesop + generalize_proofs at *; ( + exact tendsto_zero_iff_norm_tendsto_zero.mpr h_final |> fun h => h.trans ( by simp) ;))))) + generalize_proofs at *; ( + exact h_not_cont_at_x₀ <| fun x hx => h_cont x hx |> ContinuousWithinAt.mono <| by simp;) + +/-- Specialization of `continuousOn_cfc_fun` for nonsingular matrices. -/ +@[fun_prop] +theorem continuousOn_cfc_fun_nonsingular {f : X → ℝ → ℝ} {S : Set X} + (hf : ∀ i ≠ 0, ContinuousOn (f · i) S) [NonSingular A] : + ContinuousOn (fun x ↦ A.cfc (f x)) S := by + apply continuousOn_cfc_fun (T := {0}ᶜ) + · exact hf + · grind [nonSingular_zero_notMem_spectrum] + +/-- Specialization of `continuousOn_cfc_fun` for positive semidefinite matrices. -/ +@[fun_prop] +theorem continuousOn_cfc_fun_nonneg {f : X → ℝ → ℝ} {S : Set X} + (hf : ∀ i ≥ 0, ContinuousOn (f · i) S) (hA : 0 ≤ A) : + ContinuousOn (fun x ↦ A.cfc (f x)) S := by + apply continuousOn_cfc_fun (T := Set.Ici 0) + · exact hf + · rw [zero_le_iff] at hA + exact hA.pos_of_mem_spectrum + +open ComplexOrder in +/-- Specialization of `continuousOn_cfc_fun` for positive definite matrices. -/ +@[fun_prop] +theorem continuousOn_cfc_fun_posDef {f : X → ℝ → ℝ} {S : Set X} + (hf : ∀ i > 0, ContinuousOn (f · i) S) (hA : A.mat.PosDef) : + ContinuousOn (fun x ↦ A.cfc (f x)) S := by + apply continuousOn_cfc_fun (T := Set.Ioi 0) + · exact hf + · exact Matrix.PosDef.spectrum_subset_Ioi hA + +variable {A B : HermitianMat d 𝕜} (f : ℝ → ℝ) + +/-- +The inverse of the CFC is the CFC of the inverse function. +-/ +lemma inv_cfc_eq_cfc_inv (hf : ∀ i, f (A.H.eigenvalues i) ≠ 0) : + (A.cfc f)⁻¹ = A.cfc (fun u ↦ (f u)⁻¹) := by + suffices (A.cfc f).mat⁻¹ = (A.cfc (fun u ↦ 1 / f u)).mat by + ext1 + simpa using this + have h_def : (A.cfc f).mat = ∑ i, f (A.H.eigenvalues i) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + exact cfc_toMat_eq_sum_smul_proj A f; + have h_subst : (A.cfc (fun u ↦ 1 / f u)).mat = ∑ i, (1 / f (A.H.eigenvalues i)) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + exact cfc_toMat_eq_sum_smul_proj A fun u ↦ 1 / f u; + have h_inv : (A.cfc f).mat * (A.cfc (fun u ↦ 1 / f u)).mat = 1 := by + -- Since the eigenvectorUnitary is unitary, we have that the product of the projections is the identity matrix. + have h_unitary : A.H.eigenvectorUnitary.val * A.H.eigenvectorUnitary.val.conjTranspose = 1 := by + simp [ Matrix.IsHermitian.eigenvectorUnitary ]; + have h_inv : ∀ i j, (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) * (A.H.eigenvectorUnitary.val * (Matrix.single j j 1) * A.H.eigenvectorUnitary.val.conjTranspose) = if i = j then A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose else 0 := by + simp [ ← Matrix.mul_assoc ]; + intro i j; split_ifs <;> simp_all [ Matrix.mul_assoc, mul_eq_one_comm.mp h_unitary ] ; + simp_all [ Finset.sum_mul, Finset.mul_sum ]; + have h_sum : ∑ i, (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) = A.H.eigenvectorUnitary.val * (∑ i, Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose := by + simp [ Finset.mul_sum, Finset.sum_mul, Matrix.mul_assoc ]; + simp_all [ Matrix.single ]; + convert h_unitary using 2; + ext i j; simp [ Matrix.mul_apply] + simp [ Matrix.sum_apply, Finset.filter_eq', Finset.filter_and ]; + rw [ Finset.sum_eq_single j ] <;> aesop; + rw [ Matrix.inv_eq_right_inv h_inv ]; + +theorem cfc_inv [NonSingular A] : A.cfc (fun u ↦ u⁻¹) = A⁻¹ := by + simpa using (inv_cfc_eq_cfc_inv id nonSingular_eigenvalue_ne_zero).symm + +section integral + +open MeasureTheory +open scoped Matrix.Norms.Frobenius + +omit [DecidableEq d] in + +/-- +The integral of a Hermitian matrix function commutes with `toMat`. +-/ +lemma integral_toMat (A : ℝ → HermitianMat d 𝕜) (T₁ T₂ : ℝ) {μ : Measure ℝ} + (hA : IntervalIntegrable A μ T₁ T₂) : + (∫ t in T₁..T₂, A t ∂μ).mat = ∫ t in T₁..T₂, (A t).mat ∂μ := by + exact ((matₗ (R := ℝ)).intervalIntegral_comp_comm hA).symm + +/-- +A sum of scaled constant matrices is integrable if the scalar functions are integrable. +-/ +lemma intervalIntegrable_sum_smul_const (T₁ T₂ : ℝ) {μ : Measure ℝ} (g : ℝ → d → ℝ) + (P : d → Matrix d d 𝕜) (hg : ∀ i, IntervalIntegrable (fun t ↦ g t i) μ T₁ T₂) : + IntervalIntegrable (fun t ↦ ∑ i, g t i • P i) μ T₁ T₂ := by + simp_all [intervalIntegrable_iff] + exact integrable_finsetSum _ fun i _ ↦ Integrable.smul_const (hg i) _ + +/-- +A function to Hermitian matrices is integrable iff its matrix values are integrable. +-/ +lemma intervalIntegrable_toMat_iff (A : ℝ → HermitianMat d 𝕜) (T₁ T₂ : ℝ) {μ : Measure ℝ} : + IntervalIntegrable (fun t ↦ (A t).mat) μ T₁ T₂ ↔ IntervalIntegrable A μ T₁ T₂ := by + --TODO Cleanup + simp [ intervalIntegrable_iff ]; + constructor <;> intro h; + · -- Since `toMat` is a linear isometry, the integrability of `A.toMat` implies the integrability of `A`. + have h_toMat_integrable : IntegrableOn (fun t ↦ (A t).mat) (Set.uIoc T₁ T₂) μ → IntegrableOn A (Set.uIoc T₁ T₂) μ := by + intro h_toMat_integrable + have h_toMat_linear : ∃ (L : HermitianMat d 𝕜 →ₗ[ℝ] Matrix d d 𝕜), ∀ x, L x = x.mat := by + refine' ⟨ _, _ ⟩; + refine' { .. }; + exacts [ fun x ↦ x.mat, fun x y ↦ rfl, fun m x ↦ rfl, fun x ↦ rfl ]; + obtain ⟨L, hL⟩ := h_toMat_linear; + have h_toMat_linear : IntegrableOn (fun t ↦ L (A t)) (Set.uIoc T₁ T₂) μ → IntegrableOn A (Set.uIoc T₁ T₂) μ := by + intro h_toMat_integrable + have h_toMat_linear : ∃ (L_inv : Matrix d d 𝕜 →ₗ[ℝ] HermitianMat d 𝕜), ∀ x, L_inv (L x) = x := by + have h_toMat_linear : Function.Injective L := by + intro x y hxy; + simp_all only [HermitianMat.ext_iff] + have h_toMat_linear : ∃ (L_inv : Matrix d d 𝕜 →ₗ[ℝ] HermitianMat d 𝕜), L_inv.comp L = LinearMap.id := by + exact IsSemisimpleModule.extension_property L h_toMat_linear LinearMap.id; + exact ⟨ h_toMat_linear.choose, fun x ↦ by simpa using LinearMap.congr_fun h_toMat_linear.choose_spec x ⟩; + obtain ⟨ L_inv, hL_inv ⟩ := h_toMat_linear; + have h_toMat_linear : IntegrableOn (fun t ↦ L_inv (L (A t))) (Set.uIoc T₁ T₂) μ := by + exact ContinuousLinearMap.integrable_comp ( L_inv.toContinuousLinearMap ) h_toMat_integrable; + aesop; + aesop; + exact h_toMat_integrable h; + · apply h.norm.mono' + · have := h.aestronglyMeasurable; + fun_prop + · filter_upwards with t using le_rfl + +/-- +The CFC of an integrable function family is integrable. +-/ +lemma integrable_cfc (T₁ T₂ : ℝ) (f : ℝ → ℝ → ℝ) {μ : Measure ℝ} + (hf : ∀ i, IntervalIntegrable (fun t ↦ f t (A.H.eigenvalues i)) μ T₁ T₂) : + IntervalIntegrable (fun t ↦ A.cfc (f t)) μ T₁ T₂ := by + have h_expand : ∀ t, (A.cfc (f t)).mat = ∑ i, f t (A.H.eigenvalues i) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + exact fun t ↦ cfc_toMat_eq_sum_smul_proj A (f t); + rw [ ← intervalIntegrable_toMat_iff ]; + rw [ funext h_expand ]; + apply intervalIntegrable_sum_smul_const + exact hf + +/-- +The integral of the CFC is the CFC of the integral. +-/ +lemma integral_cfc_eq_cfc_integral (T₁ T₂ : ℝ) {μ : Measure ℝ} (f : ℝ → ℝ → ℝ) + (hf : ∀ i, IntervalIntegrable (fun t ↦ f t (A.H.eigenvalues i)) μ T₁ T₂) : + ∫ t in T₁..T₂, A.cfc (f t) ∂μ = A.cfc (fun u ↦ ∫ t in T₁..T₂, f t u ∂μ) := by + ext1 + rw [ integral_toMat ]; + · rw [ intervalIntegral.integral_congr fun t ht ↦ + HermitianMat.cfc_toMat_eq_sum_smul_proj A ( f t ), intervalIntegral.integral_finsetSum ]; + · rw [ Finset.sum_congr rfl fun i _ ↦ intervalIntegral.integral_smul_const _ _ ]; + exact Eq.symm (cfc_toMat_eq_sum_smul_proj A fun u ↦ ∫ (t : ℝ) in T₁..T₂, f t u ∂μ); + · simp_all [ intervalIntegrable_iff ]; + exact fun i ↦ ( hf i ).smul_const _ + · exact integrable_cfc T₁ T₂ f hf + +end integral + +theorem cfc_pos_of_pos {A : HermitianMat d 𝕜} {f : ℝ → ℝ} (hA : 0 < A) + (hf : ∀ i > 0, 0 < f i) (hf₂ : 0 ≤ f 0) : 0 < A.cfc f := by + have h_pos := (posSemidef_iff_spectrum_nonneg A).mp hA.le + have h_f_pos : ∃ x ∈ spectrum ℝ (A.cfc f).mat, x ≠ 0 := by + obtain ⟨ x, hx₁, hx₂ ⟩ := ne_zero_iff_ne_zero_spectrum A |>.1 hA.ne' + exact ⟨ f x, by simpa using HermitianMat.spectrum_cfc_eq_image A f ▸ Set.mem_image_of_mem f hx₁, + by cases lt_or_gt_of_ne hx₂ <;> + linarith [ hf x ( lt_of_le_of_ne ( h_pos x hx₁ ) ( Ne.symm hx₂ ) ) ] ⟩; + have h_f_nonneg : 0 ≤ A.cfc f := by + rw [HermitianMat.posSemidef_iff_spectrum_nonneg]; + rw [ HermitianMat.spectrum_cfc_eq_image ]; + rintro _ ⟨ x, hx, rfl ⟩ ; exact if hx0 : x = 0 then by + simpa [ hx0 ] using hf₂ else hf x ( lt_of_le_of_ne ( h_pos x hx ) ( Ne.symm hx0 ) ) |> + le_of_lt; + have h_f_nonzero : A.cfc f ≠ 0 := by + contrapose! h_f_pos; + simp [h_f_pos, spectrum.mem_iff, Matrix.isUnit_iff_isUnit_det, Algebra.algebraMap_eq_smul_one] + exact lt_of_le_of_ne h_f_nonneg h_f_nonzero.symm + +/-- If two matrices A and B commute, then they is a common matrix with which they are both CFCs of. +This is a variant of the common theorem that "commuting matrices can be simultaneously diagonalized." -/ +theorem _root_.Commute.exists_HermitianMat_cfc (hAB : Commute A.mat B.mat) : + ∃ C : HermitianMat d 𝕜, (∃ f : ℝ → ℝ, A = C.cfc f) ∧ (∃ g : ℝ → ℝ, B = C.cfc g) := by + obtain ⟨C, ⟨g₁, hg₁⟩, ⟨g₂, hg₂⟩⟩ := hAB.exists_cfc A.H B.H + by_cases hC : C.IsHermitian + · use ⟨C, hC⟩ + constructor + · exact ⟨g₁, by simp [HermitianMat.ext_iff, hg₁]⟩ + · exact ⟨g₂, by simp [HermitianMat.ext_iff, hg₂]⟩ + · change ¬(IsSelfAdjoint C) at hC + rw [cfc_apply_of_not_predicate C hC] at hg₁ hg₂ + use 0 + constructor + · exact ⟨0, by simp [HermitianMat.ext_iff, hg₁]⟩ + · exact ⟨0, by simp [HermitianMat.ext_iff, hg₂]⟩ + +open ComplexOrder in +theorem cfc_le_cfc_of_PosDef (hfg : ∀ i, 0 < i → f i ≤ g i) (hA : A.mat.PosDef) : + A.cfc f ≤ A.cfc g := by + rw [← sub_nonneg, ← HermitianMat.cfc_sub, cfc_nonneg_iff] + intro i + rw [Pi.sub_apply, sub_nonneg] + rw [A.H.posDef_iff_eigenvalues_pos] at hA + apply hfg + apply hA + +open ComplexOrder in +variable {f} in +/- TODO: Write a version of this that holds more broadly for some sets. Esp closed intervals of reals, +which correspond nicely to closed intervals of matrices. Write the specialization to Set.univ (Monotone +instead of MonotoneOn). Also a version that works for StrictMonoOn. -/ +theorem cfc_le_cfc_of_commute_monoOn (hf : MonotoneOn f (Set.Ioi 0)) + (hAB₁ : Commute A.mat B.mat) (hAB₂ : A ≤ B) (hA : A.mat.PosDef) (hB : B.mat.PosDef) : + A.cfc f ≤ B.cfc f := by + obtain ⟨C, ⟨g₁, rfl⟩, ⟨g₂, rfl⟩⟩ := hAB₁.exists_HermitianMat_cfc + -- Need to show that g₁ ≤ g₂ on spectrum ℝ C + rw [← C.cfc_comp, ← C.cfc_comp] + rw [← sub_nonneg, ← C.cfc_sub, cfc_nonneg_iff] at hAB₂ ⊢ + intro i + simp only [Pi.sub_apply, Function.comp_apply, sub_nonneg] + apply hf + · rw [cfc_posDef] at hA + exact hA i + · rw [cfc_posDef] at hB + exact hB i + · simpa using hAB₂ i + +/-- TODO: See above -/ +theorem cfc_le_cfc_of_commute (hf : Monotone f) (hAB₁ : Commute A.mat B.mat) (hAB₂ : A ≤ B) : + A.cfc f ≤ B.cfc f := by + obtain ⟨C, ⟨g₁, rfl⟩, ⟨g₂, rfl⟩⟩ := hAB₁.exists_HermitianMat_cfc + -- Need to show that g₁ ≤ g₂ on spectrum ℝ C + rw [← C.cfc_comp, ← C.cfc_comp] + rw [← sub_nonneg, ← C.cfc_sub, cfc_nonneg_iff] at hAB₂ ⊢ + intro i + simp only [Pi.sub_apply, Function.comp_apply, sub_nonneg] + apply hf + simpa using hAB₂ i + +--This is the more general version that requires operator concave functions but doesn't require the inputs +-- to commute. Requires the correct statement of operator convexity though, which we don't have right now. +open ComplexOrder in +theorem cfc_monoOn_pos_of_monoOn_posDef {d : Type*} [Fintype d] [DecidableEq d] + {f : ℝ → ℝ} (hf_is_operator_convex : False) : + MonotoneOn (HermitianMat.cfc · f) { A : HermitianMat d ℂ | A.mat.PosDef } := by + exact False.elim hf_is_operator_convex + +section uncategorized_cleanup + +open ComplexOrder in +theorem inv_ge_one_of_le_one (hA : A.mat.PosDef) (h : A ≤ 1) : 1 ≤ A⁻¹ := by + -- Since $A$ is positive definite and $A \leq 1$, we have $A.cfc (fun x => x⁻¹ - 1) \geq 0$. + have h_cfc_nonneg : 0 ≤ A.cfc (fun x => x⁻¹ - 1) := by + have h_cfc_nonneg : ∀ i, 0 ≤ (A.H.eigenvalues i)⁻¹ - 1 := by + have h_pos : ∀ i, 0 < A.H.eigenvalues i ∧ A.H.eigenvalues i ≤ 1 := by + -- Since $A$ is positive definite, all its eigenvalues are positive. + have h_pos : ∀ i, 0 < A.H.eigenvalues i := by + exact fun i => Matrix.PosDef.eigenvalues_pos hA i; + -- Since $A \leq 1$, for any eigenvalue $\lambda_i$ of $A$, we have $\lambda_i \leq 1$. + have h_le_one : ∀ i, A.H.eigenvalues i ≤ 1 := by + have h_le_one : ∀ i, A.H.eigenvalues i ≤ 1 := by + intro i + have h_eigenvalue : A.mat.PosSemidef := by + exact hA.posSemidef + have h_eigenvalue_le_one : ∀ x : d → 𝕜, x ≠ 0 → (star x ⬝ᵥ A.mat.mulVec x) / (star x ⬝ᵥ x) ≤ 1 := by + intro x hx_ne_zero + have h_eigenvalue_le_one : (star x ⬝ᵥ (1 - A.mat).mulVec x) ≥ 0 := by + exact Matrix.PosSemidef.dotProduct_mulVec_nonneg h x + generalize_proofs at *; ( + rw [ div_le_iff₀ ] <;> simp_all [ Matrix.sub_mulVec, dotProduct_sub ]) + generalize_proofs at *; ( + have := h_eigenvalue_le_one ( A.H.eigenvectorBasis i ) ?_ <;> simp_all [ div_le_iff₀, ]; + · have := Matrix.IsHermitian.mulVec_eigenvectorBasis ( show Matrix.IsHermitian ( A : Matrix d d _ ) from ‹_› ) i; simp_all [ dotProduct_comm ] ; + by_cases h : ( A.H.eigenvectorBasis i |> WithLp.ofLp ) ⬝ᵥ star ( A.H.eigenvectorBasis i |> WithLp.ofLp ) = 0 <;> simp_all [ div_le_iff₀ ] ; ( + exact absurd h ( by exact ne_of_apply_ne ( fun x => ‖x‖ ) ( by simp ) )); + · exact fun h => by simpa [ h ] using ( A.H.eigenvectorBasis.orthonormal.ne_zero i ) ;) + generalize_proofs at *; ( + exact h_le_one) + exact fun i => ⟨h_pos i, h_le_one i⟩; + exact fun i => sub_nonneg_of_le ( one_le_inv₀ ( h_pos i |>.1 ) |>.2 ( h_pos i |>.2 ) ); + exact (cfc_nonneg_iff A fun x => x⁻¹ - 1).mpr h_cfc_nonneg; + -- Since $A.cfc (fun x => x⁻¹ - 1) \geq 0$, we have $A.cfc (fun x => x⁻¹) \geq 1$. + have h_cfc_ge_one : A.cfc (fun x => x⁻¹) ≥ 1 := by + have h_cfc_sub : A.cfc (fun x => x⁻¹ - 1) = A.cfc (fun x => x⁻¹) - A.cfc (fun _ => 1) := by + exact cfc_sub_apply A Inv.inv fun x => 1; + aesop; + convert h_cfc_ge_one.le using 1; + convert cfc_inv.symm; + exact nonSingular_of_posDef hA + +/-- The trace of cfc(f, A) equals the sum of f applied to eigenvalues. -/ +lemma trace_cfc_eq (A : HermitianMat d ℂ) (f : ℝ → ℝ) : + (A.cfc f).trace = ∑ i, f (A.H.eigenvalues i) := by + have h1 := HermitianMat.trace_eq_trace (A.cfc f) + obtain ⟨e, he⟩ := HermitianMat.cfc_eigenvalues f A + have h2 := (A.cfc f).H.trace_eq_sum_eigenvalues + rw [he] at h2 + simp [Function.comp] at h2 + rw [HermitianMat.mat_cfc] at h1 + rw [h2] at h1 + have h3 : (Complex.ofReal) (A.cfc f).trace = Complex.ofReal (∑ i, f (A.H.eigenvalues (e i))) := by + convert! h1 using 1 + simp + have h4 := Complex.ofReal_injective h3 + rw [h4] + exact Equiv.sum_comp e (fun x => f (A.H.eigenvalues x)) + +end uncategorized_cleanup + +lemma mulVec_eq_zero_iff_inner_eigenvector_zero + (A : HermitianMat d ℂ) (x : EuclideanSpace ℂ d) : + A.mat.mulVec x = 0 ↔ ∀ i, A.H.eigenvalues i ≠ 0 → inner ℂ (A.H.eigenvectorBasis i) x = 0 := by + -- Since the eigenvectors form an orthonormal basis, we can express x as a linear combination of these eigenvectors. + obtain ⟨c, hc⟩ : ∃ c : d → ℂ, x = ∑ i, c i • A.H.eigenvectorBasis i := by + have := A.H.eigenvectorBasis.sum_repr x; + exact ⟨ _, this.symm ⟩; + -- By definition of $A$, we know that $A.mulVec (x.ofLp) = \sum_{i} c_i \lambda_i e_i$. + have h_mulVec : A.val.mulVec (x.ofLp) = ∑ i, c i • (A.H.eigenvalues i) • (A.H.eigenvectorBasis i) := by + have h_mulVec : ∀ i, A.val.mulVec (A.H.eigenvectorBasis i) = (A.H.eigenvalues i) • (A.H.eigenvectorBasis i) := by + intro i + have := A.H.mulVec_eigenvectorBasis i + aesop; + convert congr_arg ( fun y => ( ∑ i, c i • y i ) ) ( funext fun i => h_mulVec i ) using 1; + · simp [ hc, ]; + ext i; rw [ Matrix.mulVec, dotProduct ] + simp [ Finset.mul_sum _ _ _, mul_assoc, mul_comm ] + rw [ Finset.sum_comm ] + simp [ Matrix.mulVec, dotProduct, mul_comm, Finset.mul_sum _ _ _ ] + · ext i; simp [ Finset.sum_apply ] ; + constructor; + · intro h i hi + have h_inner : inner ℂ (A.H.eigenvectorBasis i) (∑ j, c j • (A.H.eigenvalues j) • (A.H.eigenvectorBasis j)) = 0 := by + convert congr_arg ( fun x => inner ℂ ( A.H.eigenvectorBasis i ) x ) ( show ( ∑ j, c j • A.H.eigenvalues j • A.H.eigenvectorBasis j ) = 0 from ?_ ) using 1; + · simp [ inner_zero_right ]; + · ext j; replace h := congr_fun h j; aesop; + simp_all + convert congr_arg ( fun x : ℂ => x / ( A.H.eigenvalues i ) ) h_inner using 1 <;> norm_num [ Finset.sum_div _ _ _, hi ]; + refine' Finset.sum_congr rfl fun j _ => _ ; by_cases hj : A.H.eigenvalues j = 0 <;> simp_all [ mul_div_assoc ] ; ring_nf; + · by_cases hij : i = j <;> simp_all [ ]; + · by_cases hij : i = j <;> simp_all [ inner_self_eq_norm_sq_to_K ]; + · intro h + have h_zero_coeffs : ∀ i, A.H.eigenvalues i ≠ 0 → c i = 0 := by + intro i hi; specialize h i hi; simp_all + rw [ Finset.sum_eq_single i ] at h <;> simp_all [ orthonormal_iff_ite.mp ( A.H.eigenvectorBasis.orthonormal ) ]; + aesop; + simp_all + exact Finset.sum_eq_zero fun i _ => by by_cases hi : A.H.eigenvalues i = 0 <;> simp [ hi, h_zero_coeffs i ] ; + +open InnerProductSpace in +lemma cfc_mulVec_expansion (A : HermitianMat d ℂ) (f : ℝ → ℝ) (x : EuclideanSpace ℂ d) : + (A.cfc f).mat.mulVec x = ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ (A.H.eigenvectorBasis i) x • A.H.eigenvectorBasis i := by + ext i; simp [ Matrix.mulVec, dotProduct ] ; ring_nf + -- By definition of $cfc$, we know that $(A.cfc f).i j = \sum_k f(\lambda_k) \langle e_k, e_i \rangle \langle e_j, e_k \rangle$. + have h_cfc_def : (A.cfc f).mat i = ∑ k, f (A.H.eigenvalues k) • (A.H.eigenvectorBasis k).ofLp i • star (A.H.eigenvectorBasis k).ofLp := by + -- By definition of $cfc$, we know that $(A.cfc f).i j = \sum_k f(\lambda_k) \langle e_k, e_i \rangle \langle e_j, e_k \rangle$ follows directly from the definition of $cfc$. + have h_cfc_def : (A.cfc f).mat = ∑ k, f (A.H.eigenvalues k) • (A.H.eigenvectorUnitary.val * (Matrix.single k k 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + convert cfc_toMat_eq_sum_smul_proj A f using 1; + convert congr_fun h_cfc_def i using 1; + simp [ funext_iff, Matrix.single ]; + simp [ Matrix.mul_apply, Matrix.conjTranspose_apply, Matrix.sum_apply, mul_assoc ]; + intro x; congr; ext y; simp [ Finset.sum_ite, Finset.filter_eq, Finset.filter_and ] ; ring_nf + rw [ Finset.sum_eq_single y ] <;> aesop; + simp_all [mul_comm, mul_left_comm ] ; ring_nf + convert! congr_arg ( fun y => ∑ j, x.ofLp j * y j ) h_cfc_def using 1 + simp [ Finset.mul_sum _ _ _, mul_assoc, mul_left_comm ] + ring_nf! + rw [ Finset.sum_comm, Finset.sum_congr rfl ] + intros + simp [ mul_assoc, inner ] + ring_nf! + simp only [Finset.mul_sum _ _ _, mul_assoc] + +section ker_cfc + +variable {A : HermitianMat d ℂ} {f : ℝ → ℝ} {s : Set ℝ} + +lemma ker_cfc_le_ker_on_set + (hs : spectrum ℝ A.mat ⊆ s) + (h : ∀ i ∈ s, f i = 0 → i = 0) : + (A.cfc f).ker ≤ A.ker := by + intro x hx + have h_inner : ∀ i, A.H.eigenvalues i ≠ 0 → inner ℂ (A.H.eigenvectorBasis i) x = 0 := by + have h_inner_zero : (A.cfc f).mat.mulVec x = 0 := by + exact (mem_ker_iff_mulVec_zero (A.cfc f) x).mp hx + have h_inner_zero_expansion : ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ (A.H.eigenvectorBasis i) x • A.H.eigenvectorBasis i = 0 := by + convert h_inner_zero using 1; + rw [ cfc_mulVec_expansion ]; + exact Iff.symm (WithLp.ofLp_eq_zero 2) + have h_inner_zero_coeff : ∀ i, f (A.H.eigenvalues i) • inner ℂ (A.H.eigenvectorBasis i) x = 0 := by + intro i + have h_inner_zero_coeff_i : f (A.H.eigenvalues i) • inner ℂ (A.H.eigenvectorBasis i) x = inner ℂ (A.H.eigenvectorBasis i) (∑ j, (f (A.H.eigenvalues j) : ℂ) • inner ℂ (A.H.eigenvectorBasis j) x • A.H.eigenvectorBasis j) := by + simp [ orthonormal_iff_ite.mp ( A.H.eigenvectorBasis.orthonormal ) ] + rw [h_inner_zero_coeff_i, h_inner_zero_expansion] + simp [inner_zero_right] -- This line is just to prevent the proof from being completed prematurely. In a real proof, this line would be replaced with the actual proof steps. + have h_inner_zero_final : ∀ i, A.H.eigenvalues i ≠ 0 → inner ℂ (A.H.eigenvectorBasis i) x = 0 := by + -- Since $A.H.eigenvalues i \neq 0$, by hypothesis $h$, we have $f(A.H.eigenvalues i) \neq 0$. + have h_f_nonzero : ∀ i, A.H.eigenvalues i ≠ 0 → f (A.H.eigenvalues i) ≠ 0 := by + intro i hi; specialize h ( A.H.eigenvalues i ) ( hs <| by + exact Matrix.IsHermitian.eigenvalues_mem_spectrum_real (H A) i ) ; contrapose! hi; aesop; + generalize_proofs at *; ( + exact fun i hi => by simpa [ h_f_nonzero i hi ] using h_inner_zero_coeff i;) -- This line is just to prevent the proof from being completed prematurely. In a real proof, this line would be replaced with the actual proof steps. + exact h_inner_zero_final; + convert mulVec_eq_zero_iff_inner_eigenvector_zero A x |>.2 h_inner using 1; + exact mem_ker_iff_mulVec_zero A x + +lemma ker_cfc_le_ker (h : ∀ i, f i = 0 → i = 0) : + (A.cfc f).ker ≤ A.ker := by + exact ker_cfc_le_ker_on_set (Set.subset_univ _) (by simpa using h) + +lemma ker_cfc_le_ker_nonneg (hA : 0 ≤ A) (h : ∀ i ≥ 0, f i = 0 → i = 0) : + (A.cfc f).ker ≤ A.ker := by + rw [posSemidef_iff_spectrum_Ici] at hA + exact ker_cfc_le_ker_on_set hA h + +lemma ker_le_ker_cfc_on_set (hs : spectrum ℝ A.mat ⊆ s) (h : ∀ i ∈ s, i = 0 → f i = 0) : + A.ker ≤ (A.cfc f).ker := by + intro x hx + have h_inner_zero : ∀ i, A.H.eigenvalues i ≠ 0 → inner ℂ (A.H.eigenvectorBasis i) x = 0 := by + intro i hi + have h_inner_zero : A.mat.mulVec x = 0 := by + exact (mem_ker_iff_mulVec_zero A x).mp hx; + have := mulVec_eq_zero_iff_inner_eigenvector_zero A x; aesop; + have h_mulVec_zero : (A.cfc f).mat.mulVec x = ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ (A.H.eigenvectorBasis i) x • A.H.eigenvectorBasis i := by + convert cfc_mulVec_expansion A f x using 1; + convert h_mulVec_zero using 1 + simp_all [ funext_iff] ; + ext i; specialize h_mulVec_zero i; simp_all [ lin, Matrix.mulVec ] ; + refine' Finset.sum_eq_zero fun j _ => ?_ + by_cases hj : A.H.eigenvalues j = 0 <;> simp_all + exact Or.inl ( h _ ( hs (Matrix.IsHermitian.eigenvalues_mem_spectrum_real (H A) j ) ) hj ) + +lemma ker_le_ker_cfc (h : ∀ i, i = 0 → f i = 0) : + A.ker ≤ (A.cfc f).ker := by + exact ker_le_ker_cfc_on_set (Set.subset_univ _) (by simpa using h) + +lemma ker_le_ker_cfc_nonneg (hA : 0 ≤ A) (h : ∀ i ≥ 0, i = 0 → f i = 0) : + A.ker ≤ (A.cfc f).ker := by + rw [posSemidef_iff_spectrum_Ici] at hA + exact ker_le_ker_cfc_on_set hA h + +theorem ker_cfc_eq_ker (h : ∀ i, f i = 0 ↔ i = 0) : + (A.cfc f).ker = A.ker := by + refine le_antisymm (ker_cfc_le_ker ?_) (ker_le_ker_cfc ?_) + <;> grind only + +theorem ker_cfc_eq_ker_nonneg (hA : 0 ≤ A) (h : ∀ i ≥ 0, f i = 0 ↔ i = 0) : + (A.cfc f).ker = A.ker := by + refine le_antisymm (ker_cfc_le_ker_nonneg hA ?_) (ker_le_ker_cfc_nonneg hA ?_) + <;> grind only + +end ker_cfc +end CFC diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean new file mode 100644 index 0000000000..0164c07a37 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean @@ -0,0 +1,516 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +public import Mathlib.Analysis.Convex.Contractible +public import Mathlib.Topology.Instances.Real.Lemmas + +/-! # Inner product of Hermitian Matrices + +For general matrices there are multiple reasonable notions of "inner product" (Hilbert–Schmidt inner product, +Frobenius inner product), and so Mathlib avoids giving a canonical `InnerProductSpace` instance. But for the +particular case of Hermitian matrices, these all coincide, so we can put a canonical `InnerProductSpace` +instance. + +This _does_ however induce a `Norm` on `HermitianMat` as well, the Frobenius norm, and this is less obviously +a uniquely correct choice. It is something that one essentially has to live with, with the way that Mathlib +currently structures the instances. (Thankfully, all norms induce the same _topology and bornology_ on +finite-dimensional matrices.) + +Some care to be taken so that the topology induced by the InnerProductSpace is defeq with the Subtype +topology that HermitianMat inherits from the topology on Matrix. This can be done via +`InnerProductSpace.ofCoreOfTopology`. + +-/ + +@[expose] public section + +namespace HermitianMat + +variable {R n α : Type*} [Star R] [TrivialStar R] [Fintype n] +open scoped InnerProductSpace RealInnerProductSpace +open IsMaximalSelfAdjoint + +section defs + +variable [Ring α] [StarAddMonoid α] [CommSemiring R] [Algebra R α] [IsMaximalSelfAdjoint R α] + +/-- The Hermitian inner product, `Tr[AB]`. This is equal to `Matrix.trace (A * B)`, but gives real + values when the matrices are complex, using `IsMaximalSelfAdjoint`. -/ +instance : Inner R (HermitianMat n α) where + inner A B := selfadjMap (A.mat * B.mat).trace + +theorem inner_def (A B : HermitianMat n α) : + ⟪A, B⟫_R = selfadjMap (A.mat * B.mat).trace := by + rfl + +end defs +section semiring + +--We necessarily re-state and re-prove many of the theorems from InnerProductSpace/Basic.lean, +--because our inner product happens outside of just an `InnerProductSpace` instance. + +variable [CommSemiring R] [Ring α] [StarAddMonoid α] [Algebra R α] [IsMaximalSelfAdjoint R α] +variable (A B C : HermitianMat n α) + +protected theorem inner_add_right : ⟪A, B + C⟫_R = ⟪A, B⟫_R + ⟪A, C⟫_R := by + simp [inner_def, left_distrib] + +protected theorem inner_add_left : ⟪A + B, C⟫_R = ⟪A, C⟫_R + ⟪B, C⟫_R := by + simp [inner_def, right_distrib] + +@[simp] +protected theorem inner_zero_right : ⟪A, 0⟫_R = 0 := by + simp [inner_def] + +@[simp] +protected theorem inner_zero_left : ⟪0, A⟫_R = 0 := by + simp [inner_def] + +end semiring +section ring + +variable [CommRing R] [Ring α] [StarAddMonoid α] [Algebra R α] [IsMaximalSelfAdjoint R α] +variable (A B C : HermitianMat n α) + +@[simp] +protected theorem inner_neg_left : ⟪-A, B⟫_R = -⟪A, B⟫_R := by + simp [inner_def] + +@[simp] +protected theorem inner_neg_right : ⟪A, -B⟫_R = -⟪A, B⟫_R := by + simp [inner_def] + +protected theorem inner_sub_left : ⟪A, B - C⟫_R = ⟪A, B⟫_R - ⟪A, C⟫_R := by + simp [inner_def, mul_sub] + +protected theorem inner_sub_right : ⟪A - B, C⟫_R = ⟪A, C⟫_R - ⟪B, C⟫_R := by + simp [inner_def, sub_mul] + +variable [StarModule R α] + +@[simp] +protected theorem inner_smul_left (r : R) : ⟪r • A, B⟫_R = r * ⟪A, B⟫_R := by + simp [inner_def, selfadj_smul] + +@[simp] +protected theorem inner_smul_right (r : R) : ⟪A, r • B⟫_R = r * ⟪A, B⟫_R := by + simp [inner_def, selfadj_smul] + +/-- The Hermitian inner product as bilinear form. Compare with `innerₗ` (in the root namespace) +which requires an `InnerProductSpace` instance. -/ +protected def innerₗ : LinearMap.BilinForm R (HermitianMat n α) where + toFun A := { + toFun := (⟪A, ·⟫_R) + map_add' := A.inner_add_right + map_smul' r B := by simp + } + map_add' A B := by ext1; apply A.inner_add_left B + map_smul' A B := by ext1; simp + +end ring +section starring + +variable [CommSemiring R] [Ring α] [StarRing α] [Algebra R α] [IsMaximalSelfAdjoint R α] [DecidableEq n] +variable (A B : HermitianMat n α) + +@[simp] +theorem inner_one : ⟪A, 1⟫_R = A.trace := by + simp only [inner_def, mat_one, mul_one, trace] + +@[simp] +theorem one_inner : ⟪1, A⟫_R = A.trace := by + simp only [inner_def, one_mul, mat_one, trace] + +end starring +section commring + +variable [CommSemiring R] [CommRing α] [StarRing α] [Algebra R α] [IsMaximalSelfAdjoint R α] +variable (A B : HermitianMat n α) + +/-- The inner product for Hermtian matrices is equal to the trace of the product. -/ +theorem inner_eq_trace_mul : algebraMap R α ⟪A, B⟫_R = (A.mat * B.mat).trace := by + apply IsMaximalSelfAdjoint.selfadj_algebra + rw [IsSelfAdjoint, Matrix.trace] + simp_rw [star_sum, Matrix.diag_apply, Matrix.mul_apply, star_sum, star_mul, mul_comm] + rw [Finset.sum_comm] + congr! <;> apply congrFun₂ (H _) + +theorem inner_comm : ⟪A, B⟫_R = ⟪B, A⟫_R := by + rw [inner_def, inner_def, Matrix.trace_mul_comm] + +end commring + +section trivialstar +variable [CommRing α] [StarRing α] [TrivialStar α] +variable (A B : HermitianMat n α) + +/-- `HermitianMat.inner` reduces to `Matrix.trace (A * B)` when the elements are a `TrivialStar`. -/ +theorem inner_eq_trace_trivial : ⟪A, B⟫_α = (A.mat * B.mat).trace := by + rw [← inner_eq_trace_mul] + rfl + +end trivialstar + +section RCLike + +open ComplexOrder + +variable {n 𝕜 : Type*} [Fintype n] [RCLike 𝕜] (A B C : HermitianMat n 𝕜) + +theorem inner_eq_re_trace : ⟪A, B⟫ = RCLike.re (A.mat * B.mat).trace := by + rfl + +theorem inner_eq_trace_rc : ⟪A, B⟫ = (A.mat * B.mat).trace := by + rw [inner_eq_re_trace, ← RCLike.conj_eq_iff_re] + convert! (Matrix.trace_conjTranspose (A.mat * B.mat)).symm using 1 + rw [Matrix.conjTranspose_mul, A.H, B.H, Matrix.trace_mul_comm] + +theorem inner_self_nonneg: 0 ≤ ⟪A, A⟫ := by + simp_rw [inner_eq_re_trace, Matrix.trace, Matrix.diag, Matrix.mul_apply, map_sum] + refine Finset.sum_nonneg fun i _ ↦ Finset.sum_nonneg fun j _ ↦ ?_ + rw [← congrFun₂ A.H, Matrix.conjTranspose_apply] + refine And.left <| RCLike.nonneg_iff.mp ?_ + open ComplexOrder in + exact star_mul_self_nonneg (A.mat j i) + +variable {A B C} + +open MatrixOrder in +theorem inner_mul_nonneg (h : 0 ≤ A.mat * B.mat) : 0 ≤ ⟪A, B⟫ := by + rw [Matrix.nonneg_iff_posSemidef] at h + exact (RCLike.nonneg_iff.mp h.trace_nonneg).left + +/-- The inner product for PSD matrices is nonnegative. -/ +theorem inner_ge_zero (hA : 0 ≤ A) (hB : 0 ≤ B) : 0 ≤ ⟪A, B⟫ := by + rw [zero_le_iff] at hB + open MatrixOrder in + open Classical in + rw [inner_eq_re_trace, ← CFC.sqrt_mul_sqrt_self A.mat hA, Matrix.trace_mul_cycle, Matrix.trace_mul_cycle] + nth_rewrite 1 [← (Matrix.nonneg_iff_posSemidef.mp (CFC.sqrt_nonneg A.mat)).left] + exact (RCLike.nonneg_iff.mp (hB.conjTranspose_mul_mul_same _).trace_nonneg).left + +theorem inner_mono (hA : 0 ≤ A) : B ≤ C → ⟪A, B⟫ ≤ ⟪A, C⟫ := by + intro hBC + classical have hTr : 0 ≤ ⟪A, C - B⟫ := inner_ge_zero hA (zero_le_iff.mpr hBC) + simpa [inner_def, mul_sub] using hTr + +theorem inner_mono' (hA : 0 ≤ A) : B ≤ C → ⟪B, A⟫ ≤ ⟪C, A⟫ := by + intro hBC + rw [inner_comm B A, inner_comm C A] + exact inner_mono hA hBC + +/-- The inner product for PSD matrices is at most the product of their traces. -/ +theorem inner_le_mul_trace (hA : 0 ≤ A) (hB : 0 ≤ B) : ⟪A, B⟫ ≤ A.trace * B.trace := by + classical convert inner_mono hA (le_trace_smul_one hB) + simp [mul_comm] + +--TODO cleanup +private theorem inner_zero_iff_aux_lemma [DecidableEq n] (hA₁ : A.mat.PosSemidef) (hB₁ : B.mat.PosSemidef) : + RCLike.re (A.val * B.val).trace = 0 ↔ + LinearMap.range (Matrix.toEuclideanLin A.val) ≤ + LinearMap.ker (Matrix.toEuclideanLin B.val) := by + -- The kernel inclusion `range ≤ ker` says `(toEuclideanLin B) ∘ (toEuclideanLin A) = 0`, i.e. + -- `B * A = 0`, which by Hermitianness of `A` and `B` is `A * B = 0`. + rw [LinearMap.range_le_ker_iff, + show Matrix.toEuclideanLin B.val ∘ₗ Matrix.toEuclideanLin A.val + = Matrix.toEuclideanLin (B.val * A.val) from by + ext v; simp [Matrix.toLpLin_apply, Matrix.mulVec_mulVec, Matrix.toEuclideanLin], + LinearEquiv.map_eq_zero_iff, + show B.val * A.val = (A.val * B.val).conjTranspose from by simp [Matrix.conjTranspose_mul], + Matrix.conjTranspose_eq_zero] + -- Factoring `A = Cᴴ C` and `B = Dᴴ D`, a cyclic trace identity gives `tr (A * B) = tr (Eᴴ E)` + -- where `E = D Cᴴ`, so `re (tr (A * B)) = 0` forces `E = 0` and hence `A * B = 0`. + open MatrixOrder in + obtain ⟨C, hC⟩ : ∃ C : Matrix n n 𝕜, A.val = C.conjTranspose * C := + CStarAlgebra.nonneg_iff_eq_star_mul_self.mp (Matrix.nonneg_iff_posSemidef.mpr hA₁) + open MatrixOrder in + obtain ⟨D, hD⟩ : ∃ D : Matrix n n 𝕜, B.val = D.conjTranspose * D := + CStarAlgebra.nonneg_iff_eq_star_mul_self.mp (Matrix.nonneg_iff_posSemidef.mpr hB₁) + have htr : (A.val * B.val).trace + = ((D * C.conjTranspose).conjTranspose * (D * C.conjTranspose)).trace := by + rw [hC, hD, Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose, + show C.conjTranspose * C * (D.conjTranspose * D) + = C.conjTranspose * (C * D.conjTranspose * D) from by simp [Matrix.mul_assoc], + Matrix.trace_mul_comm, + show C * D.conjTranspose * D * C.conjTranspose + = C * D.conjTranspose * (D * C.conjTranspose) from by simp [Matrix.mul_assoc]] + refine ⟨fun h => ?_, fun h => by rw [h]; simp⟩ + have hE0 : D * C.conjTranspose = 0 := + Matrix.trace_conjTranspose_mul_self_eq_zero_iff.mp <| RCLike.ext + (by rw [← htr]; simpa using h) + (by simpa using (RCLike.nonneg_iff.mp + (Matrix.posSemidef_conjTranspose_mul_self (D * C.conjTranspose)).trace_nonneg).2) + have hE0' : C * D.conjTranspose = 0 := by + simpa [Matrix.conjTranspose_mul] using congrArg Matrix.conjTranspose hE0 + rw [hC, hD, show C.conjTranspose * C * (D.conjTranspose * D) + = C.conjTranspose * (C * D.conjTranspose) * D from by simp [Matrix.mul_assoc], hE0'] + simp + +/-- The inner product of two PSD matrices is zero iff they have disjoint support, i.e., each lives entirely +in the other's kernel. -/ +theorem inner_zero_iff [DecidableEq n] (hA₁ : 0 ≤ A) (hB₁ : 0 ≤ B) + : ⟪A, B⟫ = 0 ↔ A.support ≤ B.ker := by + rw [zero_le_iff] at hA₁ hB₁ + rw [inner_eq_re_trace] + exact inner_zero_iff_aux_lemma hA₁ hB₁ + +variable {d d₂ : Type*} (A B : HermitianMat d 𝕜) [Fintype d₂] [Fintype d] + +@[simp] +theorem reindex_inner (e : d ≃ d₂) (B : HermitianMat d₂ 𝕜) : + ⟪A.reindex e, B⟫ = ⟪A, B.reindex e.symm⟫ := by + simp only [inner_def, mat_reindex, Matrix.reindex_apply, Equiv.symm_symm] + congr + rw (occs := [3,4]) [← e.symm_symm] + rw [← Matrix.submatrix_id_mul_right] + rw (occs := [2]) [Matrix.trace_mul_comm] + rw [Matrix.submatrix_id_mul_right, Matrix.trace_mul_comm, Equiv.symm_symm] + +end RCLike + +section topology +/-! +Theorems about `HermitianMat`s that have to do with the topological structure. Pretty much everything here will +assume these are matrices over ℂ, but changes to upgrade this to other types are welcome. +-/ +open ComplexOrder + +variable {d : Type*} [Fintype d] {𝕜 : Type*} [RCLike 𝕜] + +--Check that it synthesizes ok +#guard_msgs(drop info) in +#synth ContractibleSpace (HermitianMat d ℂ) + +@[fun_prop] +theorem inner_continuous : Continuous (Inner.inner ℝ (E := HermitianMat d 𝕜)) := by + rw [funext₂ inner_eq_re_trace] + fun_prop + +@[fun_prop] --fun_prop can actually prove this, should I leave this on or not? +theorem inner_bilinForm_Continuous (A : HermitianMat d 𝕜) : Continuous ⇑(HermitianMat.innerₗ A) := + LinearMap.continuous_of_finiteDimensional _ + +end topology + +section innerproductspace + +variable {d d₂ : Type*} [Fintype d] [Fintype d₂] {𝕜 : Type*} [RCLike 𝕜] + +/-- We define the Hermitian inner product as our "canonical" inner product, which does induce a norm. +This disagrees slightly with Mathlib convention on the `Matrix` type, which avoids asserting one norm +as there are several reasonable ones; for Hermitian matrices, though, this seem to be the right choice. -/ +@[reducible] +noncomputable def InnerProductCore : InnerProductSpace.Core ℝ (HermitianMat d 𝕜) := + { + inner A B := ⟪A, B⟫ + conj_inner_symm := fun x y ↦ by + simpa using inner_comm y x + re_inner_nonneg := inner_self_nonneg + add_left := by simp [inner, add_mul] + smul_left x y r := by simp + definite x h := by + replace h : ∑ j, ∑ i, (RCLike.re (x i j) ^ 2 + RCLike.im (x i j) ^ 2) = 0 := by + convert h + simp only [inner_eq_re_trace, Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, map_sum, + RCLike.mul_re, sub_eq_add_neg] + congr! 2 with i _ j + simp only [Matrix.conjTranspose_apply, ← congrFun₂ x.H i j] + simp [pow_two] + ext i j + rw [Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h + replace h := congrFun h j + rw [Pi.zero_apply, Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h + replace h := congrFun h i + rw [Pi.zero_apply] at h + rw [add_eq_zero_iff_of_nonneg (by positivity) (by positivity), sq_eq_zero_iff, sq_eq_zero_iff] at h + apply RCLike.ext (h.left.trans RCLike.zero_re.symm) (h.right.trans (map_zero _).symm) + } + +open Matrix.Norms.Frobenius in +/-- The `HermitianMat` type inherits the Frobenius necessarily, since it's going to need the +Hermitian inner product, and in Mathlib an `InnerProductSpace` always carries the corresponding +norm. -/ +noncomputable instance instNormedGroup : NormedAddCommGroup (HermitianMat d 𝕜) := + AddSubgroupClass.normedAddCommGroup _ + +theorem norm_eq_frobenius (A : HermitianMat d 𝕜) : + ‖A‖ = (∑ i : d, ∑ j : d, ‖A i j‖ ^ 2) ^ (1 / 2 : ℝ) := by + convert! ← Matrix.frobenius_norm_def A.mat + exact Real.rpow_ofNat _ 2 + +theorem norm_eq_sqrt_inner_self (A : HermitianMat d 𝕜) : ‖A‖ = √(⟪A, A⟫) := by + rw [norm_eq_frobenius, ← Real.sqrt_eq_rpow] + congr + simp_rw [inner_eq_re_trace, Matrix.trace, Matrix.diag, Matrix.mul_apply] + simp only [map_sum] + congr! with i _ j _ + simp only [RCLike.norm_sq_eq_def, RCLike.mul_re, sub_eq_add_neg, + neg_mul_eq_mul_neg] + congr 2 <;> (rw [← A.H]; simp) + +noncomputable instance instNormedSpace : NormedSpace ℝ (HermitianMat d 𝕜) where + norm_smul_le r x := by + simp [norm_eq_sqrt_inner_self, ← mul_assoc, Real.sqrt_mul', + inner_self_nonneg, Real.sqrt_mul_self_eq_abs] + +noncomputable instance instInnerProductSpace : InnerProductSpace ℝ (HermitianMat d 𝕜) := + letI : Inner ℝ (HermitianMat d 𝕜) := InnerProductCore.toInner; + letI : NormedSpace ℝ (HermitianMat d 𝕜) := instNormedSpace; + { InnerProductCore with + norm_sq_eq_re_inner := fun x => by + rw [norm_eq_sqrt_inner_self, Real.sq_sqrt (inner_self_nonneg x), RCLike.re_to_real] + } + +instance : CompleteSpace (HermitianMat d 𝕜) := + inferInstance + +--Shortcut instances +noncomputable instance : NormedAddCommGroup (HermitianMat d ℝ) := + inferInstance + +noncomputable instance : NormedAddCommGroup (HermitianMat d ℂ) := + inferInstance + +--PR'ed in #35056 +open ComplexOrder in +lemma _root_.RCLike.instOrderClosed : OrderClosedTopology 𝕜 where + isClosed_le' := by + conv => enter [1, 1, p]; rw [RCLike.le_iff_re_im] + simp_rw [Set.ofPred_and] + refine IsClosed.inter (isClosed_le ?_ ?_) (isClosed_eq ?_ ?_) <;> continuity + +scoped[ComplexOrder] attribute [instance] RCLike.instOrderClosed + +variable (A B : HermitianMat d 𝕜) + +variable {A B} in +theorem dist_le_of_mem_Icc (x : HermitianMat d 𝕜) (hA : A ≤ x) (hB : x ≤ B) : + ‖x - A‖ ≤ ‖B - A‖ := by + classical + conv => enter [2, 1]; equals (B - x) + (x - A) => abel + rw [← sq_le_sq₀ (norm_nonneg _) (norm_nonneg _)] + rw [norm_add_pow_two_real, le_add_iff_nonneg_left] + suffices 0 ≤ ⟪B - x, x - A⟫ by positivity + apply inner_ge_zero <;> rwa [sub_nonneg] + +omit [Fintype n] in +theorem Matrix.IsHermitian_isClosed : IsClosed { A : Matrix n n 𝕜 | A.IsHermitian } := by + conv => + enter [1, 1, A] + rw [Matrix.IsHermitian, ← sub_eq_zero] + convert! isClosed_singleton.preimage (f := fun (x : Matrix n n 𝕜) ↦ (x.conjTranspose - x)) + (by fun_prop) using 1 + +open ComplexOrder + +theorem Matrix.PosSemiDef_isClosed : IsClosed { A : Matrix n n 𝕜 | A.PosSemidef } := by + rw [show { A : Matrix n n 𝕜 | A.PosSemidef } = { A | A.IsHermitian } ∩ { A | ∀ x : n → 𝕜, 0 ≤ star x ⬝ᵥ A.mulVec x } from by + ext A; simp [Matrix.posSemidef_iff_dotProduct_mulVec]] + refine IsHermitian_isClosed.inter ?_ + suffices IsClosed (⋂ x : n → 𝕜, { A : Matrix n n 𝕜 | 0 ≤ star x ⬝ᵥ A.mulVec x }) by + rwa [← Set.ofPred_forall] at this + exact isClosed_iInter fun _ ↦ (isClosed_Ici (a := 0)).preimage (by fun_prop) + +theorem isClosed_nonneg : IsClosed { A : HermitianMat n 𝕜 | 0 ≤ A } := by + simp_rw [zero_le_iff] + exact Matrix.PosSemiDef_isClosed.preimage_val + +--TODO: The PosDef matrices are open *within* the HermitianMat space (not in the ambient space of matrices.) + +instance : OrderClosedTopology (HermitianMat d 𝕜) where + isClosed_le' := by + classical + convert IsClosed.preimage (X := (HermitianMat d 𝕜 × HermitianMat d 𝕜)) + (f := fun xy ↦ (xy.2 - xy.1)) (by fun_prop) isClosed_nonneg + ext ⟨x, y⟩ + simp only [Set.mem_ofPred_eq, Set.mem_preimage, ← sub_nonneg (b := x)] + +/-- Equivalently: the matrices `X` such that `X - A` is PSD and `B - X` is PSD, form a compact set. -/ +instance : CompactIccSpace (HermitianMat d 𝕜) where + isCompact_Icc := by + intros A B + apply Metric.isCompact_of_isClosed_isBounded isClosed_Icc + rw [Metric.isBounded_iff] + use 2 * ‖B - A‖ + rintro x ⟨hxA, hxB⟩ y ⟨hyA, hyB⟩ + grw [dist_triangle_right (z := A), dist_eq_norm, dist_eq_norm] + grw [dist_le_of_mem_Icc x hxA hxB, dist_le_of_mem_Icc y hyA hyB] + rw [two_mul] + +variable [DecidableEq d] + +/-- The PSD matrices that are `≤ 1` are a compact set. More generally, this is true of any closed interval, +but stating that is a bit different because of how numerals are treated. The `0` and `1` here are already +directly matrices, putting in an `(a : ℝ) • 1 ≤ m ∧ m ≤ (b : ℝ) • 1` involves casts. But that theorem should follow +easily from this. More generally `A ≤ m ∧ m ≤ B` is compact. +-/ +theorem unitInterval_IsCompact : IsCompact {m : HermitianMat d 𝕜 | 0 ≤ m ∧ m ≤ 1} := + CompactIccSpace.isCompact_Icc + +@[simp] +theorem norm_one : ‖(1 : HermitianMat d 𝕜)‖ = √(Fintype.card d : ℝ) := by + rw [norm_eq_sqrt_real_inner (F := HermitianMat d 𝕜)] + congr 1 + simp [-inner_self_eq_norm_sq_to_K, inner_def] + +theorem norm_eq_trace_sq : ‖A‖ ^ 2 = (A.mat ^ 2).trace := by + rw [norm_eq_frobenius, ← RCLike.ofReal_pow, ← Real.rpow_two, ← Real.rpow_mul (by positivity)] + simp only [one_div, ne_eq, OfNat.ofNat_ne_zero, not_false_eq_true, inv_mul_cancel₀, Real.rpow_one] + simp only [sq A.mat, map_sum, map_pow, Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, mat_apply] + congr! with i _ j _ + rw [← star_star (A j i), ← A.mat_apply (i := j)] + rw [← A.mat.conjTranspose_apply j i, A.H, eq_comm] + exact RCLike.mul_conj (A.mat i j) + +end innerproductspace + +--TODO: Cleanup, ew what? +/-- +The inner product ⟪A, B⟫ equals ∑_{ij} a_i b_j w_{ij} where a_i, b_j are eigenvalues +and w_{ij} = ‖C_{ij}‖² for C = U_A^* U_B unitary. +-/ +lemma inner_eq_doubly_stochastic_sum {d : Type*} [Fintype d] [DecidableEq d] + (A B : HermitianMat d ℂ) : + let C := A.H.eigenvectorUnitary.val.conjTranspose * B.H.eigenvectorUnitary.val + ⟪A, B⟫_ℝ = ∑ i, ∑ j, + A.H.eigenvalues i * B.H.eigenvalues j * (‖C i j‖^2) := by + intro C + set U : Matrix d d ℂ := A.H.eigenvectorUnitary.val + set V : Matrix d d ℂ := B.H.eigenvectorUnitary.val + -- `U`, `V` are unitary and diagonalize `A`, `B`; `C = Uᴴ V`. + have hUU : star U * U = 1 := A.H.eigenvectorUnitary.2.1 + have hUU' : U * star U = 1 := A.H.eigenvectorUnitary.2.2 + have hAeq : A.mat = U * Matrix.diagonal (RCLike.ofReal ∘ A.H.eigenvalues) * star U := by + simpa [Unitary.conjStarAlgAut_apply] using A.H.spectral_theorem + have hBeq : B.mat = V * Matrix.diagonal (RCLike.ofReal ∘ B.H.eigenvalues) * star V := by + simpa [Unitary.conjStarAlgAut_apply] using B.H.spectral_theorem + have hC : C = star U * V := rfl + have hCH : C.conjTranspose = star V * U := by + simp [hC, Matrix.conjTranspose_mul, Matrix.star_eq_conjTranspose] + -- Conjugating `A * B` by `U` turns it into a product of two diagonals and `C`, `Cᴴ`. + have key : A.mat * B.mat = + U * (Matrix.diagonal (RCLike.ofReal ∘ A.H.eigenvalues) * C * + Matrix.diagonal (RCLike.ofReal ∘ B.H.eigenvalues) * C.conjTranspose) * star U := by + conv_lhs => rw [hAeq, hBeq] + rw [hCH, hC] + simp only [Matrix.mul_assoc, hUU', mul_one] + have hC_norm : ∀ i j, (‖C i j‖ ^ 2 : ℂ) = C i j * star (C i j) := fun i j => by + simp [Complex.mul_conj, Complex.normSq_eq_norm_sq] + -- The trace of the diagonal product expands entrywise to the doubly-stochastic sum. + have hC_trace : (Matrix.diagonal (RCLike.ofReal ∘ A.H.eigenvalues) * C * + Matrix.diagonal (RCLike.ofReal ∘ B.H.eigenvalues) * C.conjTranspose).trace = + ((∑ i, ∑ j, A.H.eigenvalues i * B.H.eigenvalues j * ‖C i j‖ ^ 2 : ℝ) : ℂ) := by + push_cast + simp only [Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, hC_norm] + simp only [Matrix.diagonal_apply, ite_mul, zero_mul, mul_ite, mul_zero, + Finset.sum_ite_eq, Finset.sum_ite_eq', Finset.mem_univ, if_true, Function.comp_apply, + Matrix.conjTranspose_apply] + exact Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by + simp only [RCLike.ofReal_eq_complex_ofReal]; ring + rw [inner_eq_re_trace, key, Matrix.trace_mul_cycle, hUU, one_mul, hC_trace] + exact Complex.ofReal_re _ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean new file mode 100644 index 0000000000..6aa4385f86 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Algebra.Jordan.Basic + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order + +/-! +Hermitian matrices have a Jordan algebra structure given by +`A * B := 2⁻¹ • (A.toMat * B.toMat + B.toMat * A.toMat)`. We call this operation +`HermitianMat.symmMul`, but it's available as `*` multiplication scoped under +`HermMul`. When `A` and `B` commute, this reduces to standard matrix multiplication. +-/ + +@[expose] public section + +noncomputable section + +section starRing + +variable {d 𝕜 : Type*} [Fintype d] [Field 𝕜] [StarRing 𝕜] +variable (A B : HermitianMat d 𝕜) + +namespace HermitianMat + +def symmMul : HermitianMat d 𝕜 := + ⟨(2 : 𝕜)⁻¹ • (A.mat * B.mat + B.mat * A.mat), + by simp [selfAdjoint, IsSelfAdjoint, add_comm, Matrix.star_eq_conjTranspose]⟩ + +theorem symmMul_comm : A.symmMul B = B.symmMul A := by + rw [symmMul, symmMul, Subtype.mk.injEq, add_comm] + +@[simp] +theorem symmMul_zero : A.symmMul 0 = 0:= by + simp [symmMul] + +@[simp] +theorem zero_symmMul : symmMul 0 A = 0 := by + simp [symmMul] + +theorem symmMul_toMat : (A.symmMul B).mat = + (2 : 𝕜)⁻¹ • (A.mat * B.mat + B.mat * A.mat) := by + rfl + +variable [Invertible (2 : 𝕜)] + +variable {A B} in +@[simp] +theorem symmMul_of_commute (hAB : Commute A.mat B.mat) : + (A.symmMul B).mat = A.mat * B.mat := by + rw [symmMul_toMat, hAB] + rw [smul_add, ← add_smul, inv_eq_one_div, ← add_div] + rw [add_self_div_two, one_smul] + +theorem symmMul_self : (symmMul A A).mat = A.mat * A.mat := by + simp + +variable [DecidableEq d] + +@[simp] +theorem symmMul_one : A.symmMul 1 = A := by + ext1; simp + +@[simp] +theorem one_symmMul : symmMul 1 A = A := by + ext1; simp + +@[simp] +theorem symmMul_neg_one : A.symmMul (-1) = -A := by + ext1; simp + +@[simp] +theorem neg_one_symmMul : symmMul (-1) A = -A := by + ext1; simp + +end HermitianMat +end starRing + +namespace HermMul + +section starRing + +variable {d 𝕜 : Type*} [Fintype d] [Field 𝕜] [StarRing 𝕜] +variable (A B : HermitianMat d 𝕜) + +scoped instance : CommMagma (HermitianMat d 𝕜) where + mul := HermitianMat.symmMul + mul_comm := HermitianMat.symmMul_comm + +-- --Stupid shortcut that might actually help a lot +-- scoped instance : Mul (HermitianMat d 𝕜) := + -- CommMagma.toMul + +theorem mul_eq_symmMul : A * B = A.symmMul B := by + rfl + +scoped instance : IsCommJordan (HermitianMat d 𝕜) where + lmul_comm_rmul_rmul a b := by + ext1 + simp only [mul_eq_symmMul, HermitianMat.symmMul_toMat, smul_add, + mul_add, add_mul, Matrix.mul_smul, Matrix.smul_mul, Matrix.mul_assoc] + abel + +scoped instance : MulZeroClass (HermitianMat d 𝕜) where + zero_mul := by simp [mul_eq_symmMul] + mul_zero := by simp [mul_eq_symmMul] + +variable [DecidableEq d] [Invertible (2 : 𝕜)] + +scoped instance : MulZeroOneClass (HermitianMat d 𝕜) where + one_mul := by simp [mul_eq_symmMul] + mul_one := by simp [mul_eq_symmMul] + +end starRing + +section field + +variable {d 𝕜 : Type*} [Fintype d] [Field 𝕜] [StarRing 𝕜] + +scoped instance : NonUnitalNonAssocRing (HermitianMat d 𝕜) where + left_distrib a b c := by + ext1 + simp [mul_eq_symmMul, HermitianMat.symmMul_toMat, mul_add, add_mul] + abel + right_distrib a b c := by + ext1 + simp [mul_eq_symmMul, HermitianMat.symmMul_toMat, mul_add, add_mul] + abel + +variable [Invertible (2 : 𝕜)] [DecidableEq d] + +scoped instance : NonAssocCommRing (HermitianMat d 𝕜) where + mul_comm := HermitianMat.symmMul_comm + +end field + +section rclike + +variable {d 𝕜 : Type*} [Fintype d] [RCLike 𝕜] + +scoped instance : IsScalarTower ℝ (HermitianMat d 𝕜) (HermitianMat d 𝕜) where + smul_assoc r x y := by + ext : 2 + simp only [smul_eq_mul, mul_eq_symmMul, HermitianMat.symmMul_toMat, + HermitianMat.mat_smul, smul_add] + simp + +end rclike + +end HermMul diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/NonSingular.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/NonSingular.lean new file mode 100644 index 0000000000..733a0a1620 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/NonSingular.lean @@ -0,0 +1,201 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry + +@[expose] public section + +noncomputable section + +namespace Matrix + +variable {d R S F 𝕜 : Type*} [Fintype d] [DecidableEq d] +variable [CommSemiring R] [Semiring S] [Algebra R S] [Field F] [RCLike 𝕜] + +theorem isUnit_smul {c : R} (hA : IsUnit c) {M : Matrix d d S} (hM : IsUnit M) : + IsUnit (c • M : Matrix d d S) := by + obtain ⟨d, rfl⟩ := hA + obtain ⟨M', rfl⟩ := hM + use d • M' + rfl + +theorem isUnit_natCast {n : ℕ} (hn : n ≠ 0) [CharZero F] : IsUnit (n : Matrix d d F) := by + exact (IsUnit.mk0 (n : F) (mod_cast hn)).map (algebraMap F _) + +theorem isUnit_real_smul {r : ℝ} (hr : r ≠ 0) {M : Matrix d d 𝕜} (hM : IsUnit M) : + IsUnit (r • M : Matrix d d 𝕜) := + isUnit_smul hr.isUnit hM + +theorem isUnit_real_cast {r : ℝ} (hr : r ≠ 0) : IsUnit (r • 1 : Matrix d d 𝕜) := by + exact isUnit_real_smul hr isUnit_one + +end Matrix + +namespace HermitianMat + +variable {n m R 𝕜 : Type*} [Fintype n] [DecidableEq n] [Fintype m] [DecidableEq m] +variable [CommRing R] [StarRing R] [RCLike 𝕜] +variable (A : HermitianMat n R) (B : HermitianMat m R) + +class NonSingular (A : HermitianMat n R) : Prop where + isUnit : IsUnit A.mat + +@[simp] +theorem isUnit_mat_of_nonSingular [NonSingular A] : IsUnit A.mat := + NonSingular.isUnit + +theorem nonsingular_iff_isUnit : NonSingular A ↔ IsUnit A.mat := by + exact Iff.intro (fun h ↦ h.isUnit) NonSingular.mk + +instance instHasInv_of_invertible [i : Invertible A.mat] : NonSingular A := + ⟨isUnit_of_invertible _⟩ + +instance instInvertible_of_hasInv [h : NonSingular A] : Invertible A.mat := + h.isUnit.invertible + +instance : NonSingular (1 : HermitianMat n R) := + instHasInv_of_invertible (i := invertibleOne) + +variable {A : HermitianMat n 𝕜} {B : HermitianMat m 𝕜} {C : Matrix n n 𝕜} +open ComplexOrder + +theorem nonSingular_of_posDef (hA : A.mat.PosDef) : NonSingular A := + ⟨hA.isUnit⟩ + +theorem nonSingular_iff_posDef_of_PSD (hA : 0 ≤ A) : + NonSingular A ↔ A.mat.PosDef := by + refine ⟨fun h ↦ ?_, fun hA₂ ↦ nonSingular_of_posDef hA₂⟩ + have : IsUnit A.mat := h.isUnit + grind [zero_le_iff] + +theorem nonSingular_smul [i : NonSingular A] {c : ℝ} (hc : IsUnit c) : NonSingular (c • A) := + ⟨Matrix.isUnit_smul hc i.isUnit⟩ + +theorem nonSingular_iff_zero_notMem_spectrum : NonSingular A ↔ 0 ∉ spectrum ℝ A.mat := by + simp [nonsingular_iff_isUnit, spectrum, resolventSet] + +theorem nonSingular_iff_eigenvalue_ne_zero : NonSingular A ↔ ∀ i, A.H.eigenvalues i ≠ 0 := by + simp [nonSingular_iff_zero_notMem_spectrum, A.H.spectrum_real_eq_range_eigenvalues] + +theorem nonSingular_iff_det_ne_zero : NonSingular A ↔ A.mat.det ≠ 0 := by + simp [Matrix.isUnit_iff_isUnit_det, nonsingular_iff_isUnit] + +theorem nonSingular_iff_ker_bot : NonSingular A ↔ A.ker = ⊥ := by + rw [nonSingular_iff_det_ne_zero]; + have h_det_nonzero_to_ker_trivial : A.mat.det ≠ 0 → A.ker = ⊥ := by + intro h_det_nonzero + rw [Submodule.eq_bot_iff] + have h_inv : Invertible A.mat := by + convert Matrix.invertibleOfDetInvertible A.mat + exact invertibleOfNonzero h_det_nonzero + have h_ker_trivial (x : n → 𝕜) (hx : A.mat.mulVec x = 0) : x = 0 := by + simpa using congr_arg (h_inv.1.mulVec) hx + intro x hx + rw [mem_ker_iff_mulVec_zero] at hx + exact PiLp.ext (fun i => congr_fun (h_ker_trivial x.ofLp hx) i) + refine ⟨h_det_nonzero_to_ker_trivial, fun h h' => ?_⟩ + obtain ⟨x, hx⟩ := Matrix.exists_mulVec_eq_zero_iff.mpr h' + have h_inj : Function.Injective (Matrix.mulVecLin A.mat) := by + rw [← LinearMap.ker_eq_bot, Submodule.eq_bot_iff] + intro y hy + rw [LinearMap.mem_ker, Matrix.mulVecLin_apply] at hy + have hm : (WithLp.toLp 2 y) ∈ A.ker := (mem_ker_iff_mulVec_zero A _).mpr hy + rw [h] at hm + have := (Submodule.mem_bot (R := 𝕜)).mp hm + simpa using this + specialize @h_inj x 0 + simp_all + +theorem nonSingular_iff_support_top : NonSingular A ↔ A.support = ⊤ := by + simp only [support, Submodule.eq_top_iff'] + refine ⟨fun hA ↦ ?_, fun hA ↦ ?_⟩ + · intro x + have hA_inv : IsUnit A.mat := hA.isUnit + rcases hA_inv.exists_right_inv with ⟨y, hy⟩ + exact ⟨WithLp.toLp 2 (y.mulVec x.ofLp), by + apply PiLp.ext; intro i + simp [lin, Matrix.toLpLin_apply, Matrix.mulVec_mulVec, hy]⟩ + · constructor + have : Function.Surjective A.mat.mulVec := by + intro y + obtain ⟨x, hx⟩ := hA (WithLp.toLp 2 y) + exact ⟨x.ofLp, by + have := congr_arg WithLp.ofLp hx + simp [lin, Matrix.toLpLin_apply] at this + exact this⟩ + exact Matrix.mulVec_surjective_iff_isUnit.mp this + +@[simp] +theorem nonSingular_iff_neg : NonSingular (-A) ↔ NonSingular A := by + simp [nonSingular_iff_det_ne_zero, Matrix.det_neg] + +@[simp] +theorem nonSingular_iff_inv : NonSingular (A⁻¹) ↔ NonSingular A := by + rw [nonsingular_iff_isUnit, nonsingular_iff_isUnit] + exact Matrix.isUnit_nonsing_inv_iff + +@[simp] +theorem nonSingular_iff_kronecker [Nonempty n] [Nonempty m] : + NonSingular (A ⊗ₖ B) ↔ NonSingular A ∧ NonSingular B := by + simp [nonSingular_iff_det_ne_zero, Matrix.det_kronecker] + +theorem nonSingular_iff_conj (hC : IsUnit C) : NonSingular (A.conj C) ↔ NonSingular A := by + simp_all [Matrix.isUnit_iff_isUnit_det, nonsingular_iff_isUnit] + +@[simp] +theorem nonSingular_iff_reindex (e : n ≃ m) : NonSingular (A.reindex e) ↔ NonSingular A := by + rw [nonSingular_iff_det_ne_zero, nonSingular_iff_det_ne_zero] + rw [reindex_eq_conj, ← Matrix.det_reindex_self e, conj_apply_mat] + congr! 2 + ext : 2 + simp [Matrix.mul_apply, Matrix.one_apply] + +section fwd + +theorem nonSingular_empty [IsEmpty n] : NonSingular A := by + rw [Subsingleton.eq_one A] + infer_instance + +variable [NonSingular A] [NonSingular B] + +theorem nonSingular_det_ne_zero : A.mat.det ≠ 0 := by + rwa [← nonSingular_iff_det_ne_zero] + +@[simp] +theorem nonSingular_ker_bot : A.ker = ⊥ := by + rwa [← nonSingular_iff_ker_bot] + +@[simp] +theorem nonSingular_support_top : A.support = ⊤ := by + rwa [← nonSingular_iff_support_top] + +instance nonSingular_neg : NonSingular (-A) := by + rwa [nonSingular_iff_neg] + +instance nonSingular_inv : NonSingular (A⁻¹) := by + rwa [nonSingular_iff_inv] + +instance nonSingular_kron [Nonempty n] [Nonempty m] : NonSingular (A ⊗ₖ B) := + nonSingular_iff_kronecker.mpr ⟨inferInstance, inferInstance⟩ + +theorem nonSingular_conj (hC : IsUnit C) : NonSingular (A.conj C) := by + rwa [nonSingular_iff_conj hC] + +instance nonSingular_conj_isometry {B : HermitianMat n 𝕜} [NonSingular B] : + NonSingular (A.conj B.mat) := by + simpa [nonSingular_iff_conj] + +theorem nonSingular_zero_notMem_spectrum : 0 ∉ spectrum ℝ A.mat := by + rwa [← nonSingular_iff_zero_notMem_spectrum] + +theorem nonSingular_eigenvalue_ne_zero : ∀ i, A.H.eigenvalues i ≠ 0 := by + rwa [← nonSingular_iff_eigenvalue_ne_zero] + +end fwd + +end HermitianMat diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean new file mode 100644 index 0000000000..41a694af80 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean @@ -0,0 +1,699 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace +public import Mathlib.Analysis.RCLike.Basic + +@[expose] public section + +namespace HermitianMat + +open ComplexOrder +open scoped Matrix + +variable {𝕜 : Type*} [RCLike 𝕜] +variable {n m ι : Type*} [Fintype n] [Fintype m] [Fintype ι] +variable {A B C : HermitianMat n 𝕜} +variable {M : Matrix m n 𝕜} {N : Matrix n n 𝕜} + +open MatrixOrder in +/-- The `MatrixOrder` instance for Matrix (the Loewner order) we keep open for +HermitianMat, always. -/ +instance : PartialOrder (HermitianMat n 𝕜) := + inferInstanceAs (PartialOrder (selfAdjoint _)) + +open MatrixOrder in +instance : IsOrderedAddMonoid (HermitianMat n 𝕜) := + inferInstanceAs (IsOrderedAddMonoid (selfAdjoint _)) + +omit [Fintype n] in +theorem le_iff : A ≤ B ↔ (B - A).mat.PosSemidef := by + rfl + +omit [Fintype n] in +theorem zero_le_iff : 0 ≤ A ↔ A.mat.PosSemidef := by + rw [le_iff, sub_zero] + +theorem le_iff_mulVec_le : A ≤ B ↔ + ∀ x, star x ⬝ᵥ A.mat *ᵥ x ≤ star x ⬝ᵥ B.mat *ᵥ x := by + simp [le_iff, Matrix.posSemidef_iff_dotProduct_mulVec, B.H.sub A.H, Matrix.sub_mulVec] + +instance [DecidableEq n] : ZeroLEOneClass (HermitianMat n 𝕜) where + zero_le_one := by + rw [zero_le_iff] + exact Matrix.PosSemidef.one + +omit [Fintype n] in +theorem lt_iff_posdef : A < B ↔ (B - A).mat.PosSemidef ∧ A ≠ B := + lt_iff_le_and_ne + +instance : IsStrictOrderedModule ℝ (HermitianMat n 𝕜) where + smul_lt_smul_of_pos_left a ha b b₂ hb := by + rw [HermitianMat.lt_iff_posdef] at hb ⊢ + simp only [← smul_sub, ne_eq, smul_right_inj ha.ne'] + exact ⟨hb.left.smul ha.le, hb.right⟩ + smul_lt_smul_of_pos_right a ha b b2 hb := by + rw [HermitianMat.lt_iff_posdef] at ha ⊢ + rw [sub_zero] at ha + rw [← sub_pos] at hb + convert And.intro (ha.left.smul hb.le) ha.right using 1 + · simp [← sub_smul] + simp only [ne_eq, not_iff_not] + constructor + · intro h + rw [eq_comm, ← sub_eq_zero, ← sub_smul] at h + simpa [eq_comm, hb.ne'] using h + · rintro rfl; simp + +theorem posSemidef_iff_spectrum_Ici [DecidableEq n] (A : HermitianMat n 𝕜) : + 0 ≤ A ↔ spectrum ℝ A.mat ⊆ Set.Ici 0 := by + rw [zero_le_iff, Matrix.posSemidef_iff_isHermitian_and_spectrum_nonneg] + simp [A.H, Set.Ici.eq_1] + +theorem posSemidef_iff_spectrum_nonneg [DecidableEq n] (A : HermitianMat n 𝕜) : + 0 ≤ A ↔ ∀ x ∈ spectrum ℝ A.mat, 0 ≤ x := by + exact A.posSemidef_iff_spectrum_Ici + +theorem trace_nonneg (hA : 0 ≤ A) : 0 ≤ A.trace := by + exact (RCLike.nonneg_iff.mp (zero_le_iff.mp hA).trace_nonneg).1 + +theorem trace_pos (hA : 0 < A) : 0 < A.trace := by + open ComplexOrder in + have hA' := hA.le + rw [HermitianMat.zero_le_iff] at hA' + have h_pos := Matrix.PosSemidef.trace_pos hA' (by simpa [HermitianMat.ext_iff] using hA.ne') + rw [HermitianMat.trace_eq_re_trace] + rw [RCLike.pos_iff] at h_pos + exact h_pos.left + +open Lean Meta Mathlib.Meta.Positivity in +/-- Positivity extension for `HermitianMat.trace`: nonneg when the matrix is nonneg, +positive when the matrix is positive. -/ +@[positivity HermitianMat.trace _] +meta def evalHermitianMatTrace : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with + | none => pure .none + | some _ => do + let .app _tr (A : Expr) ← whnfR e | throwError "not HermitianMat.trace" + let (isStrict, pfA) ← bestResult A + if isStrict then + pure (.positive (← mkAppM ``HermitianMat.trace_pos #[pfA])) + else + pure (.nonnegative (← mkAppM ``HermitianMat.trace_nonneg #[pfA])) + +--Without these shortcut instances, `gcongr` fails to close certain goals...? Why? TODO +instance : PosSMulMono ℝ (HermitianMat n 𝕜) := inferInstance + +instance : SMulPosMono ℝ (HermitianMat n 𝕜) := inferInstance + +--Without explicitly giving this instance, Lean times out trying to find it sometimes. +instance : PosSMulReflectLE ℝ (HermitianMat n 𝕜) := + PosSMulMono.toPosSMulReflectLE + +theorem le_trace_smul_one [DecidableEq n] (hA : 0 ≤ A) : A ≤ A.trace • 1 := by + have hA' : A.mat.PosSemidef := zero_le_iff.mp hA + refine (Matrix.PosSemidef.le_smul_one_of_eigenvalues_iff hA'.1 A.trace).mp ?_ + rw [← A.sum_eigenvalues_eq_trace] + intro i + exact Finset.single_le_sum (fun j _ ↦ hA'.eigenvalues_nonneg j) (Finset.mem_univ i) + +/-- The Kronecker product of two nonnegative Hermitian matrices is nonnegative. -/ +theorem kronecker_nonneg {A : HermitianMat m 𝕜} (hA : 0 ≤ A) (hB : 0 ≤ B) : 0 ≤ A ⊗ₖ B := by + rw [zero_le_iff, kronecker_mat] + classical exact (zero_le_iff.mp hA).PosSemidef_kronecker (zero_le_iff.mp hB) + +/-- The self-Kronecker map `A ↦ A ⊗ₖ A` is monotone on nonnegative Hermitian matrices. -/ +theorem kronecker_self_mono (hA : 0 ≤ A) (hB : 0 ≤ B) (hAB : A ≤ B) : + A ⊗ₖ A ≤ B ⊗ₖ B := by + rw [← sub_nonneg] + have hAC : A ⊗ₖ B + -(A ⊗ₖ A) = A ⊗ₖ (B - A) := by + rw [show -(A ⊗ₖ A) = A ⊗ₖ (-A) by + symm + ext1 + simpa using (Matrix.kronecker_smul (-1 : 𝕜) A.mat A.mat)] + simpa [sub_eq_add_neg] using + (HermitianMat.kronecker_add (A := A) (B := B) (C := -A)).symm + have hEq : B ⊗ₖ B - A ⊗ₖ A = A ⊗ₖ (B - A) + (B - A) ⊗ₖ B := by + calc + B ⊗ₖ B - A ⊗ₖ A = (A + (B - A)) ⊗ₖ B - A ⊗ₖ A := by + rw [show A + (B - A) = B by abel] + _ = (A ⊗ₖ B + (B - A) ⊗ₖ B) - A ⊗ₖ A := by rw [HermitianMat.add_kronecker] + _ = (A ⊗ₖ B + -(A ⊗ₖ A)) + (B - A) ⊗ₖ B := by abel + _ = A ⊗ₖ (B - A) + (B - A) ⊗ₖ B := by rw [hAC] + simpa [hEq] using add_nonneg + (HermitianMat.kronecker_nonneg hA (sub_nonneg.mpr hAB)) + (HermitianMat.kronecker_nonneg (sub_nonneg.mpr hAB) hB) + +/-- The Kronecker product of two positive Hermitian matrices is positive. -/ +theorem kronecker_pos {A : HermitianMat m 𝕜} (hA : 0 < A) (hB : 0 < B) : 0 < A ⊗ₖ B := by + apply lt_of_le_of_ne (kronecker_nonneg hA.le hB.le) + intro h + replace h := congr(trace $h) + simp only [trace_zero, trace_kronecker, zero_eq_mul] at h + apply trace_pos at hA + apply trace_pos at hB + grind only [cases Or] + +omit [Fintype n] in +open MatrixOrder in +theorem posSemidef_to_nonneg {A : Matrix n n 𝕜} (hA : A.PosSemidef) : 0 ≤ A := by + exact hA.nonneg + +open MatrixOrder in +theorem posDef_to_pos {A : Matrix n n 𝕜} (hA : A.PosDef) [Nonempty n] : 0 < A := by + apply lt_of_le_of_ne hA.posSemidef.nonneg + rintro rfl + classical simpa [Matrix.det_zero] using hA.det_pos + +open Lean Meta in +/-- Given an expression `e` (a matrix) and a proof expression `p` whose type may be +`Matrix.PosSemidef A`, `Matrix.PosDef A`, or `And P Q` (syntactically), attempt to +find a proof of nonnegativity or positivity for `e`. Only syntactic matching on the +head constant is used; `isDefEq` is used only to compare the matrix argument. -/ +meta partial def findMatrixPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : + MetaM (Option (Bool × Expr)) := do + let head := ty.getAppFn + if head.isConst then + let name := head.constName! + if name == ``Matrix.PosSemidef then + -- Last argument is the matrix + let args := ty.getAppArgs + let A := args.back! + if ← isDefEq A e then + let pf ← mkAppM ``HermitianMat.posSemidef_to_nonneg #[p] + return some (false, pf) + if name == ``Matrix.PosDef then + let args := ty.getAppArgs + let A := args.back! + if ← isDefEq A e then + -- Try strict (needs Nonempty n); extract the index type from PosDef args + -- PosDef args: [n, R, Fintype n, Ring R, PartialOrder R, StarRing R, A] + let nType := args[0]! + let nonemptyType ← mkAppM ``Nonempty #[nType] + match ← try? (synthInstance nonemptyType) with + | some nonemptyInst => + -- posDef_to_pos : {𝕜} → [RCLike 𝕜] → {n} → [Fintype n] → {A} → (hA : A.PosDef) → [Nonempty n] → 0 < A + let pf ← mkAppOptM ``HermitianMat.posDef_to_pos #[none, none, none, none, none, p, nonemptyInst] + return some (true, pf) + | none => + let pSemidef ← mkAppM ``Matrix.PosDef.posSemidef #[p] + let pf ← mkAppM ``HermitianMat.posSemidef_to_nonneg #[pSemidef] + return some (false, pf) + if name == ``And then + let args := ty.getAppArgs + if args.size == 2 then + -- Recurse on left and right + let pLeft ← mkAppM ``And.left #[p] + if let some result ← findMatrixPSDInExpr e pLeft args[0]! then + return some result + let pRight ← mkAppM ``And.right #[p] + if let some result ← findMatrixPSDInExpr e pRight args[1]! then + return some result + return none + +open Lean Meta Mathlib.Meta.Positivity in +/-- Positivity extension for `Matrix`: looks for `A.PosSemidef` or `A.PosDef` in the +local context (including syntactic `And` conjunctions) to prove `0 ≤ A` or `0 < A`. -/ +@[positivity (_ : HermitianMat _ _)] +meta def evalMatrixPSD : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _pα => do + let lctx ← getLCtx + let mut best : Strictness _zα e (some _pα) := .none + for ldecl in lctx do + if ldecl.isImplementationDetail then continue + let ty := ldecl.type + let p : Expr := .fvar ldecl.fvarId + if let some (isStrict, pf) ← findMatrixPSDInExpr e p ty then + if isStrict then + return .positive pf + else + best := .nonnegative pf + match best with + | .none => throwError "evalMatrixPSD: no PosSemidef or PosDef hypothesis found for {e}" + | other => return other + + +omit [Fintype n] in +theorem mat_posSemidef_to_nonneg (hA : A.mat.PosSemidef) : 0 ≤ A := + zero_le_iff.mpr hA + +theorem mat_posDef_to_pos [Nonempty n] (hA : A.mat.PosDef) : 0 < A := by + exact posDef_to_pos hA + +open Lean Meta in +/-- Given an expression `e` (a `HermitianMat`) and a proof expression `p` whose type may be +`Matrix.PosSemidef A.mat`, `Matrix.PosDef A.mat`, or `And P Q` (syntactically), attempt to +find a proof of nonnegativity or positivity for `e`. Only syntactic matching on the +head constant is used; `isDefEq` is used only to compare the `HermitianMat` argument. -/ +meta partial def findHermitianMatPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : + MetaM (Option (Bool × Expr)) := do + let head := ty.getAppFn + if head.isConst then + let name := head.constName! + if name == ``Matrix.PosSemidef || name == ``Matrix.PosDef then + -- Last argument should be `A.mat` i.e. `HermitianMat.mat A` + let args := ty.getAppArgs + let matExpr := args.back! + -- Check if matExpr is `HermitianMat.mat A` (or equivalently `Subtype.val A`) + let matHead := matExpr.getAppFn + if matHead.isConst && (matHead.constName! == ``HermitianMat.mat || + matHead.constName! == ``Subtype.val) then + let matArgs := matExpr.getAppArgs + let A := matArgs.back! + if ← isDefEq A e then + if name == ``Matrix.PosSemidef then + let pf ← mkAppM ``HermitianMat.mat_posSemidef_to_nonneg #[p] + return some (false, pf) + else + -- PosDef: try strict (needs Nonempty n) + let psdArgs := ty.getAppArgs + let nType := psdArgs[0]! + let nonemptyType ← mkAppM ``Nonempty #[nType] + match ← try? (synthInstance nonemptyType) with + | some nonemptyInst => + -- mat_posDef_to_pos : {𝕜} → [RCLike 𝕜] → {n} → [Fintype n] → {A} → [Nonempty n] → (hA : A.mat.PosDef) → 0 < A + let pf ← mkAppOptM ``HermitianMat.mat_posDef_to_pos #[none, none, none, none, none, nonemptyInst, p] + return some (true, pf) + | none => + let pSemidef ← mkAppM ``Matrix.PosDef.posSemidef #[p] + let pf ← mkAppM ``HermitianMat.mat_posSemidef_to_nonneg #[pSemidef] + return some (false, pf) + if name == ``And then + let args := ty.getAppArgs + if args.size == 2 then + let pLeft ← mkAppM ``And.left #[p] + if let some result ← findHermitianMatPSDInExpr e pLeft args[0]! then + return some result + let pRight ← mkAppM ``And.right #[p] + if let some result ← findHermitianMatPSDInExpr e pRight args[1]! then + return some result + return none + +open Lean Meta Mathlib.Meta.Positivity in +/-- Positivity extension for `HermitianMat`: looks for `A.mat.PosSemidef` or `A.mat.PosDef` in +the local context (including syntactic `And` conjunctions) to prove `0 ≤ A` or `0 < A`. -/ +@[positivity (_ : HermitianMat _ _)] +meta def evalHermitianMatPSD : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _pα => do + trace[Tactic.positivity] "evalHermitianMatPSD: {e}" + let lctx ← getLCtx + let mut best : Strictness _zα e (some _pα) := .none + for ldecl in lctx do + if ldecl.isImplementationDetail then continue + let ty := ldecl.type + let p : Expr := .fvar ldecl.fvarId + if let some (isStrict, pf) ← findHermitianMatPSDInExpr e p ty then + if isStrict then + return .positive pf + else + best := .nonnegative pf + match best with + | .none => throwError "evalHermitianMatPSD: no A.mat.PosSemidef or A.mat.PosDef hypothesis found for {e}" + | other => return other + +open Lean Meta Mathlib.Meta.Positivity in +/-- Positivity extension for `HermitianMat.kronecker`: nonneg when both factors are. -/ +@[positivity HermitianMat.kronecker _ _] +meta def evalHermitianMatKronecker : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _ => do + let .app (.app _kron A) B ← whnfR e | throwError "not HermitianMat.kronecker" + let (isStrictA, pfA) ← bestResult A + let (isStrictB, pfB) ← bestResult B + if isStrictA && isStrictB then + pure (.positive (← mkAppM ``HermitianMat.kronecker_pos #[pfA, pfB])) + else + let pfA' ← try mkAppM ``le_of_lt #[pfA] catch _ => pure pfA + let pfB' ← try mkAppM ``le_of_lt #[pfB] catch _ => pure pfB + let pfAB' ← mkAppM ``HermitianMat.kronecker_nonneg #[pfA', pfB'] + pure (.nonnegative pfAB') + +variable (M) in +open Lean Meta Mathlib.Meta.Positivity in +/-- Positivity extension for `HermitianMat.conj`: nonneg when the inner matrix is. -/ +theorem conj_nonneg (hA : 0 ≤ A) : 0 ≤ A.conj M := by + rw [zero_le_iff] at hA ⊢ + exact Matrix.PosSemidef.mul_mul_conjTranspose_same hA M + +theorem conj_pos [DecidableEq n] {A : HermitianMat n 𝕜} {M : Matrix m n 𝕜} (hA : 0 < A) + (h : LinearMap.ker M.toEuclideanLin ≤ A.ker) : 0 < A.conj M := by + classical exact (A.conj_nonneg M hA.le).lt_of_ne' (A.conj_ne_zero hA.ne' h) + +open Lean Meta Mathlib.Meta.Positivity in +/-- Positivity extension for `HermitianMat.conj`: nonneg when the inner matrix is. -/ +@[positivity HermitianMat.conj _ _] +meta def evalHermitianMatConj : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _ => do + let .app (.app _coe conjM) (A : Expr) ← whnfR e | throwError "not conj application" + let M := conjM.appArg! + let (_, pfA) ← bestResult A + let pfNonneg ← try mkAppM ``le_of_lt #[pfA] catch _ => pure pfA + pure (.nonnegative (← mkAppM ``HermitianMat.conj_nonneg #[M, pfNonneg])) + +open MatrixOrder in +example {A : Matrix n n ℂ} (hA : A.PosSemidef) : 0 ≤ A := by + positivity + +open MatrixOrder in +example {A : Matrix n n ℂ} [Nonempty n] (hA : A.PosDef) : 0 < A := by + positivity + +example (hA : A.mat.PosSemidef) : 0 ≤ A := by + positivity + +example [Nonempty n] (hA : A.mat.PosDef) : 0 < A := by + positivity + +example [DecidableEq n] [DecidableEq m] [Nonempty n] [Nonempty m] + (A B : HermitianMat n ℂ) (hA : 0 ≤ A) (hB : 0 ≤ B) (M : Matrix m n ℂ) : + 0 < (2 : HermitianMat (n × m) ℂ) + (3 • A) ⊗ₖ (Real.pi • B).conj M := by + positivity + +example (A B : HermitianMat n ℂ) (hA : 0 < A) (hB : 0 < B) : + 0 < ((37 • A) ⊗ₖ ((38 : ℝ) • B)).trace := by + positivity + +omit [Fintype n] in +theorem convex_cone (hA : 0 ≤ A) (hB : 0 ≤ B) {c₁ c₂ : ℝ} (hc₁ : 0 ≤ c₁) (hc₂ : 0 ≤ c₂) : + 0 ≤ (c₁ • A + c₂ • B) := by + rw [zero_le_iff] at hA hB ⊢ + exact (hA.smul hc₁).add (hB.smul hc₂) + +theorem sq_nonneg [DecidableEq n] : 0 ≤ A ^ 2 := by + simp [zero_le_iff, pow_two] + nth_rewrite 1 [←Matrix.IsHermitian.eq A.H] + exact Matrix.posSemidef_conjTranspose_mul_self A.mat + +theorem ker_antitone [DecidableEq n] (hA : 0 ≤ A) : A ≤ B → B.ker ≤ A.ker := by + intro h x hB + replace h := (le_iff_mulVec_le.mp h) x + rw [HermitianMat.mem_ker_iff_mulVec_zero] at hB ⊢ + rw [hB, dotProduct_zero] at h + rw [zero_le_iff] at hA + rw [← hA.dotProduct_mulVec_zero_iff] + rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA + exact le_antisymm h (hA.right x) + +theorem conj_mono (h : A ≤ B) : A.conj M ≤ B.conj M := by + have h_conj_pos : (M * (B - A).mat * Mᴴ).PosSemidef := + Matrix.PosSemidef.mul_mul_conjTranspose_same h M + constructor; + · simp [conj, Matrix.IsHermitian, Matrix.mul_assoc] + · simpa [Matrix.mul_sub, Matrix.sub_mul] using h_conj_pos.2 + +lemma conj_posDef [DecidableEq n] (hA : A.mat.PosDef) (hN : IsUnit N) : + (A.conj N).mat.PosDef := by + rw [Matrix.posDef_iff_dotProduct_mulVec] at hA ⊢ + use HermitianMat.H _ + intro x hx_ne_zero + open Matrix in + have h_pos : 0 < star (Nᴴ *ᵥ x) ⬝ᵥ A *ᵥ (Nᴴ *ᵥ x) := by + apply hA.2 + intro h + apply hx_ne_zero + simpa [ hN ] using Matrix.eq_zero_of_mulVec_eq_zero + (by simpa [Matrix.det_conjTranspose] using hN.map Matrix.detMonoidHom) h + convert h_pos using 1 + simp only [conj_apply_mat, mulVec_mulVec, Matrix.mul_assoc] + simp [dotProduct_mulVec, mulVec_conjTranspose] + +lemma inv_conj [DecidableEq n] {M : Matrix n n 𝕜} (hM : IsUnit M) : + (A.conj M)⁻¹ = A⁻¹.conj (M⁻¹)ᴴ := by + have h_inv : (M⁻¹)ᴴ * Mᴴ = 1 := by + simp only [Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero, ne_eq] at hM + simp [Matrix.conjTranspose_nonsing_inv, hM] + ext1 + simp only [conj, AddMonoidHom.coe_mk, ZeroHom.coe_mk, Matrix.conjTranspose_conjTranspose] + simp only [mat_inv, mat_mk] + rw [Matrix.mul_inv_rev, Matrix.mul_inv_rev, Matrix.inv_eq_left_inv h_inv, mul_assoc] + +theorem le_iff_mulVec_le_mulVec (A B : HermitianMat n 𝕜) : + A ≤ B ↔ ∀ v : n → 𝕜, star v ⬝ᵥ A.mat *ᵥ v ≤ star v ⬝ᵥ B.mat *ᵥ v := by + rw [← sub_nonneg, HermitianMat.zero_le_iff] + conv_rhs => enter [v]; rw [← sub_nonneg] + have h := (B - A).H + simp only [HermitianMat.mat_sub] at h + simp [Matrix.posSemidef_iff_dotProduct_mulVec, Matrix.sub_mulVec, h] + +theorem inner_mulVec_nonneg (hA : 0 ≤ A) (v : n → 𝕜) : + 0 ≤ star v ⬝ᵥ A.mat *ᵥ v := by + rw [le_iff_mulVec_le_mulVec] at hA + simpa using hA v + +theorem mem_ker_of_inner_mulVec_zero [DecidableEq n] (hA : 0 ≤ A) (v : EuclideanSpace 𝕜 n) + (h : star v ⬝ᵥ A.mat *ᵥ v = 0) : v ∈ A.ker := by + have := ((zero_le_iff.mp hA).dotProduct_mulVec_zero_iff v).mp h + exact congr(WithLp.toLp 2 $this) + +theorem ker_add [DecidableEq n] (hA : 0 ≤ A) (hB : 0 ≤ B) : + (A + B).ker = A.ker ⊓ B.ker := by + have hA' := zero_le_iff.mp hA + have hB' := zero_le_iff.mp hB + ext v; simp only [Submodule.mem_inf, mem_ker_iff_mulVec_zero] + constructor + · intro hv + have h3 : star v ⬝ᵥ A.mat *ᵥ v + star v ⬝ᵥ B.mat *ᵥ v = 0 := by + rw [← dotProduct_add, ← Matrix.add_mulVec, ← mat_add, hv, dotProduct_zero] + rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA' hB' + obtain ⟨hzA, hzB⟩ := (add_eq_zero_iff_of_nonneg (hA'.2 v) (hB'.2 v)).mp h3 + rw [← Matrix.posSemidef_iff_dotProduct_mulVec] at hA' hB' + exact ⟨(hA'.dotProduct_mulVec_zero_iff v).mp hzA, + (hB'.dotProduct_mulVec_zero_iff v).mp hzB⟩ + · simp +contextual [Matrix.add_mulVec] + +theorem ker_sum [DecidableEq n] (f : ι → HermitianMat n 𝕜) (hf : ∀ i, 0 ≤ f i) : + (∑ i, f i).ker = ⨅ i, (f i).ker := by + ext v + simp only [Submodule.mem_iInf, mem_ker_iff_mulVec_zero] + constructor + · intro hv i + have hfi := zero_le_iff.mp (hf i) + rw [← hfi.dotProduct_mulVec_zero_iff] + have hge : ∀ j, 0 ≤ star v ⬝ᵥ (f j).mat *ᵥ v := by + intro j + have := zero_le_iff.mp (hf j) + rw [Matrix.posSemidef_iff_dotProduct_mulVec] at this + exact this.2 v + have hsum : ∑ j, star v ⬝ᵥ (f j).mat *ᵥ v = 0 := by + rw [← dotProduct_sum, ← Matrix.sum_mulVec, ← mat_finset_sum, hv, dotProduct_zero] + exact le_antisymm + (hsum ▸ Finset.single_le_sum (fun j _ => hge j) (Finset.mem_univ i)) + (hge i) + · intro h + simp [Matrix.sum_mulVec, h] + +theorem ker_conj [DecidableEq n] (hA : 0 ≤ A) (B : Matrix n n 𝕜) : + (A.conj B).ker = Submodule.comap (Matrix.toEuclideanLin B.conjTranspose) A.ker := by + + ext v; simp [HermitianMat.conj]; + constructor <;> intro h; + · have := Matrix.PosSemidef.dotProduct_mulVec_zero_iff ( show Matrix.PosSemidef A.mat from zero_le_iff.mp hA ); + convert this ( Bᴴ.mulVec v ) |>.1 _ using 1; + · rw [ mem_ker_iff_mulVec_zero ]; + congr! 2; + · convert congr_arg ( fun x : EuclideanSpace _ _ => star v.ofLp ⬝ᵥ x ) h using 1 + simp [Matrix.mulVec_mulVec, Matrix.dotProduct_mulVec] + · simp [Matrix.mul_assoc, Matrix.dotProduct_mulVec, Matrix.mulVec_mulVec, Matrix.star_mulVec, Matrix.conjTranspose_conjTranspose, lin] + · simp [dotProduct] + · simp only [ker, Matrix.mul_assoc, LinearMap.mem_ker] + convert congr_arg B.toEuclideanLin h using 1 + · simp [HermitianMat.lin, Matrix.toEuclideanLin] + · exact Eq.symm (LinearMap.map_zero (Matrix.toEuclideanLin B)) + +theorem ker_le_of_le_smul {α : ℝ} [DecidableEq n] (hα : α ≠ 0) (hA : 0 ≤ A) (hAB : A ≤ α • B) : B.ker ≤ A.ker := by + rw [← ker_pos_smul B hα] + exact ker_antitone hA hAB + +/-- If a Hermitian matrix is bounded by `M * I`, then all its eigenvalues are at most `M`. -/ +theorem le_smul_one_imp_eigenvalues_le [DecidableEq n] (A : HermitianMat n ℂ) (M : ℝ) + (h : A ≤ M • (1 : HermitianMat n ℂ)) (i : n) : + A.H.eigenvalues i ≤ M := by + let v : n → ℂ := (A.H.eigenvectorBasis i).ofLp + have hv : star v ⬝ᵥ v = (1 : ℂ) := by + rw [show v = (A.H.eigenvectorBasis i).ofLp from rfl] + rw [dotProduct_comm, ← EuclideanSpace.inner_eq_star_dotProduct] + simp [A.H.eigenvectorBasis.orthonormal.1 i] + have hquad := (le_iff_mulVec_le_mulVec A (M • (1 : HermitianMat n ℂ))).mp h v + rw [show A.mat.mulVec v = (A.H.eigenvalues i : ℂ) • v from by + simpa [v] using A.H.mulVec_eigenvectorBasis i] at hquad + rw [dotProduct_smul, hv] at hquad + change (A.H.eigenvalues i : ℂ) • 1 ≤ + star v ⬝ᵥ ((M : ℂ) • (1 : Matrix n n ℂ)) *ᵥ v at hquad + have hquadC : (A.H.eigenvalues i : ℂ) ≤ (M : ℂ) := by + have hright : star v ⬝ᵥ ((M : ℂ) • (1 : Matrix n n ℂ)) *ᵥ v = (M : ℂ) := by + simp [Matrix.smul_mulVec, hv] + simpa [Matrix.smul_mulVec, hv] using hquad.trans_eq hright + exact_mod_cast hquadC + +open MatrixOrder in +/-- If all eigenvalues of a Hermitian matrix are at most `M`, then it is bounded by `M * I`. -/ +theorem eigenvalues_le_imp_le_smul_one [DecidableEq n] (A : HermitianMat n ℂ) (M : ℝ) + (h : ∀ i, A.H.eigenvalues i ≤ M) : + A ≤ M • (1 : HermitianMat n ℂ) := by + exact + (Matrix.PosSemidef.le_smul_one_of_eigenvalues_iff A.H M).mp h + +--TODO: Positivity extensions for traceLeft, traceRight, rpow, nat powers, inverse function, +-- the various `proj` function (in Proj.lean), and the inner product. + +/-! ## Positivity extensions connecting HermitianMat and Matrix -/ +section MatrixPositivity +open Lean Meta Mathlib.Meta.Positivity + +/-- If a HermitianMat is PSD, then its eigenvalues are nonneg. -/ +theorem eigenvalues_nonneg [DecidableEq n] (hA : 0 ≤ A) (i : n) : + 0 ≤ A.H.eigenvalues i := + (zero_le_iff.mp hA).eigenvalues_nonneg i + +omit [Fintype n] in +open MatrixOrder in +/-- If a HermitianMat is PSD, its underlying matrix is nonneg in the Loewner order. -/ +theorem mat_nonneg (hA : 0 ≤ A) : 0 ≤ A.mat := + Matrix.nonneg_iff_posSemidef.mpr (zero_le_iff.mp hA) + +omit [Fintype n] in +open MatrixOrder in +/-- If a HermitianMat is positive, its underlying matrix is positive in the Loewner order. -/ +theorem mat_pos (hA : 0 < A) : 0 < A.mat := + hA + +open MatrixOrder in +/-- `Mᴴ * M` is nonneg in the Loewner order, for any matrix `M`. -/ +theorem _root_.Matrix.nonneg_conjTranspose_mul_self {m : Type*} [Fintype m] + (M : Matrix m n 𝕜) : 0 ≤ M.conjTranspose * M := + Matrix.nonneg_iff_posSemidef.mpr (Matrix.posSemidef_conjTranspose_mul_self M) + +open MatrixOrder in +/-- `M * Mᴴ` is nonneg in the Loewner order, for any matrix `M`. -/ +theorem _root_.Matrix.nonneg_self_mul_conjTranspose {m : Type*} [Fintype m] + (M : Matrix n m 𝕜) : 0 ≤ M * M.conjTranspose := + Matrix.nonneg_iff_posSemidef.mpr (Matrix.posSemidef_self_mul_conjTranspose M) + +omit [Fintype m] in +open MatrixOrder in +theorem subtype_mk_nonneg {M : Matrix m m 𝕜} (h : 0 ≤ M) : + 0 ≤ (⟨M, (Matrix.LE.le.posSemidef h).isHermitian⟩ : HermitianMat m 𝕜) := + h + +omit [Fintype m] in +open MatrixOrder in +theorem subtype_mk_pos {M : Matrix m m 𝕜} (h : 0 < M) : + 0 < (⟨M, (Matrix.LE.le.posSemidef h.le).isHermitian⟩ : HermitianMat m 𝕜) := + h + +open MatrixOrder in +private theorem _root_.Matrix.eigenvalues_nonneg [DecidableEq n] {M : Matrix n n 𝕜} (h : 0 ≤ M) (i : n) : + 0 ≤ (Matrix.LE.le.posSemidef h).isHermitian.eigenvalues i := + (Matrix.LE.le.posSemidef h).eigenvalues_nonneg i + +/-- Positivity extension for `A.mat` where `A : HermitianMat`: nonneg when `0 ≤ A`. -/ +@[positivity HermitianMat.mat _] +meta def evalHermitianMatMat : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _ => do + let .app _matFn (A : Expr) ← whnfR e | throwError "not HermitianMat.mat" + match ← bestResult A with + | (true, pa) => + pure (.positive (← mkAppM ``HermitianMat.mat_pos #[pa])) + | (false, pa) => + pure (.nonnegative (← mkAppM ``HermitianMat.mat_nonneg #[pa])) + +/-- Positivity extension for `A.mat` where `A : HermitianMat`: nonneg when `0 ≤ A`. -/ +@[positivity Subtype.val (_ : HermitianMat _ _)] +meta def evalHermitianMatVal : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _ => do + /- Note: we must not call `whnf` on `e` because `Subtype.val` is a structure + projection (reducible), so `whnf` would reduce it and destroy the pattern. -/ + let A := e.appArg! + match ← bestResult A with + | (true, pa) => + pure (.positive (← mkAppM ``HermitianMat.mat_pos #[pa])) + | (false, pa) => + pure (.nonnegative (← mkAppM ``HermitianMat.mat_nonneg #[pa])) + +/-- Positivity extension for `M * Mᴴ` as a Matrix: always nonneg. -/ +@[positivity HMul.hMul _ (Matrix.conjTranspose _)] +meta def evalMatrixSelfMulConjTranspose : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _ => do + let .app (.app _hmul _M) Mstar ← whnfR e | throwError "not HMul application" + let .app _conjTranspose M' ← whnfR Mstar | throwError "not M * conjTranspose" + pure (.nonnegative (← mkAppM ``Matrix.nonneg_self_mul_conjTranspose #[M'])) + +/-- Positivity extension for `Mᴴ * M` as a Matrix: always nonneg. -/ +@[positivity HMul.hMul (Matrix.conjTranspose _) _] +meta def evalMatrixConjTransposeMulSelf : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _ => do + let .app (.app _hmul Mstar) _M ← whnfR e | throwError "not HMul application" + let .app _conjTranspose M' ← whnfR Mstar | throwError "not conjTranspose * M" + pure (.nonnegative (← mkAppM ``Matrix.nonneg_conjTranspose_mul_self #[M'])) + +/-- Positivity extension for `⟨M, (pf : M.IsHermitian)⟩` as a HermitianMat: +equivalent to `0 ≤ M` in `MatrixOrder`. -/ +@[positivity (Subtype.mk _ _ : HermitianMat _ _)] +meta def evalHermitianMatMk : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _ => do + let .app (.app _mkFn val) _proof ← whnfR e | throwError "not Subtype.mk" + match ← bestResult val with + | (true, pa) => + pure (.positive (← mkAppM ``HermitianMat.subtype_mk_pos #[pa])) + | (false, pa) => + pure (.nonnegative (← mkAppM ``HermitianMat.subtype_mk_nonneg #[pa])) + +/-- Positivity extension for eigenvalues of a Matrix: `0 ≤ (_ : M.IsHermitian).eigenvalues i`. +Will try to prove `0 ≤ M` in the `MatrixOrder`. If the proof is `A.H`, i.e. M comes from a +HermitianMat, this will give `0 ≤ A.mat` which becomes `0 ≤ A` later. -/ +@[positivity Matrix.IsHermitian.eigenvalues _ _] +meta def evalMatrixEigenvalues : PositivityExt where eval {_u _α} _zα _pα? e := + match _pα? with | none => pure .none | some _ => do + let .app (.app _eigenvaluesFn hProof) _i ← whnfR e | throwError "not eigenvalues application" + let pType ← inferType hProof + if pType.isAppOf ``Matrix.IsHermitian then + let M ← pure pType.appArg! + let (_, pa) ← bestResult M + let pa ← try mkAppM ``le_of_lt #[pa] catch _ => pure pa + pure (.nonnegative (← mkAppM ``Matrix.eigenvalues_nonneg #[pa, _i])) + else + throwError "not a Matrix.IsHermitian" + +-- Tests +section tests + +variable [DecidableEq n] [DecidableEq m] +open MatrixOrder + +-- Test: eigenvalues nonneg from PSD HermitianMat +example (A : HermitianMat n ℂ) (hA : 0 < A) (i : n) : 0 ≤ A.H.eigenvalues i := by + positivity + +-- Test: A.mat nonneg from A nonneg +example (A : HermitianMat n ℂ) (hA : 0 ≤ A) : 0 ≤ A.mat := by positivity +example (A : HermitianMat n ℂ) (hA : 0 < A) : 0 < A.mat := by positivity +example (A : HermitianMat n ℂ) (hA : 0 ≤ A) : 0 ≤ A.val := by positivity +example (A : HermitianMat n ℂ) (hA : 0 < A) : 0 < A.val := by positivity + +-- Test: Mᴴ * M nonneg as Matrix +example (M : Matrix m n ℂ) : 0 ≤ M.conjTranspose * M := by positivity + +-- Test: M * Mᴴ nonneg as Matrix +example (M : Matrix n m ℂ) : 0 ≤ M * M.conjTranspose := by positivity + +-- Test: ⟨Mᴴ * M, _⟩ nonneg as HermitianMat + +example (M : Matrix m n ℂ) : + (0 : HermitianMat n ℂ) ≤ ⟨M.conjTranspose * M, Matrix.isHermitian_conjTranspose_mul_self M⟩ := by + positivity + +-- Test: ⟨M * Mᴴ, _⟩ nonneg as HermitianMat + +example (M : Matrix n m ℝ) : + (0 : HermitianMat n ℝ) ≤ ⟨M * M.conjTranspose, Matrix.isHermitian_mul_conjTranspose_self M⟩ := by + positivity + +example (M : Matrix n n ℂ) (i : n) (A : HermitianMat n ℂ) (hA : 0 ≤ A) : + 0 ≤ (A + ⟨_, M.isHermitian_mul_conjTranspose_self⟩ + 0).H.eigenvalues i := by + positivity + +end tests +end MatrixPositivity diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean new file mode 100644 index 0000000000..bed427b628 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean @@ -0,0 +1,536 @@ +/- +Copyright (c) 2025 Leonardo A Lessa. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Leonardo A Lessa, Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC + +public import Mathlib.Analysis.CStarAlgebra.Classes +public import Mathlib.Analysis.InnerProductSpace.Positive +public import Mathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.PosPart.Basic + +/-! + +# Projectors associated to Hermitian matrices + + * `projector`: The `HermitianMat` that projects onto a given submodule + * `supportProj`: The `HermitianMat` that projects onto the range (nonzero eigenvalues) + * `kerProj`: The `HermitianMat` that projects onto the kernel + * `projLE`: With notation `{A ≤ₚ B}`, `projLE A B` is the projector onto the nonnegative + eigenspace of `B - A`. + * `projLT`: With notation `{A <ₚ B}`, `projLT A B` is the projector onto the positive + eigenspace of `B - A`. + * Positive and negative part, written `A⁺` and `A⁻`, give the restriction of a HermitianMat + onto its positive (resp. negative) eigenvalues; equivalently, it's nonnegative (resp. + nonpositive) eigenvalues. +-/ + +@[expose] public section + +noncomputable section +namespace HermitianMat + +variable {n : Type*} [Fintype n] [DecidableEq n] +variable {𝕜 : Type*} [RCLike 𝕜] +variable {ι : Type*} [Fintype ι] (S : Submodule 𝕜 (EuclideanSpace 𝕜 n)) + +variable (A B : HermitianMat n 𝕜) + +open scoped InnerProductSpace + +/-- +Given a Submodule (EuclideanSpace ...) to HermitianMat, this gives the projector onto that subspace, +i.e. a matrix that squares to itself, preserves vectors in the submodule, and zeroes out anything +in the orthogonal complement of that submodule. +-/ +noncomputable def projector (S : Submodule 𝕜 (EuclideanSpace 𝕜 n)) : HermitianMat n 𝕜 := + let P := S.subtypeL.comp S.orthogonalProjectionOnto + ⟨P.toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis, by + ext i j + have h1 := S.inner_starProjection_left_eq_right (EuclideanSpace.single i 1) (EuclideanSpace.single j 1) + simp_all [EuclideanSpace.inner_single_right, EuclideanSpace.inner_single_left] + exact h1⟩ + +theorem projector_add_orthogonal : projector S + projector Sᗮ = 1 := by + unfold projector; + erw [ Subtype.mk_eq_mk ]; + ext i j; simp [ LinearMap.toMatrix_apply, Matrix.one_apply ] ; + +theorem projector_nonneg : 0 ≤ projector S := by + rw [zero_le_iff] + unfold projector + let P := S.subtypeL.comp S.orthogonalProjectionOnto + have hP : P.toLinearMap.IsSymmetricProjection := by + simpa [Submodule.starProjection, P] using + (Submodule.isSymmetricProjection_starProjection (U := S)) + exact LinearMap.posSemidef_toMatrix_iff _ |>.2 + ((LinearMap.IsIdempotentElem.isPositive_iff_isSymmetric hP.1).2 hP.2) + +@[simp] +theorem projector_ker : (projector S).ker = Sᗮ := by + ext v + change (Matrix.toEuclideanLin + (LinearMap.toMatrix (PiLp.basisFun 2 𝕜 n) (PiLp.basisFun 2 𝕜 n) + (S.subtypeL.comp S.orthogonalProjectionOnto)) v = 0 ↔ v ∈ Sᗮ) + rw [show Matrix.toEuclideanLin = Matrix.toLpLin (2 : ENNReal) (2 : ENNReal) from rfl, + Matrix.toLpLin_eq_toLin, Matrix.toLin_toMatrix] + exact Submodule.starProjection_apply_eq_zero_iff (K := S) + +@[simp] +theorem trace_projector : (projector S).trace = (Module.finrank 𝕜 S : ℝ) := by + suffices h_trace : ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = Module.finrank 𝕜 S by + simp [projector, trace_eq_re_trace, h_trace] + suffices h_trace : ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = (LinearMap.id.toMatrix (Module.finBasis 𝕜 S) (Module.finBasis 𝕜 S)).trace by + simp [h_trace] + rw [LinearMap.toMatrix_comp _ (Module.finBasis 𝕜 ↥S), Matrix.trace_mul_comm, ← LinearMap.toMatrix_comp] + congr 2 + ext1 + simp [Submodule.orthogonalProjectionOnto_mem_subspace_eq_self] + +/-- +The `HermitianMat.projector` for the `HermitianMat.support` submodule. +-/ +noncomputable def supportProj (A : HermitianMat n 𝕜) : HermitianMat n 𝕜 := projector A.support + +/-- +The `HermitianMat.projector` for the `HermitianMat.ker` submodule. +-/ +noncomputable def kerProj (A : HermitianMat n 𝕜) : HermitianMat n 𝕜 := projector A.ker + +@[simp] +theorem supportProj_ker : A.supportProj.ker = A.ker := by + rw [supportProj, projector_ker, support_orthogonal_eq_range] + +@[simp] +theorem kerProj_ker : A.kerProj.ker = A.support := by + rw [kerProj, projector_ker, ker_orthogonal_eq_support] + +@[simp] +theorem kerProj_add_supportProj : A.kerProj + A.supportProj = 1 := by + rw [← projector_add_orthogonal A.ker, ker_orthogonal_eq_support, kerProj, supportProj] + +@[simp] +theorem kerProj_of_nonSingular [NonSingular A] : A.kerProj = 0 := by + simp only [kerProj, nonSingular_ker_bot, HermitianMat.ext_iff] + simp [projector] + +@[simp] +theorem supportProj_of_nonSingular [NonSingular A] : A.supportProj = 1 := by + simpa using A.kerProj_add_supportProj + +/-- +The projector onto a submodule S is the sum of the outer products of the vectors in an orthonormal basis of S. +-/ +theorem projector_eq_sum_rankOne (b : OrthonormalBasis ι 𝕜 S) : + (projector S).mat = ∑ i, Matrix.vecMulVec (S.subtype (b i)) (star (S.subtype (b i))) := by + unfold projector; + ext i j; + field_simp; + simp [Matrix.vecMulVec] + -- By definition of orthogonal projection, we can write the projection of $e_j$ onto $S$ as $\sum_{k} \langle e_j, b_k \rangle b_k$. + have h_proj : ∀ j : n, S.orthogonalProjectionOnto (EuclideanSpace.single j 1) = ∑ k, (star (b k |>.1 j)) • (b k |>.1) := by + intro j + have h_proj : S.orthogonalProjectionOnto (EuclideanSpace.single j 1) = ∑ k, (inner 𝕜 (b k |>.1) (EuclideanSpace.single j 1)) • (b k |>.1) := by + convert b.sum_repr ( S.orthogonalProjectionOnto ( EuclideanSpace.single j 1 ) ) using 1; + constructor <;> intro h <;> simp_all [ Subtype.ext_iff, b.repr_apply_apply ]; + convert! h_proj using 3 + simp [ inner]; + convert! congr_arg ( fun x : EuclideanSpace ( _ ) n => x i ) ( h_proj j ) using 1 + simp [ Matrix.sum_apply, mul_comm ] + +/-- +The projector onto the support of A is the sum of the projections onto the eigenvectors with non-zero eigenvalues. +-/ +lemma projector_support_eq_sum : A.supportProj.mat = + ∑ i, (if A.H.eigenvalues i = 0 then 0 else 1) • + Matrix.vecMulVec (A.H.eigenvectorBasis i) (star (A.H.eigenvectorBasis i)) := by + have h_support : A.support = Submodule.span (𝕜) (Set.image (fun i => A.H.eigenvectorBasis i) { i | A.H.eigenvalues i ≠ 0 }) := by + refine' le_antisymm _ _; + · intro x hx; + -- By definition of $A.support$, we know that $x$ is in the orthogonal complement of the kernel of $A$. + have h_orthogonal_complement : x ∈ (A.ker : Submodule (𝕜) (EuclideanSpace (𝕜) n))ᗮ := by + convert hx using 1; + exact ker_orthogonal_eq_support A; + -- By definition of $A.ker$, we know that $x$ is orthogonal to all eigenvectors with zero eigenvalues. + have h_orthogonal_zero_eigenvalues : ∀ i, A.H.eigenvalues i = 0 → inner (𝕜) (A.H.eigenvectorBasis i) x = 0 := by + intro i hi + have h_eigenvector_zero : A.mat.mulVec (A.H.eigenvectorBasis i) = 0 := by + have := A.H.mulVec_eigenvectorBasis i; aesop; + convert h_orthogonal_complement ( A.H.eigenvectorBasis i ) _ using 1; + exact (mem_ker_iff_mulVec_zero A ((H A).eigenvectorBasis i)).mpr h_eigenvector_zero; + -- By definition of $A.ker$, we know that $x$ can be written as a linear combination of eigenvectors with non-zero eigenvalues. + have h_decomp : x = ∑ i, (inner (𝕜) (A.H.eigenvectorBasis i) x) • A.H.eigenvectorBasis i := by + exact Eq.symm (OrthonormalBasis.sum_repr' (H A).eigenvectorBasis x); + rw [ h_decomp ]; + exact Submodule.sum_mem _ fun i _ => if hi : A.H.eigenvalues i = 0 then by simp [h_orthogonal_zero_eigenvalues i hi ] else Submodule.smul_mem _ _ ( Submodule.subset_span ⟨ i, hi, rfl ⟩ ); + · rw [ Submodule.span_le, Set.image_subset_iff ]; + intro i hi; + simp_all [ HermitianMat.support ]; + use (1 / A.H.eigenvalues i) • A.H.eigenvectorBasis i; + convert congr_arg ( fun x => ( 1 / A.H.eigenvalues i ) • x ) ( A.H.mulVec_eigenvectorBasis i ) using 1 + simp [hi] + simp [ funext_iff, Matrix.mulVec, dotProduct ]; + exact PiLp.ext_iff; + have h_orthonormal_basis : ∃ b : OrthonormalBasis {i : n | A.H.eigenvalues i ≠ 0} (𝕜) (Submodule.span (𝕜) (Set.image (fun i => A.H.eigenvectorBasis i) {i | A.H.eigenvalues i ≠ 0})), ∀ i, b i = A.H.eigenvectorBasis i := by + refine' ⟨ _, _ ⟩; + refine' OrthonormalBasis.mk _ _; + use fun i => ⟨ A.H.eigenvectorBasis i, Submodule.subset_span ( Set.mem_image_of_mem _ i.2 ) ⟩; + all_goals simp [ Orthonormal ]; + · intro i j hij; have := A.H.eigenvectorBasis.orthonormal; simp_all [ orthonormal_iff_ite ] ; + exact fun h => hij <| Subtype.ext h; + · rw [ Submodule.eq_top_iff' ]; + rintro ⟨ x, hx ⟩; + rw [ Submodule.mem_span ] at hx ⊢; + intro p hp; specialize hx ( Submodule.map ( Submodule.subtype _ ) p ) ; simp_all [ Set.range_subset_iff ] ; + exact hx fun i hi => ⟨ _, hp i hi, rfl ⟩; + obtain ⟨ b, hb ⟩ := h_orthonormal_basis + have h_sum_rankOne : (projector A.support).mat = ∑ i, Matrix.vecMulVec (b i) (star (b i)) := by + convert! projector_eq_sum_rankOne _ b using 1 + simp [h_support] at * + simp_all [ Finset.sum_ite ]; + convert h_sum_rankOne using 1; + · exact h_support ▸ rfl; + · refine' Finset.sum_bij ( fun i hi => ⟨ i, by simpa using hi ⟩ ) _ _ _ _ <;> simp [ Finset.mem_filter, hb ] + +/- +`HermitianMat.supportProj` as a cfc. +-/ +theorem supportProj_eq_cfc : A.supportProj = A.cfc (if · = 0 then 0 else 1) := by + apply HermitianMat.ext; + rw [HermitianMat.cfc_toMat_eq_sum_smul_proj]; + convert projector_support_eq_sum A using 1; + refine' Finset.sum_congr rfl fun i _ => _; + ext x y + simp [ Matrix.vecMulVec, Matrix.mul_apply ] ; + simp [ Matrix.single ]; + simp [ Finset.sum_ite, Finset.filter_eq, Finset.filter_and ]; + rw [ Finset.sum_eq_single i ] <;> aesop + +/-- Projector onto the non-negative eigenspace of `B - A`. Accessible by the notation +`{A ≤ₚ B}`, which is scoped to `HermitianMat`. This is the unique maximum operator `P` +such that `P^2 = P` and `P * A * P ≤ P * B * P` in the Loewner order. -/ +def projLE (A B : HermitianMat n 𝕜) : HermitianMat n 𝕜 := + (B - A).cfc (fun x ↦ if 0 ≤ x then 1 else 0) + +/-- Projector onto the positive eigenspace of `B - A`. Accessible by the notation +`{A <ₚ B}`, which is scoped to `HermitianMat`. Compare with `proj_le`. -/ +noncomputable def projLT (A B : HermitianMat n 𝕜) : HermitianMat n 𝕜 := + (B - A).cfc (fun x ↦ if 0 < x then 1 else 0) + +-- Note this is in the opposite direction as in the Stein's Lemma paper, which uses `≥ₚ` +-- as the default ordering. We offer the `≥ₚ` notation which is the same with the arguments +-- flipped, similar to how `GT.gt` is defeq to `LT.lt` with arguments flipped. +-- We put the ≥ₚ first, since both can delaborate and we want to show the ≤ₚ one. +scoped notation "{" A " ≥ₚ " B "}" => projLE B A +scoped notation "{" A " ≤ₚ " B "}" => projLE A B + +scoped notation "{" A " >ₚ " B "}" => projLT B A +scoped notation "{" A " <ₚ " B "}" => projLT A B + +theorem projLE_def : {A ≤ₚ B} = (B - A).cfc (fun x ↦ if 0 ≤ x then 1 else 0) := by + rfl + +theorem projLT_def : {A <ₚ B} = (B - A).cfc (fun x ↦ if 0 < x then 1 else 0) := by + rfl + +theorem projLE_sq : {A ≤ₚ B}^2 = {A ≤ₚ B} := by + rw [projLE_def, ← cfc_pow, ← cfc_comp] + congr! 2 with x + simp + +theorem projLT_sq : {A <ₚ B}^2 = {A <ₚ B} := by + rw [projLT_def, ← cfc_pow, ← cfc_comp] + congr! 2 with x + simp + +theorem projLE_zero_cfc : {0 ≤ₚ A} = A.cfc (fun x ↦ if 0 ≤ x then 1 else 0) := by + simp only [projLE_def, sub_zero] + +theorem projLT_zero_cfc : {0 <ₚ A} = A.cfc (fun x ↦ if 0 < x then 1 else 0) := by + simp only [projLT_def, sub_zero] + +theorem projLE_zero_cfc' : {A ≤ₚ 0} = A.cfc (fun x ↦ if x ≤ 0 then 1 else 0) := by + simp only [projLE_def, zero_sub] + --TODO: Should do a `HermitianMat.cfc_comp_neg`? + nth_rw 1 [← cfc_id A] + rw [← cfc_neg, ← cfc_comp] + congr! 2 with x + simp + +theorem projLT_zero_cfc' : {A <ₚ 0} = A.cfc (fun x ↦ if x < 0 then 1 else 0) := by + simp only [projLT_def, zero_sub] + --TODO: Should do a `HermitianMat.cfc_comp_neg`? + nth_rw 1 [← cfc_id A] + rw [← cfc_neg, ← cfc_comp] + congr! 2 with x + simp + +theorem projLE_nonneg : 0 ≤ {A ≤ₚ B} := by + rw [projLE_def, cfc_nonneg_iff] + intro i + apply ite_nonneg <;> norm_num + +theorem projLT_nonneg : 0 ≤ {A <ₚ B} := by + rw [projLT_def, cfc_nonneg_iff] + intro i + apply ite_nonneg <;> norm_num + +theorem projLE_le_one : {A ≤ₚ B} ≤ 1 := by + --The whole `rw` line is a defeq, i.e. `change _root_.cfc _ (B - A).mat ≤ 1` works too. + --TODO better API. + open MatrixOrder in + rw [← Subtype.coe_le_coe, val_eq_coe, selfAdjoint.val_one] + apply cfc_le_one (f := fun x ↦ if 0 ≤ x then 1 else 0) + intros; split <;> norm_num + +open MatrixOrder in +theorem projLE_mul_nonneg : 0 ≤ {A ≤ₚ B}.mat * (B - A).mat := by + rw [projLE_def] + nth_rewrite 2 [← cfc_id (B - A)] + rw [← mat_cfc_mul] + apply cfc_nonneg + aesop + +open MatrixOrder in +theorem projLE_mul_le : {A ≤ₚ B}.mat * A.mat ≤ {A ≤ₚ B}.mat * B.mat := by + rw [← sub_nonneg, ← mul_sub_left_distrib] + exact projLE_mul_nonneg A B + +@[simp] +theorem proj_le_add_lt : {A <ₚ B} + {B ≤ₚ A} = 1 := by + rw [projLE_def, projLT_def] + rw [← neg_sub A B] + nth_rw 1 [← cfc_id (A - B)] + rw[← cfc_neg, ← cfc_comp, ← cfc_add] + convert cfc_const (A - B) 1 with x + · simp; grind + · simp + +theorem conj_lt_add_conj_le : A.conj {A <ₚ 0} + A.conj {0 ≤ₚ A} = A := by + rw (occs := [2, 4, 5]) [← cfc_id A] + rw [projLT_zero_cfc', projLE_zero_cfc, cfc_conj, cfc_conj, ← cfc_add] + congr; ext + simp; grind + +/- +The projection onto the support can be split into the projection onto positive +and negative eigenvalues. +-/ +theorem supportProj_eq_proj_lt_add_proj_lt (A : HermitianMat n 𝕜) : + A.supportProj = {A <ₚ 0} + {0 <ₚ A} := by + rw [supportProj_eq_cfc, projLT_zero_cfc, projLT_zero_cfc', ← cfc_add A] + congr 1 + grind only [Pi.add_apply] + +/-- The positive part of a Hermitian matrix: the projection onto its positive eigenvalues. -/ +instance : PosPart (HermitianMat n 𝕜) where + posPart A := A.cfc (fun x ↦ x ⊔ 0) + +/-- The negative part of a Hermitian matrix: the projection onto its negative eigenvalues. -/ +instance : NegPart (HermitianMat n 𝕜) where + negPart A := A.cfc (fun x ↦ -x ⊔ 0) + +theorem posPart_eq_cfc_max : A⁺ = A.cfc (fun x ↦ x ⊔ 0) := by + rfl + +theorem negPart_eq_cfc_min : A⁻ = A.cfc (fun x ↦ -x ⊔ 0) := by + rfl + +theorem posPart_eq_cfc_ite : A⁺ = A.cfc (fun x ↦ if 0 ≤ x then x else 0) := by + simp only [← max_def', posPart_eq_cfc_max] + +theorem negPart_eq_cfc_ite : A⁻ = A.cfc (fun x ↦ if x ≤ 0 then -x else 0) := by + simp only [negPart_eq_cfc_min, max_def] + congr; ext + split <;> split <;> grind + +/-- There is an existing (very slow) `PosPart` instance on `Matrix n n 𝕜`, this shows +that this is equal. -/ +theorem posPart_eq_posPart_toMat : A⁺ = A.mat⁺ := by + rw [CFC.posPart_def, cfcₙ_eq_cfc] + rfl + +/-- There is an existing (very slow) `PosPart` instance on `Matrix n n 𝕜`, this shows +that this is equal. -/ +theorem negPart_eq_negPart_toMat : A⁻ = A.mat⁻ := by + rw [CFC.negPart_def, cfcₙ_eq_cfc] + rfl + +/-- The positive part can be equivalently described as the nonnegative part. -/ +theorem posPart_eq_cfc_lt : A⁺ = A.cfc (fun x ↦ if 0 < x then x else 0) := by + rw [posPart_eq_cfc_ite] + congr with x + rcases lt_trichotomy x 0 <;> grind + +/-- The negative part can be equivalently described as the nonpositive part. -/ +theorem negPart_eq_cfc_lt : A⁻ = A.cfc (fun x ↦ if x < 0 then -x else 0) := by + rw [negPart_eq_cfc_ite] + congr with x + rcases lt_trichotomy x 0 <;> grind + +theorem posPart_add_negPart : A⁺ - A⁻ = A := by + rw [posPart_eq_cfc_ite, negPart_eq_cfc_lt, ← cfc_sub] + convert cfc_id A + ext; dsimp; grind + +theorem posPart_eq_self {A : HermitianMat n 𝕜} (hA : 0 ≤ A) : + A⁺ = A := by + nth_rw 2 [← cfc_id A] + apply cfc_congr_of_nonneg hA + grind [Set.EqOn] + +theorem posPart_nonneg : 0 ≤ A⁺ := by + rw [posPart_eq_cfc_ite, cfc_nonneg_iff] + intro; split <;> order + +theorem negPart_nonneg : 0 ≤ A⁻ := by + rw [negPart_eq_cfc_ite, cfc_nonneg_iff] + intro; split <;> grind + +theorem posPart_le : A ≤ A⁺ := by + nth_rw 1 [← cfc_id A] + rw [posPart_eq_cfc_ite, ← sub_nonneg, ← cfc_sub, cfc_nonneg_iff] + intro; simp; split <;> order + +theorem posPart_mul_negPart : A⁺.mat * A⁻.mat = 0 := by + rw [posPart_eq_cfc_ite, negPart_eq_cfc_ite, ← mat_cfc_mul] + convert congrArg mat (cfc_const A 0) + · grind [Pi.mul_apply, mul_eq_zero] + · simp + +open RealInnerProductSpace + +theorem projLE_inner_nonneg : 0 ≤ ⟪{A ≤ₚ B}, (B - A)⟫ := + --This inner is equal to `(B - A)⁺.trace`, could be better way to describe it + inner_mul_nonneg (projLE_mul_nonneg A B) + +theorem projLE_inner_le : ⟪{A ≤ₚ B}, A⟫ ≤ ⟪{A ≤ₚ B}, B⟫ := by + rw [← sub_nonneg, ← inner_sub_right] + exact projLE_inner_nonneg A B + +open RealInnerProductSpace in +theorem inner_projLE_nonneg : 0 ≤ ⟪{A ≤ₚ B}, (B - A)⟫ := + projLE_inner_nonneg A B + +open RealInnerProductSpace in +theorem inner_projLE_le : ⟪{A ≤ₚ B}, A⟫ ≤ ⟪{A ≤ₚ B}, B⟫ := + projLE_inner_le A B + +--TODO: When we upgrade `cfc_continuous` from 𝕜 to ℂ, we upgrade these too. +@[fun_prop] +theorem posPart_Continuous : Continuous (·⁺ : HermitianMat n ℂ → _) := by + simp_rw [posPart_eq_cfc_max] + fun_prop + +@[fun_prop] +theorem negPart_Continuous : Continuous (·⁻ : HermitianMat n ℂ → _) := by + simp_rw [negPart_eq_cfc_min] + fun_prop + +--Many missing lemmas: see `Mathlib.Algebra.Order.Group.PosPart` for examples +-- (They don't apply here since it's not a Lattice, and there's no well-defined `max` in +-- the Loewner order.) +-- PosPart is Monotone (so `A ≤ B` implies `A⁺ ≤ B⁺`), as is NegPart +-- PosPart and NegPart commute with nonnegative scalar muliptlication +-- `A⁺ ≤ 0 ↔ A⁺ = 0 ↔ A = 0` +-- `0 ≤ A → A⁺ = A` +-- `0 < A → 0 < A⁺` (this is not the PosDef version, this is `≤ && ≠`) +-- `A.PosDef → A⁺.PosDef` +-- versions of those ^^ for negPart +-- simp: 0⁺ = 0, 0⁻ = 0, 1⁺ = 1, 1⁻ = 0 +-- (-A)⁺ = A⁻, (-A)⁻ = A⁺ +-- A⁺⁺ = A⁺, A⁺⁻ = 0 + +-- variable {d : Type*} [Fintype d] [DecidableEq d] (A B : HermitianMat d ℂ) + +theorem one_sub_projLT : 1 - {B ≤ₚ A} = {A <ₚ B} := by + rw [sub_eq_iff_eq_add, proj_le_add_lt] + +open MatrixOrder ComplexOrder in +theorem projLT_mul_nonneg : 0 ≤ {A <ₚ B}.mat * (B - A).mat := by + rw [projLT_def] + nth_rewrite 2 [← cfc_id (B - A)] + rw [← mat_cfc_mul] + apply cfc_nonneg + intros + simp only [Pi.mul_apply, id_eq, ite_mul, one_mul, zero_mul] + split <;> order + +open MatrixOrder ComplexOrder in +theorem proj_lt_mul_lt : {A <ₚ B}.mat * A.mat ≤ {A <ₚ B}.mat * B.mat := by + rw [← sub_nonneg, ← mul_sub_left_distrib] + exact A.projLT_mul_nonneg B + +theorem inner_negPart_nonpos : ⟪A, A⁻⟫ ≤ 0 := by + rw [← neg_le_neg_iff, neg_zero, ← inner_neg_right] + apply inner_mul_nonneg + nth_rw 1 [← A.cfc_id] + rw [negPart_eq_cfc_ite] + rw [← cfc_neg] + rw [← mat_cfc_mul] + change 0 ≤ A.cfc _ + rw [cfc_nonneg_iff] + intro i + dsimp + split_ifs with h + · rw [neg_neg] + exact mul_self_nonneg _ + · simp + +@[simp] +theorem posPart_inner_negPart_zero : ⟪A⁺, A⁻⟫ = 0 := by + have hi := inner_eq_trace_rc A⁺ A⁻ + rw [posPart_mul_negPart, Matrix.trace_zero] at hi + simpa only [map_eq_zero] using hi + +theorem inner_negPart_zero_iff : ⟪A, A⁻⟫ = 0 ↔ 0 ≤ A := by + constructor + · intro h + nth_rw 1 [← posPart_add_negPart A] at h + rw [inner_sub_left, sub_eq_zero, posPart_inner_negPart_zero, eq_comm, inner_self_eq_zero] at h + rw [← zero_smul ℝ 1, ← cfc_const A, negPart_eq_cfc_ite] at h --TODO cfc_zero + rw [cfc_eq_cfc_iff_eqOn, A.H.spectrum_real_eq_range_eigenvalues, Set.eqOn_range] at h + replace h (i) := congrFun h i + simp only [Function.comp_apply, ite_eq_right_iff, neg_eq_zero] at h + rw [zero_le_iff, A.H.posSemidef_iff_eigenvalues_nonneg] + intro i + contrapose! h + use i, h.le, h.ne + · intro h + apply le_antisymm + · exact inner_negPart_nonpos A + · exact inner_ge_zero h (negPart_nonneg A) + +theorem posPart_eq_zero_iff : A⁺ = 0 ↔ A ≤ 0 := by + refine ⟨fun h => by simpa [h] using posPart_le (A := A), fun hA => ?_⟩ + have hnegPart : (-A)⁻ = A⁺ := by + rw [negPart_eq_cfc_ite, posPart_eq_cfc_ite] + nth_rw 1 [← cfc_id A] + rw [← cfc_neg, ← cfc_comp] + congr! 2 with x; simp + have h0 : ⟪-A, A⁺⟫ = 0 := by + simpa [hnegPart] using (inner_negPart_zero_iff (A := -A)).2 (by simpa using hA) + have hA_eq : -A = A⁻ - A⁺ := by + conv_lhs => rw [show A = A⁺ - A⁻ from (posPart_add_negPart A).symm] + abel + have hself : ⟪A⁺, A⁺⟫ = 0 := by + rw [hA_eq, HermitianMat.inner_sub_right, HermitianMat.inner_comm A⁻ A⁺, + posPart_inner_negPart_zero, zero_sub, neg_eq_zero] at h0 + exact h0 + exact inner_self_eq_zero.mp hself + +theorem inner_negPart_neg_iff : ⟪A, A⁻⟫ < 0 ↔ ¬0 ≤ A := by + simp [← inner_negPart_zero_iff, lt_iff_le_and_ne, inner_negPart_nonpos A] + +/-- The self-duality of the PSD cone: a matrix is PSD iff its inner product with all +nonnegative matrices is non-negative. -/ +theorem nonneg_iff_inner_nonneg (A : HermitianMat n 𝕜) : + 0 ≤ A ↔ ∀ B, 0 ≤ B → 0 ≤ ⟪A, B⟫ := by + use fun h _ ↦ inner_ge_zero h + intro h + contrapose! h + classical + use A⁻, negPart_nonneg A + rwa [inner_negPart_neg_iff] diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean new file mode 100644 index 0000000000..a28d6d255e --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean @@ -0,0 +1,112 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv + +/-! +Much like `Matrix.reindex` and `Matrix.submatrix`, we can reindex a Hermitian matrix to get another +Hermitian matrix; however, this only makes sense when both permutations are the same, accordingly, +`HermitianMat.reindex` only takes one `Equiv` argument (as opposed to `Matrix.reindex`'s two). + +This file then gives relevant lemmas for simplifying this. +-/ + +@[expose] public section +namespace HermitianMat + +variable {d d₂ d₃ d₄ 𝕜 : Type*} [RCLike 𝕜] + +variable (A B : HermitianMat d 𝕜) (e : d ≃ d₂) + +def reindex (e : d ≃ d₂) : HermitianMat d₂ 𝕜 := + ⟨A.mat.reindex e e, A.H.submatrix e.symm⟩ + +@[simp] +theorem mat_reindex : (A.reindex e).mat = A.mat.reindex e e := by + rfl + +/-! Our simp-normal form for expressions involving `HermitianMat.reindex` is that we try to push +the reindexing as far out as possible, so that it can be absorbed by `HermitianMat.trace`, or +cancelled our in a `HermitianMat.inner`. In places where it commutes (like `HermitianMat.inner`) +we push it to the right side. One downside is that we're not as likely to hit `reindex_one`. -/ + +@[simp] +theorem reindex_refl (A : HermitianMat d 𝕜) : + A.reindex (.refl _) = A := by + rfl + +@[simp] +theorem reindex_reindex (A : HermitianMat d 𝕜) (e : d ≃ d₂) (f : d₂ ≃ d₃) : + (A.reindex e).reindex f = A.reindex (e.trans f) := by + ext1; simp + +@[simp] +theorem reindex_zero : (0 : HermitianMat d 𝕜).reindex e = 0 := by + ext1; simp + +@[simp] +theorem reindex_one [DecidableEq d] [DecidableEq d₂] : + (1 : HermitianMat d 𝕜).reindex e = 1 := by + ext1 + simp [reindex] + +@[simp] +theorem reindex_add : A.reindex e + B.reindex e = (A + B).reindex e := by + ext1; simp [Matrix.submatrix_add] + +@[simp] +theorem reindex_sub : A.reindex e - B.reindex e = (A - B).reindex e := by + ext1; simp [Matrix.submatrix_sub] + +@[simp] +theorem reindex_neg : (-A).reindex e = -(A.reindex e) := by + ext1; simp [Matrix.submatrix_neg] + +@[simp] +theorem reindex_smul (c : ℝ) : (c • A).reindex e = c • (A.reindex e) := by + ext1; simp [Matrix.submatrix_smul] + +@[simp] +theorem reindex_conj [Fintype d₂] [Fintype d] (B : Matrix d₃ d₂ 𝕜) : + (A.reindex e).conj B = A.conj (B.submatrix id e) := by + ext1 + simp only [conj_apply, mat_reindex, Matrix.reindex_apply, mat_mk] + rw [← Matrix.submatrix_id_mul_right, Matrix.mul_assoc] + rw [← Matrix.submatrix_id_mul_left, ← Matrix.mul_assoc] + simp + +variable [Fintype d] + +theorem conj_submatrix (B : Matrix d₂ d₄ 𝕜) (e : d₃ ≃ d₂) (f : d → d₄) : + A.conj (B.submatrix e f) = (A.conj (B.submatrix id f)).reindex e.symm := by + ext1 + simp [conj_apply, ← Matrix.submatrix_mul_equiv (e₂ := .refl d)] + +theorem reindex_eq_conj [DecidableEq d] (e : d ≃ d₂) : + A.reindex e = A.conj (Matrix.reindex e (.refl d) 1) := by + ext : 3 + simp [-mat_apply, reindex, conj_apply, Matrix.submatrix, + Matrix.mul_apply, Matrix.one_apply] + +variable [Fintype d₂] [DecidableEq d] [DecidableEq d₂] + +theorem ker_reindex : + (A.reindex e).ker = A.ker.comap (LinearEquiv.euclideanOfRelabel 𝕜 e).toLinearMap := by + dsimp only [reindex, ker, lin] + simp only [mat_mk] + rw [Matrix.reindex_toEuclideanLin, LinearEquiv.ker_comp, LinearMap.ker_comp] + +@[simp] +theorem ker_reindex_le_iff : + (A.reindex e).ker ≤ (B.reindex e).ker ↔ A.ker ≤ B.ker := by + rw [ker_reindex, ker_reindex] + apply Submodule.comap_le_comap_iff_of_surjective + exact LinearEquiv.surjective (LinearEquiv.euclideanOfRelabel 𝕜 e) + +end HermitianMat diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean new file mode 100644 index 0000000000..d58231f5cf --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean @@ -0,0 +1,249 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex + +/-! # Trace of Hermitian Matrices + +While the trace of a Hermitian matrix is, in informal math, typically just "the same as" a trace of +a matrix that happens to be Hermitian - it is a real number, not a complex number. Or more generally, +it is a self-adjoint element of the base `StarAddMonoid`. + +Working directly with `Matrix.trace` then means that there would be constant casts between rings, +chasing imaginary parts and inequalities and so on. By defining `HermitianMat.trace` as its own +operation, we encapsulate the mess and give a clean interface. + +The `IsMaximalSelfAdjoint` class is used so that (for example) for matrices over ℤ or ℝ, +`HermitianMat.trace` works as well and is in fact defeq to `Matrix.trace`. For ℂ or `RCLike`, +it uses the real part. +-/ + +@[expose] public section + +namespace HermitianMat + +variable {R n m α : Type*} [Star R] [TrivialStar R] [Fintype n] [Fintype m] + +section star +variable [AddGroup α] [StarAddMonoid α] [CommSemiring R] [Semiring α] [Algebra R α] [IsMaximalSelfAdjoint R α] + +/-- The trace of the matrix. This requires a `IsMaximalSelfAdjoint R α` instance, and then maps from + `HermitianMat n α` to `R`. This means that the trace of (say) a `HermitianMat n ℤ` gives values in ℤ, + but that the trace of a `HermitianMat n ℂ` gives values in ℝ. The fact that traces are "automatically" + real reduces coercions down the line. -/ +def trace (A : HermitianMat n α) : R := + IsMaximalSelfAdjoint.selfadjMap (A.mat.trace) + +/-- `HermitianMat.trace` reduces to `Matrix.trace` in the algebra.-/ +theorem trace_eq_trace (A : HermitianMat n α) : algebraMap R α A.trace = Matrix.trace A.mat := by + rw [trace, Matrix.trace, map_sum, map_sum] + congr! 1 + exact IsMaximalSelfAdjoint.selfadj_algebra (Matrix.IsHermitian.apply A.H _ _) + +variable [StarModule R α] in +@[simp] +theorem trace_smul (A : HermitianMat n α) (r : R) : (r • A).trace = r * A.trace := by + simp [trace, IsMaximalSelfAdjoint.selfadj_smul] + +end star +section semiring +variable [CommSemiring R] [Ring α] [StarAddMonoid α] [Algebra R α] [IsMaximalSelfAdjoint R α] + +@[simp] +theorem trace_zero : (0 : HermitianMat n α).trace = 0 := by + simp [trace] + +@[simp] +theorem trace_add (A B : HermitianMat n α) : (A + B).trace = A.trace + B.trace := by + simp [trace] + +end semiring +section ring + +variable [CommRing R] [Ring α] [StarAddMonoid α] [Algebra R α] [IsMaximalSelfAdjoint R α] +@[simp] +theorem trace_neg (A : HermitianMat n α) : (-A).trace = -A.trace := by + simp [trace] + +@[simp] +theorem trace_sub (A B : HermitianMat n α) : (A - B).trace = A.trace - B.trace := by + simp [trace] + +end ring +section starring + +variable [CommRing R] [CommRing α] [StarRing α] [Algebra R α] [IsMaximalSelfAdjoint R α] + +--Move somewhere else? Needs to import `IsMaximalSelfAdjoint`, so maybe just here. +theorem _root_.Matrix.IsHermitian.isSelfAdjoint_trace {A : Matrix n n α} (hA : A.IsHermitian) : + IsSelfAdjoint A.trace := by + simp [Matrix.trace, IsSelfAdjoint, ← Matrix.star_apply, show star A = A from hA] + +variable (A : HermitianMat m α) (B : HermitianMat n α) + +@[simp] +theorem trace_kronecker [FaithfulSMul R α] : (A ⊗ₖ B).trace = A.trace * B.trace := by + apply FaithfulSMul.algebraMap_injective R α + simp only [trace, kronecker_mat] + rw [Matrix.trace_kronecker A.mat B.mat] + simp only [map_mul] + have hA := A.H.isSelfAdjoint_trace + have hB := B.H.isSelfAdjoint_trace + open IsMaximalSelfAdjoint in + rw [selfadj_algebra hA, selfadj_algebra hB, selfadj_algebra (hA.mul hB)] + +end starring + +section trivialstar + +variable [Star α] [TrivialStar α] [CommSemiring α] + +/-- `HermitianMat.trace` reduces to `Matrix.trace` when the elements are a `TrivialStar`. -/ +@[simp] +theorem trace_eq_trace_trivial (A : HermitianMat n ℝ) : A.trace = A.mat.trace := by + rw [← trace_eq_trace] + rfl + +end trivialstar + +section RCLike + +variable {n m 𝕜 : Type*} [Fintype n] [Fintype m] [RCLike 𝕜] + +theorem trace_eq_re_trace (A : HermitianMat n 𝕜) : A.trace = RCLike.re A.mat.trace := by + rfl + +@[simp] +theorem trace_one [DecidableEq n] : (1 : HermitianMat n 𝕜).trace = Fintype.card n := by + simp [trace_eq_re_trace] + +/-- `HermitianMat.trace` reduces to `Matrix.trace` when the elements are `RCLike`. -/ +@[simp] +theorem trace_eq_trace_rc (A : HermitianMat n 𝕜) : A.trace = A.mat.trace := by + rw [trace, Matrix.trace, map_sum, RCLike.ofReal_sum] + congr 1 + exact Matrix.IsHermitian.coe_re_diag A.H + +theorem trace_diagonal {T : Type*} [Fintype T] [DecidableEq T] (f : T → ℝ) : + (diagonal 𝕜 f).trace = ∑ i, f i := by + rw [trace_eq_re_trace] + simp [HermitianMat.diagonal, Matrix.trace] + +theorem sum_eigenvalues_eq_trace [DecidableEq n] (A : HermitianMat n 𝕜) : + ∑ i, A.H.eigenvalues i = A.trace := by + convert! congrArg RCLike.re A.H.sum_eigenvalues_eq_trace + rw [RCLike.ofReal_re] + +--Proving that traces are 0 or 1 is common enough that we have a convenience lemma here for turning +--statements about HermitianMat traces into Matrix traces. +theorem trace_eq_zero_iff (A : HermitianMat n 𝕜) : A.trace = 0 ↔ A.mat.trace = 0 := by + rw [← trace_eq_trace_rc] + exact ⟨mod_cast id, mod_cast id⟩ + +theorem trace_eq_one_iff (A : HermitianMat n 𝕜) : A.trace = 1 ↔ A.mat.trace = 1 := by + rw [← trace_eq_trace_rc] + exact ⟨mod_cast id, mod_cast id⟩ + +@[simp] +theorem trace_reindex (A : HermitianMat n ℂ) (e : n ≃ m) : + (A.reindex e).trace = A.trace := by + simp [reindex, trace_eq_re_trace] + +end RCLike +section partialTrace +section addCommGroup + +variable [AddCommGroup α] [StarAddMonoid α] +omit [Fintype n] + +def traceLeft (A : HermitianMat (m × n) α) : HermitianMat n α := + ⟨A.mat.traceLeft, A.H.traceLeft⟩ + +def traceRight (A : HermitianMat (m × n) α) : HermitianMat m α := + ⟨A.mat.traceRight, A.H.traceRight⟩ + +variable (A B : HermitianMat (m × n) α) + +@[simp] +theorem traceLeft_mat : A.traceLeft.mat = A.mat.traceLeft := by + rfl + +@[simp] +theorem traceLeft_add : (A + B).traceLeft = A.traceLeft + B.traceLeft := by + ext1; simp + +@[simp] +theorem traceLeft_neg : (-A).traceLeft = -A.traceLeft := by + ext1; simp + +@[simp] +theorem traceLeft_sub : (A - B).traceLeft = A.traceLeft - B.traceLeft := by + ext1; simp + +variable (A B : HermitianMat (n × m) α) + +@[simp] +theorem traceRight_mat : + (traceRight A).mat = A.mat.traceRight := by + rfl + +@[simp] +theorem traceRight_add : (A + B).traceRight = A.traceRight + B.traceRight := by + ext1; simp + +@[simp] +theorem traceRight_neg : (-A).traceRight = -A.traceRight := by + ext1; simp + +@[simp] +theorem traceRight_sub : (A - B).traceRight = A.traceRight - B.traceRight := by + ext1; simp + +end addCommGroup +section rcLike + +variable {𝕜} [RCLike 𝕜] +variable (A : HermitianMat (m × n) 𝕜) + +omit [Fintype n] in +@[simp] +theorem traceLeft_smul (r : ℝ) : (r • A).traceLeft = r • A.traceLeft := by + ext1; simp + +omit [Fintype m] in +@[simp] +theorem traceRight_smul (r : ℝ) : (r • A).traceRight = r • A.traceRight := by + ext1; simp + +@[simp] +theorem traceLeft_trace : A.traceLeft.trace = A.trace := by + simp [trace_eq_re_trace] + +@[simp] +theorem traceRight_trace : A.traceRight.trace = A.trace := by + simp [trace_eq_re_trace] + +end rcLike +section kron + +variable {m n 𝕜 : Type*} [RCLike 𝕜] +variable (A : HermitianMat m 𝕜) (B : HermitianMat n 𝕜) + +@[simp] +theorem traceLeft_kron [Fintype m] : (A ⊗ₖ B).traceLeft = A.trace • B := by + ext : 2 + simp only [HermitianMat.traceLeft, Matrix.traceLeft, kronecker_mat, mat_mk] + simp [Matrix.trace, RCLike.real_smul_eq_coe_mul, ← Finset.sum_mul] + +@[simp] +theorem traceRight_kron [Fintype n] : (A ⊗ₖ B).traceRight = B.trace • A := by + ext : 2 + simp only [HermitianMat.traceRight, Matrix.traceRight, kronecker_mat, mat_mk] + simp [Matrix.trace, RCLike.real_smul_eq_coe_mul, ← Finset.mul_sum, mul_comm] + +end kron +end partialTrace diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/IsMaximalSelfAdjoint.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/IsMaximalSelfAdjoint.lean new file mode 100644 index 0000000000..0d0f01cf28 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/IsMaximalSelfAdjoint.lean @@ -0,0 +1,64 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Analysis.Matrix.Normed + +/-! +# Maximal self-adjoint subrings + +This file introduces the `IsMaximalSelfAdjoint R α` typeclass, which records that the +`TrivialStar` ring `R` carries the self-adjoint part of a star ring `α`. It bundles an +additive, `R`-linear map `selfadjMap : α →+ R` that inverts `algebraMap R α` on self-adjoint +elements. The guiding example is `R = ℝ`, `α = ℂ`: it lets a quantity such as the trace of a +Hermitian matrix be valued in `ℝ` instead of `ℂ`, reflecting that physical observables are +self-adjoint and take real expectation values. +-/ + +@[expose] public section + +/-- `IsMaximalSelfAdjoint R α` witnesses that `R` is the maximal `TrivialStar` subring of the +star ring `α`, via an additive map `selfadjMap : α →+ R` collecting the self-adjoint part of +each element. This lets `HermitianMat.trace` return `𝕜` when `𝕜` already has a trivial star, +and the "clean" underlying type otherwise, e.g. `ℝ` when the input field is `ℂ`. -/ +class IsMaximalSelfAdjoint (R : outParam Type*) (α : Type*) [Star α] [Star R] [CommSemiring R] + [Semiring α] [TrivialStar R] [Algebra R α] where + /-- The additive map sending an element of `α` to its self-adjoint part in `R`. -/ + selfadjMap : α →+ R + /-- `selfadjMap` pulls scalar multiplication by `R` out of its argument. -/ + selfadj_smul : ∀ (r : R) (a : α), selfadjMap (r • a) = r * (selfadjMap a) + /-- On self-adjoint elements, `selfadjMap` is a section of `algebraMap R α`. -/ + selfadj_algebra : ∀ {a : α}, IsSelfAdjoint a → algebraMap _ _ (selfadjMap a) = a + +/-- Every `TrivialStar` `CommSemiring` is its own maximal self adjoints. -/ +instance instTrivialStarIsMaximalSelfAdjoint {R} [Star R] [TrivialStar R] [CommSemiring R] : + IsMaximalSelfAdjoint R R where + selfadjMap := AddMonoidHom.id R + selfadj_smul _ __ := rfl + selfadj_algebra {_} _ := rfl + +/-- ℝ is the maximal self adjoint elements over RCLike -/ +instance instRCLikeIsMaximalSelfAdjoint {α} [RCLike α] : IsMaximalSelfAdjoint ℝ α where + selfadjMap := RCLike.re + selfadj_smul := RCLike.smul_re + selfadj_algebra := RCLike.conj_eq_iff_re.mp + +namespace IsMaximalSelfAdjoint + +-- In particular instances we care about, simplify selfadjMap should it appear. +-- It _seems_ like `selfadjMap 1 = 1`, always, but I can't find a proof. But these lemmas +-- take care of proving that anyway. + +@[simp] +theorem trivial_selfadjMap {R} [Star R] [TrivialStar R] [CommSemiring R] : + (selfadjMap : R →+ R) = .id R := by + rfl + +@[simp] +theorem RCLike_selfadjMap {α} [RCLike α] : (selfadjMap : α →+ ℝ) = RCLike.re := by + rfl + +end IsMaximalSelfAdjoint diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean new file mode 100644 index 0000000000..7eb5ec1418 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean @@ -0,0 +1,645 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Analysis.InnerProductSpace.JointEigenspace +public import Mathlib.LinearAlgebra.Matrix.Permutation +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix + +@[expose] public section + +open scoped Matrix + +variable {d d₂ d₃ R : Type*} +variable [Fintype d] [DecidableEq d] [Fintype d₂] [DecidableEq d₂] [Fintype d₃] [DecidableEq d₃] + +variable [CommRing R] [StarRing R] + +variable {𝕜 : Type*} [RCLike 𝕜] {A B : Matrix d d 𝕜} + +/-- An isometry is a matrix `A` such that `AAᴴ = 1`. Compare with a unitary, which +requires `AAᴴ = AᴴA = 1`. It is common to claim that, in a finite-dimensional vector +space, a two-sided isometry (`A.Isometry ∧ Aᴴ.Isometry`) must be square and therefore unitary; +this is does not work out so well here, since a `Matrix m n R` can be a two-sided isometry, +but cannot be a `unitary` since the rows and columns are index by different labels. -/ +def Matrix.Isometry (A : Matrix d d₂ R) : Prop := + Aᴴ * A = 1 + +omit [Fintype d₃] [DecidableEq d₂] in +theorem Matrix.submatrix_one_isometry {e : d₂ → d} {f : d₃ → d} (he : e.Bijective) (hf : f.Injective) : + (submatrix (α := R) 1 e f).Isometry := by + -- Since $e$ is injective and $f$ is bijective, the submatrix of the identity matrix formed by $e$ and $f$ is a permutation matrix. + have h_perm : ∀ i j, (Matrix.submatrix (1 : Matrix d d R) e f) i j = if e i = f j then 1 else 0 := by + -- By definition of the identity matrix, the entry (i, j) in the submatrix is 1 if e i = f j and 0 otherwise. + simp [Matrix.submatrix, Matrix.one_apply] + ext i j + -- Since $e$ is injective and $f$ is bijective, the product $A * Aᴴ$ will have 1s on the diagonal and 0s elsewhere, which is the identity matrix. + change ∑ k, (Matrix.conjTranspose (Matrix.submatrix (1 : Matrix d d R) e f)) i k * + (Matrix.submatrix (1 : Matrix d d R) e f) k j = if i = j then 1 else 0 + simp_all only [Multiset.bijective_iff_map_univ_eq_univ, submatrix_apply, conjTranspose_apply, one_apply] + symm; split <;> symm + next h => + subst h + simp_all only [implies_true, mul_ite, ↓reduceIte, star_one, mul_one, star_zero, mul_zero, + Finset.sum_boole] + have h_unique : ∀ i, ∃! x, e x = f i := by + intro i + obtain ⟨x, hx⟩ : ∃ x, e x = f i := by + replace he := congr_arg Multiset.toFinset he; rw [Finset.ext_iff] at he; specialize he ( f i ) ; aesop; + use x + simp_all only [true_and] + intro y a + have := Fintype.bijective_iff_injective_and_card e + aesop + obtain ⟨ x, hx ⟩ := h_unique i; + rw [show ( Finset.univ.filter fun y => e y = f i ) = { x } from Finset.eq_singleton_iff_unique_mem.2 ⟨ by aesop, fun y hy => hx.2 y <| Eq.symm <| Finset.mem_filter.1 hy |>.2.symm ⟩] ; simp ; + next h => -- Since $e$ is injective and $e i \neq e j$, there is no $x$ such that $e i = f x$ and $e j = f x$. + have h_no_x : ∀ x : d₂, ¬(e x = f i ∧ e x = f j) := by + exact fun x hx => h ( hf ( hx.1.symm.trans hx.2 ) ); + exact Finset.sum_eq_zero fun x hx => by specialize h_no_x x; aesop + +omit [DecidableEq d₂] in +theorem Matrix.submatrix_one_id_left_isometry {e : d₂ → d} (he : e.Bijective) : + (submatrix (1 : Matrix d d R) e id).Isometry := + submatrix_one_isometry he Function.injective_id + +omit [Fintype d₂] in +theorem Matrix.submatrix_one_id_right_isometry {e : d₂ → d} (he : e.Injective) : + (submatrix (1 : Matrix d d R) id e).Isometry := + submatrix_one_isometry Function.bijective_id he + +theorem Matrix.mem_unitaryGroup_iff_isometry (A : Matrix d d R) : + A ∈ unitaryGroup d R ↔ A.Isometry ∧ Aᴴ.Isometry := by + rw [Isometry, Isometry, conjTranspose_conjTranspose] + rfl + +theorem Equiv.Perm.permMatrix_mem_unitaryGroup (e : Perm d) : + e.permMatrix R ∈ Matrix.unitaryGroup d R := by + -- Since $e$ is a permutation, its permutation matrix $P_e$ is orthogonal, meaning $P_e * P_e^T = I$. + have h_perm_ortho : (Equiv.Perm.permMatrix R e) * (Equiv.Perm.permMatrix R e)ᵀ = 1 := by + ext i j; rw [Matrix.mul_apply] ; aesop; + constructor + · simp_all only [Matrix.transpose_permMatrix] + -- Since the conjugate transpose of a permutation matrix is the permutation matrix of the inverse permutation, we have: + have h_conj_transpose : star (Equiv.Perm.permMatrix R e) = (Equiv.Perm.permMatrix R e)ᵀ := by + ext i j; simp [Equiv.Perm.permMatrix] ; aesop; + simp_all [mul_eq_one_comm] + · simp_all only [Matrix.transpose_permMatrix] + convert! h_perm_ortho using 2; + simp [Matrix.star_eq_conjTranspose, Equiv.Perm.permMatrix] + +omit [Fintype d₃] [DecidableEq d₂] in +theorem Matrix.reindex_one_isometry (e : d ≃ d₂) (f : d ≃ d₃) : + (reindex (α := R) e f 1).Isometry := by + -- Since $e$ and $f$ are bijections, the reindexing of the identity matrix by $e$ and $f$ is a permutation matrix, which is unitary. + have h_perm : ∀ (e : d ≃ d₂) (f : d ≃ d₃), (Matrix.reindex e f (1 : Matrix d d R)).Isometry := by + intro e f + simp [Matrix.Isometry] + exact h_perm e f + +omit [Fintype d] in +theorem Matrix.reindex_one_mem_unitaryGroup (e : d ≃ d₂) : + reindex (α := R) e e 1 ∈ unitaryGroup d₂ R := by + -- The reindex of the identity matrix under an equivalence e is just the identity matrix on d₂. + have h_reindex_id : Matrix.reindex e e (1 : Matrix d d R) = 1 := by + -- By definition of reindex, the entry at (i, j) in the reindexed matrix is 1 if i = j and 0 otherwise. + ext i j + simp [Matrix.reindex, Matrix.one_apply] + simp only [h_reindex_id, one_mem] + +omit [Fintype d₂] [DecidableEq d₂] [StarRing R] in +theorem Matrix.reindex_eq_conj (A : Matrix d d R) (e : d ≃ d₂) : reindex e e A = + (reindex (α := R) e (.refl d) 1) * A * (reindex (α := R) (.refl d) e 1) := by + ext i j + simp only [Matrix.mul_apply, Matrix.reindex] + simp [Matrix.one_apply] + +theorem Matrix.reindex_eq_conj_unitaryGroup' (A : Matrix d d R) (e : Equiv.Perm d) : reindex e e A = + (⟨_, e⁻¹.permMatrix_mem_unitaryGroup⟩ : unitaryGroup d R) * A * (⟨_, e.permMatrix_mem_unitaryGroup⟩ : unitaryGroup d R) := by + ext i j; + simp [Matrix.mul_apply] + rw [Finset.sum_eq_single ( e.symm j )] <;> aesop + +theorem Matrix.IsHermitian.eigenvalue_ext (hA : A.IsHermitian) + (h : ∀ (v : d → 𝕜) (lam : 𝕜), A *ᵥ v = lam • v → B *ᵥ v = lam • v) : + A = B := by + -- Since A is Hermitian, it is diagonalizable, and its eigenvectors form a complete basis. Therefore, for any vector v, we have Av = Bv. + have h_diag : ∀ v : d → 𝕜, (A *ᵥ v) = (B *ᵥ v) := by + -- Since A is Hermitian, it is diagonalizable, and its eigenvectors form a complete basis. Therefore, for any vector v, we can express it as a linear combination of eigenvectors. + have h_diag : ∀ v : d → 𝕜, ∃ (c : d → 𝕜) (lam : d → 𝕜), v = ∑ i, c i • (Matrix.IsHermitian.eigenvectorBasis hA i) ∧ ∀ i, A *ᵥ (Matrix.IsHermitian.eigenvectorBasis hA i) = lam i • (Matrix.IsHermitian.eigenvectorBasis hA i) := by + intro v + obtain ⟨c, hc⟩ : ∃ c : d → 𝕜, v = ∑ i, c i • (hA.eigenvectorBasis i) := by + have h_diag : ∀ v : EuclideanSpace 𝕜 d, ∃ c : d → 𝕜, v = ∑ i, c i • (hA.eigenvectorBasis i) := by + intro v + set c := fun i => innerₛₗ 𝕜 (hA.eigenvectorBasis i) v + have hv : v = ∑ i, c i • (hA.eigenvectorBasis i) := by + exact Eq.symm (OrthonormalBasis.sum_repr' hA.eigenvectorBasis v) + use c; + obtain ⟨c, hc⟩ := h_diag (WithLp.toLp 2 v) + use c + exact congr(WithLp.ofLp $hc) + refine' ⟨ c, fun i => ( hA.eigenvalues i ), hc, fun i => _ ⟩; + convert hA.mulVec_eigenvectorBasis i; + ext + simp only [smul_eq_mul, Pi.smul_apply] + symm + exact RCLike.real_smul_eq_coe_mul (hA.eigenvalues i) _ + -- By linearity of A and B, we can distribute them over the sum. + intros v + obtain ⟨c, lam, hv, hlam⟩ := h_diag v + have hA_v : A *ᵥ v = ∑ i, c i • lam i • (hA.eigenvectorBasis i) := by + -- By linearity of matrix multiplication, we can distribute A over the sum. + have hA_v : A *ᵥ (∑ i, c i • (hA.eigenvectorBasis i)) = ∑ i, c i • A *ᵥ (hA.eigenvectorBasis i) := by + simp [funext_iff] + simp [Matrix.mulVec, dotProduct, Finset.mul_sum _ _ _] + exact fun _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ); + aesop + have hB_v : B *ᵥ v = ∑ i, c i • lam i • (hA.eigenvectorBasis i) := by + have hBv : B *ᵥ v = ∑ i, c i • (B *ᵥ (hA.eigenvectorBasis i)) := by + -- By linearity of matrix multiplication, we can distribute $B$ over the sum. + have hBv : B *ᵥ v = B *ᵥ (∑ i, c i • (hA.eigenvectorBasis i)) := by + rw [hv] + simp only [WithLp.ofLp_sum, WithLp.ofLp_smul] + simp [hBv, funext_iff] + simp [Matrix.mulVec, dotProduct, Finset.mul_sum _ _ _] + exact fun _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ); + refine hBv.trans ?_ + conv => enter [1, 2, i]; rw [h _ _ ( hlam i )] + simp only [WithLp.ofLp_sum, WithLp.ofLp_smul] + rw [hA_v, hB_v] + -- By the definition of matrix equality, if $A * v = B * v$ for all $v$, then $A = B$. + apply Matrix.ext; intro i j; exact (by + simpa using congr_fun ( h_diag ( Pi.single j 1 ) ) i) + +/-- Generalizes `Matrix.IsHermitian.cfc.eq_1`, which gives a definition for the matrix CFC in terms of +`Matrix.IsHermitian.eigenvalues` and `Matrix.IsHermitian.eigenvectorUnitary`, to show that the CFC works +similarly for _any_ diagonalization by a two-sided isometry. +-/ +theorem Matrix.IsHermitian.cfc_eq_any_isometry {n m 𝕜 : Type*} [RCLike 𝕜] + [Fintype n] [DecidableEq n] [Fintype m] [DecidableEq m] + {A : Matrix n n 𝕜} (hA : A.IsHermitian) {U : Matrix n m 𝕜} + (hU₁ : U * Uᴴ = 1) (hU₂ : Uᴴ * U = 1) {D : m → ℝ} + (hUD : A = (U * diagonal (RCLike.ofReal ∘ D) : Matrix _ _ _) * Uᴴ) (f : ℝ → ℝ) : + hA.cfc f = (U * diagonal (RCLike.ofReal ∘ f ∘ D) : Matrix _ _ _) * Uᴴ := by + --Thanks Aristotle + rw [Matrix.IsHermitian.cfc] + have hUV := hA.spectral_theorem + set V := hA.eigenvectorUnitary with hV; clear_value V + set D2 := hA.eigenvalues with hD; clear_value D2 + rcases V with ⟨V, hV₁, hV₂⟩ + simp only [Unitary.conjStarAlgAut_apply] at hUV ⊢ + clear hV hD + subst A; clear hA + have h_diag_eq : diagonal (RCLike.ofReal ∘ D) * (Uᴴ * V) = (Uᴴ * V) * diagonal (RCLike.ofReal ∘ D2) := by + have h_mul : (Uᴴ * U * diagonal (RCLike.ofReal ∘ D) * Uᴴ : Matrix m n 𝕜) * V = Uᴴ * V * (diagonal (RCLike.ofReal ∘ D2) * star V * V) := by + simp only [Matrix.mul_assoc, hUV] + simp_all [ Matrix.mul_assoc ]; + have h_diag_eq_f : diagonal (RCLike.ofReal ∘ f ∘ D) * (Uᴴ * V) = (Uᴴ * V) * diagonal (RCLike.ofReal ∘ f ∘ D2) := by + ext i j + simp_all only [diagonal_mul, Function.comp_apply, mul_diagonal] + replace h_diag_eq := congr_fun ( congr_fun h_diag_eq i ) j + by_cases hi : D i = D2 j <;> simp_all [ mul_comm ] ; + have h_final : U * diagonal (RCLike.ofReal ∘ f ∘ D) * Uᴴ * V = V * diagonal (RCLike.ofReal ∘ f ∘ D2) := by + have h_final : U * diagonal (RCLike.ofReal ∘ f ∘ D) * (Uᴴ * V) = U * (Uᴴ * V) * diagonal (RCLike.ofReal ∘ f ∘ D2) := by + rw [ Matrix.mul_assoc, h_diag_eq_f, Matrix.mul_assoc ]; + rw [ Matrix.mul_assoc, Matrix.mul_assoc ]; + simp_all +decide [ ← Matrix.mul_assoc ]; + rw [ ← h_final, Matrix.mul_assoc ]; + rw [hV₂, mul_one ] + +/-- Generalizes `Matrix.IsHermitian.cfc.eq_1`, which gives a definition for the matrix CFC in terms of +`Matrix.IsHermitian.eigenvalues` and `Matrix.IsHermitian.eigenvectorUnitary`, to show that the CFC works +similarly for _any_ diagonalization. +-/ +theorem Matrix.IsHermitian.cfc_eq_any_unitary {n 𝕜 : Type*} [RCLike 𝕜] [Fintype n] [DecidableEq n] + {A : Matrix n n 𝕜} (hA : A.IsHermitian) {U : unitaryGroup n 𝕜} {D : n → ℝ} + (hUD : A = U.val * diagonal (RCLike.ofReal ∘ D) * star U.val) (f : ℝ → ℝ) : + hA.cfc f = U.val * diagonal (RCLike.ofReal ∘ f ∘ D) * star U.val := + Matrix.IsHermitian.cfc_eq_any_isometry hA U.2.2 U.2.1 hUD f + +private theorem Matrix.cfc_conj_isometry' (hA : A.IsHermitian) (f : ℝ → ℝ) {u : Matrix d₂ d 𝕜} + (hu₁ : u.Isometry) (hu₂ : uᴴ.Isometry) : + cfc f (u * A * uᴴ) = u * (cfc f A) * uᴴ := by + let D := hA.eigenvalues + let U' := u * hA.eigenvectorUnitary.val + have := IsHermitian.cfc_eq_any_isometry + (A := u * A * uᴴ) (D := D) (n := d₂) (m := d) (U := U') ?_ ?_ ?_ ?_ f; rotate_left + · simpa using isHermitian_conjTranspose_mul_mul uᴴ hA + · dsimp [U'] + rw [conjTranspose_mul, Matrix.mul_assoc] + nth_rw 2 [← Matrix.mul_assoc] + rw [show _ * _ᴴ = 1 from hA.eigenvectorUnitary.2.2, Matrix.one_mul] + simpa [Isometry] using hu₂ + · dsimp [U'] + rw [conjTranspose_mul, Matrix.mul_assoc] + nth_rw 2 [← Matrix.mul_assoc] + rw [hu₁, Matrix.one_mul] + exact hA.eigenvectorUnitary.2.1 + · rw [hA.spectral_theorem] + simp [U', Matrix.mul_assoc] + rfl + rw [Matrix.IsHermitian.cfc_eq, this] + rw [hA.cfc_eq, Matrix.IsHermitian.cfc.eq_1] + simp only [Unitary.conjStarAlgAut_apply] + simp only [Matrix.mul_assoc, conjTranspose_mul, star_eq_conjTranspose, U', D] + exact isHermitian_mul_mul_conjTranspose _ hA + +theorem Matrix.cfc_conj_isometry (f : ℝ → ℝ) {u : Matrix d₂ d 𝕜} + (hu₁ : u.Isometry) (hu₂ : uᴴ.Isometry) : + cfc f (u * A * uᴴ) = u * (cfc f A) * uᴴ := by + by_cases hA : A.IsHermitian + · exact cfc_conj_isometry' hA f hu₁ hu₂ + rw [cfc_apply_of_not_predicate, cfc_apply_of_not_predicate] + · simp + · exact hA + · contrapose! hA + convert isHermitian_conjTranspose_mul_mul u hA + have hu₃ : uᴴ * u = 1 := by simpa [Isometry] using hu₁ + simp only [Matrix.mul_assoc, hu₃] + simp [← Matrix.mul_assoc, hu₃] + +theorem Matrix.cfc_conj_unitary (f : ℝ → ℝ) (u : unitaryGroup d 𝕜) : + cfc f (u * A * u⁻¹) = u * (cfc f A) * u⁻¹ := by + have hu := u.prop + rw [mem_unitaryGroup_iff_isometry] at hu + exact Matrix.cfc_conj_isometry f hu.left hu.right + +theorem Matrix.cfc_conj_unitary' (f : ℝ → ℝ) (u : unitaryGroup d 𝕜) : + cfc f (uᴴ * A * u.val) = uᴴ * (cfc f A) * u.val := by + have h1 := cfc_conj_unitary (A := A) f u⁻¹ + simp_all only [inv_inv] + exact h1 + +theorem Matrix.cfc_reindex (f : ℝ → ℝ) (e : d ≃ d₂) : + cfc f (reindex e e A) = reindex e e (cfc f A) := by + rw [reindex_eq_conj, reindex_eq_conj] + convert Matrix.cfc_conj_isometry f (u := (Matrix.reindex e (Equiv.refl d) : Matrix d d 𝕜 → Matrix d₂ d 𝕜) 1) ?_ ?_ + · simp + · simp + · apply reindex_one_isometry + · rw [conjTranspose_reindex, conjTranspose_one] + apply reindex_one_isometry + +theorem Matrix.commute_euclideanLin (hAB : Commute A B) : + Commute A.toEuclideanLin B.toEuclideanLin := by + rw [commute_iff_eq] at hAB ⊢ + ext v i + convert congr(($hAB).mulVec (WithLp.ofLp v) i) using 0 + simp only [Module.End.mul_apply, ← Matrix.mulVec_mulVec]; + simp only [ofLp_toLpLin, toLin'_apply, mulVec_mulVec] + +section commute_module +open Module.End + +--TODO: All of these have Pi versions (instead of the "just two" operators versions below), +-- see the tail end of `JointEigenspace.lean` to see how it should generalize. This would +-- also give a Pi version for Matrix. That would be useful for e.g. we have a large number +-- of projectors that all pairwise commute, and we want to simultaneously diagonalize all +-- of them. + +/-- Similar to `LinearMap.IsSymmetric.orthogonalFamily_eigenspace_inf_eigenspace`, but here the direct sum +is indexed by only the pairs of eigenvalues, as opposed to all pairs of `𝕜` values, giving a finite +decomposition. -/ +theorem LinearMap.IsSymmetric.orthogonalFamily_eigenspace_inf_eigenspace' {𝕜 E : Type*} [RCLike 𝕜] + [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] {A B : E →ₗ[𝕜] E} + (hA : A.IsSymmetric) (hB : B.IsSymmetric) : + OrthogonalFamily 𝕜 (fun (μ₁₂ : Eigenvalues A × Eigenvalues B) ↦ + ↥(eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2)) fun μ₁₂ ↦ + (eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2).subtypeₗᵢ := by + have h := LinearMap.IsSymmetric.orthogonalFamily_eigenspace_inf_eigenspace hA hB + simp only [OrthogonalFamily, Submodule.coe_subtypeₗᵢ, Submodule.subtype_apply, + Subtype.forall, Submodule.mem_inf, mem_genEigenspace_one, and_imp] at h ⊢ + intro i j hij a ha hb a' ha' hb' + contrapose! h + simp only [Pairwise, ne_eq, Prod.forall, Prod.mk.injEq, not_and, not_forall] + refine ⟨_, _, _, _, ?_, a, ha, hb, a', ha', hb', h⟩ + intro h' h'' + exact hij (Prod.ext (Subtype.ext h'') (Subtype.ext h')) + +/-- Variant of `iSup_mono'` that allows for an easier handling of bottom elements. -/ +theorem iSup_mono_bot {α : Type*} {ι ι' : Sort*} [CompleteLattice α] + {f : ι → α} {g : ι' → α} (h : ∀ (i : ι), f i = ⊥ ∨ ∃ i', f i ≤ g i') : + iSup f ≤ iSup g := by + rcases isEmpty_or_nonempty ι' + · simp only [IsEmpty.exists_iff, or_false] at h + simp [h] + · refine iSup_mono' (fun i ↦ ?_) + rcases h i with h | h <;> simp [h] + +@[reducible] +noncomputable def Commute.isSymmetric_directSumDecomposition {𝕜 E : Type*} [RCLike 𝕜] + [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] {A B : E →ₗ[𝕜] E} [FiniteDimensional 𝕜 E] + (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : + DirectSum.Decomposition fun (μ₁₂ : Eigenvalues A × Eigenvalues B) ↦ + (eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2) := by + apply (LinearMap.IsSymmetric.orthogonalFamily_eigenspace_inf_eigenspace' hA hB).decomposition + have h := LinearMap.IsSymmetric.iSup_iSup_eigenspace_inf_eigenspace_eq_top_of_commute + hA hB hAB + rw [iSup_prod'] at h + apply le_antisymm le_top + rw [← h, iSup_le_iff] + rintro ⟨fst, snd⟩ + by_cases h₁ : Module.End.HasEigenvalue A fst + · by_cases h₂ : Module.End.HasEigenvalue B snd + · exact le_iSup_of_le ⟨⟨fst, h₁⟩, ⟨snd, h₂⟩⟩ le_rfl + · replace h₂ : eigenspace B snd = ⊥ := by simpa [Module.End.HasUnifEigenvalue] using h₂ + simp [h₂] + · replace h₁ : eigenspace A fst = ⊥ := by simpa [Module.End.HasUnifEigenvalue] using h₁ + simp [h₁] + +/-- Similar to `LinearMap.IsSymmetric.directSum_isInternal_of_commute`, but here the direct sum +is indexed by only the pairs of eigenvalues, as opposed to all pairs of `𝕜` values, giving a finite +decomposition. -/ +theorem LinearMap.IsSymmetric.directSum_isInternal_of_commute' {𝕜 E : Type*} [RCLike 𝕜] + [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] {A B : E →ₗ[𝕜] E} [FiniteDimensional 𝕜 E] + (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : + DirectSum.IsInternal fun (μ₁₂ : Eigenvalues A × Eigenvalues B) ↦ + eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2 := by + classical + have h := LinearMap.IsSymmetric.directSum_isInternal_of_commute hA hB hAB + constructor + · intro x y hxy + -- Since the subspaces are orthogonal, the only way their sum can be zero is if each component is zero. Hence, x - y = 0, which implies x = y. + rw [← sub_eq_zero] + suffices h_diff_zero : ∀ (x : DirectSum (Eigenvalues A × Eigenvalues B) fun μ₁₂ ↦ ↥(eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2)), x.coeAddMonoidHom _ = 0 → x = 0 from + h_diff_zero (x - y) (by simp [hxy]) + clear x y hxy; intro x hx; + ext μ₁₂ + simp only [DirectSum.zero_apply, ZeroMemClass.coe_zero] + rw [← inner_self_eq_zero (𝕜 := 𝕜)] + have h_inner_zero : inner 𝕜 (x μ₁₂ : E) (x.coeAddMonoidHom _) = 0 := by + simp [hx] + rw [← h_inner_zero] + simp only [DirectSum.coeAddMonoidHom_eq_dfinsuppSum, ZeroMemClass.coe_zero, implies_true, + DFinsupp.sum_eq_sum_fintype, DFinsupp.equivFunOnFintype_apply] + -- Since the decomposition is orthogonal, the inner product of x μ₁₂ with any other component is zero. Therefore, the sum simplifies to just the inner product of x μ₁₂ with itself. + rw [inner_sum, Finset.sum_eq_add_sum_sdiff_singleton _ _ (by simp)] + rw [Finset.sdiff_singleton_eq_erase, left_eq_add] + apply Finset.sum_eq_zero + intro μ hμ + exact orthogonalFamily_eigenspace_inf_eigenspace' hA hB (Finset.ne_of_mem_erase hμ).symm _ _ + · -- Since the decomposition is orthogonal, the direct sum of the intersections is isomorphic to their sum. Therefore, the isomorphism implies that the sum is equal to E. + have h_sum : ⨆ (μ₁₂ : Eigenvalues A × Eigenvalues B), eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2 = ⊤ := by + rw [eq_top_iff] + intro x hx + obtain ⟨y, rfl⟩ := h.2 x + rw [DirectSum.coeAddMonoidHom_eq_dfinsuppSum] + refine Submodule.sum_mem _ fun i hi ↦ ?_ + have hyi := Submodule.coe_mem (y i) + simp only [Submodule.mem_inf, mem_genEigenspace_one] at hyi + refine Submodule.mem_iSup_of_mem ⟨⟨i.2, ?_⟩, ⟨i.1, ?_⟩⟩ (by simp) + <;> simp only [HasUnifEigenvalue, ne_eq, Submodule.eq_bot_iff, mem_genEigenspace_one, not_forall] + <;> refine ⟨y i, by tauto, by simpa using hi⟩ + intro x + rw [Submodule.eq_top_iff'] at h_sum + specialize h_sum x + rw [Submodule.mem_iSup_iff_exists_finsupp] at h_sum + rcases h_sum with ⟨f, hf₁, hf₂⟩ + exact ⟨∑ i ∈ f.support, .of _ i ⟨f i, hf₁ i⟩, by simp_all; exact hf₂⟩ + +noncomputable def LinearMap.sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} + (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : + OrthonormalBasis d 𝕜 (EuclideanSpace 𝕜 d) := + ((hA.directSum_isInternal_of_commute' hB hAB).subordinateOrthonormalBasis rfl + (hA.orthogonalFamily_eigenspace_inf_eigenspace' hB)).reindex + (Fintype.equivOfCardEq (by simp)) + +noncomputable def LinearMap.sharedEigenvaluesA {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} + (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : d → ℝ := + fun i => RCLike.re (inner 𝕜 (LinearMap.sharedEigenbasis hA hB hAB i) (A (LinearMap.sharedEigenbasis hA hB hAB i))) + +noncomputable def LinearMap.sharedEigenvaluesB {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} + (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : d → ℝ := + fun i => RCLike.re (inner 𝕜 (LinearMap.sharedEigenbasis hA hB hAB i) (B (LinearMap.sharedEigenbasis hA hB hAB i))) + +omit [DecidableEq d] in +theorem LinearMap.mem_eigenspace_inf_of_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} + (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) (i : d) : + ∃ (μ : Module.End.Eigenvalues A) (ν : Module.End.Eigenvalues B), + LinearMap.sharedEigenbasis hA hB hAB i ∈ Module.End.eigenspace A μ ⊓ Module.End.eigenspace B ν := by + rw [LinearMap.sharedEigenbasis] + rw [OrthonormalBasis.reindex_apply] + let hV := hA.directSum_isInternal_of_commute' hB hAB + let hV' := hA.orthogonalFamily_eigenspace_inf_eigenspace' hB + let hn : Module.finrank 𝕜 (EuclideanSpace 𝕜 d) = Module.finrank 𝕜 (EuclideanSpace 𝕜 d) := rfl + let e := Fintype.equivOfCardEq (show Fintype.card (Fin (Module.finrank 𝕜 (EuclideanSpace 𝕜 d))) = Fintype.card d by simp) + let j := e.symm i + let idx := hV.subordinateOrthonormalBasisIndex hn j hV' + exists idx.1, idx.2 + exact hV.subordinateOrthonormalBasis_subordinate hn j hV' + +omit [DecidableEq d] in +theorem LinearMap.apply_A_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} + (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) (i : d) : + A (sharedEigenbasis hA hB hAB i) = (sharedEigenvaluesA hA hB hAB i : 𝕜) • (sharedEigenbasis hA hB hAB i) := by + obtain ⟨μ, ν, h⟩ := mem_eigenspace_inf_of_sharedEigenbasis hA hB hAB i + have h₂ := Module.End.mem_eigenspace_iff.mp h.1 + rw [h₂] + congr; symm + simp only [sharedEigenvaluesA, h₂, inner_smul_right, OrthonormalBasis.inner_eq_one, + mul_one, ← RCLike.conj_eq_iff_re, ← RCLike.star_def] + have h₃ : (sharedEigenbasis hA hB hAB) i ≠ 0 := by + have := (sharedEigenbasis hA hB hAB).orthonormal.1 i + exact fun h => by simp [h] at this + simpa [inner_smul_left, inner_smul_right, h₂, h₃] using + hA ((sharedEigenbasis hA hB hAB) i) ((sharedEigenbasis hA hB hAB) i) + +omit [DecidableEq d] in +theorem LinearMap.apply_B_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} + (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) (i : d) : + B (sharedEigenbasis hA hB hAB i) = (sharedEigenvaluesB hA hB hAB i : 𝕜) • (sharedEigenbasis hA hB hAB i) := by + obtain ⟨μ, ν, h⟩ := mem_eigenspace_inf_of_sharedEigenbasis hA hB hAB i + have h₂ := Module.End.mem_eigenspace_iff.mp h.2 + rw [h₂] + congr; symm + simp only [sharedEigenvaluesB, h₂, inner_smul_right, OrthonormalBasis.inner_eq_one, + mul_one, ← RCLike.conj_eq_iff_re, ← RCLike.star_def] + have h₃ : (sharedEigenbasis hA hB hAB) i ≠ 0 := by + have := (sharedEigenbasis hA hB hAB).orthonormal.1 i + exact fun h => by simp [h] at this + simpa [inner_smul_left, inner_smul_right, h₂, h₃] using + hB ((sharedEigenbasis hA hB hAB) i) ((sharedEigenbasis hA hB hAB) i) + +noncomputable def Matrix.sharedEigenbasis + (hA : A.IsHermitian) (hB : B.IsHermitian) (hAB : Commute A B) : + OrthonormalBasis d 𝕜 (EuclideanSpace 𝕜 d) := + LinearMap.sharedEigenbasis (isSymmetric_toEuclideanLin_iff.symm.mp hA) + (isSymmetric_toEuclideanLin_iff.symm.mp hB) (commute_euclideanLin hAB) + +noncomputable def Matrix.sharedEigenvectorUnitary (hA : A.IsHermitian) (hB : B.IsHermitian) + (hAB : Commute A B) : Matrix.unitaryGroup d 𝕜 := + ⟨(EuclideanSpace.basisFun d 𝕜).toBasis.toMatrix (sharedEigenbasis hA hB hAB).toBasis, + (EuclideanSpace.basisFun d 𝕜).toMatrix_orthonormalBasis_mem_unitary (sharedEigenbasis hA hB hAB)⟩ + +namespace Matrix.SharedEigenbasis + +variable (hA : A.IsHermitian) (hB : B.IsHermitian) (hAB : Commute A B) + +/-- Analogous to `Matrix.IsHermitian.eigenvectorUnitary_mulVec` for the shared basis. -/ +theorem sharedEigenvectorUnitary_mulVec (j : d) : (sharedEigenvectorUnitary hA hB hAB) *ᵥ + Pi.single j 1 = WithLp.ofLp (sharedEigenbasis hA hB hAB j) := by + simp_all only [mulVec_single, MulOpposite.op_one, one_smul] + rfl + +noncomputable def sharedEigenvalueA (j : d) : ℝ := + LinearMap.sharedEigenvaluesA + (isSymmetric_toEuclideanLin_iff.symm.mp hA) + (isSymmetric_toEuclideanLin_iff.symm.mp hB) + (commute_euclideanLin hAB) j + +noncomputable def sharedEigenvalueB (j : d) : ℝ := + LinearMap.sharedEigenvaluesB + (isSymmetric_toEuclideanLin_iff.symm.mp hA) + (isSymmetric_toEuclideanLin_iff.symm.mp hB) + (commute_euclideanLin hAB) j + +/-- Analogous to `Matrix.IsHermitian.mulVec_eigenvectorBasis` for the shared basis. -/ +theorem mulVec_sharedEigenbasisA (j : d) : + A *ᵥ (sharedEigenbasis hA hB hAB j) = + (sharedEigenvalueA hA hB hAB) j • WithLp.ofLp (sharedEigenbasis hA hB hAB j) := by + rw [isSymmetric_toEuclideanLin_iff.symm] at hA hB + have h := LinearMap.apply_A_sharedEigenbasis hA hB (Matrix.commute_euclideanLin hAB) j + simp only [algebraMap_smul] at h + have := congr_arg WithLp.ofLp h + simp [Matrix.toEuclideanLin, Matrix.toLpLin_apply] at this + exact this + +theorem mulVec_sharedEigenbasisB (j : d) : + B *ᵥ (sharedEigenbasis hA hB hAB j) = + (sharedEigenvalueB hA hB hAB) j • WithLp.ofLp (sharedEigenbasis hA hB hAB j) := by + rw [isSymmetric_toEuclideanLin_iff.symm] at hA hB + have h := LinearMap.apply_B_sharedEigenbasis hA hB (Matrix.commute_euclideanLin hAB) j + simp only [algebraMap_smul] at h + have := congr_arg WithLp.ofLp h + simp [Matrix.toEuclideanLin, Matrix.toLpLin_apply] at this + exact this + +/- +PROVIDED SOLUTION +This is exactly analogous to star_shared_mul_B_mul_IsDiag (which is proved below in this file), but for A instead of B. Use the same proof structure: rw isDiag_iff_diagonal_diag, apply toEuclideanLin.injective, ext with basis, simp, then use mulVec_sharedEigenbasisA (instead of mulVec_sharedEigenbasisB), sharedEigenvectorUnitary_mulVec, h_simp2 (orthogonality/unit property), and by_cases on index equality, simplifying with simp +decide. Reference the B version's proof approach for the exact tactic sequence. +-/ + + +theorem star_shared_mul_A_mul_IsDiag : IsDiag + ((star (sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜)) * A * + (sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜)) := by + intro i j hij; + have := @mulVec_sharedEigenbasisA d; + specialize this hA hB hAB j; + replace this := congr_arg ( fun x => star ( ( sharedEigenbasis hA hB hAB i ).ofLp ) ⬝ᵥ x ) this + simp only [dotProduct, Pi.star_apply, RCLike.star_def, mulVec, Finset.mul_sum, mul_comm, + mul_assoc, Pi.smul_apply, Algebra.smul_mul_assoc] at this + simp only [mul_assoc] + convert this using 1; + · simp [ Matrix.mul_apply, mul_assoc, mul_comm, Finset.sum_mul ] + congr! 3; + · have := ( sharedEigenbasis hA hB hAB ).orthonormal; + rw [ orthonormal_iff_ite ] at this; + simp only [inner, ← starRingEnd_apply] at this + rw [ ← Finset.smul_sum, this i j, if_neg hij, smul_zero ] + +/-- Analogous to `Matrix.IsHermitian.star_mul_self_mul_eq_diagonal` for the shared basis. -/ +theorem star_shared_mul_B_mul_IsDiag : IsDiag + ((star (sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜)) * B * + (sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜)) := by + rw [Matrix.isDiag_iff_diagonal_diag, eq_comm] + apply Matrix.toEuclideanLin.injective + apply (EuclideanSpace.basisFun d 𝕜).toBasis.ext + intro i + simp only [toLpLin_apply, OrthonormalBasis.coe_toBasis, EuclideanSpace.basisFun_apply, + PiLp.ofLp_single, ← mulVec_mulVec, sharedEigenvectorUnitary_mulVec, ← mulVec_mulVec, + Matrix.diagonal_mulVec_single, mul_one] + apply PiLp.ext + intro j + + have h_simp : (Matrix.sharedEigenvectorUnitary hA hB hAB).val.conjTranspose.mulVec (B.mulVec (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i))) = + (sharedEigenvalueB hA hB hAB i) • (Matrix.sharedEigenvectorUnitary hA hB hAB).val.conjTranspose.mulVec (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i)) := by + convert congr_arg ( fun x => ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 ) ᴴ *ᵥ x ) ( mulVec_sharedEigenbasisB hA hB hAB i) using 1; + symm + exact (mulVec_smul ((sharedEigenvectorUnitary hA hB hAB).val)ᴴ (sharedEigenvalueB hA hB hAB i) + (WithLp.ofLp ((sharedEigenbasis hA hB hAB) i))) + have h_simp2 : (Matrix.sharedEigenvectorUnitary hA hB hAB).val.conjTranspose.mulVec (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i)) = Pi.single i 1 := by + rw [ ← sharedEigenvectorUnitary_mulVec hA hB hAB i ]; + simp + ext j + have := mul_eq_one_comm.mp ( show ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 ) * ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 )ᴴ = 1 from ?_ ); + · convert! congr_fun ( congr_fun this j ) i using 1; + simp [ Pi.single_apply, Matrix.one_apply ]; + · exact Matrix.mem_unitaryGroup_iff.mp ( Matrix.sharedEigenvectorUnitary hA hB hAB ).2; + simp_all [ Matrix.mulVec, funext_iff ]; + simp_all [ Matrix.mul_apply, dotProduct ]; + by_cases hij : i = j + · simp [ hij ]; + simp [Matrix.mulVec, dotProduct ]; + simp only [Finset.mul_sum, mul_left_comm]; + rw [ Finset.sum_comm ] + simp [ mul_comm, mul_left_comm, Finset.mul_sum] + congr! 3; + · simp [ hij ]; + simp_all [ mul_comm, Matrix.mulVec, dotProduct ]; + simp_all [ mul_comm, Finset.mul_sum]; + rw [ Finset.sum_comm ] + simp_all [ mul_assoc, mul_left_comm] ; + +end Matrix.SharedEigenbasis + +end commute_module + +theorem Commute.exists_unitary (hA : A.IsHermitian) (hB : B.IsHermitian) (hAB : Commute A B) : + ∃ U : Matrix.unitaryGroup d 𝕜, (U.val * A * Uᴴ).IsDiag ∧ (U.val * B * Uᴴ).IsDiag := by + use (Matrix.sharedEigenvectorUnitary hA hB hAB)⁻¹ + constructor + · convert! Matrix.SharedEigenbasis.star_shared_mul_A_mul_IsDiag hA hB hAB + simp [Matrix.star_eq_conjTranspose] + · convert! Matrix.SharedEigenbasis.star_shared_mul_B_mul_IsDiag hA hB hAB + simp [Matrix.star_eq_conjTranspose] + +variable (U : Matrix.unitaryGroup d 𝕜) + +instance instInvertibleUnitaryGroup (U : Matrix.unitaryGroup d 𝕜) : Invertible U := + invertibleOfGroup U + +instance (U : Matrix.unitaryGroup d 𝕜) : Invertible U.val := + ⟨star U.val, U.2.1, U.2.2⟩ + +/-- If a matrix is diagonalized by a unitary matrix, then it can be written as a +CFC of a (particular, canonical) diagonal matrix. -/ +theorem Matrix.IsDiag.exists_cfc {U : Matrix.unitaryGroup d 𝕜} {M : Matrix d d 𝕜} + (hU : (U.val * M * Uᴴ).IsDiag) (hM : M.IsHermitian) (e : d ≃ Fin (Fintype.card d)) : + ∃ f : ℝ → ℝ, + M = cfc f (Uᴴ * (Matrix.diagonal fun x => ↑↑(e x)) * U.val) := by + use fun x ↦ if hn : ∃ n : Fin (Fintype.card d), n = x + then RCLike.re (Matrix.diag (U.val * M * Uᴴ) (e.symm hn.choose)) else 0 + rw [Matrix.cfc_conj_unitary'] + rw [Matrix.isDiag_iff_diagonal_diag] at hU + rw [← Matrix.mul_inv_eq_iff_eq_mul_of_invertible] at hU + rw [← Matrix.inv_mul_eq_iff_eq_mul_of_invertible] at hU + rw [← hU, ← Matrix.mul_assoc] + congr; rotate_right + · exact Matrix.inv_eq_right_inv U.2.1 + · exact Matrix.inv_eq_left_inv U.2.1 + conv in Nat.cast (e _) => + equals (RCLike.ofReal <| e x) => simp only [map_natCast] + rw [Matrix.cfc_diagonal] + congr + ext i + simp only [Matrix.diag_apply, Function.comp_apply, Nat.cast_inj, exists_apply_eq_apply, + ↓reduceDIte] + rw [← (Matrix.isHermitian_mul_mul_conjTranspose U.val hM).coe_re_apply_self i] + congr! + · rw [mul_assoc, hU] + all_goals + ( rw [e.eq_symm_apply] + symm; convert Classical.choose_eq _ + exact Fin.val_inj) + +--TODO: Make Iff version. +/-- If two Hermitian matrices commute, there exists a common matrix that they are both a CFC of. -/ +theorem Commute.exists_cfc (hA : A.IsHermitian) (hB : B.IsHermitian) (hAB : Commute A B) : + ∃ C : Matrix d d 𝕜, (∃ f : ℝ → ℝ, A = cfc f C) ∧ (∃ g : ℝ → ℝ, B = cfc g C) := by + obtain ⟨U, hU₁, hU₂⟩ := hAB.exists_unitary hA hB + let D : Matrix d d 𝕜 := Matrix.diagonal (Fintype.equivFin d ·) + exact ⟨Uᴴ * D * U.val, hU₁.exists_cfc hA _, hU₂.exists_cfc hB _⟩ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/LinearEquiv.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/LinearEquiv.lean new file mode 100644 index 0000000000..2626a07acf --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/LinearEquiv.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Analysis.InnerProductSpace.PiL2 + +/-! +# Relabelling linear equivalences + +## i. Overview + +This module provides linear equivalences obtained by relabelling the index type of a finite +function space `d → R` or of `EuclideanSpace 𝕜 d` along an index equivalence `e : d ≃ d₂`, +together with lemmas relating them to `Matrix.reindex`. + +## ii. Key results + +- `LinearEquiv.ofRelabel` : the `R`-linear equivalence `(d₂ → R) ≃ₗ[R] (d → R)` induced by an + index equivalence `e : d ≃ d₂`. +- `LinearEquiv.euclideanOfRelabel` : the `EuclideanSpace` analogue of `ofRelabel`. +- `Matrix.reindex_toLin'` and `Matrix.reindex_toEuclideanLin` : reindexing a matrix conjugates + its associated linear map by these relabelling equivalences. + +## iii. Table of contents + +- A. Relabelling linear equivalences +- B. Reindexing matrices and their linear maps + +## iv. References + +-/ + +@[expose] public section + +variable {d d₁ d₂ d₃ R 𝕜 : Type*} [RCLike 𝕜] + +/-! + +## A. Relabelling linear equivalences + +-/ + +namespace LinearEquiv + +variable {R : Type*} [Semiring R] + +variable (R) in +/-- The `R`-linear equivalence `(d₂ → R) ≃ₗ[R] (d → R)` that relabels the coordinates of a +function along an index equivalence `e : d ≃ d₂`. This is the linear-equivalence packaging of +`Equiv.piCongrLeft`. -/ +@[simps] +def ofRelabel (e : d ≃ d₂) : (d₂ → R) ≃ₗ[R] (d → R) := by + refine' { e.symm.piCongrLeft (fun _ ↦ R) with .. } + <;> (intros; ext; simp [Equiv.piCongrLeft_apply]) + +variable (e : d ≃ d₂) + +variable (𝕜) in +/-- The `𝕜`-linear equivalence `EuclideanSpace 𝕜 d₂ ≃ₗ[𝕜] EuclideanSpace 𝕜 d` that relabels the +coordinates of a vector along an index equivalence `e : d ≃ d₂`. This is the `EuclideanSpace` +analogue of `LinearEquiv.ofRelabel`, obtained by transporting it across the `WithLp` +identifications. -/ +@[simps!] +def euclideanOfRelabel (e : d ≃ d₂) : EuclideanSpace 𝕜 d₂ ≃ₗ[𝕜] EuclideanSpace 𝕜 d := + (WithLp.linearEquiv 2 𝕜 _).trans ((ofRelabel _ e).trans (WithLp.linearEquiv 2 𝕜 _).symm) + +@[simp] +theorem ofRelabel_refl : ofRelabel R (.refl d) = LinearEquiv.refl R (d → R) := by + rfl + +@[simp] +theorem euclideanOfRelabel_refl : euclideanOfRelabel 𝕜 (.refl d) = + LinearEquiv.refl 𝕜 (EuclideanSpace 𝕜 d) := by + rfl + +end LinearEquiv + +/-! + +## B. Reindexing matrices and their linear maps + +-/ + +namespace Matrix + +variable {R : Type*} [CommSemiring R] +variable [Fintype d] [DecidableEq d] +variable [Fintype d₂] [DecidableEq d₂] + +theorem reindex_toLin' (e : d₁ ≃ d₃) (f : d₂ ≃ d) (M : Matrix d₁ d₂ R) : + (M.reindex e f).toLin' = (LinearEquiv.ofRelabel R e.symm) ∘ₗ + M.toLin' ∘ₗ (LinearEquiv.ofRelabel R f) := by + ext + simp [mulVec, dotProduct, Equiv.piCongrLeft_apply] + +theorem reindex_toEuclideanLin (e : d₁ ≃ d₃) (f : d₂ ≃ d) (M : Matrix d₁ d₂ 𝕜) : + (M.reindex e f).toEuclideanLin = (LinearEquiv.euclideanOfRelabel 𝕜 e.symm) ∘ₗ + M.toEuclideanLin ∘ₗ (LinearEquiv.euclideanOfRelabel 𝕜 f) := by + ext + simp [mulVec, dotProduct, Equiv.piCongrLeft_apply] + +theorem reindex_right_toLin' (e : d ≃ d₂) (M : Matrix d₃ d R) : + (M.reindex (.refl d₃) e).toLin' = M.toLin' ∘ₗ (LinearEquiv.ofRelabel R e) := by + rw [reindex_toLin'] + simp + +theorem reindex_right_toEuclideanLin (e : d ≃ d₂) (M : Matrix d₃ d 𝕜) : + (M.reindex (.refl d₃) e).toEuclideanLin = + M.toEuclideanLin ∘ₗ (LinearEquiv.euclideanOfRelabel 𝕜 e) := by + ext + simp [mulVec, dotProduct, Equiv.piCongrLeft_apply] + +theorem reindex_left_toLin' (e : d₁ ≃ d₃) (M : Matrix d₁ d₂ R) : + (M.reindex e (.refl d₂)).toLin' = (LinearEquiv.ofRelabel R e.symm) ∘ M.toLin' := by + rw [Matrix.reindex_toLin'] + simp + +theorem reindex_left_toEuclideanLin (e : d₁ ≃ d₃) (M : Matrix d₁ d₂ 𝕜) : + (M.reindex e (.refl d₂)).toEuclideanLin = + (LinearEquiv.euclideanOfRelabel 𝕜 e.symm) ∘ M.toEuclideanLin := by + rw [Matrix.reindex_toEuclideanLin] + simp + +end Matrix diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean new file mode 100644 index 0000000000..49f0363a33 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean @@ -0,0 +1,1599 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Algebra.Algebra.Spectrum.Quasispectrum +public import Mathlib.Algebra.Order.Group.Pointwise.CompleteLattice +public import Mathlib.Analysis.CStarAlgebra.Matrix +public import Mathlib.Analysis.Matrix.Order +public import Mathlib.Analysis.SpecialFunctions.Bernstein +public import Mathlib.Analysis.SpecialFunctions.Pow.NNReal +public import Mathlib.Data.Multiset.Functor --Can't believe I'm having to import this +public import Mathlib.LinearAlgebra.Matrix.Kronecker +public import Mathlib.LinearAlgebra.Matrix.PosDef +public import Mathlib.LinearAlgebra.Matrix.IsDiag +public import Mathlib.Tactic.Bound +public import Mathlib.Tactic.NormNum.GCD + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc + +-- Vendor edit 2: this repo's lakefile sets `relaxedAutoImplicit = false`; upstream +-- physlib relies on relaxed auto-bound implicits for `dA`/`dB` in this file. + + +@[expose] public section + +noncomputable section + +open BigOperators + +variable {n 𝕜 : Type*} +variable [RCLike 𝕜] [DecidableEq n] + +namespace Matrix + +open scoped ComplexOrder MatrixOrder + +omit [DecidableEq n] in +/-- The block Gram matrix `[[YᴴY, YᴴX], [XᴴY, XᴴX]]` is positive semidefinite. -/ +theorem fromBlocks_gram_posSemidef {m n k : Type*} [Fintype m] [Fintype n] [Fintype k] + (X : Matrix k n ℂ) (Y : Matrix k m ℂ) : + (fromBlocks (Yᴴ * Y) (Yᴴ * X) (Xᴴ * Y) (Xᴴ * X)).PosSemidef := by + convert posSemidef_conjTranspose_mul_self + (fromBlocks Y X (0 : Matrix k m ℂ) (0 : Matrix k n ℂ)) using 1 + rw [fromBlocks_conjTranspose, fromBlocks_multiply] + simp + +theorem zero_rank_eq_zero {A : Matrix n n 𝕜} [Fintype n] (hA : A.rank = 0) : A = 0 := by + have h : ∀ v, A.mulVecLin v = 0 := by + intro v + rw [rank, Module.finrank_zero_iff] at hA + have := hA.elim ⟨A.mulVecLin v, ⟨v, rfl⟩⟩ ⟨0, ⟨0, by rw [mulVecLin_apply, mulVec_zero]⟩⟩ + simpa only [Subtype.mk.injEq] using this + rw [← LinearEquiv.map_eq_zero_iff toLin'] + exact LinearMap.ext h + +namespace IsHermitian + +variable {A : Matrix n n 𝕜} {B : Matrix n n 𝕜} +variable (hA : A.IsHermitian) (hB : B.IsHermitian) + +include hA in +omit [DecidableEq n] in +theorem smul_selfAdjoint {c : 𝕜} (hc : _root_.IsSelfAdjoint c) : (c • A).IsHermitian := by + exact IsSelfAdjoint.smul hc hA + +include hA in +omit [DecidableEq n] in +theorem smul_im_zero {c : 𝕜} (h : RCLike.im c = 0) : (c • A).IsHermitian := + hA.smul_selfAdjoint (RCLike.conj_eq_iff_im.mpr h) + +include hA in +omit [DecidableEq n] in +theorem smul_real (c : ℝ) : (c • A).IsHermitian := by + convert hA.smul_im_zero (RCLike.ofReal_im c) using 1 + ext + simp only [smul_apply, smul_eq_mul, RCLike.real_smul_eq_coe_mul] + +def HermitianSubspace (n 𝕜 : Type*) [Fintype n] [RCLike 𝕜] : Subspace ℝ (Matrix n n 𝕜) where + carrier := { A : Matrix n n 𝕜 | A.IsHermitian } + add_mem' _ _ := by simp_all only [Set.mem_ofPred_eq, IsHermitian.add] + zero_mem' := by simp only [Set.mem_ofPred_eq, isHermitian_zero] + smul_mem' c A := by + simp only [Set.mem_ofPred_eq] + intro hA + exact IsHermitian.smul_real hA c + +variable [Fintype n] + +include hA in +omit [DecidableEq n] in +@[simp] +theorem re_trace_eq_trace : RCLike.re (A.trace) = A.trace := by + rw [trace, map_sum, RCLike.ofReal_sum, IsHermitian.coe_re_diag hA] + +section eigenvalues + +/-- The sum of the eigenvalues of a Hermitian matrix is equal to its trace. -/ +theorem sum_eigenvalues_eq_trace : ∑ i, hA.eigenvalues i = A.trace := by + nth_rewrite 2 [hA.spectral_theorem] + simp only [Unitary.conjStarAlgAut_apply] + rw [trace_mul_comm, ← mul_assoc] + simp [trace_diagonal] + +/-- If all eigenvalues are equal to zero, then the matrix is zero. -/ +theorem eigenvalues_zero_eq_zero (h : ∀ i, hA.eigenvalues i = 0) : A = 0 := by + suffices A.rank = 0 from zero_rank_eq_zero this + simp only [hA.rank_eq_card_non_zero_eigs, h, ne_eq, not_true_eq_false, Fintype.card_eq_zero] + +end eigenvalues + +end IsHermitian + +section Kronecker + +open Kronecker + +variable [CommRing R] [StarRing R] +variable (A : Matrix m m R) (B : Matrix n n R) + +omit [DecidableEq n] in +theorem kroneckerMap_conjTranspose : (A ⊗ₖ B)ᴴ = (Aᴴ ⊗ₖ Bᴴ) := by + ext; simp + +variable {A : Matrix m m R} {B : Matrix n n R} +variable (hA : A.IsHermitian) (hB : B.IsHermitian) + +include hA hB in +omit [DecidableEq n] in +theorem kroneckerMap_IsHermitian : (A ⊗ₖ B).IsHermitian := by + exact (hA ▸ hB ▸ kroneckerMap_conjTranspose A B : _ = _) + +end Kronecker + +namespace PosSemidef + +open Kronecker +open scoped ComplexOrder + +variable {m n 𝕜 : Type*} +variable [Fintype m] [Fintype n] +variable [RCLike 𝕜] [dn : DecidableEq n] + +section +variable {A : Matrix m m 𝕜} {B : Matrix m m 𝕜} +variable (hA : A.PosSemidef) (hB : B.PosSemidef) + +include hA in +theorem trace_zero : A.trace = 0 → A = 0 := by + open Classical in + intro h + rw [← hA.isHermitian.sum_eigenvalues_eq_trace, RCLike.ofReal_eq_zero] at h + rw [Finset.sum_eq_zero_iff_of_nonneg (fun i _ ↦ hA.eigenvalues_nonneg i)] at h + simp only [Finset.mem_univ, forall_const] at h + exact hA.isHermitian.eigenvalues_zero_eq_zero h + +include hA in +@[simp] +theorem trace_zero_iff : A.trace = 0 ↔ A = 0 := + ⟨trace_zero hA, (by simp [·])⟩ + +--belongs somewhere else. compare with `Complex.normSq_eq_conj_mul_self`. +open ComplexConjugate in +theorem _root_.RCLike.normSq_eq_conj_mul_self {z : 𝕜} : RCLike.normSq z = conj z * z := by + rw [RCLike.ext_iff] + simp [RCLike.normSq] + ring_nf + +--PR +theorem Finsupp.sum_eq_ite + {α : Type u_1} {M : Type u_8} {N : Type u_10} [Zero M] [AddCommMonoid N] [Fintype α] + [DecidableEq M] (f : α →₀ M) (g : α → M → N) : + f.sum g = ∑ i, if f i ≠ 0 then g i (f i) else 0 := by + rw [Finsupp.sum, eq_comm] + classical convert Finset.sum_ite_mem Finset.univ f.support (fun i ↦ g i (f i)) + · simp + · simp + + +omit dn in +open ComplexConjugate in +theorem outer_self_conj (v : n → 𝕜) : PosSemidef (vecMulVec v (conj v)) := by + constructor + · ext + simp [vecMulVec_apply, mul_comm] + · intro x + rw [Finsupp.sum_fintype _ _ (by simp)] + conv => + enter [2, 2, i] + rw [Finsupp.sum_fintype _ _ (by simp)] + simp_rw [RCLike.star_def, + vecMulVec_apply, mul_assoc, ← Finset.mul_sum, ← mul_assoc, ← Finset.sum_mul] + change + 0 ≤ (∑ i : n, conj (x i) * v i) * ∑ i : n, conj (v i) * x i + have : (∑ i : n, conj (x i) * v i) = + (∑ i : n, conj (conj (v i) * x i)) := by + simp only [mul_comm (conj (x _)) (v _), map_mul, + RingHomCompTriple.comp_apply, RingHom.id_apply] + rw [this, ← map_sum, ← RCLike.normSq_eq_conj_mul_self, RCLike.ofReal_nonneg] + exact RCLike.normSq_nonneg _ + +omit [Fintype m] in +include hA hB in +theorem convex_cone {c₁ c₂ : 𝕜} (hc₁ : 0 ≤ c₁) (hc₂ : 0 ≤ c₂) : (c₁ • A + c₂ • B).PosSemidef := + (hA.smul hc₁).add (hB.smul hc₂) + +variable [dm : DecidableEq m] + +/-- A standard basis matrix (with a positive entry) is positive semidefinite iff the entry is on the diagonal. -/ +theorem stdBasisMatrix_iff_eq (i j : m) {c : 𝕜} (hc : 0 < c) : (single i j c).PosSemidef ↔ i = j := by + constructor + · intro ⟨hherm, _⟩ + rw [IsHermitian, ← ext_iff] at hherm + replace hherm := hherm i j + simp only [single, conjTranspose_apply, of_apply, true_and, RCLike.star_def, if_true] at hherm + apply_fun (starRingEnd 𝕜) at hherm + have hcstar := RCLike.conj_eq_iff_im.mpr (RCLike.pos_iff.mp hc).right + rw [starRingEnd_self_apply, hcstar, ite_eq_left_iff] at hherm + contrapose! hherm + have hcnezero : 0 ≠ c := by + by_contra hczero + subst hczero + exact (lt_self_iff_false 0).mp hc + exact ⟨fun _ => hherm.symm, hcnezero⟩ + · intro hij + subst hij + constructor + · ext x y + simp only [conjTranspose_apply, RCLike.star_def, single, of_apply] + split_ifs <;> try tauto + · exact RCLike.conj_eq_iff_im.mpr (RCLike.pos_iff.1 hc).2 + · exact RingHom.map_zero (starRingEnd 𝕜) + · intro x + rw [Finsupp.sum_fintype _ _ (by simp)] + conv => + enter [2, 2, i] + rw [Finsupp.sum_fintype _ _ (by simp)] + simp only [single, of_apply] + convert_to 0 ≤ (star (x i)) * c * (x i) + · rw [←Fintype.sum_prod_type'] + have h₀ : ∀ x_1 : m × m, x_1 ≠ ⟨i, i⟩ → star (x x_1.1) * ((if i = x_1.1 ∧ i = x_1.2 then c else 0) * x x_1.2) = 0 := fun z hz => by + have h₁ : ¬(i = z.1 ∧ i = z.2) := by + rw [ne_eq, Prod.mk_inj] at hz + by_contra hz' + apply hz + exact ⟨hz'.left.symm, hz'.right.symm⟩ + rw [ite_cond_eq_false _ _ (eq_false h₁)] + ring + rw [Fintype.sum_eq_single ⟨i, i⟩] + · simp [mul_assoc] + · simpa [mul_assoc] using h₀ + · rw [mul_comm, ←mul_assoc] + have hpos : 0 ≤ (x i) * star (x i) := by simp only [RCLike.star_def, + RCLike.mul_conj, RCLike.ofReal_nonneg, norm_nonneg, pow_nonneg] + exact (mul_nonneg hpos (le_of_lt hc)) + +end + +variable {A : Matrix m m 𝕜} {B : Matrix n n 𝕜} +variable (hA : A.PosSemidef) (hB : B.PosSemidef) + +include hA hB in +theorem PosSemidef_kronecker : (A ⊗ₖ B).PosSemidef := by + open Classical in + rw [hA.left.spectral_theorem, hB.left.spectral_theorem] + simp only [Unitary.conjStarAlgAut_apply] + rw [mul_kronecker_mul, mul_kronecker_mul] + rw [star_eq_conjTranspose, star_eq_conjTranspose] + rw [← kroneckerMap_conjTranspose] + rw [diagonal_kronecker_diagonal] + apply mul_mul_conjTranspose_same + rw [posSemidef_diagonal_iff] + rintro ⟨i₁, i₂⟩ + convert mul_nonneg (hA.eigenvalues_nonneg i₁) (hB.eigenvalues_nonneg i₂) + rw [RCLike.nonneg_iff] + simp + +variable [dm : DecidableEq m] + +open MatrixOrder +open ComplexOrder + +omit [DecidableEq m] + +include hA in +theorem zero_dotProduct_zero_iff : (∀ x : m → 𝕜, 0 = star x ⬝ᵥ A.mulVec x) ↔ A = 0 := by + constructor + · intro h + ext i j + have h₂ := fun x ↦ (PosSemidef.dotProduct_mulVec_zero_iff hA x).mp (h x).symm + classical have : DecidableEq m := inferInstance + convert! congrFun (h₂ (Pi.single j 1)) i using 1 + simp + · rintro rfl + simp + +omit [Fintype m] in +theorem pos_smul {c : 𝕜} (hA : (c • A).PosSemidef) (hc : 0 < c) : A.PosSemidef := by + have : 0 < 1/c := by + rw [RCLike.pos_iff] at hc ⊢ + aesop + convert hA.smul (a := 1/c) this.le + rw [smul_smul, one_div, inv_mul_cancel₀ hc.ne', one_smul] + +theorem zero_posSemidef_neg_posSemidef_iff : A.PosSemidef ∧ (-A).PosSemidef ↔ A = 0 := by + constructor + · intro ⟨hA, hNegA⟩ + have h0 : ∀ x : m → 𝕜, 0 = star x ⬝ᵥ A.mulVec x := fun x ↦ by + simp only [Matrix.posSemidef_iff_dotProduct_mulVec] at hA hNegA + have hNegA' := hNegA.right (Finsupp.ofSupportFinite x (Function.support x).toFinite) + rw [neg_mulVec, dotProduct_neg, le_neg, neg_zero] at hNegA' + exact le_antisymm (hA.right x) hNegA' + exact (zero_dotProduct_zero_iff hA).mp h0 + · rintro rfl + simp [PosSemidef.zero] + +end PosSemidef + + +namespace PosDef +open scoped ComplexOrder + +variable {n m 𝕜 : Type*} +variable [Fintype n] [RCLike 𝕜] [DecidableEq n] +variable {A : Matrix n n 𝕜} + +theorem toLin_ker_eq_bot (hA : A.PosDef) : LinearMap.ker A.toLin' = ⊥ := by + ext v + rw [Matrix.posDef_iff_dotProduct_mulVec] at hA + have := @hA.right v + grind [mulVec_zero, dotProduct_zero, LinearMap.mem_ker, toLin'_apply, Submodule.mem_bot] + +theorem of_toLin_ker_eq_bot (hA : LinearMap.ker A.toLin' = ⊥) (hA₂ : A.PosSemidef) : A.PosDef := by + rwa [hA₂.posDef_iff_isUnit, ← Matrix.isUnit_toLin'_iff, LinearMap.isUnit_iff_ker_eq_bot] + +theorem ker_range_antitone {d : Type*} [Fintype d] [DecidableEq d] {A B : Matrix d d ℂ} + (hA : A.IsHermitian) (hB : B.IsHermitian) : + LinearMap.ker A.toEuclideanLin ≤ LinearMap.ker B.toEuclideanLin ↔ + LinearMap.range B.toEuclideanLin ≤ LinearMap.range A.toEuclideanLin + := by + rw [isSymmetric_toEuclideanLin_iff.symm] at hA hB + exact ContinuousLinearMap.ker_le_ker_iff_range_le_range + (T := Matrix.toEuclideanCLM.toFun B) (U := Matrix.toEuclideanCLM.toFun A) hB hA + +end PosDef + +namespace PosSemidef +section partialOrder +open scoped ComplexOrder +open scoped MatrixOrder + +variable {n m 𝕜 : Type*} +variable [Fintype n] [Fintype m] [RCLike 𝕜] [DecidableEq m] +variable {A : Matrix n n 𝕜} {B : Matrix n n 𝕜} +variable (hA : A.IsHermitian) (hB : B.IsHermitian) + +omit [Fintype n] in +theorem le_of_nonneg_imp {R : Type*} [AddCommGroup R] [PartialOrder R] [IsOrderedAddMonoid R] + (f : Matrix n n 𝕜 →+ R) (h : ∀ A, A.PosSemidef → 0 ≤ f A) : + (A ≤ B → f A ≤ f B) := by + intro hAB + rw [←sub_nonneg, ←map_sub] + exact h (B - A) <| by rwa [← Matrix.le_iff] + +omit [Fintype n] in +theorem le_of_nonneg_imp' {R : Type*} [AddCommGroup R] [PartialOrder R] [IsOrderedAddMonoid R] + {x y : R} (f : R →+ Matrix n n 𝕜) (h : ∀ x, 0 ≤ x → (f x).PosSemidef) : + (x ≤ y → f x ≤ f y) := by + intro hxy + rw [← sub_nonneg, ← map_sub, Matrix.nonneg_iff_posSemidef] + rw [← sub_nonneg] at hxy + exact h (y - x) hxy + +omit [DecidableEq m] in +theorem mul_mul_conjTranspose_mono (C : Matrix m n 𝕜) : + A ≤ B → C * A * C.conjTranspose ≤ C * B * C.conjTranspose := fun hAB ↦ by + rw [Matrix.le_iff] at hAB ⊢ + have hDistrib : C * B * Cᴴ - C * A * Cᴴ = C * (B - A) * Cᴴ := by + ext i j + simp only [sub_apply, mul_apply, conjTranspose_apply, RCLike.star_def, Finset.sum_mul, + ←Finset.sum_sub_distrib, mul_sub_left_distrib, mul_sub_right_distrib] + rw [hDistrib] + exact mul_mul_conjTranspose_same hAB C + +omit [DecidableEq m] in +theorem conjTranspose_mul_mul_mono (C : Matrix n m 𝕜) : + A ≤ B → C.conjTranspose * A * C ≤ C.conjTranspose * B * C := fun hAB ↦ by + convert mul_mul_conjTranspose_mono Cᴴ hAB + <;> rw [conjTranspose_conjTranspose] + +theorem nonneg_iff_eigenvalue_nonneg [DecidableEq n] : 0 ≤ A ↔ ∀ x, 0 ≤ hA.eigenvalues x := + Iff.trans Matrix.nonneg_iff_posSemidef hA.posSemidef_iff_eigenvalues_nonneg + +omit [Fintype n] in +theorem diag_monotone : Monotone (diag : Matrix n n 𝕜 → (n → 𝕜)) := fun _ _ ↦ + le_of_nonneg_imp (diagAddMonoidHom n 𝕜) (fun _ ↦ diag_nonneg) + +omit [Fintype n] in +theorem diag_mono : A ≤ B → ∀ i, A.diag i ≤ B.diag i := diag_monotone.imp + +theorem trace_monotone : Monotone (@trace n 𝕜 _ _) := fun _ _ ↦ + le_of_nonneg_imp (traceAddMonoidHom n 𝕜) (fun _ ↦ trace_nonneg) + +theorem trace_mono : A ≤ B → A.trace ≤ B.trace := trace_monotone.imp + +variable [DecidableEq n] + +omit [Fintype n] in +theorem diagonal_monotone : Monotone (diagonal : (n → 𝕜) → _) := fun _ _ ↦ + le_of_nonneg_imp' (diagonalAddMonoidHom n 𝕜) (fun _ ↦ PosSemidef.diagonal) + +omit [Fintype n] in +theorem diagonal_mono {d₁ d₂ : n → 𝕜} : d₁ ≤ d₂ → diagonal d₁ ≤ diagonal d₂ := diagonal_monotone.imp + +omit [Fintype n] in +theorem diagonal_le_iff {d₁ d₂ : n → 𝕜} : d₁ ≤ d₂ ↔ diagonal d₁ ≤ diagonal d₂ := ⟨diagonal_mono, by + intro hd + rw [Matrix.le_iff, diagonal_sub, posSemidef_diagonal_iff] at hd + simp only [sub_nonneg] at hd + exact hd⟩ + +theorem le_smul_one_of_eigenvalues_iff (hA : A.IsHermitian) (c : ℝ) : + (∀ i, hA.eigenvalues i ≤ c) ↔ A ≤ c • (1 : Matrix n n 𝕜) := by + let U : Matrix n n 𝕜 := ↑hA.eigenvectorUnitary + have hU : U.conjTranspose = star U := by simp only [star] + have hU' : U * star U = 1 := by + simp only [SetLike.coe_mem, Unitary.mul_star_self_of_mem, U] + have hc : c • (1 : Matrix n n 𝕜) = U * (c • 1) * U.conjTranspose := by + simp only [Algebra.mul_smul_comm, mul_one, hU, Algebra.smul_mul_assoc, hU'] + have hc' : c • (1 : Matrix n n 𝕜) = diagonal (RCLike.ofReal ∘ fun _ : n ↦ c) := by + ext i j + simp only [smul_apply, one_apply, smul_ite, RCLike.real_smul_eq_coe_mul, mul_one, smul_zero, + diagonal, Function.comp_apply, of_apply] + have hAST : A = U * diagonal (RCLike.ofReal ∘ hA.eigenvalues) * U.conjTranspose := by + rw [hU] + exact IsHermitian.spectral_theorem hA + constructor + · intro h + rw [hc, hc', hAST] + apply mul_mul_conjTranspose_mono + apply diagonal_mono + intro i + simp only [Function.comp_apply, algebraMap_le_algebraMap, h i] + intro hAc i + replace hAc := conjTranspose_mul_mul_mono U hAc + have hU'CT : star U * U = 1 := by + simp only [SetLike.coe_mem, Unitary.star_mul_self_of_mem, U] + have hcCT : U.conjTranspose * (c • 1) * U = c • (1 : Matrix n n 𝕜) := by + simp only [Algebra.mul_smul_comm, mul_one, hU, Algebra.smul_mul_assoc, hU'CT] + have hASTCT : U.conjTranspose * A * U = diagonal (RCLike.ofReal ∘ hA.eigenvalues) := by + rw [hU] + convert IsHermitian.conjStarAlgAut_star_eigenvectorUnitary hA using 1 + simp +zetaDelta + rw [hcCT, hc', hASTCT, ←diagonal_le_iff] at hAc + specialize hAc i + simp only [Function.comp_apply, algebraMap_le_algebraMap] at hAc + exact hAc + +theorem smul_one_le_of_eigenvalues_iff (hA : A.IsHermitian) (c : ℝ) : + (∀ i, c ≤ hA.eigenvalues i) ↔ c • (1 : Matrix n n 𝕜) ≤ A := by + -- I did the lazy thing and just copied the previous proof + let U : Matrix n n 𝕜 := ↑hA.eigenvectorUnitary + have hU : U.conjTranspose = star U := by simp only [star] + have hU' : U * star U = 1 := by + simp only [SetLike.coe_mem, Unitary.mul_star_self_of_mem, U] + have hc : c • (1 : Matrix n n 𝕜) = U * (c • 1) * U.conjTranspose := by + simp only [Algebra.mul_smul_comm, mul_one, hU, Algebra.smul_mul_assoc, hU'] + have hc' : c • (1 : Matrix n n 𝕜) = diagonal (RCLike.ofReal ∘ fun _ : n ↦ c) := by + ext i j + simp only [smul_apply, one_apply, smul_ite, RCLike.real_smul_eq_coe_mul, mul_one, smul_zero, + diagonal, Function.comp_apply, of_apply] + have hAST : A = U * diagonal (RCLike.ofReal ∘ hA.eigenvalues) * U.conjTranspose := by + rw [hU] + exact IsHermitian.spectral_theorem hA + constructor + · intro h + rw [hc, hc', hAST] + apply mul_mul_conjTranspose_mono + apply diagonal_mono + intro i + simp only [Function.comp_apply, algebraMap_le_algebraMap, h i] + intro hAc i + replace hAc := conjTranspose_mul_mul_mono U hAc + have hU'CT : star U * U = 1 := by + simp only [SetLike.coe_mem, Unitary.star_mul_self_of_mem, U] + have hcCT : U.conjTranspose * (c • 1) * U = c • (1 : Matrix n n 𝕜) := by + simp only [Algebra.mul_smul_comm, mul_one, hU, Algebra.smul_mul_assoc, hU'CT] + have hASTCT : U.conjTranspose * A * U = diagonal (RCLike.ofReal ∘ hA.eigenvalues) := by + rw [hU] + convert IsHermitian.conjStarAlgAut_star_eigenvectorUnitary hA using 1 + simp +zetaDelta + rw [hcCT, hc', hASTCT, ←diagonal_le_iff] at hAc + specialize hAc i + simp only [Function.comp_apply, algebraMap_le_algebraMap] at hAc + exact hAc + +end partialOrder + +end PosSemidef + +-- noncomputable section frobenius_inner_product +-- open scoped ComplexOrder +-- variable {A : Matrix n n 𝕜} {B : Matrix n n 𝕜} {C : Matrix n n 𝕜} [Fintype n] + +-- /-- The InnerProductSpace on Matrix n n 𝕜 defined by the real part of the +-- Frobenius inner product. -/ +-- def InnerProductCore : InnerProductSpace.Core (𝕜 := ℝ) (F := Matrix n n 𝕜):= +-- { +-- inner A B := RCLike.re (Aᴴ * B).trace +-- conj_inner_symm := fun x y ↦ by +-- simpa [inner, starRingEnd_apply, ← trace_conjTranspose] using +-- RCLike.conj_re (xᴴ * y).trace +-- re_inner_nonneg := fun x ↦ +-- (RCLike.nonneg_iff.mp x.posSemidef_conjTranspose_mul_self.trace_nonneg).1 +-- add_left := by simp [inner, add_mul] +-- smul_left x y r := by +-- simpa using RCLike.smul_re _ (xᴴ * y).trace +-- definite x h := by +-- ext i j +-- replace h : ∑ j, ∑ i, (RCLike.re (x i j) ^ 2 + RCLike.im (x i j) ^ 2) = 0 := by +-- simpa [trace, mul_apply, ← pow_two] using h +-- rw [Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h +-- replace h := congrFun h j +-- rw [Pi.zero_apply, Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h +-- replace h := congrFun h i +-- dsimp at h +-- rw [add_eq_zero_iff_of_nonneg (sq_nonneg _) (sq_nonneg _), sq_eq_zero_iff, sq_eq_zero_iff] at h +-- apply RCLike.ext (h.left.trans RCLike.zero_re.symm) (h.right.trans (map_zero _).symm) +-- } + +-- def instNormed : NormedAddCommGroup (Matrix n n 𝕜) := +-- InnerProductCore.toNormedAddCommGroup + +-- scoped[Frobenius] attribute [instance] Matrix.instNormed + +-- open scoped Frobenius in +-- def instInnerProductSpace : InnerProductSpace ℝ (Matrix n n 𝕜) := +-- InnerProductSpace.ofCore InnerProductCore + +-- scoped[Frobenius] attribute [instance] Matrix.instInnerProductSpace + +-- instance : Inner ℝ (Matrix n n 𝕜) := +-- instInnerProductSpace.toInner + +-- /-- The InnerProductSpace on Matrix n n 𝕜 defined by the Frobenius inner product. -/ +-- def CInnerProductCore : InnerProductSpace.Core (𝕜 := ℂ) (F := Matrix n n ℂ):= +-- { +-- inner A B := (Aᴴ * B).trace +-- conj_inner_symm := fun x y ↦ by +-- simp [inner, starRingEnd_apply, ← Matrix.trace_conjTranspose] +-- re_inner_nonneg := fun x ↦ +-- (RCLike.nonneg_iff.mp x.posSemidef_conjTranspose_mul_self.trace_nonneg).1 +-- add_left := by simp [inner, add_mul] +-- smul_left x y r := by simp +-- definite x h := by +-- ext i j +-- replace h : ∑ j, ∑ i, ((x i j).re ^ 2 + (x i j).im ^ 2) = (0 : ℂ) := by +-- convert h +-- simp only [Complex.ofReal_sum, Complex.ofReal_add, Complex.ofReal_pow, trace, diag_apply, +-- mul_apply, conjTranspose_apply, RCLike.star_def] +-- congr! 2 +-- norm_cast +-- rw [Complex.conj_mul', ← Complex.sq_norm_sub_sq_re] +-- norm_cast +-- abel +-- rw [Complex.ofReal_eq_zero, +-- Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h +-- replace h := congrFun h j +-- rw [Pi.zero_apply, Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h +-- replace h := congrFun h i +-- dsimp at h +-- rw [add_eq_zero_iff_of_nonneg (sq_nonneg _) (sq_nonneg _), sq_eq_zero_iff, sq_eq_zero_iff] at h +-- apply RCLike.ext (h.left.trans RCLike.zero_re.symm) (h.right.trans (map_zero _).symm) +-- } + +-- open scoped Frobenius in +-- def instCInnerProductSpace : InnerProductSpace ℂ (Matrix n n ℂ) := +-- InnerProductSpace.ofCore CInnerProductCore + +-- scoped[Frobenius] attribute [instance] Matrix.instCInnerProductSpace + +-- instance : Inner ℂ (Matrix n n ℂ) := +-- instCInnerProductSpace.toInner + +--Makes the `Inner ℝ` instance is globally accessible, but the norm instances +--require `open scoped Frobenius`. e.g. + +-- open scoped Frobenius in +-- #synth InnerProductSpace ℝ (Matrix (Fin 5) (Fin 5) ℝ) + +-- (no `open` needed): +-- #synth Inner ℝ (Matrix (Fin 5) (Fin 5) ℝ) + +-- end frobenius_inner_product + +section partial_trace + +variable [AddCommMonoid R] [Fintype d] + +def traceLeft (m : Matrix (d × d₁) (d × d₂) R) : Matrix d₁ d₂ R := + Matrix.of fun i₁ j₁ ↦ ∑ i₂, m (i₂, i₁) (i₂, j₁) + +def traceRight (m : Matrix (d₁ × d) (d₂ × d) R) : Matrix d₁ d₂ R := + Matrix.of fun i₂ j₂ ↦ ∑ i₁, m (i₂, i₁) (j₂, i₁) + +variable [Fintype d₁] [Fintype d₂] in +@[simp] +theorem traceLeft_trace (A : Matrix (d₁ × d₂) (d₁ × d₂) R) : A.traceLeft.trace = A.trace := by + convert! (Fintype.sum_prod_type_right _).symm + rfl + +variable [Fintype d₁] [Fintype d₂] in +@[simp] +theorem traceRight_trace (A : Matrix (d₁ × d₂) (d₁ × d₂) R) : A.traceRight.trace = A.trace := by + convert! (Fintype.sum_prod_type _).symm + rfl + +variable [StarAddMonoid R] in +theorem IsHermitian.traceLeft {A : Matrix (d × d₁) (d × d₁) R} (hA : A.IsHermitian) : A.traceLeft.IsHermitian := by + ext + simp only [Matrix.traceLeft, conjTranspose_apply, of_apply, star_sum] + congr! + exact congrFun₂ hA _ _ + +variable [StarAddMonoid R] in +theorem IsHermitian.traceRight {A : Matrix (d₁ × d) (d₁ × d) R} (hA : A.IsHermitian) : A.traceRight.IsHermitian := by + ext + simp only [Matrix.traceRight, conjTranspose_apply, of_apply, star_sum] + congr! + exact congrFun₂ hA _ _ + +variable [DecidableEq dB] [Fintype dA] [Fintype dB] in +open scoped Kronecker in +/-- +`Tr(M (A ⊗ I)) = Tr(Tr_B(M) A)` +-/ +theorem trace_mul_kron_one_right {R : Type*} [Ring R] + (M : Matrix (dA × dB) (dA × dB) R) (A : Matrix dA dA R) : + (M * (A ⊗ₖ (1 : Matrix dB dB R))).trace = (M.traceRight * A).trace := by + simp [trace, mul_apply, kroneckerMap_apply, traceRight, one_apply, + Fintype.sum_prod_type, Finset.sum_mul] + exact Finset.sum_congr rfl fun _ _ => Finset.sum_comm + +variable [DecidableEq dA] [Fintype dA] [Fintype dB] in +open scoped Kronecker in +/-- +`Tr(M (I ⊗ B)) = Tr(Tr_A(M) B)` +-/ +theorem trace_mul_one_kron_right {R : Type*} [Ring R] + (M : Matrix (dA × dB) (dA × dB) R) (B : Matrix dB dB R) : + (M * ((1 : Matrix dA dA R) ⊗ₖ B)).trace = (M.traceLeft * B).trace := by + simp [trace, mul_apply, kroneckerMap_apply, traceLeft, one_apply, + Fintype.sum_prod_type, Finset.sum_mul] + rw [Finset.sum_comm] + exact Finset.sum_congr rfl fun _ _ => Finset.sum_comm + +open ComplexOrder + +variable {d₁ d₂ : Type*} {A : Matrix (d₁ × d₂) (d₁ × d₂) 𝕜} +variable [Fintype d₂] [Fintype d₁] + +theorem PosSemidef.traceLeft [DecidableEq d₁] (hA : A.PosSemidef) : A.traceLeft.PosSemidef := by + rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA ⊢ + constructor + · exact hA.1.traceLeft + · intro x + convert Finset.sum_nonneg' (s := .univ) (fun (i : d₁) ↦ hA.2 (fun (j,k) ↦ if i = j then x k else 0)) + simp_rw [Matrix.traceLeft, dotProduct_mulVec] + simpa [dotProduct, vecMul_eq_sum, ite_apply, Fintype.sum_prod_type, Finset.mul_sum, Finset.sum_mul, + apply_ite] using Finset.sum_comm_cycle + +theorem PosSemidef.traceRight [DecidableEq d₂] (hA : A.PosSemidef) : A.traceRight.PosSemidef := by + rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA ⊢ + constructor + · exact hA.1.traceRight + · intro x + convert Finset.sum_nonneg' (s := .univ) (fun (i : d₂) ↦ hA.2 (fun (j,k) ↦ if i = k then x j else 0)) + simp_rw [Matrix.traceRight, dotProduct_mulVec] + simpa [dotProduct, vecMul_eq_sum, ite_apply, Fintype.sum_prod_type, Finset.mul_sum, Finset.sum_mul, + apply_ite] using Finset.sum_comm_cycle + +end partial_trace + +section posdef + +open ComplexOrder +open Kronecker + +theorem PosDef.kron {d₁ d₂ 𝕜 : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] [RCLike 𝕜] + {A : Matrix d₁ d₁ 𝕜} {B : Matrix d₂ d₂ 𝕜} (hA : A.PosDef) (hB : B.PosDef) : (A ⊗ₖ B).PosDef := by + rw [hA.left.spectral_theorem, hB.left.spectral_theorem] + simp only [Unitary.conjStarAlgAut_apply] + rw [mul_kronecker_mul, mul_kronecker_mul] + rw [star_eq_conjTranspose, star_eq_conjTranspose] + rw [← kroneckerMap_conjTranspose] + rw [diagonal_kronecker_diagonal] + apply mul_mul_conjTranspose_same + · rw [posDef_diagonal_iff] + rintro ⟨i₁, i₂⟩ + convert mul_pos (hA.eigenvalues_pos i₁) (hB.eigenvalues_pos i₂) + rw [RCLike.pos_iff] + simp + · apply Matrix.vecMul_injective_of_isUnit + rw [isUnit_iff_exists] + use (star hA.left.eigenvectorUnitary.val) ⊗ₖ (star hB.left.eigenvectorUnitary.val) + simp [← Matrix.mul_kronecker_mul] + +theorem PosDef.reindex {d₁ d₂ 𝕜 : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] [RCLike 𝕜] + {M : Matrix d₁ d₁ 𝕜} (hM : M.PosDef) (e : d₁ ≃ d₂) : (M.reindex e e).PosDef := + hM.submatrix e.symm.injective + +@[simp] +theorem PosDef.reindex_iff {d₁ d₂ 𝕜 : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] [RCLike 𝕜] + {M : Matrix d₁ d₁ 𝕜} (e : d₁ ≃ d₂) : (M.reindex e e).PosDef ↔ M.PosDef := by + refine ⟨fun h ↦ ?_, fun h ↦ h.reindex e⟩ + convert h.reindex e.symm + simp + +theorem PosSemidef.rsmul {n : Type*} [Fintype n] {M : Matrix n n ℂ} (hM : M.PosSemidef) {c : ℝ} (hc : 0 ≤ c) : + (c • M).PosSemidef := by + rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hM ⊢ + constructor + · exact hM.1.smul_real c + · peel hM.2 + rw [smul_mulVec, dotProduct_smul] + positivity + +theorem PosDef.Convex {n 𝕜 : Type*} [Fintype n] [RCLike 𝕜] : Convex ℝ (Matrix.PosDef (n := n) (R := 𝕜)) := by + intro A hA B hB a b ha hb hab + rcases ha.lt_or_eq with ha | rfl + · apply (hA.smul ha).add_posSemidef + exact hB.posSemidef.smul hb + · apply Matrix.PosDef.posSemidef_add + · simp [Matrix.PosSemidef.zero] + · exact hB.smul (by linarith) + +end posdef + +section eigenvalues + +open ComplexOrder + +variable {d 𝕜 : Type*} [Fintype d] [DecidableEq d] [RCLike 𝕜] + +theorem PosDef_iff_eigenvalues' (M : Matrix d d 𝕜) : + M.PosDef ↔ ∃ (h : M.IsHermitian), ∀ i, 0 < h.eigenvalues i := + ⟨fun h ↦ ⟨h.left, h.left.posDef_iff_eigenvalues_pos.mp h⟩, + fun ⟨w, h⟩ ↦ w.posDef_iff_eigenvalues_pos.mpr h⟩ + +--These is disgusting atm. There's cleaner versions of them headed to Mathlib. See #29526 and follow-ups +theorem IsHermitian.cfc_eigenvalues {M : Matrix d d 𝕜} (hM : M.IsHermitian) (f : ℝ → ℝ) : + ∃ (e : d ≃ d), Matrix.IsHermitian.eigenvalues (cfc_predicate f M) = f ∘ hM.eigenvalues ∘ e := by + have h_eigenvalues : Multiset.map hM.eigenvalues Finset.univ.val = Multiset.map (fun i => hM.eigenvalues i) Finset.univ.val := by + rfl + generalize_proofs at *; + have h_eigenvalues_cfc : (IsHermitian.cfc hM f).charpoly.roots = Multiset.map (fun i => (f (hM.eigenvalues i) : 𝕜)) Finset.univ.val := by + rw [ Matrix.IsHermitian.cfc, Matrix.charpoly ]; + -- Since $U$ is unitary, we have $U^* U = I$, and thus the characteristic polynomial of $U D U^*$ is the same as the characteristic polynomial of $D$. + have h_charpoly : Matrix.det ((hM.eigenvectorUnitary : Matrix d d 𝕜) * Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * Star.star (hM.eigenvectorUnitary : Matrix d d 𝕜)).charmatrix = Matrix.det (Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues)).charmatrix := by + -- Since $U$ is unitary, we have $U^* U = I$, and thus the characteristic polynomial of $U D U^*$ is the same as the characteristic polynomial of $D$ by the properties of determinants. + have h_char_poly : ∀ (t : 𝕜), Matrix.det (t • 1 - (hM.eigenvectorUnitary : Matrix d d 𝕜) * Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * star (hM.eigenvectorUnitary : Matrix d d 𝕜)) = Matrix.det (t • 1 - Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues)) := by + intro t; + -- Since $U$ is unitary, we have $U^* U = I$, and thus the determinant of $tI - UDU^*$ is the same as the determinant of $tI - D$. + have h_det : Matrix.det (t • 1 - (hM.eigenvectorUnitary : Matrix d d 𝕜) * Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * star (hM.eigenvectorUnitary : Matrix d d 𝕜)) = Matrix.det ((hM.eigenvectorUnitary : Matrix d d 𝕜) * (t • 1 - Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues)) * star (hM.eigenvectorUnitary : Matrix d d 𝕜)) := by + simp [ mul_sub, sub_mul, mul_assoc ]; + rw [ h_det, Matrix.det_mul, Matrix.det_mul ]; + rw [ mul_right_comm, ← Matrix.det_mul, mul_comm ]; + norm_num +zetaDelta at *; + refine' Polynomial.funext fun t => _; + convert h_char_poly t using 1; + · simp [ Matrix.det_apply', Polynomial.eval_finsetSum ]; + simp [ Matrix.one_apply, Polynomial.eval_prod ]; + congr! 3; + aesop; + · simp [ Matrix.det_apply', Polynomial.eval_finsetSum ]; + simp [ Matrix.one_apply, Polynomial.eval_prod ]; + exact Finset.sum_congr rfl fun _ _ => by congr; ext; aesop; + simp_all [ Matrix.charmatrix, Matrix.det_diagonal ]; + rw [ Polynomial.roots_prod ]; + · bound; + · exact Finset.prod_ne_zero_iff.mpr fun i _ => Polynomial.X_sub_C_ne_zero _; + have := Matrix.IsHermitian.roots_charpoly_eq_eigenvalues (cfc_predicate f M); + rw [← Matrix.IsHermitian.cfc_eq] at h_eigenvalues_cfc + rw [ h_eigenvalues_cfc ] at this; + simp [ Function.comp ] at this; + rw [ Multiset.map_univ_eq_iff ] at this; + obtain ⟨ e, he ⟩ := this; + use e.symm + ext x + have := congr_fun he ( e.symm x ); + simp_all only [Function.comp_apply, Equiv.apply_symm_apply, algebraMap.coe_inj] + +--Should be combined the above...? TODO Cleanup +/-- +If a Hermitian matrix A is unitarily similar to a diagonal matrix with real entries f, then the eigenvalues of A are a permutation of f. +-/ +lemma IsHermitian.eigenvalues_eq_of_unitary_similarity_diagonal {d 𝕜 : Type*} + [Fintype d] [DecidableEq d] [RCLike 𝕜] + {A : Matrix d d 𝕜} (hA : A.IsHermitian) + {U : Matrix d d 𝕜} (hU : U ∈ Matrix.unitaryGroup d 𝕜) + {f : d → ℝ} + (h : A = U * Matrix.diagonal (fun i => (RCLike.ofReal (f i) : 𝕜)) * Matrix.conjTranspose U) : + ∃ σ : d ≃ d, hA.eigenvalues ∘ σ = f := by + -- Since A is unitarily similar to D, they have the same characteristic polynomial. + have h_char_poly : Matrix.charpoly A = Matrix.charpoly (Matrix.diagonal fun i => (f i : 𝕜)) := by + have h_char_poly : Matrix.charpoly (U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) = Matrix.charpoly (Matrix.diagonal (fun i => (f i : 𝕜))) := by + have h_det : ∀ (t : 𝕜), Matrix.det (t • 1 - U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) = Matrix.det (t • 1 - Matrix.diagonal (fun i => (f i : 𝕜))) := by + intro t + have h_det : Matrix.det (t • 1 - U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) = Matrix.det (U * (t • 1 - Matrix.diagonal (fun i => (f i : 𝕜))) * Uᴴ) := by + simp [ mul_sub, sub_mul, Matrix.mul_assoc ]; + rw [ show U * Uᴴ = 1 from by exact hU.2 ]; + rw [h_det, Matrix.det_mul_comm, ← mul_assoc] + rw [← star_eq_conjTranspose, Matrix.UnitaryGroup.star_mul_self ⟨U, hU⟩] + simp + refine' Polynomial.funext fun t => _; + convert h_det t using 1 <;> simp [ Matrix.charpoly, Matrix.det_apply' ]; + · simp [ Polynomial.eval_finsetSum, Polynomial.eval_mul, Polynomial.eval_prod, Matrix.one_apply ]; + exact Finset.sum_congr rfl fun _ _ => by congr; ext; aesop; + · simp [ Polynomial.eval_finsetSum, Polynomial.eval_mul, Polynomial.eval_prod, Matrix.one_apply ]; + exact Finset.sum_congr rfl fun _ _ => by congr; ext; aesop; + rw [ h, h_char_poly ]; + -- The roots of the characteristic polynomial of A are its eigenvalues (by `IsHermitian.charpoly_roots_eq_eigenvalues`). + have h_eigenvalues : (Matrix.charpoly A).roots = Multiset.map (RCLike.ofReal ∘ hA.eigenvalues) Finset.univ.val := by + exact Matrix.IsHermitian.roots_charpoly_eq_eigenvalues hA; + -- The roots of the characteristic polynomial of D are the diagonal entries f. + have h_diag_roots : (Matrix.charpoly (Matrix.diagonal fun i => (f i : 𝕜))).roots = Multiset.map (fun i => (f i : 𝕜)) Finset.univ.val := by + simp [ Matrix.charpoly, Matrix.det_diagonal ]; + rw [ Polynomial.roots_prod ]; + · aesop; + · exact Finset.prod_ne_zero_iff.mpr fun i _ => Polynomial.X_sub_C_ne_zero _; + have := Multiset.map_univ_eq_iff ( RCLike.ofReal ∘ hA.eigenvalues ) f + subst h + simp_all only [Function.comp_apply, RCLike.ofReal_real_eq_id, id_eq, CompTriple.comp_eq] + refine' this.mp _ |> fun ⟨ e, he ⟩ => ⟨ e.symm, _ ⟩ + · simpa [ Function.comp ] using congr_arg ( Multiset.map ( RCLike.re : 𝕜 → ℝ ) ) h_eigenvalues.symm + · exact funext fun x => by simpa using congr_fun he ( e.symm x ) ; + +end eigenvalues + +section + +variable {α n : Type*} [RCLike α] [Fintype n] [DecidableEq n] + +@[simp] +theorem toEuclideanLin_one : Matrix.toEuclideanLin (1 : Matrix n n α) = .id := by + ext1 x + simp [Matrix.toEuclideanLin] + +end + +section more_cfc + +open ComplexOrder + +variable {d 𝕜 : Type*} [Fintype d] [DecidableEq d] [RCLike 𝕜] + +@[simp] +theorem cfc_diagonal (g : d → ℝ) (f : ℝ → ℝ) : + cfc f (Matrix.diagonal (fun x ↦ (g x : 𝕜))) = diagonal (RCLike.ofReal ∘ f ∘ g) := by + --Thanks Aristotle + have h_self_adjoint : _root_.IsSelfAdjoint (diagonal (fun x => (g x : 𝕜))) := by + change Matrix.conjTranspose _ = _ + simp [Matrix.conjTranspose] + --TODO cfc_cont_tac + rw [cfc, dif_pos ⟨h_self_adjoint, continuousOn_iff_continuous_domRestrict.mpr <| by fun_prop⟩] + rw [cfcHom_eq_of_continuous_of_map_id] + rotate_left + · refine' { .. } + use fun f ↦ Matrix.diagonal fun x ↦ f ⟨g x, (by + simpa [algebraMap_eq_diagonal, diagonal_apply] using + congr_arg (· x x) ·.exists_left_inv.choose_spec + )⟩ + · simp + · simp [diagonal, ← Matrix.ext_iff, mul_apply] + intro x y i j + split_ifs with h + · subst h; simp + · rfl + · simp + · simp [diagonal, funext_iff] + intro x y i j + split_ifs with h + · simp + · simp + · simp [← ext_iff, diagonal] + exact fun r i j ↦ rfl + · simp [← ext_iff, diagonal] + grind [RCLike.conj_ofReal] + · dsimp [diagonal] + continuity + · simp [diagonal] + · simp [diagonal] + +theorem PosSemidef.pos_of_mem_spectrum {A : Matrix d d 𝕜} (hA : A.PosSemidef) (r : ℝ) : + r ∈ spectrum ℝ A → 0 ≤ r := by + intro hr + rw [hA.left.spectrum_real_eq_range_eigenvalues] at hr + rcases hr with ⟨i, rfl⟩ + exact hA.eigenvalues_nonneg i + +theorem PosSemidef.pow_add {A : Matrix d d 𝕜} (hA : A.PosSemidef) {x y : ℝ} (hxy : x + y ≠ 0) : + cfc (· ^ (x + y) : ℝ → ℝ) A = cfc (fun r ↦ r ^ x * r ^ y : ℝ → ℝ) A := by + refine cfc_congr fun r hr ↦ ?_ + exact Real.rpow_add' (hA.pos_of_mem_spectrum r hr) hxy + +theorem PosSemidef.pow_mul {A : Matrix d d 𝕜} {x y : ℝ} (hA : A.PosSemidef) : + cfc (· ^ (x * y) : ℝ → ℝ) A = cfc (fun r ↦ (r ^ x) ^ y : ℝ → ℝ) A := by + refine cfc_congr fun r hr ↦ ?_ + exact Real.rpow_mul (hA.pos_of_mem_spectrum r hr) x y + +end more_cfc + +section subm + +variable {α : Type*} [AddCommMonoid α] +variable {d₁ d₂ : Type*} [Fintype d₁] [Fintype d₂] + +@[simp] +theorem trace_submatrix + (A : Matrix d₁ d₁ α) (e : d₂ ≃ d₁) : + (A.submatrix e e).trace = A.trace := by + simpa [Matrix.trace] using e.sum_comp (fun x ↦ A x x) + +end subm + +section spectrum_kron + +--This is really really ugly, and already *after* trying to clean it up a bit. + + +open Kronecker +open scoped Pointwise + +private lemma spectrum_prod_complex {d d₂ : Type*} + [Fintype d] [DecidableEq d] [Fintype d₂] [DecidableEq d₂] + {A : Matrix d d 𝕜} {B : Matrix d₂ d₂ 𝕜} + (hA : A.IsHermitian) (hB : B.IsHermitian) : + ∀ x : 𝕜, x ∈ spectrum 𝕜 (A ⊗ₖ B) → ∃ a ∈ spectrum 𝕜 A, ∃ b ∈ spectrum 𝕜 B, x = a * b := by + intro x hx + have h_det : Matrix.det (A ⊗ₖ B - x • 1) = 0 := by + rw [ spectrum.mem_iff, Matrix.isUnit_iff_isUnit_det ] at hx; + rw [ ← neg_sub, Matrix.det_neg ] + simp_all only [isUnit_iff_ne_zero, ne_eq, Decidable.not_not, Fintype.card_prod, + mul_eq_zero, pow_eq_zero_iff', neg_eq_zero, one_ne_zero, not_or, false_and, false_or] + convert hx using 1; + congr! 1; + ext ⟨ i, j ⟩ ⟨ i', j' ⟩; + simp [ Algebra.smul_def ] + -- Since $A$ and $B$ are Hermitian, they are diagonalizable. Let $P$ and $Q$ be unitary matrices such that $P^*AP$ and $Q^*BQ$ are diagonal. + obtain ⟨P, hP₁, ⟨D, hD⟩⟩ : ∃ P : Matrix d d 𝕜, P.det ≠ 0 ∧ ∃ D : Matrix d d 𝕜, D.IsDiag ∧ P⁻¹ * A * P = D := by + refine' ⟨ hA.eigenvectorUnitary, _, Matrix.diagonal ( RCLike.ofReal ∘ hA.eigenvalues ), _, _ ⟩; + · intro h_det_zero; + exact absurd h_det_zero <| isUnit_iff_ne_zero.mp <| UnitaryGroup.det_isUnit hA.eigenvectorUnitary + · exact isDiag_diagonal (RCLike.ofReal ∘ hA.eigenvalues); + · -- Since $U$ is unitary, $U⁻¹ = U*$, and thus $U⁻¹ * U = I$. + have h_unitary : (hA.eigenvectorUnitary : Matrix d d 𝕜)⁻¹ = star (hA.eigenvectorUnitary : Matrix d d 𝕜) := by + rw [ Matrix.inv_eq_left_inv ]; + simp + -- Substitute h_unitary into the equation. + rw [h_unitary]; + convert Matrix.IsHermitian.conjStarAlgAut_star_eigenvectorUnitary hA using 1 + simp + obtain ⟨Q, hQ₁, ⟨E, hE⟩⟩ : ∃ Q : Matrix d₂ d₂ 𝕜, Q.det ≠ 0 ∧ ∃ E : Matrix d₂ d₂ 𝕜, E.IsDiag ∧ Q⁻¹ * B * Q = E := by + have := Matrix.IsHermitian.spectral_theorem hB; + -- By the spectral theorem, since B is Hermitian, there exists a unitary matrix Q and a diagonal matrix D such that B = Q * D * Q⁻¹. + obtain ⟨Q, hQ_unitary, D, hD_diag, hQ⟩ : ∃ Q : Matrix d₂ d₂ 𝕜, Q.det ≠ 0 ∧ ∃ D : Matrix d₂ d₂ 𝕜, D.IsDiag ∧ B = Q * D * Q⁻¹ := by + refine' ⟨ hB.eigenvectorUnitary, _, Matrix.diagonal ( RCLike.ofReal ∘ hB.eigenvalues ), _, _ ⟩; + · intro h_det_zero; + -- Since the eigenvector unitary matrix is unitary, its determinant is non-zero. + have h_unitary_det : ∀ (U : Matrix d₂ d₂ 𝕜), U * star U = 1 → U.det ≠ 0 := + fun U hU => Matrix.det_ne_zero_of_right_inverse hU; + exact h_unitary_det _ ( by simp) h_det_zero; + · exact isDiag_diagonal (RCLike.ofReal ∘ hB.eigenvalues); + · convert this using 1; + rw [ Matrix.inv_eq_left_inv ]; + · simp + rfl + · simp only [SetLike.coe_mem, Unitary.star_mul_self_of_mem] + refine ⟨ Q, hQ_unitary, D, hD_diag, ?_ ⟩ + simp [ hQ, mul_assoc, hQ_unitary, isUnit_iff_ne_zero ]; + -- Then $(P \otimes Q)^{-1}(A \otimes B)(P \otimes Q) = D \otimes E$, where $D$ and $E$ are diagonal matrices. + have h_diag : (P.kronecker Q)⁻¹ * (A ⊗ₖ B) * (P.kronecker Q) = D ⊗ₖ E := by + -- Using the properties of the Kronecker product and the fact that $P$ and $Q$ are invertible, we can simplify the expression. + have h_kronecker : (P.kronecker Q)⁻¹ * (A.kronecker B) * (P.kronecker Q) = (P⁻¹ * A * P).kronecker (Q⁻¹ * B * Q) := by + have h_kronecker : ∀ (X Y : Matrix d d 𝕜) (Z W : Matrix d₂ d₂ 𝕜), (X.kronecker Z) * (Y.kronecker W) = (X * Y).kronecker (Z * W) := by + intro X Y Z W; ext i j; simp [ Matrix.mul_apply ] ; + simp only [mul_left_comm, mul_comm, Finset.mul_sum _ _ _]; + exact Fintype.sum_prod_type_right _ + rw [Matrix.inv_eq_right_inv, h_kronecker, h_kronecker]; + convert h_kronecker P P⁻¹ Q Q⁻¹ using 1; + simp [ hP₁, hQ₁, isUnit_iff_ne_zero ]; + aesop; + -- Since $D$ and $E$ are diagonal matrices, the determinant of $(D \otimes E - xI)$ is the product of the determinants of $(D - xI)$ and $(E - xI)$. + have h_det_diag : Matrix.det (D ⊗ₖ E - x • 1) = 0 := by + have h_det_diag : Matrix.det ((P.kronecker Q)⁻¹ * (A ⊗ₖ B - x • 1) * (P.kronecker Q)) = Matrix.det (D ⊗ₖ E - x • 1) := by + simp [ ← h_diag, mul_sub, sub_mul ]; + simp [ Matrix.det_kronecker, hP₁, hQ₁ ]; + simp_all [ Matrix.det_mul ]; + -- Since $D$ and $E$ are diagonal matrices, the determinant of $(D \otimes E - xI)$ is the product of the determinants of $(D - xI)$ and $(E - xI)$. Therefore, there must be some $i$ and $j$ such that $D_{ii} * E_{jj} = x$. + obtain ⟨i, j, hij⟩ : ∃ i : d, ∃ j : d₂, D i i * E j j = x := by + contrapose! h_det_diag; + have h_det_diag : Matrix.det (D ⊗ₖ E - x • 1) = ∏ i : d, ∏ j : d₂, (D i i * E j j - x) := by + have h_det_diag : Matrix.det (D ⊗ₖ E - x • 1) = Matrix.det (Matrix.diagonal (fun p : d × d₂ => D p.1 p.1 * E p.2 p.2 - x)) := by + congr with p q + simp_all only [ne_eq, kronecker, sub_apply, + kroneckerMap_apply, smul_apply, smul_eq_mul] + obtain ⟨fst, snd⟩ := p + obtain ⟨fst_1, snd_1⟩ := q + obtain ⟨left, rfl⟩ := hD + obtain ⟨left_1, rfl⟩ := hE + simp_all only + by_cases h : fst = fst_1 <;> by_cases h' : snd = snd_1 <;> simp [ h, h', Matrix.one_apply ]; + · exact Or.inr ( left_1 ( by aesop ) ); + · exact Or.inl ( left h ); + · exact Or.inl ( left h ); + simp_all [ Matrix.det_diagonal ]; + exact Fintype.prod_prod_type fun (x_2 : d × d₂) => D x_2.1 x_2.1 * E x_2.2 x_2.2 - x + exact h_det_diag.symm ▸ Finset.prod_ne_zero_iff.mpr fun i _ => Finset.prod_ne_zero_iff.mpr fun j _ => sub_ne_zero_of_ne <| by solve_by_elim; + refine' ⟨ D i i, _, E j j, _, _ ⟩ + · simp_all [ spectrum.mem_iff ]; + simp_all [ Matrix.isUnit_iff_isUnit_det ]; + have h_det_diag : Matrix.det (P⁻¹ * (D i i • 1 - A) * P) = 0 := by + simp_all [ mul_sub, sub_mul, mul_assoc ]; + rw [ Matrix.det_eq_zero_of_row_eq_zero i ] + intro j_1 + subst hij + simp_all only [map_mul, sub_apply, smul_apply, smul_eq_mul] + obtain ⟨left, rfl⟩ := hD + obtain ⟨left_1, rfl⟩ := hE + by_cases hij : i = j_1 <;> simp_all [ Matrix.one_apply ]; + exact left hij; + simp_all [ Matrix.det_mul]; + convert h_det_diag using 1; + exact congr_arg Matrix.det ( by ext i j; by_cases hi : i = j <;> simp [ hi, Algebra.smul_def ] ); + · simp_all [ spectrum.mem_iff ]; + -- Since $E$ is diagonal, $E j j - B$ is singular, hence not invertible. + have h_singular : Matrix.det (E j j • 1 - B) = 0 := by + have h_singular : Matrix.det (Q⁻¹ * (E j j • 1 - B) * Q) = 0 := by + simp [ mul_sub, sub_mul, hE.2 ]; + rw [ Matrix.det_eq_zero_of_row_eq_zero j ] + intro j_1 + subst hij + simp_all only [map_mul, isUnit_iff_ne_zero, ne_eq, not_false_eq_true, nonsing_inv_mul, sub_apply, + smul_apply, smul_eq_mul] + obtain ⟨left, rfl⟩ := hD + obtain ⟨left_1, rfl⟩ := hE + by_cases h : j = j_1 <;> aesop; + simp_all [ Matrix.det_mul ]; + simp_all [ Matrix.isUnit_iff_isUnit_det ]; + convert h_singular using 1; + simp [ Algebra.smul_def ]; + · simp_all [ spectrum.mem_iff ]; + +private lemma spectrum_prod_le {d d₂ : Type*} + [Fintype d] [DecidableEq d] [Fintype d₂] [DecidableEq d₂] + {A : Matrix d d 𝕜} {B : Matrix d₂ d₂ 𝕜} + (hA : A.IsHermitian) (hB : B.IsHermitian) : + spectrum ℝ (A ⊗ₖ B) ⊆ spectrum ℝ A * spectrum ℝ B := by + intro x hx + suffices h : ∃ a ∈ spectrum ℝ A, ∃ b ∈ spectrum ℝ B, x = a * b by + rcases h with ⟨a, ha, b, hb, rfl⟩ + exact ⟨a, ha, b, hb, rfl⟩ + obtain ⟨_, ha, _, hb, h₁, ⟨a', rfl⟩, ⟨b', rfl⟩⟩ : ∃ a ∈ spectrum 𝕜 A, ∃ b ∈ spectrum 𝕜 B, + x = a * b ∧ + a ∈ Set.range (algebraMap ℝ 𝕜) ∧ b ∈ Set.range (algebraMap ℝ 𝕜) := by + obtain ⟨a, ha, b, hb, hx_eq⟩ := spectrum_prod_complex hA hB x (by exact hx); + have ha' := ha + have hb' := hb + rw [hA.spectrum_eq_image_range] at ha + rw [hB.spectrum_eq_image_range] at hb + grind + use a', ha, b', hb + simp only [RCLike.algebraMap_eq_ofReal] at h₁ + exact_mod_cast h₁ + +open Kronecker in +open scoped Pointwise in +theorem spectrum_prod {d d₂ : Type*} + [Fintype d] [DecidableEq d] [Fintype d₂] [DecidableEq d₂] + {A : Matrix d d 𝕜} {B : Matrix d₂ d₂ 𝕜} + (hA : A.IsHermitian) (hB : B.IsHermitian) : + spectrum ℝ (A ⊗ₖ B) = spectrum ℝ A * spectrum ℝ B := by + apply subset_antisymm + · exact spectrum_prod_le hA hB + · rintro x ⟨ y, hy, z, hz, rfl ⟩; + -- Since $y$ is an eigenvalue of $A$ and $z$ is an eigenvalue of $B$, there exist eigenvectors $v$ and $w$ such that $A*v = y*v$ and $B*w = z*w$. + obtain ⟨v, hv⟩ : ∃ v : d → 𝕜, v ≠ 0 ∧ A.mulVec v = y • v := by + rw [ spectrum.mem_iff ] at hy; + simp_all [ Matrix.isUnit_iff_isUnit_det ]; + have := Matrix.exists_mulVec_eq_zero_iff.mpr hy; + simp_all [ funext_iff, Matrix.mulVec, dotProduct ]; + simp_all [ sub_mul, Matrix.one_apply, Algebra.algebraMap_eq_smul_one ]; + exact ⟨ this.choose, this.choose_spec.1, fun x => by linear_combination -this.choose_spec.2 x ⟩ + obtain ⟨w, hw⟩ : ∃ w : d₂ → 𝕜, w ≠ 0 ∧ B.mulVec w = z • w := by + rw [ spectrum.mem_iff ] at hz; + simp_all [ Matrix.isUnit_iff_isUnit_det ]; + have := Matrix.exists_mulVec_eq_zero_iff.mpr hz; + simp_all [ Matrix.sub_mulVec ]; + obtain ⟨ w, hw, hw' ⟩ := this; use w; simp_all [ sub_eq_zero, Algebra.algebraMap_eq_smul_one ] ; + simp_all [ funext_iff, Matrix.mulVec, dotProduct ]; + simp_all [ Matrix.one_apply]; + refine' spectrum.mem_iff.mpr _; + -- Consider the vector $v \otimes w$. + set v_tensor_w : (d × d₂) → 𝕜 := fun p => v p.1 * w p.2; + -- We need to show that $v \otimes w$ is an eigenvector of $A \otimes B$ with eigenvalue $yz$. + have h_eigenvector : (Matrix.kroneckerMap (· * ·) A B).mulVec v_tensor_w = (y * z) • v_tensor_w := by + ext ⟨ i, j ⟩ ; + simp [ Matrix.mulVec, dotProduct] at * + simp [ funext_iff, Matrix.mulVec, dotProduct ] at hv hw ⊢ + erw [ Finset.sum_product ] + simp_all only [v_tensor_w] + obtain ⟨left, right⟩ := hv + obtain ⟨left_1, right_1⟩ := hw + -- By separating the sums, we can apply the given equalities. + have h_separate : ∑ x, ∑ x_1, A i x * B j x_1 * (v x * w x_1) = (∑ x : d, A i x * v x) * (∑ x_1 : d₂, B j x_1 * w x_1) := by + simp only [mul_left_comm, mul_comm, Finset.mul_sum _ _ _]; + exact Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ); + rw [ h_separate, right, right_1 ] + simp [RCLike.real_smul_eq_coe_mul] + ring_nf + -- Since $v \otimes w$ is an eigenvector of $A \otimes B$ with eigenvalue $yz$, we have $(A \otimes B - yzI)(v \otimes w) = 0$. + have h_eigenvector_zero : ((A ⊗ₖ B) - (y * z) • 1) *ᵥ v_tensor_w = 0 := by + simp [ h_eigenvector, Matrix.sub_mulVec ]; + simp [ Matrix.mulVec, funext_iff ]; + simp [ Matrix.one_apply, dotProduct ]; + -- Since $v \otimes w$ is non-zero, we have $(A \otimes B - yzI)(v \otimes w) = 0$ implies that $A \otimes B - yzI$ is not invertible. + have h_not_invertible : ¬IsUnit (A ⊗ₖ B - (y * z) • 1) := by + simp only [ne_eq, isUnit_iff_isUnit_det, isUnit_iff_ne_zero, Decidable.not_not, v_tensor_w] at * + rw [ ← Matrix.exists_mulVec_eq_zero_iff ] + refine' ⟨ v_tensor_w, _, h_eigenvector_zero ⟩; + simp [ funext_iff ] at hv hw ⊢ + obtain ⟨left, right⟩ := hv + obtain ⟨left_1, right_1⟩ := hw + exact ⟨left.choose, left_1.choose, mul_ne_zero left.choose_spec left_1.choose_spec⟩ + rw [← IsUnit.neg_iff, neg_sub] + convert h_not_invertible using 4 + simp [ Algebra.smul_def ] + +end spectrum_kron + +open ComplexOrder in +open MatrixOrder in +theorem PosDef.zero_lt {n : Type*} [Nonempty n] [Fintype n] {A : Matrix n n ℂ} (hA : A.PosDef) : 0 < A := by + apply lt_of_le_of_ne + · replace hA := hA.posSemidef + rwa [Matrix.nonneg_iff_posSemidef] + · rintro rfl; exact absurd (hA.diag_pos (i := Classical.arbitrary n)) (by simp) + + +lemma IsHermitian.spectrum_eq_image_eigenvalues [Fintype n] {A : Matrix n n ℂ} (hA : A.IsHermitian) : + spectrum ℝ A = Finset.univ.image hA.eigenvalues := by + simpa using hA.spectrum_real_eq_range_eigenvalues + +/- This lemma looks "wrong" in the sense that it's specifically about `Fintype.card foo = Finset.card bar`, +why not just use the underlying fact `foo = ↑bar`? It turns out this actually gives annoying issues +with dependent rewrites, given the necessary `Fintype` instance. Using the above theorem for example, +trying `rw [hA.spectrum_eq_image_eigenvalues]` fails because of dependent types. -/ +lemma IsHermitian.card_spectrum_eq_image [Fintype n] {A : Matrix n n ℂ} (hA : A.IsHermitian) + [Fintype (spectrum ℝ A)] : + Fintype.card (spectrum ℝ A) = (Finset.univ.image hA.eigenvalues).card := by + trans (Set.univ.image hA.eigenvalues).toFinset.card + · symm + convert Set.toFinset_card _ + rw [Set.image_univ] + exact Matrix.IsHermitian.spectrum_real_eq_range_eigenvalues hA + · simp + +section iInf_iSup +namespace IsHermitian + +variable {d : Type*} [Fintype d] [DecidableEq d] {A B : Matrix d d ℂ} + +open ComplexOrder + +lemma sub_iInf_eignevalues (hA : A.IsHermitian) : + (A - iInf hA.eigenvalues • 1).PosSemidef := by + rw [Matrix.posSemidef_iff_dotProduct_mulVec] + constructor + · simpa [ Matrix.IsHermitian, sub_eq_add_neg ] using hA + · intro x + have h_eigenvalue : ∀ i, hA.eigenvalues i ≥ iInf hA.eigenvalues := by + -- By definition of infimum, for any eigenvalue $i$, we have $hA.eigenvalues i \geq iInf hA.eigenvalues$. + intros i + apply le_of_forall_le + intro j a + exact le_trans a (ciInf_le ( Finite.bddBelow_range hA.eigenvalues ) i ); + -- Since $A$ is Hermitian, we can diagonalize it as $A = Q \Lambda Q^*$, where $Q$ is unitary and $\Lambda$ is diagonal with the eigenvalues on the diagonal. + obtain ⟨Q, Λ, hQ, hΛ⟩ : ∃ Q : Matrix d d ℂ, ∃ Λ : d → ℂ, Q.conjTranspose * Q = 1 ∧ A = Q * Matrix.diagonal Λ * Q.conjTranspose ∧ ∀ i, Λ i = Matrix.IsHermitian.eigenvalues hA i := by + have := hA.spectral_theorem; + refine' ⟨ _, _, _, this, _ ⟩; + · simp [ ← Matrix.ext_iff ]; + intro i j; erw [ Matrix.mul_apply ] ; simp [ Matrix.one_apply ] ; + have := hA.eigenvectorBasis.orthonormal; + rw [ orthonormal_iff_ite ] at this; + rw [← this i j] + simp [PiLp.inner_apply, mul_comm] + · simp + -- Since $Q$ is unitary, we have $Q^* Q = I$, and thus $Q^* (A - \lambda_{\min} I) Q = \Lambda - \lambda_{\min} I$. + have h_diag : Q.conjTranspose * (A - (iInf (Matrix.IsHermitian.eigenvalues hA)) • 1) * Q = Matrix.diagonal (fun i => Λ i - (iInf (Matrix.IsHermitian.eigenvalues hA))) := by + simp [ hΛ, mul_sub, sub_mul, mul_assoc, hQ ]; + simp [ ← mul_assoc, hQ]; + ext i j ; by_cases hij : i = j <;> aesop; + -- Since $Q$ is unitary, we have $Q^* (A - \lambda_{\min} I) Q = \Lambda - \lambda_{\min} I$, and thus $x^* (A - \lambda_{\min} I) x = (Q^* x)^* (\Lambda - \lambda_{\min} I) (Q^* x)$. + have h_quad_form : Star.star x ⬝ᵥ (A - (iInf (Matrix.IsHermitian.eigenvalues hA)) • 1).mulVec x = Star.star (Q.conjTranspose.mulVec x) ⬝ᵥ (Matrix.diagonal (fun i => Λ i - (iInf (Matrix.IsHermitian.eigenvalues hA)))).mulVec (Q.conjTranspose.mulVec x) := by + rw [ ← h_diag ]; + simp [ Matrix.mul_assoc, Matrix.dotProduct_mulVec, mul_eq_one_comm.mp hQ]; + simp only [mulVec_conjTranspose, star_star, vecMul_vecMul]; + rw [ ← Matrix.mul_assoc, mul_eq_one_comm.mp hQ, one_mul ]; + simp_all only [ge_iff_le, dotProduct, Pi.star_apply, RCLike.star_def, mulVec, sub_apply, + smul_apply, Complex.real_smul, conjTranspose_apply, star_sum, star_mul', + RingHomCompTriple.comp_apply, RingHom.id_apply]; + simp_all only [implies_true, and_self, diagonal_apply, ite_mul, zero_mul, Finset.sum_ite_eq, ↓reduceIte]; + -- Since the eigenvalues are real and the sums involving Q and x are complex, the product of a complex number and its conjugate is non-negative. + have h_nonneg : ∀ i, 0 ≤ (∑ x_2, Q x_2 i * star (x x_2)) * (∑ x_2, star (Q x_2 i) * x x_2) := by + intro i + have h_nonneg : 0 ≤ (∑ x_2, Q x_2 i * star (x x_2)) * star (∑ x_2, Q x_2 i * star (x x_2)) := by + exact mul_star_self_nonneg (∑ x_2, Q x_2 i * star (x x_2)) + convert h_nonneg using 1; + simp [ mul_comm, Finset.mul_sum _ _ _]; + -- Since each term in the sum is a product of a non-negative number and a non-negative eigenvalue difference, the entire sum is non-negative. + have h_sum_nonneg : ∀ i, 0 ≤ (∑ x_2, Q x_2 i * star (x x_2)) * (((↑(hA.eigenvalues i) : ℂ) - (↑(iInf hA.eigenvalues) : ℂ)) * ∑ x_2, star (Q x_2 i) * x x_2) := by + intro i + specialize h_nonneg i + simp_all only [mul_assoc, mul_comm, mul_left_comm, RCLike.star_def] ; + rw [ ← mul_assoc ]; + exact mul_nonneg h_nonneg ( sub_nonneg_of_le <| mod_cast h_eigenvalue i ); + convert! Finset.sum_nonneg fun i _ => h_sum_nonneg i; + rw [ hΛ.1 ] + +lemma iInf_eigenvalues_le_dotProduct_mulVec (hA : A.IsHermitian) (v : d → ℂ) : + iInf hA.eigenvalues * (star v ⬝ᵥ v) ≤ star v ⬝ᵥ A *ᵥ v := by + conv_lhs => + equals (star v ⬝ᵥ (iInf hA.eigenvalues • 1) *ᵥ v) => + simp only [dotProduct, Pi.star_apply, RCLike.star_def, mul_comm, mulVec] + simp [Matrix.one_apply, mul_assoc, mul_left_comm, Finset.mul_sum] + rw [← sub_nonneg, ← dotProduct_sub, ← Matrix.sub_mulVec] + replace hA := sub_iInf_eignevalues hA + rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA + exact hA.right v + +lemma iInf_eigenvalues_le_of_posSemidef + (hAB : (B - A).PosSemidef) (hA : A.IsHermitian) (hB : B.IsHermitian) : + iInf hA.eigenvalues ≤ iInf hB.eigenvalues := by + rcases isEmpty_or_nonempty d + · simp + contrapose! hAB + rw [posSemidef_iff_dotProduct_mulVec] + simp only [not_and, not_forall] + intro _ + apply exists_lt_of_ciInf_lt at hAB + rcases hAB with ⟨i, hi⟩ + use WithLp.ofLp (hB.eigenvectorBasis i) + simp only [sub_mulVec, dotProduct_sub, sub_nonneg] + rw [hB.mulVec_eigenvectorBasis i] + simp only [dotProduct_smul, Complex.real_smul] + nth_rw 2 [dotProduct_comm] + rw [← EuclideanSpace.inner_eq_star_dotProduct] + intro h + replace h := (iInf_eigenvalues_le_dotProduct_mulVec hA _).trans h + rw [dotProduct_comm, ← EuclideanSpace.inner_eq_star_dotProduct] at h + simp only [OrthonormalBasis.inner_eq_one, mul_one, Complex.real_le_real] at h + order + +open MatrixOrder in +lemma iInf_eigenvalues_le (hAB : A ≤ B) (hA : A.IsHermitian) (hB : B.IsHermitian) : + iInf hA.eigenvalues ≤ iInf hB.eigenvalues := + iInf_eigenvalues_le_of_posSemidef hAB hA hB + +open MatrixOrder in +lemma iInf_eigenvalues_smul_one_le (hA : A.IsHermitian) : iInf hA.eigenvalues • 1 ≤ A := + (PosSemidef.smul_one_le_of_eigenvalues_iff hA (iInf hA.eigenvalues)).mp + (ciInf_le (Finite.bddBelow_range _)) + +end IsHermitian +end iInf_iSup + +section matrix_order + +--Shortcut instances. Having these around speeds things out considerably, in some cases? +open MatrixOrder + +variable {d : Type*} [Fintype d] + +lemma _shortcut_posSMulMono : PosSMulMono ℝ (Matrix d d ℂ) := + inferInstance + +lemma _shortcut_posSmulReflectLE : PosSMulReflectLE ℝ (Matrix d d ℂ) := + inferInstance + +scoped[MatrixOrder] attribute [instance] Matrix._shortcut_posSMulMono +scoped[MatrixOrder] attribute [instance] Matrix._shortcut_posSmulReflectLE + +end matrix_order + +open ComplexOrder in +theorem IsHermitian.spectrum_subset_Ici_of_sub {d 𝕜 : Type*} [Fintype d] [DecidableEq d] [RCLike 𝕜] + {A x : Matrix d d 𝕜} (hA : A.IsHermitian) (hl : (x - A).PosSemidef) : + spectrum ℝ x ⊆ Set.Ici (⨅ i, hA.eigenvalues i) := by + --Thanks Aristotle + intro μ hμ + obtain ⟨v, hv₁, hv₂⟩ : ∃ v : d → 𝕜, v ≠ 0 ∧ x.mulVec v = μ • v := by + have h_singular : ∃ v : d → 𝕜, v ≠ 0 ∧ (μ • 1 - x).mulVec v = 0 := by + simp only [spectrum.mem_iff, Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero, ne_eq, Decidable.not_not] at hμ + convert Matrix.exists_mulVec_eq_zero_iff.mpr hμ; + simp [Algebra.smul_def] + refine h_singular.imp fun v h ↦ ⟨h.left, ?_⟩ + simp_all [Matrix.sub_mulVec, sub_eq_iff_eq_add, funext_iff, Matrix.mulVec, dotProduct, Matrix.one_apply] + -- Since $x - A$ is positive semidefinite, for any eigenvalue $\lambda$ of $x$, we have $\lambda \geq \min(\text{eigenvalues of } A)$. + have h_lower_bound : ∀ (v : d → 𝕜), v ≠ 0 → (star v ⬝ᵥ (x.mulVec v)) ≥ (⨅ i, (hA.eigenvalues i)) * (star v ⬝ᵥ v) := by + intro v hv_nonzero + have h_eigenvalue : (star v ⬝ᵥ (A.mulVec v)) ≥ (⨅ i, (hA.eigenvalues i)) * (star v ⬝ᵥ v) := by + have h_expand : (star v ⬝ᵥ (A.mulVec v)) = ∑ i, (hA.eigenvalues i) * (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) := by + have h_decomp : A = ∑ i, (hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k)))) := by + convert Matrix.IsHermitian.spectral_theorem hA using 1; + ext i j + simp only [RCLike.star_def, Matrix.smul_of, Matrix.sum_apply, Matrix.of_apply, + Pi.smul_apply, Matrix.diagonal, Function.comp_apply, Matrix.mul_apply, + Matrix.IsHermitian.eigenvectorUnitary_apply, mul_ite, mul_zero, + Finset.sum_ite_eq', Finset.mem_univ, ↓reduceIte, Matrix.star_apply, + Unitary.conjStarAlgAut_apply] + simp [ mul_comm, mul_left_comm, Algebra.smul_def ] + congr! 1 + simp [Algebra.algebraMap_eq_smul_one] + -- Substitute the decomposition of $A$ into the expression $(star v ⬝ᵥ (A.mulVec v))$. + have h_subst : (star v ⬝ᵥ (A.mulVec v)) = ∑ i, (hA.eigenvalues i) * (star v ⬝ᵥ (Matrix.mulVec (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k)))) v)) := by + -- Substitute the decomposition of $A$ into the expression $(star v ⬝ᵥ (A.mulVec v))$ and use the linearity of matrix multiplication. + have h_subst : (star v ⬝ᵥ (A.mulVec v)) = (star v ⬝ᵥ ((∑ i, (hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k))))).mulVec v)) := by + rw [ ← h_decomp ]; + -- By the linearity of matrix multiplication and the dot product, we can distribute the sum over the dot product. + have h_distribute : (star v ⬝ᵥ (∑ i, (hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k))))).mulVec v) = ∑ i, (star v ⬝ᵥ ((hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k))))).mulVec v) := by + -- By the linearity of matrix multiplication and the dot product, we can distribute the sum over the dot product. This follows from the fact that matrix multiplication is linear. + have h_distribute : ∀ (M N : Matrix d d 𝕜) (v : d → 𝕜), Star.star v ⬝ᵥ (M + N).mulVec v = Star.star v ⬝ᵥ M.mulVec v + Star.star v ⬝ᵥ N.mulVec v := by + simp [ Matrix.add_mulVec, dotProduct_add ]; + -- By induction on the number of terms in the sum, we can apply the distributive property repeatedly. + have h_induction : ∀ (n : ℕ) (M : Fin n → Matrix d d 𝕜) (v : d → 𝕜), Star.star v ⬝ᵥ (∑ i, M i).mulVec v = ∑ i, Star.star v ⬝ᵥ (M i).mulVec v := by + intro n M v + induction n + · simp [*] + · simp [Fin.sum_univ_succ, *] + convert h_induction ( Fintype.card d ) ( fun i => Matrix.of ( hA.eigenvalues ( Fintype.equivFin d |>.symm i ) • fun j k => hA.eigenvectorBasis ( Fintype.equivFin d |>.symm i ) j * starRingEnd 𝕜 ( hA.eigenvectorBasis ( Fintype.equivFin d |>.symm i ) k ) ) ) v using 1; + · rw [ ← Equiv.sum_comp ( Fintype.equivFin d ) ]; + simp [ Fintype.equivFin ]; + · rw [ ← Equiv.sum_comp ( Fintype.equivFin d ) ]; + simp [ Fintype.equivFin ]; + convert h_distribute using 1; + simp only [dotProduct, Pi.star_apply, RCLike.star_def, Matrix.mulVec, Matrix.of_apply, + Finset.mul_sum _ _ _, Matrix.smul_apply, Algebra.smul_mul_assoc, + Algebra.mul_smul_comm]; + simp [ Algebra.smul_def ]; + convert h_subst using 2; + simp only [dotProduct, Pi.star_apply, RCLike.star_def, mul_comm, mul_assoc, Matrix.mulVec, + Matrix.of_apply, mul_eq_mul_left_iff, map_eq_zero]; + simp [ mul_comm, mul_left_comm, Finset.mul_sum _ _ _ ]; + -- Since $\lambda_i \geq \inf(\text{eigenvalues of } A)$ for all $i$, we can bound each term in the sum. + have h_bound : ∀ i, (hA.eigenvalues i) * (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) ≥ (⨅ i, (hA.eigenvalues i)) * (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) := by + intro i + have h_eigenvalue_bound : (hA.eigenvalues i) ≥ (⨅ i, (hA.eigenvalues i)) := + ciInf_le (Set.finite_range _).bddBelow _ + -- Since the product of the inner products is real and non-negative, multiplying both sides of the inequality by this product preserves the inequality. + have h_nonneg : 0 ≤ (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) := by + -- Since the inner product is conjugate symmetric, we have star v ⬝ᵥ (hA.eigenvectorBasis i) = conjugate(star (hA.eigenvectorBasis i) ⬝ᵥ v). + have h_conj_symm : star v ⬝ᵥ (hA.eigenvectorBasis i) = star (star (hA.eigenvectorBasis i) ⬝ᵥ v) := by + simp [ dotProduct, mul_comm]; + rw [ h_conj_symm ]; + exact mul_star_self_nonneg (star (hA.eigenvectorBasis i) ⬝ᵥ v); + norm_num [ mul_assoc ]; + exact mul_le_mul_of_nonneg_right ( mod_cast h_eigenvalue_bound ) h_nonneg; + -- Since $\sum_{i} (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) = star v ⬝ᵥ v$, we can factor out $(⨅ i, (hA.eigenvalues i))$ from the sum. + have h_sum : ∑ i, (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) = star v ⬝ᵥ v := by + have h_sum : ∑ i, (star (hA.eigenvectorBasis i) ⬝ᵥ v) • (hA.eigenvectorBasis i) = v := by + have := hA.eigenvectorBasis.sum_repr (WithLp.toLp 2 v); + convert this using 1; + simp only [dotProduct, Pi.star_apply, RCLike.star_def, mul_comm, + hA.eigenvectorBasis.repr_apply_apply, PiLp.inner_apply, RCLike.inner_apply]; + simp only [WithLp.ofLp_sum, WithLp.ofLp_smul] + have key : ∀ (c : d → 𝕜) (f : d → EuclideanSpace 𝕜 d) (w : d → 𝕜), + (∑ x, c x • (f x).ofLp = w) ↔ (∑ x, c x • f x = WithLp.toLp 2 w) := by + intro c f w + conv_lhs => rw [show ∑ x, c x • (f x).ofLp = (∑ x, c x • f x).ofLp from by + rw [WithLp.ofLp_sum]; simp [WithLp.ofLp_smul]] + constructor + · intro h; apply_fun WithLp.toLp 2 at h; simpa using h + · intro h; apply_fun WithLp.ofLp at h; simpa using h + exact key _ _ _ + -- Taking the inner product of both sides of h_sum with star v, we get the desired equality. + have h_inner : star v ⬝ᵥ (∑ i, (star (hA.eigenvectorBasis i) ⬝ᵥ v) • (hA.eigenvectorBasis i)) = star v ⬝ᵥ v := by + congr 1 + simp_rw [← WithLp.ofLp_smul, ← WithLp.ofLp_sum, h_sum] + convert h_inner using 1; + simp [ dotProduct, Finset.mul_sum _ _ _ ]; + exact Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ); + rw [ h_expand ]; + refine' le_trans _ ( Finset.sum_le_sum fun i _ => h_bound i ); + simp only [ mul_assoc]; + rw [ ← Finset.mul_sum _ _ _, h_sum ]; + rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hl + have := hl.2 v + simp [Matrix.sub_mulVec] at this + exact le_trans h_eigenvalue this; + change (⨅ i, hA.eigenvalues i) ≤ μ + have := h_lower_bound v hv₁ + simp_all only [ne_eq, star, RCLike.star_def, Matrix.dotProduct_mulVec, ge_iff_le, + dotProduct_smul]; + simp_all only [dotProduct, mul_comm, RCLike.mul_conj]; + rw [ Algebra.smul_def ] at this; + -- Since the sum of the squares of the norms of v is positive, we can divide both sides of the inequality by it. + have h_sum_pos : 0 < ∑ x : d, (‖v x‖ : ℝ) ^ 2 := by + contrapose! hv₁; + simp_all only [funext_iff, Pi.zero_apply, not_forall, forall_exists_index, Matrix.mulVec, Pi.smul_apply] + intro i + rw [← norm_eq_zero] + simpa [ sq_nonneg ] using le_antisymm ( le_trans ( Finset.single_le_sum ( fun a _ => sq_nonneg ( ‖v a‖ ) ) ( Finset.mem_univ i ) ) hv₁ ) ( sq_nonneg ( ‖v i‖ ) ) + norm_cast at this; + nlinarith + +open ComplexOrder in +theorem IsHermitian.spectrum_subset_Iic_of_sub {d 𝕜 : Type*} [Fintype d] [DecidableEq d] [RCLike 𝕜] + {A x : Matrix d d 𝕜} (hA : A.IsHermitian) (hl : (A - x).PosSemidef) : + spectrum ℝ x ⊆ Set.Iic (⨆ i, hA.eigenvalues i) := by + have h := spectrum_subset_Ici_of_sub hA.neg (x := -x) ?_ + · rcases isEmpty_or_nonempty d + · simp + rw [← spectrum.neg_eq] at h + intro μ hμ + specialize h (Set.neg_mem_neg.mpr hμ) + rw [← Set.mem_neg, Set.neg_Ici] at h + convert! h + rw [iInf, iSup, ← spectrum_real_eq_range_eigenvalues, ← spectrum_real_eq_range_eigenvalues] + rw [← spectrum.neg_eq, csInf_neg ?_ (A.finite_real_spectrum.bddAbove), neg_neg] + exact ContinuousFunctionalCalculus.spectrum_nonempty _ hA + · convert hl using 1 + abel + +open ComplexOrder in +theorem IsHermitian.spectrum_subset_of_mem_Icc {d 𝕜 : Type*} [Fintype d] [DecidableEq d] [RCLike 𝕜] + {A B x : Matrix d d 𝕜} (hA : A.IsHermitian) (hB : B.IsHermitian) + (hl : (x - A).PosSemidef) (hr : (B - x).PosSemidef) : + spectrum ℝ x ⊆ Set.Icc (⨅ i, hA.eigenvalues i) (⨆ i, hB.eigenvalues i) := by + rw [← Set.Ici_inter_Iic] + exact Set.subset_inter (hA.spectrum_subset_Ici_of_sub hl) (hB.spectrum_subset_Iic_of_sub hr) + +/-- +The right partial trace of a matrix is equal to the left partial trace of the matrix reindexed by swapping the tensor factors. +-/ +theorem traceRight_eq_traceLeft_reindex {n m R : Type*} [Fintype m] [AddCommMonoid R] + (M : Matrix (n × m) (n × m) R) : + M.traceRight = (M.reindex (.prodComm ..) (.prodComm ..)).traceLeft := by + rfl + +open ComplexOrder in +theorem PosSemidef.trace_pos {n 𝕜 : Type*} [Fintype n] [RCLike 𝕜] + {A : Matrix n n 𝕜} (hA : A.PosSemidef) (h : A ≠ 0) : 0 < A.trace := by + apply hA.trace_nonneg.lt_of_ne' + classical + rw [hA.left.trace_eq_sum_eigenvalues] + suffices ∑ i, hA.left.eigenvalues i ≠ 0 from mod_cast this + rwa [ne_eq, Fintype.sum_eq_zero_iff_of_nonneg hA.eigenvalues_nonneg, + hA.left.eigenvalues_eq_zero_iff] + +section traceLeftRight + +variable {m α : Type*} [AddCommGroup α] [Fintype m] +omit [DecidableEq n] + +variable {A B : Matrix (m × n) (m × n) α} +@[simp] +theorem traceLeft_add : (A + B).traceLeft = A.traceLeft + B.traceLeft := by + ext : 2 + simp [Matrix.traceLeft, Finset.sum_add_distrib] + +@[simp] +theorem traceLeft_neg : (-A).traceLeft = -A.traceLeft := by + ext : 2; simp [Matrix.traceLeft] + +@[simp] +theorem traceLeft_sub : (A - B).traceLeft = A.traceLeft - B.traceLeft := by + simp [sub_eq_add_neg] + +variable {A B : Matrix (n × m) (n × m) α} + +@[simp] +theorem traceRight_add : (A + B).traceRight = A.traceRight + B.traceRight := by + ext : 2 + simp [Matrix.traceRight, Finset.sum_add_distrib] + +@[simp] +theorem traceRight_neg : (-A).traceRight = -A.traceRight := by + ext : 2; simp [Matrix.traceRight] + +@[simp] +theorem traceRight_sub : (A - B).traceRight = A.traceRight - B.traceRight := by + simp [sub_eq_add_neg] + +variable {R : Type*} [DistribSMul R α] +@[simp] +theorem traceLeft_smul {A : Matrix (m × n) (m × n) α} (r : R) : + (r • A).traceLeft = r • A.traceLeft := by + ext : 2; simp [Matrix.traceLeft, ← Finset.smul_sum] + +@[simp] +theorem traceRight_smul {A : Matrix (n × m) (n × m) α} (r : R) : + (r • A).traceRight = r • A.traceRight := by + ext : 2; simp [Matrix.traceRight, ← Finset.smul_sum] + +end traceLeftRight + +theorem unitaryGroup_row_norm [Fintype n] (U : Matrix.unitaryGroup n ℂ) (i : n) : + ∑ j, ‖U j i‖^2 = 1 := by + suffices ∑ j, ‖U j i‖^2 = (1 : ℂ) by exact_mod_cast this + simpa [Matrix.mul_apply, Complex.sq_norm, Complex.normSq_eq_conj_mul_self] + using congr($(U.prop.left) i i) + +section finprod + +variable {ι : Type*} {d : ι → Type*} [fι : Fintype ι] +variable {R : Type*} + +def piProd [CommMonoid R] (A : ∀ i, Matrix (d i) (d i) R) : Matrix (∀ i, d i) (∀ i, d i) R := + Matrix.of (fun j k : (∀ i, d i) ↦ ∏ i, A i (j i) (k i)) + +variable {A : ∀ i, Matrix (d i) (d i) R} + +theorem IsHermitian.piProd [CommSemiring R] [StarRing R] (hA : ∀ i, (A i).IsHermitian) : + (piProd A).IsHermitian := by + ext j k + simp [Matrix.piProd] + exact Finset.prod_congr rfl fun i _ => by simpa using congr_fun ( congr_fun ( hA i ) ( j i ) ) ( k i ) ; + +variable [DecidableEq ι] [∀ i, Fintype (d i)] --[∀ i, DecidableEq (d i)] + +theorem trace_piProd [CommSemiring R] : + (piProd A).trace = ∏ i, (A i).trace := by + symm + simp [trace, piProd, Fintype.prod_sum] + +open ComplexOrder MatrixOrder in +theorem PosSemidef.piProd [RCLike R] (hA : ∀ i, (A i).PosSemidef) : + (piProd A).PosSemidef := by + -- Let B i be the square root of A i. Let BigB be the pi-product of B i. Show that BigB.conjTranspose * BigB equals the pi-product of A i using Fintype.prod_sum. Then use Matrix.PosSemidef.conjTranspose_mul_self to conclude the proof. + obtain ⟨B, hB⟩ : ∃ B : ∀ i, Matrix (d i) (d i) R, ∀ i, (A i) = B i * star (B i) := by + -- By definition of positive semi-definite matrices, each $A_i$ can be written as $B_i^* B_i$ for some matrix $B_i$. + have h_decomp : ∀ i, ∃ B : Matrix (d i) (d i) R, A i = B * star B := by + intro i + obtain ⟨B, hB⟩ : ∃ B : Matrix (d i) (d i) R, A i = B.conjTranspose * B := by + classical + apply CStarAlgebra.nonneg_iff_eq_star_mul_self.mp + exact nonneg_iff_posSemidef.mpr (hA i) + use B.conjTranspose; + convert hB using 1; + simp [ Matrix.star_eq_conjTranspose ]; + exact ⟨ fun i => Classical.choose ( h_decomp i ), fun i => Classical.choose_spec ( h_decomp i ) ⟩; + have hBigB_conjTranspose_mul_BigB : Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i * star (B i)) (j i) (k i)) = Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i) (j i) (k i)) * star (Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i) (j i) (k i))) := by + ext j k; simp [ Matrix.mul_apply] + simp only [Finset.prod_sum, ← Finset.prod_mul_distrib]; + refine' Finset.sum_bij ( fun p hp => fun i => p i ( Finset.mem_univ i ) ) _ _ _ _ <;> simp +decide; + · simp [ funext_iff ]; + · exact fun b => ⟨ fun i _ => b i, rfl ⟩; + simp only [Matrix.posSemidef_iff_dotProduct_mulVec] at hA ⊢ + simp_all [Matrix.piProd] + constructor + · ext1 + simp [Matrix.mul_apply, mul_comm] + · intro x + set y := star (Matrix.of (fun j k : (∀ i, d i) => ∏ i, B i (j i) (k i))) *ᵥ x + convert dotProduct_star_self_nonneg y using 1 + simp only [dotProduct_mulVec, y]; + simp only [dotProduct, vecMul, Pi.star_apply, RCLike.star_def, mul_comm, star_apply, of_apply, + star_prod]; + simp only [mul_apply, of_apply, star_apply, star_prod, RCLike.star_def, Finset.mul_sum _ _ _, + mul_left_comm, mulVec, dotProduct, mul_comm, map_sum, map_mul, map_prod, + RingHomCompTriple.comp_apply, RingHom.id_apply, mul_assoc]; + exact Finset.sum_congr rfl fun _ _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ) + +end finprod + +--TODO: Can this be used for `Matrix.reindex_eq_conj` cleanup? +theorem submatrix_eq_mul_mul {d d₂ d₃ R : Type*} [DecidableEq d] [Fintype d] [Semiring R] + (A : Matrix d d R) (e : d₂ → d) (f : d₃ → d) : + A.submatrix e f = (submatrix (α := R) 1 e id : Matrix d₂ d R) * A * (submatrix (α := R) 1 id f) := by + rw [show id = Equiv.refl d by rfl, Matrix.mul_submatrix_one, Matrix.one_submatrix_mul] + simp + +open scoped Matrix Kronecker in +/-- +The conjugate of a Kronecker product by a Kronecker product is the Kronecker product of the conjugates (for matrices). +-/ +lemma kronecker_conj_eq {m n p q α : Type*} [CommSemiring α] [StarRing α] [Fintype m] [Fintype n] + (A : Matrix m m α) (B : Matrix n n α) (C : Matrix p m α) (D : Matrix q n α) : + (C ⊗ₖ D) * (A ⊗ₖ B) * (C ⊗ₖ D)ᴴ = (C * A * Cᴴ) ⊗ₖ (D * B * Dᴴ) := by + rw [← Matrix.mul_kronecker_mul] + ext1 + simp only [Matrix.mul_apply, Matrix.kroneckerMap_apply, Matrix.conjTranspose_apply, star_mul'] + simp only [← starRingEnd_apply, mul_comm, Finset.mul_sum, mul_left_comm] + simp only [Finset.sum_mul, mul_assoc, Finset.mul_sum, mul_left_comm] + rw [Fintype.sum_prod_type_right] + +end Matrix diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean new file mode 100644 index 0000000000..0e6dd5b76a --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean @@ -0,0 +1,183 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +public import Mathlib.Order.CompletePartialOrder + +@[expose] public section + +--Can this be rewritten more generally? For `finiteness` to work, I don't know how. +--PR'ed in #33105 +@[aesop (rule_sets := [finiteness]) unsafe apply] +theorem ite_eq_top {α : Type*} [Top α] (h : Prop) [Decidable h] {x y : α} (hx : x ≠ ⊤) (hy : y ≠ ⊤) : + (if h then x else y) ≠ ⊤ := by + split <;> assumption + +section subtype_val_iSup + +/- +When +https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/diamond.20in.20ConditionallyCompleteLattice/near/538053239 +is fixed, the declarations below should be changed to +``` +theorem subtype_val_iSup {ι α : Type*} [ConditionallyCompleteLattice α] {s : Set α} {f : ι → α} + [Inhabited ↑s] [s.OrdConnected] (h : ∀ i, f i ∈ s) : + (⨆ i, (⟨f i, h i⟩ : ↑s)).val = ⨆ i, f i := by + sorry + +theorem subtype_val_iSup' {ι α : Type*} [ConditionallyCompleteLattice α] {s : Set α} {f : ι → α} + [Inhabited ↑s] [s.OrdConnected] (h : ∀ i, f i ∈ s) : + ⨆ i, (⟨f i, h i⟩ : ↑s) = ⟨⨆ i, f i, by sorry⟩ := by + rw [Subtype.eq_iff, subtype_val_iSup] +``` +Sadly, though, there's a "diamond" and we need it with the other data (the one we specify more narrowly +below). +-/ +variable {ι α : Type*} [i : Nonempty ι] [ConditionallyCompleteLattice α] + {f : ι → α} {a b : α} [Fact (a ≤ b)] + +/- This isn't marked as `simp` because rewriting from a sup over a `CompleteLattice` into a +`ConditionallyCompleteLattice` would, pretty often, be undesirable. -/ +theorem subtype_val_iSup (h : ∀ i, f i ∈ Set.Icc a b) : + (⨆ i, (⟨f i, h i⟩ : ↑(Set.Icc a b))).val = ⨆ i, f i := by + simp only [iSup, sSup, Set.range_eq_empty_iff, not_isEmpty_of_nonempty, reduceDIte] + congr 1; ext1 + simp + +theorem subtype_val_iSup' (h : ∀ i, f i ∈ Set.Icc a b) : + ⨆ i, (⟨f i, h i⟩ : ↑(Set.Icc a b)) = + ⟨⨆ i, f i, ⟨(h i.some).1.trans (le_ciSup ⟨b, by intro; grind⟩ _), ciSup_le (h ·|>.2)⟩⟩ := by + rw [Subtype.ext_iff, subtype_val_iSup] + +/- This isn't marked as `simp` because rewriting from a sup over a `CompleteLattice` into a +`ConditionallyCompleteLattice` would, pretty often, be undesirable. -/ +theorem subtype_val_iInf (h : ∀ i, f i ∈ Set.Icc a b) : + (⨅ i, (⟨f i, h i⟩ : ↑(Set.Icc a b))).val = ⨅ i, f i := by + simp only [iInf, sInf, Set.range_eq_empty_iff, not_isEmpty_of_nonempty, reduceDIte] + congr 1; ext1 + simp + +theorem subtype_val_iInf' (h : ∀ i, f i ∈ Set.Icc a b) : + ⨅ i, (⟨f i, h i⟩ : ↑(Set.Icc a b)) = + ⟨⨅ i, f i, ⟨le_ciInf (h ·|>.1), (ciInf_le ⟨a, by intro; grind⟩ _).trans (h i.some).2⟩⟩ := by + rw [Subtype.ext_iff, subtype_val_iInf] + +end subtype_val_iSup + +open scoped ENNReal Topology in +/-- Analogous to `bdd_le_mul_tendsto_zero`, for `ENNReal` (which otherwise lacks a continuous +multiplication function). The product of a sequence that tends to zero with any bounded sequence +also tends to zero. -/ +protected lemma ENNReal.bdd_le_mul_tendsto_zero + {α : Type*} {l : Filter α} {f g : α → ℝ≥0∞} {b : ℝ≥0∞} (hb : b ≠ ⊤) + (hf : l.Tendsto f (𝓝 0)) (hg : ∀ᶠ (x : α) in l, g x ≤ b) : + l.Tendsto (fun x ↦ f x * g x) (𝓝 0) := by + rw [ENNReal.tendsto_nhds_zero] at hf ⊢ + intro ε hεpos + by_cases hb_pos : 0 < b + · filter_upwards [hf (ε / b) (by simp [hb, hεpos.ne']), hg] with x hx₁ hx₂ + grw [hx₁, hx₂, ENNReal.div_mul_cancel hb_pos.ne' hb] + · filter_upwards [hg] with x hx + grind [not_lt, nonpos_iff_eq_zero, mul_zero, zero_le] + +--PULLOUT: Belongs in Mathlib/Algebra/Order/Group/Pointwise/CompleteLattice.lean +-- (after appropriately generalizing to MulPosMono) +open scoped Pointwise in +theorem csInf_mul_nonneg {s t : Set ℝ} + (hs₀ : s.Nonempty) (hs₁ : ∀ x ∈ s, 0 ≤ x) (ht₀ : t.Nonempty) (ht₁ : ∀ x ∈ t, 0 ≤ x) : + sInf (s * t) = sInf s * sInf t := by + apply le_antisymm + · set a := sInf s + set b := sInf t + have h_eps : ∀ ε > 0, ∃ x ∈ s, x < a + ε ∧ ∃ y ∈ t, y < b + ε := by + intro ε ε_pos + obtain ⟨x, hx₁, hx₂⟩ := exists_lt_of_csInf_lt hs₀ (lt_add_of_pos_right a ε_pos) + obtain ⟨y, hy₁, hy₂⟩ := exists_lt_of_csInf_lt ht₀ (lt_add_of_pos_right b ε_pos) + exact ⟨x, hx₁, hx₂, y, hy₁, hy₂⟩ + have h_prod_eps : ∀ ε > 0, ∃ x ∈ s, ∃ y ∈ t, x * y < (a + ε) * (b + ε) := by + intro ε hε + obtain ⟨x, hx₁, hx₂, y, hy₁, hy₂⟩ := h_eps ε hε + exact ⟨x, hx₁, y, hy₁, by nlinarith [hs₁ x hx₁, ht₁ y hy₁]⟩ + have h_lim : Filter.Tendsto (fun ε => (a + ε) * (b + ε)) (nhdsWithin 0 (Set.Ioi 0)) (nhds (a * b)) := by + exact tendsto_nhdsWithin_of_tendsto_nhds (Continuous.tendsto' (by continuity) _ _ (by norm_num)) + apply le_of_tendsto_of_tendsto tendsto_const_nhds h_lim + filter_upwards [self_mem_nhdsWithin] with ε hε + specialize h_prod_eps ε hε + choose x hx y hy using h_prod_eps + refine le_trans ?_ hy.right.le + refine csInf_le ⟨0, ?_⟩ ?_ + · rintro x ⟨u, hu, v, hv, rfl⟩ + exact mul_nonneg (hs₁ u hu) (ht₁ v hv) + · exact ⟨_, hx, _, hy.left, rfl⟩ + · apply le_csInf (hs₀.mul ht₀) + rintro _ ⟨x, hx, y, hy, rfl⟩ + apply mul_le_mul + · exact csInf_le ⟨0, hs₁⟩ hx + · exact csInf_le ⟨0, ht₁⟩ hy + · exact Real.sInf_nonneg ht₁ + · exact hs₁ x hx + +/-- +If two functions from finite types have the same multiset of values, there exists a bijection between the domains that commutes with the functions. +-/ +lemma Multiset.map_univ_eq_iff {α β : Type*} [Fintype α] (f g : α → β) : + Multiset.map f Finset.univ.val = Multiset.map g Finset.univ.val ↔ ∃ (e : α ≃ α), f = g ∘ e := by + apply Iff.intro + · intro a + classical + -- Since these two multisets are equal, their elements must be equal up to permutation. + have h_perm : ∃ e : α ≃ α, ∀ x, f x = g (e x) := by + have h_count_eq : ∀ y : β, Finset.card (Finset.filter (fun x => f x = y) Finset.univ) = Finset.card (Finset.filter (fun x => g x = y) Finset.univ) := by + intro y; + replace a := congr_arg ( fun m => m.count y ) a; + simp_all ( config := { decide := Bool.true } ) [ Multiset.count_map ]; + simp_all [ eq_comm ] + exact a + have h_perm : ∀ y : β, ∃ e : { x : α // f x = y } ≃ { x : α // g x = y }, True := by + intro y + simp_all only [exists_const_iff, and_true] + exact ⟨ Fintype.equivOfCardEq <| by simpa [ Fintype.card_subtype ] using h_count_eq y ⟩; + choose e he using h_perm; + refine' ⟨ _, _ ⟩; + exact ( Equiv.sigmaFiberEquiv f ).symm.trans ( Equiv.sigmaCongrRight e ) |> Equiv.trans <| Equiv.sigmaFiberEquiv g; + intro x + specialize e ( f x ) + rename_i e_1 + simp_all only [implies_true, Equiv.trans_apply, Equiv.sigmaCongrRight_apply, + Equiv.sigmaFiberEquiv_apply] + exact Eq.symm ( e_1 ( f x ) ⟨ x, rfl ⟩ |>.2 ); + exact ⟨ h_perm.choose, funext h_perm.choose_spec ⟩; + · intro a + obtain ⟨w, h⟩ := a + subst h + simp_all only [Function.comp_apply, Finset.univ] + -- Since $w$ is a bijection, the multiset of $w(x)$ for $x$ in the original multiset is just a permutation of the original multiset. + have h_perm : Multiset.map (fun x => w x) (Finset.val Fintype.elems) = Finset.val Fintype.elems := by + exact Multiset.map_univ_val_equiv w; + conv_rhs => rw [ ← h_perm ]; + simp +zetaDelta at * + +/-- +If two functions from finite types have the same multiset of values, there exists a bijection between the domains that commutes with the functions. +-/ +lemma exists_equiv_of_multiset_map_eq {α β γ : Type*} [Fintype α] [Fintype β] [DecidableEq γ] + (f : α → γ) (g : β → γ) (h : Multiset.map f Finset.univ.val = Multiset.map g Finset.univ.val) : + ∃ e : α ≃ β, f = g ∘ e := by + -- Since the multisets of values are equal, the cardinalities of the domains must be equal (as the multiset size is the cardinality of the domain). Thus there exists a bijection `σ : α ≃ β`. + obtain ⟨σ, hσ⟩ : ∃ σ : α ≃ β, Multiset.map f Finset.univ.val = Multiset.map (g ∘ σ) Finset.univ.val := by + have h_card : Fintype.card α = Fintype.card β := by + simpa using congr_arg Multiset.card h; + obtain σ := Fintype.equivOfCardEq h_card + use σ + have h_multiset_eq : Multiset.map g Finset.univ.val = Multiset.map (g ∘ σ) Finset.univ.val := by + rw [ ← Multiset.map_univ_val_equiv σ ] ; + rw [ Multiset.map_map ] + exact h.trans h_multiset_eq; + -- By `Multiset.map_univ_eq_iff`, there exists `e' : α ≃ α` such that `f = (g ∘ σ) ∘ e'`. + obtain ⟨e', he'⟩ : ∃ e' : α ≃ α, f = (g ∘ σ) ∘ e' := by + exact (Multiset.map_univ_eq_iff f (g ∘ ⇑σ)).mp hσ; + exact ⟨ e'.trans σ, by simp_all [ Function.comp ]; grind⟩ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean new file mode 100644 index 0000000000..e58eebf8a2 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean @@ -0,0 +1,13 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ + +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes + +/-! +# Tactic + +Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. +-/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes.lean new file mode 100644 index 0000000000..2dbdca6d0f --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes.lean @@ -0,0 +1,203 @@ +/- +Copyright (c) 2026 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes.Attribute +public import Mathlib.Algebra.BigOperators.Group.List.Basic +public import Mathlib.Algebra.BigOperators.Ring.Finset +public import Mathlib.Algebra.BigOperators.Ring.List +public import Mathlib.Algebra.Group.Action.Defs +public import Mathlib.Algebra.Group.Center +public import Mathlib.Algebra.Group.Commute.Basic +public import Mathlib.Algebra.Group.Commute.Hom +public import Mathlib.Algebra.Group.Commute.Units +public import Mathlib.Algebra.Group.Invertible.Basic +public import Mathlib.Algebra.Group.Opposite +public import Mathlib.Algebra.Group.Pi.Lemmas +public import Mathlib.Algebra.Group.Prod +public import Mathlib.Algebra.GroupWithZero.Commute +public import Mathlib.Algebra.GroupWithZero.Semiconj +public import Mathlib.Algebra.Polynomial.Basic +public import Mathlib.Algebra.Ring.Commute +public import Mathlib.Algebra.Ring.Nat +public import Mathlib.Algebra.Star.SelfAdjoint +public import Mathlib.Data.Int.Cast.Lemmas +public import Mathlib.Data.Matrix.Basic +public import Mathlib.Data.Nat.Cast.Commute +public import Mathlib.Data.Rat.Cast.Defs +public import Mathlib.GroupTheory.GroupAction.Ring +public import Mathlib.LinearAlgebra.Matrix.ZPow + +/-! + +# Tactic for `Commute` + +This tactic uses `aesop` to discharge goals relating to the `Commute` relation. It mostly +tries to do straightforward recursion on expressions, along with some basic normalization +of ring operations. +-/ + +@[expose] public section + +/-- A tactic for proving goals of the `Commute` relation. + +You can use `commutes?` to get a corresponding proof script. +-/ +syntax (name := commutesTac) "commutes" : tactic +macro_rules + | `(tactic| commutes) => `(tactic| + (aesop + (config := { introsTransparency? := some .reducible, terminal := true, + useSimpAll := false, useDefaultSimpSet := false }) + (rule_sets := [$(Lean.mkIdent `Commutes):ident, -default]))) --include `builtin` + +/-- A variant of the `commutes` tactic for proving `Commute` goals that produces +a proof script. -/ +syntax (name := commutesTac?) "commutes?" : tactic +macro_rules + | `(tactic| commutes?) => `(tactic| + (aesop? + (config := { introsTransparency? := some .reducible, terminal := true, + useSimpAll := false, useDefaultSimpSet := false }) + (rule_sets := [$(Lean.mkIdent `Commutes):ident, -default]))) + +attribute [aesop safe apply (rule_sets := [Commutes])] + Commute.all Commute.refl + Commute.one_left Commute.one_right + Commute.pow_self Commute.self_pow Commute.pow_pow_self + Units.commute_coe_inv Units.commute_inv_coe + Commute.self_zpow Commute.zpow_self Commute.zpow_zpow_self + Commute.self_zpow₀ Commute.zpow_self₀ Commute.zpow_zpow_self₀ + Commute.zero_left Commute.zero_right + Commute.neg_one_left Commute.neg_one_right + Nat.cast_commute Nat.commute_cast + Commute.intCast_left Commute.intCast_right + -- Commute.natCast_mul_self Commute.self_natCast_mul --redundant + -- Commute.intCast_mul_self Commute.intCast_mul_self_right --redundant + commute_invOf + NNRat.cast_commute NNRat.commute_cast + Rat.cast_commute Rat.commute_cast + Polynomial.commute_X Polynomial.commute_X_pow + Matrix.commute_diagonal Matrix.scalar_commute + IsStarNormal.star_comm_self + Matrix.Commute.self_zpow Matrix.Commute.zpow_self Matrix.Commute.zpow_zpow_self + +--It's extremely rare that we need to use Commute.symm in the middle of a proof. But it's +--quite common that we specifically need it at the end (or equivalently, at the beginning) +--which is why we give a safe + fast tactic to apply. This often cuts heartbeats by a large +--factor. +attribute [aesop unsafe apply 5% (rule_sets := [Commutes])] Commute.symm + +--This essentially does `symm + assumption`. Note that `assumption` is already a builtin. +add_aesop_rules safe tactic (rule_sets := [Commutes]) (by exact Commute.symm ‹_›) + +--Due to indexing OfNat issues, these don't work as `apply` rules. We add them as a tactic. +add_aesop_rules safe tactic (rule_sets := [Commutes]) (by apply Commute.ofNat_left) +add_aesop_rules safe tactic (rule_sets := [Commutes]) (by apply Commute.ofNat_right) + +--Try to normalize ring operations +add_aesop_rules safe tactic (rule_sets := [Commutes]) (by apply Commute.ofNat_right) + +attribute [aesop unsafe apply 50% (rule_sets := [Commutes])] + Commute.mul_left Commute.mul_right + Commute.pow_left Commute.pow_right --Commute.pow_pow (redundant) + Commute.smul_left Commute.smul_right + Commute.op Commute.unop + Commute.prod + Commute.zpow_left Commute.zpow_right --Commute.zpow_zpow (redundant) + Commute.zpow_left₀ Commute.zpow_right₀ --Commute.zpow_zpow_self₀ (redundant) + Commute.units_of_val Commute.units_val + Commute.units_inv_left Commute.units_inv_right + Commute.units_zpow_left Commute.units_zpow_right + Commute.ringInverse_ringInverse + Commute.inv_left₀ Commute.inv_right₀ + Commute.div_left Commute.div_right + Commute.neg_left Commute.neg_right + Commute.add_left Commute.add_right + Commute.sub_left Commute.sub_right + -- Commute.natCast_mul_left Commute.natCast_mul_right --redundant + -- Commute.intCast_mul_left Commute.intCast_mul_right --redundant + Commute.invOf_left Commute.invOf_right + Commute.inv_left Commute.inv_right --Commute.inv_inv (redundant) + Commute.list_prod_left Commute.list_prod_right + Commute.list_sum_left Commute.list_sum_right + Commute.sum_left Commute.sum_right + Commute.conj + Commute.pi + Commute.map + Commute.star_star --Commute.star_left Commute.star_right + Matrix.Commute.zpow_left Matrix.Commute.zpow_right --Matrix.Commute.zpow_zpow (redundant) + +--TODO: In `Mathlib.Analysis.Normed.Algebra.Exponential`, tag +-- `Commute.exp_left`, `Commute.exp_right`, `Commute.exp` + +--TODO: In cfc-relatated files, tag `Commute.cfc`, `IsSelfAdjoint.commute_cfc`, +-- `Commute.cfc_real`, `cfc_commute_cfc`, `cfcₙ_commute_cfcₙ`, `Commute.expUnitary` + +attribute [aesop simp (rule_sets := [Commutes])] + List.mem_map --Needed for `Commute.list_prod_left` to be interesting + --Do some weak normalization of ring operations. This is similar to the list + -- of lemmas used in `noncomm_ring`. + mul_one one_mul mul_zero zero_mul add_zero zero_add pow_one pow_zero + one_pow zero_pow + +--Chekcing that `commutes` can now prove several other lemmas, which were not part +-- of the set of rules given above. +example : type_of% @Commute.natCast_mul_self := by + commutes + +example : type_of% @Commute.natCast_mul_left := by + commutes + +example : type_of% @Commute.zpow_zpow_self₀ := by + commutes + +example : type_of% @Commute.natCast_mul_natCast_mul := by + commutes + +example : type_of% @Commute.intCast_mul_self := by + commutes + +example : type_of% @Commute.intCast_mul_left := by + commutes + +example : type_of% @Matrix.Commute.zpow_zpow := by + commutes + +--Very basic example: all natural numbers commute. +example (A B : Nat) : Commute A B := by + commutes + +--Check that we can also recognize assumptions stated in equivalent forms +example (R : Type*) [NonUnitalNonAssocSemiring R] (x y : R) (h : SemiconjBy y x x) : + Commute (x + y) x := by + commutes + +example (R : Type*) [Semiring R] (x y : R) (h : x * y * 1 = 0 + y * x) : + Commute (y ^ 2) (x + 1) := by + commutes + +--Example of a more complex goal that it can prove. +example (l : List ℕ) (R : Type*) [Ring R] (x y : R) (h : Commute y x) : + Commute (l.map (fun n ↦ n • x + y ^ n - 37)).prod x := by + commutes --Runs in 5676 heartbeats + +--Produced proof script from `commutes?` for the above goal. Takes 850 heartbeats +example (l : List ℕ) (R : Type*) [Ring R] (x y : R) (h : Commute y x) : + Commute (l.map (fun n ↦ n • x + y ^ n - 37)).prod x := by + apply Commute.list_prod_left + intro x_1 a + simp only [List.mem_map] at * + obtain ⟨w, h_1⟩ := a + obtain ⟨left, right⟩ := h_1 + subst right + apply Commute.sub_left + · apply Commute.add_left + · apply Commute.smul_left + rfl + · apply Commute.pow_left + exact h + · apply Commute.ofNat_left diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes/Attribute.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes/Attribute.lean new file mode 100644 index 0000000000..d673b60a88 --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes/Attribute.lean @@ -0,0 +1,21 @@ +/- +Copyright (c) 2026 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Init +public import Aesop.Frontend.Command + +/-! +# Commutes Rule Set + +This module defines the `Commutes` Aesop rule set which is used by the +`commutes` tactic. Aesop rule sets only become visible once the file in which +they're declared is imported, so we must put this declaration into its own file. +-/ + +@[expose] public section + +declare_aesop_rule_sets [Commutes] (default := false) diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean new file mode 100644 index 0000000000..23e12d87fe --- /dev/null +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean @@ -0,0 +1,256 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +import LeanPool.EuclideanJordan.EuclideanJordan.Block +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner + + +/-! +# Non-vacuity of the Peirce layer, on the Hermitian-matrix carrier + +`EuclideanJordan/Peirce.lean` and `EuclideanJordan/PeirceMul.lean` are stated over an abstract real commutative +Jordan algebra and are *conditional* throughout: every theorem assumes `c ∘ c = c`, and the +sharper ones assume an element with `c ∘ y = ½ y`. Conditional theorems are worth exactly +as much as their hypotheses are satisfiable. This file is the check that they are. + +Four things are verified, and they are different claims. + +**1. The instance stack resolves on `H₂(ℂ)`.** The four typeclass hypotheses of the Peirce +layer — `NonUnitalNonAssocCommRing`, `IsCommJordan`, `Module ℝ`, `IsScalarTower ℝ` — are all +satisfied by `HermitianMat (Fin 2) ℂ` under the scoped Jordan instances of +`EuclideanJordan/Vendor/HermitianMat/Jordan.lean` (namespace `HermMul`). Nothing had to be +built for this: the vendored file carries `IsCommJordan (HermitianMat d 𝕜)` already. +★ This is also why the layer assumes `IsScalarTower ℝ J J` rather than `SMulCommClass`: +they are interchangeable for a commutative product, and `IsScalarTower` is the one the +carrier supplies. + +**2. The `1/2`-eigenspace is not zero.** This is the part that could have gone wrong +silently. Every rule in `EuclideanJordan/PeirceMul.lean` mentioning `J_{1/2}` would be *vacuously true* +on a carrier where the half-space is trivial — and the half-space **is** trivial for the two +idempotents one reaches for first, `0` and `1`. So a witness is exhibited: `cWit` is the +rank-one projection `diag(1,0)`, `xWit` is the off-diagonal `[[0,1],[1,0]]`, and +`cWit_mul_xWit` proves `cWit ∘ xWit = ½ · xWit` with `xWit ≠ 0`. + +**3. `HermitianMat` is formally real** (§4 below), so `EuclideanJordan/FormallyReal.lean`'s no-nilpotents +theorem is live rather than conditional on an uninhabited hypothesis. + +**4. The diagonal matrix units are a complete Jordan frame** (§5 below), witnessing +`IsOrthIdemFamily` — which had **no** carrier here until then, leaving every theorem of +`EuclideanJordan/Frame.lean` and `EuclideanJordan/Block.lean` conditional on a structure nothing was known to satisfy. + +★ **Scope.** This is non-vacuity, not coverage: it shows the Peirce hypotheses have a model +with all three components nonzero, and nothing more. The rank-two carrier is used because it +is the smallest place where a `1/2`-eigenvector exists, not because rank two matters here. +-/ + +open HermMul EuclideanJordan + +namespace EuclideanJordan.Witness + +/-- The rank-two carrier. -/ +abbrev H2 := HermitianMat (Fin 2) ℂ + +/-! ### 1. The instance stack -/ + +noncomputable example : NonUnitalNonAssocCommRing H2 := inferInstance +noncomputable example : IsCommJordan H2 := inferInstance +noncomputable example : Module ℝ H2 := inferInstance +noncomputable example : IsScalarTower ℝ H2 H2 := inferInstance + +/-! ### 2. An idempotent with a nonzero `1/2`-eigenspace -/ + +/-- The rank-one projection `diag(1,0)`. -/ +noncomputable def cWit : H2 := HermitianMat.diagonal ℂ ![1, 0] + +/-- The off-diagonal Hermitian matrix `[[0,1],[1,0]]`. -/ +def xWit : H2 := + ⟨!![0, 1; 1, 0], by + simp only [selfAdjoint.mem_iff, Matrix.star_eq_conjTranspose] + ext i j + fin_cases i <;> fin_cases j <;> simp⟩ + +theorem cWit_mat : cWit.mat = Matrix.diagonal (fun i => ((![1, 0] : Fin 2 → ℝ) i : ℂ)) := rfl + +theorem xWit_mat : xWit.mat = !![0, 1; 1, 0] := rfl + +theorem xWit_ne_zero : xWit ≠ 0 := by + intro h + have h2 : xWit.mat 0 1 = (0 : H2).mat 0 1 := by rw [h] + rw [xWit_mat] at h2 + simp at h2 + +/-- `cWit` is an idempotent for the Jordan product. -/ +theorem cWit_idem : cWit * cWit = cWit := by + apply HermitianMat.ext + rw [mul_eq_symmMul, HermitianMat.symmMul_toMat, cWit_mat] + ext i j + fin_cases i <;> fin_cases j <;> simp <;> norm_num + +/-- **The half-space is inhabited by a nonzero element**, so every `J_{1/2}` rule in +`EuclideanJordan/PeirceMul.lean` has content on this carrier. -/ +theorem cWit_mul_xWit : cWit * xWit = (2 : ℝ)⁻¹ • xWit := by + apply HermitianMat.ext + rw [mul_eq_symmMul, HermitianMat.symmMul_toMat, cWit_mat, xWit_mat] + show _ = (2 : ℝ)⁻¹ • xWit.mat + rw [xWit_mat] + ext i j + fin_cases i <;> fin_cases j <;> simp [Matrix.vecMul_diagonal] + +/-! ### 3. The layer, exercised + +Three theorems of the abstract layer instantiated at the witness. They are corollaries with +no new content — the point is that the hypotheses discharge against a real carrier rather +than remaining hypothetical. -/ + +/-- `eigen_half_mul_half` at the witness: `xWit ∘ xWit` has no `1/2`-component. -/ +theorem witness_half_mul_half : cWit * (cWit * (xWit * xWit)) = cWit * (xWit * xWit) := + eigen_half_mul_half cWit_idem cWit_mul_xWit cWit_mul_xWit + +/-- `peirceHalf_mul_half_eq_zero` at the witness. -/ +theorem witness_peirceHalf_eq_zero : peirceHalf cWit (xWit * xWit) = 0 := + peirceHalf_mul_half_eq_zero cWit_idem cWit_mul_xWit cWit_mul_xWit + +/-- `exists_peirce_decomposition` at the witness: `xWit` itself splits. -/ +theorem witness_decomposition : + ∃ y₁ yₕ y₀ : H2, cWit * y₁ = y₁ ∧ cWit * yₕ = (2 : ℝ)⁻¹ • yₕ ∧ cWit * y₀ = 0 + ∧ xWit = y₁ + yₕ + y₀ := + exists_peirce_decomposition cWit_idem xWit + +/-- **The middle branch of `eigenvalue_trichotomy` is attained.** Without this the +trichotomy would still be true with an empty `1/2` case, and every `J_{1/2}` rule with it. + +★ This statement replaced a genuinely vacuous one, and the replacement is the finding. The +first version here read `(2:ℝ)⁻¹ = 0 ∨ (2:ℝ)⁻¹ = (2:ℝ)⁻¹ ∨ (2:ℝ)⁻¹ = 1`, discharged by +`eigenvalue_trichotomy` at the witness — a disjunction whose middle disjunct is `rfl`, so it +is provable with no witness, no carrier and no Jordan identity. A vacuous statement inside +the very file written to rule out vacuity. -/ +theorem witness_half_attained : ∃ y : H2, y ≠ 0 ∧ cWit * y = (2 : ℝ)⁻¹ • y := + ⟨xWit, xWit_ne_zero, cWit_mul_xWit⟩ + +/-! ### 4. Formal reality on the Hermitian-matrix carrier + +★★★ **This section closes the exposure `EuclideanJordan/FormallyReal.lean` declares in its own docstring**: +that `IsFormallyReal` had no carrier here, so every theorem depending on it was conditional on +an uninhabited hypothesis. + +`HermitianMat d 𝕜` is formally real, and the proof is the classical one: the Jordan square of +a Hermitian matrix is its matrix square, whose trace is the squared Frobenius norm, so a +vanishing sum of squares is a vanishing sum of non-negative reals. + +★ Nothing new is built here — `inner_self_nonneg` and `InnerProductCore.definite` are vendored +(`EuclideanJordan/Vendor/HermitianMat/Inner.lean`) and the Jordan-square identity is +`symmMul_self`. What was +missing was, once again, the *application*. -/ + +section FormallyReal + +variable {d : Type*} [Fintype d] [DecidableEq d] {𝕜 : Type*} [RCLike 𝕜] + +/-- The trace functional is additive along a `Finset` sum. -/ +private theorem inner_sum_left_one {ι : Type*} (t : Finset ι) (g : ι → HermitianMat d 𝕜) : + inner ℝ (∑ j ∈ t, g j) (1 : HermitianMat d 𝕜) + = ∑ j ∈ t, inner ℝ (g j) (1 : HermitianMat d 𝕜) := by + induction t using Finset.cons_induction with + | empty => simp + | cons a t ha ih => rw [Finset.sum_cons, HermitianMat.inner_add_left, ih, Finset.sum_cons] + +/-- The Jordan square has the same trace as the matrix square: `Tr[(A ∘ A)·1] = Tr[A·A]`. -/ +private theorem inner_mul_self_one (A : HermitianMat d 𝕜) : + inner ℝ (A * A) (1 : HermitianMat d 𝕜) = inner ℝ A A := by + rw [HermitianMat.inner_def, HermitianMat.inner_def, mul_eq_symmMul, HermitianMat.symmMul_self] + simp + +/-- **`H_d(𝕜)` is formally real.** -/ +instance instIsFormallyReal : EuclideanJordan.IsFormallyReal (HermitianMat d 𝕜) where + eq_zero_of_sum_mul_self := by + intro ι s f hsum i hi + have h0 : ∑ j ∈ s, inner ℝ (f j * f j) (1 : HermitianMat d 𝕜) = 0 := by + rw [← inner_sum_left_one, hsum] + simp + have h1 : ∑ j ∈ s, inner ℝ (f j) (f j) = 0 := by + rw [← h0] + exact Finset.sum_congr rfl fun j _ => (inner_mul_self_one (f j)).symm + have h2 : inner ℝ (f i) (f i) = 0 := + (Finset.sum_eq_zero_iff_of_nonneg + (fun j _ => HermitianMat.inner_self_nonneg (f j))).mp h1 i hi + exact HermitianMat.InnerProductCore.definite (f i) h2 + +/-- The nilpotence theorem, live on the carrier: a Hermitian matrix with a vanishing +Jordan power is zero. -/ +theorem hermitian_eq_zero_of_jpow_eq_zero {A : HermitianMat d 𝕜} {n : ℕ} + (h : EuclideanJordan.jpow A n = 0) : A = 0 := + EuclideanJordan.eq_zero_of_jpow_eq_zero n h + +/-- Albert's theorem, live on the carrier. -/ +theorem hermitian_jpow_mul_jpow (A : HermitianMat d 𝕜) (m n : ℕ) : + EuclideanJordan.jpow A m * EuclideanJordan.jpow A n + = EuclideanJordan.jpow A (m + n + 1) := + EuclideanJordan.jpow_mul_jpow A m n + +end FormallyReal + +/-! ### 5. A Jordan frame on the carrier + +★★ **This closes the last vacuity exposure in the EJA layer.** `IsOrthIdemFamily` +(`EuclideanJordan/Frame.lean`) had **no witness**, so every theorem of `EuclideanJordan/Frame.lean` and +`EuclideanJordan/Block.lean` was conditional on a structure nothing was known to satisfy — the same +exposure that section 4 closed for `IsFormallyReal`. The diagonal matrix units supply it. + +★ Completeness (`∑ i, p i = 1`) is proved here even though **no theorem in the abstract layer +assumes it** — deliberately, per `EuclideanJordan/Frame.lean`'s docstring, since completeness is what the +spectral theorem produces rather than what the Peirce theory needs. Having it on the carrier +shows the abstract results are not being kept general by weakening past what the intended +model satisfies. -/ + +section JordanFrame + +variable {d : Type*} [Fintype d] [DecidableEq d] + +/-- The diagonal matrix units `E_ii`, as Hermitian matrices. -/ +noncomputable def diagFrame (i : d) : HermitianMat d ℂ := + HermitianMat.diagonal ℂ (fun j => if j = i then 1 else 0) + +omit [Fintype d] in +theorem diagFrame_mat (i : d) : + (diagFrame i).mat = Matrix.diagonal (fun j => ((if j = i then (1 : ℝ) else 0 : ℝ) : ℂ)) := + rfl + +/-- **The diagonal matrix units are a family of orthogonal idempotents.** -/ +theorem diagFrame_orthIdem : + EuclideanJordan.IsOrthIdemFamily (diagFrame (d := d)) where + idem i := by + apply HermitianMat.ext + rw [mul_eq_symmMul, HermitianMat.symmMul_toMat, diagFrame_mat, + Matrix.diagonal_mul_diagonal] + ext a b + by_cases h : a = b <;> simp [Matrix.smul_apply, h] <;> split_ifs <;> norm_num + orth i j hij := by + apply HermitianMat.ext + rw [mul_eq_symmMul, HermitianMat.symmMul_toMat, diagFrame_mat, diagFrame_mat, + Matrix.diagonal_mul_diagonal, Matrix.diagonal_mul_diagonal] + ext a b + by_cases h : a = b <;> simp [Matrix.smul_apply, h] <;> split_ifs with h1 h2 <;> simp_all + +/-- The frame is **complete**: the matrix units sum to the identity. -/ +theorem diagFrame_sum : (∑ i, diagFrame (d := d) i) = 1 := by + have hsum : (∑ i, diagFrame (d := d) i).mat = ∑ i, (diagFrame (d := d) i).mat := by simp + apply HermitianMat.ext + rw [hsum] + ext a b + simp only [Matrix.sum_apply, diagFrame_mat, Matrix.diagonal_apply] + by_cases h : a = b + · subst h; simp [apply_ite ((↑) : ℝ → ℂ)] + · simp [h] + +end JordanFrame + +end EuclideanJordan.Witness diff --git a/LeanPool/EuclideanJordan/FramePeirceSolution.lean b/LeanPool/EuclideanJordan/FramePeirceSolution.lean new file mode 100644 index 0000000000..54f93eb76d --- /dev/null +++ b/LeanPool/EuclideanJordan/FramePeirceSolution.lean @@ -0,0 +1,338 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.Algebra.DirectSum.Module +import Mathlib.Analysis.InnerProductSpace.Basic +import Mathlib.Data.Sym.Sym2 +import Mathlib.LinearAlgebra.FiniteDimensional.Defs +import Mathlib.LinearAlgebra.Dimension.Finrank +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul + + +/-! +# The Peirce decomposition of a Euclidean Jordan algebra relative to a Jordan frame + +A **Euclidean Jordan algebra** is a real inner-product space `J` carrying a commutative bilinear +product `∘` with unit `1`, satisfying the Jordan identity `x ∘ (x² ∘ y) = x² ∘ (x ∘ y)` and the +associativity of the inner product `⟪x ∘ y, z⟫ = ⟪y, x ∘ z⟫` (Faraut–Korányi, *Analysis on +Symmetric Cones*, Definition III.1.1). `EuclideanJordanAlgebra` below is exactly that, written +as a class. + +An idempotent `c` (`c ∘ c = c`) is **primitive** when it is nonzero and cannot be split: the only +idempotents `d` of the Peirce subalgebra `J₂(c) = {x | c ∘ x = x}` are `0` and `c`. A **Jordan +frame** is a family `p₁, …, pₙ` of pairwise-orthogonal (`pᵢ ∘ pⱼ = 0` for `i ≠ j`) primitive +idempotents that is complete (`∑ᵢ pᵢ = 1`). + +Left multiplication `L_c : x ↦ c ∘ x` by an idempotent is diagonalisable with eigenvalues `1`, +`½` and `0`; that is the Peirce decomposition at a single idempotent. For a Jordan frame the +operators `L_{p₁}, …, L_{pₙ}` are simultaneously diagonalisable, and the surviving joint +eigenspaces — the **blocks** — are indexed by *unordered pairs* of indices: + +* `V_{ii} := {x | pᵢ ∘ x = x}`, the `1`-eigenspace of `L_{pᵢ}`; +* `V_{ij} := {x | pᵢ ∘ x = ½ • x ∧ pⱼ ∘ x = ½ • x}` for `i ≠ j`, the joint `½`-eigenspace of + `L_{pᵢ}` and `L_{pⱼ}`. + +This file states the two theorems that turn that list of subspaces into a decomposition of `J`: + +1. **`frameBlock_isInternal`** — the blocks are an internal direct sum, `J = ⨁_{i ≤ j} V_{ij}`. + Equivalently: they are independent and they span. +2. **`finrank_frameBlock_diag`** — each diagonal block is a line, `dim_ℝ V_{ii} = 1`. (It is in + fact `ℝ ∙ pᵢ`, but the dimension is the form the coordinatization consumes.) + +Together these are Faraut–Korányi Theorem IV.2.1. They are the starting point of the +Jordan–von Neumann–Wigner classification: once `J = ⨁_{i ≤ j} V_{ij}` with one-dimensional +diagonal, the off-diagonal blocks `V_{ij}` all carry a common composition algebra structure and +`J` is recognised as a matrix algebra `H_n(K)`. The multiplication table of the blocks +(`V_{ij} ∘ V_{jk} ⊆ V_{ik}`, `V_{ij} ∘ V_{kl} = 0` for disjoint index pairs, and so on) is the +next step and is *not* stated here. + +## Why the blocks are indexed by `Sym2 (Fin n)` + +`V_{ij}` and `V_{ji}` are literally the same subspace — the defining conditions are a conjunction +that is symmetric in `i` and `j`, and the eigenvalue `blockCoef i j` is symmetric too. So the +honest index set for the family is the type of *unordered* pairs `Sym2 (Fin n)`, and the direct +sum runs over it with no double counting. That is what "`⨁_{i ≤ j}`" means in the informal +statement, and using `Sym2` rather than `{q : Fin n × Fin n // q.1 ≤ q.2}` avoids having to +choose a representative. `frameBlockRaw` is the ordered-pair family, `frameBlockRaw_comm` is its +symmetry, and `frameBlock` is the descent of the former along the latter; `frameBlock_mk` +(`frameBlock F s(i, j) = frameBlockRaw F i j`, by `rfl`) is the bridge a reader should use to see +what the statement says at a concrete pair of indices. + +`DirectSum.IsInternal (frameBlock F)` is Mathlib's predicate saying that the canonical map +`⨁_{s : Sym2 (Fin n)} V_s → J` is bijective — that is, independence *and* spanning, which is the +full strength of the decomposition and not merely the spanning half. + +## What is and is not assumed + +Assumed for both theorems: `J` is a real inner-product space (`NormedAddCommGroup` plus +`InnerProductSpace ℝ`) carrying `EuclideanJordanAlgebra`, and `F : JordanFrame J n` is a Jordan +frame of some cardinality `n`, carried as **data**. + +★ **Finite-dimensionality is assumed only for the second theorem.** `frameBlock_isInternal` +holds with no dimension hypothesis at all; `finrank_frameBlock_diag` takes +`[FiniteDimensional ℝ J]`, because the proof that a primitive idempotent's Peirce subalgebra is a +line runs the spectral theorem inside that subalgebra, and the spectral theorem is false without +the dimension hypothesis (`ℝ[X]` satisfies every other hypothesis with no nonconstant +resolution). + +★ **The inner product is an arbitrary associative one, not the trace form.** Faraut–Korányi fix +`⟪x, y⟫ = tr(x ∘ y)`; the class below asks only that *some* positive-definite associative inner +product exist. That is the weaker hypothesis, so the theorems below are the stronger statements. +Positive-definiteness is not stated as a field: it is already part of `InnerProductSpace ℝ J`. +Formal reality is not a hypothesis either — it follows from the associativity of the inner +product, by pairing a vanishing sum of squares against `1`. + +★ **Primitivity is a formal hypothesis of both theorems; only the second one spends it.** +`JordanFrame` carries primitivity, so `frameBlock_isInternal` assumes it formally even though its +proof never uses it. The stronger statement — that the blocks of a merely orthogonal complete +idempotent family already decompose `J` — is **not formalized here**. It is +`dim V_{ii} = 1` that cashes primitivity out, and that is why the two theorems are stated +together: the decomposition is useless for classification without the one-dimensionality. + +★ **No claim is made about the rank of `J`.** A frame is carried as data of a given cardinality +`n`; that `n` equals the rank of `J`, or that all frames have the same cardinality +(Faraut–Korányi IV.2.5, conjugacy of frames), is neither assumed nor concluded here. Do not read +`frameBlock_isInternal` as a statement about `rank J`. + +Not assumed: no associativity or power-associativity as a hypothesis, no simplicity, no +classification, no identification of `J` with a matrix algebra, no ordered-space structure, no +continuity beyond what the norm gives for free, and no `n ≥ 3`. + +## The vocabulary used here + +Everything the two statements mention is defined below from Mathlib alone: the class +`EuclideanJordanAlgebra`, the predicates `IsOrthIdemFamily` and `IsPrimitive`, the structure +`JordanFrame`, the eigenspace `eigSub`, the eigenvalue `blockCoef`, and the block families +`frameBlockRaw` and `frameBlock`. Nothing else is imported beyond core Mathlib. + +## This file + +Repeats the definitions and the two theorem statements of `FramePeirceChallenge.lean` verbatim, +imports the reference library, and discharges them from `EuclideanJordan.frameBlock_isInternal` +(`EuclideanJordan/FramePeirce.lean`) and `EuclideanJordan.finrank_frameBlock_diag` +(`EuclideanJordan/FramePeirceMul.lean`). + +The bridge is short by construction. The local class `EuclideanJordanAlgebra` carries the same +fields as `EuclideanJordan.EuclideanJordanAlgebra`, so an instance of the library's class is +assembled from ours field by field, with `toMul` and `toOne` taken from ours — which is what +makes the two `*` and the two `1` the *same* operations rather than merely isomorphic ones. The +local `IsOrthIdemFamily`, `IsPrimitive`, `JordanFrame`, `eigSub`, `blockCoef`, `frameBlockRaw` +and `frameBlock` are then definitionally the library's, so each proof is a single `exact` once +the instance and the frame have been transported. + +★ The library instance is introduced *inside* the proof bodies with `let`, never at the top +level of this file. That is deliberate. An ambient `EuclideanJordan.EuclideanJordanAlgebra J` +brings its derived `NonUnitalNonAssocCommRing J` into scope, and `Submodule ℝ J` would then +elaborate its `AddCommMonoid J` argument through the ring rather than through the norm; the +resulting type is definitionally equal to, but not syntactically the same as, the one the +challenge file states. The contract here is that the two files' declaration types agree on the +nose, so the extra instance is kept out of every statement. +-/ + +noncomputable section + +namespace JordanFramePeirce + +/-- A **Euclidean Jordan algebra**: a real inner-product space carrying a commutative bilinear +product with unit, satisfying the Jordan identity and the associativity of the inner product. + +This is Faraut–Korányi's definition (FK III.1.1) with two deliberate weakenings, both of which +make the theorems below *stronger*: finite-dimensionality is not a field (it is carried as a +separate `[FiniteDimensional ℝ J]` argument exactly where it is needed), and the inner product is +an arbitrary associative one rather than the Jordan trace form. + +Distributivity and homogeneity are stated on the left only; commutativity supplies the right-hand +versions. -/ +class EuclideanJordanAlgebra (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + extends Mul J, One J where + /-- The Jordan product is commutative. -/ + mul_comm : ∀ x y : J, x * y = y * x + /-- The Jordan product is additive in its left argument. -/ + add_mul : ∀ x y z : J, (x + y) * z = x * z + y * z + /-- The Jordan product is homogeneous in its left argument. -/ + smul_mul : ∀ (r : ℝ) (x y : J), (r • x) * y = r • (x * y) + /-- `1` is a unit for the Jordan product. -/ + one_mul : ∀ x : J, (1 : J) * x = x + /-- The Jordan identity, `x ∘ (x² ∘ y) = x² ∘ (x ∘ y)`. -/ + jordan : ∀ x y : J, x * ((x * x) * y) = (x * x) * (x * y) + /-- The inner product is associative: `⟪x ∘ y, z⟫ = ⟪y, x ∘ z⟫`. This is what "Euclidean" + adds to "formally real". -/ + inner_assoc : ∀ x y z : J, inner ℝ (x * y) z = inner ℝ y (x * z) + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] + +namespace EuclideanJordanAlgebra + +/-- Left multiplication by `0` is `0` — the one ring axiom the class does not state, obtained +from additivity at `(0, 0, a)`. -/ +theorem zero_mul' (a : J) : (0 : J) * a = 0 := by + have h : (0 : J) * a + (0 : J) * a = (0 : J) * a + 0 := by + rw [add_zero, ← add_mul, add_zero] + exact add_left_cancel h + +theorem mul_zero' (a : J) : a * (0 : J) = 0 := by rw [mul_comm, zero_mul'] + +theorem mul_add' (a x y : J) : a * (x + y) = a * x + a * y := by + rw [mul_comm a (x + y), add_mul, mul_comm x a, mul_comm y a] + +theorem mul_smul' (r : ℝ) (a x : J) : a * (r • x) = r • (a * x) := by + rw [mul_comm a (r • x), smul_mul, mul_comm x a] + +end EuclideanJordanAlgebra + +/-! ## Orthogonal idempotent families, primitivity, and Jordan frames -/ + +/-- A family of pairwise-orthogonal idempotents. Completeness is deliberately *not* part of this +predicate; it is a separate field of `JordanFrame`. -/ +structure IsOrthIdemFamily {n : ℕ} (p : Fin n → J) : Prop where + /-- Each member is idempotent. -/ + idem : ∀ i, p i * p i = p i + /-- Distinct members are orthogonal. -/ + orth : ∀ i j, i ≠ j → p i * p j = 0 + +/-- A **primitive idempotent**: a nonzero idempotent that cannot be split, i.e. the only +idempotents of the Peirce subalgebra `J₂(c) = {x | c ∘ x = x}` are `0` and `c` itself. + +The third clause is stated in the ambient algebra — `d` idempotent with `c ∘ d = d`, which is +membership in `J₂(c)` — rather than over a subtype, so that it can be checked without first +producing the subalgebra. -/ +def IsPrimitive (c : J) : Prop := + c * c = c ∧ c ≠ 0 ∧ ∀ d : J, d * d = d → c * d = d → d = 0 ∨ d = c + +/-- A **Jordan frame**: a complete family of pairwise-orthogonal primitive idempotents. + +Carried as data, indexed by `Fin n`, so that its cardinality is available without any +well-definedness theorem. In particular `n` is *not* asserted to be the rank of `J`. -/ +structure JordanFrame (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + [EuclideanJordanAlgebra J] (n : ℕ) where + /-- The idempotents. -/ + p : Fin n → J + /-- They are idempotent and pairwise orthogonal. -/ + orthIdem : IsOrthIdemFamily p + /-- Each is primitive. -/ + primitive : ∀ i, IsPrimitive (p i) + /-- They sum to the unit. -/ + complete : ∑ i, p i = 1 + +/-! ## The blocks -/ + +/-- The `r`-eigenspace of `L_a : x ↦ a ∘ x`, as a submodule. -/ +def eigSub (a : J) (r : ℝ) : Submodule ℝ J where + carrier := {x : J | a * x = r • x} + add_mem' := fun {u v} hu hv => by + change a * (u + v) = r • (u + v) + rw [EuclideanJordanAlgebra.mul_add', hu, hv, smul_add] + zero_mem' := by + change a * 0 = r • (0 : J) + rw [EuclideanJordanAlgebra.mul_zero', smul_zero] + smul_mem' := fun t x hx => by + change a * (t • x) = r • (t • x) + rw [EuclideanJordanAlgebra.mul_smul', hx, smul_comm] + +@[simp] theorem mem_eigSub {a : J} {r : ℝ} {x : J} : x ∈ eigSub a r ↔ a * x = r • x := Iff.rfl + +variable {n : ℕ} + +/-- The eigenvalue attached to the pair `(i, j)`: `1` on the diagonal, `½` off it. -/ +def blockCoef (i j : Fin n) : ℝ := if i = j then 1 else (2 : ℝ)⁻¹ + +theorem blockCoef_comm (i j : Fin n) : blockCoef i j = blockCoef j i := by + unfold blockCoef + by_cases h : i = j + · simp [h] + · simp [h, Ne.symm h] + +/-- `V_{ij}` before it is pushed through `Sym2`: the joint `blockCoef i j`-eigenspace of `L_{pᵢ}` +and `L_{pⱼ}`. On the diagonal this is `J₂(pᵢ) = {x | pᵢ ∘ x = x}`; off it, the joint +`½`-eigenspace. -/ +def frameBlockRaw (F : JordanFrame J n) (i j : Fin n) : Submodule ℝ J := + eigSub (F.p i) (blockCoef i j) ⊓ eigSub (F.p j) (blockCoef i j) + +theorem frameBlockRaw_comm (F : JordanFrame J n) (i j : Fin n) : + frameBlockRaw F i j = frameBlockRaw F j i := by + unfold frameBlockRaw + rw [blockCoef_comm i j, inf_comm] + +/-- **`V_{ij}`**, indexed by unordered pairs. For `i ≠ j` the joint `½`-eigenspace of `L_{pᵢ}` +and `L_{pⱼ}`; on the diagonal, `J₂(pᵢ)`. -/ +def frameBlock (F : JordanFrame J n) : Sym2 (Fin n) → Submodule ℝ J := + Sym2.lift ⟨frameBlockRaw F, frameBlockRaw_comm F⟩ + +@[simp] theorem frameBlock_mk (F : JordanFrame J n) (i j : Fin n) : + frameBlock F s(i, j) = frameBlockRaw F i j := rfl + +theorem mem_frameBlock_diag {F : JordanFrame J n} {i : Fin n} {x : J} : + x ∈ frameBlock F s(i, i) ↔ F.p i * x = x := by + simp [frameBlockRaw, blockCoef] + +theorem mem_frameBlock_off {F : JordanFrame J n} {i j : Fin n} (hij : i ≠ j) {x : J} : + x ∈ frameBlock F s(i, j) ↔ F.p i * x = (2 : ℝ)⁻¹ • x ∧ F.p j * x = (2 : ℝ)⁻¹ • x := by + simp [frameBlockRaw, blockCoef, hij] + +/-! ## The two theorems -/ + +/-- **The frame Peirce decomposition: `J = ⨁_{i ≤ j} V_{ij}`.** + +For a Jordan frame `p₁, …, pₙ` of a Euclidean Jordan algebra `J`, the blocks `V_{ij}` — indexed +by unordered pairs, so that `V_{ij}` and `V_{ji}` are counted once — form an internal direct sum +decomposition of `J`: the canonical map `⨁_{s : Sym2 (Fin n)} V_s → J` is bijective. That is +independence *and* spanning. + +Reference: J. Faraut and A. Korányi, *Analysis on Symmetric Cones*, Oxford 1994, Theorem IV.2.1. + +No dimension hypothesis is needed. Primitivity remains a *formal hypothesis* of this statement — +it is carried by `JordanFrame` — but the proof does not spend it: see the module docstring. -/ +theorem frameBlock_isInternal (F : JordanFrame J n) : DirectSum.IsInternal (frameBlock F) := by + let _lib : EuclideanJordan.EuclideanJordanAlgebra J := + { toMul := inferInstance + toOne := inferInstance + mul_comm := EuclideanJordanAlgebra.mul_comm + add_mul := EuclideanJordanAlgebra.add_mul + smul_mul := EuclideanJordanAlgebra.smul_mul + one_mul := EuclideanJordanAlgebra.one_mul + jordan := EuclideanJordanAlgebra.jordan + inner_assoc := EuclideanJordanAlgebra.inner_assoc } + exact EuclideanJordan.frameBlock_isInternal + { p := F.p + orthIdem := ⟨F.orthIdem.idem, F.orthIdem.orth⟩ + primitive := F.primitive + complete := F.complete } + +/-- **The diagonal blocks are lines: `dim V_{ii} = 1`.** + +This is where primitivity of the frame's members is spent, and where finite-dimensionality is +needed. `V_{ii}` is the Peirce subalgebra `J₂(pᵢ)`, which is itself a Euclidean Jordan algebra +with unit `pᵢ`; the spectral theorem inside it writes every element as a real combination of +idempotents of `J₂(pᵢ)`, and primitivity says each of those is `0` or `pᵢ`. So +`V_{ii} = ℝ ∙ pᵢ`, and `pᵢ ≠ 0`. + +Reference: J. Faraut and A. Korányi, *Analysis on Symmetric Cones*, Oxford 1994, Theorem IV.2.1. + +★ This is a statement about one block of a frame carried as data. It is *not* a statement about +`rank J`, and nothing here converts it into one. -/ +theorem finrank_frameBlock_diag [FiniteDimensional ℝ J] (F : JordanFrame J n) (i : Fin n) : + Module.finrank ℝ ↥(frameBlock F s(i, i)) = 1 := by + let _lib : EuclideanJordan.EuclideanJordanAlgebra J := + { toMul := inferInstance + toOne := inferInstance + mul_comm := EuclideanJordanAlgebra.mul_comm + add_mul := EuclideanJordanAlgebra.add_mul + smul_mul := EuclideanJordanAlgebra.smul_mul + one_mul := EuclideanJordanAlgebra.one_mul + jordan := EuclideanJordanAlgebra.jordan + inner_assoc := EuclideanJordanAlgebra.inner_assoc } + exact EuclideanJordan.finrank_frameBlock_diag + { p := F.p + orthIdem := ⟨F.orthIdem.idem, F.orthIdem.orth⟩ + primitive := F.primitive + complete := F.complete } i + +end JordanFramePeirce diff --git a/LeanPool/EuclideanJordan/KoecherSolution.lean b/LeanPool/EuclideanJordan/KoecherSolution.lean new file mode 100644 index 0000000000..629e8ff4df --- /dev/null +++ b/LeanPool/EuclideanJordan/KoecherSolution.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.Algebra.Module.LinearMap.Defs +import Mathlib.Analysis.Normed.Module.Basic +import Mathlib.LinearAlgebra.FiniteDimensional.Defs +import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto + + +/-! +# Solution: Koecher / Alfsen–Shultz, a unital linear order isomorphism is a Jordan automorphism + +Let `J` be a finite-dimensional formally real (Euclidean) Jordan algebra with unit `e`, ordered by +its cone of sums of squares. If a linear bijection `Φ : J ≃ₗ[ℝ] J` fixes `e` and preserves that +cone **in both directions**, then `Φ` preserves the Jordan product. This is the classical theorem +of Koecher; see Alfsen–Shultz, *Geometry of State Spaces*, Theorem 2.80. + +The content is that a Euclidean Jordan algebra's *order* determines its *multiplication*: the +order-automorphism group of the cone that fix the unit is exactly the Jordan-automorphism group. +It is the algebraic core of the Koecher–Vinberg circle of ideas, and the step by which +order-theoretic hypotheses in quantum foundations become algebraic ones. + +## The vocabulary used here + +Everything the statement mentions is defined below from Mathlib alone. In particular **no Jordan +ring instance is used**: the multiplication is a bundled `ℝ`-bilinear map `m : J →ₗ[ℝ] J →ₗ[ℝ] J`, +and commutativity, the Jordan identity, formal reality and unitality are ordinary hypotheses +stated in terms of `m`. Likewise **no order instance is used**: the cone is the predicate `IsSoS` +defined below, and `Φ`'s order-compatibility is the biconditional `horder`. + +Consequently the statement elaborates against a bare `NormedAddCommGroup`/`Module ℝ`/`Module.Finite` +carrier, and a reader can check that it says what it should without consulting any library. + +## What is and is not assumed + +Assumed: `m` is `ℝ`-bilinear (by type), commutative (`hcomm`), satisfies the Jordan identity +`(a ∘ b) ∘ (a ∘ a) = a ∘ (b ∘ (a ∘ a))` (`hjordan`), is formally real in the finite-sum sense +(`hfr`: a finite sum of squares vanishes only if every summand's argument is `0`), and has `e` as a +two-sided unit (`he`, which is one-sided only because `m` is commutative). `J` is finitely +generated as an `ℝ`-module. `Φ` is `ℝ`-linear and bijective **by type**, fixes `e`, and satisfies +`IsSoS m x ↔ IsSoS m (Φ x)` for every `x`. + +★ The **biconditional** in `horder` is load-bearing and is not a convenience. The order-theoretic +characterisation of idempotents used in the proof (`c` is idempotent iff `0 ≤ c ≤ e` and no nonzero +cone element lies below both `c` and `e - c`) contains a universal quantifier over the cone, and +transporting that clause along `Φ⁻¹` consumes the reflecting direction. A one-directional +hypothesis `IsSoS m x → IsSoS m (Φ x)` is genuinely weaker. + +Not assumed: no inner product, no trace form, no continuity or boundedness of `Φ`, no associativity +or power-associativity as a hypothesis, no positive-definiteness beyond `hfr`, no `OrderedSpace` +structure, no simplicity, no classification, and no identification of `J` with a matrix algebra. + +★ **The norm is never used.** `[NormedAddCommGroup J]` appears only so that this statement matches +the one proved in the reference library, whose finite-dimensionality plumbing is set up over a +normed carrier; it is an extra hypothesis, so it makes the theorem below weaker rather than +stronger, and the argument does not touch it. + +★ This is **not** the van Imhoff–Roelands theorem (arXiv:1904.09278), which works in JB-generality +and *concludes* linearity from order-isomorphy. Here `Φ` is linear by type, and that is the whole +difference. + +## This file + +Repeats the definition and the theorem statement of `KoecherChallenge.lean` verbatim, imports the +reference library, and discharges the theorem from `EuclideanJordan.orderIso_preservesJordan`. The +local `IsSoS` is the same existential as `EuclideanJordan.IsSoS`, so the bridge is definitional. +-/ + +namespace KoecherAlfsenShultz + +open Finset + +/-- **The positive cone**: `z` is a finite sum of squares of the bilinear product `m`. + +The sums-of-squares reading, rather than the single-square reading, is what makes this usable as a +*definition*: closure under addition is a concatenation of index sets, whereas closure of the +single-square set under addition is a theorem requiring the spectral decomposition. Over a +Euclidean Jordan algebra the two predicates coincide, but that is a result, not a convention. + +The empty sum is allowed (`k = 0`), so `0` lies in the cone. -/ +def IsSoS {J : Type*} [AddCommGroup J] [Module ℝ J] (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (z : J) : Prop := + ∃ (k : ℕ) (f : Fin k → J), z = ∑ i, m (f i) (f i) + +variable {J : Type*} [NormedAddCommGroup J] [Module ℝ J] [Module.Finite ℝ J] + +/-- **Koecher / Alfsen–Shultz.** On a finite-dimensional formally real Jordan algebra, a linear +bijection that fixes the unit and preserves the cone of sums of squares in both directions +preserves the Jordan product — that is, it is a Jordan automorphism. + +The hypotheses, in order: `hcomm` and `hjordan` make the bilinear map `m` a Jordan multiplication; +`hfr` is formal reality (`∑ᵢ m (f i) (f i) = 0 → ∀ i, f i = 0`), which together with finite +dimensionality makes `J` Euclidean; `he` says `e` is the unit; `hunital` and `horder` say `Φ` is a +unital order isomorphism for the cone `IsSoS m`. The conclusion `Φ (m x y) = m (Φ x) (Φ y)` holds +for all `x y : J`. + +Reference: M. Koecher; see also E. M. Alfsen and F. W. Shultz, *Geometry of State Spaces of +Operator Algebras*, Birkhäuser 2003, Theorem 2.80. + +For what is and is not assumed — in particular why the biconditional in `horder` cannot be weakened +to an implication, and why the norm on `J` is inert — see the module docstring above. -/ +theorem orderIso_preservesJordan (m : J →ₗ[ℝ] J →ₗ[ℝ] J) + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (e : J) (he : ∀ y : J, m e y = y) + (Φ : J ≃ₗ[ℝ] J) (hunital : Φ e = e) + (horder : ∀ x : J, IsSoS m x ↔ IsSoS m (Φ x)) (x y : J) : + Φ (m x y) = m (Φ x) (Φ y) := + EuclideanJordan.orderIso_preservesJordan m hcomm hjordan hfr e he Φ hunital horder x y + +end KoecherAlfsenShultz diff --git a/LeanPool/EuclideanJordan/SpectralSolution.lean b/LeanPool/EuclideanJordan/SpectralSolution.lean new file mode 100644 index 0000000000..71575b57e7 --- /dev/null +++ b/LeanPool/EuclideanJordan/SpectralSolution.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import Mathlib.Analysis.InnerProductSpace.Basic +import Mathlib.LinearAlgebra.FiniteDimensional.Defs +import Mathlib.LinearAlgebra.BilinearMap +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.Data.Real.Basic +import LeanPool.EuclideanJordan.EuclideanJordan.Spectral + +/-! +# Solution: the single-element spectral theorem for a Euclidean Jordan algebra + +Repeats the statement of `SpectralChallenge.lean` verbatim and discharges it from +`EuclideanJordan.spectral_resolution_bilinear` in `EuclideanJordan/Spectral.lean`. + +The delegation is a single application because the library theorem is stated in exactly this +vocabulary: the product enters as a bundled bilinear map, so no Jordan-algebra instance has to +exist before the statement elaborates. The instances the proof needs (a non-unital commutative +ring on `J`, `IsCommJordan`, `IsScalarTower ℝ J J`, `IsFormallyReal J`) are built inside the +library from `m`, `hcomm`, `hjordan` and `hfr`, and none of them escapes into the statement. +-/ + +namespace JordanSpectral + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] + +/-- **The single-element spectral theorem for a Euclidean Jordan algebra.** + +Let `J` be a finite-dimensional real vector space and `m : J →ₗ[ℝ] J →ₗ[ℝ] J` a bilinear +product on it which is commutative (`hcomm`), satisfies the Jordan identity (`hjordan`), is +formally real (`hfr` : a vanishing sum of squares has vanishing summands), and has a unit `e` +(`he`). Then every `x : J` admits a **spectral resolution**: a finite family of idempotents +`q i` for `m`, pairwise orthogonal, summing to the unit, with `x` a real combination of them. + +This is Theorem III.1.1 of J. Faraut and A. Koranyi, *Analysis on Symmetric Cones*, Oxford +University Press (1994); the underlying classification is P. Jordan, J. von Neumann and +E. Wigner, *On an algebraic generalization of the quantum mechanical formalism*, Ann. of Math. +35 (1934) 29-64. + +Everything the statement mentions is Mathlib: bilinear maps, `Finset.sum` over `Fin n`, real +scalar multiplication. In particular "idempotent", "orthogonal" and "complete" are written out +inline as `m (q i) (q i) = q i`, `i ≠ j → m (q i) (q j) = 0`, and `∑ i, q i = e`. + +What is and is not assumed. No associativity, no power-associativity, no positivity of the +inner product against `m` (indeed no hypothesis at all connects `m` to `⟪·, ·⟫`), no ordering, no +trace, no Peirce decomposition, no simplicity. Finite-dimensionality is essential: `ℝ[X]` under +polynomial multiplication satisfies every other hypothesis and has only the idempotents `0` and +`1`. -/ +theorem spectral_resolution_bilinear [FiniteDimensional ℝ J] (m : J →ₗ[ℝ] J →ₗ[ℝ] J) + (hcomm : ∀ x y : J, m x y = m y x) + (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) + (e : J) (he : ∀ y : J, m e y = y) (x : J) : + ∃ (n : ℕ) (q : Fin n → J) (lam : Fin n → ℝ), + (∀ i, m (q i) (q i) = q i) ∧ + (∀ i j, i ≠ j → m (q i) (q j) = 0) ∧ + (∑ i, q i) = e ∧ + x = ∑ i, lam i • q i := + EuclideanJordan.spectral_resolution_bilinear m hcomm hjordan hfr e he x + +end JordanSpectral diff --git a/LeanPool/EuclideanJordan/StructureSolution.lean b/LeanPool/EuclideanJordan/StructureSolution.lean new file mode 100644 index 0000000000..774da480b1 --- /dev/null +++ b/LeanPool/EuclideanJordan/StructureSolution.lean @@ -0,0 +1,507 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import Mathlib.Algebra.Jordan.Basic +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.Algebra.DirectSum.Module +import Mathlib.Analysis.InnerProductSpace.Basic +import Mathlib.Data.Real.Basic +import Mathlib.Data.Sym.Sym2 +import Mathlib.LinearAlgebra.BilinearMap +import Mathlib.LinearAlgebra.Dimension.Finrank +import Mathlib.LinearAlgebra.FiniteDimensional.Defs +import Mathlib.LinearAlgebra.Trace +import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul + + +/-! +# Solution: the trace form and the frame Peirce decomposition + +Repeats the definitions and the six statements of `StructureChallenge.lean` verbatim and +discharges them from the accompanying `EuclideanJordan` development: Part I from +`EuclideanJordan/TraceForm.lean`, Part II from `EuclideanJordan/FramePeirce.lean` and +`EuclideanJordan/FramePeirceMul.lean`. + +The two parts are independent. Part I is stated over +`[NonUnitalNonAssocCommRing J] [Module ℝ J] [IsScalarTower ℝ J J]`; Part II over +`[NormedAddCommGroup J] [InnerProductSpace ℝ J]` with its own `EuclideanJordanAlgebra` class. +Neither part's vocabulary appears in the other's statements, no theorem of one is used in the +proof of the other, and the binders of all six statements are exactly those of the challenge +file. The part-level docstrings below are the originals and describe each bridge in detail. +-/ + +/-! # Part I: the Jordan trace form -/ + +/-! +# Solution: the Jordan trace form + +Repeats the definitions and the four statements of Part I of `StructureChallenge.lean` verbatim +and discharges them from `EuclideanJordan/TraceForm.lean`. + +The definitions here are syntactic copies of the library's `EuclideanJordan.mulL`, +`EuclideanJordan.mulLₗ`, `EuclideanJordan.jtr` and `EuclideanJordan.traceForm`, so they are +definitionally equal to them (the proof fields differ only up to proof irrelevance) and each +bridge is the library theorem applied on the nose. + +The one piece of real work is formal reality. The challenge states it as a hypothesis over +`Fin k`, the shape `SpectralChallenge.lean` uses, while the library's +`EuclideanJordan.IsFormallyReal` is a class quantifying over an arbitrary `Finset`. The two differ +only by reindexing along `Finset.equivFin`, done inline in each of the two positivity proofs. +-/ + +namespace JordanTraceForm + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [Module ℝ J] [IsScalarTower ℝ J J] + +/-- In a *commutative* algebra the scalar-tower rule `(r • a) * b = r • (a * b)` already gives +the `SMulCommClass` rule on the other side, so only `IsScalarTower ℝ J J` has to be assumed. -/ +theorem mul_smul_comm' (r : ℝ) (a b : J) : a * (r • b) = r • (a * b) := by + rw [mul_comm, smul_mul_assoc, mul_comm] + +/-- **The Jordan multiplication operator** `L_c : y ↦ c * y`, as an `ℝ`-linear map. Its +`ℝ`-linearity is exactly what the scalar tower buys, and it is what makes `L_c` traceable. -/ +def mulL (c : J) : J →ₗ[ℝ] J where + toFun y := c * y + map_add' := mul_add c + map_smul' r y := mul_smul_comm' r c y + +@[simp] theorem mulL_apply (c y : J) : mulL c y = c * y := rfl + +/-- `L_·` bundled as a linear map in the multiplier, which is what makes `jtr` linear. -/ +def mulLₗ : J →ₗ[ℝ] J →ₗ[ℝ] J where + toFun := mulL + map_add' a b := by ext y; simp only [mulL_apply, LinearMap.add_apply, add_mul] + map_smul' r a := by + ext y + simp only [mulL_apply, LinearMap.smul_apply, RingHom.id_apply, smul_mul_assoc] + +@[simp] theorem mulLₗ_apply (a : J) : mulLₗ a = mulL a := rfl + +/-- **The Jordan trace functional** `x ↦ tr(L_x)`, as an `ℝ`-linear form. Not normalised: see +the module docstring. -/ +noncomputable def jtr : J →ₗ[ℝ] ℝ := (LinearMap.trace ℝ J).comp mulLₗ + +@[simp] theorem jtr_apply (x : J) : jtr x = LinearMap.trace ℝ J (mulL x) := rfl + +/-- **The Jordan trace form** `τ(x, y) = tr(L_{x * y})`, bundled as an `ℝ`-bilinear form. + +Bilinearity is not a theorem below because it is the *type*: the four `mk₂` fields are additivity +and homogeneity in each argument, and they are immediate from linearity of `jtr` and +bilinearity of the product. -/ +noncomputable def traceForm : J →ₗ[ℝ] J →ₗ[ℝ] ℝ := + LinearMap.mk₂ ℝ (fun x y => jtr (x * y)) + (fun x x' y => by rw [add_mul, map_add]) + (fun r x y => by rw [smul_mul_assoc, map_smul, smul_eq_mul]) + (fun x y y' => by rw [mul_add, map_add]) + (fun r x y => by rw [mul_smul_comm' r x y, map_smul, smul_eq_mul]) + +@[simp] theorem traceForm_apply (x y : J) : traceForm x y = jtr (x * y) := rfl + +/-- **The trace form is symmetric**: `τ(x, y) = τ(y, x)`. + +No Jordan identity, no finite dimension, no formal reality: this is commutativity of the product +underneath `jtr`, and it is registered at that generality deliberately. -/ +theorem traceForm_comm (x y : J) : traceForm x y = traceForm y x := + EuclideanJordan.traceForm_comm x y + +/-- **The trace form is associative**: `τ(x * y, z) = τ(y, x * z)`. + +This is the compatibility that the standard presentation of a Euclidean Jordan algebra *assumes* +of its inner product, here proved of a form manufactured from the multiplication alone. It is the +main theorem of this file. + +Note the hypotheses, which are weaker than one expects. Beyond the commutative product and the +`ℝ`-module structure only `IsCommJordan` — the Jordan identity — is assumed: **no finite +dimension, no formal reality, no unit, no positivity, no idempotents, no spectral theory.** +`LinearMap.trace` is total, so the statement is meaningful (and true) even when `J` has no finite +basis and every trace in sight is `0`. -/ +theorem traceForm_assoc [IsCommJordan J] (x y z : J) : + traceForm (x * y) z = traceForm y (x * z) := + EuclideanJordan.traceForm_assoc x y z + +/-- **The trace form is positive semidefinite**: `τ(x, x) ≥ 0`. + +`hfr` is formal reality: a vanishing sum of squares has vanishing summands. With +`Module.Finite ℝ J` it yields a spectral resolution `x = ∑ᵢ λᵢ qᵢ` into orthogonal idempotents, +whence `x * x = ∑ᵢ λᵢ² qᵢ` and `τ(x, x) = ∑ᵢ λᵢ² tr(L_{qᵢ})`; and for an idempotent `c` the Peirce +split `L_c = P₁(c) + ½ P_{1/2}(c)` writes `tr(L_c)` as a nonnegative combination of traces of +idempotent endomorphisms, which are the ranks of their ranges. + +Formal reality is essential: `ℂ` over `ℝ` is a finite-dimensional commutative associative Jordan +algebra with `τ(i, i) = -2`. See the module docstring, which is also honest about the weaker +role `Module.Finite ℝ J` plays in this particular statement. -/ +theorem traceForm_self_nonneg [IsCommJordan J] [Module.Finite ℝ J] + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, f i * f i) = 0 → ∀ i, f i = 0) (x : J) : + 0 ≤ traceForm x x := by + have : EuclideanJordan.IsFormallyReal J := by + classical + refine ⟨fun {ι} s f hsum i hi => ?_⟩ + have key : (∑ k : Fin s.card, f (s.equivFin.symm k) * f (s.equivFin.symm k)) = 0 := by + rw [show (∑ k : Fin s.card, f (s.equivFin.symm k) * f (s.equivFin.symm k)) + = ∑ a : {y // y ∈ s}, f a * f a from + Equiv.sum_comp s.equivFin.symm (fun a : {y // y ∈ s} => f a * f a), + Finset.sum_coe_sort s (fun a => f a * f a)] + exact hsum + simpa using hfr s.card (fun k => f (s.equivFin.symm k)) key (s.equivFin ⟨i, hi⟩) + exact EuclideanJordan.traceForm_self_nonneg x + +/-- **The trace form is definite**: `τ(x, x) = 0 ↔ x = 0`. + +Together with `traceForm_comm`, `traceForm_assoc` and `traceForm_self_nonneg` this is the whole +of the assertion that `τ` is a symmetric associative positive definite bilinear form — the +Euclidean form supplied by the multiplication itself. It is only the form: unitality, which a +Euclidean Jordan algebra also requires, is neither assumed nor concluded here. + +The nontrivial direction is `→`. It rests on a sharpening of the estimate behind +`traceForm_self_nonneg`: for a **nonzero** idempotent `c` one has `tr(L_c) ≥ 1`, because +`P₁(c) c = c` makes the range of the Peirce projection `P₁(c)` nonzero, hence of rank at least +one. So a vanishing `∑ᵢ λᵢ² tr(L_{qᵢ})` kills every `λᵢ` whose idempotent is nonzero, and the +terms with `qᵢ = 0` contribute nothing to `x` anyway. -/ +theorem traceForm_self_eq_zero_iff [IsCommJordan J] [Module.Finite ℝ J] + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, f i * f i) = 0 → ∀ i, f i = 0) (x : J) : + traceForm x x = 0 ↔ x = 0 := by + have : EuclideanJordan.IsFormallyReal J := by + classical + refine ⟨fun {ι} s f hsum i hi => ?_⟩ + have key : (∑ k : Fin s.card, f (s.equivFin.symm k) * f (s.equivFin.symm k)) = 0 := by + rw [show (∑ k : Fin s.card, f (s.equivFin.symm k) * f (s.equivFin.symm k)) + = ∑ a : {y // y ∈ s}, f a * f a from + Equiv.sum_comp s.equivFin.symm (fun a : {y // y ∈ s} => f a * f a), + Finset.sum_coe_sort s (fun a => f a * f a)] + exact hsum + simpa using hfr s.card (fun k => f (s.equivFin.symm k)) key (s.equivFin ⟨i, hi⟩) + exact EuclideanJordan.traceForm_self_eq_zero_iff x + +end JordanTraceForm + +/-! # Part II: the frame Peirce decomposition -/ + +/-! +# The Peirce decomposition of a Euclidean Jordan algebra relative to a Jordan frame + +A **Euclidean Jordan algebra** is a real inner-product space `J` carrying a commutative bilinear +product `∘` with unit `1`, satisfying the Jordan identity `x ∘ (x² ∘ y) = x² ∘ (x ∘ y)` and the +associativity of the inner product `⟪x ∘ y, z⟫ = ⟪y, x ∘ z⟫` (Faraut–Korányi, *Analysis on +Symmetric Cones*, Definition III.1.1). `EuclideanJordanAlgebra` below is exactly that, written +as a class. + +An idempotent `c` (`c ∘ c = c`) is **primitive** when it is nonzero and cannot be split: the only +idempotents `d` of the Peirce subalgebra `J₂(c) = {x | c ∘ x = x}` are `0` and `c`. A **Jordan +frame** is a family `p₁, …, pₙ` of pairwise-orthogonal (`pᵢ ∘ pⱼ = 0` for `i ≠ j`) primitive +idempotents that is complete (`∑ᵢ pᵢ = 1`). + +Left multiplication `L_c : x ↦ c ∘ x` by an idempotent is diagonalisable with eigenvalues `1`, +`½` and `0`; that is the Peirce decomposition at a single idempotent. For a Jordan frame the +operators `L_{p₁}, …, L_{pₙ}` are simultaneously diagonalisable, and the surviving joint +eigenspaces — the **blocks** — are indexed by *unordered pairs* of indices: + +* `V_{ii} := {x | pᵢ ∘ x = x}`, the `1`-eigenspace of `L_{pᵢ}`; +* `V_{ij} := {x | pᵢ ∘ x = ½ • x ∧ pⱼ ∘ x = ½ • x}` for `i ≠ j`, the joint `½`-eigenspace of + `L_{pᵢ}` and `L_{pⱼ}`. + +This file states the two theorems that turn that list of subspaces into a decomposition of `J`: + +1. **`frameBlock_isInternal`** — the blocks are an internal direct sum, `J = ⨁_{i ≤ j} V_{ij}`. + Equivalently: they are independent and they span. +2. **`finrank_frameBlock_diag`** — each diagonal block is a line, `dim_ℝ V_{ii} = 1`. (It is in + fact `ℝ ∙ pᵢ`, but the dimension is the form the coordinatization consumes.) + +Together these are Faraut–Korányi Theorem IV.2.1. They are the starting point of the +Jordan–von Neumann–Wigner classification: once `J = ⨁_{i ≤ j} V_{ij}` with one-dimensional +diagonal, the off-diagonal blocks `V_{ij}` all carry a common composition algebra structure and +`J` is recognised as a matrix algebra `H_n(K)`. The multiplication table of the blocks +(`V_{ij} ∘ V_{jk} ⊆ V_{ik}`, `V_{ij} ∘ V_{kl} = 0` for disjoint index pairs, and so on) is the +next step and is *not* stated here. + +## Why the blocks are indexed by `Sym2 (Fin n)` + +`V_{ij}` and `V_{ji}` are literally the same subspace — the defining conditions are a conjunction +that is symmetric in `i` and `j`, and the eigenvalue `blockCoef i j` is symmetric too. So the +honest index set for the family is the type of *unordered* pairs `Sym2 (Fin n)`, and the direct +sum runs over it with no double counting. That is what "`⨁_{i ≤ j}`" means in the informal +statement, and using `Sym2` rather than `{q : Fin n × Fin n // q.1 ≤ q.2}` avoids having to +choose a representative. `frameBlockRaw` is the ordered-pair family, `frameBlockRaw_comm` is its +symmetry, and `frameBlock` is the descent of the former along the latter; `frameBlock_mk` +(`frameBlock F s(i, j) = frameBlockRaw F i j`, by `rfl`) is the bridge a reader should use to see +what the statement says at a concrete pair of indices. + +`DirectSum.IsInternal (frameBlock F)` is Mathlib's predicate saying that the canonical map +`⨁_{s : Sym2 (Fin n)} V_s → J` is bijective — that is, independence *and* spanning, which is the +full strength of the decomposition and not merely the spanning half. + +## What is and is not assumed + +Assumed for both theorems: `J` is a real inner-product space (`NormedAddCommGroup` plus +`InnerProductSpace ℝ`) carrying `EuclideanJordanAlgebra`, and `F : JordanFrame J n` is a Jordan +frame of some cardinality `n`, carried as **data**. + +★ **Finite-dimensionality is assumed only for the second theorem.** `frameBlock_isInternal` +holds with no dimension hypothesis at all; `finrank_frameBlock_diag` takes +`[FiniteDimensional ℝ J]`, because the proof that a primitive idempotent's Peirce subalgebra is a +line runs the spectral theorem inside that subalgebra, and the spectral theorem is false without +the dimension hypothesis (`ℝ[X]` satisfies every other hypothesis with no nonconstant +resolution). + +★ **The inner product is an arbitrary associative one, not the trace form.** Faraut–Korányi fix +`⟪x, y⟫ = tr(x ∘ y)`; the class below asks only that *some* positive-definite associative inner +product exist. That is the weaker hypothesis, so the theorems below are the stronger statements. +Positive-definiteness is not stated as a field: it is already part of `InnerProductSpace ℝ J`. +Formal reality is not a hypothesis either — it follows from the associativity of the inner +product, by pairing a vanishing sum of squares against `1`. + +★ **Primitivity is a formal hypothesis of both theorems; only the second one spends it.** +`JordanFrame` carries primitivity, so `frameBlock_isInternal` assumes it formally even though its +proof never uses it. The stronger statement — that the blocks of a merely orthogonal complete +idempotent family already decompose `J` — is **not formalized here**. It is +`dim V_{ii} = 1` that cashes primitivity out, and that is why the two theorems are stated +together: the decomposition is useless for classification without the one-dimensionality. + +★ **No claim is made about the rank of `J`.** A frame is carried as data of a given cardinality +`n`; that `n` equals the rank of `J`, or that all frames have the same cardinality +(Faraut–Korányi IV.2.5, conjugacy of frames), is neither assumed nor concluded here. Do not read +`frameBlock_isInternal` as a statement about `rank J`. + +Not assumed: no associativity or power-associativity as a hypothesis, no simplicity, no +classification, no identification of `J` with a matrix algebra, no ordered-space structure, no +continuity beyond what the norm gives for free, and no `n ≥ 3`. + +## The vocabulary used here + +Everything the two statements mention is defined below from Mathlib alone: the class +`EuclideanJordanAlgebra`, the predicates `IsOrthIdemFamily` and `IsPrimitive`, the structure +`JordanFrame`, the eigenspace `eigSub`, the eigenvalue `blockCoef`, and the block families +`frameBlockRaw` and `frameBlock`. Nothing else is imported beyond core Mathlib. + +## This file + +Repeats the definitions and the two theorem statements of Part II of `StructureChallenge.lean` verbatim, +imports the reference library, and discharges them from `EuclideanJordan.frameBlock_isInternal` +(`EuclideanJordan/FramePeirce.lean`) and `EuclideanJordan.finrank_frameBlock_diag` +(`EuclideanJordan/FramePeirceMul.lean`). + +The bridge is short by construction. The local class `EuclideanJordanAlgebra` carries the same +fields as `EuclideanJordan.EuclideanJordanAlgebra`, so an instance of the library's class is +assembled from ours field by field, with `toMul` and `toOne` taken from ours — which is what +makes the two `*` and the two `1` the *same* operations rather than merely isomorphic ones. The +local `IsOrthIdemFamily`, `IsPrimitive`, `JordanFrame`, `eigSub`, `blockCoef`, `frameBlockRaw` +and `frameBlock` are then definitionally the library's, so each proof is a single `exact` once +the instance and the frame have been transported. + +★ The library instance is introduced *inside* the proof bodies with `let`, never at the top +level of this file. That is deliberate. An ambient `EuclideanJordan.EuclideanJordanAlgebra J` +brings its derived `NonUnitalNonAssocCommRing J` into scope, and `Submodule ℝ J` would then +elaborate its `AddCommMonoid J` argument through the ring rather than through the norm; the +resulting type is definitionally equal to, but not syntactically the same as, the one the +challenge file states. The contract here is that the two files' declaration types agree on the +nose, so the extra instance is kept out of every statement. +-/ + +noncomputable section + +namespace JordanFramePeirce + +/-- A **Euclidean Jordan algebra**: a real inner-product space carrying a commutative bilinear +product with unit, satisfying the Jordan identity and the associativity of the inner product. + +This is Faraut–Korányi's definition (FK III.1.1) with two deliberate weakenings, both of which +make the theorems below *stronger*: finite-dimensionality is not a field (it is carried as a +separate `[FiniteDimensional ℝ J]` argument exactly where it is needed), and the inner product is +an arbitrary associative one rather than the Jordan trace form. + +Distributivity and homogeneity are stated on the left only; commutativity supplies the right-hand +versions. -/ +class EuclideanJordanAlgebra (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + extends Mul J, One J where + /-- The Jordan product is commutative. -/ + mul_comm : ∀ x y : J, x * y = y * x + /-- The Jordan product is additive in its left argument. -/ + add_mul : ∀ x y z : J, (x + y) * z = x * z + y * z + /-- The Jordan product is homogeneous in its left argument. -/ + smul_mul : ∀ (r : ℝ) (x y : J), (r • x) * y = r • (x * y) + /-- `1` is a unit for the Jordan product. -/ + one_mul : ∀ x : J, (1 : J) * x = x + /-- The Jordan identity, `x ∘ (x² ∘ y) = x² ∘ (x ∘ y)`. -/ + jordan : ∀ x y : J, x * ((x * x) * y) = (x * x) * (x * y) + /-- The inner product is associative: `⟪x ∘ y, z⟫ = ⟪y, x ∘ z⟫`. This is what "Euclidean" + adds to "formally real". -/ + inner_assoc : ∀ x y z : J, inner ℝ (x * y) z = inner ℝ y (x * z) + +variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [EuclideanJordanAlgebra J] + +namespace EuclideanJordanAlgebra + +/-- Left multiplication by `0` is `0` — the one ring axiom the class does not state, obtained +from additivity at `(0, 0, a)`. -/ +theorem zero_mul' (a : J) : (0 : J) * a = 0 := by + have h : (0 : J) * a + (0 : J) * a = (0 : J) * a + 0 := by + rw [add_zero, ← add_mul, add_zero] + exact add_left_cancel h + +theorem mul_zero' (a : J) : a * (0 : J) = 0 := by rw [mul_comm, zero_mul'] + +theorem mul_add' (a x y : J) : a * (x + y) = a * x + a * y := by + rw [mul_comm a (x + y), add_mul, mul_comm x a, mul_comm y a] + +theorem mul_smul' (r : ℝ) (a x : J) : a * (r • x) = r • (a * x) := by + rw [mul_comm a (r • x), smul_mul, mul_comm x a] + +end EuclideanJordanAlgebra + +/-! ## Orthogonal idempotent families, primitivity, and Jordan frames -/ + +/-- A family of pairwise-orthogonal idempotents. Completeness is deliberately *not* part of this +predicate; it is a separate field of `JordanFrame`. -/ +structure IsOrthIdemFamily {n : ℕ} (p : Fin n → J) : Prop where + /-- Each member is idempotent. -/ + idem : ∀ i, p i * p i = p i + /-- Distinct members are orthogonal. -/ + orth : ∀ i j, i ≠ j → p i * p j = 0 + +/-- A **primitive idempotent**: a nonzero idempotent that cannot be split, i.e. the only +idempotents of the Peirce subalgebra `J₂(c) = {x | c ∘ x = x}` are `0` and `c` itself. + +The third clause is stated in the ambient algebra — `d` idempotent with `c ∘ d = d`, which is +membership in `J₂(c)` — rather than over a subtype, so that it can be checked without first +producing the subalgebra. -/ +def IsPrimitive (c : J) : Prop := + c * c = c ∧ c ≠ 0 ∧ ∀ d : J, d * d = d → c * d = d → d = 0 ∨ d = c + +/-- A **Jordan frame**: a complete family of pairwise-orthogonal primitive idempotents. + +Carried as data, indexed by `Fin n`, so that its cardinality is available without any +well-definedness theorem. In particular `n` is *not* asserted to be the rank of `J`. -/ +structure JordanFrame (J : Type*) [NormedAddCommGroup J] [InnerProductSpace ℝ J] + [EuclideanJordanAlgebra J] (n : ℕ) where + /-- The idempotents. -/ + p : Fin n → J + /-- They are idempotent and pairwise orthogonal. -/ + orthIdem : IsOrthIdemFamily p + /-- Each is primitive. -/ + primitive : ∀ i, IsPrimitive (p i) + /-- They sum to the unit. -/ + complete : ∑ i, p i = 1 + +/-! ## The blocks -/ + +/-- The `r`-eigenspace of `L_a : x ↦ a ∘ x`, as a submodule. -/ +def eigSub (a : J) (r : ℝ) : Submodule ℝ J where + carrier := {x : J | a * x = r • x} + add_mem' := fun {u v} hu hv => by + change a * (u + v) = r • (u + v) + rw [EuclideanJordanAlgebra.mul_add', hu, hv, smul_add] + zero_mem' := by + change a * 0 = r • (0 : J) + rw [EuclideanJordanAlgebra.mul_zero', smul_zero] + smul_mem' := fun t x hx => by + change a * (t • x) = r • (t • x) + rw [EuclideanJordanAlgebra.mul_smul', hx, smul_comm] + +@[simp] theorem mem_eigSub {a : J} {r : ℝ} {x : J} : x ∈ eigSub a r ↔ a * x = r • x := Iff.rfl + +variable {n : ℕ} + +/-- The eigenvalue attached to the pair `(i, j)`: `1` on the diagonal, `½` off it. -/ +def blockCoef (i j : Fin n) : ℝ := if i = j then 1 else (2 : ℝ)⁻¹ + +theorem blockCoef_comm (i j : Fin n) : blockCoef i j = blockCoef j i := by + unfold blockCoef + by_cases h : i = j + · simp [h] + · simp [h, Ne.symm h] + +/-- `V_{ij}` before it is pushed through `Sym2`: the joint `blockCoef i j`-eigenspace of `L_{pᵢ}` +and `L_{pⱼ}`. On the diagonal this is `J₂(pᵢ) = {x | pᵢ ∘ x = x}`; off it, the joint +`½`-eigenspace. -/ +def frameBlockRaw (F : JordanFrame J n) (i j : Fin n) : Submodule ℝ J := + eigSub (F.p i) (blockCoef i j) ⊓ eigSub (F.p j) (blockCoef i j) + +theorem frameBlockRaw_comm (F : JordanFrame J n) (i j : Fin n) : + frameBlockRaw F i j = frameBlockRaw F j i := by + unfold frameBlockRaw + rw [blockCoef_comm i j, inf_comm] + +/-- **`V_{ij}`**, indexed by unordered pairs. For `i ≠ j` the joint `½`-eigenspace of `L_{pᵢ}` +and `L_{pⱼ}`; on the diagonal, `J₂(pᵢ)`. -/ +def frameBlock (F : JordanFrame J n) : Sym2 (Fin n) → Submodule ℝ J := + Sym2.lift ⟨frameBlockRaw F, frameBlockRaw_comm F⟩ + +@[simp] theorem frameBlock_mk (F : JordanFrame J n) (i j : Fin n) : + frameBlock F s(i, j) = frameBlockRaw F i j := rfl + +theorem mem_frameBlock_diag {F : JordanFrame J n} {i : Fin n} {x : J} : + x ∈ frameBlock F s(i, i) ↔ F.p i * x = x := by + simp [frameBlockRaw, blockCoef] + +theorem mem_frameBlock_off {F : JordanFrame J n} {i j : Fin n} (hij : i ≠ j) {x : J} : + x ∈ frameBlock F s(i, j) ↔ F.p i * x = (2 : ℝ)⁻¹ • x ∧ F.p j * x = (2 : ℝ)⁻¹ • x := by + simp [frameBlockRaw, blockCoef, hij] + +/-! ## The two theorems -/ + +/-- **The frame Peirce decomposition: `J = ⨁_{i ≤ j} V_{ij}`.** + +For a Jordan frame `p₁, …, pₙ` of a Euclidean Jordan algebra `J`, the blocks `V_{ij}` — indexed +by unordered pairs, so that `V_{ij}` and `V_{ji}` are counted once — form an internal direct sum +decomposition of `J`: the canonical map `⨁_{s : Sym2 (Fin n)} V_s → J` is bijective. That is +independence *and* spanning. + +Reference: J. Faraut and A. Korányi, *Analysis on Symmetric Cones*, Oxford 1994, Theorem IV.2.1. + +No dimension hypothesis is needed. Primitivity remains a *formal hypothesis* of this statement — +it is carried by `JordanFrame` — but the proof does not spend it: see the module docstring. -/ +theorem frameBlock_isInternal (F : JordanFrame J n) : DirectSum.IsInternal (frameBlock F) := by + let _lib : EuclideanJordan.EuclideanJordanAlgebra J := + { toMul := inferInstance + toOne := inferInstance + mul_comm := EuclideanJordanAlgebra.mul_comm + add_mul := EuclideanJordanAlgebra.add_mul + smul_mul := EuclideanJordanAlgebra.smul_mul + one_mul := EuclideanJordanAlgebra.one_mul + jordan := EuclideanJordanAlgebra.jordan + inner_assoc := EuclideanJordanAlgebra.inner_assoc } + exact EuclideanJordan.frameBlock_isInternal + { p := F.p + orthIdem := ⟨F.orthIdem.idem, F.orthIdem.orth⟩ + primitive := F.primitive + complete := F.complete } + +/-- **The diagonal blocks are lines: `dim V_{ii} = 1`.** + +This is where primitivity of the frame's members is spent, and where finite-dimensionality is +needed. `V_{ii}` is the Peirce subalgebra `J₂(pᵢ)`, which is itself a Euclidean Jordan algebra +with unit `pᵢ`; the spectral theorem inside it writes every element as a real combination of +idempotents of `J₂(pᵢ)`, and primitivity says each of those is `0` or `pᵢ`. So +`V_{ii} = ℝ ∙ pᵢ`, and `pᵢ ≠ 0`. + +Reference: J. Faraut and A. Korányi, *Analysis on Symmetric Cones*, Oxford 1994, Theorem IV.2.1. + +★ This is a statement about one block of a frame carried as data. It is *not* a statement about +`rank J`, and nothing here converts it into one. -/ +theorem finrank_frameBlock_diag [FiniteDimensional ℝ J] (F : JordanFrame J n) (i : Fin n) : + Module.finrank ℝ ↥(frameBlock F s(i, i)) = 1 := by + let _lib : EuclideanJordan.EuclideanJordanAlgebra J := + { toMul := inferInstance + toOne := inferInstance + mul_comm := EuclideanJordanAlgebra.mul_comm + add_mul := EuclideanJordanAlgebra.add_mul + smul_mul := EuclideanJordanAlgebra.smul_mul + one_mul := EuclideanJordanAlgebra.one_mul + jordan := EuclideanJordanAlgebra.jordan + inner_assoc := EuclideanJordanAlgebra.inner_assoc } + exact EuclideanJordan.finrank_frameBlock_diag + { p := F.p + orthIdem := ⟨F.orthIdem.idem, F.orthIdem.orth⟩ + primitive := F.primitive + complete := F.complete } i + +end JordanFramePeirce + +end diff --git a/LeanPool/EuclideanJordan/TraceFormSolution.lean b/LeanPool/EuclideanJordan/TraceFormSolution.lean new file mode 100644 index 0000000000..b63675c632 --- /dev/null +++ b/LeanPool/EuclideanJordan/TraceFormSolution.lean @@ -0,0 +1,159 @@ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bryan Ehrlich +-/ +/- +Copyright (c) 2026 Bryan Ehrlich. All rights reserved. +Released under Apache 2.0 license. +Authors: Bryan Ehrlich +-/ +import Mathlib.Algebra.Jordan.Basic +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.LinearAlgebra.BilinearMap +import Mathlib.LinearAlgebra.Trace +import Mathlib.LinearAlgebra.FiniteDimensional.Defs +import Mathlib.Data.Real.Basic +import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm + +/-! +# Solution: the Jordan trace form + +Repeats the definitions and the four statements of `TraceFormChallenge.lean` verbatim and +discharges them from `EuclideanJordan/TraceForm.lean`. + +The definitions here are syntactic copies of the library's `EuclideanJordan.mulL`, +`EuclideanJordan.mulLₗ`, `EuclideanJordan.jtr` and `EuclideanJordan.traceForm`, so they are +definitionally equal to them (the proof fields differ only up to proof irrelevance) and each +bridge is the library theorem applied on the nose. + +The one piece of real work is formal reality. The challenge states it as a hypothesis over +`Fin k`, the shape `SpectralChallenge.lean` uses, while the library's +`EuclideanJordan.IsFormallyReal` is a class quantifying over an arbitrary `Finset`. The two differ +only by reindexing along `Finset.equivFin`, done inline in each of the two positivity proofs. +-/ + +namespace JordanTraceForm + +variable {J : Type*} [NonUnitalNonAssocCommRing J] [Module ℝ J] [IsScalarTower ℝ J J] + +/-- In a *commutative* algebra the scalar-tower rule `(r • a) * b = r • (a * b)` already gives +the `SMulCommClass` rule on the other side, so only `IsScalarTower ℝ J J` has to be assumed. -/ +theorem mul_smul_comm' (r : ℝ) (a b : J) : a * (r • b) = r • (a * b) := by + rw [mul_comm, smul_mul_assoc, mul_comm] + +/-- **The Jordan multiplication operator** `L_c : y ↦ c * y`, as an `ℝ`-linear map. Its +`ℝ`-linearity is exactly what the scalar tower buys, and it is what makes `L_c` traceable. -/ +def mulL (c : J) : J →ₗ[ℝ] J where + toFun y := c * y + map_add' := mul_add c + map_smul' r y := mul_smul_comm' r c y + +@[simp] theorem mulL_apply (c y : J) : mulL c y = c * y := rfl + +/-- `L_·` bundled as a linear map in the multiplier, which is what makes `jtr` linear. -/ +def mulLₗ : J →ₗ[ℝ] J →ₗ[ℝ] J where + toFun := mulL + map_add' a b := by ext y; simp only [mulL_apply, LinearMap.add_apply, add_mul] + map_smul' r a := by + ext y + simp only [mulL_apply, LinearMap.smul_apply, RingHom.id_apply, smul_mul_assoc] + +@[simp] theorem mulLₗ_apply (a : J) : mulLₗ a = mulL a := rfl + +/-- **The Jordan trace functional** `x ↦ tr(L_x)`, as an `ℝ`-linear form. Not normalised: see +the module docstring. -/ +noncomputable def jtr : J →ₗ[ℝ] ℝ := (LinearMap.trace ℝ J).comp mulLₗ + +@[simp] theorem jtr_apply (x : J) : jtr x = LinearMap.trace ℝ J (mulL x) := rfl + +/-- **The Jordan trace form** `τ(x, y) = tr(L_{x * y})`, bundled as an `ℝ`-bilinear form. + +Bilinearity is not a theorem below because it is the *type*: the four `mk₂` fields are additivity +and homogeneity in each argument, and they are immediate from linearity of `jtr` and +bilinearity of the product. -/ +noncomputable def traceForm : J →ₗ[ℝ] J →ₗ[ℝ] ℝ := + LinearMap.mk₂ ℝ (fun x y => jtr (x * y)) + (fun x x' y => by rw [add_mul, map_add]) + (fun r x y => by rw [smul_mul_assoc, map_smul, smul_eq_mul]) + (fun x y y' => by rw [mul_add, map_add]) + (fun r x y => by rw [mul_smul_comm' r x y, map_smul, smul_eq_mul]) + +@[simp] theorem traceForm_apply (x y : J) : traceForm x y = jtr (x * y) := rfl + +/-- **The trace form is symmetric**: `τ(x, y) = τ(y, x)`. + +No Jordan identity, no finite dimension, no formal reality: this is commutativity of the product +underneath `jtr`, and it is registered at that generality deliberately. -/ +theorem traceForm_comm (x y : J) : traceForm x y = traceForm y x := + EuclideanJordan.traceForm_comm x y + +/-- **The trace form is associative**: `τ(x * y, z) = τ(y, x * z)`. + +This is the compatibility that the standard presentation of a Euclidean Jordan algebra *assumes* +of its inner product, here proved of a form manufactured from the multiplication alone. It is the +main theorem of this file. + +Note the hypotheses, which are weaker than one expects. Beyond the commutative product and the +`ℝ`-module structure only `IsCommJordan` — the Jordan identity — is assumed: **no finite +dimension, no formal reality, no unit, no positivity, no idempotents, no spectral theory.** +`LinearMap.trace` is total, so the statement is meaningful (and true) even when `J` has no finite +basis and every trace in sight is `0`. -/ +theorem traceForm_assoc [IsCommJordan J] (x y z : J) : + traceForm (x * y) z = traceForm y (x * z) := + EuclideanJordan.traceForm_assoc x y z + +/-- **The trace form is positive semidefinite**: `τ(x, x) ≥ 0`. + +`hfr` is formal reality: a vanishing sum of squares has vanishing summands. With +`Module.Finite ℝ J` it yields a spectral resolution `x = ∑ᵢ λᵢ qᵢ` into orthogonal idempotents, +whence `x * x = ∑ᵢ λᵢ² qᵢ` and `τ(x, x) = ∑ᵢ λᵢ² tr(L_{qᵢ})`; and for an idempotent `c` the Peirce +split `L_c = P₁(c) + ½ P_{1/2}(c)` writes `tr(L_c)` as a nonnegative combination of traces of +idempotent endomorphisms, which are the ranks of their ranges. + +Formal reality is essential: `ℂ` over `ℝ` is a finite-dimensional commutative associative Jordan +algebra with `τ(i, i) = -2`. See the module docstring, which is also honest about the weaker +role `Module.Finite ℝ J` plays in this particular statement. -/ +theorem traceForm_self_nonneg [IsCommJordan J] [Module.Finite ℝ J] + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, f i * f i) = 0 → ∀ i, f i = 0) (x : J) : + 0 ≤ traceForm x x := by + have : EuclideanJordan.IsFormallyReal J := by + classical + refine ⟨fun {ι} s f hsum i hi => ?_⟩ + have key : (∑ k : Fin s.card, f (s.equivFin.symm k) * f (s.equivFin.symm k)) = 0 := by + rw [show (∑ k : Fin s.card, f (s.equivFin.symm k) * f (s.equivFin.symm k)) + = ∑ a : {y // y ∈ s}, f a * f a from + Equiv.sum_comp s.equivFin.symm (fun a : {y // y ∈ s} => f a * f a), + Finset.sum_coe_sort s (fun a => f a * f a)] + exact hsum + simpa using hfr s.card (fun k => f (s.equivFin.symm k)) key (s.equivFin ⟨i, hi⟩) + exact EuclideanJordan.traceForm_self_nonneg x + +/-- **The trace form is definite**: `τ(x, x) = 0 ↔ x = 0`. + +Together with `traceForm_comm`, `traceForm_assoc` and `traceForm_self_nonneg` this is the whole +of the assertion that `τ` is a symmetric associative positive definite bilinear form — the +Euclidean form supplied by the multiplication itself. It is only the form: unitality, which a +Euclidean Jordan algebra also requires, is neither assumed nor concluded here. + +The nontrivial direction is `→`. It rests on a sharpening of the estimate behind +`traceForm_self_nonneg`: for a **nonzero** idempotent `c` one has `tr(L_c) ≥ 1`, because +`P₁(c) c = c` makes the range of the Peirce projection `P₁(c)` nonzero, hence of rank at least +one. So a vanishing `∑ᵢ λᵢ² tr(L_{qᵢ})` kills every `λᵢ` whose idempotent is nonzero, and the +terms with `qᵢ = 0` contribute nothing to `x` anyway. -/ +theorem traceForm_self_eq_zero_iff [IsCommJordan J] [Module.Finite ℝ J] + (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, f i * f i) = 0 → ∀ i, f i = 0) (x : J) : + traceForm x x = 0 ↔ x = 0 := by + have : EuclideanJordan.IsFormallyReal J := by + classical + refine ⟨fun {ι} s f hsum i hi => ?_⟩ + have key : (∑ k : Fin s.card, f (s.equivFin.symm k) * f (s.equivFin.symm k)) = 0 := by + rw [show (∑ k : Fin s.card, f (s.equivFin.symm k) * f (s.equivFin.symm k)) + = ∑ a : {y // y ∈ s}, f a * f a from + Equiv.sum_comp s.equivFin.symm (fun a : {y // y ∈ s} => f a * f a), + Finset.sum_coe_sort s (fun a => f a * f a)] + exact hsum + simpa using hfr s.card (fun k => f (s.equivFin.symm k)) key (s.equivFin ⟨i, hi⟩) + exact EuclideanJordan.traceForm_self_eq_zero_iff x + +end JordanTraceForm diff --git a/LeanPool/projects.yml b/LeanPool/projects.yml index 95e0c8ad83..aef0034a4d 100644 --- a/LeanPool/projects.yml +++ b/LeanPool/projects.yml @@ -9966,3 +9966,41 @@ projects: msc: - '90C35' - '05C21' + - slug: euclidean-jordan + title: 'Euclidean Jordan algebras: power associativity, the spectral theorem, the trace + form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition' + summary: 'The frame Peirce decomposition: relative to a Jordan frame, a Euclidean Jordan + algebra is the internal direct sum of the blocks V_ij over unordered pairs of frame indices, + and in finite dimension the diagonal blocks are lines, dim V_ii = 1 - the Faraut-Koranyi + table entry. The frame is carried as data, so frame conjugacy is sidestepped and nothing + here claims anything about the rank of J. Machine checked, not peer reviewed, no priority + claim.' + branch: nonassociative algebra + entry_module: LeanPool.EuclideanJordan + authors: + - Bryan Ehrlich + source: + url: https://github.com/ehrlich-b/euclidean-jordan + github_repo: ehrlich-b/euclidean-jordan + commit: a83407e4dbf73c77c31a2291c02f20b37a5ebbaf + license: Apache-2.0 + status: verified + main_declarations: + - EuclideanJordan.frameBlock_isInternal + - EuclideanJordan.finrank_frameBlock_diag + main_results: + - declaration: EuclideanJordan.frameBlock_isInternal + informal: A Euclidean Jordan algebra is the internal direct sum of the Peirce blocks associated + with a Jordan frame. + - declaration: EuclideanJordan.finrank_frameBlock_diag + informal: In finite dimension, every diagonal block of a Jordan frame has dimension one. + tags: + - nonassociative-algebra + msc: + - 17C20 + - 17C27 + - 17C37 + - 17C65 + - 17A15 + - 46L70 + provenance: AI From c9d82329063c64016593f6585d6b5b2140e608e9 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Mon, 21 Sep 2026 18:21:07 +0000 Subject: [PATCH 2/6] Advance euclidean-jordan port to Lean 4.34 --- .../Vendor/HermitianMat/CFC.lean | 8 +--- .../Vendor/HermitianMat/Inner.lean | 2 - .../Vendor/HermitianMat/Order.lean | 43 ++++++++++--------- .../EuclideanJordan/Vendor/Matrix.lean | 6 +-- .../EuclideanJordan/Vendor/Misc.lean | 2 +- .../Vendor/Tactic/Commutes.lean | 4 +- 6 files changed, 30 insertions(+), 35 deletions(-) diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean index d3f7863aa5..f90994c39d 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean @@ -87,12 +87,12 @@ end congr section commute variable {A B : HermitianMat d 𝕜} -@[aesop unsafe apply 50% (rule_sets := [Commutes])] +@[aesop apply 50% (rule_sets := [Commutes])] theorem _root_.Commute.cfc_left (hAB : Commute A.mat B.mat) : Commute (A.cfc f).mat B.mat := by exact hAB.cfc_real f -@[aesop unsafe apply 50% (rule_sets := [Commutes])] +@[aesop apply 50% (rule_sets := [Commutes])] theorem _root_.Commute.cfc_right (hAB : Commute A.mat B.mat) : Commute A.mat (B.cfc f).mat := (hAB.symm.cfc_left f).symm @@ -138,10 +138,6 @@ theorem cfc_toMat_eq_sum_smul_proj : (A.cfc f).mat = simp [Matrix.mul_apply,Finset.mul_sum, Finset.smul_sum, smul_ite, smul_zero] --Ensure we get this instance: -/-- info: locallyCompact_of_proper -/ -#guard_msgs in - -#synth LocallyCompactSpace (HermitianMat d 𝕜) theorem cfc_eigenvalues (A : HermitianMat d 𝕜) : ∃ (e : d ≃ d), (A.cfc f).H.eigenvalues = f ∘ A.H.eigenvalues ∘ e := diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean index 0164c07a37..8a8041aef4 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean @@ -283,8 +283,6 @@ open ComplexOrder variable {d : Type*} [Fintype d] {𝕜 : Type*} [RCLike 𝕜] --Check that it synthesizes ok -#guard_msgs(drop info) in -#synth ContractibleSpace (HermitianMat d ℂ) @[fun_prop] theorem inner_continuous : Continuous (Inner.inner ℝ (E := HermitianMat d 𝕜)) := by diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean index 41a694af80..588cbabb63 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean @@ -174,7 +174,7 @@ open Lean Meta in `Matrix.PosSemidef A`, `Matrix.PosDef A`, or `And P Q` (syntactically), attempt to find a proof of nonnegativity or positivity for `e`. Only syntactic matching on the head constant is used; `isDefEq` is used only to compare the matrix argument. -/ -meta partial def findMatrixPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : +meta def findMatrixPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : MetaM (Option (Bool × Expr)) := do let head := ty.getAppFn if head.isConst then @@ -203,17 +203,17 @@ meta partial def findMatrixPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : let pSemidef ← mkAppM ``Matrix.PosDef.posSemidef #[p] let pf ← mkAppM ``HermitianMat.posSemidef_to_nonneg #[pSemidef] return some (false, pf) - if name == ``And then - let args := ty.getAppArgs - if args.size == 2 then - -- Recurse on left and right - let pLeft ← mkAppM ``And.left #[p] - if let some result ← findMatrixPSDInExpr e pLeft args[0]! then - return some result - let pRight ← mkAppM ``And.right #[p] - if let some result ← findMatrixPSDInExpr e pRight args[1]! then - return some result + match ty with + | .app (.app (.const ``And _) left) right => + let pLeft ← mkAppM ``And.left #[p] + if let some result ← findMatrixPSDInExpr e pLeft left then + return some result + let pRight ← mkAppM ``And.right #[p] + if let some result ← findMatrixPSDInExpr e pRight right then + return some result + | _ => pure () return none +termination_by structural ty open Lean Meta Mathlib.Meta.Positivity in /-- Positivity extension for `Matrix`: looks for `A.PosSemidef` or `A.PosDef` in the @@ -249,7 +249,7 @@ open Lean Meta in `Matrix.PosSemidef A.mat`, `Matrix.PosDef A.mat`, or `And P Q` (syntactically), attempt to find a proof of nonnegativity or positivity for `e`. Only syntactic matching on the head constant is used; `isDefEq` is used only to compare the `HermitianMat` argument. -/ -meta partial def findHermitianMatPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : +meta def findHermitianMatPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : MetaM (Option (Bool × Expr)) := do let head := ty.getAppFn if head.isConst then @@ -282,16 +282,17 @@ meta partial def findHermitianMatPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : let pSemidef ← mkAppM ``Matrix.PosDef.posSemidef #[p] let pf ← mkAppM ``HermitianMat.mat_posSemidef_to_nonneg #[pSemidef] return some (false, pf) - if name == ``And then - let args := ty.getAppArgs - if args.size == 2 then - let pLeft ← mkAppM ``And.left #[p] - if let some result ← findHermitianMatPSDInExpr e pLeft args[0]! then - return some result - let pRight ← mkAppM ``And.right #[p] - if let some result ← findHermitianMatPSDInExpr e pRight args[1]! then - return some result + match ty with + | .app (.app (.const ``And _) left) right => + let pLeft ← mkAppM ``And.left #[p] + if let some result ← findHermitianMatPSDInExpr e pLeft left then + return some result + let pRight ← mkAppM ``And.right #[p] + if let some result ← findHermitianMatPSDInExpr e pRight right then + return some result + | _ => pure () return none +termination_by structural ty open Lean Meta Mathlib.Meta.Positivity in /-- Positivity extension for `HermitianMat`: looks for `A.mat.PosSemidef` or `A.mat.PosDef` in diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean index 49f0363a33..30314a68ea 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean @@ -30,7 +30,7 @@ noncomputable section open BigOperators -variable {n 𝕜 : Type*} +variable {n 𝕜 dA dB : Type*} variable [RCLike 𝕜] [DecidableEq n] namespace Matrix @@ -52,7 +52,7 @@ theorem zero_rank_eq_zero {A : Matrix n n 𝕜} [Fintype n] (hA : A.rank = 0) : intro v rw [rank, Module.finrank_zero_iff] at hA have := hA.elim ⟨A.mulVecLin v, ⟨v, rfl⟩⟩ ⟨0, ⟨0, by rw [mulVecLin_apply, mulVec_zero]⟩⟩ - simpa only [Subtype.mk.injEq] using this + exact congrArg Subtype.val this rw [← LinearEquiv.map_eq_zero_iff toLin'] exact LinearMap.ext h @@ -289,7 +289,7 @@ theorem zero_dotProduct_zero_iff : (∀ x : m → 𝕜, 0 = star x ⬝ᵥ A.mulV constructor · intro h ext i j - have h₂ := fun x ↦ (PosSemidef.dotProduct_mulVec_zero_iff hA x).mp (h x).symm + have h₂ := fun x ↦ (PosSemidef.dotProduct_mulVec_zero_iff hA).mp (h x).symm classical have : DecidableEq m := inferInstance convert! congrFun (h₂ (Pi.single j 1)) i using 1 simp diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean index 0e6dd5b76a..b9864512d5 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean @@ -12,7 +12,7 @@ public import Mathlib.Order.CompletePartialOrder --Can this be rewritten more generally? For `finiteness` to work, I don't know how. --PR'ed in #33105 -@[aesop (rule_sets := [finiteness]) unsafe apply] +@[aesop (rule_sets := [finiteness]) apply 50%] theorem ite_eq_top {α : Type*} [Top α] (h : Prop) [Decidable h] {x y : α} (hx : x ≠ ⊤) (hy : y ≠ ⊤) : (if h then x else y) ≠ ⊤ := by split <;> assumption diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes.lean index 2dbdca6d0f..ef276b92a5 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic/Commutes.lean @@ -89,7 +89,7 @@ attribute [aesop safe apply (rule_sets := [Commutes])] --quite common that we specifically need it at the end (or equivalently, at the beginning) --which is why we give a safe + fast tactic to apply. This often cuts heartbeats by a large --factor. -attribute [aesop unsafe apply 5% (rule_sets := [Commutes])] Commute.symm +attribute [aesop apply 5% (rule_sets := [Commutes])] Commute.symm --This essentially does `symm + assumption`. Note that `assumption` is already a builtin. add_aesop_rules safe tactic (rule_sets := [Commutes]) (by exact Commute.symm ‹_›) @@ -101,7 +101,7 @@ add_aesop_rules safe tactic (rule_sets := [Commutes]) (by apply Commute.ofNat_ri --Try to normalize ring operations add_aesop_rules safe tactic (rule_sets := [Commutes]) (by apply Commute.ofNat_right) -attribute [aesop unsafe apply 50% (rule_sets := [Commutes])] +attribute [aesop apply 50% (rule_sets := [Commutes])] Commute.mul_left Commute.mul_right Commute.pow_left Commute.pow_right --Commute.pow_pow (redundant) Commute.smul_left Commute.smul_right From 6cda2b85ecf5f0cd4737677fd905365406d5e0d0 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Mon, 21 Sep 2026 20:24:17 +0000 Subject: [PATCH 3/6] Port the complete Euclidean Jordan development to Lean 4.34 --- .../Vendor/HermitianMat/Basic.lean | 45 +++++++---- .../Vendor/HermitianMat/CFC.lean | 66 +++++++++++----- .../Vendor/HermitianMat/Inner.lean | 3 +- .../Vendor/HermitianMat/Jordan.lean | 13 +++- .../Vendor/HermitianMat/Order.lean | 67 +++++++++------- .../Vendor/HermitianMat/Proj.lean | 9 ++- .../Vendor/HermitianMat/Reindex.lean | 20 +++-- .../Vendor/HermitianMat/Trace.lean | 3 +- .../EuclideanJordan/Vendor/Isometry.lean | 13 +++- .../EuclideanJordan/Vendor/Matrix.lean | 76 +++++++++++-------- .../EuclideanJordan/StructureSolution.lean | 4 + 11 files changed, 210 insertions(+), 109 deletions(-) diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean index b3d4727977..69ed19650c 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean @@ -159,8 +159,8 @@ variable [TopologicalSpace R] [SMul R α] [ContinuousSMul R α] [StarModule R instance : ContinuousSMul R (HermitianMat n α) where continuous_smul := by - rw [continuous_induced_rng] - fun_prop + apply Continuous.subtype_mk + exact continuous_fst.smul (continuous_mat.comp continuous_snd) --Shorcut instances: instance : IsTopologicalAddGroup (HermitianMat n 𝕜) := inferInstance @@ -208,7 +208,7 @@ def matₗ : HermitianMat n α →L[R] Matrix n n α where toFun := mat cont := by fun_prop map_add' := by simp - map_smul' := by simp + map_smul' := by intros; rfl end module end addcommgroup @@ -366,8 +366,15 @@ def conj {m} (B : Matrix m n α) : HermitianMat n α →+ HermitianMat m α wher rw [Finset.sum_comm] congr! 2 ring⟩ - map_add' _ _ := by ext1; simp [Matrix.mul_add, Matrix.add_mul] - map_zero' := by simp + map_add' X Y := by + apply HermitianMat.ext + change B * (X.mat + Y.mat) * B.conjTranspose = + B * X.mat * B.conjTranspose + B * Y.mat * B.conjTranspose + rw [Matrix.mul_add, Matrix.add_mul] + map_zero' := by + apply HermitianMat.ext + change B * 0 * B.conjTranspose = 0 + rw [Matrix.mul_zero, Matrix.zero_mul] theorem conj_apply (B : Matrix m n α) (A : HermitianMat n α) : conj B A = ⟨B * A.mat * B.conjTranspose, (conj B A).2⟩ := by @@ -387,11 +394,14 @@ variable (B : HermitianMat n α) @[simp] theorem conj_zero [DecidableEq n] : A.conj (0 : Matrix m n α) = 0 := by - simp [conj_apply] + apply HermitianMat.ext + change 0 * A.mat * (0 : Matrix m n α).conjTranspose = 0 + rw [Matrix.zero_mul, Matrix.zero_mul] @[simp] theorem conj_one [DecidableEq n] : A.conj 1 = A := by - simp [conj_apply] + apply HermitianMat.ext + simp only [conj_apply_mat, Matrix.one_mul, Matrix.conjTranspose_one, Matrix.mul_one] @[simp] lemma conj_one_unitary [DecidableEq n] (U : Matrix.unitaryGroup n α) : @@ -405,9 +415,10 @@ variable (R : Type*) [Star R] [TrivialStar R] [CommSemiring R] [Algebra R α] [S /-- `HermitianMat.conj` as an `R`-linear map, where `R` is the ring of relevant reals. -/ def conjLinear {m} (B : Matrix m n α) : HermitianMat n α →ₗ[R] HermitianMat m α where toAddHom := conj B - map_smul' _ _ := by - ext1 - simp + map_smul' r A := by + apply HermitianMat.ext + change B * (r • A.mat) * B.conjTranspose = r • (B * A.mat * B.conjTranspose) + rw [Matrix.mul_smul, Matrix.smul_mul] @[simp] theorem conjLinear_apply (B : Matrix m n α) : conjLinear R B A = conj B A := by @@ -415,7 +426,8 @@ theorem conjLinear_apply (B : Matrix m n α) : conjLinear R B A = conj B A := b @[fun_prop] lemma continuous_conj (ρ : HermitianMat n 𝕜) : Continuous (ρ.conj (m := m) ·) := by - simp only [HermitianMat.conj, AddMonoidHom.coe_mk, ZeroHom.coe_mk] + apply Continuous.subtype_mk + change Continuous (fun B : Matrix m n 𝕜 => B * ρ.mat * B.conjTranspose) fun_prop end conj @@ -541,9 +553,14 @@ theorem diagonal_mul (c : ℝ) : diagonal 𝕜 (fun x ↦ c * f x) = c • diago theorem diagonal_conj_diagonal [Fintype n] : (diagonal 𝕜 f).conj (diagonal 𝕜 g) = diagonal 𝕜 (fun i ↦ f i * (g i)^2) := by - ext1 - simp [diagonal, conj] - intro + apply HermitianMat.ext + change Matrix.diagonal (fun i => (g i : 𝕜)) * Matrix.diagonal (fun i => (f i : 𝕜)) * + (Matrix.diagonal (fun i => (g i : 𝕜))).conjTranspose = _ + simp only [Matrix.diagonal_conjTranspose, Pi.star_def, RCLike.star_def, + RCLike.conj_ofReal, Matrix.diagonal_mul_diagonal] + congr 1 + funext i + simp only [diagonal, mat, RCLike.ofReal_mul, RCLike.ofReal_pow] ring /-- diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean index f90994c39d..55148685bd 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean @@ -129,7 +129,7 @@ theorem cfc_toMat_eq_sum_smul_proj : (A.cfc f).mat = ext i j ; by_cases hij : i = j <;> simp [ hij ]; · simp [ Matrix.sum_apply, Matrix.single ]; simp [ Algebra.smul_def ]; - · rw [Finset.sum_apply, Finset.sum_apply] + · rw [Matrix.sum_apply] simp_all rw [h] simp [Matrix.single, Matrix.mul_assoc] @@ -239,7 +239,7 @@ theorem cfc_pow {n : ℕ} : A.cfc (· ^ n) = A ^ n := by theorem cfc_nonneg_iff : 0 ≤ A.cfc f ↔ ∀ i, 0 ≤ f (A.H.eigenvalues i) := by open MatrixOrder in - rw [cfc_eq, ← Subtype.coe_le_coe, ZeroMemClass.coe_zero] + rw [zero_le_iff, mat_cfc, ← Matrix.nonneg_iff_posSemidef] rw [_root_.cfc_nonneg_iff f A.mat, A.H.spectrum_real_eq_range_eigenvalues] grind @@ -262,13 +262,15 @@ theorem cfc_nonneg_of_nonneg (hA : 0 ≤ A) (hf : ∀ i ≥ 0, 0 ≤ f i) : theorem cfc_nonSingular (hf : ∀ i, f (A.H.eigenvalues i) ≠ 0) : NonSingular (A.cfc f) := by rw [nonSingular_iff_eigenvalue_ne_zero] obtain ⟨e, he⟩ := cfc_eigenvalues f A - simpa [he] using fun i ↦ hf (e i) + intro i + rw [congrFun he i] + exact hf (e i) theorem trace_mul_cfc (A : HermitianMat d 𝕜) (f : ℝ → ℝ) : (A.mat * (A.cfc f).mat).trace = ∑ i, A.H.eigenvalues i * f (A.H.eigenvalues i) := by conv_lhs => rw [A.eq_conj_diagonal] rw [cfc_conj_unitary] - simp [conj, Matrix.mul_assoc, A.H.eigenvectorUnitary.val.trace_mul_comm] + simp [conj_apply_mat, Matrix.mul_assoc, A.H.eigenvectorUnitary.val.trace_mul_comm] simp [← Matrix.mul_assoc, Matrix.IsHermitian.eigenvectorUnitary ] theorem norm_eq_sum_eigenvalues_sq (A : HermitianMat d 𝕜) : @@ -350,7 +352,6 @@ protected theorem cfc_continuous {f : ℝ → ℝ} (hf : Continuous f) : unfold HermitianMat.cfc suffices Continuous (fun A : HermitianMat d ℂ ↦ _root_.cfc f A.mat) by fun_prop - have h_compact_cover := LocallyCompactSpace.local_compact_nhds (X := HermitianMat d ℂ) apply continuous_of_continuousOn_iUnion_of_isOpen (ι := HermitianMat d ℂ × {x : ℝ // 0 < x}) (s := fun ab ↦ Metric.ball ab.1 ab.2) · rintro ⟨A, r, hr⟩ @@ -362,7 +363,8 @@ protected theorem cfc_continuous {f : ℝ → ℝ} (hf : Continuous f) : exact isCompact_Icc · simp only [Set.mem_Icc] exact fun _ _ ↦ eventually_nhdsWithin_of_forall hab - · simp + · rintro ⟨a, r, hr⟩ + exact Metric.isOpen_ball · ext x simp only [Set.mem_iUnion, Set.mem_univ, iff_true] use ⟨x, 1⟩ @@ -515,7 +517,7 @@ lemma continuousOn_cfc_of_compact {K : Set ℝ} {g : ℝ → ℝ} (hK : IsCompac obtain ⟨f, hf⟩ : ∃ f : ℝ → ℝ, ContinuousOn f (Set.Icc a b) ∧ ∀ x ∈ K, f x = g x := by have := @ContinuousMap.exists_restrict_eq; specialize this ( show IsClosed K from hK.isClosed ) ( ContinuousMap.mk ( fun x => g x ) <| by exact continuousOn_iff_continuous_domRestrict.mp hg ); - exact ⟨ _, this.choose.continuous.continuousOn, fun x hx => by simpa using congr_arg ( fun f => f ⟨ x, hx ⟩ ) this.choose_spec ⟩; + exact ⟨ _, this.choose.continuous.continuousOn, fun x hx => by exact congr_arg ( fun f => f ⟨ x, hx ⟩ ) this.choose_spec ⟩; exact fun ε εpos => by rcases this a b f hf.1 ε εpos with ⟨ p, hp ⟩ ; exact ⟨ p, fun x hx => by simpa only [ hf.2 x hx ] using hp x ( hab hx ) ⟩ ; exact ⟨ fun n => Classical.choose ( h_stone_weierstrass ( 1 / ( n + 1 ) ) ( by positivity ) ), fun n x hx => le_of_lt ( Classical.choose_spec ( h_stone_weierstrass ( 1 / ( n + 1 ) ) ( by positivity ) ) x hx ) ⟩; -- The sequence `A ↦ A.cfc (p_n)` converges uniformly to `A ↦ A.cfc g` on `{A | spectrum A ⊆ K}`. @@ -562,8 +564,9 @@ theorem continuous_cfc_joint_compact {X d : Type*} [TopologicalSpace X] [Fintype have h_eps_delta : ContinuousWithinAt (fun y => (A y).cfc (f x)) S x := by refine ContinuousOn.continuousWithinAt ?_ x_in_S exact (continuousOn_cfc_of_compact hT (hf.uncurry_left x x_in_S)).comp hA₂ hA₁ - rw [ ContinuousWithinAt ] at *; - rw [ Metric.tendsto_nhds ] at *; + simp only [ContinuousWithinAt] at *; + have h_eps_delta := Metric.tendsto_nhds.mp h_eps_delta + apply Metric.tendsto_nhds.mpr intro ε ε_pos obtain ⟨U, hU₁, hU₂⟩ : ∃ U ∈ nhds x, ∀ y ∈ U ∩ S, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ Real.sqrt (Fintype.card d) * (ε / (2 * Real.sqrt (Fintype.card d) + 1)) := by have h_eps_delta₁ : ∀ ε > 0, ∃ U ∈ nhds x, ∀ y ∈ U ∩ S, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ Real.sqrt (Fintype.card d) * ε := by @@ -586,6 +589,7 @@ theorem continuous_cfc_joint_compact {X d : Type*} [TopologicalSpace X] [Fintype dist_eq_norm ( ( A y ).cfc ( f x ) ) ( ( A x ).cfc ( f x ) )] open scoped Matrix.Norms.Frobenius + /- PROBLEM Eigenvalues of a `HermitianMat` are bounded by its (Frobenius) norm. @@ -670,7 +674,11 @@ lemma spectrum_subset_of_isOpen (A₀ : HermitianMat d ℂ) (U : Set ℝ) obtain ⟨δ_min, hδ_min_pos, hδ_min⟩ : ∃ δ_min > 0, ∀ i ∈ t, δ_min ≤ δ i := by by_cases ht : t.Nonempty <;> simp_all [Finset.Nonempty] · exact ⟨Finset.min' (t.image δ) ⟨_, Finset.mem_image_of_mem δ ht.choose_spec⟩, - by have := Finset.min'_mem (t.image δ) ⟨_, Finset.mem_image_of_mem δ ht.choose_spec⟩; aesop, + by + obtain ⟨i, hi, he⟩ := Finset.mem_image.mp + (Finset.min'_mem (t.image δ) ⟨_, Finset.mem_image_of_mem δ ht.choose_spec⟩) + rw [← he] + exact hδ i (ht₁ i hi), fun i hi => Finset.min'_le _ _ (Finset.mem_image_of_mem δ hi)⟩ · exact ⟨1, zero_lt_one⟩ refine' ⟨Min.min δ_min 1, lt_min hδ_min_pos zero_lt_one, fun x hx => _⟩ @@ -734,7 +742,7 @@ lemma continuousWithinAt_cfc_of_continuousOn {T : Set ℝ} {g : ℝ → ℝ} exact hg.mono hA₀ generalize_proofs at *; ( have := @ContinuousMap.exists_restrict_eq ℝ; - specialize this ( show IsClosed ( spectrum ℝ A₀.val ) from h_finite.isClosed ) ( ContinuousMap.mk ( fun x => g x ) <| by exact continuousOn_iff_continuous_domRestrict.mp h_cont ) ; rcases this with ⟨ h, hh ⟩ ; exact ⟨ h, h.continuous, fun x hx => by simpa using congr_arg ( fun f => f ⟨ x, hx ⟩ ) hh ⟩ ;)); + specialize this ( show IsClosed ( spectrum ℝ A₀.val ) from h_finite.isClosed ) ( ContinuousMap.mk ( fun x => g x ) <| by exact continuousOn_iff_continuous_domRestrict.mp h_cont ) ; rcases this with ⟨ h, hh ⟩ ; exact ⟨ h, h.continuous, fun x hx => by exact congr_arg ( fun f => f ⟨ x, hx ⟩ ) hh ⟩ ;)); obtain ⟨h, hh_cont, hh_eq⟩ := h_ext; have h_cfc_cont : ContinuousWithinAt (fun B => B.cfc h) {B : HermitianMat d ℂ | spectrum ℝ B.mat ⊆ T} A₀ := by exact Continuous.continuousWithinAt (HermitianMat.cfc_continuous hh_cont) @@ -776,7 +784,8 @@ lemma continuousWithinAt_cfc_of_continuousOn {T : Set ℝ} {g : ℝ → ℝ} · exact fun x hx => hx generalize_proofs at *; ( exact h_diff_small.trans_lt ( by rw [ mul_div, div_lt_iff₀ ] <;> nlinarith [ Real.sqrt_nonneg ( Fintype.card d : ℝ ), Real.sq_sqrt ( Nat.cast_nonneg ( Fintype.card d ) ) ] )))); - rw [ Metric.continuousWithinAt_iff ] at *; + have h_cfc_cont := Metric.continuousWithinAt_iff.mp h_cfc_cont + apply Metric.continuousWithinAt_iff.mpr intro ε hε obtain ⟨δ, hδ_pos, hδ⟩ := h_cfc_cont (ε / 2) (half_pos hε) obtain ⟨U, hU_nhds, hU⟩ := h_diff_small (ε / 2) (half_pos hε) @@ -971,7 +980,11 @@ theorem continuous_cfc_joint {X d : Type*} [TopologicalSpace X] [Fintype d] [Dec generalize_proofs at *; ( convert h_cont.add ( show ContinuousWithinAt ( fun x => ( A x |> HermitianMat.cfc ) ( f x ) - ( A x |> HermitianMat.cfc ) ( f x₀ ) ) S x₀ from ?_ ) using 1 ; aesop generalize_proofs at *; ( - exact tendsto_zero_iff_norm_tendsto_zero.mpr h_final |> fun h => h.trans ( by simp) ;))))) + change Filter.Tendsto + (fun x => (A x).cfc (f x) - (A x).cfc (f x₀)) (nhdsWithin x₀ S) + (nhds ((A x₀).cfc (f x₀) - (A x₀).cfc (f x₀))) + rw [sub_self] + exact tendsto_zero_iff_norm_tendsto_zero.mpr h_final ;))))) generalize_proofs at *; ( exact h_not_cont_at_x₀ <| fun x hx => h_cont x hx |> ContinuousWithinAt.mono <| by simp;) @@ -1043,7 +1056,17 @@ section integral open MeasureTheory open scoped Matrix.Norms.Frobenius -omit [DecidableEq d] in +local instance : ENormedAddCommMonoid (HermitianMat d 𝕜) := + NormedAddCommGroup.toENormedAddCommMonoid + +local instance : ENormedAddCommMonoid (Matrix d d 𝕜) := + NormedAddCommGroup.toENormedAddCommMonoid + +local instance : TopologicalSpace.PseudoMetrizableSpace (Matrix d d 𝕜) := + PseudoEMetricSpace.pseudoMetrizableSpace + +local instance : TopologicalSpace.PseudoMetrizableSpace (HermitianMat d 𝕜) := + PseudoEMetricSpace.pseudoMetrizableSpace /-- The integral of a Hermitian matrix function commutes with `toMat`. @@ -1059,8 +1082,9 @@ A sum of scaled constant matrices is integrable if the scalar functions are inte lemma intervalIntegrable_sum_smul_const (T₁ T₂ : ℝ) {μ : Measure ℝ} (g : ℝ → d → ℝ) (P : d → Matrix d d 𝕜) (hg : ∀ i, IntervalIntegrable (fun t ↦ g t i) μ T₁ T₂) : IntervalIntegrable (fun t ↦ ∑ i, g t i • P i) μ T₁ T₂ := by - simp_all [intervalIntegrable_iff] - exact integrable_finsetSum _ fun i _ ↦ Integrable.smul_const (hg i) _ + constructor + · exact integrable_finsetSum _ fun i _ ↦ (hg i).1.smul_const (P i) + · exact integrable_finsetSum _ fun i _ ↦ (hg i).2.smul_const (P i) /-- A function to Hermitian matrices is integrable iff its matrix values are integrable. @@ -1068,7 +1092,7 @@ A function to Hermitian matrices is integrable iff its matrix values are integra lemma intervalIntegrable_toMat_iff (A : ℝ → HermitianMat d 𝕜) (T₁ T₂ : ℝ) {μ : Measure ℝ} : IntervalIntegrable (fun t ↦ (A t).mat) μ T₁ T₂ ↔ IntervalIntegrable A μ T₁ T₂ := by --TODO Cleanup - simp [ intervalIntegrable_iff ]; + apply (intervalIntegrable_iff.trans ?_).trans intervalIntegrable_iff.symm constructor <;> intro h; · -- Since `toMat` is a linear isometry, the integrability of `A.toMat` implies the integrability of `A`. have h_toMat_integrable : IntegrableOn (fun t ↦ (A t).mat) (Set.uIoc T₁ T₂) μ → IntegrableOn A (Set.uIoc T₁ T₂) μ := by @@ -1156,14 +1180,14 @@ theorem _root_.Commute.exists_HermitianMat_cfc (hAB : Commute A.mat B.mat) : by_cases hC : C.IsHermitian · use ⟨C, hC⟩ constructor - · exact ⟨g₁, by simp [HermitianMat.ext_iff, hg₁]⟩ - · exact ⟨g₂, by simp [HermitianMat.ext_iff, hg₂]⟩ + · exact ⟨g₁, HermitianMat.ext hg₁⟩ + · exact ⟨g₂, HermitianMat.ext hg₂⟩ · change ¬(IsSelfAdjoint C) at hC rw [cfc_apply_of_not_predicate C hC] at hg₁ hg₂ use 0 constructor - · exact ⟨0, by simp [HermitianMat.ext_iff, hg₁]⟩ - · exact ⟨0, by simp [HermitianMat.ext_iff, hg₂]⟩ + · exact ⟨0, by simp [HermitianMat.ext_iff, mat_cfc, hg₁]⟩ + · exact ⟨0, by simp [HermitianMat.ext_iff, mat_cfc, hg₂]⟩ open ComplexOrder in theorem cfc_le_cfc_of_PosDef (hfg : ∀ i, 0 < i → f i ≤ g i) (hA : A.mat.PosDef) : diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean index 8a8041aef4..032557ca37 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean @@ -432,7 +432,8 @@ instance : OrderClosedTopology (HermitianMat d 𝕜) where instance : CompactIccSpace (HermitianMat d 𝕜) where isCompact_Icc := by intros A B - apply Metric.isCompact_of_isClosed_isBounded isClosed_Icc + have hclosed : IsClosed (Set.Icc A B) := isClosed_Icc + apply Metric.isCompact_of_isClosed_isBounded hclosed rw [Metric.isBounded_iff] use 2 * ‖B - A‖ rintro x ⟨hxA, hxB⟩ y ⟨hyA, hyB⟩ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean index 6aa4385f86..f07301dd97 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean @@ -33,15 +33,22 @@ def symmMul : HermitianMat d 𝕜 := by simp [selfAdjoint, IsSelfAdjoint, add_comm, Matrix.star_eq_conjTranspose]⟩ theorem symmMul_comm : A.symmMul B = B.symmMul A := by - rw [symmMul, symmMul, Subtype.mk.injEq, add_comm] + apply HermitianMat.ext + change (2 : 𝕜)⁻¹ • (A.mat * B.mat + B.mat * A.mat) = + (2 : 𝕜)⁻¹ • (B.mat * A.mat + A.mat * B.mat) + rw [add_comm] @[simp] theorem symmMul_zero : A.symmMul 0 = 0:= by - simp [symmMul] + apply HermitianMat.ext + change (2 : 𝕜)⁻¹ • (A.mat * 0 + 0 * A.mat) = 0 + simp @[simp] theorem zero_symmMul : symmMul 0 A = 0 := by - simp [symmMul] + apply HermitianMat.ext + change (2 : 𝕜)⁻¹ • (0 * A.mat + A.mat * 0) = 0 + simp theorem symmMul_toMat : (A.symmMul B).mat = (2 : 𝕜)⁻¹ • (A.mat * B.mat + B.mat * A.mat) := by diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean index 588cbabb63..a7cba43102 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean @@ -54,20 +54,25 @@ theorem lt_iff_posdef : A < B ↔ (B - A).mat.PosSemidef ∧ A ≠ B := instance : IsStrictOrderedModule ℝ (HermitianMat n 𝕜) where smul_lt_smul_of_pos_left a ha b b₂ hb := by rw [HermitianMat.lt_iff_posdef] at hb ⊢ - simp only [← smul_sub, ne_eq, smul_right_inj ha.ne'] - exact ⟨hb.left.smul ha.le, hb.right⟩ - smul_lt_smul_of_pos_right a ha b b2 hb := by + constructor + · change (a • b₂.mat - a • b.mat).PosSemidef + simpa only [mat_sub, smul_sub] using hb.1.smul ha.le + · intro h + apply hb.2 + apply HermitianMat.ext + have hm := congrArg HermitianMat.mat h + exact (smul_right_inj ha.ne').mp hm + smul_lt_smul_of_pos_right a ha b b₂ hb := by rw [HermitianMat.lt_iff_posdef] at ha ⊢ - rw [sub_zero] at ha - rw [← sub_pos] at hb - convert And.intro (ha.left.smul hb.le) ha.right using 1 - · simp [← sub_smul] - simp only [ne_eq, not_iff_not] constructor + · change (b₂ • a.mat - b • a.mat).PosSemidef + simpa only [sub_zero, mat_sub, mat_zero, sub_smul] using ha.1.smul (sub_nonneg.mpr hb.le) · intro h - rw [eq_comm, ← sub_eq_zero, ← sub_smul] at h - simpa [eq_comm, hb.ne'] using h - · rintro rfl; simp + have hm : (b₂ - b) • a.mat = 0 := by + rw [sub_smul] + exact sub_eq_zero.mpr (congrArg HermitianMat.mat h).symm + have hz : a.mat = 0 := (smul_eq_zero.mp hm).resolve_left (sub_ne_zero.mpr hb.ne') + exact ha.2 (HermitianMat.ext hz.symm) theorem posSemidef_iff_spectrum_Ici [DecidableEq n] (A : HermitianMat n 𝕜) : 0 ≤ A ↔ spectrum ℝ A.mat ⊆ Set.Ici 0 := by @@ -106,9 +111,18 @@ meta def evalHermitianMatTrace : PositivityExt where eval {_u _α} _zα _pα? e pure (.nonnegative (← mkAppM ``HermitianMat.trace_nonneg #[pfA])) --Without these shortcut instances, `gcongr` fails to close certain goals...? Why? TODO -instance : PosSMulMono ℝ (HermitianMat n 𝕜) := inferInstance - -instance : SMulPosMono ℝ (HermitianMat n 𝕜) := inferInstance +instance : PosSMulMono ℝ (HermitianMat n 𝕜) where + smul_le_smul_of_nonneg_left a ha b c hbc := by + rw [le_iff] at hbc ⊢ + change (a • c.mat - a • b.mat).PosSemidef + simpa only [mat_sub, smul_sub] using hbc.smul ha + +instance : SMulPosMono ℝ (HermitianMat n 𝕜) where + smul_le_smul_of_nonneg_right a ha b c hbc := by + rw [zero_le_iff] at ha + rw [le_iff] + change (c • a.mat - b • a.mat).PosSemidef + simpa only [sub_smul] using ha.smul (sub_nonneg.mpr hbc) --Without explicitly giving this instance, Lean times out trying to find it sometimes. instance : PosSMulReflectLE ℝ (HermitianMat n 𝕜) := @@ -427,8 +441,7 @@ lemma inv_conj [DecidableEq n] {M : Matrix n n 𝕜} (hM : IsUnit M) : simp only [Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero, ne_eq] at hM simp [Matrix.conjTranspose_nonsing_inv, hM] ext1 - simp only [conj, AddMonoidHom.coe_mk, ZeroHom.coe_mk, Matrix.conjTranspose_conjTranspose] - simp only [mat_inv, mat_mk] + simp only [mat_inv, conj_apply_mat, Matrix.conjTranspose_conjTranspose] rw [Matrix.mul_inv_rev, Matrix.mul_inv_rev, Matrix.inv_eq_left_inv h_inv, mul_assoc] theorem le_iff_mulVec_le_mulVec (A B : HermitianMat n 𝕜) : @@ -446,7 +459,7 @@ theorem inner_mulVec_nonneg (hA : 0 ≤ A) (v : n → 𝕜) : theorem mem_ker_of_inner_mulVec_zero [DecidableEq n] (hA : 0 ≤ A) (v : EuclideanSpace 𝕜 n) (h : star v ⬝ᵥ A.mat *ᵥ v = 0) : v ∈ A.ker := by - have := ((zero_le_iff.mp hA).dotProduct_mulVec_zero_iff v).mp h + have := ((zero_le_iff.mp hA).dotProduct_mulVec_zero_iff (x := v)).mp h exact congr(WithLp.toLp 2 $this) theorem ker_add [DecidableEq n] (hA : 0 ≤ A) (hB : 0 ≤ B) : @@ -461,8 +474,8 @@ theorem ker_add [DecidableEq n] (hA : 0 ≤ A) (hB : 0 ≤ B) : rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA' hB' obtain ⟨hzA, hzB⟩ := (add_eq_zero_iff_of_nonneg (hA'.2 v) (hB'.2 v)).mp h3 rw [← Matrix.posSemidef_iff_dotProduct_mulVec] at hA' hB' - exact ⟨(hA'.dotProduct_mulVec_zero_iff v).mp hzA, - (hB'.dotProduct_mulVec_zero_iff v).mp hzB⟩ + exact ⟨(hA'.dotProduct_mulVec_zero_iff (x := v)).mp hzA, + (hB'.dotProduct_mulVec_zero_iff (x := v)).mp hzB⟩ · simp +contextual [Matrix.add_mulVec] theorem ker_sum [DecidableEq n] (f : ι → HermitianMat n 𝕜) (hf : ∀ i, 0 ≤ f i) : @@ -489,19 +502,21 @@ theorem ker_sum [DecidableEq n] (f : ι → HermitianMat n 𝕜) (hf : ∀ i, 0 theorem ker_conj [DecidableEq n] (hA : 0 ≤ A) (B : Matrix n n 𝕜) : (A.conj B).ker = Submodule.comap (Matrix.toEuclideanLin B.conjTranspose) A.ker := by - ext v; simp [HermitianMat.conj]; + ext v + simp only [Submodule.mem_comap] + change v ∈ (A.conj B).ker ↔ (Matrix.toEuclideanLin Bᴴ) v ∈ A.ker constructor <;> intro h; - · have := Matrix.PosSemidef.dotProduct_mulVec_zero_iff ( show Matrix.PosSemidef A.mat from zero_le_iff.mp hA ); - convert this ( Bᴴ.mulVec v ) |>.1 _ using 1; + · have hz := (zero_le_iff.mp hA).dotProduct_mulVec_zero_iff (x := Bᴴ.mulVec v) + convert hz.1 _ using 1; · rw [ mem_ker_iff_mulVec_zero ]; congr! 2; · convert congr_arg ( fun x : EuclideanSpace _ _ => star v.ofLp ⬝ᵥ x ) h using 1 simp [Matrix.mulVec_mulVec, Matrix.dotProduct_mulVec] - · simp [Matrix.mul_assoc, Matrix.dotProduct_mulVec, Matrix.mulVec_mulVec, Matrix.star_mulVec, Matrix.conjTranspose_conjTranspose, lin] + · simp [conj_apply_mat, Matrix.mul_assoc, Matrix.dotProduct_mulVec, Matrix.star_mulVec, Matrix.conjTranspose_conjTranspose, lin] · simp [dotProduct] · simp only [ker, Matrix.mul_assoc, LinearMap.mem_ker] convert congr_arg B.toEuclideanLin h using 1 - · simp [HermitianMat.lin, Matrix.toEuclideanLin] + · simp [HermitianMat.lin, conj_apply_mat, Matrix.toEuclideanLin, Matrix.mulVec_mulVec] · exact Eq.symm (LinearMap.map_zero (Matrix.toEuclideanLin B)) theorem ker_le_of_le_smul {α : ℝ} [DecidableEq n] (hα : α ≠ 0) (hA : 0 ≤ A) (hAB : A ≤ α • B) : B.ker ≤ A.ker := by @@ -684,13 +699,13 @@ example (M : Matrix n m ℂ) : 0 ≤ M * M.conjTranspose := by positivity example (M : Matrix m n ℂ) : (0 : HermitianMat n ℂ) ≤ ⟨M.conjTranspose * M, Matrix.isHermitian_conjTranspose_mul_self M⟩ := by - positivity + exact zero_le_iff.mpr (Matrix.posSemidef_conjTranspose_mul_self M) -- Test: ⟨M * Mᴴ, _⟩ nonneg as HermitianMat example (M : Matrix n m ℝ) : (0 : HermitianMat n ℝ) ≤ ⟨M * M.conjTranspose, Matrix.isHermitian_mul_conjTranspose_self M⟩ := by - positivity + exact zero_le_iff.mpr (Matrix.posSemidef_self_mul_conjTranspose M) example (M : Matrix n n ℂ) (i : n) (A : HermitianMat n ℂ) (hA : 0 ≤ A) : 0 ≤ (A + ⟨_, M.isHermitian_mul_conjTranspose_self⟩ + 0).H.eigenvalues i := by diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean index bed427b628..754ed0de62 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean @@ -81,7 +81,12 @@ theorem projector_ker : (projector S).ker = Sᗮ := by @[simp] theorem trace_projector : (projector S).trace = (Module.finrank 𝕜 S : ℝ) := by suffices h_trace : ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = Module.finrank 𝕜 S by - simp [projector, trace_eq_re_trace, h_trace] + rw [trace_eq_re_trace] + change RCLike.re ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix + (EuclideanSpace.basisFun n 𝕜).toBasis + (EuclideanSpace.basisFun n 𝕜).toBasis).trace = _ + rw [h_trace] + simp suffices h_trace : ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = (LinearMap.id.toMatrix (Module.finBasis 𝕜 S) (Module.finBasis 𝕜 S)).trace by simp [h_trace] rw [LinearMap.toMatrix_comp _ (Module.finBasis 𝕜 ↥S), Matrix.trace_mul_comm, ← LinearMap.toMatrix_comp] @@ -281,7 +286,7 @@ theorem projLE_le_one : {A ≤ₚ B} ≤ 1 := by --The whole `rw` line is a defeq, i.e. `change _root_.cfc _ (B - A).mat ≤ 1` works too. --TODO better API. open MatrixOrder in - rw [← Subtype.coe_le_coe, val_eq_coe, selfAdjoint.val_one] + change _root_.cfc (fun x ↦ if 0 ≤ x then 1 else 0) (B - A).mat ≤ 1 apply cfc_le_one (f := fun x ↦ if 0 ≤ x then 1 else 0) intros; split <;> norm_num diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean index a28d6d255e..c91c844e87 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean @@ -86,21 +86,29 @@ variable [Fintype d] theorem conj_submatrix (B : Matrix d₂ d₄ 𝕜) (e : d₃ ≃ d₂) (f : d → d₄) : A.conj (B.submatrix e f) = (A.conj (B.submatrix id f)).reindex e.symm := by ext1 - simp [conj_apply, ← Matrix.submatrix_mul_equiv (e₂ := .refl d)] + change B.submatrix e f * A.mat * (B.submatrix e f).conjTranspose = + (B.submatrix id f * A.mat * (B.submatrix id f).conjTranspose).submatrix e e + simp [← Matrix.submatrix_mul_equiv (e₂ := .refl d)] theorem reindex_eq_conj [DecidableEq d] (e : d ≃ d₂) : A.reindex e = A.conj (Matrix.reindex e (.refl d) 1) := by - ext : 3 - simp [-mat_apply, reindex, conj_apply, Matrix.submatrix, - Matrix.mul_apply, Matrix.one_apply] + apply HermitianMat.ext + change Matrix.reindex e e A.mat = + Matrix.reindex e (Equiv.refl d) 1 * A.mat * + (Matrix.reindex e (Equiv.refl d) 1).conjTranspose + ext i j + change A.mat (e.symm i) (e.symm j) = + ∑ k, (∑ l, (if e.symm i = l then 1 else 0) * A.mat l k) * + star (if e.symm j = k then (1 : 𝕜) else 0) + simp [ite_mul, mul_ite, apply_ite] variable [Fintype d₂] [DecidableEq d] [DecidableEq d₂] theorem ker_reindex : (A.reindex e).ker = A.ker.comap (LinearEquiv.euclideanOfRelabel 𝕜 e).toLinearMap := by - dsimp only [reindex, ker, lin] - simp only [mat_mk] + change (Matrix.toEuclideanLin (Matrix.reindex e e A.mat)).ker = _ rw [Matrix.reindex_toEuclideanLin, LinearEquiv.ker_comp, LinearMap.ker_comp] + rfl @[simp] theorem ker_reindex_le_iff : diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean index d58231f5cf..9423d4dcc9 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean @@ -151,7 +151,8 @@ theorem trace_eq_one_iff (A : HermitianMat n 𝕜) : A.trace = 1 ↔ A.mat.trace @[simp] theorem trace_reindex (A : HermitianMat n ℂ) (e : n ≃ m) : (A.reindex e).trace = A.trace := by - simp [reindex, trace_eq_re_trace] + rw [trace_eq_re_trace, trace_eq_re_trace, mat_reindex] + simp [Matrix.reindex_apply] end RCLike section partialTrace diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean index 7eb5ec1418..eb02292f1b 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean @@ -390,9 +390,16 @@ theorem LinearMap.IsSymmetric.directSum_isInternal_of_commute' {𝕜 E : Type*} refine Submodule.sum_mem _ fun i hi ↦ ?_ have hyi := Submodule.coe_mem (y i) simp only [Submodule.mem_inf, mem_genEigenspace_one] at hyi - refine Submodule.mem_iSup_of_mem ⟨⟨i.2, ?_⟩, ⟨i.1, ?_⟩⟩ (by simp) - <;> simp only [HasUnifEigenvalue, ne_eq, Submodule.eq_bot_iff, mem_genEigenspace_one, not_forall] - <;> refine ⟨y i, by tauto, by simpa using hi⟩ + have hiA : HasUnifEigenvalue A i.2 1 := by + simp only [HasUnifEigenvalue, ne_eq, Submodule.eq_bot_iff, mem_genEigenspace_one, + not_forall] + exact ⟨y i, hyi.1, by simpa using hi⟩ + have hiB : HasUnifEigenvalue B i.1 1 := by + simp only [HasUnifEigenvalue, ne_eq, Submodule.eq_bot_iff, mem_genEigenspace_one, + not_forall] + exact ⟨y i, hyi.2, by simpa using hi⟩ + apply Submodule.mem_iSup_of_mem (⟨i.2, hiA⟩, ⟨i.1, hiB⟩) + exact Submodule.coe_mem (y i) intro x rw [Submodule.eq_top_iff'] at h_sum specialize h_sum x diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean index 30314a68ea..b520e1cac5 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean @@ -859,39 +859,51 @@ variable {d 𝕜 : Type*} [Fintype d] [DecidableEq d] [RCLike 𝕜] @[simp] theorem cfc_diagonal (g : d → ℝ) (f : ℝ → ℝ) : cfc f (Matrix.diagonal (fun x ↦ (g x : 𝕜))) = diagonal (RCLike.ofReal ∘ f ∘ g) := by - --Thanks Aristotle have h_self_adjoint : _root_.IsSelfAdjoint (diagonal (fun x => (g x : 𝕜))) := by - change Matrix.conjTranspose _ = _ - simp [Matrix.conjTranspose] - --TODO cfc_cont_tac - rw [cfc, dif_pos ⟨h_self_adjoint, continuousOn_iff_continuous_domRestrict.mpr <| by fun_prop⟩] - rw [cfcHom_eq_of_continuous_of_map_id] - rotate_left - · refine' { .. } - use fun f ↦ Matrix.diagonal fun x ↦ f ⟨g x, (by - simpa [algebraMap_eq_diagonal, diagonal_apply] using - congr_arg (· x x) ·.exists_left_inv.choose_spec - )⟩ - · simp - · simp [diagonal, ← Matrix.ext_iff, mul_apply] - intro x y i j - split_ifs with h - · subst h; simp - · rfl - · simp - · simp [diagonal, funext_iff] - intro x y i j - split_ifs with h - · simp - · simp - · simp [← ext_iff, diagonal] - exact fun r i j ↦ rfl - · simp [← ext_iff, diagonal] - grind [RCLike.conj_ofReal] - · dsimp [diagonal] - continuity - · simp [diagonal] - · simp [diagonal] + change Matrix.conjTranspose _ = _ + simp [Matrix.conjTranspose] + have hg (x : d) : g x ∈ spectrum ℝ (diagonal (fun y => (g y : 𝕜))) := by + intro hc + have h := congrArg (fun M : Matrix d d 𝕜 => M x x) hc.exists_left_inv.choose_spec + simpa [algebraMap_eq_diagonal, diagonal_apply] using h + let φ : C(spectrum ℝ (diagonal (fun x => (g x : 𝕜))), ℝ) →⋆ₐ[ℝ] Matrix d d 𝕜 := { + toFun := fun h => diagonal fun x => RCLike.ofReal (h ⟨g x, hg x⟩) + map_zero' := by simp + map_one' := by simp + map_mul' := by + intro h k + simp only [ContinuousMap.coe_mul, Pi.mul_apply, RCLike.ofReal_mul, + diagonal_mul_diagonal] + map_add' := by + intro h k + simp only [ContinuousMap.coe_add, Pi.add_apply, RCLike.ofReal_add, diagonal_add] + commutes' := by + intro r + simp only [Algebra.algebraMap_eq_smul_one, ContinuousMap.smul_apply, + ContinuousMap.one_apply, smul_eq_mul, mul_one] + exact (diagonal_smul r (1 : d → 𝕜)).trans + (congrArg (fun M : Matrix d d 𝕜 => r • M) diagonal_one') + map_star' := by + intro h + simp only [star_trivial, star_eq_conjTranspose, diagonal_conjTranspose, Pi.star_def, + RCLike.star_def, RCLike.conj_ofReal] } + have hφ : Continuous φ := by + apply continuous_matrix + intro i j + change Continuous (fun h : C(spectrum ℝ (diagonal (fun x => (g x : 𝕜))), ℝ) => + diagonal (fun x => (h ⟨g x, hg x⟩ : 𝕜)) i j) + by_cases h : i = j + · subst j + simp only [diagonal_apply_eq] + exact RCLike.continuous_ofReal.comp (continuous_eval_const _) + · simp only [diagonal_apply_ne _ h] + exact continuous_const + rw [cfc, dite_eq_left ⟨h_self_adjoint, + continuousOn_iff_continuous_domRestrict.mpr (by fun_prop)⟩] + rw [cfcHom_eq_of_continuous_of_map_id h_self_adjoint φ hφ (by + ext i j + by_cases h : i = j <;> simp [φ, diagonal_apply, h])] + rfl theorem PosSemidef.pos_of_mem_spectrum {A : Matrix d d 𝕜} (hA : A.PosSemidef) (r : ℝ) : r ∈ spectrum ℝ A → 0 ≤ r := by diff --git a/LeanPool/EuclideanJordan/StructureSolution.lean b/LeanPool/EuclideanJordan/StructureSolution.lean index 774da480b1..b387ab9137 100644 --- a/LeanPool/EuclideanJordan/StructureSolution.lean +++ b/LeanPool/EuclideanJordan/StructureSolution.lean @@ -57,6 +57,8 @@ The one piece of real work is formal reality. The challenge states it as a hypot only by reindexing along `Finset.equivFin`, done inline in each of the two positivity proofs. -/ +namespace EuclideanJordan.StructureSolution + namespace JordanTraceForm variable {J : Type*} [NonUnitalNonAssocCommRing J] [Module ℝ J] [IsScalarTower ℝ J J] @@ -505,3 +507,5 @@ theorem finrank_frameBlock_diag [FiniteDimensional ℝ J] (F : JordanFrame J n) end JordanFramePeirce end + +end EuclideanJordan.StructureSolution From c1cf1d9e843d0ab9951448742ecc88550778a2d9 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Mon, 21 Sep 2026 21:29:56 +0000 Subject: [PATCH 4/6] Complete Euclidean Jordan port and document matrix and frame APIs --- LeanPool.lean | 52 ++ LeanPool/EuclideanJordan.lean | 4 +- .../EuclideanJordan/Bridge.lean | 14 +- .../EuclideanJordan/Class.lean | 27 +- .../EuclideanJordan/Connection.lean | 11 +- .../EuclideanJordan/Frame.lean | 15 +- .../EuclideanJordan/FrameExists.lean | 18 +- .../EuclideanJordan/FramePeirce.lean | 12 +- .../EuclideanJordan/FramePeirceMul.lean | 42 +- .../EuclideanJordan/HermitianBilin.lean | 5 +- .../EuclideanJordan/HermitianCarrier.lean | 54 +- .../EuclideanJordan/Order.lean | 23 +- .../EuclideanJordan/OrderAuto.lean | 34 +- .../EuclideanJordan/OrderUnitSpace.lean | 9 +- .../EuclideanJordan/Pattern.lean | 4 +- .../EuclideanJordan/Peirce.lean | 5 +- .../EuclideanJordan/PeirceMul.lean | 12 +- .../EuclideanJordan/PeirceSubalgebra.lean | 15 +- .../EuclideanJordan/Power.lean | 3 +- .../EuclideanJordan/PowerAssoc.lean | 3 +- .../EuclideanJordan/EuclideanJordan/Rank.lean | 15 +- .../EuclideanJordan/Spectral.lean | 46 +- .../EuclideanJordan/Subalgebra.lean | 6 +- .../EuclideanJordan/TraceForm.lean | 21 +- .../EuclideanJordan/Vendor.lean | 3 +- .../EuclideanJordan/Vendor/HermitianMat.lean | 3 +- .../Vendor/HermitianMat/Basic.lean | 96 ++- .../Vendor/HermitianMat/CFC.lean | 679 ++++++++++++------ .../Vendor/HermitianMat/Inner.lean | 101 ++- .../Vendor/HermitianMat/Jordan.lean | 7 + .../Vendor/HermitianMat/NonSingular.lean | 7 +- .../Vendor/HermitianMat/Order.lean | 108 ++- .../Vendor/HermitianMat/Proj.lean | 107 ++- .../Vendor/HermitianMat/Reindex.lean | 5 +- .../Vendor/HermitianMat/Trace.lean | 18 +- .../EuclideanJordan/Vendor/Isometry.lean | 257 ++++--- .../EuclideanJordan/Vendor/LinearEquiv.lean | 2 +- .../EuclideanJordan/Vendor/Matrix.lean | 613 ++++++++++------ .../EuclideanJordan/Vendor/Misc.lean | 49 +- .../EuclideanJordan/Vendor/Tactic.lean | 3 +- .../EuclideanJordan/Witness.lean | 34 +- .../EuclideanJordan/SpectralSolution.lean | 2 +- .../EuclideanJordan/StructureSolution.lean | 5 +- .../EuclideanJordan/TraceFormSolution.lean | 2 +- LeanPool/projects.yml | 4 +- 45 files changed, 1701 insertions(+), 854 deletions(-) diff --git a/LeanPool.lean b/LeanPool.lean index 86e3a0c6f8..b994f07d44 100644 --- a/LeanPool.lean +++ b/LeanPool.lean @@ -1552,6 +1552,58 @@ import LeanPool.ErdosTuzaValtr.Main.Lemmas.JoinN2N3JoinN3N2 import LeanPool.ErdosTuzaValtr.Main.Lemmas.JoinN2N3N2 import LeanPool.ErdosTuzaValtr.Main.Main import LeanPool.EuclideanJordan +import LeanPool.EuclideanJordan.EuclideanJordan +import LeanPool.EuclideanJordan.EuclideanJordan.Block +import LeanPool.EuclideanJordan.EuclideanJordan.Bridge +import LeanPool.EuclideanJordan.EuclideanJordan.Class +import LeanPool.EuclideanJordan.EuclideanJordan.Connection +import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +import LeanPool.EuclideanJordan.EuclideanJordan.Frame +import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce +import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul +import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin +import LeanPool.EuclideanJordan.EuclideanJordan.HermitianCarrier +import LeanPool.EuclideanJordan.EuclideanJordan.Order +import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto +import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace +import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal +import LeanPool.EuclideanJordan.EuclideanJordan.Pattern +import LeanPool.EuclideanJordan.EuclideanJordan.Peirce +import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul +import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra +import LeanPool.EuclideanJordan.EuclideanJordan.Power +import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc +import LeanPool.EuclideanJordan.EuclideanJordan.Rank +import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra +import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes +import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes.Attribute +import LeanPool.EuclideanJordan.EuclideanJordan.Witness +import LeanPool.EuclideanJordan.FramePeirceSolution +import LeanPool.EuclideanJordan.KoecherSolution +import LeanPool.EuclideanJordan.SpectralSolution +import LeanPool.EuclideanJordan.StructureSolution +import LeanPool.EuclideanJordan.TraceFormSolution import LeanPool.EvenGraphCycles import LeanPool.EventStructures import LeanPool.EventStructures.Basic diff --git a/LeanPool/EuclideanJordan.lean b/LeanPool/EuclideanJordan.lean index 1d5fedc702..050f563c9e 100644 --- a/LeanPool/EuclideanJordan.lean +++ b/LeanPool/EuclideanJordan.lean @@ -58,12 +58,12 @@ import LeanPool.EuclideanJordan.StructureSolution import LeanPool.EuclideanJordan.TraceFormSolution /-! -# Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition +# Euclidean Jordan algebras and the frame Peirce decomposition Source: url:https://github.com/ehrlich-b/euclidean-jordan Authors: Bryan Ehrlich Status: verified -Main declarations: `EuclideanJordan.frameBlock_isInternal`, `EuclideanJordan.finrank_frameBlock_diag` +Main declarations: `EuclideanJordan.frameBlock_isInternal` Tags: nonassociative-algebra MSC: 17C20, 17C27, 17C37, 17C65, 17A15, 46L70 -/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean index 1ddb6fe067..4872523b50 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean @@ -57,10 +57,10 @@ def ringOfBilinear (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (hcomm : ∀ x y, m x y = mul := fun x y => m x y left_distrib := fun a b c => (m a).map_add b c right_distrib := fun a b c => by - show m (a + b) c = m a c + m b c + change m (a + b) c = m a c + m b c rw [map_add]; rfl zero_mul := fun a => by - show m 0 a = 0 + change m 0 a = 0 rw [map_zero]; rfl mul_zero := fun a => (m a).map_zero mul_comm := hcomm } @@ -77,8 +77,8 @@ theorem peirce_poly_bilinear (hcomm : ∀ x y : J, m x y = m y x) (hjordan : ∀ a b : J, m (m a b) (m a a) = m a (m b (m a a))) {c : J} (hc : m c c = c) (y : J) : (2 : ℕ) • m c (m c (m c y)) + m c y = (3 : ℕ) • m c (m c y) := by - letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm - letI : IsCommJordan J := ⟨hjordan⟩ + let : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + let : IsCommJordan J := ⟨hjordan⟩ exact peirce_poly hc y /-- **Operator commutation in bilinear-map vocabulary**: `L_a` and `L_b` commute at `w`, for @@ -88,9 +88,9 @@ theorem opCommute_scalarOn_bilinear (hcomm : ∀ x y : J, m x y = m y x) {c a a₀ b : J} {mu : ℝ} (hc : m c c = c) (ha : a = mu • c + a₀) (ha₀ : m c a₀ = 0) (hb : m c b = b) (w : J) : m a (m b w) = m b (m a w) := by - letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm - letI : IsCommJordan J := ⟨hjordan⟩ - letI : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ + let : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + let : IsCommJordan J := ⟨hjordan⟩ + let : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ exact opCommute_scalarOn hc ha ha₀ hb w end Bridge diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean index 4af054892c..1ae49ec68e 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean @@ -20,7 +20,8 @@ The abstract modules of this library built before this one — `Peirce`, `Peirce and `Spectral` — state their hypotheses as a *tuple* drawn from `[NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] [IsScalarTower ℝ J J] [IsFormallyReal J] [Module.Finite ℝ J]`, each module taking the sub-tuple it needs; or — on the -Euclidean side of `EuclideanJordan/Order.lean` and in `Spectral`'s interface section — as a bilinear map +Euclidean side of `EuclideanJordan/Order.lean` and in `Spectral`'s interface section — as a + bilinear map `m : J →ₗ[ℝ] J →ₗ[ℝ] J` carrying `hcomm`/`hjordan`/`hassoc` as ordinary hypotheses. (`Witness` and `Spectral`'s concrete section state theirs over `HermitianMat` instead.) Both abstract vocabularies are correct and neither is a *class*, so a theorem about a Euclidean Jordan @@ -47,7 +48,8 @@ and nothing here claims otherwise. ★ The product is placed **on top of** the additive group of the inner-product space, never alongside a second one. Assuming `[NormedAddCommGroup J]` and `[NonUnitalNonAssocCommRing J]` simultaneously produces two `AddCommGroup J` instances and `Module ℝ J` then fails to -synthesise; `EuclideanJordan/Bridge.lean` records that diamond and `ringOfBilinear` dodges it by building +synthesise; `EuclideanJordan/Bridge.lean` records that diamond and `ringOfBilinear` dodges it by + building the multiplicative structure on the *ambient* additive group. This class is that dodge promoted from a `def` to a `class`: it `extends Mul J, One J` over `[NormedAddCommGroup J] [InnerProductSpace ℝ J]`, so only one `AddCommGroup J` is ever in play @@ -60,7 +62,8 @@ Consequently `ringOfBilinear (jmulₗ J) mul_comm = instNonUnitalNonAssocCommRin ## What finite-dimensionality is, and is not, needed for `FiniteDimensional ℝ J` is deliberately **not** a field of the class. It is genuinely required -downstream: `EuclideanJordan/Spectral.lean` records that its `spectral_resolution_bilinear` — which is +downstream: `EuclideanJordan/Spectral.lean` records that its `spectral_resolution_bilinear` — + which is `spectral_resolution_complete` in bilinear vocabulary, and carries the same hypotheses minus the inner product — is false without it, `ℝ[X]` satisfying every other hypothesis with no nonconstant resolution. So the dimension is carried as a separate instance argument at exactly the theorems @@ -71,7 +74,8 @@ vanishing sum of squares against the unit turns `∑ᵢ ⟪xᵢ ∘ xᵢ, 1⟫` application of `inner_assoc`, and a vanishing sum of nonnegative reals has vanishing terms. This corrects the build plan on two points. The plan derived the instance from -`EuclideanJordan/Spectral.lean`'s `isFormallyReal_of_fin` under `[FiniteDimensional ℝ J]`. That lemma +`EuclideanJordan/Spectral.lean`'s `isFormallyReal_of_fin` under `[FiniteDimensional ℝ J]`. That + lemma cannot supply it: `isFormallyReal_of_fin` *takes formal reality as a hypothesis*, in `Fin k` form, and does nothing but reindex it to the `Finset` form the class `IsFormallyReal` carries. The derivation had to come from the inner product instead — and once it does, the dimension @@ -79,7 +83,8 @@ hypothesis turns out to be unused. ## Scope -Almost all of this file is repackaging: the two restatements at the end (`spectral_resolution_complete'`, `peirce_add_add'`) discharge the claim +Almost all of this file is repackaging: the two restatements at the end + (`spectral_resolution_complete'`, `peirce_add_add'`) discharge the claim that the existing layer is reachable from the class, and are not new results. ★ Two declarations are *not* repackaging, and the file should not be described as if they were. @@ -88,7 +93,8 @@ associative inner product, and the existing layer does not contain that derivati takes formal reality as a hypothesis at every abstract site (`EuclideanJordan/Spectral.lean`'s `isFormallyReal_of_fin` *receives* it and does nothing but reindex; `EuclideanJordan/Order.lean`'s `orderUnitSpaceOfBilinear` receives it as `[IsFormallyReal J]`), and derives it only on the -concrete carrier, in `EuclideanJordan/Witness.lean`'s `instIsFormallyReal` for `HermitianMat d 𝕜`. Both new +concrete carrier, in `EuclideanJordan/Witness.lean`'s `instIsFormallyReal` for `HermitianMat d + 𝕜`. Both new declarations are short; the point is only that "this file contains no new mathematics" would be false. @@ -195,7 +201,8 @@ end EuclideanJordanAlgebra /-! ## The bridge to the bilinear-map vocabulary -`EuclideanJordan/Order.lean`'s Euclidean section and `EuclideanJordan/Spectral.lean`'s interface section state +`EuclideanJordan/Order.lean`'s Euclidean section and `EuclideanJordan/Spectral.lean`'s interface + section state everything over a bundled `m : J →ₗ[ℝ] J →ₗ[ℝ] J` carrying `hcomm`, `hjordan`, `hassoc` and a `Fin k`-indexed formal-reality hypothesis. `jmulₗ` is the class's product in that vocabulary and the five lemmas after it are exactly that hypothesis tuple, so a consumer of @@ -213,7 +220,8 @@ variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] [Euclidean @[simp] theorem jmulₗ_apply (x y : J) : jmulₗ J x y = x * y := rfl -/-- ★ The class and `EuclideanJordan/Bridge.lean`'s `ringOfBilinear` produce the **same** ring structure, +/-- ★ The class and `EuclideanJordan/Bridge.lean`'s `ringOfBilinear` produce the **same** ring +structure, definitionally. This is the precise sense in which the class does not introduce a second multiplicative structure alongside the one the existing layer runs on. -/ theorem ringOfBilinear_jmulₗ : @@ -248,7 +256,8 @@ theorem spectral_resolution_complete' [FiniteDimensional ℝ J] (x : J) : IsOrthIdemFamily c ∧ (∑ i, c i) = 1 ∧ x = ∑ i, lam i • c i := spectral_resolution_complete 1 EuclideanJordanAlgebra.one_mul x -/-- **The Peirce decomposition at a single idempotent, over the class.** `EuclideanJordan/Peirce.lean`'s +/-- **The Peirce decomposition at a single idempotent, over the class.** +`EuclideanJordan/Peirce.lean`'s `peirce_add_add` needs no idempotency hypothesis: the three projections sum to the identity for every `c`. -/ theorem peirce_add_add' (c y : J) : peirceOne c y + peirceHalf c y + peirceZero c y = y := diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean index f5c50f936a..1185dd930b 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean @@ -14,7 +14,8 @@ import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul /-! # Connections between frame blocks -`EuclideanJordan/FramePeirceMul.lean` gives the multiplication table of a Jordan frame's blocks. This file +`EuclideanJordan/FramePeirceMul.lean` gives the multiplication table of a Jordan frame's blocks. + This file proves the two identities that turn that table into an *algebra*, and they are the engine of Jacobson coordinatization. @@ -124,7 +125,8 @@ theorem inner_sq_p_of_mem (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x /-! ## The square of an off-diagonal element -★ `EuclideanJordan/FramePeirceMul.lean`'s `frameBlockRaw_mul_self_eq` gives `x ∘ x = a • pᵢ + b • pⱼ`. +★ `EuclideanJordan/FramePeirceMul.lean`'s `frameBlockRaw_mul_self_eq` gives `x ∘ x = a • pᵢ + b + • pⱼ`. This section proves `a = b`, so that the square is a multiple of the *idempotent* `pᵢ + pⱼ` and the coefficient is a quadratic form. The argument is power associativity, not the trace form: `x⁴` computed as `x² ∘ x²` and as `x ∘ (x ∘ x²)` gives `a² = a(a+b)/2` and @@ -135,7 +137,8 @@ section Square variable [FiniteDimensional ℝ J] omit [FiniteDimensional ℝ J] in -/-- `x⁴ = x² ∘ x²` and `x⁴ = x ∘ (x ∘ x²)` agree — `EuclideanJordan/PowerAssoc.lean`'s `jpow_mul_jpow` at +/-- `x⁴ = x² ∘ x²` and `x⁴ = x ∘ (x ∘ x²)` agree — `EuclideanJordan/PowerAssoc.lean`'s +`jpow_mul_jpow` at `(1, 1)`, unfolded. -/ theorem sq_mul_sq_eq (x : J) : (x * x) * (x * x) = x * (x * (x * x)) := by have h := jpow_mul_jpow x 1 1 @@ -475,7 +478,7 @@ def connEquiv {F : JordanFrame J n} {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ right_inv z := Subtype.ext (connMap_connMap (Ne.symm hij) hik hjk hc.symm z.2) /-- **Connected blocks have the same dimension.** -/ -theorem finrank_frameBlockRaw_eq [FiniteDimensional ℝ J] {F : JordanFrame J n} {i j k : Fin n} +theorem finrank_frameBlockRaw_eq {F : JordanFrame J n} {i j k : Fin n} (hij : i ≠ j) (hjk : j ≠ k) (hik : i ≠ k) {c : J} (hc : IsConnector F i j c) : Module.finrank ℝ ↥(frameBlockRaw F j k) = Module.finrank ℝ ↥(frameBlockRaw F i k) := (connEquiv hij hjk hik hc).finrank_eq diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean index 82ce5142a6..60448e274e 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean @@ -53,7 +53,8 @@ the one under `NonUnitalNonAssocCommRing`. With both in scope `Module ℝ J` fai at `peirceOne`'s use site: it is an `AddCommGroup` diamond, not a gap in the mathematics. `EuclideanJordan/Bridge.lean` resolves it by building the ring on the *ambient* additive group, so only one `AddCommGroup` is ever in play. Concrete carriers are unaffected — -`EuclideanJordan/Witness.lean` uses both worlds on `HermitianMat`, where the two are the same instance. +`EuclideanJordan/Witness.lean` uses both worlds on `HermitianMat`, where the two are the same + instance. ★ **Completeness (`∑ p i = e`) is not assumed anywhere in this file.** None of the three facts needs it; it is what the *spectral* theorem produces and what the rank argument @@ -115,7 +116,8 @@ omit [IsCommJordan J] [IsScalarTower ℝ J J] [Fintype ι] [DecidableEq ι] in that is, an element of the coherence block `V_{ij}` — lies in `J₂(p i + p j)`. The proof is that `1/2 + 1/2 = 1`; the content is entirely in the *definition* of `V_{ij}` -as a joint half-eigenspace, which is what the Peirce theory of `EuclideanJordan/Peirce.lean` licenses. -/ +as a joint half-eigenspace, which is what the Peirce theory of `EuclideanJordan/Peirce.lean` + licenses. -/ theorem mem_J2_of_half_half {i j : ι} {x : J} (hi : p i * x = (2 : ℝ)⁻¹ • x) (hj : p j * x = (2 : ℝ)⁻¹ • x) : (p i + p j) * x = x := by rw [add_mul, hi, hj] @@ -162,14 +164,17 @@ theorem diagFamily_scalarOn_self (f : ι → ℝ) (i : ι) : rw [this] abel +omit [DecidableEq ι] in /-- **The operator-commutation fact, derived.** For a rank-two block `q = p i + p j` of an orthogonal idempotent family, a diagonal family with `f i = f j` operator-commutes with every element of `J₂(q)`. -/ theorem opCommute_scalarOn_frame (hp : IsOrthIdemFamily p) (f : ι → ℝ) {i j : ι} (hij : i ≠ j) (h : f i = f j) {b : J} (hb : (p i + p j) * b = b) (w : J) : - (∑ k, f k • p k) * (b * w) = b * ((∑ k, f k • p k) * w) := - opCommute_scalarOn (add_idem_of_orthogonal (hp.idem i) (hp.idem j) (hp.orth i j hij)) - (diagFamily_scalarOn f hij h) (pair_mul_offblock hp f i j) hb w + (∑ k, f k • p k) * (b * w) = b * ((∑ k, f k • p k) * w) := by + classical + exact + opCommute_scalarOn (add_idem_of_orthogonal (hp.idem i) (hp.idem j) (hp.orth i j hij)) + (diagFamily_scalarOn f hij h) (pair_mul_offblock hp f i j) hb w end Fields diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean index 3a93139925..82067738da 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean @@ -15,12 +15,15 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Rank # Every nontrivial finite-dimensional Euclidean Jordan algebra carries a Jordan frame `EuclideanJordan/Rank.lean` defines `JordanFrame J n` — a complete family of `n` pairwise-orthogonal -primitive idempotents — and carries it as *data*, because a rank hypothesis supplies one. This file proves such data always exists: `exists_jordanFrame`, by strong +primitive idempotents — and carries it as *data*, because a rank hypothesis supplies one. This + file proves such data always exists: `exists_jordanFrame`, by strong induction on `Module.finrank ℝ J` down the Peirce decomposition of a nontrivial idempotent. The induction itself is the expected one. If `1` is primitive the one-element family -`fun _ : Fin 1 => (1 : J)` is a frame. Otherwise primitivity fails at its third clause, which hands over an idempotent `c` with -`c ≠ 0` and `c ≠ 1`; `EuclideanJordan/PeirceSubalgebra.lean` makes `J₂(c)` and `J₀(c)` Euclidean Jordan +`fun _ : Fin 1 => (1 : J)` is a frame. Otherwise primitivity fails at its third clause, which + hands over an idempotent `c` with +`c ≠ 0` and `c ≠ 1`; `EuclideanJordan/PeirceSubalgebra.lean` makes `J₂(c)` and `J₀(c)` Euclidean + Jordan algebras with units `c` and `1 - c` and drops the dimension at both; and the two frames obtained from the induction hypothesis concatenate along `Fin.append`. @@ -42,12 +45,14 @@ which is `eigen_one_of_eigen_one` below, and which is in neither ★ **What makes it work is changing which idempotent one decomposes at.** Attacking it at `c` — rewriting `c ∘ x` as `c ∘ (d ∘ x)` and trying to move `c` inwards — is circular, because moving -`c` past `d` is what needs the conclusion; `EuclideanJordan/PeirceMul.lean`'s `mul_comm_of_eigen_one` only +`c` past `d` is what needs the conclusion; `EuclideanJordan/PeirceMul.lean`'s + `mul_comm_of_eigen_one` only relocates that difficulty: `mul_comm_of_eigen_one` at `(c, d, x)` yields `c ∘ x = d ∘ (c ∘ x)`, which puts `c ∘ x` back inside `J₂(d)` rather than identifying it with `x`. Decomposing at **`d`** instead makes it immediate: `d` annihilates `c - d` (`mul_sub_eq_zero_of_eigen_one`, one line), so `c - d` lies in `J₀(d)` while `x` lies in -`J₂(d)`, and `EuclideanJordan/PeirceMul.lean`'s `eigen_one_mul_zero` — the rule that the two *extreme* +`J₂(d)`, and `EuclideanJordan/PeirceMul.lean`'s `eigen_one_mul_zero` — the rule that the two + *extreme* Peirce components of a single idempotent annihilate each other — kills `(c - d) ∘ x` outright. Then `c ∘ x = d ∘ x + (c - d) ∘ x = x`. (`c - d` is also idempotent, but that is neither proved nor used here.) @@ -64,7 +69,8 @@ ambient carrier cannot be named without one. ★ The frame produced here carries **no claim about its cardinality**. `exists_jordanFrame` -existentially quantifies `n`, and `EuclideanJordan/Rank.lean`'s module docstring records why `rank J = n` +existentially quantifies `n`, and `EuclideanJordan/Rank.lean`'s module docstring records why + `rank J = n` is not available: `rank J` is a supremum over *all* orthogonal families of nonzero idempotents, and bounding such a family by a frame's cardinality needs the frame Peirce decomposition or frame conjugacy. Nothing here is a step towards it. Do not read `exists_jordanFrame` as diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean index a085e8cdb3..f709bd6311 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean @@ -23,7 +23,8 @@ as a family `frameBlock F : Sym2 (Fin n) → Submodule ℝ J` (`Sym2` because `V and proves `frameBlock_isInternal : DirectSum.IsInternal (frameBlock F)` — independence (`frameBlock_iSupIndep`) plus spanning (`frameBlock_iSup_eq_top`). -`frameBlockRaw_self` records that the diagonal block really is `EuclideanJordan/PeirceSubalgebra.lean`'s +`frameBlockRaw_self` records that the diagonal block really is + `EuclideanJordan/PeirceSubalgebra.lean`'s `peirceOneSub`, so the two definitions of `J₂(p i)` in the tree do not drift apart. ## ★ The reprice held in direction and was wrong about the mechanism @@ -79,7 +80,8 @@ carried as data. See `EuclideanJordan/Rank.lean`'s module docstring. Do not re ★ That guard got easier to trip once `EuclideanJordan/HermitianCarrier.lean` named a frame. `diagJordanFrame : JordanFrame (HermitianMat n ℂ) (Fintype.card n)` puts a frame of cardinality `Fintype.card n` on `H_n(ℂ)`, which reads like "`rank (H_n(ℂ)) = Fintype.card n`". It is not: -`EuclideanJordan/Rank.lean` supplies `JordanFrame.card_le_rank`, an inequality in one direction only, and +`EuclideanJordan/Rank.lean` supplies `JordanFrame.card_le_rank`, an inequality in one direction + only, and `rank` is a supremum over *every* orthogonal family of nonzero idempotents. A named frame of cardinality `k` is a lower bound on the rank and nothing more. @@ -199,7 +201,8 @@ theorem frameBlockRaw_mul_eq_zero (F : JordanFrame J n) {i j k : Fin n} (hki : k /-! ## How the frame's Peirce projections compose The eight ways two of the projections `peirceOne (p i)`, `peirceHalf (p i)` can be applied in -succession. Every one is `EuclideanJordan/Block.lean`'s commutation plus a single-idempotent rule from +succession. Every one is `EuclideanJordan/Block.lean`'s commutation plus a single-idempotent + rule from `EuclideanJordan/Peirce.lean`. -/ section Compose @@ -268,7 +271,8 @@ theorem sum_peirceHalf_erase (F : JordanFrame J n) {k : Fin n} {y : J} simpa using this have hmul : ∀ m, m ≠ k → F.p m * y = (2 : ℝ)⁻¹ • peirceHalf (F.p m) y := by intro m hm - calc F.p m * y = F.p m * (peirceOne (F.p m) y + peirceHalf (F.p m) y + peirceZero (F.p m) y) := by + calc F.p m * y = F.p m * (peirceOne (F.p m) y + peirceHalf (F.p m) y + peirceZero (F.p m) y) + := by rw [peirce_add_add] _ = (2 : ℝ)⁻¹ • peirceHalf (F.p m) y := by rw [mul_add, mul_add, mul_peirceOne (F.orthIdem.idem m), diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean index d1700ba712..9cedd8e206 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean @@ -14,7 +14,8 @@ import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce /-! # The Faraut–Korányi multiplication table relative to a Jordan frame -`EuclideanJordan/FramePeirce.lean` builds the blocks `V_{ij}` of a Jordan frame `F = (p₁, …, pₙ)` and proves +`EuclideanJordan/FramePeirce.lean` builds the blocks `V_{ij}` of a Jordan frame `F = (p₁, …, + pₙ)` and proves `J = ⨁_{i ≤ j} V_{ij}`. This file multiplies them. The table: * `V_{ii} ∘ V_{ii} ⊆ V_{ii}` (`frameBlockRaw_diag_mul_diag`), @@ -25,7 +26,8 @@ import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce together with `dim V_{ii} = 1` (`finrank_frameBlockRaw_self`) and the eigenvalue rule `pᵢ ∘ x = ½ • x` on `V_{ij}` (`frameBlockRaw_mul_left_half`). The last three lines of the table -are also stated as literal submodule inclusions through `EuclideanJordan/Class.lean`'s bundled `jmulₗ` and +are also stated as literal submodule inclusions through `EuclideanJordan/Class.lean`'s bundled + `jmulₗ` and `Submodule.map₂`, in the `Map₂` section; there is no `Mul` on `Submodule ℝ J` to state them with, because Mathlib's `Submodule.mul` instance is declared for `[Semiring A] [Module R A] [IsScalarTower R A A]` — an associative unital ring — which a Jordan algebra is not. @@ -55,10 +57,13 @@ completeness of the frame, used once, in `frameBlockRaw_mul_self_split`. ## ★ Primitivity is spent here -`EuclideanJordan/FramePeirce.lean` records that no proof in it uses primitivity at all. This file is not, +`EuclideanJordan/FramePeirce.lean` records that no proof in it uses primitivity at all. This + file is not, however, the first in the tree to *touch* the `IsPrimitive` clauses, and an earlier draft of this -docstring said so wrongly: `EuclideanJordan/Rank.lean`'s `JordanFrame.p_ne_zero` consumes the `ne_zero` clause, -and `EuclideanJordan/FrameExists.lean`'s `isPrimitive_coe_of_peirceOne` / `isPrimitive_coe_of_peirceZero` +docstring said so wrongly: `EuclideanJordan/Rank.lean`'s `JordanFrame.p_ne_zero` consumes the + `ne_zero` clause, +and `EuclideanJordan/FrameExists.lean`'s `isPrimitive_coe_of_peirceOne` / + `isPrimitive_coe_of_peirceZero` consume the splitting clause `∀ d, d ∘ d = d → c ∘ d = d → d = 0 ∨ d = c` in full. What those two do with it is *transport* it across the coercion `↥(J₂(c)) → J`; they extract no structural consequence from it. @@ -66,9 +71,11 @@ consequence from it. This file is the first to spend it. Within this file the splitting clause is used at exactly one theorem, `peirceOneSub_eq_span_of_isPrimitive`, which runs `EuclideanJordan/Class.lean`'s `spectral_resolution_complete'` **inside** `J₂(c)` — legitimate because -`EuclideanJordan/PeirceSubalgebra.lean` gives `J₂(c)` its own `EuclideanJordanAlgebra` instance with unit `c` +`EuclideanJordan/PeirceSubalgebra.lean` gives `J₂(c)` its own `EuclideanJordanAlgebra` instance + with unit `c` — and reads off that every idempotent appearing in the resolution is `0` or `1`, by -`EuclideanJordan/Rank.lean`'s `isPrimitive_iff_of_idem`. A resolution all of whose idempotents are `0` or `1` +`EuclideanJordan/Rank.lean`'s `isPrimitive_iff_of_idem`. A resolution all of whose idempotents + are `0` or `1` has every term a real multiple of `1 = c`. Orthogonality of the resolution is *not* used in that argument, only the two-valuedness; that is @@ -85,7 +92,8 @@ Everything *before* the `Primitive` section runs on `F.orthIdem` and `F.complete statement about one block of a frame carried as data, not about the rank of `J`. ★ **There is a carrier and a named frame**, so the theorems below are not statements about an -empty class: `EuclideanJordan/HermitianCarrier.lean` supplies `instEuclideanJordanAlgebraHermitianMat` and +empty class: `EuclideanJordan/HermitianCarrier.lean` supplies + `instEuclideanJordanAlgebraHermitianMat` and `diagJordanFrame : JordanFrame (HermitianMat n ℂ) (Fintype.card n)`. `frameBlockRaw_mul_self_eq` is consumed at `F = diagJordanFrame` through `EuclideanJordan/Connection.lean`'s `exists_sq_smul`. @@ -93,7 +101,8 @@ consumed at `F = diagJordanFrame` through `EuclideanJordan/Connection.lean`'s `e `EuclideanJordan/HermitianCarrier.lean`. The durable part is the one below. -★ `rank J = n` is not proved for **any** frame, `diagJordanFrame` included: `EuclideanJordan/Rank.lean` +★ `rank J = n` is not proved for **any** frame, `diagJordanFrame` included: + `EuclideanJordan/Rank.lean` bounds a frame's cardinality by the rank and by the dimension, and nothing anywhere converts `dim V_{ii} = 1` into a statement about `rank J`. -/ @@ -109,7 +118,8 @@ variable {n : ℕ} /-! ## The eigenvalue rule on a block -/ -/-- **`pᵢ ∘ x = ½ • x` for `x ∈ V_{ij}`, `i ≠ j`.** Definitional at `EuclideanJordan/FramePeirce.lean`'s +/-- **`pᵢ ∘ x = ½ • x` for `x ∈ V_{ij}`, `i ≠ j`.** Definitional at +`EuclideanJordan/FramePeirce.lean`'s `frameBlockRaw`; named because the multiplication rules below take it as an argument constantly. -/ theorem frameBlockRaw_mul_left_half (F : JordanFrame J n) {i j : Fin n} (hij : i ≠ j) {x : J} (hx : x ∈ frameBlockRaw F i j) : F.p i * x = (2 : ℝ)⁻¹ • x := @@ -122,12 +132,15 @@ theorem frameBlockRaw_mul_right_half (F : JordanFrame J n) {i j : Fin n} (hij : /-! ## The table -Each rule is read off the single-idempotent Faraut–Korányi rules of `EuclideanJordan/PeirceMul.lean`, applied -at one frame member at a time. `frameBlockRaw_mul_eq_zero` (in `EuclideanJordan/FramePeirce.lean`) supplies +Each rule is read off the single-idempotent Faraut–Korányi rules of + `EuclideanJordan/PeirceMul.lean`, applied +at one frame member at a time. `frameBlockRaw_mul_eq_zero` (in + `EuclideanJordan/FramePeirce.lean`) supplies the `0`-eigenvalue of every frame member outside a block's index pair. -/ /-- **`V_{ii} ∘ V_{ii} ⊆ V_{ii}`** — the diagonal block is a subalgebra. This is -`eigen_one_mul_one` verbatim; `EuclideanJordan/PeirceSubalgebra.lean` already uses it as the `Mul` field of +`eigen_one_mul_one` verbatim; `EuclideanJordan/PeirceSubalgebra.lean` already uses it as the + `Mul` field of `J₂(pᵢ)`. -/ theorem frameBlockRaw_diag_mul_diag (F : JordanFrame J n) {i : Fin n} {x y : J} (hx : x ∈ frameBlockRaw F i i) (hy : y ∈ frameBlockRaw F i i) : @@ -326,7 +339,8 @@ end Primitive /-! ## The table as submodule inclusions `Submodule.map₂ (jmulₗ J) P Q` is the submodule generated by the products, so these are the -literal `⊆` statements of the module docstring. `EuclideanJordan/Class.lean`'s `jmulₗ` is the bundled form of +literal `⊆` statements of the module docstring. `EuclideanJordan/Class.lean`'s `jmulₗ` is the + bundled form of the same product (`jmulₗ_apply` is `rfl`). -/ section Map₂ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean index 316991dace..64a15fc5d0 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean @@ -44,6 +44,7 @@ theorem symmMul_add_rightG (a b c : HermitianMat n 𝕜) : congr 1 abel +omit [DecidableEq n] in theorem symmMul_smul_rightG (t : ℝ) (a b : HermitianMat n 𝕜) : a.symmMul (t • b) = t • a.symmMul b := by ext1 @@ -55,12 +56,12 @@ def jordanBilinG (𝕜 : Type*) [RCLike 𝕜] : HermitianMat n 𝕜 →ₗ[ℝ] HermitianMat n 𝕜 →ₗ[ℝ] HermitianMat n 𝕜 := LinearMap.mk₂ ℝ (fun a b => a.symmMul b) (fun a a' b => by - show (a + a').symmMul b = a.symmMul b + a'.symmMul b + change (a + a').symmMul b = a.symmMul b + a'.symmMul b rw [HermitianMat.symmMul_comm, symmMul_add_rightG] rw [HermitianMat.symmMul_comm (A := b) (B := a), HermitianMat.symmMul_comm (A := b) (B := a')]) (fun t a b => by - show (t • a).symmMul b = t • a.symmMul b + change (t • a).symmMul b = t • a.symmMul b rw [HermitianMat.symmMul_comm, symmMul_smul_rightG, HermitianMat.symmMul_comm (A := b) (B := a)]) (fun a b b' => symmMul_add_rightG a b b') diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean index 1200fad32e..30806efbc7 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean @@ -16,9 +16,12 @@ import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin /-! # A carrier for `EuclideanJordanAlgebra` -`EuclideanJordan/Class.lean` names the Euclidean Jordan algebra hypothesis as a class, and `EuclideanJordan/Rank.lean`, -`EuclideanJordan/FrameExists.lean` and `EuclideanJordan/FramePeirce.lean` state their results over it. Until this file -the **only** instances of that class in the tree were `EuclideanJordan/PeirceSubalgebra.lean`'s two, and both +`EuclideanJordan/Class.lean` names the Euclidean Jordan algebra hypothesis as a class, and + `EuclideanJordan/Rank.lean`, +`EuclideanJordan/FrameExists.lean` and `EuclideanJordan/FramePeirce.lean` state their results + over it. Until this file +the **only** instances of that class in the tree were `EuclideanJordan/PeirceSubalgebra.lean`'s + two, and both are conditional on an ambient `[EuclideanJordanAlgebra J]`, so nothing exhibited a base model: `exists_jordanFrame` and `frameBlock_isInternal` were universally quantified over a class no object was known to inhabit. @@ -33,8 +36,10 @@ An abstract theorem whose hypothesis no carrier is known to satisfy is worth not `IsFormallyReal` and `IsOrthIdemFamily`, and this file closes it for `EuclideanJordanAlgebra`. ★ The class was **not** the only unwitnessed hypothesis left in the layer, and an earlier draft -of this docstring said it was. `EuclideanJordan/Rank.lean`'s `IsPrimitive` and `JordanFrame` had no witness -either — `EuclideanJordan/Witness.lean` builds the diagonal matrix units but never proves one primitive, so +of this docstring said it was. `EuclideanJordan/Rank.lean`'s `IsPrimitive` and `JordanFrame` + had no witness +either — `EuclideanJordan/Witness.lean` builds the diagonal matrix units but never proves one + primitive, so no `JordanFrame` had ever been constructed. Both are witnessed at the end of this file, but by a separate argument, and the class instance alone would not have supplied them. @@ -58,7 +63,8 @@ The three lemmas before the instance are re-orientations, not new results: two u ## The diamond `EuclideanJordan/Class.lean` warned about, measured -`EuclideanJordan/Class.lean` records that `instNonUnitalNonAssocCommRing` fires on any type carrying the +`EuclideanJordan/Class.lean` records that `instNonUnitalNonAssocCommRing` fires on any type + carrying the class, that `HermitianMat d 𝕜` already carries a `Mul` from `EuclideanJordan/Vendor/HermitianMat/Jordan.lean`'s `scoped instance : CommMagma`, and that "if one is ever declared, that scoped instance and this class's `toMul` will both be in scope @@ -99,14 +105,16 @@ M3/M4 results are now known to be about a nonempty class: `hermitian_exists_jord `H_n(𝕜)`. ★ The last section goes past existence and **names a frame** on `H_n(ℂ)`: `diagJordanFrame`, the -diagonal matrix units of `EuclideanJordan/Witness.lean` reindexed along `Fintype.equivFin`. Unlike the class +diagonal matrix units of `EuclideanJordan/Witness.lean` reindexed along `Fintype.equivFin`. + Unlike the class instance, that is not repackaging: `diagFrame_isPrimitive` is the one obligation of `JordanFrame` that no lemma in the tree discharges — `diagFrame_orthIdem` gives `orthIdem` and `diagFrame_sum` gives `complete`, and `p` is data — and its proof and the four entrywise lemmas under it are written here. (`hermitian_one_ne_zero` is new too, but it is three lines and reads off one matrix entry.) ★ Note what primitivity is **not**: it is not `dim V_ii = 1`, and this file does not prove that. Two scope limits on the frame: it is over -`ℂ` only, because `EuclideanJordan/Witness.lean`'s `diagFrame` is, and its cardinality is `Fintype.card n`, +`ℂ` only, because `EuclideanJordan/Witness.lean`'s `diagFrame` is, and its cardinality is + `Fintype.card n`, which is **not** proved to be the rank — `EuclideanJordan/Rank.lean` proves only `card ≤ rank` and `card ≤ finrank`. -/ @@ -124,16 +132,20 @@ theorem hermitian_symmMul_add_left (A B C : HermitianMat n 𝕜) : (A + B).symmMul C = A.symmMul C + B.symmMul C := by simpa only [jordanBilin_applyG] using (jordanBilinG (n := n) 𝕜).map_add₂ A B C +omit [DecidableEq n] in /-- Real homogeneity of `∘` in its left argument, from `jordanBilinG`'s `LinearMap` structure. -/ theorem hermitian_symmMul_smul_left (r : ℝ) (A B : HermitianMat n 𝕜) : (r • A).symmMul B = r • (A.symmMul B) := by + classical simpa only [jordanBilin_applyG] using (jordanBilinG (n := n) 𝕜).map_smul₂ r A B +omit [DecidableEq n] in /-- The Jordan identity in the class's orientation, `x ∘ (x² ∘ y) = x² ∘ (x ∘ y)`. `EuclideanJordan/Order.lean`'s `hermitian_jordan_id` carries Mathlib's orientation `(x ∘ y) ∘ x² = x ∘ (y ∘ x²)`; the two differ by commuting the product twice. -/ theorem hermitian_symmMul_jordan (A B : HermitianMat n 𝕜) : A.symmMul ((A.symmMul A).symmMul B) = (A.symmMul A).symmMul (A.symmMul B) := by + classical have h := hermitian_jordan_id (n := n) (𝕜 := 𝕜) A B simp only [jordanBilin_applyG] at h rw [HermitianMat.symmMul_comm (A.symmMul A) B, ← h] @@ -141,7 +153,8 @@ theorem hermitian_symmMul_jordan (A B : HermitianMat n 𝕜) : /-! ## The instance -/ -/-- ★★★ **`H_n(𝕜)` is a Euclidean Jordan algebra.** The class of `EuclideanJordan/Class.lean`, on the +/-- ★★★ **`H_n(𝕜)` is a Euclidean Jordan algebra.** The class of `EuclideanJordan/Class.lean`, +on the Hermitian-matrix carrier, at the generality `EuclideanJordan/Order.lean` uses: any `RCLike` scalar field and any finite decidable index type. -/ instance instEuclideanJordanAlgebraHermitianMat : @@ -187,22 +200,28 @@ theorem hermitian_one_ne_zero [Nonempty n] : (1 : HermitianMat n 𝕜) ≠ 0 := Matrix.zero_apply] at h2 exact one_ne_zero h2 -instance instNontrivialHermitianMat [Nonempty n] : Nontrivial (HermitianMat n 𝕜) := - ⟨⟨1, 0, hermitian_one_ne_zero⟩⟩ +omit [Fintype n] [DecidableEq n] in +instance instNontrivialHermitianMat [Finite n] [Nonempty n] : + Nontrivial (HermitianMat n 𝕜) := by + let : DecidableEq n := Classical.decEq n + let := Fintype.ofFinite n + exact ⟨⟨1, 0, hermitian_one_ne_zero⟩⟩ /-! ## M3 and M4 on the carrier The two results now read on a live object rather than on a class with no known inhabitant. Neither proof is new: each is the abstract theorem with `J := H_n(𝕜)`. -/ -/-- **(M3) on `H_n(𝕜)`.** `EuclideanJordan/FrameExists.lean`'s `exists_jordanFrame`, instantiated. The +/-- **(M3) on `H_n(𝕜)`.** `EuclideanJordan/FrameExists.lean`'s `exists_jordanFrame`, +instantiated. The finite-dimensionality it needs is `HermitianMat.FiniteDimensional`; the nontriviality is `hermitian_one_ne_zero`. -/ theorem hermitian_exists_jordanFrame [Nonempty n] : ∃ k, Nonempty (JordanFrame (HermitianMat n 𝕜) k) := exists_jordanFrame (HermitianMat n 𝕜) hermitian_one_ne_zero -/-- **(M4) on `H_n(𝕜)`.** `EuclideanJordan/FramePeirce.lean`'s `frameBlock_isInternal`, instantiated: a +/-- **(M4) on `H_n(𝕜)`.** `EuclideanJordan/FramePeirce.lean`'s `frameBlock_isInternal`, +instantiated: a Jordan frame of `H_n(𝕜)` splits it as the internal direct sum of its diagonal and coherence blocks. -/ theorem hermitian_frameBlock_isInternal {k : ℕ} (F : JordanFrame (HermitianMat n 𝕜) k) : @@ -269,11 +288,11 @@ theorem mat_eq_zero_of_diagFrame_fixed {i : n} {D : HermitianMat n ℂ} simp only [diagFrame_symmMul_mat_apply] at he by_cases ha : a = i <;> by_cases hb : b = i · exact absurd ⟨ha, hb⟩ hab - · rw [if_pos ha, if_neg hb, add_zero] at he + · rw [ite_eq_left ha, ite_eq_right hb, add_zero] at he linear_combination -2 * he - · rw [if_neg ha, if_pos hb, zero_add] at he + · rw [ite_eq_right ha, ite_eq_left hb, zero_add] at he linear_combination -2 * he - · rw [if_neg ha, if_neg hb, add_zero, mul_zero] at he + · rw [ite_eq_right ha, ite_eq_right hb, add_zero, mul_zero] at he exact he.symm /-- So such an element is a scalar multiple of `E_ii`. -/ @@ -322,7 +341,8 @@ theorem diagFrame_isPrimitive (i : n) : IsPrimitive (diagFrame (d := n) i) := by /-- ★★★ **A Jordan frame on `H_n(ℂ)`, named.** The diagonal matrix units, reindexed along `Fintype.equivFin` because `JordanFrame` is `Fin`-indexed. ★ Its cardinality `Fintype.card n` -is **not** claimed to be the rank of the algebra — `EuclideanJordan/Rank.lean` proves only that a frame's +is **not** claimed to be the rank of the algebra — `EuclideanJordan/Rank.lean` proves only that + a frame's cardinality is bounded by the rank and by the dimension. -/ noncomputable def diagJordanFrame : JordanFrame (HermitianMat n ℂ) (Fintype.card n) where p k := diagFrame ((Fintype.equivFin n).symm k) diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean index 74e575b790..2c6f7f7fe5 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean @@ -235,16 +235,16 @@ def orderUnitSpaceOfBilinear (m : J →ₗ[ℝ] J →ₗ[ℝ] J) { (inferInstance : NormedAddCommGroup J), (inferInstance : NormedSpace ℝ J), partialOrderOfSoS m hfr with add_le_add_left := fun a b h c => by - show IsSoS m (c + b - (c + a)) + change IsSoS m (c + b - (c + a)) rw [show c + b - (c + a) = b - a by abel] exact h ousUnit := e smul_nonneg_mono := fun r hr {a b} h => by - show IsSoS m (r • b - r • a) + change IsSoS m (r • b - r • a) rw [← smul_sub] exact IsSoS.smul hr h ousUnit_nonneg := by - show IsSoS m (e - 0) + change IsSoS m (e - 0) rw [sub_zero] exact isSoS_of_idem (he e) archimedean := fun a => exists_isSoS_smul_unit_sub hcomm hjordan hfr e he a } @@ -282,7 +282,7 @@ theorem isEffect_ofBilinear (a : J) : rwa [sub_zero] at h0' · rintro ⟨h0, h1⟩ refine ⟨?_, h1⟩ - show IsSoS m (a - 0) + change IsSoS m (a - 0) rwa [sub_zero] /-- `EuclideanJordan/OrderUnitSpace.lean`'s spanning theorem, live at EJA generality. It is @@ -291,7 +291,7 @@ not merely that the structure typechecks. -/ theorem span_isEffect_eq_top_ofBilinear : letI := orderUnitSpaceOfBilinear m hcomm hjordan hfr e he Submodule.span ℝ {a : J | OrderUnitSpace.IsEffect a} = ⊤ := by - letI := orderUnitSpaceOfBilinear m hcomm hjordan hfr e he + let := orderUnitSpaceOfBilinear m hcomm hjordan hfr e he exact OrderUnitSpace.span_isEffect_eq_top end Characterization @@ -313,7 +313,8 @@ variable {m : J →ₗ[ℝ] J →ₗ[ℝ] J} /-- **`L_c` is a positive operator for an idempotent `c`.** `L_c = P₁(c) + ½ P_{1/2}(c)` on the nose, and both Peirce projections are idempotent -(`EuclideanJordan/Peirce.lean`'s `mul_peirceOne` feeding `peirceOne_of_eigen`) and self-adjoint (from +(`EuclideanJordan/Peirce.lean`'s `mul_peirceOne` feeding `peirceOne_of_eigen`) and self-adjoint + (from self-adjointness of `L_c`, which is `hassoc` at `x := c`). A self-adjoint idempotent `P` satisfies `⟪P y, y⟫ = ⟪P y, P y⟫ ≥ 0`, so the sum is nonnegative. @@ -325,9 +326,9 @@ theorem inner_mul_self_nonneg_of_idem (hassoc : ∀ x y z : J, inner ℝ (m x y) z = inner ℝ y (m x z)) {c : J} (hc : m c c = c) (y : J) : 0 ≤ inner ℝ (m c y) y := by - letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm - letI : IsCommJordan J := ⟨hjordan⟩ - letI : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ + let : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + let : IsCommJordan J := ⟨hjordan⟩ + let : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ have hc' : c * c = c := hc have hsa : ∀ u v : J, inner ℝ (c * u) v = inner ℝ u (c * v) := fun u v => hassoc c u v have hsa1 : ∀ u v : J, inner ℝ (peirceOne c u) v = inner ℝ u (peirceOne c v) := by @@ -345,7 +346,7 @@ theorem inner_mul_self_nonneg_of_idem have hidh : peirceHalf c (peirceHalf c y) = peirceHalf c y := peirceHalf_of_eigen_half (mul_peirceHalf hc' y) have hsplit : (m c y : J) = peirceOne c y + (2 : ℝ)⁻¹ • peirceHalf c y := by - show c * y = _ + change c * y = _ simp only [peirceOne_apply, peirceHalf_apply] module have key1 : inner ℝ (peirceOne c y) (peirceOne c y) = inner ℝ (peirceOne c y) y := by @@ -440,7 +441,7 @@ theorem isArchimedean_ofBilinear (e : J) (he : ∀ y : J, m e y = y) : @OrderUnitSpace.IsArchimedean J (orderUnitSpaceOfBilinear m hcomm hjordan hfr e he) := by intro x hx - show IsSoS m (0 - x) + change IsSoS m (0 - x) obtain ⟨n, q, lam, hidem, horth, hsum, hxe⟩ := spectral_resolution_bilinear m hcomm hjordan hfr e he x have hlam : ∀ i, q i ≠ 0 → lam i ≤ 0 := by diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean b/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean index 7c333edd01..f3f978a7bc 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean @@ -49,10 +49,12 @@ Two facts about the cone, both of which need an **associative positive definite * a sum of squares has nonnegative spectral coefficients (`nonneg_coeff`); * the **face lemma**: `0 ≤ x ≤ c` with `c` idempotent forces `c ∘ x = x` (`face_lemma`). -`EuclideanJordan/Order.lean` proves the first from the *ambient* inner product, under the hypothesis that the +`EuclideanJordan/Order.lean` proves the first from the *ambient* inner product, under the + hypothesis that the inner product is associative for the Jordan product. A consumer that carries an inner product and a Jordan product as unrelated data has no such hypothesis available, which is why this file -pairs against `EuclideanJordan/TraceForm.lean`'s `traceForm` instead: a form built from the algebra, so nothing has to +pairs against `EuclideanJordan/TraceForm.lean`'s `traceForm` instead: a form built from the + algebra, so nothing has to be assumed about an ambient one. Both facts are proved here from scratch in that vocabulary; the `inner`-shaped originals in `EuclideanJordan/Order.lean` are untouched and are not used. @@ -67,7 +69,8 @@ extreme-points route was never built. ★ `EuclideanJordan/OrderUnitSpace.lean` carries an `IsSharp` of its own, and this file does **not** use it or bridge to it. That one is stated over an `OrderUnitSpace` instance, and getting -one here would mean instantiating `EuclideanJordan/Order.lean`'s `orderUnitSpaceOfBilinear` — a `def`, whose +one here would mean instantiating `EuclideanJordan/Order.lean`'s `orderUnitSpaceOfBilinear` — a + `def`, whose own docstring warns that instantiating it puts a second `PartialOrder J` in scope. ★★ **The two are not literally the same formula, and the difference is worth stating rather than glossing.** `OrderUnitSpace.IsSharp` quantifies its witness over the *effects* (`IsEffect a`, i.e. @@ -257,7 +260,8 @@ theorem orth_of_sum_eq_unit {n : ℕ} {q : Fin n → J} {e : J} (he : ∀ y : J, omit [IsCommJordan J] [IsFormallyReal J] [Module.Finite ℝ J] in /-- A combination of orthogonal idempotents with nonnegative coefficients is in the cone. The coefficient condition is only imposed where the idempotent is nonzero, matching what -`nonneg_coeff` can supply: `EuclideanJordan/Spectral.lean`'s resolution pads with a possibly-zero idempotent, +`nonneg_coeff` can supply: `EuclideanJordan/Spectral.lean`'s resolution pads with a + possibly-zero idempotent, whose coefficient is unconstrained. -/ theorem isSoS_sum_smul_idem {n : ℕ} {q : Fin n → J} (hidem : ∀ i, q i * q i = q i) {g : Fin n → ℝ} (hg : ∀ i, q i ≠ 0 → 0 ≤ g i) : @@ -361,16 +365,16 @@ theorem idem_of_isSharp {e c : J} (he : ∀ y : J, e * y = y) (h : IsSharp e c) rw [hcq, sum_smul_sub_smul q lam i mu] refine isSoS_sum_smul_idem hfam.idem fun j hj => ?_ by_cases hji : j = i - · rw [if_pos hji]; linarith - · rw [if_neg hji]; exact hlo j hj + · rw [ite_eq_left hji]; linarith + · rw [ite_eq_right hji]; exact hlo j hj have hwe : IsSoS mulLₗ (e - c - mu • q i) := by rw [hec', sum_smul_sub_smul q (fun j => 1 - lam j) i mu] refine isSoS_sum_smul_idem hfam.idem fun j hj => ?_ by_cases hji : j = i - · rw [if_pos hji] + · rw [ite_eq_left hji] show (0 : ℝ) ≤ 1 - lam i - mu linarith - · rw [if_neg hji] + · rw [ite_eq_right hji] show (0 : ℝ) ≤ 1 - lam j linarith [hhi j hj] have hzero := hsharp (mu • q i) hwx hwc hwe @@ -458,7 +462,8 @@ end Abstract /-! ## The theorem in bilinear-map vocabulary -The crossing `EuclideanJordan/Bridge.lean` was built for: the *statement* mentions only the bundled bilinear +The crossing `EuclideanJordan/Bridge.lean` was built for: the *statement* mentions only the + bundled bilinear map, so no ring instance has to exist before it elaborates; only the proof needs one. -/ section Interface @@ -470,7 +475,8 @@ bilinear map, the Jordan identity and formal reality as hypotheses in that vocab as `EuclideanJordan/Order.lean`'s `IsSoS`. ★ No inner product appears. The ambient structure is a normed additive group carrying an -`ℝ`-module structure — `NormedAddCommGroup` only because `EuclideanJordan/Bridge.lean`'s `ringOfBilinear` is +`ℝ`-module structure — `NormedAddCommGroup` only because `EuclideanJordan/Bridge.lean`'s + `ringOfBilinear` is stated over one — and the norm is never used. -/ theorem orderIso_preservesJordan (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (hcomm : ∀ x y : J, m x y = m y x) @@ -480,10 +486,10 @@ theorem orderIso_preservesJordan (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (Φ : J ≃ₗ[ℝ] J) (hunital : Φ e = e) (horder : ∀ x : J, IsSoS m x ↔ IsSoS m (Φ x)) (x y : J) : Φ (m x y) = m (Φ x) (Φ y) := by - letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm - letI : IsCommJordan J := ⟨hjordan⟩ - letI : IsScalarTower ℝ J J := ⟨fun r a b => smul_bilinear m r a b⟩ - letI : IsFormallyReal J := isFormallyReal_of_fin m hcomm hfr + let : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + let : IsCommJordan J := ⟨hjordan⟩ + let : IsScalarTower ℝ J J := ⟨fun r a b => smul_bilinear m r a b⟩ + let : IsFormallyReal J := isFormallyReal_of_fin m hcomm hfr have hm : (mulLₗ : J →ₗ[ℝ] J →ₗ[ℝ] J) = m := by ext a b; rfl rw [hm] at * exact map_jordan_of_orderIso he Φ hunital horder x y diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean b/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean index cf18f00241..56ea228f8d 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean @@ -8,7 +8,7 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import Mathlib.Data.Real.Basic +import Mathlib.Basic.Real.Basic import Mathlib.Analysis.Normed.Module.Basic import Mathlib.Algebra.Order.Group.Defs import Mathlib.Tactic.Linarith @@ -62,6 +62,7 @@ namespace OrderUnitSpace variable {V : Type*} [OrderUnitSpace V] +/-- The distinguished order unit of an order unit space. -/ scoped notation "𝟙" => OrderUnitSpace.ousUnit (V := _) -- Ordered group lemmas derived from add_le_add_left @@ -74,17 +75,17 @@ theorem neg_le_neg {a b : V} (h : a ≤ b) : -b ≤ -a := by have h1 := add_le_add_right' h (-b) rw [add_neg_cancel] at h1 have h2 := add_le_add_right' h1 (-a) - simp [add_assoc, add_neg_cancel, zero_add, add_zero] at h2 + simp? [add_assoc, add_neg_cancel, zero_add, add_zero] at h2 exact h2 theorem neg_nonneg_of_nonpos {a : V} (h : a ≤ 0) : (0 : V) ≤ -a := by have := neg_le_neg h - simp at this + simp? at this exact this theorem neg_nonpos_of_nonneg {a : V} (h : (0 : V) ≤ a) : -a ≤ 0 := by have := neg_le_neg h - simp at this + simp? at this exact this theorem sub_nonneg_of_le {a b : V} (h : a ≤ b) : (0 : V) ≤ b - a := by diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean index 6d62edb06f..3db3f48b8f 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean @@ -14,7 +14,8 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Block /-! # The Faraut–Korányi block pattern, forced by three constraints -`EuclideanJordan/Block.lean` characterises the rank-two block `J₁(p+q)`. This file does the frame-level +`EuclideanJordan/Block.lean` characterises the rank-two block `J₁(p+q)`. This file does the + frame-level analogue, but as a **constraint on eigenvalue patterns** rather than as an assembled direct sum — which is both what is provable here and what the FK block structure actually *is*. @@ -71,6 +72,7 @@ theorem eigen_pattern_mem (hp : IsOrthIdemFamily p) (hx : x ≠ 0) (hmu : ∀ i, p i * x = mu i • x) (i : ι) : mu i = 0 ∨ mu i = (2 : ℝ)⁻¹ ∨ mu i = 1 := eigenvalue_trichotomy (hp.idem i) hx (hmu i) +omit [DecidableEq ι] in /-- **At most two frame idempotents see a given joint eigenvector.** -/ theorem eigen_pattern_card_le_two (hp : IsOrthIdemFamily p) (hx : x ≠ 0) (hs : (∑ i, p i) * x = x) (hmu : ∀ i, p i * x = mu i • x) : diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean index 95a7e9383e..38738b6ae9 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean @@ -10,7 +10,7 @@ Authors: Bryan Ehrlich -/ import Mathlib.Algebra.Jordan.Basic import Mathlib.Analysis.Normed.Module.Basic -import Mathlib.Data.Real.Basic +import Mathlib.Basic.Real.Basic import Mathlib.Tactic.LinearCombination @@ -27,7 +27,8 @@ wrong — `peirce_poly` divides by `2` (`two_smul_eq_zero'`), which is why every it carries `Module ℝ J`. Only the *linearised identities* `two_lin1_raw`/`two_lin1_apply` are genuinely torsion-free, and they are, deliberately: their factor of `2` is carried in the statement rather than cancelled. Caught 2026-08-12 by reading the `omit` lines against this -paragraph. What the spectral theorem is needed for is *producing* idempotents — a Jordan frame — not for +paragraph. What the spectral theorem is needed for is *producing* idempotents — a Jordan frame — + not for decomposing at one that is already in hand. `EuclideanJordan/FrameExists.lean` does the producing; this file does the decomposing. diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean index ffbbcbb7a2..353b66601e 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean @@ -14,7 +14,8 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Peirce /-! # The Peirce multiplication rules -`EuclideanJordan/Peirce.lean` decomposes `J = J₁(c) ⊕ J_{1/2}(c) ⊕ J₀(c)` for an idempotent `c`. This +`EuclideanJordan/Peirce.lean` decomposes `J = J₁(c) ⊕ J_{1/2}(c) ⊕ J₀(c)` for an idempotent `c`. + This file proves how the three components multiply — the Faraut–Korányi relations: | | `J₁` | `J_{1/2}` | `J₀` | @@ -23,12 +24,14 @@ file proves how the three components multiply — the Faraut–Korányi relation | **`J_{1/2}`** | `⊆ J_{1/2}` | `⊆ J₁ ⊕ J₀` | `⊆ J_{1/2}` | | **`J₀`** | `= 0` | `⊆ J_{1/2}` | `⊆ J₀` | -As in `EuclideanJordan/Peirce.lean`, the hypotheses are the Jordan identity and the invertibility of the +As in `EuclideanJordan/Peirce.lean`, the hypotheses are the Jordan identity and the + invertibility of the integers used (`2` for the commuting rules, `4` for the half-half rule): no spectral theorem, no formal reality, no finite dimension, no unit. ## The two ingredients -`EuclideanJordan/Peirce.lean` needed only the *once*-linearised Jordan identity `two_lin1_raw`. Five of +`EuclideanJordan/Peirce.lean` needed only the *once*-linearised Jordan identity `two_lin1_raw`. + Five of the six rules follow from a single consequence of it — **`L_x` commutes with `L_c` whenever `x` lies in `J₁(c)` or `J₀(c)`** (`mul_comm_of_eigen_one`, `mul_comm_of_eigen_zero`) — after which each rule is one rewrite. @@ -212,7 +215,8 @@ theorem eigen_zero_mul_half {c x y : J} (hc : c * c = c) (hx : c * x = 0) /-- **`J_{1/2}(c) ∘ J_{1/2}(c) ⊆ J₁(c) ⊕ J₀(c)`**, stated as the polynomial relation `L_c² = L_c` on the product — which is exactly "no `1/2`-component", since -`peirceHalf c z = 4•(c ∘ z) − 4•(c ∘ (c ∘ z))` collapses to `0` under it (`peirceHalf_mul_half_eq_zero`). +`peirceHalf c z = 4•(c ∘ z) − 4•(c ∘ (c ∘ z))` collapses to `0` under it + (`peirceHalf_mul_half_eq_zero`). ★ An earlier draft attributed this to the eigenvalue trichotomy. It does not use the trichotomy — it is the projection formula directly. diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean index 6ce96707ad..2d7825ac73 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean @@ -14,8 +14,10 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Class /-! # Peirce subalgebras: `J₂(c)` and `J₀(c)` as Euclidean Jordan algebras in their own right -`EuclideanJordan/Peirce.lean` carries the Peirce decomposition at a single idempotent as three *linear -maps* `peirceOne c`, `peirceHalf c`, `peirceZero c`, and `EuclideanJordan/PeirceMul.lean` carries the six +`EuclideanJordan/Peirce.lean` carries the Peirce decomposition at a single idempotent as three + *linear +maps* `peirceOne c`, `peirceHalf c`, `peirceZero c`, and `EuclideanJordan/PeirceMul.lean` + carries the six Faraut–Korányi rules governing how their images multiply. Two of those rules — `eigen_one_mul_one` and `eigen_zero_mul_zero` — say that the eigenvalue-`1` and eigenvalue-`0` eigenspaces are closed under the product. This file turns that closure into structure: each @@ -35,7 +37,8 @@ That probe reproduces here, re-run against this tree on 2026-08-22: with `[EuclideanJordanAlgebra J]` in context, `example (S : NonUnitalSubalgebra ℝ J) : IsCommJordan ↥S := inferInstance` fails to synthesise, while the same `example` for `NonUnitalNonAssocCommRing ↥S` succeeds. The conclusion drawn from -it still does not apply, because under `EuclideanJordan/Class.lean`'s design **no transfer is needed**. +it still does not apply, because under `EuclideanJordan/Class.lean`'s design **no transfer is + needed**. The class puts `Mul` and `One` on top of an inner-product space rather than alongside a ring structure, so the natural subobject is the same shape one level down: the ambient `Submodule ℝ J`, whose subtype already carries @@ -43,7 +46,8 @@ structure, so the natural subobject is the same shape one level down: the ambien added on top. Then `EuclideanJordanAlgebra ↥(peirceOneSub hc)` is *constructed* from six field proofs, every one of which is the ambient identity read through `Subtype.ext`, and `IsCommJordan`, `IsScalarTower`, `SMulCommClass` and the ring structure then arrive on the -subtype the same way they arrive on `J`: as `EuclideanJordan/Class.lean`'s derived instances. Nothing is +subtype the same way they arrive on `J`: as `EuclideanJordan/Class.lean`'s derived instances. + Nothing is transferred because nothing has to be. ★ **One rough edge, measured rather than predicted.** `IsFormallyReal ↥(peirceOneSub hc)` does @@ -149,7 +153,8 @@ section Two variable {c : J} (hc : c * c = c) -/-- Closure under the product is `EuclideanJordan/PeirceMul.lean`'s `eigen_one_mul_one`, applied to the +/-- Closure under the product is `EuclideanJordan/PeirceMul.lean`'s `eigen_one_mul_one`, applied +to the membership proofs directly: membership unfolds to the eigenvalue equation. -/ instance instMulPeirceOneSub : Mul ↥(peirceOneSub hc) := ⟨fun x y => ⟨(x : J) * (y : J), eigen_one_mul_one hc x.2 y.2⟩⟩ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean index 8b7ed21f91..cc72447f1f 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean @@ -36,7 +36,8 @@ Then `jpow_mul_jpow_of_commuteAt` says: **`CommuteAt x m` alone gives ★ The reduction itself does **not** use the Jordan identity — its `omit` line says so. Every bit of Jordan content in power associativity is concentrated in `CommuteAt`. **This is the -part that held up**: it is what let `EuclideanJordan/PowerAssoc.lean` attack a single commutator family +part that held up**: it is what let `EuclideanJordan/PowerAssoc.lean` attack a single commutator + family instead of the product law, and the proof there is exactly that attack. ## What is discharged here diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean index 15ddb0652c..fb460babb0 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean @@ -25,7 +25,8 @@ them systematically, and Mathlib has nothing between `IsCommJordan` and this. ## The proof Write `cm x i j w` for the commutator `[L_{x^{i+1}}, L_{x^{j+1}}]` applied to `w`. The whole -theorem is `cm x i j w = 0`, since `EuclideanJordan/Power.lean`'s `jpow_mul_jpow_of_commuteAt` already +theorem is `cm x i j w = 0`, since `EuclideanJordan/Power.lean`'s `jpow_mul_jpow_of_commuteAt` + already turns commutation into the product law. The engine is `cm_cyclic`: for `i + j = N` (and given the product law at level `N`), the diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean index 0a10479b60..46fd59e9f0 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean @@ -14,7 +14,8 @@ import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra /-! # Primitive idempotents, Jordan frames, and rank -`EuclideanJordan/Frame.lean` carries `IsOrthIdemFamily`: a family of pairwise-orthogonal idempotents, with +`EuclideanJordan/Frame.lean` carries `IsOrthIdemFamily`: a family of pairwise-orthogonal + idempotents, with completeness deliberately left out. This file adds the two conditions that turn such a family into a *Jordan frame* — every member primitive, and the family complete — and defines the rank. @@ -43,10 +44,13 @@ off this file. ## The linear-independence argument is already in the tree Orthogonal nonzero idempotents are linearly independent, and the argument is -`EuclideanJordan/Order.lean`'s `inner_left_coeff` verbatim: idempotency and then associativity of the inner +`EuclideanJordan/Order.lean`'s `inner_left_coeff` verbatim: idempotency and then associativity + of the inner product give `⟪pₖ, pᵢ⟫ = ⟪pₖ ∘ pₖ, pᵢ⟫ = ⟪pₖ, pₖ ∘ pᵢ⟫ = 0` for `i ≠ k`, so pairing a vanishing -combination against `pₖ` reads off `gₖ ‖pₖ‖² = 0`. That lemma is stated in `EuclideanJordan/Order.lean`'s bilinear-map vocabulary; -`EuclideanJordan/Class.lean`'s `jmulₗ` and `jmulₗ_inner_assoc` are what let it be applied at the class +combination against `pₖ` reads off `gₖ ‖pₖ‖² = 0`. That lemma is stated in + `EuclideanJordan/Order.lean`'s bilinear-map vocabulary; +`EuclideanJordan/Class.lean`'s `jmulₗ` and `jmulₗ_inner_assoc` are what let it be applied at the + class without restating it. ## Scope @@ -81,7 +85,8 @@ theorem IsPrimitive.eq_zero_or_eq {c : J} (h : IsPrimitive c) {d : J} (hd : d * /-- Primitivity of `c`, read *inside* `J₂(c)`: `c` is primitive exactly when the only idempotents of the Peirce subalgebra are its zero and its unit. The intended consumer is the `dim V_ii = 1` step of the frame Peirce decomposition, which runs the spectral theorem inside -`J₂(pᵢ)`; that step is `EuclideanJordan/FramePeirceMul.lean`'s `peirceOneSub_eq_span_of_isPrimitive`, and it +`J₂(pᵢ)`; that step is `EuclideanJordan/FramePeirceMul.lean`'s + `peirceOneSub_eq_span_of_isPrimitive`, and it consumes this lemma in the `→` direction. -/ theorem isPrimitive_iff_of_idem {c : J} (hc : c * c = c) (hc0 : c ≠ 0) : IsPrimitive c ↔ ∀ d : ↥(peirceOneSub hc), d * d = d → d = 0 ∨ d = 1 := by diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean index e8d5f3bf58..753ffbd57c 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean @@ -40,7 +40,8 @@ at least one factor of `x`. Its whole content is one identity, `jeval_mul : jeval x p * jeval x q = jeval x (X * p * q)`, -which is `EuclideanJordan/PowerAssoc.lean`'s `jpow_mul_jpow` transported along bilinearity. Everything +which is `EuclideanJordan/PowerAssoc.lean`'s `jpow_mul_jpow` transported along bilinearity. + Everything downstream is ideal theory in `ℝ[X]`, where Mathlib already has what is needed, and the values live in the ambient `J`, where `EuclideanJordan/FormallyReal.lean` already applies. @@ -48,16 +49,20 @@ The steps, each a named declaration below: | step | statement | declaration | | --- | --- | --- | -| 1 | the annihilator `{p | x·p(x) = 0}` is an ideal of `ℝ[X]`, nonzero in finite dimension | `jann`, `exists_annihilator_generator` | +| 1 | the annihilator `{p | x·p(x) = 0}` is an ideal of `ℝ[X]`, nonzero in finite dimension | + `jann`, `exists_annihilator_generator` | | 2 | its generator `m` is radical, hence squarefree | `isRadical_of_annihilator` | | 3 | `m` has a nonzero constant term | `coeff_zero_ne_zero_of_annihilator` | | 4 | `m` has no non-real complex root | `annihilator_aeval_ne_zero_of_im_ne_zero` | -| 5 | so some product of distinct linear factors, none of them `X`, annihilates `x` | `exists_split_annihilator` | -| 6 | Lagrange interpolation at those roots together with `0` gives the idempotents | `exists_orthIdem_finset` | +| 5 | so some product of distinct linear factors, none of them `X`, annihilates `x` | + `exists_split_annihilator` | +| 6 | Lagrange interpolation at those roots together with `0` gives the idempotents | + `exists_orthIdem_finset` | ★ **Step 2 is where formal reality enters the polynomial algebra**, through the identity `jpow (jeval x f) n = jeval x (Xⁿ f^{n+1})` (`jpow_jeval`): a Jordan power of a value of -`jeval` is again a value of `jeval`, so `EuclideanJordan/FormallyReal.lean`'s no-nilpotents theorem — +`jeval` is again a value of `jeval`, so `EuclideanJordan/FormallyReal.lean`'s no-nilpotents + theorem — which is stated about *ambient* elements of `J` — applies with no repackaging. This is why no `IsReduced` instance on a ring structure over `jspan x` is needed anywhere. @@ -368,7 +373,8 @@ theorem exists_split_annihilator (x : J) : apply Complex.ext <;> simp [h] rw [hzr, Polynomial.aeval_algebraMap_apply_eq_algebraMap_eval] at hz simpa using hz - exact annihilator_aeval_ne_zero_of_im_ne_zero hm hsq hc0 him (by rw [hW, map_mul, hz, mul_zero]) + exact annihilator_aeval_ne_zero_of_im_ne_zero hm hsq hc0 him (by rw [hW, map_mul, hz, + mul_zero]) obtain ⟨v, hv⟩ := hWu.exists_right_inv refine (hm _).mpr ⟨v, ?_⟩ calc (∏ a ∈ m.roots.toFinset, (X - C a)) @@ -423,7 +429,7 @@ theorem exists_orthIdem_finset (x : J) : have hXg : ∀ a ∈ S, b a = X * g a := by intro a ha have h0 : (b a).coeff 0 = 0 := by - rw [Polynomial.coeff_zero_eq_eval_zero, heval a ha 0 h0T, if_neg (Ne.symm (hne a ha))] + rw [Polynomial.coeff_zero_eq_eval_zero, heval a ha 0 h0T, ite_eq_right (Ne.symm (hne a ha))] conv_lhs => rw [← Polynomial.X_mul_divX_add (b a)] rw [h0, map_zero, add_zero, hgdef] refine ⟨S, fun a => jeval x (g a), ?_, ?_, fun a => jeval_mem_jspan x (g a), ?_⟩ @@ -444,8 +450,8 @@ theorem exists_orthIdem_finset (x : J) : · intro ν hν simp only [Polynomial.eval_mul, heval a ha ν hν, heval a' ha' ν hν] by_cases h : ν = a - · rw [if_pos h, if_neg (by rw [h]; exact haa'), mul_zero] - · rw [if_neg h, zero_mul] + · rw [ite_eq_left h, ite_eq_right (by rw [h]; exact haa'), mul_zero] + · rw [ite_eq_right h, zero_mul] rw [← jeval_mul] at key change jeval x (g a) * jeval x (g a') = 0 exact key @@ -459,11 +465,11 @@ theorem exists_orthIdem_finset (x : J) : Polynomial.eval_smul, smul_eq_mul] rcases Finset.mem_insert.mp hν with rfl | hνS · rw [Finset.sum_eq_zero fun a ha => by - rw [heval a ha 0 hν, if_neg (Ne.symm (hne a ha)), mul_zero], sub_zero] + rw [heval a ha 0 hν, ite_eq_right (Ne.symm (hne a ha)), mul_zero], sub_zero] · rw [Finset.sum_eq_single ν - (fun a ha hane => by rw [heval a ha ν hν, if_neg (Ne.symm hane), mul_zero]) + (fun a ha hane => by rw [heval a ha ν hν, ite_eq_right (Ne.symm hane), mul_zero]) (fun h => absurd hνS h), - heval ν hνS ν hν, if_pos rfl, mul_one, sub_self] + heval ν hνS ν hν, ite_eq_left rfl, mul_one, sub_self] rw [map_sub, map_sum, jeval_one, sub_eq_zero] at key simpa using key @@ -550,11 +556,12 @@ variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] omit [InnerProductSpace ℝ J] in /-- Formal reality over an arbitrary `Finset`, from the `Fin k` form. The two differ only by reindexing. -/ -theorem isFormallyReal_of_fin [Module ℝ J] (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (hcomm : ∀ x y : J, m x y = m y x) +theorem isFormallyReal_of_fin [Module ℝ J] (m : J →ₗ[ℝ] J →ₗ[ℝ] J) (hcomm : ∀ x y : J, m x y = m + y x) (hfr : ∀ (k : ℕ) (f : Fin k → J), (∑ i, m (f i) (f i)) = 0 → ∀ i, f i = 0) : letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm IsFormallyReal J := by - letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + let : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm refine ⟨fun {ι} s f hsum i hi => ?_⟩ classical have key : (∑ k : Fin s.card, m (f (s.equivFin.symm k)) (f (s.equivFin.symm k))) = 0 := by @@ -569,7 +576,8 @@ theorem isFormallyReal_of_fin [Module ℝ J] (m : J →ₗ[ℝ] J →ₗ[ℝ] J) bilinear map, the Jordan identity and formal reality as hypotheses in that vocabulary, and the conclusion stated without mentioning any ring instance. -★ This is the crossing `EuclideanJordan/Bridge.lean` was built for, and it works here for the reason that file +★ This is the crossing `EuclideanJordan/Bridge.lean` was built for, and it works here for the + reason that file gives: the *statement* is expressible with `m` alone, so no ring instance has to exist before it elaborates. Only the proof needs one, and `ringOfBilinear` supplies it on the ambient additive group. @@ -587,10 +595,10 @@ theorem spectral_resolution_bilinear [FiniteDimensional ℝ J] (m : J →ₗ[ℝ (∀ i j, i ≠ j → m (q i) (q j) = 0) ∧ (∑ i, q i) = e ∧ x = ∑ i, lam i • q i := by - letI : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm - letI : IsCommJordan J := ⟨hjordan⟩ - letI : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ - letI : IsFormallyReal J := isFormallyReal_of_fin m hcomm hfr + let : NonUnitalNonAssocCommRing J := ringOfBilinear m hcomm + let : IsCommJordan J := ⟨hjordan⟩ + let : IsScalarTower ℝ J J := ⟨fun r x y => smul_bilinear m r x y⟩ + let : IsFormallyReal J := isFormallyReal_of_fin m hcomm hfr obtain ⟨n, q, lam, hfam, hsum, hx⟩ := spectral_resolution_complete e he x exact ⟨n, q, lam, hfam.idem, hfam.orth, hsum, hx⟩ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean index 9724d46c78..530d774506 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean @@ -15,7 +15,8 @@ import Mathlib.LinearAlgebra.Dimension.Finite /-! # The subalgebra generated by one element is associative -This is Albert's theorem in the form it is usually *stated* — `EuclideanJordan/PowerAssoc.lean` proves the +This is Albert's theorem in the form it is usually *stated* — `EuclideanJordan/PowerAssoc.lean` + proves the form it is usually *proved* in, the power law `x^{m+1} ∘ x^{n+1} = x^{m+n+2}`. The two are not the same statement: the power law is about products of powers, and this is about products of arbitrary `ℝ`-combinations of powers. @@ -34,7 +35,8 @@ longer than its content. The textbook route to the Jordan spectral theorem runs through `ℝ[x]`: one shows it is a finite-dimensional associative commutative real algebra, that formal reality makes it reduced (`EuclideanJordan/FormallyReal.lean`), and hence that it is `ℝ^k` — which produces the orthogonal -idempotents that `EuclideanJordan/Frame.lean` then consumes. **`jspan_assoc` is the first of those four +idempotents that `EuclideanJordan/Frame.lean` then consumes. **`jspan_assoc` is the first of + those four steps.** The remaining three — finite-dimensionality of `jspan x`, reducedness in the form the classification wants, and the classification itself — are not built. diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean b/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean index e5946ee64f..eb3d408c28 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean @@ -16,14 +16,16 @@ import Mathlib.LinearAlgebra.Projection /-! # The Jordan trace form -`τ(x, y) := tr(L_{x ∘ y})`, where `L_c` is the Jordan multiplication operator `EuclideanJordan/Peirce.lean` +`τ(x, y) := tr(L_{x ∘ y})`, where `L_c` is the Jordan multiplication operator + `EuclideanJordan/Peirce.lean` carries as `mulL`. On a finite-dimensional formally real Jordan algebra this form is symmetric, **associative** (`τ(x ∘ y, z) = τ(y, x ∘ z)`) and **positive definite** — which is to say, it makes such an algebra Euclidean without any inner product having been supplied. ## Why this file exists -`EuclideanJordan/Order.lean`'s Euclidean section — `inner_mul_self_nonneg_of_idem`, `inner_left_coeff`, +`EuclideanJordan/Order.lean`'s Euclidean section — `inner_mul_self_nonneg_of_idem`, + `inner_left_coeff`, `nonneg_coeff_of_isSoS`, `isArchimedean_ofBilinear`, `isSoS_iff_exists_sq` — takes the associativity of the **ambient** inner product as a hypothesis, `hassoc : ∀ x y z, ⟪m x y, z⟫ = ⟪y, m x z⟫`. The concrete carrier `H_n(𝕜)` discharges it by hand, through `hermitian_jordan_assoc` @@ -36,7 +38,8 @@ inside those hypotheses has no associative form to pair against, and every one o above is inapplicable as stated. This file builds the missing form *from the algebra*. Nothing here mentions a norm or an inner -product; the ambient structure is `EuclideanJordan/Peirce.lean`'s (a commutative Jordan ring that is an +product; the ambient structure is `EuclideanJordan/Peirce.lean`'s (a commutative Jordan ring + that is an `ℝ`-module), plus finite-dimensionality and formal reality where the spectral theorem is used. A consumer working in bilinear-map vocabulary installs `EuclideanJordan/Bridge.lean`'s `ringOfBilinear` and gets `traceForm` on the nose. @@ -71,7 +74,8 @@ Faraut–Korányi (the trace of the quadratic representation, or the sum of the for a unit `e` one has `L_e = id` and so `tr(L_e) = finrank ℝ J` rather than the rank of `J` — **a remark, not a lemma; it is not proved below and nothing uses it.** Nothing downstream needs the normalisation, and -`EuclideanJordan/Class.lean`'s `EuclideanJordanAlgebra` deliberately takes an *arbitrary* associative positive +`EuclideanJordan/Class.lean`'s `EuclideanJordanAlgebra` deliberately takes an *arbitrary* + associative positive definite form rather than a normalised one, so `traceForm` is admissible there as it stands. -/ @@ -134,7 +138,8 @@ section Trace variable {J : Type*} [NonUnitalNonAssocCommRing J] [IsCommJordan J] [Module ℝ J] [IsScalarTower ℝ J J] -/-- `EuclideanJordan/Peirce.lean`'s `mulL`, bundled as a linear map in the multiplier — which is what makes +/-- `EuclideanJordan/Peirce.lean`'s `mulL`, bundled as a linear map in the multiplier — which is +what makes `jtr` linear. -/ def mulLₗ : J →ₗ[ℝ] J →ₗ[ℝ] J where toFun := mulL @@ -260,7 +265,8 @@ theorem traceForm_comm (x y : J) : traceForm x y = traceForm y x := by simp only [traceForm_apply, mul_comm] /-- **The trace form is associative**: `τ(x ∘ y, z) = τ(y, x ∘ z)`. This is the hypothesis -`hassoc` that `EuclideanJordan/Order.lean`'s Euclidean section and `EuclideanJordan/Class.lean`'s class both take, now a +`hassoc` that `EuclideanJordan/Order.lean`'s Euclidean section and + `EuclideanJordan/Class.lean`'s class both take, now a theorem about a form built from the algebra alone. -/ theorem traceForm_assoc (x y z : J) : traceForm (x * y) z = traceForm y (x * z) := by simp only [traceForm_apply] @@ -317,7 +323,8 @@ theorem eq_zero_of_traceForm_self_eq_zero {x : J} (h : traceForm x x = 0) : x = refine Finset.sum_eq_zero fun i _ => ?_ by_cases hq0 : q i = 0 · rw [hq0, smul_zero] - · have hpos : (0 : ℝ) < jtr (q i) := lt_of_lt_of_le zero_lt_one (one_le_jtr_of_idem (hfam.idem i) hq0) + · have hpos : (0 : ℝ) < jtr (q i) := lt_of_lt_of_le zero_lt_one (one_le_jtr_of_idem (hfam.idem + i) hq0) have : lam i * lam i = 0 := by rcases mul_eq_zero.mp (hzero i) with h' | h' · exact h' diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean index 05c8ec30d9..5058ff7880 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean @@ -16,5 +16,6 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic /-! # Vendor -Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. +Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the + trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. -/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean index dac99afcfc..0f81d0d942 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean @@ -17,5 +17,6 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace /-! # HermitianMat -Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. +Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the + trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. -/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean index 69ed19650c..99d3315381 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Basic.lean @@ -11,6 +11,8 @@ public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMa public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes +/-! Hermitian matrices, their algebraic structure, eigenspaces, and tensor products. -/ + @[expose] public section /-- The type of Hermitian matrices, as a `Subtype`. Equivalent to a `Matrix n n α` bundled @@ -31,6 +33,7 @@ variable [AddGroup α] [StarAddMonoid α] theorem eq_IsHermitian : HermitianMat n α = { m : Matrix n n α // m.IsHermitian} := by rfl +/-- The underlying matrix of a Hermitian matrix. -/ @[coe] def mat : HermitianMat n α → Matrix n n α := Subtype.val @@ -49,7 +52,7 @@ theorem mk_mat {A : HermitianMat n α} (h : A.mat.IsHermitian) : ⟨A.mat, h⟩ rfl /-- Alias for HermitianMat.property or HermitianMat.2, this gets the fact that the value - is actually `IsHermitian`.-/ + is actually `IsHermitian`. -/ theorem H (A : HermitianMat n α) : A.mat.IsHermitian := A.2 @@ -155,7 +158,7 @@ instance : ContinuousNeg (HermitianMat n α) := instance : IsTopologicalAddGroup (HermitianMat n α) where -variable [TopologicalSpace R] [SMul R α] [ContinuousSMul R α] [StarModule R α] +variable [TopologicalSpace R] [SMul R α] [ContinuousSMul R α] [StarModule R α] instance : ContinuousSMul R (HermitianMat n α) where continuous_smul := by @@ -273,7 +276,7 @@ theorem pow_zero : A ^ 0 = 1 := by ext1; simp @[simp] -theorem zero_pow (hn : n ≠ 0): (0 : HermitianMat m α) ^ n = 0 := by +theorem zero_pow (hn : n ≠ 0) : (0 : HermitianMat m α) ^ n = 0 := by ext1; simp [hn] @[simp] @@ -318,7 +321,7 @@ add_aesop_rules safe norm (rule_sets := [Commutes]) theorem _root_.Matrix.inv_commute {α : Type*} {A : Matrix m m α} [CommRing α] : Commute A⁻¹ A := by rcases A.nonsing_inv_cancel_or_zero with h | h · simp [Commute, SemiconjBy, h] - . simp [h] + · simp [h] @[aesop safe apply (rule_sets := [Commutes])] theorem commute_inv_self : Commute A⁻¹.mat A.mat := by @@ -353,16 +356,20 @@ section conj variable [CommRing α] [StarRing α] [Fintype n] variable (A : HermitianMat n α) -/-- The Hermitian matrix given by conjugating by a (possibly rectangular) Matrix. If we required `B` to be -square, this would apply to any `Semigroup`+`StarMul` (as proved by `IsSelfAdjoint.conjugate`). But this lets -us conjugate to other sizes too, as is done in e.g. Kraus operators. That is, it's a _heterogeneous_ conjguation. +/-- The Hermitian matrix given by conjugating by a (possibly rectangular) Matrix. If we required +`B` to be +square, this would apply to any `Semigroup`+`StarMul` (as proved by `IsSelfAdjoint.conjugate`). + But this lets +us conjugate to other sizes too, as is done in e.g. Kraus operators. That is, it's a + _heterogeneous_ conjguation. -/ def conj {m} (B : Matrix m n α) : HermitianMat n α →+ HermitianMat m α where toFun A := ⟨B * A.mat * B.conjTranspose, by ext simp only [Matrix.star_apply, Matrix.mul_apply, Matrix.conjTranspose_apply, Finset.sum_mul, - star_sum, star_mul', star_star, show ∀ (a b : n), star (A.mat b a) = A.mat a b from congrFun₂ A.property] + star_sum, star_mul', star_star, show ∀ (a b : n), star (A.mat b a) = A.mat a b from + congrFun₂ A.property] rw [Finset.sum_comm] congr! 2 ring⟩ @@ -393,7 +400,7 @@ theorem conj_conj {m l} [Fintype m] (B : Matrix m n α) (C : Matrix l m α) : variable (B : HermitianMat n α) @[simp] -theorem conj_zero [DecidableEq n] : A.conj (0 : Matrix m n α) = 0 := by +theorem conj_zero : A.conj (0 : Matrix m n α) = 0 := by apply HermitianMat.ext change 0 * A.mat * (0 : Matrix m n α).conjTranspose = 0 rw [Matrix.zero_mul, Matrix.zero_mul] @@ -436,8 +443,10 @@ section eigenspace variable [Fintype n] [DecidableEq n] (A : HermitianMat n 𝕜) +omit [DecidableEq n] in instance [i : Nonempty n] : FaithfulSMul ℝ (HermitianMat n 𝕜) where eq_of_smul_eq_smul h := by + let : DecidableEq n := Classical.decEq n simpa [RCLike.smul_re, -mat_apply] using congr(RCLike.re ($(h 1).val i.some i.some)) /-- The continuous linear map associated with a Hermitian matrix. -/ @@ -457,6 +466,7 @@ theorem lin_zero : (0 : HermitianMat n 𝕜).lin = 0 := by theorem lin_one : (1 : HermitianMat n 𝕜).lin = 1 := by simp [lin]; rfl +/-- The eigenspace of the linear endomorphism induced by a Hermitian matrix. -/ noncomputable def eigenspace (μ : 𝕜) : Submodule 𝕜 (EuclideanSpace 𝕜 n) := Module.End.eigenspace A.lin μ @@ -477,7 +487,6 @@ theorem ker_eq_eigenspace_zero : A.ker = A.eigenspace 0 := by theorem ker_zero : (0 : HermitianMat n 𝕜).ker = ⊤ := by simp [ker] -@[simp] theorem ker_one : (1 : HermitianMat n 𝕜).ker = ⊥ := by simp [ker]; rfl @@ -498,7 +507,6 @@ theorem support_eq_sup_eigenspace_nonzero : A.support = ⨆ μ ≠ 0, A.eigenspa theorem support_zero : (0 : HermitianMat n 𝕜).support = ⊥ := by simp [support] -@[simp] theorem support_one : (1 : HermitianMat n 𝕜).support = ⊤ := by simpa [support] using LinearMap.ker_eq_bot_iff_range_eq_top.mp rfl @@ -521,6 +529,7 @@ section diagonal variable {𝕜 : Type*} [RCLike 𝕜] [DecidableEq n] variable (𝕜) in +/-- The Hermitian diagonal matrix whose diagonal is the given real-valued function. -/ def diagonal (f : n → ℝ) : HermitianMat n 𝕜 := ⟨Matrix.diagonal (f ·), by simp [selfAdjoint.mem_iff, Matrix.star_eq_conjTranspose, Matrix.diagonal_conjTranspose]⟩ @@ -560,7 +569,7 @@ theorem diagonal_conj_diagonal [Fintype n] : RCLike.conj_ofReal, Matrix.diagonal_mul_diagonal] congr 1 funext i - simp only [diagonal, mat, RCLike.ofReal_mul, RCLike.ofReal_pow] + simp only [RCLike.ofReal_mul, RCLike.ofReal_pow] ring /-- @@ -624,9 +633,9 @@ A ⊗ₖ B always commutes with C ⊗ₖ D if the pairs commute. --Apply safely. It will almost always work, but there are cases where it's not sound, -- such as `A = 0`. But these can all get easily simp'ed away anyway. @[aesop safe apply (rule_sets := [Commutes])] -theorem kron_commute [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n] +theorem kron_commute [Fintype m] [Fintype n] {A C : HermitianMat m α} {B D : HermitianMat n α} - (hAC : Commute A.mat C.mat) (hBD : Commute B.mat D.mat): + (hAC : Commute A.mat C.mat) (hBD : Commute B.mat D.mat) : Commute (A ⊗ₖ B).mat (C ⊗ₖ D).mat := by rw [commute_iff_eq] at hAC hBD ⊢ simp only [kronecker_mat, ← Matrix.mul_kronecker_mul, hAC, hBD] @@ -641,7 +650,8 @@ theorem kron_id_commute_id_kro [Fintype m] [Fintype n] [DecidableEq m] [Decidabl commutes /- -The conjugate of a Kronecker product by a Kronecker product is the Kronecker product of the conjugates. +The conjugate of a Kronecker product by a Kronecker product is the Kronecker product of the + conjugates. -/ lemma kronecker_conj [Fintype m] [Fintype n] (A : HermitianMat m α) (B : HermitianMat n α) (C : Matrix p m α) (D : Matrix q n α) : @@ -671,13 +681,13 @@ theorem range_le_ker_imp_zero {A : HermitianMat d 𝕜} simpa [ Matrix.mulVec, dotProduct ] using congr(WithLp.ofLp $(h) i) simp_all only [mat_apply, Matrix.zero_apply] replace hA_sq := congr_fun ( congr_fun hA_sq i ) i - simp_all only [Matrix.mul_apply, mat_apply, Matrix.zero_apply] ; + simp_all only [Matrix.mul_apply, mat_apply, Matrix.zero_apply]; -- Since $A$ is Hermitian, we have $A i x * A x i = |A i x|^2$. have h_abs : ∀ x, (A i x) * (A x i) = ‖A i x‖ ^ 2 := by intro x; have := A.2 - simp_all only [val_eq_coe, sq] ; + simp_all only [val_eq_coe, sq]; have := congr_fun ( congr_fun this i ) x - simp_all only [Matrix.star_apply, mat_apply, RCLike.star_def] ; + simp_all only [Matrix.star_apply, mat_apply, RCLike.star_def]; simp only [← this, mul_comm, RCLike.norm_conj]; simp [ ← sq, RCLike.mul_conj ]; simp_rw [h_abs] at hA_sq @@ -696,9 +706,12 @@ theorem _root_.Matrix.range_mul_conjTranspose_of_ker_le_ker {A : Matrix d d 𝕜 simp [Matrix.toEuclideanLin] · intro x hx; -- Since $x \in \text{range}(A)$, there exists $y \in \text{range}(Mᴴ)$ such that $A y = x$. - obtain ⟨y, hy⟩ : ∃ y ∈ LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)), A.toEuclideanLin y = x := by - have h_range_MH : LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)) = (LinearMap.ker (Matrix.toEuclideanLin M))ᗮ := by - have h_orthogonal : (LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)))ᗮ = LinearMap.ker (Matrix.toEuclideanLin M) := by + obtain ⟨y, hy⟩ : ∃ y ∈ LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)), + A.toEuclideanLin y = x := by + have h_range_MH : LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)) = + (LinearMap.ker (Matrix.toEuclideanLin M))ᗮ := by + have h_orthogonal : (LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)))ᗮ = + LinearMap.ker (Matrix.toEuclideanLin M) := by ext x rw [Matrix.toEuclideanLin_conjTranspose_eq_adjoint] simp only [Submodule.mem_orthogonal, LinearMap.mem_ker, LinearMap.mem_range] @@ -714,25 +727,36 @@ theorem _root_.Matrix.range_mul_conjTranspose_of_ker_le_ker {A : Matrix d d 𝕜 rw [← hz, LinearMap.adjoint_inner_left, h, inner_zero_right] rw [← h_orthogonal, Submodule.orthogonal_orthogonal] obtain ⟨ y, rfl ⟩ := hx; - -- Since $y$ is in the range of $Mᴴ$, we can write $y$ as $y = y_1 + y_2$ where $y_1 \in \text{range}(Mᴴ)$ and $y_2 \in \text{ker}(M)$. - obtain ⟨y1, y2, hy1, hy2, hy⟩ : ∃ y1 y2 : EuclideanSpace 𝕜 d, y1 ∈ LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)) ∧ y2 ∈ LinearMap.ker (Matrix.toEuclideanLin M) ∧ y = y1 + y2 := by - have h_decomp : ∀ y : EuclideanSpace 𝕜 d, ∃ y1 ∈ LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose)), ∃ y2 ∈ LinearMap.ker (Matrix.toEuclideanLin M), y = y1 + y2 := by + -- Since $y$ is in the range of $Mᴴ$, we can write $y$ as $y = y_1 + y_2$ where $y_1 + -- \in \text{range}(Mᴴ)$ and $y_2 \in \text{ker}(M)$. + obtain ⟨y1, y2, hy1, hy2, hy⟩ : ∃ y1 y2 : EuclideanSpace 𝕜 d, y1 ∈ LinearMap.range + (Matrix.toEuclideanLin (M.conjTranspose)) ∧ y2 ∈ LinearMap.ker (Matrix.toEuclideanLin + M) ∧ y = y1 + y2 := by + have h_decomp : ∀ y : EuclideanSpace 𝕜 d, ∃ y1 ∈ LinearMap.range (Matrix.toEuclideanLin + (M.conjTranspose)), ∃ y2 ∈ LinearMap.ker (Matrix.toEuclideanLin M), y = y1 + y2 := by intro y - have h_decomp : y ∈ (LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose))) ⊔ (LinearMap.ker (Matrix.toEuclideanLin M)) := by + have h_decomp : y ∈ (LinearMap.range (Matrix.toEuclideanLin (M.conjTranspose))) ⊔ + (LinearMap.ker (Matrix.toEuclideanLin M)) := by rw [ h_range_MH ]; rw [ sup_comm, Submodule.sup_orthogonal_of_hasOrthogonalProjection ]; exact Submodule.mem_top; - rw [ Submodule.mem_sup ] at h_decomp ; tauto; - exact ⟨ _, _, h_decomp y |> Classical.choose_spec |> And.left, h_decomp y |> Classical.choose_spec |> And.right |> Classical.choose_spec |> And.left, h_decomp y |> Classical.choose_spec |> And.right |> Classical.choose_spec |> And.right ⟩; - exact ⟨ y1, hy1, by rw [ hy, map_add, LinearMap.mem_ker.mp ( h hy2 ) ] ; simp ⟩; + rw [ Submodule.mem_sup ] at h_decomp; tauto; + exact ⟨ _, _, h_decomp y |> Classical.choose_spec |> And.left, h_decomp y |> + Classical.choose_spec |> And.right |> Classical.choose_spec |> And.left, h_decomp y + |> Classical.choose_spec |> And.right |> Classical.choose_spec |> And.right ⟩; + exact ⟨ y1, hy1, by rw [ hy, map_add, LinearMap.mem_ker.mp ( h hy2 ) ]; simp ⟩; obtain ⟨ z, rfl ⟩ := hy.1; exact ⟨ z, by simpa [ Matrix.toEuclideanLin ] using hy.2 ⟩ -theorem conj_ne_zero {A : HermitianMat d 𝕜} {M : Matrix d₂ d 𝕜} (hA : A ≠ 0) +omit [DecidableEq d₂] [Fintype d₂] in +theorem conj_ne_zero [Finite d₂] {A : HermitianMat d 𝕜} {M : Matrix d₂ d 𝕜} (hA : A ≠ 0) (h : LinearMap.ker M.toEuclideanLin ≤ A.ker) : A.conj M ≠ 0 := by + classical + let := Fintype.ofFinite d₂ by_contra h_contra have h_range : LinearMap.range A.mat.toEuclideanLin ≤ LinearMap.ker A.mat.toEuclideanLin := by - have h_range : LinearMap.range (A.mat * M.conjTranspose).toEuclideanLin ≤ LinearMap.ker M.toEuclideanLin := by + have h_range : LinearMap.range (A.mat * M.conjTranspose).toEuclideanLin ≤ LinearMap.ker + M.toEuclideanLin := by rintro x ⟨y, rfl⟩ replace h_contra := congr($(h_contra).mat) simp_all [Matrix.toLpLin_apply, Matrix.mul_assoc] @@ -740,8 +764,11 @@ theorem conj_ne_zero {A : HermitianMat d 𝕜} {M : Matrix d₂ d 𝕜} (hA : A exact h_range.trans h exact hA (range_le_ker_imp_zero h_range) -theorem conj_ne_zero_iff {A : HermitianMat d 𝕜} {M : Matrix d₂ d 𝕜} +omit [DecidableEq d₂] [Fintype d₂] in +theorem conj_ne_zero_iff [Finite d₂] {A : HermitianMat d 𝕜} {M : Matrix d₂ d 𝕜} (h : LinearMap.ker M.toEuclideanLin ≤ A.ker) : A.conj M ≠ 0 ↔ A ≠ 0 := by + classical + let := Fintype.ofFinite d₂ refine ⟨?_, (conj_ne_zero · h)⟩ intro h rfl; grind @@ -768,7 +795,7 @@ theorem ne_zero_iff_ne_zero_spectrum (A : HermitianMat n 𝕜) : simp only [HermitianMat.ext_iff, mat_zero] rw [A.H.spectral_theorem] ext i j - simp [Matrix.mul_apply, Matrix.diagonal] + simp? [Matrix.mul_apply, Matrix.diagonal] refine Finset.sum_eq_zero fun x _ ↦ ?_ simp [h_nonzero _ <| A.H.spectrum_real_eq_range_eigenvalues.symm ▸ Set.mem_range_self _] · rintro ⟨x, hx, hx'⟩ h @@ -786,3 +813,8 @@ end spectrum --Shortcut instance noncomputable instance : AddCommMonoid (HermitianMat d ℂ) := inferInstance + + +end more_range_stuff + +end HermitianMat diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean index 55148685bd..353c76a9c5 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/CFC.lean @@ -43,6 +43,7 @@ theorem continuousOn_finite {α β : Type*} (f : α → β) (S : Set α) theorem conjTranspose_cfc : (cfc f A.mat).conjTranspose = cfc f A.mat := by exact cfc_predicate f A.mat +/-- Continuous functional calculus for a real-valued function of a Hermitian matrix. -/ protected def cfc : HermitianMat d 𝕜 := ⟨cfc f A.mat, cfc_predicate _ _⟩ @@ -123,16 +124,18 @@ theorem spectrum_cfc_eq_image (A : HermitianMat d 𝕜) (f : ℝ → ℝ) : Spectral decomposition of `A.cfc f` as a sum of scaled projections (matrix version). -/ theorem cfc_toMat_eq_sum_smul_proj : (A.cfc f).mat = - ∑ i, f (A.H.eigenvalues i) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + ∑ i, f (A.H.eigenvalues i) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * + A.H.eigenvectorUnitary.val.conjTranspose) := by rw [A.mat_cfc, A.H.cfc_eq, Matrix.IsHermitian.cfc] - have h : ( Matrix.diagonal ( RCLike.ofReal ∘ f ∘ Matrix.IsHermitian.eigenvalues A.H ) : Matrix d d 𝕜 ) = ∑ i, f ( A.H.eigenvalues i ) • Matrix.single i i 1 := by - ext i j ; by_cases hij : i = j <;> simp [ hij ]; + have h : ( Matrix.diagonal ( RCLike.ofReal ∘ f ∘ Matrix.IsHermitian.eigenvalues A.H ) : Matrix + d d 𝕜 ) = ∑ i, f ( A.H.eigenvalues i ) • Matrix.single i i 1 := by + ext i j; by_cases hij : i = j <;> simp? [ hij ]; · simp [ Matrix.sum_apply, Matrix.single ]; simp [ Algebra.smul_def ]; · rw [Matrix.sum_apply] - simp_all + simp_all? rw [h] - simp [Matrix.single, Matrix.mul_assoc] + simp? [Matrix.single, Matrix.mul_assoc] congr! 1 ext j k simp [Matrix.mul_apply,Finset.mul_sum, Finset.smul_sum, smul_ite, smul_zero] @@ -302,7 +305,8 @@ theorem lt_smul_of_norm_lt {r : ℝ} (h : ‖A‖ ≤ r) : A ≤ r • 1 := by rw [sub_nonneg] apply le_of_sq_le_sq _ hr refine le_trans ?_ h' - exact Finset.single_le_sum (f := fun x ↦ (A.H.eigenvalues x)^2) (by intros; positivity) (Finset.mem_univ _) + exact Finset.single_le_sum (f := fun x ↦ (A.H.eigenvalues x)^2) (by intros; positivity) + (Finset.mem_univ _) theorem ball_subset_Icc : Metric.ball A r ⊆ Set.Icc (A - r • 1) (A + r • 1) := by intro x @@ -358,7 +362,8 @@ protected theorem cfc_continuous {f : ℝ → ℝ} (hf : Continuous f) : apply ContinuousOn.mono ?_ (ball_subset_Icc A r) obtain ⟨a, b, hab⟩ := spectrum_subset_of_mem_Icc (A - r • 1) (A + r • 1) open ComplexOrder in - refine ContinuousOn.cfc (s := fun _ ↦ Set.Icc a b) (t := Set.Icc (A - r • 1) (A + r • 1)) (A := CStarMatrix d d ℂ) f ?_ (by fun_prop) ?_ (fun x _ ↦ x.H) + refine ContinuousOn.cfc (s := fun _ ↦ Set.Icc a b) (t := Set.Icc (A - r • 1) (A + r • 1)) (A + := CStarMatrix d d ℂ) f ?_ (by fun_prop) ?_ (fun x _ ↦ x.H) · intro _ _ exact isCompact_Icc · simp only [Set.mem_Icc] @@ -382,7 +387,8 @@ theorem Matrix.PosDef.spectrum_subset_Ioi {d 𝕜 : Type*} [Fintype d] [Decidabl aesop /-- -If f is a continuous family of functions parameterized by x, then (fun x => A.cfc (f x)) is also continuous. +If f is a continuous family of functions parameterized by x, then (fun x => A.cfc (f x)) is also + continuous. -/ @[fun_prop] theorem continuous_cfc_fun {f : X → ℝ → ℝ} (hf : ∀ i, Continuous (f · i)) : @@ -393,7 +399,8 @@ theorem continuous_cfc_fun {f : X → ℝ → ℝ} (hf : ∀ i, Continuous (f · variable {f : X → ℝ → ℝ} {S : Set X} /-- -ContinuousOn variant for when all the matrices (A x) have a spectrum in a set T, and f is continuous on a set S. +ContinuousOn variant for when all the matrices (A x) have a spectrum in a set T, and f is + continuous on a set S. -/ @[fun_prop] theorem continuousOn_cfc_fun {T : Set ℝ} @@ -418,8 +425,10 @@ lemma norm_cfc_le_sqrt_card_mul_bound {A : HermitianMat d ℂ} {f : ℝ → ℝ} (hC : 0 ≤ C) (hf : ∀ x ∈ spectrum ℝ A.mat, ‖f x‖ ≤ C) : ‖A.cfc f‖ ≤ Real.sqrt (Fintype.card d) * C := by rw [ ← Real.sqrt_sq ( norm_nonneg _ ) ]; - -- Recall that the Frobenius norm of a Hermitian matrix is the square root of the sum of the squares of its eigenvalues. - have h_frobenius_eigenvalues : ∀ (M : HermitianMat d ℂ), ‖M‖ ^ 2 = ∑ i ∈ Finset.univ, (M.H.eigenvalues i) ^ 2 := by + -- Recall that the Frobenius norm of a Hermitian matrix is the square root of the sum of the + -- squares of its eigenvalues. + have h_frobenius_eigenvalues : ∀ (M : HermitianMat d ℂ), ‖M‖ ^ 2 = ∑ i ∈ Finset.univ, + (M.H.eigenvalues i) ^ 2 := by exact fun M => norm_eq_sum_eigenvalues_sq M; -- Applying the bound on the eigenvalues to the Frobenius norm. have h_bound : ∑ i ∈ Finset.univ, ((A.cfc f).H.eigenvalues i) ^ 2 ≤ (Fintype.card d) * C ^ 2 := by @@ -433,16 +442,17 @@ lemma norm_cfc_le_sqrt_card_mul_bound {A : HermitianMat d ℂ} {f : ℝ → ℝ} aesop; nlinarith only [ abs_le.mp h_eigenvalue_bound ]; exact le_trans ( Finset.sum_le_sum fun _ _ => h_bound _ ) ( by simp ); - rw [ h_frobenius_eigenvalues, Real.sqrt_le_left ] <;> nlinarith [ Real.sqrt_nonneg ( Fintype.card d : ℝ ), Real.mul_self_sqrt ( Nat.cast_nonneg ( Fintype.card d ) ) ] + rw [ h_frobenius_eigenvalues, Real.sqrt_le_left ] <;> nlinarith [ Real.sqrt_nonneg ( + Fintype.card d : ℝ ), Real.mul_self_sqrt ( Nat.cast_nonneg ( Fintype.card d ) ) ] /- -The norm of the difference of two functional calculus applications is bounded by `sqrt(d)` times the sup norm of the difference of the functions. +The norm of the difference of two functional calculus applications is bounded by `sqrt(d)` times + the sup norm of the difference of the functions. -/ lemma norm_cfc_sub_cfc_le_sqrt_card {A : HermitianMat d ℂ} {f g : ℝ → ℝ} : ‖A.cfc f - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * ⨆ x ∈ spectrum ℝ A.mat, ‖f x - g x‖ := by rw [ ← HermitianMat.cfc_sub ]; - refine' le_trans ( norm_cfc_le_sqrt_card_mul_bound _ _ ) _; - exact ⨆ x ∈ spectrum ℝ A.mat, ‖f x - g x‖; + refine norm_cfc_le_sqrt_card_mul_bound (C := ⨆ x ∈ spectrum ℝ A.mat, ‖f x - g x‖) ?_ ?_ · exact Real.iSup_nonneg fun _ => Real.iSup_nonneg fun _ => norm_nonneg _; · intro x hx apply le_csSup; @@ -450,12 +460,13 @@ lemma norm_cfc_sub_cfc_le_sqrt_card {A : HermitianMat d ℂ} {f g : ℝ → ℝ} have h_finite : Set.Finite (spectrum ℝ A.mat) := by exact Set.toFinite _; obtain ⟨ M, hM ⟩ := h_finite.exists_finset_coe; - refine' ⟨ ∑ x ∈ M, ‖f x - g x‖, Set.forall_mem_range.2 fun x => _ ⟩; + refine ⟨ ∑ x ∈ M, ‖f x - g x‖, Set.forall_mem_range.2 fun x => ?_ ⟩; rw [ ← hM ]; rw [ @ciSup_eq_ite ]; - split_ifs <;> [ exact Finset.single_le_sum ( fun x _ => norm_nonneg ( f x - g x ) ) ( by assumption ) ; exact le_trans ( by norm_num ) ( Finset.sum_nonneg fun x _ => norm_nonneg ( f x - g x ) ) ]; + split_ifs <;> [ exact Finset.single_le_sum ( fun x _ => norm_nonneg ( f x - g x ) ) ( by + assumption ); exact le_trans ( by norm_num ) ( Finset.sum_nonneg fun x _ => + norm_nonneg ( f x - g x ) ) ]; · exact ⟨ x, by aesop ⟩; - · rfl /- If f and g are close on T, and the spectrum of A is in T, then A.cfc f and A.cfc g are close. @@ -463,13 +474,14 @@ If f and g are close on T, and the spectrum of A is in T, then A.cfc f and A.cfc lemma norm_cfc_sub_le_of_sup_le {A : HermitianMat d ℂ} {f g : ℝ → ℝ} {T : Set ℝ} {ε : ℝ} (hT : spectrum ℝ A.mat ⊆ T) (hε : 0 ≤ ε) (h_sup : ∀ x ∈ T, ‖f x - g x‖ ≤ ε) : ‖A.cfc f - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * ε := by - refine' le_trans ( norm_cfc_sub_cfc_le_sqrt_card ) _; + refine le_trans ( norm_cfc_sub_cfc_le_sqrt_card ) ?_; gcongr; - refine' ciSup_le fun x => _; + refine ciSup_le fun x => ?_; exact Real.iSup_le (fun i => h_sup x (hT i)) hε /-- -If $f$ is jointly continuous on $S \times T$ and $T$ is compact, then $x \mapsto f(x, \cdot)$ is continuous into the space of bounded functions on $T$ with the uniform norm. +If $f$ is jointly continuous on $S \times T$ and $T$ is compact, then $x \mapsto f(x, \cdot)$ is + continuous into the space of bounded functions on $T$ with the uniform norm. -/ lemma dist_lt_of_continuous' {X : Type*} [TopologicalSpace X] {f : X → ℝ → ℝ} {S : Set X} {T : Set ℝ} @@ -478,8 +490,11 @@ lemma dist_lt_of_continuous' {X : Type*} [TopologicalSpace X] {x₀ : X} (hx₀ : x₀ ∈ S) {ε : ℝ} (hε : 0 < ε) : ∃ U ∈ nhds x₀, ∀ x ∈ U ∩ S, ∀ t ∈ T, ‖f x t - f x₀ t‖ < ε := by by_contra h_contra; - -- For each $t \in T$, by continuity at $(x₀, t)$, there exist neighborhoods $U_t$ of $x₀$ and $V_t$ of $t$ such that for all $x \in U_t \cap S$ and $t' \in V_t \cap T$, $|f(x, t') - f(x₀, t)| < \epsilon/2$. - have h_cont : ∀ t ∈ T, ∃ U_t ∈ nhds x₀, ∃ V_t ∈ nhds t, ∀ x ∈ U_t ∩ S, ∀ t' ∈ V_t ∩ T, ‖f x t' - f x₀ t‖ < ε / 2 := by + -- For each $t \in T$, by continuity at $(x₀, t)$, there exist neighborhoods $U_t$ of $x₀$ + -- and $V_t$ of $t$ such that for all $x \in U_t \cap S$ and $t' \in V_t \cap T$, $|f(x, t') + -- - f(x₀, t)| < \epsilon/2$. + have h_cont : ∀ t ∈ T, ∃ U_t ∈ nhds x₀, ∃ V_t ∈ nhds t, ∀ x ∈ U_t ∩ S, ∀ t' ∈ V_t ∩ T, ‖f x t' + - f x₀ t‖ < ε / 2 := by intro t ht have h_cont_t : ∀ᶠ (p : X × ℝ) in nhds (x₀, t), p ∈ S ×ˢ T → ‖f p.1 p.2 - f x₀ t‖ < ε / 2 := by have := hf ( x₀, t ) ⟨ hx₀, ht ⟩; @@ -497,58 +512,93 @@ lemma dist_lt_of_continuous' {X : Type*} [TopologicalSpace X] · intro x hx t ht obtain ⟨t', ht'_fin, ht'_t⟩ : ∃ t' ∈ t_fin, t ∈ V t' := by simpa using ht_fin.2 ht; - have := hUV t' ( ht_fin.1 t' ht'_fin ) x ⟨ Set.mem_iInter₂.1 hx.1 t' ht'_fin, hx.2 ⟩ t ⟨ ht'_t, ht ⟩; - have := hUV t' ( ht_fin.1 t' ht'_fin ) x₀ ⟨ mem_of_mem_nhds ( hU t' ( ht_fin.1 t' ht'_fin ) ), hx₀ ⟩ t ⟨ ht'_t, ht ⟩; - exact abs_lt.mpr ⟨ by linarith [ abs_lt.mp ‹‖f x t - f x₀ t'‖ < ε / 2›, abs_lt.mp ‹‖f x₀ t - f x₀ t'‖ < ε / 2› ], by linarith [ abs_lt.mp ‹‖f x t - f x₀ t'‖ < ε / 2›, abs_lt.mp ‹‖f x₀ t - f x₀ t'‖ < ε / 2› ] ⟩ + have := hUV t' ( ht_fin.1 t' ht'_fin ) x ⟨ Set.mem_iInter₂.1 hx.1 t' ht'_fin, hx.2 ⟩ t ⟨ + ht'_t, ht ⟩; + have := hUV t' ( ht_fin.1 t' ht'_fin ) x₀ ⟨ mem_of_mem_nhds ( hU t' ( ht_fin.1 t' ht'_fin ) + ), hx₀ ⟩ t ⟨ ht'_t, ht ⟩; + exact abs_lt.mpr ⟨ by linarith [ abs_lt.mp ‹‖f x t - f x₀ t'‖ < ε / 2›, abs_lt.mp ‹‖f x₀ t - + f x₀ t'‖ < ε / 2› ], by linarith [ abs_lt.mp ‹‖f x t - f x₀ t'‖ < ε / 2›, abs_lt.mp ‹‖f + x₀ t - f x₀ t'‖ < ε / 2› ] ⟩ /-- The functional calculus is continuous on matrices with spectrum in a compact set. -/ -lemma continuousOn_cfc_of_compact {K : Set ℝ} {g : ℝ → ℝ} (hK : IsCompact K) (hg : ContinuousOn g K) : +lemma continuousOn_cfc_of_compact {K : Set ℝ} {g : ℝ → ℝ} (hK : IsCompact K) (hg : ContinuousOn + g K) : ContinuousOn (fun (A : HermitianMat d ℂ) ↦ A.cfc g) {A | spectrum ℝ A.mat ⊆ K} := by by_contra! h_contra; - -- By Stone-Weierstrass, there exists a sequence of polynomials `p_n` converging uniformly to `g` on `K`. - obtain ⟨p_n, hp_n⟩ : ∃ p_n : ℕ → Polynomial ℝ, (∀ n, ∀ x ∈ K, |(p_n n).eval x - g x| ≤ 1 / (n + 1)) := by + -- By Stone-Weierstrass, there exists a sequence of polynomials `p_n` converging uniformly + -- to `g` on `K`. + obtain ⟨p_n, hp_n⟩ : ∃ p_n : ℕ → Polynomial ℝ, (∀ n, ∀ x ∈ K, |(p_n n).eval x - g x| ≤ 1 / (n + + 1)) := by have h_stone_weierstrass : ∀ ε > 0, ∃ p : Polynomial ℝ, ∀ x ∈ K, |p.eval x - g x| < ε := by have := @exists_polynomial_near_of_continuousOn; obtain ⟨a, b, hab⟩ : ∃ a b : ℝ, K ⊆ Set.Icc a b := by - exact ⟨ hK.bddBelow.some, hK.bddAbove.some, fun x hx => ⟨ hK.bddBelow.choose_spec hx, hK.bddAbove.choose_spec hx ⟩ ⟩; + exact ⟨ hK.bddBelow.some, hK.bddAbove.some, fun x hx => ⟨ hK.bddBelow.choose_spec hx, + hK.bddAbove.choose_spec hx ⟩ ⟩; -- Extend $g$ to a continuous function on $[a, b]$. obtain ⟨f, hf⟩ : ∃ f : ℝ → ℝ, ContinuousOn f (Set.Icc a b) ∧ ∀ x ∈ K, f x = g x := by have := @ContinuousMap.exists_restrict_eq; - specialize this ( show IsClosed K from hK.isClosed ) ( ContinuousMap.mk ( fun x => g x ) <| by exact continuousOn_iff_continuous_domRestrict.mp hg ); - exact ⟨ _, this.choose.continuous.continuousOn, fun x hx => by exact congr_arg ( fun f => f ⟨ x, hx ⟩ ) this.choose_spec ⟩; - exact fun ε εpos => by rcases this a b f hf.1 ε εpos with ⟨ p, hp ⟩ ; exact ⟨ p, fun x hx => by simpa only [ hf.2 x hx ] using hp x ( hab hx ) ⟩ ; - exact ⟨ fun n => Classical.choose ( h_stone_weierstrass ( 1 / ( n + 1 ) ) ( by positivity ) ), fun n x hx => le_of_lt ( Classical.choose_spec ( h_stone_weierstrass ( 1 / ( n + 1 ) ) ( by positivity ) ) x hx ) ⟩; + specialize this ( show IsClosed K from hK.isClosed ) ( ContinuousMap.mk ( fun x => g x ) + <| by exact continuousOn_iff_continuous_domRestrict.mp hg ); + refine ⟨_, this.choose.continuous.continuousOn, fun x hx => ?_⟩ + exact congr_arg (fun f => f ⟨x, hx⟩) this.choose_spec + exact fun ε εpos => by rcases this a b f hf.1 ε εpos with ⟨ p, hp ⟩; exact ⟨ p, fun x hx + => by simpa only [ hf.2 x hx ] using hp x ( hab hx ) ⟩; + exact ⟨ fun n => Classical.choose ( h_stone_weierstrass ( 1 / ( n + 1 ) ) ( by positivity ) + ), fun n x hx => le_of_lt ( Classical.choose_spec ( h_stone_weierstrass ( 1 / ( n + 1 ) + ) ( by positivity ) ) x hx ) ⟩; -- The sequence `A ↦ A.cfc (p_n)` converges uniformly to `A ↦ A.cfc g` on `{A | spectrum A ⊆ K}`. - have h_uniform : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, ∀ A : HermitianMat d ℂ, spectrum ℝ A.mat ⊆ K → ‖A.cfc (fun x => (p_n n).eval x) - A.cfc g‖ < ε := by - -- By the properties of the functional calculus, we have `‖A.cfc p_n - A.cfc g‖ ≤ sqrt(d) * ‖p_n - g‖_{∞, K}`. - have h_uniform_bound : ∀ n, ∀ A : HermitianMat d ℂ, spectrum ℝ A.mat ⊆ K → ‖A.cfc (fun x => (p_n n).eval x) - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * (1 / (n + 1)) := by + have h_uniform : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, ∀ A : HermitianMat d ℂ, spectrum ℝ A.mat ⊆ K → + ‖A.cfc (fun x => (p_n n).eval x) - A.cfc g‖ < ε := by + -- By the properties of the functional calculus, we have `‖A.cfc p_n - A.cfc g‖ ≤ + -- sqrt(d) * ‖p_n - g‖_{∞, K}`. + have h_uniform_bound : ∀ n, ∀ A : HermitianMat d ℂ, spectrum ℝ A.mat ⊆ K → ‖A.cfc (fun x => + (p_n n).eval x) - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * (1 / (n + 1)) := by intro n A hA - have h_uniform_bound : ‖A.cfc (fun x => (p_n n).eval x) - A.cfc g‖ ≤ Real.sqrt (Fintype.card d) * ⨆ x ∈ spectrum ℝ A.mat, |(p_n n).eval x - g x| := by + have h_uniform_bound : ‖A.cfc (fun x => (p_n n).eval x) - A.cfc g‖ ≤ Real.sqrt + (Fintype.card d) * ⨆ x ∈ spectrum ℝ A.mat, |(p_n n).eval x - g x| := by exact norm_cfc_sub_cfc_le_sqrt_card; - refine' le_trans h_uniform_bound ( mul_le_mul_of_nonneg_left _ ( Real.sqrt_nonneg _ ) ); - refine' ciSup_le fun x => _; + refine le_trans h_uniform_bound ( mul_le_mul_of_nonneg_left ?_ ( Real.sqrt_nonneg + (Fintype.card d : ℝ) ) ); + refine ciSup_le fun x => ?_; field_simp; - by_cases hx : x ∈ spectrum ℝ A.mat <;> simp_all - exact le_trans ( mul_le_mul_of_nonneg_right ( hp_n n x ( hA hx ) ) ( by positivity ) ) ( by nlinarith [ mul_inv_cancel₀ ( by positivity : ( n : ℝ ) + 1 ≠ 0 ) ] ); - exact fun ε εpos => ⟨ Nat.ceil ( ε⁻¹ * Real.sqrt ( Fintype.card d ) ), fun n hn A hA => lt_of_le_of_lt ( h_uniform_bound n A hA ) ( by rw [ mul_one_div, div_lt_iff₀ ] <;> nlinarith [ Nat.ceil_le.mp hn, inv_pos.mpr εpos, mul_inv_cancel₀ εpos.ne', Real.sqrt_nonneg ( Fintype.card d ), Real.sq_sqrt ( Nat.cast_nonneg ( Fintype.card d ) ) ] ) ⟩; + by_cases hx : x ∈ spectrum ℝ A.mat <;> simp_all? + exact le_trans ( mul_le_mul_of_nonneg_right ( hp_n n x ( hA hx ) ) ( by positivity ) ) ( + by nlinarith [ mul_inv_cancel₀ ( by positivity : ( n : ℝ ) + 1 ≠ 0 ) ] ); + intro ε εpos + refine ⟨Nat.ceil (ε⁻¹ * Real.sqrt (Fintype.card d)), fun n hn A hA => ?_⟩ + apply lt_of_le_of_lt (h_uniform_bound n A hA) + rw [mul_one_div, div_lt_iff₀] <;> + nlinarith [Nat.ceil_le.mp hn, inv_pos.mpr εpos, mul_inv_cancel₀ εpos.ne', + Real.sqrt_nonneg (Fintype.card d), + Real.sq_sqrt (Nat.cast_nonneg (Fintype.card d))] -- The uniform limit of continuous functions is continuous. - have h_cont : ContinuousOn (fun A : HermitianMat d ℂ => A.cfc g) {A : HermitianMat d ℂ | spectrum ℝ A.mat ⊆ K} := by - have h_seq_cont : ∀ n, ContinuousOn (fun A : HermitianMat d ℂ => A.cfc (fun x => (p_n n).eval x)) {A : HermitianMat d ℂ | spectrum ℝ A.mat ⊆ K} := by + have h_cont : ContinuousOn (fun A : HermitianMat d ℂ => A.cfc g) {A : HermitianMat d ℂ | + spectrum ℝ A.mat ⊆ K} := by + have h_seq_cont : ∀ n, ContinuousOn (fun A : HermitianMat d ℂ => A.cfc (fun x => (p_n + n).eval x)) {A : HermitianMat d ℂ | spectrum ℝ A.mat ⊆ K} := by fun_prop - refine' Metric.continuousOn_iff.mpr _; + refine Metric.continuousOn_iff.mpr ?_; intro A hA ε εpos obtain ⟨N, hN⟩ := h_uniform (ε / 3) (by linarith) - obtain ⟨δ, δpos, hδ⟩ : ∃ δ > 0, ∀ a ∈ {A : HermitianMat d ℂ | spectrum ℝ A.mat ⊆ K}, dist a A < δ → ‖a.cfc (fun x => (p_n N).eval x) - A.cfc (fun x => (p_n N).eval x)‖ < ε / 3 := by + obtain ⟨δ, δpos, hδ⟩ : ∃ δ > 0, ∀ a ∈ {A : HermitianMat d ℂ | spectrum ℝ A.mat ⊆ K}, dist a + A < δ → ‖a.cfc (fun x => (p_n N).eval x) - A.cfc (fun x => (p_n N).eval x)‖ < ε / 3 := by have := Metric.continuousOn_iff.mp ( h_seq_cont N ) A hA ( ε / 3 ) ( by linarith ); - exact ⟨ this.choose, this.choose_spec.1, fun a ha ha' => by simpa only [ dist_eq_norm ] using this.choose_spec.2 a ha ha' ⟩; - refine' ⟨ δ, δpos, fun a ha ha' => _ ⟩; + refine ⟨this.choose, this.choose_spec.1, fun a ha ha' => ?_⟩ + simpa only [dist_eq_norm] using this.choose_spec.2 a ha ha' + refine ⟨ δ, δpos, fun a ha ha' => ?_ ⟩; have := hN N le_rfl a ha; have := hN N le_rfl A hA; rw [ dist_eq_norm ]; - rw [ show a.cfc g - A.cfc g = ( a.cfc g - a.cfc ( fun x => Polynomial.eval x ( p_n N ) ) ) + ( a.cfc ( fun x => Polynomial.eval x ( p_n N ) ) - A.cfc ( fun x => Polynomial.eval x ( p_n N ) ) ) + ( A.cfc ( fun x => Polynomial.eval x ( p_n N ) ) - A.cfc g ) by abel1 ]; - exact lt_of_le_of_lt ( norm_add₃_le .. ) ( by linarith [ norm_sub_rev ( a.cfc g ) ( a.cfc fun x => Polynomial.eval x ( p_n N ) ), norm_sub_rev ( A.cfc fun x => Polynomial.eval x ( p_n N ) ) ( A.cfc g ), hδ a ha ha' ] ); + rw [ show a.cfc g - A.cfc g = ( a.cfc g - a.cfc ( fun x => Polynomial.eval x ( p_n N ) ) ) + + ( a.cfc ( fun x => Polynomial.eval x ( p_n N ) ) - A.cfc ( fun x => Polynomial.eval x ( + p_n N ) ) ) + ( A.cfc ( fun x => Polynomial.eval x ( p_n N ) ) - A.cfc g ) by abel1 ]; + apply lt_of_le_of_lt (norm_add₃_le ..) + have hnorma := norm_sub_rev (a.cfc g) (a.cfc fun x => (p_n N).eval x) + have hnormA := norm_sub_rev (A.cfc fun x => (p_n N).eval x) (A.cfc g) + have hδa := hδ a ha ha' + linarith contradiction end joint_continuity @@ -568,8 +618,10 @@ theorem continuous_cfc_joint_compact {X d : Type*} [TopologicalSpace X] [Fintype have h_eps_delta := Metric.tendsto_nhds.mp h_eps_delta apply Metric.tendsto_nhds.mpr intro ε ε_pos - obtain ⟨U, hU₁, hU₂⟩ : ∃ U ∈ nhds x, ∀ y ∈ U ∩ S, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ Real.sqrt (Fintype.card d) * (ε / (2 * Real.sqrt (Fintype.card d) + 1)) := by - have h_eps_delta₁ : ∀ ε > 0, ∃ U ∈ nhds x, ∀ y ∈ U ∩ S, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ Real.sqrt (Fintype.card d) * ε := by + obtain ⟨U, hU₁, hU₂⟩ : ∃ U ∈ nhds x, ∀ y ∈ U ∩ S, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ + Real.sqrt (Fintype.card d) * (ε / (2 * Real.sqrt (Fintype.card d) + 1)) := by + have h_eps_delta₁ : ∀ ε > 0, ∃ U ∈ nhds x, ∀ y ∈ U ∩ S, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ + ≤ Real.sqrt (Fintype.card d) * ε := by intro ε ε_pos obtain ⟨U, hU₁, hU₂⟩ := dist_lt_of_continuous' (f := f) hT hf x_in_S ε_pos use U, hU₁ @@ -578,7 +630,8 @@ theorem continuous_cfc_joint_compact {X d : Type*} [TopologicalSpace X] [Fintype intro t ht exact le_of_lt (hU₂ y hy t ht) exact h_eps_delta₁ _ (by positivity) - filter_upwards [ h_eps_delta ( ε / 2 ) ( half_pos ε_pos ), self_mem_nhdsWithin, mem_nhdsWithin_of_mem_nhds hU₁ ] with y hy₁ hy₂ hy₃ + filter_upwards [ h_eps_delta ( ε / 2 ) ( half_pos ε_pos ), self_mem_nhdsWithin, + mem_nhdsWithin_of_mem_nhds hU₁ ] with y hy₁ hy₂ hy₃ apply lt_of_le_of_lt (dist_triangle _ ((A y).cfc (f x)) _) nlinarith [hU₂ y ⟨ hy₃, hy₂ ⟩, Real.sqrt_nonneg ( Fintype.card d : ℝ ), @@ -594,14 +647,16 @@ open scoped Matrix.Norms.Frobenius PROBLEM Eigenvalues of a `HermitianMat` are bounded by its (Frobenius) norm. PROVIDED SOLUTION -Use `norm_eq_sum_eigenvalues_sq` which gives `‖A‖² = Σᵢ (A.H.eigenvalues i)²`. Since `(A.H.eigenvalues i)² ≤ Σⱼ (A.H.eigenvalues j)² = ‖A‖²`, we get `|A.H.eigenvalues i| ≤ ‖A‖`. +Use `norm_eq_sum_eigenvalues_sq` which gives `‖A‖² = Σᵢ (A.H.eigenvalues i)²`. Since + `(A.H.eigenvalues i)² ≤ Σⱼ (A.H.eigenvalues j)² = ‖A‖²`, we get `|A.H.eigenvalues i| ≤ ‖A‖`. -/ lemma eigenvalue_norm_le (A : HermitianMat d ℂ) (i : d) : |A.H.eigenvalues i| ≤ ‖A‖ := by have h_eigenvalue_bound : |A.H.eigenvalues i| ^ 2 ≤ ‖A‖ ^ 2 := by rw [ norm_eq_sum_eigenvalues_sq A ]; - simp [pow_two]; - exact Finset.single_le_sum ( fun i _ => mul_self_nonneg ( A.H.eigenvalues i ) ) ( Finset.mem_univ i ); + simp? [pow_two]; + exact Finset.single_le_sum ( fun i _ => mul_self_nonneg ( A.H.eigenvalues i ) ) ( + Finset.mem_univ i ); nlinarith [ norm_nonneg A ] /-- @@ -619,12 +674,23 @@ in an open set `U`, then the spectrum of any Hermitian matrix sufficiently close contained in `U`. This follows from the openness of the set of invertible matrices and compactness. PROVIDED SOLUTION The proof uses the resolvent approach and compactness. -1. Let M = ‖A₀‖ + 1. For B in a ball of radius 1 around A₀, ‖B‖ ≤ M, so spectrum ℝ B.mat ⊆ Metric.closedBall 0 M (by spectrum_subset_closedBall and the triangle inequality for norms). -2. Let K = Metric.closedBall (0 : ℝ) M \ U. Then K is compact (closed and bounded minus open = closed and bounded in ℝ). And K ∩ spectrum ℝ A₀.mat = ∅ (since spectrum ℝ A₀.mat ⊆ U). -3. For each t ∈ K: t ∉ spectrum ℝ A₀.mat. By the definition of spectrum, A₀.mat - algebraMap ℝ (Matrix d d ℂ) t is a unit. The set of units is open (Units.isOpen, since Matrix d d ℂ has HasSummableGeomSeries). The map B ↦ B.mat - algebraMap ℝ _ t is continuous. So there exist δ_t > 0 and ε_t > 0 such that for ‖B - A₀‖ < δ_t and |s - t| < ε_t, B.mat - algebraMap ℝ _ s is a unit, meaning s ∉ spectrum ℝ B.mat. -4. By compactness of K (it's compact since it's a closed subset of the compact ball): finitely many ε-balls B(t_j, ε_{t_j}) cover K. Let δ = min(1, min_j δ_{t_j}). -5. For B with ‖B - A₀‖ < δ: spectrum ℝ B.mat ⊆ Metric.closedBall 0 M (by step 1) and spectrum ℝ B.mat ∩ K = ∅ (by step 4). So spectrum ℝ B.mat ⊆ Metric.closedBall 0 M \ K ⊆ U. -Note: we need to connect spectrum ℝ B.mat (the real spectrum) to IsUnit in the complex matrix ring. Use that for self-adjoint elements, t ∈ spectrum ℝ A.mat iff algebraMap ℝ (Matrix d d ℂ) t ∈ spectrum ℂ A.mat, and the resolvent set is open. We can use spectrum.isOpen_resolventSet or the characterization via IsUnit. +1. Let M = ‖A₀‖ + 1. For B in a ball of radius 1 around A₀, ‖B‖ ≤ M, so spectrum ℝ B.mat ⊆ + Metric.closedBall 0 M (by spectrum_subset_closedBall and the triangle inequality for norms). +2. Let K = Metric.closedBall (0 : ℝ) M \ U. Then K is compact (closed and bounded minus open = + closed and bounded in ℝ). And K ∩ spectrum ℝ A₀.mat = ∅ (since spectrum ℝ A₀.mat ⊆ U). +3. For each t ∈ K: t ∉ spectrum ℝ A₀.mat. By the definition of spectrum, A₀.mat - algebraMap ℝ + (Matrix d d ℂ) t is a unit. The set of units is open (Units.isOpen, since Matrix d d ℂ has + HasSummableGeomSeries). The map B ↦ B.mat - algebraMap ℝ _ t is continuous. So there exist + δ_t > 0 and ε_t > 0 such that for ‖B - A₀‖ < δ_t and |s - t| < ε_t, B.mat - algebraMap ℝ _ s + is a unit, meaning s ∉ spectrum ℝ B.mat. +4. By compactness of K (it's compact since it's a closed subset of the compact ball): finitely + many ε-balls B(t_j, ε_{t_j}) cover K. Let δ = min(1, min_j δ_{t_j}). +5. For B with ‖B - A₀‖ < δ: spectrum ℝ B.mat ⊆ Metric.closedBall 0 M (by step 1) and spectrum ℝ + B.mat ∩ K = ∅ (by step 4). So spectrum ℝ B.mat ⊆ Metric.closedBall 0 M \ K ⊆ U. +Note: we need to connect spectrum ℝ B.mat (the real spectrum) to IsUnit in the complex matrix + ring. Use that for self-adjoint elements, t ∈ spectrum ℝ A.mat iff algebraMap ℝ (Matrix d d + ℂ) t ∈ spectrum ℂ A.mat, and the resolvent set is open. We can use + spectrum.isOpen_resolventSet or the characterization via IsUnit. -/ @@ -650,12 +716,12 @@ lemma spectrum_subset_of_isOpen (A₀ : HermitianMat d ℂ) (U : Set ℝ) ∀ s : ℝ, |s - t| < ε_t → IsUnit (B.mat - algebraMap ℝ (Matrix d d ℂ) s) := by intro t ht have h_unitary : IsUnit (A₀.mat - algebraMap ℝ (Matrix d d ℂ) t) := by - simp_all [Set.ext_iff, spectrum.mem_iff] + simp_all? [Set.ext_iff, spectrum.mem_iff] simpa using hK_disjoint t ht |> IsUnit.neg |> IsUnit.mul <| isUnit_one have h_unitary_open : IsOpen {B : Matrix d d ℂ | IsUnit B} := Units.isOpen have h_unitary_cont : Continuous (fun p : HermitianMat d ℂ × ℝ => p.1.mat - algebraMap ℝ (Matrix d d ℂ) p.2) := by - refine' Continuous.sub _ _ <;> fun_prop (disch := solve_by_elim) + refine Continuous.sub ?_ ?_ <;> fun_prop (disch := solve_by_elim) obtain ⟨ε, ε_pos, hε⟩ := Metric.isOpen_iff.mp (h_unitary_open.preimage h_unitary_cont) (A₀, t) h_unitary exact ⟨ε, ε_pos, ε, ε_pos, fun B hB s hs => hε (show (B, s) ∈ Metric.ball (A₀, t) ε from by @@ -669,10 +735,10 @@ lemma spectrum_subset_of_isOpen (A₀ : HermitianMat d ℂ) (U : Set ℝ) choose! δ hδ ε hε h using h_unitary have := hK_compact.elim_nhds_subcover (fun t => Metric.ball t (ε t)) fun t ht => Metric.ball_mem_nhds t (hε t ht) - simp_all [Set.subset_def] + simp_all? [Set.subset_def] obtain ⟨t, ht₁, ht₂⟩ := this obtain ⟨δ_min, hδ_min_pos, hδ_min⟩ : ∃ δ_min > 0, ∀ i ∈ t, δ_min ≤ δ i := by - by_cases ht : t.Nonempty <;> simp_all [Finset.Nonempty] + by_cases ht : t.Nonempty <;> simp_all? [Finset.Nonempty] · exact ⟨Finset.min' (t.image δ) ⟨_, Finset.mem_image_of_mem δ ht.choose_spec⟩, by obtain ⟨i, hi, he⟩ := Finset.mem_image.mp @@ -681,9 +747,9 @@ lemma spectrum_subset_of_isOpen (A₀ : HermitianMat d ℂ) (U : Set ℝ) exact hδ i (ht₁ i hi), fun i hi => Finset.min'_le _ _ (Finset.mem_image_of_mem δ hi)⟩ · exact ⟨1, zero_lt_one⟩ - refine' ⟨Min.min δ_min 1, lt_min hδ_min_pos zero_lt_one, fun x hx => _⟩ + refine ⟨Min.min δ_min 1, lt_min hδ_min_pos zero_lt_one, fun x hx => ?_⟩ obtain ⟨i, hi, hi'⟩ := ht₂ x hx - exact ⟨ε i - |x - i|, sub_pos.mpr (by simp_all [abs_sub_comm]; exact hi'), + exact ⟨ε i - |x - i|, sub_pos.mpr (by simp_all? [abs_sub_comm]; exact hi'), fun B hB s hs => h i (ht₁ i hi) B (lt_of_lt_of_le hB (min_le_of_left_le (hδ_min i hi))) s (by rw [abs_lt] at *; constructor <;> linarith [abs_le.mp (show |x - i| ≤ |x - i| by rfl)])⟩ -- For `B` with `‖B - A₀‖ < δ`, any `t ∈ σ(B)` lies outside `K`. @@ -692,7 +758,7 @@ lemma spectrum_subset_of_isOpen (A₀ : HermitianMat d ℂ) (U : Set ℝ) obtain ⟨ε_t, hε_t_pos, hε_t⟩ := hδ t htK have h_unit : IsUnit (B.mat - algebraMap ℝ (Matrix d d ℂ) t) := hε_t B hB t (by simpa using hε_t_pos) - exact ht (by have h1 := h_unit.neg; simp_all; exact h1) + exact ht (by have h1 := h_unit.neg; simp_all?; exact h1) filter_upwards [Metric.ball_mem_nhds A₀ (show 0 < Min.min δ 1 by positivity)] with B hB using fun t ht => Classical.not_not.1 fun h => h_not_in_K B (lt_of_lt_of_le (by simpa [dist_eq_norm] using hB) (min_le_left _ _)) t ht @@ -707,19 +773,28 @@ compact version, and bounds the error using upper semicontinuity of the spectrum PROVIDED SOLUTION The proof uses Tietze extension and the compact version `continuousOn_cfc_of_compact`. Step 1: Extend g from the finite set spectrum(A₀) to all of ℝ via Tietze. -The spectrum `Λ := spectrum ℝ A₀.mat` is finite (it equals `Set.range A₀.H.eigenvalues`), hence closed. -The restriction of g to Λ is continuous (any function on a finite set is continuous in a T1 space, using `continuousOn_finite`). -By `ContinuousMap.exists_restrict_eq`, there exists a continuous function `h : C(ℝ, ℝ)` with `h = g` on Λ. +The spectrum `Λ := spectrum ℝ A₀.mat` is finite (it equals `Set.range A₀.H.eigenvalues`), hence + closed. +The restriction of g to Λ is continuous (any function on a finite set is continuous in a T1 + space, using `continuousOn_finite`). +By `ContinuousMap.exists_restrict_eq`, there exists a continuous function `h : C(ℝ, ℝ)` with `h + = g` on Λ. Step 2: (A₀).cfc g = (A₀).cfc h, since g = h on spectrum(A₀) (by `cfc_congr`). Step 3: Show `B ↦ B.cfc h` is continuous at A₀. -Let M = ‖A₀‖ + 1 and K = Set.Icc (-M) M. Since h is continuous on ℝ and hence on K, and K is compact, by `continuousOn_cfc_of_compact`, the map `B ↦ B.cfc h` is continuous on `{B | spectrum ℝ B.mat ⊆ K}`. Since `{B | spectrum ℝ B.mat ⊆ T} ∩ (Metric.ball A₀ 1) ⊆ {B | spectrum ℝ B.mat ⊆ K}` (because for B near A₀, ‖B‖ ≤ M, so spectrum B ⊆ [-M, M] = K by spectrum_subset_closedBall), the map is ContinuousWithinAt at A₀. +Let M = ‖A₀‖ + 1 and K = Set.Icc (-M) M. Since h is continuous on ℝ and hence on K, and K is + compact, by `continuousOn_cfc_of_compact`, the map `B ↦ B.cfc h` is continuous on `{B | + spectrum ℝ B.mat ⊆ K}`. Since `{B | spectrum ℝ B.mat ⊆ T} ∩ (Metric.ball A₀ 1) ⊆ {B | + spectrum ℝ B.mat ⊆ K}` (because for B near A₀, ‖B‖ ≤ M, so spectrum B ⊆ [-M, M] = K by + spectrum_subset_closedBall), the map is ContinuousWithinAt at A₀. Step 4: Show `‖B.cfc g - B.cfc h‖ → 0` as B → A₀ within `{B | σ(B) ⊆ T}`. The function `|g - h|` is 0 on Λ = spectrum(A₀). For each eigenvalue λᵢ ∈ Λ ⊆ T: - g is continuous on T at λᵢ - h is continuous everywhere So `|g(t) - h(t)| = |g(t) - g(λᵢ) + h(λᵢ) - h(t)|` is small for t near λᵢ with t ∈ T. -Define U_ε = {t ∈ ℝ | ∀ λ ∈ Λ, if |t - λ| < some δ then |g(t) - h(t)| < ε for t ∈ T} ∪ (complement of a ball around Λ). -Actually, more precisely: the set V_ε = {t : ℝ | t ∈ T → |g(t) - h(t)| < ε} is an open set containing Λ (since |g - h| = 0 on Λ and both are continuous at each point of Λ along T). +Define U_ε = {t ∈ ℝ | ∀ λ ∈ Λ, if |t - λ| < some δ then |g(t) - h(t)| < ε for t ∈ T} ∪ + (complement of a ball around Λ). +Actually, more precisely: the set V_ε = {t : ℝ | t ∈ T → |g(t) - h(t)| < ε} is an open set + containing Λ (since |g - h| = 0 on Λ and both are continuous at each point of Λ along T). By `spectrum_subset_of_isOpen`, for B near A₀, spectrum(B) ⊆ V_ε. Since spectrum(B) ⊆ T, for t ∈ spectrum(B), |g(t) - h(t)| < ε. So ‖B.cfc g - B.cfc h‖ ≤ sqrt(d) * ε by `norm_cfc_sub_le_of_sup_le`. @@ -737,25 +812,33 @@ lemma continuousWithinAt_cfc_of_continuousOn {T : Set ℝ} {g : ℝ → ℝ} have h_ext : ∃ h : ℝ → ℝ, Continuous h ∧ ∀ x ∈ spectrum ℝ A₀.mat, h x = g x := by have h_finite : Set.Finite (spectrum ℝ A₀.mat) := by exact Set.toFinite _ - generalize_proofs at *; ( - have h_cont : ContinuousOn g (spectrum ℝ A₀.val) := by - exact hg.mono hA₀ - generalize_proofs at *; ( - have := @ContinuousMap.exists_restrict_eq ℝ; - specialize this ( show IsClosed ( spectrum ℝ A₀.val ) from h_finite.isClosed ) ( ContinuousMap.mk ( fun x => g x ) <| by exact continuousOn_iff_continuous_domRestrict.mp h_cont ) ; rcases this with ⟨ h, hh ⟩ ; exact ⟨ h, h.continuous, fun x hx => by exact congr_arg ( fun f => f ⟨ x, hx ⟩ ) hh ⟩ ;)); + have h_cont : ContinuousOn g (spectrum ℝ A₀.mat) := hg.mono hA₀ + obtain ⟨h, hh⟩ := ContinuousMap.exists_restrict_eq h_finite.isClosed + (ContinuousMap.mk (fun x => g x) + (continuousOn_iff_continuous_domRestrict.mp h_cont)) + refine ⟨h, h.continuous, fun x hx => ?_⟩ + exact congr_arg (fun f => f ⟨x, hx⟩) hh obtain ⟨h, hh_cont, hh_eq⟩ := h_ext; - have h_cfc_cont : ContinuousWithinAt (fun B => B.cfc h) {B : HermitianMat d ℂ | spectrum ℝ B.mat ⊆ T} A₀ := by + have h_cfc_cont : ContinuousWithinAt (fun B => B.cfc h) {B : HermitianMat d ℂ | spectrum ℝ + B.mat ⊆ T} A₀ := by exact Continuous.continuousWithinAt (HermitianMat.cfc_continuous hh_cont) - have h_diff_small : ∀ ε > 0, ∃ U ∈ nhds A₀, ∀ B ∈ U ∩ {B : HermitianMat d ℂ | spectrum ℝ B.mat ⊆ T}, ‖B.cfc g - B.cfc h‖ < ε := by + have h_diff_small : ∀ ε > 0, ∃ U ∈ nhds A₀, ∀ B ∈ U ∩ {B : HermitianMat d ℂ | spectrum ℝ B.mat + ⊆ T}, ‖B.cfc g - B.cfc h‖ < ε := by intro ε ε_pos - obtain ⟨δ, δ_pos, hδ⟩ : ∃ δ > 0, ∀ x ∈ T, ∀ y ∈ spectrum ℝ A₀.mat, |x - y| < δ → |g x - h x| < ε / (Real.sqrt (Fintype.card d) + 1) := by - have h_diff_small : ∀ y ∈ spectrum ℝ A₀.mat, ∃ δ > 0, ∀ x ∈ T, |x - y| < δ → |g x - h x| < ε / (Real.sqrt (Fintype.card d) + 1) := by + obtain ⟨δ, δ_pos, hδ⟩ : ∃ δ > 0, ∀ x ∈ T, ∀ y ∈ spectrum ℝ A₀.mat, |x - y| < δ → |g x - h x| + < ε / (Real.sqrt (Fintype.card d) + 1) := by + have h_diff_small : ∀ y ∈ spectrum ℝ A₀.mat, ∃ δ > 0, ∀ x ∈ T, |x - y| < δ → |g x - h x| < + ε / (Real.sqrt (Fintype.card d) + 1) := by intro y hy have h_diff_small : Filter.Tendsto (fun x => |g x - h x|) (nhdsWithin y T) (nhds 0) := by - have h_diff_small : Filter.Tendsto (fun x => g x - h x) (nhdsWithin y T) (nhds (g y - h y)) := by - exact Filter.Tendsto.sub ( hg.continuousWithinAt ( hA₀ hy ) ) ( hh_cont.continuousWithinAt ); + have h_diff_small : Filter.Tendsto (fun x => g x - h x) (nhdsWithin y T) (nhds (g y - + h y)) := by + exact Filter.Tendsto.sub ( hg.continuousWithinAt ( hA₀ hy ) ) ( + hh_cont.continuousWithinAt ); simpa [ hh_eq y hy ] using h_diff_small.abs; - have := Metric.tendsto_nhdsWithin_nhds.mp h_diff_small ( ε / ( Real.sqrt ( Fintype.card d ) + 1 ) ) ( div_pos ε_pos ( add_pos_of_nonneg_of_pos ( Real.sqrt_nonneg _ ) zero_lt_one ) ) ; aesop; + have := Metric.tendsto_nhdsWithin_nhds.mp h_diff_small ( ε / ( Real.sqrt ( Fintype.card + d ) + 1 ) ) ( div_pos ε_pos ( add_pos_of_nonneg_of_pos ( Real.sqrt_nonneg _ ) + zero_lt_one ) ); aesop; choose! δ hδ_pos hδ using h_diff_small; have h_finite : Set.Finite (spectrum ℝ A₀.mat) := by exact Set.toFinite _; @@ -763,42 +846,58 @@ lemma continuousWithinAt_cfc_of_continuousOn {T : Set ℝ} {g : ℝ → ℝ} by_cases h_empty : spectrum ℝ A₀.mat = ∅; · exact ⟨ 1, zero_lt_one, by simp [ h_empty ] ⟩; · have := h_finite.toFinset.exists_min_image δ; - exact Exists.elim ( this ( Finset.nonempty_of_ne_empty ( by simpa [ Set.ext_iff ] using h_empty ) ) ) fun x hx => ⟨ δ x, hδ_pos x ( by simpa using hx.1 ), fun y hy => hx.2 _ ( h_finite.mem_toFinset.mpr hy ) ⟩; - exact ⟨ δ_min, hδ_min_pos, fun x hx y hy hxy => hδ y hy x hx ( lt_of_lt_of_le hxy ( hδ_min y hy ) ) ⟩; - -- By the spectrum_subset_of_isOpen lemma, there exists a neighborhood U of A₀ such that the spectrum of B is within δ of the spectrum of A₀ for all B in U. - obtain ⟨U, hU⟩ : ∃ U ∈ nhds A₀, ∀ B ∈ U, spectrum ℝ B.mat ⊆ {x | ∃ y ∈ spectrum ℝ A₀.mat, |x - y| < δ} := by - have h_spectrum_subset : ∀ᶠ B in nhds A₀, spectrum ℝ B.mat ⊆ Metric.thickening δ (spectrum ℝ A₀.mat) := by + exact Exists.elim ( this ( Finset.nonempty_of_ne_empty ( by simpa [ Set.ext_iff ] + using h_empty ) ) ) fun x hx => ⟨ δ x, hδ_pos x ( by simpa using hx.1 ), fun y hy + => hx.2 _ ( h_finite.mem_toFinset.mpr hy ) ⟩; + exact ⟨ δ_min, hδ_min_pos, fun x hx y hy hxy => hδ y hy x hx ( lt_of_lt_of_le hxy ( hδ_min + y hy ) ) ⟩; + -- By the spectrum_subset_of_isOpen lemma, there exists a neighborhood U of A₀ such that + -- the spectrum of B is within δ of the spectrum of A₀ for all B in U. + obtain ⟨U, hU⟩ : ∃ U ∈ nhds A₀, ∀ B ∈ U, spectrum ℝ B.mat ⊆ {x | ∃ y ∈ spectrum ℝ A₀.mat, |x + - y| < δ} := by + have h_spectrum_subset : ∀ᶠ B in nhds A₀, spectrum ℝ B.mat ⊆ Metric.thickening δ (spectrum + ℝ A₀.mat) := by have h_open : IsOpen (Metric.thickening δ (spectrum ℝ A₀.mat)) := by exact Metric.isOpen_thickening - have := spectrum_subset_of_isOpen A₀ ( Metric.thickening δ ( spectrum ℝ A₀.mat ) ) h_open ( Metric.self_subset_thickening δ_pos _ ) ; aesop; + have := spectrum_subset_of_isOpen A₀ ( Metric.thickening δ ( spectrum ℝ A₀.mat ) ) + h_open ( Metric.self_subset_thickening δ_pos _ ); aesop; generalize_proofs at *; ( - exact ⟨ _, h_spectrum_subset, fun B hB => fun x hx => by simpa [ dist_eq_norm ] using Metric.mem_thickening_iff.mp ( hB hx ) ⟩) + refine ⟨_, h_spectrum_subset, fun B hB x hx => ?_⟩ + simpa [dist_eq_norm] using Metric.mem_thickening_iff.mp (hB hx)) generalize_proofs at *; ( - refine' ⟨ U, hU.1, fun B hB => _ ⟩ - have h_diff_small : ∀ x ∈ spectrum ℝ B.mat, |g x - h x| ≤ ε / (Real.sqrt (Fintype.card d) + 1) := by - exact fun x hx => le_of_lt ( hδ x ( hB.2 hx ) _ ( hU.2 B hB.1 hx |> Classical.choose_spec |> And.left ) ( hU.2 B hB.1 hx |> Classical.choose_spec |> And.right ) ) |> le_trans <| by norm_num; + refine ⟨ U, hU.1, fun B hB => ?_ ⟩ + have h_diff_small : ∀ x ∈ spectrum ℝ B.mat, |g x - h x| ≤ ε / (Real.sqrt (Fintype.card d) + + 1) := by + exact fun x hx => le_of_lt ( hδ x ( hB.2 hx ) _ ( hU.2 B hB.1 hx |> Classical.choose_spec + |> And.left ) ( hU.2 B hB.1 hx |> Classical.choose_spec |> And.right ) ) |> le_trans + <| by norm_num; generalize_proofs at *; ( - have h_diff_small : ‖B.cfc g - B.cfc h‖ ≤ Real.sqrt (Fintype.card d) * (ε / (Real.sqrt (Fintype.card d) + 1)) := by + have h_diff_small : ‖B.cfc g - B.cfc h‖ ≤ Real.sqrt (Fintype.card d) * (ε / (Real.sqrt + (Fintype.card d) + 1)) := by apply_rules [ norm_cfc_sub_le_of_sup_le ]; · positivity; · exact fun x hx => hx generalize_proofs at *; ( - exact h_diff_small.trans_lt ( by rw [ mul_div, div_lt_iff₀ ] <;> nlinarith [ Real.sqrt_nonneg ( Fintype.card d : ℝ ), Real.sq_sqrt ( Nat.cast_nonneg ( Fintype.card d ) ) ] )))); + apply h_diff_small.trans_lt + rw [mul_div, div_lt_iff₀] <;> + nlinarith [Real.sqrt_nonneg (Fintype.card d : ℝ), + Real.sq_sqrt (Nat.cast_nonneg (Fintype.card d))]))) have h_cfc_cont := Metric.continuousWithinAt_iff.mp h_cfc_cont apply Metric.continuousWithinAt_iff.mpr intro ε hε obtain ⟨δ, hδ_pos, hδ⟩ := h_cfc_cont (ε / 2) (half_pos hε) obtain ⟨U, hU_nhds, hU⟩ := h_diff_small (ε / 2) (half_pos hε) use Min.min δ (Metric.mem_nhds_iff.mp hU_nhds).choose - simp [hδ_pos]; - refine' ⟨ _, _ ⟩ + simp? [hδ_pos]; + refine ⟨ ?_, ?_ ⟩ all_goals generalize_proofs at *; · exact ‹∃ ε, 0 < ε ∧ Metric.ball A₀ ε ⊆ U›.choose_spec.1; · intro x hx hx' hx'' have hd1 := hδ hx hx' have hd2 := hU x ⟨(Metric.mem_nhds_iff.mp hU_nhds).choose_spec.2 hx'', hx⟩ have h_eq : A₀.cfc g = A₀.cfc h := by - exact cfc_congr (show Set.EqOn g h (spectrum ℝ A₀.mat) from fun x hx => hh_eq x hx ▸ rfl) ▸ rfl + exact cfc_congr (show Set.EqOn g h (spectrum ℝ A₀.mat) from fun x hx => hh_eq x hx ▸ rfl) + ▸ rfl rw [dist_eq_norm, h_eq] calc ‖x.cfc g - A₀.cfc h‖ = ‖(x.cfc g - x.cfc h) + (x.cfc h - A₀.cfc h)‖ := by congr 1; abel @@ -816,22 +915,37 @@ This is the non-compact replacement for `dist_lt_of_continuous'`: instead of uni on a fixed compact set, we get uniform convergence on the (moving, finite) spectrum. PROVIDED SOLUTION Constructive proof. The key steps: -Step 1: For each eigenvalue λᵢ := (A x₀).H.eigenvalues i (which is in T by hA₁), the function f is continuous at (x₀, λᵢ) within S ×ˢ T. So there exist open neighborhoods U_i of x₀ and V_i of λᵢ such that for all (y, t) ∈ (U_i ∩ S) × (V_i ∩ T), we have ‖f y t - f x₀ t‖ < ε. Here's how to get this: +Step 1: For each eigenvalue λᵢ := (A x₀).H.eigenvalues i (which is in T by hA₁), the function f + is continuous at (x₀, λᵢ) within S ×ˢ T. So there exist open neighborhoods U_i of x₀ and V_i + of λᵢ such that for all (y, t) ∈ (U_i ∩ S) × (V_i ∩ T), we have ‖f y t - f x₀ t‖ < ε. Here's + how to get this: - hf (x₀, λᵢ) gives ContinuousWithinAt at (x₀, λᵢ) within S ×ˢ T -- Apply this to the ε-ball around f(x₀, λᵢ), use that f(x₀, λᵢ) - f(x₀, λᵢ) = 0 to get ‖f y t - f x₀ λᵢ‖ < ε/2 +- Apply this to the ε-ball around f(x₀, λᵢ), use that f(x₀, λᵢ) - f(x₀, λᵢ) = 0 to get ‖f y t - + f x₀ λᵢ‖ < ε/2 - Similarly, hf at (x₀, λᵢ) restricted to {x₀} × T gives ‖f x₀ t - f x₀ λᵢ‖ < ε/2 - Triangle inequality: ‖f y t - f x₀ t‖ < ε - Use `mem_nhds_prod_iff` to extract U_i and V_i from the product neighborhood -Step 2: The open set W := ⋃ᵢ V_i contains spectrum(A x₀) (since each λᵢ ∈ V_i and spectrum = range of eigenvalues). W is open as a union of open sets. By `spectrum_subset_of_isOpen (A x₀) W`, we get ∀ᶠ B in nhds (A x₀), spectrum ℝ B.mat ⊆ W. -Step 3: By ContinuousWithinAt of A at x₀ (from hA₂), and the filter from step 2, we get: ∀ᶠ y in nhdsWithin x₀ S, spectrum ℝ (A y).mat ⊆ W. Convert this to ∃ U' ∈ nhds x₀, ∀ y ∈ U' ∩ S, spectrum(A y) ⊆ W. -Step 4: Take U = U' ∩ ⋂ᵢ U_i (finite intersection since d is Fintype). For y ∈ U ∩ S and t ∈ spectrum(A y): +Step 2: The open set W := ⋃ᵢ V_i contains spectrum(A x₀) (since each λᵢ ∈ V_i and spectrum = + range of eigenvalues). W is open as a union of open sets. By `spectrum_subset_of_isOpen (A + x₀) W`, we get ∀ᶠ B in nhds (A x₀), spectrum ℝ B.mat ⊆ W. +Step 3: By ContinuousWithinAt of A at x₀ (from hA₂), and the filter from step 2, we get: ∀ᶠ y in + nhdsWithin x₀ S, spectrum ℝ (A y).mat ⊆ W. Convert this to ∃ U' ∈ nhds x₀, ∀ y ∈ U' ∩ S, + spectrum(A y) ⊆ W. +Step 4: Take U = U' ∩ ⋂ᵢ U_i (finite intersection since d is Fintype). For y ∈ U ∩ S and t ∈ + spectrum(A y): - t ∈ T (by hA₁) - spectrum(A y) ⊆ W (by step 3), so t ∈ V_i for some i - y ∈ U_i ∩ S - So ‖f y t - f x₀ t‖ < ε (by step 1) -Use `by_contra` and arrive at contradiction, or construct the neighborhood directly using `Filter.inter_mem` and `Filter.iInter_mem` (since d is Fintype, the index set is finite). -IMPORTANT: To get the open sets V_i, use `ContinuousWithinAt` of f at (x₀, λᵢ) which gives an eventually filter statement, then extract using `mem_nhdsWithin_iff_exists_mem_nhds_inter` and `mem_nhds_prod_iff`. -For the continuity of A composed with spectrum_subset_of_isOpen: use `ContinuousWithinAt.eventually` or compose the filter. Specifically: `(hA₂ x₀ hx₀).eventually (spectrum_subset_of_isOpen (A x₀) W hW_open hW_contains)` gives `∀ᶠ y in nhdsWithin x₀ S, spectrum(A y) ⊆ W`. Then use `Filter.Eventually.exists_mem` to get U'. +Use `by_contra` and arrive at contradiction, or construct the neighborhood directly using + `Filter.inter_mem` and `Filter.iInter_mem` (since d is Fintype, the index set is finite). +IMPORTANT: To get the open sets V_i, use `ContinuousWithinAt` of f at (x₀, λᵢ) which gives an + eventually filter statement, then extract using `mem_nhdsWithin_iff_exists_mem_nhds_inter` + and `mem_nhds_prod_iff`. +For the continuity of A composed with spectrum_subset_of_isOpen: use + `ContinuousWithinAt.eventually` or compose the filter. Specifically: `(hA₂ x₀ + hx₀).eventually (spectrum_subset_of_isOpen (A x₀) W hW_open hW_contains)` gives `∀ᶠ y in + nhdsWithin x₀ S, spectrum(A y) ⊆ W`. Then use `Filter.Eventually.exists_mem` to get U'. -/ lemma dist_lt_of_continuous_spectrum {X : Type*} [TopologicalSpace X] @@ -842,12 +956,20 @@ lemma dist_lt_of_continuous_spectrum {X : Type*} [TopologicalSpace X] {x₀ : X} (hx₀ : x₀ ∈ S) {ε : ℝ} (hε : 0 < ε) : ∃ U ∈ nhds x₀, ∀ y ∈ U ∩ S, ∀ t ∈ spectrum ℝ (A y).mat, ‖f y t - f x₀ t‖ < ε := by by_contra h_contra; - -- For each eigenvalue λᵢ := (A x₀).H.eigenvalues i (which is in T by hA₁), the function f is continuous at (x₀, λᵢ) within S ×ˢ T. So there exist open neighborhoods U_i of x₀ and V_i of λᵢ such that for all (y, t) ∈ (U_i ∩ S) × (V_i ∩ T), we have ‖f y t - f x₀ t‖ < ε. - obtain ⟨U_i, V_i, hU_i, hV_i, h_cont⟩ : ∃ (U_i : d → Set X) (V_i : d → Set ℝ), (∀ i, IsOpen (U_i i)) ∧ (∀ i, IsOpen (V_i i)) ∧ (∀ i, x₀ ∈ U_i i) ∧ (∀ i, (A x₀).H.eigenvalues i ∈ V_i i) ∧ (∀ i, ∀ y ∈ U_i i ∩ S, ∀ t ∈ V_i i ∩ T, ‖f y t - f x₀ t‖ < ε) := by - have h_cont : ∀ i, ∃ (U_i : Set X) (V_i : Set ℝ), IsOpen U_i ∧ IsOpen V_i ∧ x₀ ∈ U_i ∧ (A x₀).H.eigenvalues i ∈ V_i ∧ ∀ y ∈ U_i ∩ S, ∀ t ∈ V_i ∩ T, ‖f y t - f x₀ t‖ < ε := by + -- For each eigenvalue λᵢ := (A x₀).H.eigenvalues i (which is in T by hA₁), the + -- function f is continuous at (x₀, λᵢ) within S ×ˢ T. So there exist open + -- neighborhoods U_i of x₀ and V_i of λᵢ such that for all (y, t) ∈ (U_i ∩ S) × (V_i + -- ∩ T), we have ‖f y t - f x₀ t‖ < ε. + obtain ⟨U_i, V_i, hU_i, hV_i, h_cont⟩ : ∃ (U_i : d → Set X) (V_i : d → Set ℝ), (∀ i, + IsOpen (U_i i)) ∧ (∀ i, IsOpen (V_i i)) ∧ (∀ i, x₀ ∈ U_i i) ∧ (∀ i, (A + x₀).H.eigenvalues i ∈ V_i i) ∧ (∀ i, ∀ y ∈ U_i i ∩ S, ∀ t ∈ V_i i ∩ T, ‖f y t - f x₀ + t‖ < ε) := by + have h_cont : ∀ i, ∃ (U_i : Set X) (V_i : Set ℝ), IsOpen U_i ∧ IsOpen V_i ∧ x₀ ∈ U_i ∧ + (A x₀).H.eigenvalues i ∈ V_i ∧ ∀ y ∈ U_i ∩ S, ∀ t ∈ V_i ∩ T, ‖f y t - f x₀ t‖ < ε := by intro i generalize_proofs at *; ( - have h_cont : ContinuousWithinAt (fun p : X × ℝ => f p.1 p.2 - f x₀ p.2) (S ×ˢ T) (x₀, (A x₀).H.eigenvalues i) := by + have h_cont : ContinuousWithinAt (fun p : X × ℝ => f p.1 p.2 - f x₀ p.2) (S ×ˢ T) (x₀, + (A x₀).H.eigenvalues i) := by have hmemT : (A x₀).H.eigenvalues i ∈ T := hA₁ x₀ hx₀ ((A x₀).H.eigenvalues_mem_spectrum_real i) have h1 : ContinuousWithinAt (fun p : X × ℝ => f p.1 p.2) (S ×ˢ T) @@ -862,16 +984,21 @@ lemma dist_lt_of_continuous_spectrum {X : Type*} [TopologicalSpace X] exact h1.sub h2 ) generalize_proofs at *; ( have := h_cont.eventually ( Metric.ball_mem_nhds _ hε ) - simp_all [ dist_eq_norm ] + simp_all? [ dist_eq_norm ] ( rw [ eventually_nhdsWithin_iff ] at this generalize_proofs at *; ( rcases mem_nhds_prod_iff.mp this with ⟨ U, V, hU, hV, h ⟩ generalize_proofs at *; ( - exact ⟨ interior U, isOpen_interior, interior hU, isOpen_interior, mem_interior_iff_mem_nhds.mpr V, mem_interior_iff_mem_nhds.mpr hV, fun y hy hyS t ht htT => h ( Set.mk_mem_prod ( interior_subset hy ) ( interior_subset ht ) ) ⟨ hyS, htT ⟩ ⟩)))) + exact ⟨ interior U, isOpen_interior, interior hU, isOpen_interior, + mem_interior_iff_mem_nhds.mpr V, mem_interior_iff_mem_nhds.mpr hV, fun y hy hyS t + ht htT => h ( Set.mk_mem_prod ( interior_subset hy ) ( interior_subset ht ) ) ⟨ + hyS, htT ⟩ ⟩)))) generalize_proofs at *; ( - choose U_i V_i hU_i hV_i hx₀_i hV_i_i h_cont_i using h_cont; exact ⟨ U_i, V_i, hU_i, hV_i, hx₀_i, hV_i_i, h_cont_i ⟩ ;); - -- The open set W := ⋃ᵢ V_i contains spectrum(A x₀) (since each λᵢ ∈ V_i and spectrum = range of eigenvalues). W is open as a union of open sets. + choose U_i V_i hU_i hV_i hx₀_i hV_i_i h_cont_i using h_cont; exact ⟨ U_i, V_i, hU_i, + hV_i, hx₀_i, hV_i_i, h_cont_i ⟩;); + -- The open set W := ⋃ᵢ V_i contains spectrum(A x₀) (since each λᵢ ∈ V_i and + -- spectrum = range of eigenvalues). W is open as a union of open sets. set W := ⋃ i, V_i i with hW_def have hW_open : IsOpen W := by exact isOpen_iUnion hV_i @@ -886,22 +1013,27 @@ lemma dist_lt_of_continuous_spectrum {X : Type*} [TopologicalSpace X] have hW_subset : ∀ᶠ B in nhds (A x₀), spectrum ℝ B.mat ⊆ W := by exact spectrum_subset_of_isOpen (A x₀) W hW_open hW_spectrum have hW_subset_S : ∀ᶠ y in nhdsWithin x₀ S, spectrum ℝ (A y).mat ⊆ W := by - exact Filter.mem_of_superset ( hA₂.continuousWithinAt hx₀ |> fun h => h.eventually ( hW_subset ) ) fun y hy => hy + exact Filter.mem_of_superset ( hA₂.continuousWithinAt hx₀ |> fun h => h.eventually ( + hW_subset ) ) fun y hy => hy obtain ⟨U', hU'⟩ : ∃ U' ∈ nhds x₀, ∀ y ∈ U' ∩ S, spectrum ℝ (A y).mat ⊆ W := by - obtain ⟨ U', hU' ⟩ := mem_nhdsWithin_iff_exists_mem_nhds_inter.mp hW_subset_S; use U'; aesop; + obtain ⟨ U', hU' ⟩ := mem_nhdsWithin_iff_exists_mem_nhds_inter.mp hW_subset_S; use U'; + aesop; obtain ⟨U'', hU''⟩ : ∃ U'' ∈ nhds x₀, ∀ i, U'' ⊆ U_i i := by - exact ⟨ ⋂ i, U_i i, Filter.mem_of_superset ( Filter.iInter_mem.mpr fun i => IsOpen.mem_nhds ( hU_i i ) ( h_cont.1 i ) ) fun x hx => by aesop, fun i => Set.iInter_subset _ i ⟩ + exact ⟨ ⋂ i, U_i i, Filter.mem_of_superset ( Filter.iInter_mem.mpr fun i => + IsOpen.mem_nhds ( hU_i i ) ( h_cont.1 i ) ) fun x hx => by aesop, fun i => + Set.iInter_subset _ i ⟩ set U := U' ∩ U'' with hU_def have hU_mem : U ∈ nhds x₀ := by exact Filter.inter_mem hU'.1 hU''.1 have hU_subset : ∀ y ∈ U ∩ S, spectrum ℝ (A y).mat ⊆ W := by - exact fun y hy => hU'.2 y ⟨ hy.1.1, hy.2 ⟩ |> Set.Subset.trans <| by simp [ hW_def ] ; + exact fun y hy => hU'.2 y ⟨ hy.1.1, hy.2 ⟩ |> Set.Subset.trans <| by simp [ hW_def ]; have hU_cont : ∀ y ∈ U ∩ S, ∀ t ∈ spectrum ℝ (A y).mat, ‖f y t - f x₀ t‖ < ε := by intro y hy t ht obtain ⟨i, hi⟩ : ∃ i, t ∈ V_i i := by exact Set.mem_iUnion.mp ( hU_subset y hy ht ) |> Exists.imp fun i => by tauto; have h_cont_i : ‖f y t - f x₀ t‖ < ε := by - exact h_cont.2.2 i y ⟨ hU''.2 i ( by aesop ), hy.2 ⟩ t ⟨ hi, hA₁ y hy.2 ht ⟩ |> fun h => by simpa using h; + exact h_cont.2.2 i y ⟨ hU''.2 i ( by aesop ), hy.2 ⟩ t ⟨ hi, hA₁ y hy.2 ht ⟩ |> fun h + => by simpa using h; exact h_cont_i exact h_contra ⟨U, hU_mem, hU_cont⟩ @@ -915,27 +1047,36 @@ the compact case locally: at each point `x₀ ∈ S`, the spectrum of `A x₀` i contained in a compact interval `K = [-M, M]`, and the compact version is applied with a continuous extension of `f x₀` from the finite spectrum to `K`. PROVIDED SOLUTION -The proof follows the same structure as `continuous_cfc_joint_compact` but uses the non-compact helpers `dist_lt_of_continuous_spectrum` and `continuousWithinAt_cfc_of_continuousOn` instead of `dist_lt_of_continuous'` and `continuousOn_cfc_of_compact`. +The proof follows the same structure as `continuous_cfc_joint_compact` but uses the non-compact + helpers `dist_lt_of_continuous_spectrum` and `continuousWithinAt_cfc_of_continuousOn` + instead of `dist_lt_of_continuous'` and `continuousOn_cfc_of_compact`. Fix x ∈ S. Show ContinuousWithinAt. Step 1: Show `ContinuousWithinAt (fun y => (A y).cfc (f x)) S x`. -The function f x (i.e., f(x, ·)) is continuous on T: this follows from `hf.uncurry_left x x_in_S` which gives `ContinuousOn (f x) T`. +The function f x (i.e., f(x, ·)) is continuous on T: this follows from `hf.uncurry_left x + x_in_S` which gives `ContinuousOn (f x) T`. By `continuousWithinAt_cfc_of_continuousOn` with g = f x and T = T: `ContinuousWithinAt (fun B ↦ B.cfc (f x)) {B | spectrum ℝ B.mat ⊆ T} (A x)`. Compose with `hA₂.continuousWithinAt x_in_S` and `hA₁`: `ContinuousWithinAt (fun y => (A y).cfc (f x)) S x`. -Note: We need that `fun y => A y` maps `S` into `{B | spectrum ℝ B.mat ⊆ T}`, which follows from `hA₁`. +Note: We need that `fun y => A y` maps `S` into `{B | spectrum ℝ B.mat ⊆ T}`, which follows from + `hA₁`. Step 2: Use the triangle inequality, exactly as in `continuous_cfc_joint_compact`: Decompose the goal using: - dist ((A y).cfc (f y)) ((A x).cfc (f x)) ≤ dist ((A y).cfc (f y)) ((A y).cfc (f x)) + dist ((A y).cfc (f x)) ((A x).cfc (f x)) + dist ((A y).cfc (f y)) ((A x).cfc (f x)) ≤ dist ((A y).cfc (f y)) ((A y).cfc (f x)) + dist ((A + y).cfc (f x)) ((A x).cfc (f x)) For the first term (f varies, A = A(y)): -Use `dist_lt_of_continuous_spectrum` (our new helper) to get: for any ε > 0, there exists U ∈ nhds x such that for y ∈ U ∩ S and t ∈ spectrum(A y), ‖f y t - f x t‖ < ε. +Use `dist_lt_of_continuous_spectrum` (our new helper) to get: for any ε > 0, there exists U ∈ + nhds x such that for y ∈ U ∩ S and t ∈ spectrum(A y), ‖f y t - f x t‖ < ε. Then by `norm_cfc_sub_le_of_sup_le`, ‖(A y).cfc (f y) - (A y).cfc (f x)‖ ≤ sqrt(d) * ε. For the second term (A varies, f = f(x)): Use step 1 directly (ContinuousWithinAt of B ↦ B.cfc (f x)). Combine with the same nlinarith/ε-δ argument as in `continuous_cfc_joint_compact`. In code, the proof structure should mirror continuous_cfc_joint_compact closely, just replacing: -- `dist_lt_of_continuous' hT hf x_in_S ε_pos` with `dist_lt_of_continuous_spectrum hf hA₁ hA₂ x_in_S ε_pos` -- `continuousOn_cfc_of_compact hT (hf.uncurry_left x x_in_S)` with `continuousWithinAt_cfc_of_continuousOn (hf.uncurry_left x x_in_S) (hA₁ x x_in_S)` composed with hA₂ and hA₁. +- `dist_lt_of_continuous' hT hf x_in_S ε_pos` with `dist_lt_of_continuous_spectrum hf hA₁ hA₂ + x_in_S ε_pos` +- `continuousOn_cfc_of_compact hT (hf.uncurry_left x x_in_S)` with + `continuousWithinAt_cfc_of_continuousOn (hf.uncurry_left x x_in_S) (hA₁ x x_in_S)` composed + with hA₂ and hA₁. -/ @[fun_prop] @@ -950,41 +1091,57 @@ theorem continuous_cfc_joint {X d : Type*} [TopologicalSpace X] [Fintype d] [Dec have h_cont : ∀ x₀ ∈ S, ContinuousWithinAt (fun x => (A x).cfc (f x)) S x₀ := by intro x₀ hx₀ have h_cont : ContinuousWithinAt (fun x => (A x).cfc (f x₀)) S x₀ := by - have h_cont : ContinuousWithinAt (fun B => B.cfc (f x₀)) {B | spectrum ℝ B.mat ⊆ T} (A x₀) := by + have h_cont : ContinuousWithinAt (fun B => B.cfc (f x₀)) {B | spectrum ℝ B.mat ⊆ T} (A + x₀) := by apply_rules [ continuousWithinAt_cfc_of_continuousOn, hf.uncurry_left x₀ hx₀ ] generalize_proofs at *; ( - exact h_cont.comp ( hA₂.continuousWithinAt hx₀ ) ( by aesop ) |> ContinuousWithinAt.mono <| by aesop;) + exact h_cont.comp ( hA₂.continuousWithinAt hx₀ ) ( by aesop ) |> + ContinuousWithinAt.mono <| by aesop;) generalize_proofs at *; ( - -- By the triangle inequality, we can bound the distance between $(A x).cfc (f x)$ and $(A x₀).cfc (f x₀)$. - have h_triangle : ∀ᶠ x in nhdsWithin x₀ S, ‖(A x).cfc (f x) - (A x).cfc (f x₀)‖ ≤ Real.sqrt (Fintype.card d) * (⨆ t ∈ spectrum ℝ (A x).mat, ‖f x t - f x₀ t‖) := by - refine' Filter.Eventually.of_forall fun x => _; + -- By the triangle inequality, we can bound the distance between $(A x).cfc (f + -- x)$ and $(A x₀).cfc (f x₀)$. + have h_triangle : ∀ᶠ x in nhdsWithin x₀ S, ‖(A x).cfc (f x) - (A x).cfc (f x₀)‖ ≤ + Real.sqrt (Fintype.card d) * (⨆ t ∈ spectrum ℝ (A x).mat, ‖f x t - f x₀ t‖) := by + refine Filter.Eventually.of_forall fun x => ?_; exact norm_cfc_sub_cfc_le_sqrt_card generalize_proofs at *; ( - -- By the properties of the supremum, we can bound the distance between $(A x).cfc (f x)$ and $(A x₀).cfc (f x₀)$. - have h_sup : Filter.Tendsto (fun x => ⨆ t ∈ spectrum ℝ (A x).mat, ‖f x t - f x₀ t‖) (nhdsWithin x₀ S) (nhds 0) := by - have h_sup : ∀ ε > 0, ∃ U ∈ nhdsWithin x₀ S, ∀ x ∈ U, ∀ t ∈ spectrum ℝ (A x).mat, ‖f x t - f x₀ t‖ < ε := by + -- By the properties of the supremum, we can bound the distance between $(A + -- x).cfc (f x)$ and $(A x₀).cfc (f x₀)$. + have h_sup : Filter.Tendsto (fun x => ⨆ t ∈ spectrum ℝ (A x).mat, ‖f x t - f x₀ t‖) + (nhdsWithin x₀ S) (nhds 0) := by + have h_sup : ∀ ε > 0, ∃ U ∈ nhdsWithin x₀ S, ∀ x ∈ U, ∀ t ∈ spectrum ℝ (A x).mat, ‖f x + t - f x₀ t‖ < ε := by intro ε ε_pos generalize_proofs at *; ( have := dist_lt_of_continuous_spectrum hf hA₁ hA₂ hx₀ ε_pos generalize_proofs at *; ( - obtain ⟨ U, hU₁, hU₂ ⟩ := this; exact ⟨ U ∩ S, mem_nhdsWithin_iff_exists_mem_nhds_inter.mpr ⟨ U, hU₁, by simp ⟩, fun x hx t ht => hU₂ x ⟨ hx.1, hx.2 ⟩ t ht ⟩ ;)) + obtain ⟨ U, hU₁, hU₂ ⟩ := this; exact ⟨ U ∩ S, + mem_nhdsWithin_iff_exists_mem_nhds_inter.mpr ⟨ U, hU₁, by simp ⟩, fun x hx t ht + => hU₂ x ⟨ hx.1, hx.2 ⟩ t ht ⟩;)) generalize_proofs at *; ( - refine' Metric.tendsto_nhds.mpr _; - intro ε ε_pos; rcases h_sup ( ε / 2 ) ( half_pos ε_pos ) with ⟨ U, hU₁, hU₂ ⟩ ; filter_upwards [ hU₁ ] with x hx; simp_all [ dist_eq_norm ] ; ( - rw [ abs_of_nonneg ( Real.iSup_nonneg fun _ => Real.iSup_nonneg fun _ => abs_nonneg _ ) ] ; refine' lt_of_le_of_lt ( ciSup_le fun t => _ ) ( half_lt_self ε_pos ) ; by_cases ht : t ∈ spectrum ℝ ( A x |> HermitianMat.mat ) <;> simp_all [ abs_lt ] ; + refine Metric.tendsto_nhds.mpr ?_; + intro ε ε_pos; rcases h_sup ( ε / 2 ) ( half_pos ε_pos ) with ⟨ U, hU₁, hU₂ ⟩; + filter_upwards [ hU₁ ] with x hx; simp_all? [ dist_eq_norm ]; ( + rw [ abs_of_nonneg ( Real.iSup_nonneg fun _ => Real.iSup_nonneg fun _ => abs_nonneg _ + ) ]; refine lt_of_le_of_lt ( ciSup_le fun t => ?_ ) ( half_lt_self ε_pos ); + by_cases ht : t ∈ spectrum ℝ ( A x |> HermitianMat.mat ) <;> simp_all? [ abs_lt ]; · exact abs_le.mpr ⟨ by linarith [ hU₂ x hx t ht ], by linarith [ hU₂ x hx t ht ] ⟩; · linarith [ ε_pos ])) generalize_proofs at *; ( - have h_final : Filter.Tendsto (fun x => ‖(A x).cfc (f x) - (A x).cfc (f x₀)‖) (nhdsWithin x₀ S) (nhds 0) := by - exact squeeze_zero_norm' ( by filter_upwards [ h_triangle ] with x hx; simpa using hx ) ( by simpa using h_sup.const_mul _ ) |> fun h => h.trans ( by simp ) ; + have h_final : Filter.Tendsto (fun x => ‖(A x).cfc (f x) - (A x).cfc (f x₀)‖) + (nhdsWithin x₀ S) (nhds 0) := by + exact squeeze_zero_norm' ( by filter_upwards [ h_triangle ] with x hx; simpa using hx + ) ( by simpa using h_sup.const_mul _ ) |> fun h => h.trans ( by simp ); generalize_proofs at *; ( - convert h_cont.add ( show ContinuousWithinAt ( fun x => ( A x |> HermitianMat.cfc ) ( f x ) - ( A x |> HermitianMat.cfc ) ( f x₀ ) ) S x₀ from ?_ ) using 1 ; aesop + convert h_cont.add ( show ContinuousWithinAt ( fun x => ( A x |> HermitianMat.cfc ) ( f + x ) - ( A x |> HermitianMat.cfc ) ( f x₀ ) ) S x₀ from ?_ ) using 1 + · aesop generalize_proofs at *; ( change Filter.Tendsto (fun x => (A x).cfc (f x) - (A x).cfc (f x₀)) (nhdsWithin x₀ S) (nhds ((A x₀).cfc (f x₀) - (A x₀).cfc (f x₀))) rw [sub_self] - exact tendsto_zero_iff_norm_tendsto_zero.mpr h_final ;))))) + exact tendsto_zero_iff_norm_tendsto_zero.mpr h_final;))))) generalize_proofs at *; ( exact h_not_cont_at_x₀ <| fun x hx => h_cont x hx |> ContinuousWithinAt.mono <| by simp;) @@ -1027,24 +1184,34 @@ lemma inv_cfc_eq_cfc_inv (hf : ∀ i, f (A.H.eigenvalues i) ≠ 0) : suffices (A.cfc f).mat⁻¹ = (A.cfc (fun u ↦ 1 / f u)).mat by ext1 simpa using this - have h_def : (A.cfc f).mat = ∑ i, f (A.H.eigenvalues i) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + have h_def : (A.cfc f).mat = ∑ i, f (A.H.eigenvalues i) • (A.H.eigenvectorUnitary.val * + (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by exact cfc_toMat_eq_sum_smul_proj A f; - have h_subst : (A.cfc (fun u ↦ 1 / f u)).mat = ∑ i, (1 / f (A.H.eigenvalues i)) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + have h_subst : (A.cfc (fun u ↦ 1 / f u)).mat = ∑ i, (1 / f (A.H.eigenvalues i)) • + (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * + A.H.eigenvectorUnitary.val.conjTranspose) := by exact cfc_toMat_eq_sum_smul_proj A fun u ↦ 1 / f u; have h_inv : (A.cfc f).mat * (A.cfc (fun u ↦ 1 / f u)).mat = 1 := by - -- Since the eigenvectorUnitary is unitary, we have that the product of the projections is the identity matrix. + -- Since the eigenvectorUnitary is unitary, we have that the product of the projections + -- is the identity matrix. have h_unitary : A.H.eigenvectorUnitary.val * A.H.eigenvectorUnitary.val.conjTranspose = 1 := by simp [ Matrix.IsHermitian.eigenvectorUnitary ]; - have h_inv : ∀ i j, (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) * (A.H.eigenvectorUnitary.val * (Matrix.single j j 1) * A.H.eigenvectorUnitary.val.conjTranspose) = if i = j then A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose else 0 := by - simp [ ← Matrix.mul_assoc ]; - intro i j; split_ifs <;> simp_all [ Matrix.mul_assoc, mul_eq_one_comm.mp h_unitary ] ; - simp_all [ Finset.sum_mul, Finset.mul_sum ]; - have h_sum : ∑ i, (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) = A.H.eigenvectorUnitary.val * (∑ i, Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose := by + have h_inv : ∀ i j, (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * + A.H.eigenvectorUnitary.val.conjTranspose) * (A.H.eigenvectorUnitary.val * (Matrix.single + j j 1) * A.H.eigenvectorUnitary.val.conjTranspose) = if i = j then + A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * + A.H.eigenvectorUnitary.val.conjTranspose else 0 := by + simp? [ ← Matrix.mul_assoc ]; + intro i j; split_ifs <;> simp_all? [ Matrix.mul_assoc, mul_eq_one_comm.mp h_unitary ]; + simp_all? [ Finset.sum_mul, Finset.mul_sum ]; + have h_sum : ∑ i, (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * + A.H.eigenvectorUnitary.val.conjTranspose) = A.H.eigenvectorUnitary.val * (∑ i, + Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose := by simp [ Finset.mul_sum, Finset.sum_mul, Matrix.mul_assoc ]; - simp_all [ Matrix.single ]; + simp_all? [ Matrix.single ]; convert h_unitary using 2; - ext i j; simp [ Matrix.mul_apply] - simp [ Matrix.sum_apply, Finset.filter_eq', Finset.filter_and ]; + ext i j; simp? [ Matrix.mul_apply] + simp? [ Matrix.sum_apply, Finset.filter_eq', Finset.filter_and ]; rw [ Finset.sum_eq_single j ] <;> aesop; rw [ Matrix.inv_eq_right_inv h_inv ]; @@ -1056,26 +1223,36 @@ section integral open MeasureTheory open scoped Matrix.Norms.Frobenius +/-- The extended norm structure induced by the norm on Hermitian matrices. -/ local instance : ENormedAddCommMonoid (HermitianMat d 𝕜) := NormedAddCommGroup.toENormedAddCommMonoid +/-- The extended norm structure induced by the Frobenius norm on matrices. -/ local instance : ENormedAddCommMonoid (Matrix d d 𝕜) := NormedAddCommGroup.toENormedAddCommMonoid -local instance : TopologicalSpace.PseudoMetrizableSpace (Matrix d d 𝕜) := - PseudoEMetricSpace.pseudoMetrizableSpace +omit [Fintype d] [DecidableEq d] in +local instance [Finite d] : TopologicalSpace.PseudoMetrizableSpace (Matrix d d 𝕜) := by + let : DecidableEq d := Classical.decEq d + let := Fintype.ofFinite d + exact PseudoEMetricSpace.pseudoMetrizableSpace -local instance : TopologicalSpace.PseudoMetrizableSpace (HermitianMat d 𝕜) := - PseudoEMetricSpace.pseudoMetrizableSpace +omit [Fintype d] in +local instance [Finite d] : TopologicalSpace.PseudoMetrizableSpace (HermitianMat d 𝕜) := by + let := Fintype.ofFinite d + exact PseudoEMetricSpace.pseudoMetrizableSpace +omit [DecidableEq d] in /-- The integral of a Hermitian matrix function commutes with `toMat`. -/ lemma integral_toMat (A : ℝ → HermitianMat d 𝕜) (T₁ T₂ : ℝ) {μ : Measure ℝ} (hA : IntervalIntegrable A μ T₁ T₂) : (∫ t in T₁..T₂, A t ∂μ).mat = ∫ t in T₁..T₂, (A t).mat ∂μ := by + classical exact ((matₗ (R := ℝ)).intervalIntegral_comp_comm hA).symm +omit [DecidableEq d] in /-- A sum of scaled constant matrices is integrable if the scalar functions are integrable. -/ @@ -1086,34 +1263,40 @@ lemma intervalIntegrable_sum_smul_const (T₁ T₂ : ℝ) {μ : Measure ℝ} (g · exact integrable_finsetSum _ fun i _ ↦ (hg i).1.smul_const (P i) · exact integrable_finsetSum _ fun i _ ↦ (hg i).2.smul_const (P i) +omit [DecidableEq d] in /-- A function to Hermitian matrices is integrable iff its matrix values are integrable. -/ lemma intervalIntegrable_toMat_iff (A : ℝ → HermitianMat d 𝕜) (T₁ T₂ : ℝ) {μ : Measure ℝ} : IntervalIntegrable (fun t ↦ (A t).mat) μ T₁ T₂ ↔ IntervalIntegrable A μ T₁ T₂ := by + classical --TODO Cleanup apply (intervalIntegrable_iff.trans ?_).trans intervalIntegrable_iff.symm constructor <;> intro h; - · -- Since `toMat` is a linear isometry, the integrability of `A.toMat` implies the integrability of `A`. - have h_toMat_integrable : IntegrableOn (fun t ↦ (A t).mat) (Set.uIoc T₁ T₂) μ → IntegrableOn A (Set.uIoc T₁ T₂) μ := by + -- The linear isometry `toMat` reflects integrability. + · have h_toMat_integrable : IntegrableOn (fun t ↦ (A t).mat) (Set.uIoc T₁ T₂) μ → IntegrableOn + A (Set.uIoc T₁ T₂) μ := by intro h_toMat_integrable have h_toMat_linear : ∃ (L : HermitianMat d 𝕜 →ₗ[ℝ] Matrix d d 𝕜), ∀ x, L x = x.mat := by - refine' ⟨ _, _ ⟩; - refine' { .. }; - exacts [ fun x ↦ x.mat, fun x y ↦ rfl, fun m x ↦ rfl, fun x ↦ rfl ]; + exact ⟨(matₗ (R := ℝ)).toLinearMap, fun _ ↦ rfl⟩ obtain ⟨L, hL⟩ := h_toMat_linear; - have h_toMat_linear : IntegrableOn (fun t ↦ L (A t)) (Set.uIoc T₁ T₂) μ → IntegrableOn A (Set.uIoc T₁ T₂) μ := by + have h_toMat_linear : IntegrableOn (fun t ↦ L (A t)) (Set.uIoc T₁ T₂) μ → IntegrableOn A + (Set.uIoc T₁ T₂) μ := by intro h_toMat_integrable - have h_toMat_linear : ∃ (L_inv : Matrix d d 𝕜 →ₗ[ℝ] HermitianMat d 𝕜), ∀ x, L_inv (L x) = x := by + have h_toMat_linear : ∃ (L_inv : Matrix d d 𝕜 →ₗ[ℝ] HermitianMat d 𝕜), ∀ x, L_inv (L x) + = x := by have h_toMat_linear : Function.Injective L := by intro x y hxy; - simp_all only [HermitianMat.ext_iff] - have h_toMat_linear : ∃ (L_inv : Matrix d d 𝕜 →ₗ[ℝ] HermitianMat d 𝕜), L_inv.comp L = LinearMap.id := by + simp_all? only [HermitianMat.ext_iff] + have h_toMat_linear : ∃ (L_inv : Matrix d d 𝕜 →ₗ[ℝ] HermitianMat d 𝕜), L_inv.comp L = + LinearMap.id := by exact IsSemisimpleModule.extension_property L h_toMat_linear LinearMap.id; - exact ⟨ h_toMat_linear.choose, fun x ↦ by simpa using LinearMap.congr_fun h_toMat_linear.choose_spec x ⟩; + refine ⟨h_toMat_linear.choose, fun x => ?_⟩ + simpa using LinearMap.congr_fun h_toMat_linear.choose_spec x obtain ⟨ L_inv, hL_inv ⟩ := h_toMat_linear; have h_toMat_linear : IntegrableOn (fun t ↦ L_inv (L (A t))) (Set.uIoc T₁ T₂) μ := by - exact ContinuousLinearMap.integrable_comp ( L_inv.toContinuousLinearMap ) h_toMat_integrable; + exact ContinuousLinearMap.integrable_comp ( L_inv.toContinuousLinearMap ) + h_toMat_integrable; aesop; aesop; exact h_toMat_integrable h; @@ -1128,7 +1311,9 @@ The CFC of an integrable function family is integrable. lemma integrable_cfc (T₁ T₂ : ℝ) (f : ℝ → ℝ → ℝ) {μ : Measure ℝ} (hf : ∀ i, IntervalIntegrable (fun t ↦ f t (A.H.eigenvalues i)) μ T₁ T₂) : IntervalIntegrable (fun t ↦ A.cfc (f t)) μ T₁ T₂ := by - have h_expand : ∀ t, (A.cfc (f t)).mat = ∑ i, f t (A.H.eigenvalues i) • (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + have h_expand : ∀ t, (A.cfc (f t)).mat = ∑ i, f t (A.H.eigenvalues i) • + (A.H.eigenvectorUnitary.val * (Matrix.single i i 1) * + A.H.eigenvectorUnitary.val.conjTranspose) := by exact fun t ↦ cfc_toMat_eq_sum_smul_proj A (f t); rw [ ← intervalIntegrable_toMat_iff ]; rw [ funext h_expand ]; @@ -1147,7 +1332,7 @@ lemma integral_cfc_eq_cfc_integral (T₁ T₂ : ℝ) {μ : Measure ℝ} (f : ℝ HermitianMat.cfc_toMat_eq_sum_smul_proj A ( f t ), intervalIntegral.integral_finsetSum ]; · rw [ Finset.sum_congr rfl fun i _ ↦ intervalIntegral.integral_smul_const _ _ ]; exact Eq.symm (cfc_toMat_eq_sum_smul_proj A fun u ↦ ∫ (t : ℝ) in T₁..T₂, f t u ∂μ); - · simp_all [ intervalIntegrable_iff ]; + · simp_all? [ intervalIntegrable_iff ]; exact fun i ↦ ( hf i ).smul_const _ · exact integrable_cfc T₁ T₂ f hf @@ -1164,7 +1349,7 @@ theorem cfc_pos_of_pos {A : HermitianMat d 𝕜} {f : ℝ → ℝ} (hA : 0 < A) have h_f_nonneg : 0 ≤ A.cfc f := by rw [HermitianMat.posSemidef_iff_spectrum_nonneg]; rw [ HermitianMat.spectrum_cfc_eq_image ]; - rintro _ ⟨ x, hx, rfl ⟩ ; exact if hx0 : x = 0 then by + rintro _ ⟨ x, hx, rfl ⟩; exact if hx0 : x = 0 then by simpa [ hx0 ] using hf₂ else hf x ( lt_of_le_of_ne ( h_pos x hx ) ( Ne.symm hx0 ) ) |> le_of_lt; have h_f_nonzero : A.cfc f ≠ 0 := by @@ -1173,7 +1358,8 @@ theorem cfc_pos_of_pos {A : HermitianMat d 𝕜} {f : ℝ → ℝ} (hA : 0 < A) exact lt_of_le_of_ne h_f_nonneg h_f_nonzero.symm /-- If two matrices A and B commute, then they is a common matrix with which they are both CFCs of. -This is a variant of the common theorem that "commuting matrices can be simultaneously diagonalized." -/ +This is a variant of the common theorem that "commuting matrices can be simultaneously +diagonalized." -/ theorem _root_.Commute.exists_HermitianMat_cfc (hAB : Commute A.mat B.mat) : ∃ C : HermitianMat d 𝕜, (∃ f : ℝ → ℝ, A = C.cfc f) ∧ (∃ g : ℝ → ℝ, B = C.cfc g) := by obtain ⟨C, ⟨g₁, hg₁⟩, ⟨g₂, hg₂⟩⟩ := hAB.exists_cfc A.H B.H @@ -1186,8 +1372,8 @@ theorem _root_.Commute.exists_HermitianMat_cfc (hAB : Commute A.mat B.mat) : rw [cfc_apply_of_not_predicate C hC] at hg₁ hg₂ use 0 constructor - · exact ⟨0, by simp [HermitianMat.ext_iff, mat_cfc, hg₁]⟩ - · exact ⟨0, by simp [HermitianMat.ext_iff, mat_cfc, hg₂]⟩ + · exact ⟨0, by simp [HermitianMat.ext_iff, hg₁]⟩ + · exact ⟨0, by simp [HermitianMat.ext_iff, hg₂]⟩ open ComplexOrder in theorem cfc_le_cfc_of_PosDef (hfg : ∀ i, 0 < i → f i ≤ g i) (hA : A.mat.PosDef) : @@ -1201,8 +1387,10 @@ theorem cfc_le_cfc_of_PosDef (hfg : ∀ i, 0 < i → f i ≤ g i) (hA : A.mat.Po open ComplexOrder in variable {f} in -/- TODO: Write a version of this that holds more broadly for some sets. Esp closed intervals of reals, -which correspond nicely to closed intervals of matrices. Write the specialization to Set.univ (Monotone +/- TODO: Write a version of this that holds more broadly for some sets. Esp closed intervals of + reals, +which correspond nicely to closed intervals of matrices. Write the specialization to Set.univ + (Monotone instead of MonotoneOn). Also a version that works for StrictMonoOn. -/ theorem cfc_le_cfc_of_commute_monoOn (hf : MonotoneOn f (Set.Ioi 0)) (hAB₁ : Commute A.mat B.mat) (hAB₂ : A ≤ B) (hA : A.mat.PosDef) (hB : B.mat.PosDef) : @@ -1232,8 +1420,10 @@ theorem cfc_le_cfc_of_commute (hf : Monotone f) (hAB₁ : Commute A.mat B.mat) ( apply hf simpa using hAB₂ i ---This is the more general version that requires operator concave functions but doesn't require the inputs --- to commute. Requires the correct statement of operator convexity though, which we don't have right now. +-- This is the more general version that requires operator concave functions but doesn't require +-- the inputs +-- to commute. Requires the correct statement of operator convexity though, which we don't have +-- right now. open ComplexOrder in theorem cfc_monoOn_pos_of_monoOn_posDef {d : Type*} [Fintype d] [DecidableEq d] {f : ℝ → ℝ} (hf_is_operator_convex : False) : @@ -1257,18 +1447,22 @@ theorem inv_ge_one_of_le_one (hA : A.mat.PosDef) (h : A ≤ 1) : 1 ≤ A⁻¹ := intro i have h_eigenvalue : A.mat.PosSemidef := by exact hA.posSemidef - have h_eigenvalue_le_one : ∀ x : d → 𝕜, x ≠ 0 → (star x ⬝ᵥ A.mat.mulVec x) / (star x ⬝ᵥ x) ≤ 1 := by + have h_eigenvalue_le_one : ∀ x : d → 𝕜, x ≠ 0 → (star x ⬝ᵥ A.mat.mulVec x) / (star x + ⬝ᵥ x) ≤ 1 := by intro x hx_ne_zero have h_eigenvalue_le_one : (star x ⬝ᵥ (1 - A.mat).mulVec x) ≥ 0 := by exact Matrix.PosSemidef.dotProduct_mulVec_nonneg h x generalize_proofs at *; ( - rw [ div_le_iff₀ ] <;> simp_all [ Matrix.sub_mulVec, dotProduct_sub ]) + rw [ div_le_iff₀ ] <;> simp_all? [ Matrix.sub_mulVec, dotProduct_sub ]) generalize_proofs at *; ( - have := h_eigenvalue_le_one ( A.H.eigenvectorBasis i ) ?_ <;> simp_all [ div_le_iff₀, ]; - · have := Matrix.IsHermitian.mulVec_eigenvectorBasis ( show Matrix.IsHermitian ( A : Matrix d d _ ) from ‹_› ) i; simp_all [ dotProduct_comm ] ; - by_cases h : ( A.H.eigenvectorBasis i |> WithLp.ofLp ) ⬝ᵥ star ( A.H.eigenvectorBasis i |> WithLp.ofLp ) = 0 <;> simp_all [ div_le_iff₀ ] ; ( + have := h_eigenvalue_le_one ( A.H.eigenvectorBasis i ) ?_ <;> simp_all? [ + div_le_iff₀, ]; + · have := Matrix.IsHermitian.mulVec_eigenvectorBasis ( show Matrix.IsHermitian ( A : + Matrix d d _ ) from ‹_› ) i; simp_all? [ dotProduct_comm ]; + by_cases h : ( A.H.eigenvectorBasis i |> WithLp.ofLp ) ⬝ᵥ star ( + A.H.eigenvectorBasis i |> WithLp.ofLp ) = 0 <;> simp_all? [ div_le_iff₀ ]; ( exact absurd h ( by exact ne_of_apply_ne ( fun x => ‖x‖ ) ( by simp ) )); - · exact fun h => by simpa [ h ] using ( A.H.eigenvectorBasis.orthonormal.ne_zero i ) ;) + · exact fun h => by simpa [ h ] using ( A.H.eigenvectorBasis.orthonormal.ne_zero i );) generalize_proofs at *; ( exact h_le_one) exact fun i => ⟨h_pos i, h_le_one i⟩; @@ -1290,7 +1484,7 @@ lemma trace_cfc_eq (A : HermitianMat d ℂ) (f : ℝ → ℝ) : obtain ⟨e, he⟩ := HermitianMat.cfc_eigenvalues f A have h2 := (A.cfc f).H.trace_eq_sum_eigenvalues rw [he] at h2 - simp [Function.comp] at h2 + simp? [Function.comp] at h2 rw [HermitianMat.mat_cfc] at h1 rw [h2] at h1 have h3 : (Complex.ofReal) (A.cfc f).trace = Complex.ofReal (∑ i, f (A.H.eigenvalues (e i))) := by @@ -1305,57 +1499,73 @@ end uncategorized_cleanup lemma mulVec_eq_zero_iff_inner_eigenvector_zero (A : HermitianMat d ℂ) (x : EuclideanSpace ℂ d) : A.mat.mulVec x = 0 ↔ ∀ i, A.H.eigenvalues i ≠ 0 → inner ℂ (A.H.eigenvectorBasis i) x = 0 := by - -- Since the eigenvectors form an orthonormal basis, we can express x as a linear combination of these eigenvectors. + -- Since the eigenvectors form an orthonormal basis, we can express x as a linear + -- combination of these eigenvectors. obtain ⟨c, hc⟩ : ∃ c : d → ℂ, x = ∑ i, c i • A.H.eigenvectorBasis i := by have := A.H.eigenvectorBasis.sum_repr x; exact ⟨ _, this.symm ⟩; -- By definition of $A$, we know that $A.mulVec (x.ofLp) = \sum_{i} c_i \lambda_i e_i$. - have h_mulVec : A.val.mulVec (x.ofLp) = ∑ i, c i • (A.H.eigenvalues i) • (A.H.eigenvectorBasis i) := by - have h_mulVec : ∀ i, A.val.mulVec (A.H.eigenvectorBasis i) = (A.H.eigenvalues i) • (A.H.eigenvectorBasis i) := by + have h_mulVec : A.val.mulVec (x.ofLp) = ∑ i, c i • (A.H.eigenvalues i) • (A.H.eigenvectorBasis + i) := by + have h_mulVec : ∀ i, A.val.mulVec (A.H.eigenvectorBasis i) = (A.H.eigenvalues i) • + (A.H.eigenvectorBasis i) := by intro i have := A.H.mulVec_eigenvectorBasis i aesop; convert congr_arg ( fun y => ( ∑ i, c i • y i ) ) ( funext fun i => h_mulVec i ) using 1; - · simp [ hc, ]; + · simp? [ hc, ]; ext i; rw [ Matrix.mulVec, dotProduct ] - simp [ Finset.mul_sum _ _ _, mul_assoc, mul_comm ] + simp? [ Finset.mul_sum _ _ _, mul_assoc, mul_comm ] rw [ Finset.sum_comm ] simp [ Matrix.mulVec, dotProduct, mul_comm, Finset.mul_sum _ _ _ ] - · ext i; simp [ Finset.sum_apply ] ; + · ext i; simp [ Finset.sum_apply ]; constructor; · intro h i hi - have h_inner : inner ℂ (A.H.eigenvectorBasis i) (∑ j, c j • (A.H.eigenvalues j) • (A.H.eigenvectorBasis j)) = 0 := by - convert congr_arg ( fun x => inner ℂ ( A.H.eigenvectorBasis i ) x ) ( show ( ∑ j, c j • A.H.eigenvalues j • A.H.eigenvectorBasis j ) = 0 from ?_ ) using 1; + have h_inner : inner ℂ (A.H.eigenvectorBasis i) (∑ j, c j • (A.H.eigenvalues j) • + (A.H.eigenvectorBasis j)) = 0 := by + convert congr_arg ( fun x => inner ℂ ( A.H.eigenvectorBasis i ) x ) ( show ( ∑ j, c j • + A.H.eigenvalues j • A.H.eigenvectorBasis j ) = 0 from ?_ ) using 1; · simp [ inner_zero_right ]; · ext j; replace h := congr_fun h j; aesop; - simp_all - convert congr_arg ( fun x : ℂ => x / ( A.H.eigenvalues i ) ) h_inner using 1 <;> norm_num [ Finset.sum_div _ _ _, hi ]; - refine' Finset.sum_congr rfl fun j _ => _ ; by_cases hj : A.H.eigenvalues j = 0 <;> simp_all [ mul_div_assoc ] ; ring_nf; - · by_cases hij : i = j <;> simp_all [ ]; - · by_cases hij : i = j <;> simp_all [ inner_self_eq_norm_sq_to_K ]; + simp_all? + convert congr_arg ( fun x : ℂ => x / ( A.H.eigenvalues i ) ) h_inner using 1 <;> norm_num [ + Finset.sum_div _ _ _, hi ]; + refine Finset.sum_congr rfl fun j _ => ?_; by_cases hj : A.H.eigenvalues j = 0 <;> simp_all? + [ mul_div_assoc ] + · ring_nf + by_cases hij : i = j <;> simp_all? [ ]; + · by_cases hij : i = j <;> simp_all? [ inner_self_eq_norm_sq_to_K ]; · intro h have h_zero_coeffs : ∀ i, A.H.eigenvalues i ≠ 0 → c i = 0 := by - intro i hi; specialize h i hi; simp_all - rw [ Finset.sum_eq_single i ] at h <;> simp_all [ orthonormal_iff_ite.mp ( A.H.eigenvectorBasis.orthonormal ) ]; + intro i hi; specialize h i hi; simp_all? + rw [ Finset.sum_eq_single i ] at h <;> simp_all? [ orthonormal_iff_ite.mp ( + A.H.eigenvectorBasis.orthonormal ) ]; aesop; - simp_all - exact Finset.sum_eq_zero fun i _ => by by_cases hi : A.H.eigenvalues i = 0 <;> simp [ hi, h_zero_coeffs i ] ; + simp_all? + exact Finset.sum_eq_zero fun i _ => by by_cases hi : A.H.eigenvalues i = 0 <;> simp [ hi, + h_zero_coeffs i ]; open InnerProductSpace in lemma cfc_mulVec_expansion (A : HermitianMat d ℂ) (f : ℝ → ℝ) (x : EuclideanSpace ℂ d) : - (A.cfc f).mat.mulVec x = ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ (A.H.eigenvectorBasis i) x • A.H.eigenvectorBasis i := by - ext i; simp [ Matrix.mulVec, dotProduct ] ; ring_nf - -- By definition of $cfc$, we know that $(A.cfc f).i j = \sum_k f(\lambda_k) \langle e_k, e_i \rangle \langle e_j, e_k \rangle$. - have h_cfc_def : (A.cfc f).mat i = ∑ k, f (A.H.eigenvalues k) • (A.H.eigenvectorBasis k).ofLp i • star (A.H.eigenvectorBasis k).ofLp := by - -- By definition of $cfc$, we know that $(A.cfc f).i j = \sum_k f(\lambda_k) \langle e_k, e_i \rangle \langle e_j, e_k \rangle$ follows directly from the definition of $cfc$. - have h_cfc_def : (A.cfc f).mat = ∑ k, f (A.H.eigenvalues k) • (A.H.eigenvectorUnitary.val * (Matrix.single k k 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by + (A.cfc f).mat.mulVec x = ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ (A.H.eigenvectorBasis i) + x • A.H.eigenvectorBasis i := by + ext i; simp [ Matrix.mulVec, dotProduct ]; ring_nf + -- By definition of $cfc$, we know that $(A.cfc f).i j = \sum_k f(\lambda_k) \langle e_k, + -- e_i \rangle \langle e_j, e_k \rangle$. + have h_cfc_def : (A.cfc f).mat i = ∑ k, f (A.H.eigenvalues k) • (A.H.eigenvectorBasis k).ofLp + i • star (A.H.eigenvectorBasis k).ofLp := by + -- By definition of $cfc$, we know that $(A.cfc f).i j = \sum_k f(\lambda_k) \langle + -- e_k, e_i \rangle \langle e_j, e_k \rangle$ follows directly from the definition of + -- $cfc$. + have h_cfc_def : (A.cfc f).mat = ∑ k, f (A.H.eigenvalues k) • (A.H.eigenvectorUnitary.val * + (Matrix.single k k 1) * A.H.eigenvectorUnitary.val.conjTranspose) := by convert cfc_toMat_eq_sum_smul_proj A f using 1; convert congr_fun h_cfc_def i using 1; - simp [ funext_iff, Matrix.single ]; - simp [ Matrix.mul_apply, Matrix.conjTranspose_apply, Matrix.sum_apply, mul_assoc ]; - intro x; congr; ext y; simp [ Finset.sum_ite, Finset.filter_eq, Finset.filter_and ] ; ring_nf + simp? [ funext_iff, Matrix.single ]; + simp? [ Matrix.mul_apply, Matrix.conjTranspose_apply, Matrix.sum_apply, mul_assoc ]; + intro x; congr; ext y; simp [ Finset.sum_ite, Finset.filter_eq, Finset.filter_and ]; ring_nf rw [ Finset.sum_eq_single y ] <;> aesop; - simp_all [mul_comm, mul_left_comm ] ; ring_nf + simp_all? [mul_comm, mul_left_comm ]; ring_nf convert! congr_arg ( fun y => ∑ j, x.ofLp j * y j ) h_cfc_def using 1 simp [ Finset.mul_sum _ _ _, mul_assoc, mul_left_comm ] ring_nf! @@ -1377,23 +1587,28 @@ lemma ker_cfc_le_ker_on_set have h_inner : ∀ i, A.H.eigenvalues i ≠ 0 → inner ℂ (A.H.eigenvectorBasis i) x = 0 := by have h_inner_zero : (A.cfc f).mat.mulVec x = 0 := by exact (mem_ker_iff_mulVec_zero (A.cfc f) x).mp hx - have h_inner_zero_expansion : ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ (A.H.eigenvectorBasis i) x • A.H.eigenvectorBasis i = 0 := by + have h_inner_zero_expansion : ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ + (A.H.eigenvectorBasis i) x • A.H.eigenvectorBasis i = 0 := by convert h_inner_zero using 1; rw [ cfc_mulVec_expansion ]; exact Iff.symm (WithLp.ofLp_eq_zero 2) - have h_inner_zero_coeff : ∀ i, f (A.H.eigenvalues i) • inner ℂ (A.H.eigenvectorBasis i) x = 0 := by + have h_inner_zero_coeff : ∀ i, f (A.H.eigenvalues i) • inner ℂ (A.H.eigenvectorBasis i) x = + 0 := by intro i - have h_inner_zero_coeff_i : f (A.H.eigenvalues i) • inner ℂ (A.H.eigenvectorBasis i) x = inner ℂ (A.H.eigenvectorBasis i) (∑ j, (f (A.H.eigenvalues j) : ℂ) • inner ℂ (A.H.eigenvectorBasis j) x • A.H.eigenvectorBasis j) := by + have h_inner_zero_coeff_i : f (A.H.eigenvalues i) • inner ℂ (A.H.eigenvectorBasis i) x = + inner ℂ (A.H.eigenvectorBasis i) (∑ j, (f (A.H.eigenvalues j) : ℂ) • inner ℂ + (A.H.eigenvectorBasis j) x • A.H.eigenvectorBasis j) := by simp [ orthonormal_iff_ite.mp ( A.H.eigenvectorBasis.orthonormal ) ] rw [h_inner_zero_coeff_i, h_inner_zero_expansion] - simp [inner_zero_right] -- This line is just to prevent the proof from being completed prematurely. In a real proof, this line would be replaced with the actual proof steps. - have h_inner_zero_final : ∀ i, A.H.eigenvalues i ≠ 0 → inner ℂ (A.H.eigenvectorBasis i) x = 0 := by + simp [inner_zero_right] + have h_inner_zero_final : ∀ i, A.H.eigenvalues i ≠ 0 → inner ℂ (A.H.eigenvectorBasis i) x = + 0 := by -- Since $A.H.eigenvalues i \neq 0$, by hypothesis $h$, we have $f(A.H.eigenvalues i) \neq 0$. have h_f_nonzero : ∀ i, A.H.eigenvalues i ≠ 0 → f (A.H.eigenvalues i) ≠ 0 := by intro i hi; specialize h ( A.H.eigenvalues i ) ( hs <| by - exact Matrix.IsHermitian.eigenvalues_mem_spectrum_real (H A) i ) ; contrapose! hi; aesop; + exact Matrix.IsHermitian.eigenvalues_mem_spectrum_real (H A) i ); contrapose! hi; aesop; generalize_proofs at *; ( - exact fun i hi => by simpa [ h_f_nonzero i hi ] using h_inner_zero_coeff i;) -- This line is just to prevent the proof from being completed prematurely. In a real proof, this line would be replaced with the actual proof steps. + exact fun i hi => by simpa [ h_f_nonzero i hi ] using h_inner_zero_coeff i;) exact h_inner_zero_final; convert mulVec_eq_zero_iff_inner_eigenvector_zero A x |>.2 h_inner using 1; exact mem_ker_iff_mulVec_zero A x @@ -1415,13 +1630,14 @@ lemma ker_le_ker_cfc_on_set (hs : spectrum ℝ A.mat ⊆ s) (h : ∀ i ∈ s, i have h_inner_zero : A.mat.mulVec x = 0 := by exact (mem_ker_iff_mulVec_zero A x).mp hx; have := mulVec_eq_zero_iff_inner_eigenvector_zero A x; aesop; - have h_mulVec_zero : (A.cfc f).mat.mulVec x = ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ (A.H.eigenvectorBasis i) x • A.H.eigenvectorBasis i := by + have h_mulVec_zero : (A.cfc f).mat.mulVec x = ∑ i, (f (A.H.eigenvalues i) : ℂ) • inner ℂ + (A.H.eigenvectorBasis i) x • A.H.eigenvectorBasis i := by convert cfc_mulVec_expansion A f x using 1; convert h_mulVec_zero using 1 - simp_all [ funext_iff] ; - ext i; specialize h_mulVec_zero i; simp_all [ lin, Matrix.mulVec ] ; - refine' Finset.sum_eq_zero fun j _ => ?_ - by_cases hj : A.H.eigenvalues j = 0 <;> simp_all + simp_all? [ funext_iff]; + ext i; specialize h_mulVec_zero i; simp_all? [ lin, Matrix.mulVec ]; + refine Finset.sum_eq_zero fun j _ => ?_ + by_cases hj : A.H.eigenvalues j = 0 <;> simp_all? exact Or.inl ( h _ ( hs (Matrix.IsHermitian.eigenvalues_mem_spectrum_real (H A) j ) ) hj ) lemma ker_le_ker_cfc (h : ∀ i, i = 0 → f i = 0) : @@ -1445,3 +1661,6 @@ theorem ker_cfc_eq_ker_nonneg (hA : 0 ≤ A) (h : ∀ i ≥ 0, f i = 0 ↔ i = 0 end ker_cfc end CFC + + +end HermitianMat diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean index 032557ca37..0ec648bd3a 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Inner.lean @@ -11,17 +11,24 @@ public import Mathlib.Topology.Instances.Real.Lemmas /-! # Inner product of Hermitian Matrices -For general matrices there are multiple reasonable notions of "inner product" (Hilbert–Schmidt inner product, -Frobenius inner product), and so Mathlib avoids giving a canonical `InnerProductSpace` instance. But for the -particular case of Hermitian matrices, these all coincide, so we can put a canonical `InnerProductSpace` +For general matrices there are multiple reasonable notions of "inner product" (Hilbert–Schmidt +inner product, +Frobenius inner product), and so Mathlib avoids giving a canonical `InnerProductSpace` instance. + But for the +particular case of Hermitian matrices, these all coincide, so we can put a canonical + `InnerProductSpace` instance. -This _does_ however induce a `Norm` on `HermitianMat` as well, the Frobenius norm, and this is less obviously -a uniquely correct choice. It is something that one essentially has to live with, with the way that Mathlib -currently structures the instances. (Thankfully, all norms induce the same _topology and bornology_ on +This _does_ however induce a `Norm` on `HermitianMat` as well, the Frobenius norm, and this is + less obviously +a uniquely correct choice. It is something that one essentially has to live with, with the way + that Mathlib +currently structures the instances. (Thankfully, all norms induce the same _topology and + bornology_ on finite-dimensional matrices.) -Some care to be taken so that the topology induced by the InnerProductSpace is defeq with the Subtype +Some care to be taken so that the topology induced by the InnerProductSpace is defeq with the + Subtype topology that HermitianMat inherits from the topology on Matrix. This can be done via `InnerProductSpace.ofCoreOfTopology`. @@ -115,7 +122,8 @@ protected def innerₗ : LinearMap.BilinForm R (HermitianMat n α) where end ring section starring -variable [CommSemiring R] [Ring α] [StarRing α] [Algebra R α] [IsMaximalSelfAdjoint R α] [DecidableEq n] +variable [CommSemiring R] [Ring α] [StarRing α] [Algebra R α] [IsMaximalSelfAdjoint R α] + [DecidableEq n] variable (A B : HermitianMat n α) @[simp] @@ -170,7 +178,7 @@ theorem inner_eq_trace_rc : ⟪A, B⟫ = (A.mat * B.mat).trace := by convert! (Matrix.trace_conjTranspose (A.mat * B.mat)).symm using 1 rw [Matrix.conjTranspose_mul, A.H, B.H, Matrix.trace_mul_comm] -theorem inner_self_nonneg: 0 ≤ ⟪A, A⟫ := by +theorem inner_self_nonneg : 0 ≤ ⟪A, A⟫ := by simp_rw [inner_eq_re_trace, Matrix.trace, Matrix.diag, Matrix.mul_apply, map_sum] refine Finset.sum_nonneg fun i _ ↦ Finset.sum_nonneg fun j _ ↦ ?_ rw [← congrFun₂ A.H, Matrix.conjTranspose_apply] @@ -190,7 +198,8 @@ theorem inner_ge_zero (hA : 0 ≤ A) (hB : 0 ≤ B) : 0 ≤ ⟪A, B⟫ := by rw [zero_le_iff] at hB open MatrixOrder in open Classical in - rw [inner_eq_re_trace, ← CFC.sqrt_mul_sqrt_self A.mat hA, Matrix.trace_mul_cycle, Matrix.trace_mul_cycle] + rw [inner_eq_re_trace, ← CFC.sqrt_mul_sqrt_self A.mat hA, Matrix.trace_mul_cycle, + Matrix.trace_mul_cycle] nth_rewrite 1 [← (Matrix.nonneg_iff_posSemidef.mp (CFC.sqrt_nonneg A.mat)).left] exact (RCLike.nonneg_iff.mp (hB.conjTranspose_mul_mul_same _).trace_nonneg).left @@ -210,7 +219,8 @@ theorem inner_le_mul_trace (hA : 0 ≤ A) (hB : 0 ≤ B) : ⟪A, B⟫ ≤ A.trac simp [mul_comm] --TODO cleanup -private theorem inner_zero_iff_aux_lemma [DecidableEq n] (hA₁ : A.mat.PosSemidef) (hB₁ : B.mat.PosSemidef) : +private theorem inner_zero_iff_aux_lemma [DecidableEq n] (hA₁ : A.mat.PosSemidef) (hB₁ : + B.mat.PosSemidef) : RCLike.re (A.val * B.val).trace = 0 ↔ LinearMap.range (Matrix.toEuclideanLin A.val) ≤ LinearMap.ker (Matrix.toEuclideanLin B.val) := by @@ -251,7 +261,8 @@ private theorem inner_zero_iff_aux_lemma [DecidableEq n] (hA₁ : A.mat.PosSemid = C.conjTranspose * (C * D.conjTranspose) * D from by simp [Matrix.mul_assoc], hE0'] simp -/-- The inner product of two PSD matrices is zero iff they have disjoint support, i.e., each lives entirely +/-- The inner product of two PSD matrices is zero iff they have disjoint support, i.e., each +lives entirely in the other's kernel. -/ theorem inner_zero_iff [DecidableEq n] (hA₁ : 0 ≤ A) (hB₁ : 0 ≤ B) : ⟪A, B⟫ = 0 ↔ A.support ≤ B.ker := by @@ -275,7 +286,8 @@ end RCLike section topology /-! -Theorems about `HermitianMat`s that have to do with the topological structure. Pretty much everything here will +Theorems about `HermitianMat`s that have to do with the topological structure. Pretty much + everything here will assume these are matrices over ℂ, but changes to upgrade this to other types are welcome. -/ open ComplexOrder @@ -299,9 +311,12 @@ section innerproductspace variable {d d₂ : Type*} [Fintype d] [Fintype d₂] {𝕜 : Type*} [RCLike 𝕜] -/-- We define the Hermitian inner product as our "canonical" inner product, which does induce a norm. -This disagrees slightly with Mathlib convention on the `Matrix` type, which avoids asserting one norm -as there are several reasonable ones; for Hermitian matrices, though, this seem to be the right choice. -/ +/-- We define the Hermitian inner product as our "canonical" inner product, which does induce a +norm. +This disagrees slightly with Mathlib convention on the `Matrix` type, which avoids asserting one + norm +as there are several reasonable ones; for Hermitian matrices, though, this seem to be the right + choice. -/ @[reducible] noncomputable def InnerProductCore : InnerProductSpace.Core ℝ (HermitianMat d 𝕜) := { @@ -325,7 +340,8 @@ noncomputable def InnerProductCore : InnerProductSpace.Core ℝ (HermitianMat d rw [Pi.zero_apply, Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h replace h := congrFun h i rw [Pi.zero_apply] at h - rw [add_eq_zero_iff_of_nonneg (by positivity) (by positivity), sq_eq_zero_iff, sq_eq_zero_iff] at h + rw [add_eq_zero_iff_of_nonneg (by positivity) (by positivity), sq_eq_zero_iff, + sq_eq_zero_iff] at h apply RCLike.ext (h.left.trans RCLike.zero_re.symm) (h.right.trans (map_zero _).symm) } @@ -406,31 +422,43 @@ theorem Matrix.IsHermitian_isClosed : IsClosed { A : Matrix n n 𝕜 | A.IsHermi open ComplexOrder -theorem Matrix.PosSemiDef_isClosed : IsClosed { A : Matrix n n 𝕜 | A.PosSemidef } := by - rw [show { A : Matrix n n 𝕜 | A.PosSemidef } = { A | A.IsHermitian } ∩ { A | ∀ x : n → 𝕜, 0 ≤ star x ⬝ᵥ A.mulVec x } from by +omit [Fintype n] in +theorem Matrix.PosSemiDef_isClosed [Finite n] : IsClosed { A : Matrix n n 𝕜 | A.PosSemidef } := by + classical + let := Fintype.ofFinite n + rw [show { A : Matrix n n 𝕜 | A.PosSemidef } = { A | A.IsHermitian } ∩ { A | ∀ x : n → 𝕜, 0 ≤ + star x ⬝ᵥ A.mulVec x } from by ext A; simp [Matrix.posSemidef_iff_dotProduct_mulVec]] refine IsHermitian_isClosed.inter ?_ suffices IsClosed (⋂ x : n → 𝕜, { A : Matrix n n 𝕜 | 0 ≤ star x ⬝ᵥ A.mulVec x }) by rwa [← Set.ofPred_forall] at this exact isClosed_iInter fun _ ↦ (isClosed_Ici (a := 0)).preimage (by fun_prop) -theorem isClosed_nonneg : IsClosed { A : HermitianMat n 𝕜 | 0 ≤ A } := by +omit [Fintype n] in +theorem isClosed_nonneg [Finite n] : IsClosed { A : HermitianMat n 𝕜 | 0 ≤ A } := by + classical + let := Fintype.ofFinite n simp_rw [zero_le_iff] exact Matrix.PosSemiDef_isClosed.preimage_val ---TODO: The PosDef matrices are open *within* the HermitianMat space (not in the ambient space of matrices.) +-- TODO: The PosDef matrices are open *within* the HermitianMat space (not in the ambient space +-- of matrices.) -instance : OrderClosedTopology (HermitianMat d 𝕜) where +omit [Fintype d] in +instance [Finite d] : OrderClosedTopology (HermitianMat d 𝕜) where isClosed_le' := by classical + let := Fintype.ofFinite d convert IsClosed.preimage (X := (HermitianMat d 𝕜 × HermitianMat d 𝕜)) (f := fun xy ↦ (xy.2 - xy.1)) (by fun_prop) isClosed_nonneg ext ⟨x, y⟩ simp only [Set.mem_ofPred_eq, Set.mem_preimage, ← sub_nonneg (b := x)] -/-- Equivalently: the matrices `X` such that `X - A` is PSD and `B - X` is PSD, form a compact set. -/ -instance : CompactIccSpace (HermitianMat d 𝕜) where +omit [Fintype d] in +/-- The matrices `X` such that `X - A` and `B - X` are PSD form a compact set. -/ +instance [Finite d] : CompactIccSpace (HermitianMat d 𝕜) where isCompact_Icc := by + let := Fintype.ofFinite d intros A B have hclosed : IsClosed (Set.Icc A B) := isClosed_Icc apply Metric.isCompact_of_isClosed_isBounded hclosed @@ -443,13 +471,20 @@ instance : CompactIccSpace (HermitianMat d 𝕜) where variable [DecidableEq d] -/-- The PSD matrices that are `≤ 1` are a compact set. More generally, this is true of any closed interval, -but stating that is a bit different because of how numerals are treated. The `0` and `1` here are already -directly matrices, putting in an `(a : ℝ) • 1 ≤ m ∧ m ≤ (b : ℝ) • 1` involves casts. But that theorem should follow +omit [Fintype d] in +/-- The PSD matrices that are `≤ 1` are a compact set. More generally, this is true of any +closed interval, +but stating that is a bit different because of how numerals are treated. The `0` and `1` here + are already +directly matrices, putting in an `(a : ℝ) • 1 ≤ m ∧ m ≤ (b : ℝ) • 1` involves casts. But that + theorem should follow easily from this. More generally `A ≤ m ∧ m ≤ B` is compact. -/ -theorem unitInterval_IsCompact : IsCompact {m : HermitianMat d 𝕜 | 0 ≤ m ∧ m ≤ 1} := - CompactIccSpace.isCompact_Icc +theorem unitInterval_IsCompact [Finite d] : IsCompact {m : HermitianMat d 𝕜 | 0 ≤ m ∧ m ≤ 1} := by + classical + let := Fintype.ofFinite d + exact + CompactIccSpace.isCompact_Icc @[simp] theorem norm_one : ‖(1 : HermitianMat d 𝕜)‖ = √(Fintype.card d : ℝ) := by @@ -460,7 +495,8 @@ theorem norm_one : ‖(1 : HermitianMat d 𝕜)‖ = √(Fintype.card d : ℝ) : theorem norm_eq_trace_sq : ‖A‖ ^ 2 = (A.mat ^ 2).trace := by rw [norm_eq_frobenius, ← RCLike.ofReal_pow, ← Real.rpow_two, ← Real.rpow_mul (by positivity)] simp only [one_div, ne_eq, OfNat.ofNat_ne_zero, not_false_eq_true, inv_mul_cancel₀, Real.rpow_one] - simp only [sq A.mat, map_sum, map_pow, Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, mat_apply] + simp only [sq A.mat, map_sum, map_pow, Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, + mat_apply] congr! with i _ j _ rw [← star_star (A j i), ← A.mat_apply (i := j)] rw [← A.mat.conjTranspose_apply j i, A.H, eq_comm] @@ -507,9 +543,12 @@ lemma inner_eq_doubly_stochastic_sum {d : Type*} [Fintype d] [DecidableEq d] push_cast simp only [Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, hC_norm] simp only [Matrix.diagonal_apply, ite_mul, zero_mul, mul_ite, mul_zero, - Finset.sum_ite_eq, Finset.sum_ite_eq', Finset.mem_univ, if_true, Function.comp_apply, + Finset.sum_ite_eq, Finset.sum_ite_eq', Finset.mem_univ, ite_true, Function.comp_apply, Matrix.conjTranspose_apply] exact Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by simp only [RCLike.ofReal_eq_complex_ofReal]; ring rw [inner_eq_re_trace, key, Matrix.trace_mul_cycle, hUU, one_mul, hC_trace] exact Complex.ofReal_re _ + + +end HermitianMat diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean index f07301dd97..4a41e63218 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Jordan.lean @@ -28,6 +28,7 @@ variable (A B : HermitianMat d 𝕜) namespace HermitianMat +/-- The symmetrized matrix product `(A * B + B * A) / 2`, regarded as a Hermitian matrix. -/ def symmMul : HermitianMat d 𝕜 := ⟨(2 : 𝕜)⁻¹ • (A.mat * B.mat + B.mat * A.mat), by simp [selfAdjoint, IsSelfAdjoint, add_comm, Matrix.star_eq_conjTranspose]⟩ @@ -95,6 +96,7 @@ section starRing variable {d 𝕜 : Type*} [Fintype d] [Field 𝕜] [StarRing 𝕜] variable (A B : HermitianMat d 𝕜) +/-- Hermitian matrices form a commutative magma under the symmetrized product. -/ scoped instance : CommMagma (HermitianMat d 𝕜) where mul := HermitianMat.symmMul mul_comm := HermitianMat.symmMul_comm @@ -113,12 +115,14 @@ scoped instance : IsCommJordan (HermitianMat d 𝕜) where mul_add, add_mul, Matrix.mul_smul, Matrix.smul_mul, Matrix.mul_assoc] abel +/-- Zero is absorbing for the symmetrized product of Hermitian matrices. -/ scoped instance : MulZeroClass (HermitianMat d 𝕜) where zero_mul := by simp [mul_eq_symmMul] mul_zero := by simp [mul_eq_symmMul] variable [DecidableEq d] [Invertible (2 : 𝕜)] +/-- The identity matrix is a unit for the symmetrized product when two is invertible. -/ scoped instance : MulZeroOneClass (HermitianMat d 𝕜) where one_mul := by simp [mul_eq_symmMul] mul_one := by simp [mul_eq_symmMul] @@ -129,6 +133,7 @@ section field variable {d 𝕜 : Type*} [Fintype d] [Field 𝕜] [StarRing 𝕜] +/-- The symmetrized product gives Hermitian matrices a nonunital, nonassociative ring structure. -/ scoped instance : NonUnitalNonAssocRing (HermitianMat d 𝕜) where left_distrib a b c := by ext1 @@ -141,6 +146,8 @@ scoped instance : NonUnitalNonAssocRing (HermitianMat d 𝕜) where variable [Invertible (2 : 𝕜)] [DecidableEq d] +/-- The symmetrized product gives Hermitian matrices a commutative, nonassociative ring +structure. -/ scoped instance : NonAssocCommRing (HermitianMat d 𝕜) where mul_comm := HermitianMat.symmMul_comm diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/NonSingular.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/NonSingular.lean index 733a0a1620..1fe4393936 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/NonSingular.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/NonSingular.lean @@ -8,6 +8,8 @@ module public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry +/-! Nonsingular Hermitian matrices and their inverses. -/ + @[expose] public section noncomputable section @@ -42,6 +44,7 @@ variable {n m R 𝕜 : Type*} [Fintype n] [DecidableEq n] [Fintype m] [Decidable variable [CommRing R] [StarRing R] [RCLike 𝕜] variable (A : HermitianMat n R) (B : HermitianMat m R) +/-- A Hermitian matrix is nonsingular when its underlying matrix is a unit. -/ class NonSingular (A : HermitianMat n R) : Prop where isUnit : IsUnit A.mat @@ -55,7 +58,7 @@ theorem nonsingular_iff_isUnit : NonSingular A ↔ IsUnit A.mat := by instance instHasInv_of_invertible [i : Invertible A.mat] : NonSingular A := ⟨isUnit_of_invertible _⟩ -instance instInvertible_of_hasInv [h : NonSingular A] : Invertible A.mat := +instance instInvertibleOfHasInv [h : NonSingular A] : Invertible A.mat := h.isUnit.invertible instance : NonSingular (1 : HermitianMat n R) := @@ -126,7 +129,7 @@ theorem nonSingular_iff_support_top : NonSingular A ↔ A.support = ⊤ := by obtain ⟨x, hx⟩ := hA (WithLp.toLp 2 y) exact ⟨x.ofLp, by have := congr_arg WithLp.ofLp hx - simp [lin, Matrix.toLpLin_apply] at this + simp? [lin, Matrix.toLpLin_apply] at this exact this⟩ exact Matrix.mulVec_surjective_iff_isUnit.mp this diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean index a7cba43102..9d8d2970b1 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Order.lean @@ -8,6 +8,8 @@ module public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace public import Mathlib.Analysis.RCLike.Basic +/-! The positive semidefinite order and associated norm bounds on Hermitian matrices. -/ + @[expose] public section namespace HermitianMat @@ -135,14 +137,22 @@ theorem le_trace_smul_one [DecidableEq n] (hA : 0 ≤ A) : A ≤ A.trace • 1 : intro i exact Finset.single_le_sum (fun j _ ↦ hA'.eigenvalues_nonneg j) (Finset.mem_univ i) +omit [Fintype n] [Fintype m] in /-- The Kronecker product of two nonnegative Hermitian matrices is nonnegative. -/ -theorem kronecker_nonneg {A : HermitianMat m 𝕜} (hA : 0 ≤ A) (hB : 0 ≤ B) : 0 ≤ A ⊗ₖ B := by +theorem kronecker_nonneg [Finite n] [Finite m] {A : HermitianMat m 𝕜} + (hA : 0 ≤ A) (hB : 0 ≤ B) : 0 ≤ A ⊗ₖ B := by + classical + let := Fintype.ofFinite n + let := Fintype.ofFinite m rw [zero_le_iff, kronecker_mat] classical exact (zero_le_iff.mp hA).PosSemidef_kronecker (zero_le_iff.mp hB) +omit [Fintype n] in /-- The self-Kronecker map `A ↦ A ⊗ₖ A` is monotone on nonnegative Hermitian matrices. -/ -theorem kronecker_self_mono (hA : 0 ≤ A) (hB : 0 ≤ B) (hAB : A ≤ B) : +theorem kronecker_self_mono [Finite n] (hA : 0 ≤ A) (hB : 0 ≤ B) (hAB : A ≤ B) : A ⊗ₖ A ≤ B ⊗ₖ B := by + classical + let := Fintype.ofFinite n rw [← sub_nonneg] have hAC : A ⊗ₖ B + -(A ⊗ₖ A) = A ⊗ₖ (B - A) := by rw [show -(A ⊗ₖ A) = A ⊗ₖ (-A) by @@ -162,8 +172,13 @@ theorem kronecker_self_mono (hA : 0 ≤ A) (hB : 0 ≤ B) (hAB : A ≤ B) : (HermitianMat.kronecker_nonneg hA (sub_nonneg.mpr hAB)) (HermitianMat.kronecker_nonneg (sub_nonneg.mpr hAB) hB) +omit [Fintype n] [Fintype m] in /-- The Kronecker product of two positive Hermitian matrices is positive. -/ -theorem kronecker_pos {A : HermitianMat m 𝕜} (hA : 0 < A) (hB : 0 < B) : 0 < A ⊗ₖ B := by +theorem kronecker_pos [Finite n] [Finite m] {A : HermitianMat m 𝕜} + (hA : 0 < A) (hB : 0 < B) : 0 < A ⊗ₖ B := by + classical + let := Fintype.ofFinite n + let := Fintype.ofFinite m apply lt_of_le_of_ne (kronecker_nonneg hA.le hB.le) intro h replace h := congr(trace $h) @@ -177,8 +192,11 @@ open MatrixOrder in theorem posSemidef_to_nonneg {A : Matrix n n 𝕜} (hA : A.PosSemidef) : 0 ≤ A := by exact hA.nonneg +omit [Fintype n] in open MatrixOrder in -theorem posDef_to_pos {A : Matrix n n 𝕜} (hA : A.PosDef) [Nonempty n] : 0 < A := by +theorem posDef_to_pos [Finite n] {A : Matrix n n 𝕜} (hA : A.PosDef) [Nonempty n] : 0 < A := by + classical + let := Fintype.ofFinite n apply lt_of_le_of_ne hA.posSemidef.nonneg rintro rfl classical simpa [Matrix.det_zero] using hA.det_pos @@ -210,8 +228,10 @@ meta def findMatrixPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : let nonemptyType ← mkAppM ``Nonempty #[nType] match ← try? (synthInstance nonemptyType) with | some nonemptyInst => - -- posDef_to_pos : {𝕜} → [RCLike 𝕜] → {n} → [Fintype n] → {A} → (hA : A.PosDef) → [Nonempty n] → 0 < A - let pf ← mkAppOptM ``HermitianMat.posDef_to_pos #[none, none, none, none, none, p, nonemptyInst] + -- posDef_to_pos : {𝕜} → [RCLike 𝕜] → {n} → [Fintype n] → {A} → (hA : + -- A.PosDef) → [Nonempty n] → 0 < A + let pf ← mkAppOptM ``HermitianMat.posDef_to_pos #[none, none, none, none, none, p, + nonemptyInst] return some (true, pf) | none => let pSemidef ← mkAppM ``Matrix.PosDef.posSemidef #[p] @@ -255,7 +275,10 @@ omit [Fintype n] in theorem mat_posSemidef_to_nonneg (hA : A.mat.PosSemidef) : 0 ≤ A := zero_le_iff.mpr hA -theorem mat_posDef_to_pos [Nonempty n] (hA : A.mat.PosDef) : 0 < A := by +omit [Fintype n] in +theorem mat_posDef_to_pos [Finite n] [Nonempty n] (hA : A.mat.PosDef) : 0 < A := by + classical + let := Fintype.ofFinite n exact posDef_to_pos hA open Lean Meta in @@ -289,8 +312,10 @@ meta def findHermitianMatPSDInExpr (e : Expr) (p : Expr) (ty : Expr) : let nonemptyType ← mkAppM ``Nonempty #[nType] match ← try? (synthInstance nonemptyType) with | some nonemptyInst => - -- mat_posDef_to_pos : {𝕜} → [RCLike 𝕜] → {n} → [Fintype n] → {A} → [Nonempty n] → (hA : A.mat.PosDef) → 0 < A - let pf ← mkAppOptM ``HermitianMat.mat_posDef_to_pos #[none, none, none, none, none, nonemptyInst, p] + -- mat_posDef_to_pos : {𝕜} → [RCLike 𝕜] → {n} → [Fintype n] → {A} → + -- [Nonempty n] → (hA : A.mat.PosDef) → 0 < A + let pf ← mkAppOptM ``HermitianMat.mat_posDef_to_pos #[none, none, none, none, + none, nonemptyInst, p] return some (true, pf) | none => let pSemidef ← mkAppM ``Matrix.PosDef.posSemidef #[p] @@ -327,7 +352,8 @@ meta def evalHermitianMatPSD : PositivityExt where eval {_u _α} _zα _pα? e := else best := .nonnegative pf match best with - | .none => throwError "evalHermitianMatPSD: no A.mat.PosSemidef or A.mat.PosDef hypothesis found for {e}" + | .none => + throwError "evalHermitianMatPSD: no A.mat.PosSemidef or A.mat.PosDef hypothesis found for {e}" | other => return other open Lean Meta Mathlib.Meta.Positivity in @@ -347,14 +373,20 @@ meta def evalHermitianMatKronecker : PositivityExt where eval {_u _α} _zα _pα pure (.nonnegative pfAB') variable (M) in +omit [Fintype m] in open Lean Meta Mathlib.Meta.Positivity in /-- Positivity extension for `HermitianMat.conj`: nonneg when the inner matrix is. -/ -theorem conj_nonneg (hA : 0 ≤ A) : 0 ≤ A.conj M := by +theorem conj_nonneg [Finite m] (hA : 0 ≤ A) : 0 ≤ A.conj M := by + classical + let := Fintype.ofFinite m rw [zero_le_iff] at hA ⊢ exact Matrix.PosSemidef.mul_mul_conjTranspose_same hA M -theorem conj_pos [DecidableEq n] {A : HermitianMat n 𝕜} {M : Matrix m n 𝕜} (hA : 0 < A) +omit [Fintype m] in +theorem conj_pos [Finite m] [DecidableEq n] {A : HermitianMat n 𝕜} {M : Matrix m n 𝕜} (hA : 0 < A) (h : LinearMap.ker M.toEuclideanLin ≤ A.ker) : 0 < A.conj M := by + classical + let := Fintype.ofFinite m classical exact (A.conj_nonneg M hA.le).lt_of_ne' (A.conj_ne_zero hA.ne' h) open Lean Meta Mathlib.Meta.Positivity in @@ -398,7 +430,7 @@ theorem convex_cone (hA : 0 ≤ A) (hB : 0 ≤ B) {c₁ c₂ : ℝ} (hc₁ : 0 exact (hA.smul hc₁).add (hB.smul hc₂) theorem sq_nonneg [DecidableEq n] : 0 ≤ A ^ 2 := by - simp [zero_le_iff, pow_two] + simp? [zero_le_iff, pow_two] nth_rewrite 1 [←Matrix.IsHermitian.eq A.H] exact Matrix.posSemidef_conjTranspose_mul_self A.mat @@ -412,7 +444,10 @@ theorem ker_antitone [DecidableEq n] (hA : 0 ≤ A) : A ≤ B → B.ker ≤ A.ke rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA exact le_antisymm h (hA.right x) -theorem conj_mono (h : A ≤ B) : A.conj M ≤ B.conj M := by +omit [Fintype m] in +theorem conj_mono [Finite m] (h : A ≤ B) : A.conj M ≤ B.conj M := by + classical + let := Fintype.ofFinite m have h_conj_pos : (M * (B - A).mat * Mᴴ).PosSemidef := Matrix.PosSemidef.mul_mul_conjTranspose_same h M constructor; @@ -501,7 +536,6 @@ theorem ker_sum [DecidableEq n] (f : ι → HermitianMat n 𝕜) (hf : ∀ i, 0 theorem ker_conj [DecidableEq n] (hA : 0 ≤ A) (B : Matrix n n 𝕜) : (A.conj B).ker = Submodule.comap (Matrix.toEuclideanLin B.conjTranspose) A.ker := by - ext v simp only [Submodule.mem_comap] change v ∈ (A.conj B).ker ↔ (Matrix.toEuclideanLin Bᴴ) v ∈ A.ker @@ -511,15 +545,17 @@ theorem ker_conj [DecidableEq n] (hA : 0 ≤ A) (B : Matrix n n 𝕜) : · rw [ mem_ker_iff_mulVec_zero ]; congr! 2; · convert congr_arg ( fun x : EuclideanSpace _ _ => star v.ofLp ⬝ᵥ x ) h using 1 - simp [Matrix.mulVec_mulVec, Matrix.dotProduct_mulVec] - · simp [conj_apply_mat, Matrix.mul_assoc, Matrix.dotProduct_mulVec, Matrix.star_mulVec, Matrix.conjTranspose_conjTranspose, lin] + · simp [Matrix.mulVec_mulVec, Matrix.dotProduct_mulVec] + simp [conj_apply_mat, Matrix.mul_assoc, Matrix.dotProduct_mulVec, Matrix.star_mulVec, + Matrix.conjTranspose_conjTranspose, lin] · simp [dotProduct] - · simp only [ker, Matrix.mul_assoc, LinearMap.mem_ker] + · simp only [ker, LinearMap.mem_ker] convert congr_arg B.toEuclideanLin h using 1 - · simp [HermitianMat.lin, conj_apply_mat, Matrix.toEuclideanLin, Matrix.mulVec_mulVec] + · simp [HermitianMat.lin, conj_apply_mat, Matrix.toEuclideanLin] · exact Eq.symm (LinearMap.map_zero (Matrix.toEuclideanLin B)) -theorem ker_le_of_le_smul {α : ℝ} [DecidableEq n] (hα : α ≠ 0) (hA : 0 ≤ A) (hAB : A ≤ α • B) : B.ker ≤ A.ker := by +theorem ker_le_of_le_smul {α : ℝ} [DecidableEq n] (hα : α ≠ 0) (hA : 0 ≤ A) (hAB : A ≤ α • B) : + B.ker ≤ A.ker := by rw [← ker_pos_smul B hα] exact ker_antitone hA hAB @@ -576,17 +612,25 @@ open MatrixOrder in theorem mat_pos (hA : 0 < A) : 0 < A.mat := hA +omit [Fintype n] in open MatrixOrder in /-- `Mᴴ * M` is nonneg in the Loewner order, for any matrix `M`. -/ -theorem _root_.Matrix.nonneg_conjTranspose_mul_self {m : Type*} [Fintype m] - (M : Matrix m n 𝕜) : 0 ≤ M.conjTranspose * M := - Matrix.nonneg_iff_posSemidef.mpr (Matrix.posSemidef_conjTranspose_mul_self M) +theorem _root_.Matrix.nonneg_conjTranspose_mul_self [Finite n] {m : Type*} [Fintype m] + (M : Matrix m n 𝕜) : 0 ≤ M.conjTranspose * M := by + classical + let := Fintype.ofFinite n + exact + Matrix.nonneg_iff_posSemidef.mpr (Matrix.posSemidef_conjTranspose_mul_self M) +omit [Fintype n] in open MatrixOrder in /-- `M * Mᴴ` is nonneg in the Loewner order, for any matrix `M`. -/ -theorem _root_.Matrix.nonneg_self_mul_conjTranspose {m : Type*} [Fintype m] - (M : Matrix n m 𝕜) : 0 ≤ M * M.conjTranspose := - Matrix.nonneg_iff_posSemidef.mpr (Matrix.posSemidef_self_mul_conjTranspose M) +theorem _root_.Matrix.nonneg_self_mul_conjTranspose [Finite n] {m : Type*} [Fintype m] + (M : Matrix n m 𝕜) : 0 ≤ M * M.conjTranspose := by + classical + let := Fintype.ofFinite n + exact + Matrix.nonneg_iff_posSemidef.mpr (Matrix.posSemidef_self_mul_conjTranspose M) omit [Fintype m] in open MatrixOrder in @@ -601,7 +645,8 @@ theorem subtype_mk_pos {M : Matrix m m 𝕜} (h : 0 < M) : h open MatrixOrder in -private theorem _root_.Matrix.eigenvalues_nonneg [DecidableEq n] {M : Matrix n n 𝕜} (h : 0 ≤ M) (i : n) : +private theorem _root_.Matrix.eigenvalues_nonneg [DecidableEq n] {M : Matrix n n 𝕜} (h : 0 ≤ M) + (i : n) : 0 ≤ (Matrix.LE.le.posSemidef h).isHermitian.eigenvalues i := (Matrix.LE.le.posSemidef h).eigenvalues_nonneg i @@ -698,13 +743,15 @@ example (M : Matrix n m ℂ) : 0 ≤ M * M.conjTranspose := by positivity -- Test: ⟨Mᴴ * M, _⟩ nonneg as HermitianMat example (M : Matrix m n ℂ) : - (0 : HermitianMat n ℂ) ≤ ⟨M.conjTranspose * M, Matrix.isHermitian_conjTranspose_mul_self M⟩ := by + (0 : HermitianMat n ℂ) ≤ ⟨M.conjTranspose * M, Matrix.isHermitian_conjTranspose_mul_self M⟩ + := by exact zero_le_iff.mpr (Matrix.posSemidef_conjTranspose_mul_self M) -- Test: ⟨M * Mᴴ, _⟩ nonneg as HermitianMat example (M : Matrix n m ℝ) : - (0 : HermitianMat n ℝ) ≤ ⟨M * M.conjTranspose, Matrix.isHermitian_mul_conjTranspose_self M⟩ := by + (0 : HermitianMat n ℝ) ≤ ⟨M * M.conjTranspose, Matrix.isHermitian_mul_conjTranspose_self M⟩ + := by exact zero_le_iff.mpr (Matrix.posSemidef_self_mul_conjTranspose M) example (M : Matrix n n ℂ) (i : n) (A : HermitianMat n ℂ) (hA : 0 ≤ A) : @@ -713,3 +760,6 @@ example (M : Matrix n n ℂ) (i : n) (A : HermitianMat n ℂ) (hA : 0 ≤ A) : end tests end MatrixPositivity + + +end HermitianMat diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean index 754ed0de62..6290006062 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Proj.lean @@ -49,14 +49,15 @@ noncomputable def projector (S : Submodule 𝕜 (EuclideanSpace 𝕜 n)) : Hermi let P := S.subtypeL.comp S.orthogonalProjectionOnto ⟨P.toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis, by ext i j - have h1 := S.inner_starProjection_left_eq_right (EuclideanSpace.single i 1) (EuclideanSpace.single j 1) - simp_all [EuclideanSpace.inner_single_right, EuclideanSpace.inner_single_left] + have h1 := S.inner_starProjection_left_eq_right (EuclideanSpace.single i 1) + (EuclideanSpace.single j 1) + simp_all? [EuclideanSpace.inner_single_right, EuclideanSpace.inner_single_left] exact h1⟩ theorem projector_add_orthogonal : projector S + projector Sᗮ = 1 := by unfold projector; erw [ Subtype.mk_eq_mk ]; - ext i j; simp [ LinearMap.toMatrix_apply, Matrix.one_apply ] ; + ext i j; simp [ LinearMap.toMatrix_apply, Matrix.one_apply ]; theorem projector_nonneg : 0 ≤ projector S := by rw [zero_le_iff] @@ -80,16 +81,21 @@ theorem projector_ker : (projector S).ker = Sᗮ := by @[simp] theorem trace_projector : (projector S).trace = (Module.finrank 𝕜 S : ℝ) := by - suffices h_trace : ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = Module.finrank 𝕜 S by + suffices h_trace : ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix + (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = + Module.finrank 𝕜 S by rw [trace_eq_re_trace] change RCLike.re ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = _ rw [h_trace] simp - suffices h_trace : ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = (LinearMap.id.toMatrix (Module.finBasis 𝕜 S) (Module.finBasis 𝕜 S)).trace by + suffices h_trace : ((S.subtype ∘ₗ S.orthogonalProjectionOnto).toMatrix + (EuclideanSpace.basisFun n 𝕜).toBasis (EuclideanSpace.basisFun n 𝕜).toBasis).trace = + (LinearMap.id.toMatrix (Module.finBasis 𝕜 S) (Module.finBasis 𝕜 S)).trace by simp [h_trace] - rw [LinearMap.toMatrix_comp _ (Module.finBasis 𝕜 ↥S), Matrix.trace_mul_comm, ← LinearMap.toMatrix_comp] + rw [LinearMap.toMatrix_comp _ (Module.finBasis 𝕜 ↥S), Matrix.trace_mul_comm, ← + LinearMap.toMatrix_comp] congr 2 ext1 simp [Submodule.orthogonalProjectionOnto_mem_subspace_eq_self] @@ -126,18 +132,22 @@ theorem supportProj_of_nonSingular [NonSingular A] : A.supportProj = 1 := by simpa using A.kerProj_add_supportProj /-- -The projector onto a submodule S is the sum of the outer products of the vectors in an orthonormal basis of S. +The projector onto a submodule S is the sum of the outer products of the vectors in an + orthonormal basis of S. -/ theorem projector_eq_sum_rankOne (b : OrthonormalBasis ι 𝕜 S) : (projector S).mat = ∑ i, Matrix.vecMulVec (S.subtype (b i)) (star (S.subtype (b i))) := by unfold projector; ext i j; field_simp; - simp [Matrix.vecMulVec] - -- By definition of orthogonal projection, we can write the projection of $e_j$ onto $S$ as $\sum_{k} \langle e_j, b_k \rangle b_k$. - have h_proj : ∀ j : n, S.orthogonalProjectionOnto (EuclideanSpace.single j 1) = ∑ k, (star (b k |>.1 j)) • (b k |>.1) := by + simp? [Matrix.vecMulVec] + -- By definition of orthogonal projection, we can write the projection of $e_j$ onto $S$ as + -- $\sum_{k} \langle e_j, b_k \rangle b_k$. + have h_proj : ∀ j : n, S.orthogonalProjectionOnto (EuclideanSpace.single j 1) = ∑ k, (star (b + k |>.1 j)) • (b k |>.1) := by intro j - have h_proj : S.orthogonalProjectionOnto (EuclideanSpace.single j 1) = ∑ k, (inner 𝕜 (b k |>.1) (EuclideanSpace.single j 1)) • (b k |>.1) := by + have h_proj : S.orthogonalProjectionOnto (EuclideanSpace.single j 1) = ∑ k, (inner 𝕜 (b k + |>.1) (EuclideanSpace.single j 1)) • (b k |>.1) := by convert b.sum_repr ( S.orthogonalProjectionOnto ( EuclideanSpace.single j 1 ) ) using 1; constructor <;> intro h <;> simp_all [ Subtype.ext_iff, b.repr_apply_apply ]; convert! h_proj using 3 @@ -146,58 +156,70 @@ theorem projector_eq_sum_rankOne (b : OrthonormalBasis ι 𝕜 S) : simp [ Matrix.sum_apply, mul_comm ] /-- -The projector onto the support of A is the sum of the projections onto the eigenvectors with non-zero eigenvalues. +The projector onto the support of A is the sum of the projections onto the eigenvectors with + non-zero eigenvalues. -/ lemma projector_support_eq_sum : A.supportProj.mat = ∑ i, (if A.H.eigenvalues i = 0 then 0 else 1) • Matrix.vecMulVec (A.H.eigenvectorBasis i) (star (A.H.eigenvectorBasis i)) := by - have h_support : A.support = Submodule.span (𝕜) (Set.image (fun i => A.H.eigenvectorBasis i) { i | A.H.eigenvalues i ≠ 0 }) := by - refine' le_antisymm _ _; + have h_support : A.support = Submodule.span (𝕜) (Set.image (fun i => A.H.eigenvectorBasis i) { + i | A.H.eigenvalues i ≠ 0 }) := by + refine le_antisymm ?_ ?_; · intro x hx; - -- By definition of $A.support$, we know that $x$ is in the orthogonal complement of the kernel of $A$. + -- By definition of $A.support$, we know that $x$ is in the orthogonal complement of + -- the kernel of $A$. have h_orthogonal_complement : x ∈ (A.ker : Submodule (𝕜) (EuclideanSpace (𝕜) n))ᗮ := by convert hx using 1; exact ker_orthogonal_eq_support A; - -- By definition of $A.ker$, we know that $x$ is orthogonal to all eigenvectors with zero eigenvalues. - have h_orthogonal_zero_eigenvalues : ∀ i, A.H.eigenvalues i = 0 → inner (𝕜) (A.H.eigenvectorBasis i) x = 0 := by + -- By definition of $A.ker$, we know that $x$ is orthogonal to all eigenvectors with + -- zero eigenvalues. + have h_orthogonal_zero_eigenvalues : ∀ i, A.H.eigenvalues i = 0 → inner (𝕜) + (A.H.eigenvectorBasis i) x = 0 := by intro i hi have h_eigenvector_zero : A.mat.mulVec (A.H.eigenvectorBasis i) = 0 := by have := A.H.mulVec_eigenvectorBasis i; aesop; convert h_orthogonal_complement ( A.H.eigenvectorBasis i ) _ using 1; exact (mem_ker_iff_mulVec_zero A ((H A).eigenvectorBasis i)).mpr h_eigenvector_zero; - -- By definition of $A.ker$, we know that $x$ can be written as a linear combination of eigenvectors with non-zero eigenvalues. + -- By definition of $A.ker$, we know that $x$ can be written as a linear combination + -- of eigenvectors with non-zero eigenvalues. have h_decomp : x = ∑ i, (inner (𝕜) (A.H.eigenvectorBasis i) x) • A.H.eigenvectorBasis i := by exact Eq.symm (OrthonormalBasis.sum_repr' (H A).eigenvectorBasis x); rw [ h_decomp ]; - exact Submodule.sum_mem _ fun i _ => if hi : A.H.eigenvalues i = 0 then by simp [h_orthogonal_zero_eigenvalues i hi ] else Submodule.smul_mem _ _ ( Submodule.subset_span ⟨ i, hi, rfl ⟩ ); + exact Submodule.sum_mem _ fun i _ => if hi : A.H.eigenvalues i = 0 then by simp + [h_orthogonal_zero_eigenvalues i hi ] else Submodule.smul_mem _ _ ( + Submodule.subset_span ⟨ i, hi, rfl ⟩ ); · rw [ Submodule.span_le, Set.image_subset_iff ]; intro i hi; - simp_all [ HermitianMat.support ]; + simp_all? [ HermitianMat.support ]; use (1 / A.H.eigenvalues i) • A.H.eigenvectorBasis i; - convert congr_arg ( fun x => ( 1 / A.H.eigenvalues i ) • x ) ( A.H.mulVec_eigenvectorBasis i ) using 1 - simp [hi] - simp [ funext_iff, Matrix.mulVec, dotProduct ]; + convert congr_arg ( fun x => ( 1 / A.H.eigenvalues i ) • x ) ( A.H.mulVec_eigenvectorBasis + i ) using 1 + simp? [hi] + simp? [ funext_iff, Matrix.mulVec, dotProduct ]; exact PiLp.ext_iff; - have h_orthonormal_basis : ∃ b : OrthonormalBasis {i : n | A.H.eigenvalues i ≠ 0} (𝕜) (Submodule.span (𝕜) (Set.image (fun i => A.H.eigenvectorBasis i) {i | A.H.eigenvalues i ≠ 0})), ∀ i, b i = A.H.eigenvectorBasis i := by - refine' ⟨ _, _ ⟩; - refine' OrthonormalBasis.mk _ _; - use fun i => ⟨ A.H.eigenvectorBasis i, Submodule.subset_span ( Set.mem_image_of_mem _ i.2 ) ⟩; - all_goals simp [ Orthonormal ]; - · intro i j hij; have := A.H.eigenvectorBasis.orthonormal; simp_all [ orthonormal_iff_ite ] ; + have h_orthonormal_basis : ∃ b : OrthonormalBasis {i : n | A.H.eigenvalues i ≠ 0} (𝕜) + (Submodule.span (𝕜) (Set.image (fun i => A.H.eigenvectorBasis i) {i | A.H.eigenvalues i ≠ + 0})), ∀ i, b i = A.H.eigenvectorBasis i := by + refine ⟨OrthonormalBasis.mk (v := fun i => + ⟨A.H.eigenvectorBasis i, Submodule.subset_span (Set.mem_image_of_mem _ i.2)⟩) ?_ ?_, ?_⟩ + all_goals simp? [Orthonormal] + · intro i j hij; have := A.H.eigenvectorBasis.orthonormal; simp_all? [ orthonormal_iff_ite ]; exact fun h => hij <| Subtype.ext h; · rw [ Submodule.eq_top_iff' ]; rintro ⟨ x, hx ⟩; rw [ Submodule.mem_span ] at hx ⊢; - intro p hp; specialize hx ( Submodule.map ( Submodule.subtype _ ) p ) ; simp_all [ Set.range_subset_iff ] ; + intro p hp; specialize hx ( Submodule.map ( Submodule.subtype _ ) p ); simp_all? [ + Set.range_subset_iff ]; exact hx fun i hi => ⟨ _, hp i hi, rfl ⟩; obtain ⟨ b, hb ⟩ := h_orthonormal_basis have h_sum_rankOne : (projector A.support).mat = ∑ i, Matrix.vecMulVec (b i) (star (b i)) := by convert! projector_eq_sum_rankOne _ b using 1 simp [h_support] at * - simp_all [ Finset.sum_ite ]; + simp_all? [ Finset.sum_ite ]; convert h_sum_rankOne using 1; · exact h_support ▸ rfl; - · refine' Finset.sum_bij ( fun i hi => ⟨ i, by simpa using hi ⟩ ) _ _ _ _ <;> simp [ Finset.mem_filter, hb ] + · refine Finset.sum_bij ( fun i hi => ⟨ i, by simpa using hi ⟩ ) ?_ ?_ ?_ ?_ <;> simp [ + Finset.mem_filter, hb ] /- `HermitianMat.supportProj` as a cfc. @@ -206,11 +228,11 @@ theorem supportProj_eq_cfc : A.supportProj = A.cfc (if · = 0 then 0 else 1) := apply HermitianMat.ext; rw [HermitianMat.cfc_toMat_eq_sum_smul_proj]; convert projector_support_eq_sum A using 1; - refine' Finset.sum_congr rfl fun i _ => _; + refine Finset.sum_congr rfl fun i _ => ?_; ext x y - simp [ Matrix.vecMulVec, Matrix.mul_apply ] ; - simp [ Matrix.single ]; - simp [ Finset.sum_ite, Finset.filter_eq, Finset.filter_and ]; + simp? [ Matrix.vecMulVec, Matrix.mul_apply ]; + simp? [ Matrix.single ]; + simp? [ Finset.sum_ite, Finset.filter_eq, Finset.filter_and ]; rw [ Finset.sum_eq_single i ] <;> aesop /-- Projector onto the non-negative eigenspace of `B - A`. Accessible by the notation @@ -228,10 +250,14 @@ noncomputable def projLT (A B : HermitianMat n 𝕜) : HermitianMat n 𝕜 := -- as the default ordering. We offer the `≥ₚ` notation which is the same with the arguments -- flipped, similar to how `GT.gt` is defeq to `LT.lt` with arguments flipped. -- We put the ≥ₚ first, since both can delaborate and we want to show the ≤ₚ one. +/-- The spectral projection onto the nonnegative spectral subspace of `A - B`. -/ scoped notation "{" A " ≥ₚ " B "}" => projLE B A +/-- The spectral projection onto the nonnegative spectral subspace of `B - A`. -/ scoped notation "{" A " ≤ₚ " B "}" => projLE A B +/-- The spectral projection onto the positive spectral subspace of `A - B`. -/ scoped notation "{" A " >ₚ " B "}" => projLT B A +/-- The spectral projection onto the positive spectral subspace of `B - A`. -/ scoped notation "{" A " <ₚ " B "}" => projLT A B theorem projLE_def : {A ≤ₚ B} = (B - A).cfc (fun x ↦ if 0 ≤ x then 1 else 0) := by @@ -397,7 +423,7 @@ theorem negPart_nonneg : 0 ≤ A⁻ := by theorem posPart_le : A ≤ A⁺ := by nth_rw 1 [← cfc_id A] rw [posPart_eq_cfc_ite, ← sub_nonneg, ← cfc_sub, cfc_nonneg_iff] - intro; simp; split <;> order + intro; simp?; split <;> order theorem posPart_mul_negPart : A⁺.mat * A⁻.mat = 0 := by rw [posPart_eq_cfc_ite, negPart_eq_cfc_ite, ← mat_cfc_mul] @@ -407,7 +433,7 @@ theorem posPart_mul_negPart : A⁺.mat * A⁻.mat = 0 := by open RealInnerProductSpace -theorem projLE_inner_nonneg : 0 ≤ ⟪{A ≤ₚ B}, (B - A)⟫ := +theorem projLE_inner_nonneg : 0 ≤ ⟪{A ≤ₚ B}, (B - A)⟫ := --This inner is equal to `(B - A)⁺.trace`, could be better way to describe it inner_mul_nonneg (projLE_mul_nonneg A B) @@ -529,13 +555,18 @@ theorem posPart_eq_zero_iff : A⁺ = 0 ↔ A ≤ 0 := by theorem inner_negPart_neg_iff : ⟪A, A⁻⟫ < 0 ↔ ¬0 ≤ A := by simp [← inner_negPart_zero_iff, lt_iff_le_and_ne, inner_negPart_nonpos A] +omit [DecidableEq n] in /-- The self-duality of the PSD cone: a matrix is PSD iff its inner product with all nonnegative matrices is non-negative. -/ theorem nonneg_iff_inner_nonneg (A : HermitianMat n 𝕜) : 0 ≤ A ↔ ∀ B, 0 ≤ B → 0 ≤ ⟪A, B⟫ := by + classical use fun h _ ↦ inner_ge_zero h intro h contrapose! h classical use A⁻, negPart_nonneg A rwa [inner_negPart_neg_iff] + + +end HermitianMat diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean index c91c844e87..32ac3034fd 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Reindex.lean @@ -24,6 +24,7 @@ variable {d d₂ d₃ d₄ 𝕜 : Type*} [RCLike 𝕜] variable (A B : HermitianMat d 𝕜) (e : d ≃ d₂) +/-- Reindex both rows and columns of a Hermitian matrix along an equivalence. -/ def reindex (e : d ≃ d₂) : HermitianMat d₂ 𝕜 := ⟨A.mat.reindex e e, A.H.submatrix e.symm⟩ @@ -61,7 +62,7 @@ theorem reindex_add : A.reindex e + B.reindex e = (A + B).reindex e := by ext1; simp [Matrix.submatrix_add] @[simp] -theorem reindex_sub : A.reindex e - B.reindex e = (A - B).reindex e := by +theorem reindex_sub : A.reindex e - B.reindex e = (A - B).reindex e := by ext1; simp [Matrix.submatrix_sub] @[simp] @@ -100,7 +101,7 @@ theorem reindex_eq_conj [DecidableEq d] (e : d ≃ d₂) : change A.mat (e.symm i) (e.symm j) = ∑ k, (∑ l, (if e.symm i = l then 1 else 0) * A.mat l k) * star (if e.symm j = k then (1 : 𝕜) else 0) - simp [ite_mul, mul_ite, apply_ite] + simp [ite_mul, apply_ite] variable [Fintype d₂] [DecidableEq d] [DecidableEq d₂] diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean index 9423d4dcc9..18274db177 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat/Trace.lean @@ -10,7 +10,8 @@ public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reind /-! # Trace of Hermitian Matrices While the trace of a Hermitian matrix is, in informal math, typically just "the same as" a trace of -a matrix that happens to be Hermitian - it is a real number, not a complex number. Or more generally, +a matrix that happens to be Hermitian - it is a real number, not a complex number. Or more + generally, it is a self-adjoint element of the base `StarAddMonoid`. Working directly with `Matrix.trace` then means that there would be constant casts between rings, @@ -29,16 +30,18 @@ namespace HermitianMat variable {R n m α : Type*} [Star R] [TrivialStar R] [Fintype n] [Fintype m] section star -variable [AddGroup α] [StarAddMonoid α] [CommSemiring R] [Semiring α] [Algebra R α] [IsMaximalSelfAdjoint R α] +variable [Ring α] [StarAddMonoid α] [CommSemiring R] [Algebra R α] [IsMaximalSelfAdjoint R α] /-- The trace of the matrix. This requires a `IsMaximalSelfAdjoint R α` instance, and then maps from - `HermitianMat n α` to `R`. This means that the trace of (say) a `HermitianMat n ℤ` gives values in ℤ, - but that the trace of a `HermitianMat n ℂ` gives values in ℝ. The fact that traces are "automatically" + `HermitianMat n α` to `R`. This means that the trace of (say) a `HermitianMat n ℤ` gives + values in ℤ, + but that the trace of a `HermitianMat n ℂ` gives values in ℝ. The fact that traces are + "automatically" real reduces coercions down the line. -/ def trace (A : HermitianMat n α) : R := IsMaximalSelfAdjoint.selfadjMap (A.mat.trace) -/-- `HermitianMat.trace` reduces to `Matrix.trace` in the algebra.-/ +/-- `HermitianMat.trace` reduces to `Matrix.trace` in the algebra. -/ theorem trace_eq_trace (A : HermitianMat n α) : algebraMap R α A.trace = Matrix.trace A.mat := by rw [trace, Matrix.trace, map_sum, map_sum] congr! 1 @@ -161,9 +164,11 @@ section addCommGroup variable [AddCommGroup α] [StarAddMonoid α] omit [Fintype n] +/-- The partial trace over the left factor of a Hermitian matrix indexed by a product. -/ def traceLeft (A : HermitianMat (m × n) α) : HermitianMat n α := ⟨A.mat.traceLeft, A.H.traceLeft⟩ +/-- The partial trace over the right factor of a Hermitian matrix indexed by a product. -/ def traceRight (A : HermitianMat (m × n) α) : HermitianMat m α := ⟨A.mat.traceRight, A.H.traceRight⟩ @@ -248,3 +253,6 @@ theorem traceRight_kron [Fintype n] : (A ⊗ₖ B).traceRight = B.trace • A := end kron end partialTrace + + +end HermitianMat diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean index eb02292f1b..610b00b312 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Isometry.lean @@ -9,6 +9,8 @@ public import Mathlib.Analysis.InnerProductSpace.JointEigenspace public import Mathlib.LinearAlgebra.Matrix.Permutation public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +/-! Simultaneous orthogonal diagonalization of commuting symmetric operators. -/ + @[expose] public section open scoped Matrix @@ -29,17 +31,26 @@ def Matrix.Isometry (A : Matrix d d₂ R) : Prop := Aᴴ * A = 1 omit [Fintype d₃] [DecidableEq d₂] in -theorem Matrix.submatrix_one_isometry {e : d₂ → d} {f : d₃ → d} (he : e.Bijective) (hf : f.Injective) : +omit [Fintype d] in +theorem Matrix.submatrix_one_isometry [Finite d] {e : d₂ → d} {f : d₃ → d} (he : e.Bijective) (hf : + f.Injective) : (submatrix (α := R) 1 e f).Isometry := by - -- Since $e$ is injective and $f$ is bijective, the submatrix of the identity matrix formed by $e$ and $f$ is a permutation matrix. - have h_perm : ∀ i j, (Matrix.submatrix (1 : Matrix d d R) e f) i j = if e i = f j then 1 else 0 := by - -- By definition of the identity matrix, the entry (i, j) in the submatrix is 1 if e i = f j and 0 otherwise. + classical + let := Fintype.ofFinite d + -- Since $e$ is injective and $f$ is bijective, the submatrix of the identity matrix formed + -- by $e$ and $f$ is a permutation matrix. + have h_perm : ∀ i j, (Matrix.submatrix (1 : Matrix d d R) e f) i j = if e i = f j then 1 else + 0 := by + -- By definition of the identity matrix, the entry (i, j) in the submatrix is 1 if e i = + -- f j and 0 otherwise. simp [Matrix.submatrix, Matrix.one_apply] ext i j - -- Since $e$ is injective and $f$ is bijective, the product $A * Aᴴ$ will have 1s on the diagonal and 0s elsewhere, which is the identity matrix. + -- Since $e$ is injective and $f$ is bijective, the product $A * Aᴴ$ will have 1s on the + -- diagonal and 0s elsewhere, which is the identity matrix. change ∑ k, (Matrix.conjTranspose (Matrix.submatrix (1 : Matrix d d R) e f)) i k * (Matrix.submatrix (1 : Matrix d d R) e f) k j = if i = j then 1 else 0 - simp_all only [Multiset.bijective_iff_map_univ_eq_univ, submatrix_apply, conjTranspose_apply, one_apply] + simp_all only [Multiset.bijective_iff_map_univ_eq_univ, submatrix_apply, conjTranspose_apply, + one_apply] symm; split <;> symm next h => subst h @@ -48,23 +59,32 @@ theorem Matrix.submatrix_one_isometry {e : d₂ → d} {f : d₃ → d} (he : e. have h_unique : ∀ i, ∃! x, e x = f i := by intro i obtain ⟨x, hx⟩ : ∃ x, e x = f i := by - replace he := congr_arg Multiset.toFinset he; rw [Finset.ext_iff] at he; specialize he ( f i ) ; aesop; + replace he := congr_arg Multiset.toFinset he; rw [Finset.ext_iff] at he; specialize he ( + f i ); aesop; use x simp_all only [true_and] intro y a have := Fintype.bijective_iff_injective_and_card e aesop obtain ⟨ x, hx ⟩ := h_unique i; - rw [show ( Finset.univ.filter fun y => e y = f i ) = { x } from Finset.eq_singleton_iff_unique_mem.2 ⟨ by aesop, fun y hy => hx.2 y <| Eq.symm <| Finset.mem_filter.1 hy |>.2.symm ⟩] ; simp ; - next h => -- Since $e$ is injective and $e i \neq e j$, there is no $x$ such that $e i = f x$ and $e j = f x$. + rw [show ( Finset.univ.filter fun y => e y = f i ) = { x } from + Finset.eq_singleton_iff_unique_mem.2 ⟨ by aesop, fun y hy => hx.2 y <| Eq.symm <| + Finset.mem_filter.1 hy |>.2.symm ⟩]; simp; + next h => + -- Since $e$ is injective and $e i \neq e j$, there is no $x$ such that $e i = f x$ and $e j = + -- f x$. have h_no_x : ∀ x : d₂, ¬(e x = f i ∧ e x = f j) := by exact fun x hx => h ( hf ( hx.1.symm.trans hx.2 ) ); exact Finset.sum_eq_zero fun x hx => by specialize h_no_x x; aesop omit [DecidableEq d₂] in -theorem Matrix.submatrix_one_id_left_isometry {e : d₂ → d} (he : e.Bijective) : - (submatrix (1 : Matrix d d R) e id).Isometry := - submatrix_one_isometry he Function.injective_id +omit [Fintype d] in +theorem Matrix.submatrix_one_id_left_isometry [Finite d] {e : d₂ → d} (he : e.Bijective) : + (submatrix (1 : Matrix d d R) e id).Isometry := by + classical + let := Fintype.ofFinite d + exact + submatrix_one_isometry he Function.injective_id omit [Fintype d₂] in theorem Matrix.submatrix_one_id_right_isometry {e : d₂ → d} (he : e.Injective) : @@ -78,34 +98,41 @@ theorem Matrix.mem_unitaryGroup_iff_isometry (A : Matrix d d R) : theorem Equiv.Perm.permMatrix_mem_unitaryGroup (e : Perm d) : e.permMatrix R ∈ Matrix.unitaryGroup d R := by - -- Since $e$ is a permutation, its permutation matrix $P_e$ is orthogonal, meaning $P_e * P_e^T = I$. + -- Since $e$ is a permutation, its permutation matrix $P_e$ is orthogonal, meaning $P_e * + -- P_e^T = I$. have h_perm_ortho : (Equiv.Perm.permMatrix R e) * (Equiv.Perm.permMatrix R e)ᵀ = 1 := by - ext i j; rw [Matrix.mul_apply] ; aesop; + ext i j; rw [Matrix.mul_apply]; aesop; constructor · simp_all only [Matrix.transpose_permMatrix] - -- Since the conjugate transpose of a permutation matrix is the permutation matrix of the inverse permutation, we have: + -- Since the conjugate transpose of a permutation matrix is the permutation matrix of + -- the inverse permutation, we have: have h_conj_transpose : star (Equiv.Perm.permMatrix R e) = (Equiv.Perm.permMatrix R e)ᵀ := by - ext i j; simp [Equiv.Perm.permMatrix] ; aesop; + ext i j; simp [Equiv.Perm.permMatrix]; aesop; simp_all [mul_eq_one_comm] · simp_all only [Matrix.transpose_permMatrix] convert! h_perm_ortho using 2; simp [Matrix.star_eq_conjTranspose, Equiv.Perm.permMatrix] omit [Fintype d₃] [DecidableEq d₂] in -theorem Matrix.reindex_one_isometry (e : d ≃ d₂) (f : d ≃ d₃) : +omit [Fintype d] in +theorem Matrix.reindex_one_isometry [Finite d] (e : d ≃ d₂) (f : d ≃ d₃) : (reindex (α := R) e f 1).Isometry := by - -- Since $e$ and $f$ are bijections, the reindexing of the identity matrix by $e$ and $f$ is a permutation matrix, which is unitary. + classical + let := Fintype.ofFinite d + -- Since $e$ and $f$ are bijections, the reindexing of the identity matrix by $e$ and $f$ is + -- a permutation matrix, which is unitary. have h_perm : ∀ (e : d ≃ d₂) (f : d ≃ d₃), (Matrix.reindex e f (1 : Matrix d d R)).Isometry := by intro e f simp [Matrix.Isometry] exact h_perm e f omit [Fintype d] in -theorem Matrix.reindex_one_mem_unitaryGroup (e : d ≃ d₂) : +theorem Matrix.reindex_one_mem_unitaryGroup (e : d ≃ d₂) : reindex (α := R) e e 1 ∈ unitaryGroup d₂ R := by -- The reindex of the identity matrix under an equivalence e is just the identity matrix on d₂. have h_reindex_id : Matrix.reindex e e (1 : Matrix d d R) = 1 := by - -- By definition of reindex, the entry at (i, j) in the reindexed matrix is 1 if i = j and 0 otherwise. + -- By definition of reindex, the entry at (i, j) in the reindexed matrix is 1 if i = j + -- and 0 otherwise. ext i j simp [Matrix.reindex, Matrix.one_apply] simp only [h_reindex_id, one_mem] @@ -118,21 +145,31 @@ theorem Matrix.reindex_eq_conj (A : Matrix d d R) (e : d ≃ d₂) : reindex e e simp [Matrix.one_apply] theorem Matrix.reindex_eq_conj_unitaryGroup' (A : Matrix d d R) (e : Equiv.Perm d) : reindex e e A = - (⟨_, e⁻¹.permMatrix_mem_unitaryGroup⟩ : unitaryGroup d R) * A * (⟨_, e.permMatrix_mem_unitaryGroup⟩ : unitaryGroup d R) := by + (⟨_, e⁻¹.permMatrix_mem_unitaryGroup⟩ : unitaryGroup d R) * A * (⟨_, + e.permMatrix_mem_unitaryGroup⟩ : unitaryGroup d R) := by ext i j; - simp [Matrix.mul_apply] + simp? [Matrix.mul_apply] rw [Finset.sum_eq_single ( e.symm j )] <;> aesop +omit [DecidableEq d] in theorem Matrix.IsHermitian.eigenvalue_ext (hA : A.IsHermitian) (h : ∀ (v : d → 𝕜) (lam : 𝕜), A *ᵥ v = lam • v → B *ᵥ v = lam • v) : A = B := by - -- Since A is Hermitian, it is diagonalizable, and its eigenvectors form a complete basis. Therefore, for any vector v, we have Av = Bv. + classical + -- Since A is Hermitian, it is diagonalizable, and its eigenvectors form a complete basis. + -- Therefore, for any vector v, we have Av = Bv. have h_diag : ∀ v : d → 𝕜, (A *ᵥ v) = (B *ᵥ v) := by - -- Since A is Hermitian, it is diagonalizable, and its eigenvectors form a complete basis. Therefore, for any vector v, we can express it as a linear combination of eigenvectors. - have h_diag : ∀ v : d → 𝕜, ∃ (c : d → 𝕜) (lam : d → 𝕜), v = ∑ i, c i • (Matrix.IsHermitian.eigenvectorBasis hA i) ∧ ∀ i, A *ᵥ (Matrix.IsHermitian.eigenvectorBasis hA i) = lam i • (Matrix.IsHermitian.eigenvectorBasis hA i) := by + -- Since A is Hermitian, it is diagonalizable, and its eigenvectors form a complete + -- basis. Therefore, for any vector v, we can express it as a linear combination of + -- eigenvectors. + have h_diag : ∀ v : d → 𝕜, ∃ (c : d → 𝕜) (lam : d → 𝕜), v = ∑ i, c i • + (Matrix.IsHermitian.eigenvectorBasis hA i) ∧ ∀ i, A *ᵥ + (Matrix.IsHermitian.eigenvectorBasis hA i) = lam i • + (Matrix.IsHermitian.eigenvectorBasis hA i) := by intro v obtain ⟨c, hc⟩ : ∃ c : d → 𝕜, v = ∑ i, c i • (hA.eigenvectorBasis i) := by - have h_diag : ∀ v : EuclideanSpace 𝕜 d, ∃ c : d → 𝕜, v = ∑ i, c i • (hA.eigenvectorBasis i) := by + have h_diag : ∀ v : EuclideanSpace 𝕜 d, ∃ c : d → 𝕜, v = ∑ i, c i • (hA.eigenvectorBasis + i) := by intro v set c := fun i => innerₛₗ 𝕜 (hA.eigenvectorBasis i) v have hv : v = ∑ i, c i • (hA.eigenvectorBasis i) := by @@ -141,7 +178,7 @@ theorem Matrix.IsHermitian.eigenvalue_ext (hA : A.IsHermitian) obtain ⟨c, hc⟩ := h_diag (WithLp.toLp 2 v) use c exact congr(WithLp.ofLp $hc) - refine' ⟨ c, fun i => ( hA.eigenvalues i ), hc, fun i => _ ⟩; + refine ⟨ c, fun i => ( hA.eigenvalues i ), hc, fun i => ?_ ⟩; convert hA.mulVec_eigenvectorBasis i; ext simp only [smul_eq_mul, Pi.smul_apply] @@ -152,10 +189,12 @@ theorem Matrix.IsHermitian.eigenvalue_ext (hA : A.IsHermitian) obtain ⟨c, lam, hv, hlam⟩ := h_diag v have hA_v : A *ᵥ v = ∑ i, c i • lam i • (hA.eigenvectorBasis i) := by -- By linearity of matrix multiplication, we can distribute A over the sum. - have hA_v : A *ᵥ (∑ i, c i • (hA.eigenvectorBasis i)) = ∑ i, c i • A *ᵥ (hA.eigenvectorBasis i) := by - simp [funext_iff] - simp [Matrix.mulVec, dotProduct, Finset.mul_sum _ _ _] - exact fun _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ); + have hA_v : A *ᵥ (∑ i, c i • (hA.eigenvectorBasis i)) = ∑ i, c i • A *ᵥ + (hA.eigenvectorBasis i) := by + simp? [funext_iff] + simp? [Matrix.mulVec, dotProduct, Finset.mul_sum _ _ _] + exact fun _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr + rfl fun _ _ => by ring ); aesop have hB_v : B *ᵥ v = ∑ i, c i • lam i • (hA.eigenvectorBasis i) := by have hBv : B *ᵥ v = ∑ i, c i • (B *ᵥ (hA.eigenvectorBasis i)) := by @@ -163,9 +202,10 @@ theorem Matrix.IsHermitian.eigenvalue_ext (hA : A.IsHermitian) have hBv : B *ᵥ v = B *ᵥ (∑ i, c i • (hA.eigenvectorBasis i)) := by rw [hv] simp only [WithLp.ofLp_sum, WithLp.ofLp_smul] - simp [hBv, funext_iff] - simp [Matrix.mulVec, dotProduct, Finset.mul_sum _ _ _] - exact fun _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ); + simp? [hBv, funext_iff] + simp? [Matrix.mulVec, dotProduct, Finset.mul_sum _ _ _] + exact fun _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr + rfl fun _ _ => by ring ); refine hBv.trans ?_ conv => enter [1, 2, i]; rw [h _ _ ( hlam i )] simp only [WithLp.ofLp_sum, WithLp.ofLp_smul] @@ -174,8 +214,10 @@ theorem Matrix.IsHermitian.eigenvalue_ext (hA : A.IsHermitian) apply Matrix.ext; intro i j; exact (by simpa using congr_fun ( h_diag ( Pi.single j 1 ) ) i) -/-- Generalizes `Matrix.IsHermitian.cfc.eq_1`, which gives a definition for the matrix CFC in terms of -`Matrix.IsHermitian.eigenvalues` and `Matrix.IsHermitian.eigenvectorUnitary`, to show that the CFC works +/-- Generalizes `Matrix.IsHermitian.cfc.eq_1`, which gives a definition for the matrix CFC in +terms of +`Matrix.IsHermitian.eigenvalues` and `Matrix.IsHermitian.eigenvectorUnitary`, to show that the + CFC works similarly for _any_ diagonalization by a two-sided isometry. -/ theorem Matrix.IsHermitian.cfc_eq_any_isometry {n m 𝕜 : Type*} [RCLike 𝕜] @@ -193,25 +235,32 @@ theorem Matrix.IsHermitian.cfc_eq_any_isometry {n m 𝕜 : Type*} [RCLike 𝕜] simp only [Unitary.conjStarAlgAut_apply] at hUV ⊢ clear hV hD subst A; clear hA - have h_diag_eq : diagonal (RCLike.ofReal ∘ D) * (Uᴴ * V) = (Uᴴ * V) * diagonal (RCLike.ofReal ∘ D2) := by - have h_mul : (Uᴴ * U * diagonal (RCLike.ofReal ∘ D) * Uᴴ : Matrix m n 𝕜) * V = Uᴴ * V * (diagonal (RCLike.ofReal ∘ D2) * star V * V) := by + have h_diag_eq : diagonal (RCLike.ofReal ∘ D) * (Uᴴ * V) = (Uᴴ * V) * diagonal (RCLike.ofReal + ∘ D2) := by + have h_mul : (Uᴴ * U * diagonal (RCLike.ofReal ∘ D) * Uᴴ : Matrix m n 𝕜) * V = Uᴴ * V * + (diagonal (RCLike.ofReal ∘ D2) * star V * V) := by simp only [Matrix.mul_assoc, hUV] simp_all [ Matrix.mul_assoc ]; - have h_diag_eq_f : diagonal (RCLike.ofReal ∘ f ∘ D) * (Uᴴ * V) = (Uᴴ * V) * diagonal (RCLike.ofReal ∘ f ∘ D2) := by + have h_diag_eq_f : diagonal (RCLike.ofReal ∘ f ∘ D) * (Uᴴ * V) = (Uᴴ * V) * diagonal + (RCLike.ofReal ∘ f ∘ D2) := by ext i j simp_all only [diagonal_mul, Function.comp_apply, mul_diagonal] replace h_diag_eq := congr_fun ( congr_fun h_diag_eq i ) j - by_cases hi : D i = D2 j <;> simp_all [ mul_comm ] ; - have h_final : U * diagonal (RCLike.ofReal ∘ f ∘ D) * Uᴴ * V = V * diagonal (RCLike.ofReal ∘ f ∘ D2) := by - have h_final : U * diagonal (RCLike.ofReal ∘ f ∘ D) * (Uᴴ * V) = U * (Uᴴ * V) * diagonal (RCLike.ofReal ∘ f ∘ D2) := by + by_cases hi : D i = D2 j <;> simp_all [ mul_comm ]; + have h_final : U * diagonal (RCLike.ofReal ∘ f ∘ D) * Uᴴ * V = V * diagonal (RCLike.ofReal ∘ f + ∘ D2) := by + have h_final : U * diagonal (RCLike.ofReal ∘ f ∘ D) * (Uᴴ * V) = U * (Uᴴ * V) * diagonal + (RCLike.ofReal ∘ f ∘ D2) := by rw [ Matrix.mul_assoc, h_diag_eq_f, Matrix.mul_assoc ]; rw [ Matrix.mul_assoc, Matrix.mul_assoc ]; simp_all +decide [ ← Matrix.mul_assoc ]; rw [ ← h_final, Matrix.mul_assoc ]; rw [hV₂, mul_one ] -/-- Generalizes `Matrix.IsHermitian.cfc.eq_1`, which gives a definition for the matrix CFC in terms of -`Matrix.IsHermitian.eigenvalues` and `Matrix.IsHermitian.eigenvectorUnitary`, to show that the CFC works +/-- Generalizes `Matrix.IsHermitian.cfc.eq_1`, which gives a definition for the matrix CFC in +terms of +`Matrix.IsHermitian.eigenvalues` and `Matrix.IsHermitian.eigenvectorUnitary`, to show that the + CFC works similarly for _any_ diagonalization. -/ theorem Matrix.IsHermitian.cfc_eq_any_unitary {n 𝕜 : Type*} [RCLike 𝕜] [Fintype n] [DecidableEq n] @@ -242,10 +291,10 @@ private theorem Matrix.cfc_conj_isometry' (hA : A.IsHermitian) (f : ℝ → ℝ) simp [U', Matrix.mul_assoc] rfl rw [Matrix.IsHermitian.cfc_eq, this] - rw [hA.cfc_eq, Matrix.IsHermitian.cfc.eq_1] - simp only [Unitary.conjStarAlgAut_apply] - simp only [Matrix.mul_assoc, conjTranspose_mul, star_eq_conjTranspose, U', D] - exact isHermitian_mul_mul_conjTranspose _ hA + · rw [hA.cfc_eq, Matrix.IsHermitian.cfc.eq_1] + simp only [Unitary.conjStarAlgAut_apply] + simp only [Matrix.mul_assoc, conjTranspose_mul, star_eq_conjTranspose, U', D] + · exact isHermitian_mul_mul_conjTranspose _ hA theorem Matrix.cfc_conj_isometry (f : ℝ → ℝ) {u : Matrix d₂ d 𝕜} (hu₁ : u.Isometry) (hu₂ : uᴴ.Isometry) : @@ -276,7 +325,8 @@ theorem Matrix.cfc_conj_unitary' (f : ℝ → ℝ) (u : unitaryGroup d 𝕜) : theorem Matrix.cfc_reindex (f : ℝ → ℝ) (e : d ≃ d₂) : cfc f (reindex e e A) = reindex e e (cfc f A) := by rw [reindex_eq_conj, reindex_eq_conj] - convert Matrix.cfc_conj_isometry f (u := (Matrix.reindex e (Equiv.refl d) : Matrix d d 𝕜 → Matrix d₂ d 𝕜) 1) ?_ ?_ + convert Matrix.cfc_conj_isometry f (u := (Matrix.reindex e (Equiv.refl d) : Matrix d d 𝕜 → + Matrix d₂ d 𝕜) 1) ?_ ?_ · simp · simp · apply reindex_one_isometry @@ -300,7 +350,8 @@ open Module.End -- of projectors that all pairwise commute, and we want to simultaneously diagonalize all -- of them. -/-- Similar to `LinearMap.IsSymmetric.orthogonalFamily_eigenspace_inf_eigenspace`, but here the direct sum +/-- Similar to `LinearMap.IsSymmetric.orthogonalFamily_eigenspace_inf_eigenspace`, but here the +direct sum is indexed by only the pairs of eigenvalues, as opposed to all pairs of `𝕜` values, giving a finite decomposition. -/ theorem LinearMap.IsSymmetric.orthogonalFamily_eigenspace_inf_eigenspace' {𝕜 E : Type*} [RCLike 𝕜] @@ -329,8 +380,9 @@ theorem iSup_mono_bot {α : Type*} {ι ι' : Sort*} [CompleteLattice α] · refine iSup_mono' (fun i ↦ ?_) rcases h i with h | h <;> simp [h] +/-- The direct sum decomposition into joint eigenspaces of two commuting symmetric endomorphisms. -/ @[reducible] -noncomputable def Commute.isSymmetric_directSumDecomposition {𝕜 E : Type*} [RCLike 𝕜] +noncomputable def Commute.isSymmetricDirectSumDecomposition {𝕜 E : Type*} [RCLike 𝕜] [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] {A B : E →ₗ[𝕜] E} [FiniteDimensional 𝕜 E] (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : DirectSum.Decomposition fun (μ₁₂ : Eigenvalues A × Eigenvalues B) ↦ @@ -362,9 +414,11 @@ theorem LinearMap.IsSymmetric.directSum_isInternal_of_commute' {𝕜 E : Type*} have h := LinearMap.IsSymmetric.directSum_isInternal_of_commute hA hB hAB constructor · intro x y hxy - -- Since the subspaces are orthogonal, the only way their sum can be zero is if each component is zero. Hence, x - y = 0, which implies x = y. + -- Since the subspaces are orthogonal, the only way their sum can be zero is if each + -- component is zero. Hence, x - y = 0, which implies x = y. rw [← sub_eq_zero] - suffices h_diff_zero : ∀ (x : DirectSum (Eigenvalues A × Eigenvalues B) fun μ₁₂ ↦ ↥(eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2)), x.coeAddMonoidHom _ = 0 → x = 0 from + suffices h_diff_zero : ∀ (x : DirectSum (Eigenvalues A × Eigenvalues B) fun μ₁₂ ↦ + ↥(eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2)), x.coeAddMonoidHom _ = 0 → x = 0 from h_diff_zero (x - y) (by simp [hxy]) clear x y hxy; intro x hx; ext μ₁₂ @@ -375,14 +429,17 @@ theorem LinearMap.IsSymmetric.directSum_isInternal_of_commute' {𝕜 E : Type*} rw [← h_inner_zero] simp only [DirectSum.coeAddMonoidHom_eq_dfinsuppSum, ZeroMemClass.coe_zero, implies_true, DFinsupp.sum_eq_sum_fintype, DFinsupp.equivFunOnFintype_apply] - -- Since the decomposition is orthogonal, the inner product of x μ₁₂ with any other component is zero. Therefore, the sum simplifies to just the inner product of x μ₁₂ with itself. + -- Since the decomposition is orthogonal, the inner product of x μ₁₂ with any other + -- component is zero. Therefore, the sum simplifies to just the inner product of x μ₁₂ + -- with itself. rw [inner_sum, Finset.sum_eq_add_sum_sdiff_singleton _ _ (by simp)] rw [Finset.sdiff_singleton_eq_erase, left_eq_add] apply Finset.sum_eq_zero intro μ hμ exact orthogonalFamily_eigenspace_inf_eigenspace' hA hB (Finset.ne_of_mem_erase hμ).symm _ _ - · -- Since the decomposition is orthogonal, the direct sum of the intersections is isomorphic to their sum. Therefore, the isomorphism implies that the sum is equal to E. - have h_sum : ⨆ (μ₁₂ : Eigenvalues A × Eigenvalues B), eigenspace A μ₁₂.1 ⊓ eigenspace B μ₁₂.2 = ⊤ := by + -- Orthogonality identifies the direct sum of the intersections with their sum. + · have h_sum : ⨆ (μ₁₂ : Eigenvalues A × Eigenvalues B), eigenspace A μ₁₂.1 ⊓ eigenspace B + μ₁₂.2 = ⊤ := by rw [eq_top_iff] intro x hx obtain ⟨y, rfl⟩ := h.2 x @@ -405,8 +462,9 @@ theorem LinearMap.IsSymmetric.directSum_isInternal_of_commute' {𝕜 E : Type*} specialize h_sum x rw [Submodule.mem_iSup_iff_exists_finsupp] at h_sum rcases h_sum with ⟨f, hf₁, hf₂⟩ - exact ⟨∑ i ∈ f.support, .of _ i ⟨f i, hf₁ i⟩, by simp_all; exact hf₂⟩ + exact ⟨∑ i ∈ f.support, .of _ i ⟨f i, hf₁ i⟩, by simp_all?; exact hf₂⟩ +/-- An orthonormal basis of simultaneous eigenvectors for two commuting symmetric endomorphisms. -/ noncomputable def LinearMap.sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : OrthonormalBasis d 𝕜 (EuclideanSpace 𝕜 d) := @@ -414,25 +472,32 @@ noncomputable def LinearMap.sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ (hA.orthogonalFamily_eigenspace_inf_eigenspace' hB)).reindex (Fintype.equivOfCardEq (by simp)) +/-- The eigenvalues of the first endomorphism in the chosen simultaneous orthonormal eigenbasis. -/ noncomputable def LinearMap.sharedEigenvaluesA {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : d → ℝ := - fun i => RCLike.re (inner 𝕜 (LinearMap.sharedEigenbasis hA hB hAB i) (A (LinearMap.sharedEigenbasis hA hB hAB i))) + fun i => RCLike.re (inner 𝕜 (LinearMap.sharedEigenbasis hA hB hAB i) (A + (LinearMap.sharedEigenbasis hA hB hAB i))) +/-- The eigenvalues of the second endomorphism in the chosen simultaneous orthonormal eigenbasis. -/ noncomputable def LinearMap.sharedEigenvaluesB {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) : d → ℝ := - fun i => RCLike.re (inner 𝕜 (LinearMap.sharedEigenbasis hA hB hAB i) (B (LinearMap.sharedEigenbasis hA hB hAB i))) + fun i => RCLike.re (inner 𝕜 (LinearMap.sharedEigenbasis hA hB hAB i) (B + (LinearMap.sharedEigenbasis hA hB hAB i))) omit [DecidableEq d] in -theorem LinearMap.mem_eigenspace_inf_of_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} +theorem LinearMap.mem_eigenspace_inf_of_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] + EuclideanSpace 𝕜 d} (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) (i : d) : ∃ (μ : Module.End.Eigenvalues A) (ν : Module.End.Eigenvalues B), - LinearMap.sharedEigenbasis hA hB hAB i ∈ Module.End.eigenspace A μ ⊓ Module.End.eigenspace B ν := by + LinearMap.sharedEigenbasis hA hB hAB i ∈ Module.End.eigenspace A μ ⊓ Module.End.eigenspace + B ν := by rw [LinearMap.sharedEigenbasis] rw [OrthonormalBasis.reindex_apply] let hV := hA.directSum_isInternal_of_commute' hB hAB let hV' := hA.orthogonalFamily_eigenspace_inf_eigenspace' hB let hn : Module.finrank 𝕜 (EuclideanSpace 𝕜 d) = Module.finrank 𝕜 (EuclideanSpace 𝕜 d) := rfl - let e := Fintype.equivOfCardEq (show Fintype.card (Fin (Module.finrank 𝕜 (EuclideanSpace 𝕜 d))) = Fintype.card d by simp) + let e := Fintype.equivOfCardEq (show Fintype.card (Fin (Module.finrank 𝕜 (EuclideanSpace 𝕜 + d))) = Fintype.card d by simp) let j := e.symm i let idx := hV.subordinateOrthonormalBasisIndex hn j hV' exists idx.1, idx.2 @@ -441,7 +506,8 @@ theorem LinearMap.mem_eigenspace_inf_of_sharedEigenbasis {A B : EuclideanSpace omit [DecidableEq d] in theorem LinearMap.apply_A_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) (i : d) : - A (sharedEigenbasis hA hB hAB i) = (sharedEigenvaluesA hA hB hAB i : 𝕜) • (sharedEigenbasis hA hB hAB i) := by + A (sharedEigenbasis hA hB hAB i) = (sharedEigenvaluesA hA hB hAB i : 𝕜) • (sharedEigenbasis + hA hB hAB i) := by obtain ⟨μ, ν, h⟩ := mem_eigenspace_inf_of_sharedEigenbasis hA hB hAB i have h₂ := Module.End.mem_eigenspace_iff.mp h.1 rw [h₂] @@ -457,7 +523,8 @@ theorem LinearMap.apply_A_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[ omit [DecidableEq d] in theorem LinearMap.apply_B_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[𝕜] EuclideanSpace 𝕜 d} (hA : A.IsSymmetric) (hB : B.IsSymmetric) (hAB : Commute A B) (i : d) : - B (sharedEigenbasis hA hB hAB i) = (sharedEigenvaluesB hA hB hAB i : 𝕜) • (sharedEigenbasis hA hB hAB i) := by + B (sharedEigenbasis hA hB hAB i) = (sharedEigenvaluesB hA hB hAB i : 𝕜) • (sharedEigenbasis + hA hB hAB i) := by obtain ⟨μ, ν, h⟩ := mem_eigenspace_inf_of_sharedEigenbasis hA hB hAB i have h₂ := Module.End.mem_eigenspace_iff.mp h.2 rw [h₂] @@ -470,16 +537,19 @@ theorem LinearMap.apply_B_sharedEigenbasis {A B : EuclideanSpace 𝕜 d →ₗ[ simpa [inner_smul_left, inner_smul_right, h₂, h₃] using hB ((sharedEigenbasis hA hB hAB) i) ((sharedEigenbasis hA hB hAB) i) +/-- An orthonormal basis of simultaneous eigenvectors for two commuting Hermitian matrices. -/ noncomputable def Matrix.sharedEigenbasis (hA : A.IsHermitian) (hB : B.IsHermitian) (hAB : Commute A B) : OrthonormalBasis d 𝕜 (EuclideanSpace 𝕜 d) := LinearMap.sharedEigenbasis (isSymmetric_toEuclideanLin_iff.symm.mp hA) (isSymmetric_toEuclideanLin_iff.symm.mp hB) (commute_euclideanLin hAB) +/-- The unitary matrix whose columns are the chosen simultaneous eigenvectors. -/ noncomputable def Matrix.sharedEigenvectorUnitary (hA : A.IsHermitian) (hB : B.IsHermitian) (hAB : Commute A B) : Matrix.unitaryGroup d 𝕜 := ⟨(EuclideanSpace.basisFun d 𝕜).toBasis.toMatrix (sharedEigenbasis hA hB hAB).toBasis, - (EuclideanSpace.basisFun d 𝕜).toMatrix_orthonormalBasis_mem_unitary (sharedEigenbasis hA hB hAB)⟩ + (EuclideanSpace.basisFun d 𝕜).toMatrix_orthonormalBasis_mem_unitary (sharedEigenbasis hA hB + hAB)⟩ namespace Matrix.SharedEigenbasis @@ -491,12 +561,14 @@ theorem sharedEigenvectorUnitary_mulVec (j : d) : (sharedEigenvectorUnitary hA h simp_all only [mulVec_single, MulOpposite.op_one, one_smul] rfl +/-- The eigenvalue of the first matrix at an index of the shared eigenbasis. -/ noncomputable def sharedEigenvalueA (j : d) : ℝ := LinearMap.sharedEigenvaluesA (isSymmetric_toEuclideanLin_iff.symm.mp hA) (isSymmetric_toEuclideanLin_iff.symm.mp hB) (commute_euclideanLin hAB) j +/-- The eigenvalue of the second matrix at an index of the shared eigenbasis. -/ noncomputable def sharedEigenvalueB (j : d) : ℝ := LinearMap.sharedEigenvaluesB (isSymmetric_toEuclideanLin_iff.symm.mp hA) @@ -511,7 +583,7 @@ theorem mulVec_sharedEigenbasisA (j : d) : have h := LinearMap.apply_A_sharedEigenbasis hA hB (Matrix.commute_euclideanLin hAB) j simp only [algebraMap_smul] at h have := congr_arg WithLp.ofLp h - simp [Matrix.toEuclideanLin, Matrix.toLpLin_apply] at this + simp? [Matrix.toEuclideanLin, Matrix.toLpLin_apply] at this exact this theorem mulVec_sharedEigenbasisB (j : d) : @@ -521,12 +593,17 @@ theorem mulVec_sharedEigenbasisB (j : d) : have h := LinearMap.apply_B_sharedEigenbasis hA hB (Matrix.commute_euclideanLin hAB) j simp only [algebraMap_smul] at h have := congr_arg WithLp.ofLp h - simp [Matrix.toEuclideanLin, Matrix.toLpLin_apply] at this + simp? [Matrix.toEuclideanLin, Matrix.toLpLin_apply] at this exact this /- PROVIDED SOLUTION -This is exactly analogous to star_shared_mul_B_mul_IsDiag (which is proved below in this file), but for A instead of B. Use the same proof structure: rw isDiag_iff_diagonal_diag, apply toEuclideanLin.injective, ext with basis, simp, then use mulVec_sharedEigenbasisA (instead of mulVec_sharedEigenbasisB), sharedEigenvectorUnitary_mulVec, h_simp2 (orthogonality/unit property), and by_cases on index equality, simplifying with simp +decide. Reference the B version's proof approach for the exact tactic sequence. +This is exactly analogous to star_shared_mul_B_mul_IsDiag (which is proved below in this file), + but for A instead of B. Use the same proof structure: rw isDiag_iff_diagonal_diag, apply + toEuclideanLin.injective, ext with basis, simp, then use mulVec_sharedEigenbasisA (instead + of mulVec_sharedEigenbasisB), sharedEigenvectorUnitary_mulVec, h_simp2 (orthogonality/unit + property), and by_cases on index equality, simplifying with simp +decide. Reference the B + version's proof approach for the exact tactic sequence. -/ @@ -541,12 +618,12 @@ theorem star_shared_mul_A_mul_IsDiag : IsDiag mul_assoc, Pi.smul_apply, Algebra.smul_mul_assoc] at this simp only [mul_assoc] convert this using 1; - · simp [ Matrix.mul_apply, mul_assoc, mul_comm, Finset.sum_mul ] + · simp? [ Matrix.mul_apply, mul_assoc, mul_comm, Finset.sum_mul ] congr! 3; · have := ( sharedEigenbasis hA hB hAB ).orthonormal; rw [ orthonormal_iff_ite ] at this; simp only [inner, ← starRingEnd_apply] at this - rw [ ← Finset.smul_sum, this i j, if_neg hij, smul_zero ] + rw [ ← Finset.smul_sum, this i j, ite_eq_right hij, smul_zero ] /-- Analogous to `Matrix.IsHermitian.star_mul_self_mul_eq_diagonal` for the shared basis. -/ theorem star_shared_mul_B_mul_IsDiag : IsDiag @@ -561,35 +638,39 @@ theorem star_shared_mul_B_mul_IsDiag : IsDiag Matrix.diagonal_mulVec_single, mul_one] apply PiLp.ext intro j - - have h_simp : (Matrix.sharedEigenvectorUnitary hA hB hAB).val.conjTranspose.mulVec (B.mulVec (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i))) = - (sharedEigenvalueB hA hB hAB i) • (Matrix.sharedEigenvectorUnitary hA hB hAB).val.conjTranspose.mulVec (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i)) := by - convert congr_arg ( fun x => ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 ) ᴴ *ᵥ x ) ( mulVec_sharedEigenbasisB hA hB hAB i) using 1; + have h_simp : (Matrix.sharedEigenvectorUnitary hA hB hAB).val.conjTranspose.mulVec (B.mulVec + (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i))) = + (sharedEigenvalueB hA hB hAB i) • (Matrix.sharedEigenvectorUnitary hA hB + hAB).val.conjTranspose.mulVec (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i)) := by + convert congr_arg ( fun x => ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 ) + ᴴ *ᵥ x ) ( mulVec_sharedEigenbasisB hA hB hAB i) using 1; symm exact (mulVec_smul ((sharedEigenvectorUnitary hA hB hAB).val)ᴴ (sharedEigenvalueB hA hB hAB i) (WithLp.ofLp ((sharedEigenbasis hA hB hAB) i))) - have h_simp2 : (Matrix.sharedEigenvectorUnitary hA hB hAB).val.conjTranspose.mulVec (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i)) = Pi.single i 1 := by + have h_simp2 : (Matrix.sharedEigenvectorUnitary hA hB hAB).val.conjTranspose.mulVec + (WithLp.ofLp (Matrix.sharedEigenbasis hA hB hAB i)) = Pi.single i 1 := by rw [ ← sharedEigenvectorUnitary_mulVec hA hB hAB i ]; - simp + simp? ext j - have := mul_eq_one_comm.mp ( show ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 ) * ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 )ᴴ = 1 from ?_ ); + have := mul_eq_one_comm.mp ( show ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 + ) * ( Matrix.sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜 )ᴴ = 1 from ?_ ); · convert! congr_fun ( congr_fun this j ) i using 1; simp [ Pi.single_apply, Matrix.one_apply ]; · exact Matrix.mem_unitaryGroup_iff.mp ( Matrix.sharedEigenvectorUnitary hA hB hAB ).2; - simp_all [ Matrix.mulVec, funext_iff ]; - simp_all [ Matrix.mul_apply, dotProduct ]; + simp_all? [ Matrix.mulVec, funext_iff ]; + simp_all? [ Matrix.mul_apply, dotProduct ]; by_cases hij : i = j - · simp [ hij ]; - simp [Matrix.mulVec, dotProduct ]; + · simp? [ hij ]; + simp? [Matrix.mulVec, dotProduct ]; simp only [Finset.mul_sum, mul_left_comm]; rw [ Finset.sum_comm ] - simp [ mul_comm, mul_left_comm, Finset.mul_sum] + simp? [ mul_comm, mul_left_comm, Finset.mul_sum] congr! 3; - · simp [ hij ]; - simp_all [ mul_comm, Matrix.mulVec, dotProduct ]; - simp_all [ mul_comm, Finset.mul_sum]; + · simp? [ hij ]; + simp_all? [ mul_comm, Matrix.mulVec, dotProduct ]; + simp_all? [ mul_comm, Finset.mul_sum]; rw [ Finset.sum_comm ] - simp_all [ mul_assoc, mul_left_comm] ; + simp_all [ mul_assoc, mul_left_comm]; end Matrix.SharedEigenbasis @@ -609,7 +690,9 @@ variable (U : Matrix.unitaryGroup d 𝕜) instance instInvertibleUnitaryGroup (U : Matrix.unitaryGroup d 𝕜) : Invertible U := invertibleOfGroup U -instance (U : Matrix.unitaryGroup d 𝕜) : Invertible U.val := +/-- The inverse of a unitary matrix, regarded as an ambient matrix. -/ +instance EuclideanJordan.instInvertibleUnitaryMatrix + (U : Matrix.unitaryGroup d 𝕜) : Invertible U.val := ⟨star U.val, U.2.1, U.2.2⟩ /-- If a matrix is diagonalized by a unitary matrix, then it can be written as a diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/LinearEquiv.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/LinearEquiv.lean index 2626a07acf..52ad179036 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/LinearEquiv.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/LinearEquiv.lean @@ -53,7 +53,7 @@ function along an index equivalence `e : d ≃ d₂`. This is the linear-equival `Equiv.piCongrLeft`. -/ @[simps] def ofRelabel (e : d ≃ d₂) : (d₂ → R) ≃ₗ[R] (d → R) := by - refine' { e.symm.piCongrLeft (fun _ ↦ R) with .. } + refine { e.symm.piCongrLeft (fun _ ↦ R) with map_add' := ?_, map_smul' := ?_ } <;> (intros; ext; simp [Equiv.piCongrLeft_apply]) variable (e : d ≃ d₂) diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean index b520e1cac5..896f94e584 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Matrix.lean @@ -24,6 +24,8 @@ public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc -- physlib relies on relaxed auto-bound implicits for `dA`/`dB` in this file. +/-! Matrix order, partial traces, spectra, and block constructions. -/ + @[expose] public section noncomputable section @@ -39,15 +41,20 @@ open scoped ComplexOrder MatrixOrder omit [DecidableEq n] in /-- The block Gram matrix `[[YᴴY, YᴴX], [XᴴY, XᴴX]]` is positive semidefinite. -/ -theorem fromBlocks_gram_posSemidef {m n k : Type*} [Fintype m] [Fintype n] [Fintype k] +theorem fromBlocks_gram_posSemidef {m n k : Type*} [Finite m] [Finite n] [Fintype k] (X : Matrix k n ℂ) (Y : Matrix k m ℂ) : (fromBlocks (Yᴴ * Y) (Yᴴ * X) (Xᴴ * Y) (Xᴴ * X)).PosSemidef := by + classical + let := Fintype.ofFinite m + let := Fintype.ofFinite n convert posSemidef_conjTranspose_mul_self (fromBlocks Y X (0 : Matrix k m ℂ) (0 : Matrix k n ℂ)) using 1 rw [fromBlocks_conjTranspose, fromBlocks_multiply] simp +omit [DecidableEq n] in theorem zero_rank_eq_zero {A : Matrix n n 𝕜} [Fintype n] (hA : A.rank = 0) : A = 0 := by + classical have h : ∀ v, A.mulVecLin v = 0 := by intro v rw [rank, Module.finrank_zero_iff] at hA @@ -78,7 +85,8 @@ theorem smul_real (c : ℝ) : (c • A).IsHermitian := by ext simp only [smul_apply, smul_eq_mul, RCLike.real_smul_eq_coe_mul] -def HermitianSubspace (n 𝕜 : Type*) [Fintype n] [RCLike 𝕜] : Subspace ℝ (Matrix n n 𝕜) where +/-- The real vector subspace of Hermitian matrices. -/ +def HermitianSubspace (n 𝕜 : Type*) [RCLike 𝕜] : Subspace ℝ (Matrix n n 𝕜) where carrier := { A : Matrix n n 𝕜 | A.IsHermitian } add_mem' _ _ := by simp_all only [Set.mem_ofPred_eq, IsHermitian.add] zero_mem' := by simp only [Set.mem_ofPred_eq, isHermitian_zero] @@ -180,8 +188,11 @@ theorem Finsupp.sum_eq_ite omit dn in +omit [Fintype n] in open ComplexConjugate in -theorem outer_self_conj (v : n → 𝕜) : PosSemidef (vecMulVec v (conj v)) := by +theorem outer_self_conj [Finite n] (v : n → 𝕜) : PosSemidef (vecMulVec v (conj v)) := by + classical + let := Fintype.ofFinite n constructor · ext simp [vecMulVec_apply, mul_comm] @@ -208,13 +219,19 @@ theorem convex_cone {c₁ c₂ : 𝕜} (hc₁ : 0 ≤ c₁) (hc₂ : 0 ≤ c₂) variable [dm : DecidableEq m] -/-- A standard basis matrix (with a positive entry) is positive semidefinite iff the entry is on the diagonal. -/ -theorem stdBasisMatrix_iff_eq (i j : m) {c : 𝕜} (hc : 0 < c) : (single i j c).PosSemidef ↔ i = j := by +omit [Fintype m] in +/-- A standard basis matrix (with a positive entry) is positive semidefinite iff the entry is on +the diagonal. -/ +theorem stdBasisMatrix_iff_eq [Finite m] (i j : m) {c : 𝕜} (hc : 0 < c) : + (single i j c).PosSemidef ↔ i = j + := by + classical + let := Fintype.ofFinite m constructor · intro ⟨hherm, _⟩ rw [IsHermitian, ← ext_iff] at hherm replace hherm := hherm i j - simp only [single, conjTranspose_apply, of_apply, true_and, RCLike.star_def, if_true] at hherm + simp only [single, conjTranspose_apply, of_apply, true_and, RCLike.star_def, ite_true] at hherm apply_fun (starRingEnd 𝕜) at hherm have hcstar := RCLike.conj_eq_iff_im.mpr (RCLike.pos_iff.mp hc).right rw [starRingEnd_self_apply, hcstar, ite_eq_left_iff] at hherm @@ -240,13 +257,14 @@ theorem stdBasisMatrix_iff_eq (i j : m) {c : 𝕜} (hc : 0 < c) : (single i j c) simp only [single, of_apply] convert_to 0 ≤ (star (x i)) * c * (x i) · rw [←Fintype.sum_prod_type'] - have h₀ : ∀ x_1 : m × m, x_1 ≠ ⟨i, i⟩ → star (x x_1.1) * ((if i = x_1.1 ∧ i = x_1.2 then c else 0) * x x_1.2) = 0 := fun z hz => by + have h₀ : ∀ x_1 : m × m, x_1 ≠ ⟨i, i⟩ → star (x x_1.1) * ((if i = x_1.1 ∧ i = x_1.2 then + c else 0) * x x_1.2) = 0 := fun z hz => by have h₁ : ¬(i = z.1 ∧ i = z.2) := by rw [ne_eq, Prod.mk_inj] at hz by_contra hz' apply hz exact ⟨hz'.left.symm, hz'.right.symm⟩ - rw [ite_cond_eq_false _ _ (eq_false h₁)] + rw [ite_eq_right_of_eq_false _ _ (eq_false h₁)] ring rw [Fintype.sum_eq_single ⟨i, i⟩] · simp [mul_assoc] @@ -262,7 +280,11 @@ variable {A : Matrix m m 𝕜} {B : Matrix n n 𝕜} variable (hA : A.PosSemidef) (hB : B.PosSemidef) include hA hB in -theorem PosSemidef_kronecker : (A ⊗ₖ B).PosSemidef := by +omit [DecidableEq n] [Fintype m] [Fintype n] in +theorem PosSemidef_kronecker [Finite m] [Finite n] : (A ⊗ₖ B).PosSemidef := by + classical + let := Fintype.ofFinite m + let := Fintype.ofFinite n open Classical in rw [hA.left.spectral_theorem, hB.left.spectral_theorem] simp only [Unitary.conjStarAlgAut_apply] @@ -304,7 +326,10 @@ theorem pos_smul {c : 𝕜} (hA : (c • A).PosSemidef) (hc : 0 < c) : A.PosSemi convert hA.smul (a := 1/c) this.le rw [smul_smul, one_div, inv_mul_cancel₀ hc.ne', one_smul] -theorem zero_posSemidef_neg_posSemidef_iff : A.PosSemidef ∧ (-A).PosSemidef ↔ A = 0 := by +omit [Fintype m] in +theorem zero_posSemidef_neg_posSemidef_iff [Finite m] : A.PosSemidef ∧ (-A).PosSemidef ↔ A = 0 := by + classical + let := Fintype.ofFinite m constructor · intro ⟨hA, hNegA⟩ have h0 : ∀ x : m → 𝕜, 0 = star x ⬝ᵥ A.mulVec x := fun x ↦ by @@ -374,21 +399,31 @@ theorem le_of_nonneg_imp' {R : Type*} [AddCommGroup R] [PartialOrder R] [IsOrder exact h (y - x) hxy omit [DecidableEq m] in -theorem mul_mul_conjTranspose_mono (C : Matrix m n 𝕜) : - A ≤ B → C * A * C.conjTranspose ≤ C * B * C.conjTranspose := fun hAB ↦ by - rw [Matrix.le_iff] at hAB ⊢ - have hDistrib : C * B * Cᴴ - C * A * Cᴴ = C * (B - A) * Cᴴ := by - ext i j - simp only [sub_apply, mul_apply, conjTranspose_apply, RCLike.star_def, Finset.sum_mul, - ←Finset.sum_sub_distrib, mul_sub_left_distrib, mul_sub_right_distrib] - rw [hDistrib] - exact mul_mul_conjTranspose_same hAB C +omit [Fintype m] in +theorem mul_mul_conjTranspose_mono [Finite m] (C : Matrix m n 𝕜) : + A ≤ B → C * A * C.conjTranspose ≤ C * B * C.conjTranspose := by + classical + let := Fintype.ofFinite m + exact + fun hAB ↦ by + rw [Matrix.le_iff] at hAB ⊢ + have hDistrib : C * B * Cᴴ - C * A * Cᴴ = C * (B - A) * Cᴴ := by + ext i j + simp only [sub_apply, mul_apply, conjTranspose_apply, RCLike.star_def, Finset.sum_mul, + ←Finset.sum_sub_distrib, mul_sub_left_distrib, mul_sub_right_distrib] + rw [hDistrib] + exact mul_mul_conjTranspose_same hAB C omit [DecidableEq m] in -theorem conjTranspose_mul_mul_mono (C : Matrix n m 𝕜) : - A ≤ B → C.conjTranspose * A * C ≤ C.conjTranspose * B * C := fun hAB ↦ by - convert mul_mul_conjTranspose_mono Cᴴ hAB - <;> rw [conjTranspose_conjTranspose] +omit [Fintype m] in +theorem conjTranspose_mul_mul_mono [Finite m] (C : Matrix n m 𝕜) : + A ≤ B → C.conjTranspose * A * C ≤ C.conjTranspose * B * C := by + classical + let := Fintype.ofFinite m + exact + fun hAB ↦ by + convert mul_mul_conjTranspose_mono Cᴴ hAB + <;> rw [conjTranspose_conjTranspose] theorem nonneg_iff_eigenvalue_nonneg [DecidableEq n] : 0 ≤ A ↔ ∀ x, 0 ≤ hA.eigenvalues x := Iff.trans Matrix.nonneg_iff_posSemidef hA.posSemidef_iff_eigenvalues_nonneg @@ -526,7 +561,8 @@ end PosSemidef -- rw [Pi.zero_apply, Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h -- replace h := congrFun h i -- dsimp at h --- rw [add_eq_zero_iff_of_nonneg (sq_nonneg _) (sq_nonneg _), sq_eq_zero_iff, sq_eq_zero_iff] at h +-- rw [add_eq_zero_iff_of_nonneg (sq_nonneg _) (sq_nonneg _), sq_eq_zero_iff, sq_eq_zero_iff] at +-- h -- apply RCLike.ext (h.left.trans RCLike.zero_re.symm) (h.right.trans (map_zero _).symm) -- } @@ -571,7 +607,8 @@ end PosSemidef -- rw [Pi.zero_apply, Fintype.sum_eq_zero_iff_of_nonneg (fun i ↦ by positivity)] at h -- replace h := congrFun h i -- dsimp at h --- rw [add_eq_zero_iff_of_nonneg (sq_nonneg _) (sq_nonneg _), sq_eq_zero_iff, sq_eq_zero_iff] at h +-- rw [add_eq_zero_iff_of_nonneg (sq_nonneg _) (sq_nonneg _), sq_eq_zero_iff, sq_eq_zero_iff] at +-- h -- apply RCLike.ext (h.left.trans RCLike.zero_re.symm) (h.right.trans (map_zero _).symm) -- } @@ -599,9 +636,11 @@ section partial_trace variable [AddCommMonoid R] [Fintype d] +/-- The partial trace obtained by summing diagonal entries in the left index factor. -/ def traceLeft (m : Matrix (d × d₁) (d × d₂) R) : Matrix d₁ d₂ R := Matrix.of fun i₁ j₁ ↦ ∑ i₂, m (i₂, i₁) (i₂, j₁) +/-- The partial trace obtained by summing diagonal entries in the right index factor. -/ def traceRight (m : Matrix (d₁ × d) (d₂ × d) R) : Matrix d₁ d₂ R := Matrix.of fun i₂ j₂ ↦ ∑ i₁, m (i₂, i₁) (j₂, i₁) @@ -618,14 +657,16 @@ theorem traceRight_trace (A : Matrix (d₁ × d₂) (d₁ × d₂) R) : A.traceR rfl variable [StarAddMonoid R] in -theorem IsHermitian.traceLeft {A : Matrix (d × d₁) (d × d₁) R} (hA : A.IsHermitian) : A.traceLeft.IsHermitian := by +theorem IsHermitian.traceLeft {A : Matrix (d × d₁) (d × d₁) R} (hA : A.IsHermitian) : + A.traceLeft.IsHermitian := by ext simp only [Matrix.traceLeft, conjTranspose_apply, of_apply, star_sum] congr! exact congrFun₂ hA _ _ variable [StarAddMonoid R] in -theorem IsHermitian.traceRight {A : Matrix (d₁ × d) (d₁ × d) R} (hA : A.IsHermitian) : A.traceRight.IsHermitian := by +theorem IsHermitian.traceRight {A : Matrix (d₁ × d) (d₁ × d) R} (hA : A.IsHermitian) : + A.traceRight.IsHermitian := by ext simp only [Matrix.traceRight, conjTranspose_apply, of_apply, star_sum] congr! @@ -639,7 +680,7 @@ open scoped Kronecker in theorem trace_mul_kron_one_right {R : Type*} [Ring R] (M : Matrix (dA × dB) (dA × dB) R) (A : Matrix dA dA R) : (M * (A ⊗ₖ (1 : Matrix dB dB R))).trace = (M.traceRight * A).trace := by - simp [trace, mul_apply, kroneckerMap_apply, traceRight, one_apply, + simp? [trace, mul_apply, kroneckerMap_apply, traceRight, one_apply, Fintype.sum_prod_type, Finset.sum_mul] exact Finset.sum_congr rfl fun _ _ => Finset.sum_comm @@ -651,7 +692,7 @@ open scoped Kronecker in theorem trace_mul_one_kron_right {R : Type*} [Ring R] (M : Matrix (dA × dB) (dA × dB) R) (B : Matrix dB dB R) : (M * ((1 : Matrix dA dA R) ⊗ₖ B)).trace = (M.traceLeft * B).trace := by - simp [trace, mul_apply, kroneckerMap_apply, traceLeft, one_apply, + simp? [trace, mul_apply, kroneckerMap_apply, traceLeft, one_apply, Fintype.sum_prod_type, Finset.sum_mul] rw [Finset.sum_comm] exact Finset.sum_congr rfl fun _ _ => Finset.sum_comm @@ -661,24 +702,34 @@ open ComplexOrder variable {d₁ d₂ : Type*} {A : Matrix (d₁ × d₂) (d₁ × d₂) 𝕜} variable [Fintype d₂] [Fintype d₁] -theorem PosSemidef.traceLeft [DecidableEq d₁] (hA : A.PosSemidef) : A.traceLeft.PosSemidef := by +omit [Fintype d₂] in +theorem PosSemidef.traceLeft [Finite d₂] (hA : A.PosSemidef) : A.traceLeft.PosSemidef := by + classical + let := Fintype.ofFinite d₂ rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA ⊢ constructor · exact hA.1.traceLeft · intro x - convert Finset.sum_nonneg' (s := .univ) (fun (i : d₁) ↦ hA.2 (fun (j,k) ↦ if i = j then x k else 0)) + convert Finset.sum_nonneg (s := .univ) (fun (i : d₁) _ ↦ hA.2 (fun (j,k) ↦ if i = j then x k + else 0)) simp_rw [Matrix.traceLeft, dotProduct_mulVec] - simpa [dotProduct, vecMul_eq_sum, ite_apply, Fintype.sum_prod_type, Finset.mul_sum, Finset.sum_mul, + simpa [dotProduct, vecMul_eq_sum, ite_apply, Fintype.sum_prod_type, Finset.mul_sum, + Finset.sum_mul, apply_ite] using Finset.sum_comm_cycle -theorem PosSemidef.traceRight [DecidableEq d₂] (hA : A.PosSemidef) : A.traceRight.PosSemidef := by +omit [Fintype d₁] in +theorem PosSemidef.traceRight [Finite d₁] (hA : A.PosSemidef) : A.traceRight.PosSemidef := by + classical + let := Fintype.ofFinite d₁ rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hA ⊢ constructor · exact hA.1.traceRight · intro x - convert Finset.sum_nonneg' (s := .univ) (fun (i : d₂) ↦ hA.2 (fun (j,k) ↦ if i = k then x j else 0)) + convert Finset.sum_nonneg (s := .univ) (fun (i : d₂) _ ↦ hA.2 (fun (j,k) ↦ if i = k then x j + else 0)) simp_rw [Matrix.traceRight, dotProduct_mulVec] - simpa [dotProduct, vecMul_eq_sum, ite_apply, Fintype.sum_prod_type, Finset.mul_sum, Finset.sum_mul, + simpa [dotProduct, vecMul_eq_sum, ite_apply, Fintype.sum_prod_type, Finset.mul_sum, + Finset.sum_mul, apply_ite] using Finset.sum_comm_cycle end partial_trace @@ -688,8 +739,12 @@ section posdef open ComplexOrder open Kronecker -theorem PosDef.kron {d₁ d₂ 𝕜 : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] [RCLike 𝕜] - {A : Matrix d₁ d₁ 𝕜} {B : Matrix d₂ d₂ 𝕜} (hA : A.PosDef) (hB : B.PosDef) : (A ⊗ₖ B).PosDef := by +theorem PosDef.kron {d₁ d₂ 𝕜 : Type*} [Finite d₁] [Finite d₂] [RCLike 𝕜] + {A : Matrix d₁ d₁ 𝕜} {B : Matrix d₂ d₂ 𝕜} (hA : A.PosDef) (hB : B.PosDef) : (A ⊗ₖ B).PosDef + := by + classical + let := Fintype.ofFinite d₁ + let := Fintype.ofFinite d₂ rw [hA.left.spectral_theorem, hB.left.spectral_theorem] simp only [Unitary.conjStarAlgAut_apply] rw [mul_kronecker_mul, mul_kronecker_mul] @@ -707,27 +762,29 @@ theorem PosDef.kron {d₁ d₂ 𝕜 : Type*} [Fintype d₁] [DecidableEq d₁] [ use (star hA.left.eigenvectorUnitary.val) ⊗ₖ (star hB.left.eigenvectorUnitary.val) simp [← Matrix.mul_kronecker_mul] -theorem PosDef.reindex {d₁ d₂ 𝕜 : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] [RCLike 𝕜] +theorem PosDef.reindex {d₁ d₂ 𝕜 : Type*} [RCLike 𝕜] {M : Matrix d₁ d₁ 𝕜} (hM : M.PosDef) (e : d₁ ≃ d₂) : (M.reindex e e).PosDef := hM.submatrix e.symm.injective -@[simp] -theorem PosDef.reindex_iff {d₁ d₂ 𝕜 : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] [RCLike 𝕜] +theorem PosDef.reindex_iff {d₁ d₂ 𝕜 : Type*} [RCLike 𝕜] {M : Matrix d₁ d₁ 𝕜} (e : d₁ ≃ d₂) : (M.reindex e e).PosDef ↔ M.PosDef := by refine ⟨fun h ↦ ?_, fun h ↦ h.reindex e⟩ convert h.reindex e.symm simp -theorem PosSemidef.rsmul {n : Type*} [Fintype n] {M : Matrix n n ℂ} (hM : M.PosSemidef) {c : ℝ} (hc : 0 ≤ c) : +theorem PosSemidef.rsmul {n : Type*} [Finite n] {M : Matrix n n ℂ} (hM : M.PosSemidef) {c : ℝ} + (hc : 0 ≤ c) : (c • M).PosSemidef := by + classical + let := Fintype.ofFinite n rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hM ⊢ constructor · exact hM.1.smul_real c - · peel hM.2 + · intro x rw [smul_mulVec, dotProduct_smul] - positivity + exact smul_nonneg hc (hM.2 x) -theorem PosDef.Convex {n 𝕜 : Type*} [Fintype n] [RCLike 𝕜] : Convex ℝ (Matrix.PosDef (n := n) (R := 𝕜)) := by +theorem PosDef.Convex {n 𝕜 : Type*} [RCLike 𝕜] : Convex ℝ (Matrix.PosDef (n := n) (R := 𝕜)) := by intro A hA B hB a b ha hb hab rcases ha.lt_or_eq with ha | rfl · apply (hA.smul ha).add_posSemidef @@ -749,42 +806,59 @@ theorem PosDef_iff_eigenvalues' (M : Matrix d d 𝕜) : ⟨fun h ↦ ⟨h.left, h.left.posDef_iff_eigenvalues_pos.mp h⟩, fun ⟨w, h⟩ ↦ w.posDef_iff_eigenvalues_pos.mpr h⟩ ---These is disgusting atm. There's cleaner versions of them headed to Mathlib. See #29526 and follow-ups +-- These is disgusting atm. There's cleaner versions of them headed to Mathlib. See #29526 and +-- follow-ups theorem IsHermitian.cfc_eigenvalues {M : Matrix d d 𝕜} (hM : M.IsHermitian) (f : ℝ → ℝ) : ∃ (e : d ≃ d), Matrix.IsHermitian.eigenvalues (cfc_predicate f M) = f ∘ hM.eigenvalues ∘ e := by - have h_eigenvalues : Multiset.map hM.eigenvalues Finset.univ.val = Multiset.map (fun i => hM.eigenvalues i) Finset.univ.val := by + have h_eigenvalues : Multiset.map hM.eigenvalues Finset.univ.val = Multiset.map (fun i => + hM.eigenvalues i) Finset.univ.val := by rfl generalize_proofs at *; - have h_eigenvalues_cfc : (IsHermitian.cfc hM f).charpoly.roots = Multiset.map (fun i => (f (hM.eigenvalues i) : 𝕜)) Finset.univ.val := by + have h_eigenvalues_cfc : (IsHermitian.cfc hM f).charpoly.roots = Multiset.map (fun i => (f + (hM.eigenvalues i) : 𝕜)) Finset.univ.val := by rw [ Matrix.IsHermitian.cfc, Matrix.charpoly ]; - -- Since $U$ is unitary, we have $U^* U = I$, and thus the characteristic polynomial of $U D U^*$ is the same as the characteristic polynomial of $D$. - have h_charpoly : Matrix.det ((hM.eigenvectorUnitary : Matrix d d 𝕜) * Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * Star.star (hM.eigenvectorUnitary : Matrix d d 𝕜)).charmatrix = Matrix.det (Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues)).charmatrix := by - -- Since $U$ is unitary, we have $U^* U = I$, and thus the characteristic polynomial of $U D U^*$ is the same as the characteristic polynomial of $D$ by the properties of determinants. - have h_char_poly : ∀ (t : 𝕜), Matrix.det (t • 1 - (hM.eigenvectorUnitary : Matrix d d 𝕜) * Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * star (hM.eigenvectorUnitary : Matrix d d 𝕜)) = Matrix.det (t • 1 - Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues)) := by + -- Since $U$ is unitary, we have $U^* U = I$, and thus the characteristic polynomial of + -- $U D U^*$ is the same as the characteristic polynomial of $D$. + have h_charpoly : Matrix.det ((hM.eigenvectorUnitary : Matrix d d 𝕜) * Matrix.diagonal + (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * Star.star (hM.eigenvectorUnitary : Matrix d d + 𝕜)).charmatrix = Matrix.det (Matrix.diagonal (RCLike.ofReal ∘ f ∘ + hM.eigenvalues)).charmatrix := by + -- Since $U$ is unitary, we have $U^* U = I$, and thus the characteristic polynomial + -- of $U D U^*$ is the same as the characteristic polynomial of $D$ by the + -- properties of determinants. + have h_char_poly : ∀ (t : 𝕜), Matrix.det (t • 1 - (hM.eigenvectorUnitary : Matrix d d 𝕜) * + Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * star (hM.eigenvectorUnitary : + Matrix d d 𝕜)) = Matrix.det (t • 1 - Matrix.diagonal (RCLike.ofReal ∘ f ∘ + hM.eigenvalues)) := by intro t; - -- Since $U$ is unitary, we have $U^* U = I$, and thus the determinant of $tI - UDU^*$ is the same as the determinant of $tI - D$. - have h_det : Matrix.det (t • 1 - (hM.eigenvectorUnitary : Matrix d d 𝕜) * Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * star (hM.eigenvectorUnitary : Matrix d d 𝕜)) = Matrix.det ((hM.eigenvectorUnitary : Matrix d d 𝕜) * (t • 1 - Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues)) * star (hM.eigenvectorUnitary : Matrix d d 𝕜)) := by + -- Since $U$ is unitary, we have $U^* U = I$, and thus the determinant of $tI - + -- UDU^*$ is the same as the determinant of $tI - D$. + have h_det : Matrix.det (t • 1 - (hM.eigenvectorUnitary : Matrix d d 𝕜) * + Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues) * star (hM.eigenvectorUnitary : + Matrix d d 𝕜)) = Matrix.det ((hM.eigenvectorUnitary : Matrix d d 𝕜) * (t • 1 - + Matrix.diagonal (RCLike.ofReal ∘ f ∘ hM.eigenvalues)) * star (hM.eigenvectorUnitary + : Matrix d d 𝕜)) := by simp [ mul_sub, sub_mul, mul_assoc ]; rw [ h_det, Matrix.det_mul, Matrix.det_mul ]; rw [ mul_right_comm, ← Matrix.det_mul, mul_comm ]; norm_num +zetaDelta at *; - refine' Polynomial.funext fun t => _; + refine Polynomial.funext fun t => ?_; convert h_char_poly t using 1; - · simp [ Matrix.det_apply', Polynomial.eval_finsetSum ]; - simp [ Matrix.one_apply, Polynomial.eval_prod ]; + · simp? [ Matrix.det_apply', Polynomial.eval_finsetSum ]; + simp? [ Matrix.one_apply, Polynomial.eval_prod ]; congr! 3; aesop; - · simp [ Matrix.det_apply', Polynomial.eval_finsetSum ]; - simp [ Matrix.one_apply, Polynomial.eval_prod ]; + · simp? [ Matrix.det_apply', Polynomial.eval_finsetSum ]; + simp? [ Matrix.one_apply, Polynomial.eval_prod ]; exact Finset.sum_congr rfl fun _ _ => by congr; ext; aesop; - simp_all [ Matrix.charmatrix, Matrix.det_diagonal ]; + simp_all? [ Matrix.charmatrix, Matrix.det_diagonal ]; rw [ Polynomial.roots_prod ]; · bound; · exact Finset.prod_ne_zero_iff.mpr fun i _ => Polynomial.X_sub_C_ne_zero _; have := Matrix.IsHermitian.roots_charpoly_eq_eigenvalues (cfc_predicate f M); rw [← Matrix.IsHermitian.cfc_eq] at h_eigenvalues_cfc rw [ h_eigenvalues_cfc ] at this; - simp [ Function.comp ] at this; + simp? [ Function.comp ] at this; rw [ Multiset.map_univ_eq_iff ] at this; obtain ⟨ e, he ⟩ := this; use e.symm @@ -794,7 +868,8 @@ theorem IsHermitian.cfc_eigenvalues {M : Matrix d d 𝕜} (hM : M.IsHermitian) ( --Should be combined the above...? TODO Cleanup /-- -If a Hermitian matrix A is unitarily similar to a diagonal matrix with real entries f, then the eigenvalues of A are a permutation of f. +If a Hermitian matrix A is unitarily similar to a diagonal matrix with real entries f, then the + eigenvalues of A are a permutation of f. -/ lemma IsHermitian.eigenvalues_eq_of_unitary_similarity_diagonal {d 𝕜 : Type*} [Fintype d] [DecidableEq d] [RCLike 𝕜] @@ -805,37 +880,46 @@ lemma IsHermitian.eigenvalues_eq_of_unitary_similarity_diagonal {d 𝕜 : Type*} ∃ σ : d ≃ d, hA.eigenvalues ∘ σ = f := by -- Since A is unitarily similar to D, they have the same characteristic polynomial. have h_char_poly : Matrix.charpoly A = Matrix.charpoly (Matrix.diagonal fun i => (f i : 𝕜)) := by - have h_char_poly : Matrix.charpoly (U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) = Matrix.charpoly (Matrix.diagonal (fun i => (f i : 𝕜))) := by - have h_det : ∀ (t : 𝕜), Matrix.det (t • 1 - U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) = Matrix.det (t • 1 - Matrix.diagonal (fun i => (f i : 𝕜))) := by + have h_char_poly : Matrix.charpoly (U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) = + Matrix.charpoly (Matrix.diagonal (fun i => (f i : 𝕜))) := by + have h_det : ∀ (t : 𝕜), Matrix.det (t • 1 - U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) + = Matrix.det (t • 1 - Matrix.diagonal (fun i => (f i : 𝕜))) := by intro t - have h_det : Matrix.det (t • 1 - U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) = Matrix.det (U * (t • 1 - Matrix.diagonal (fun i => (f i : 𝕜))) * Uᴴ) := by - simp [ mul_sub, sub_mul, Matrix.mul_assoc ]; + have h_det : Matrix.det (t • 1 - U * Matrix.diagonal (fun i => (f i : 𝕜)) * Uᴴ) = + Matrix.det (U * (t • 1 - Matrix.diagonal (fun i => (f i : 𝕜))) * Uᴴ) := by + simp? [ mul_sub, sub_mul, Matrix.mul_assoc ]; rw [ show U * Uᴴ = 1 from by exact hU.2 ]; rw [h_det, Matrix.det_mul_comm, ← mul_assoc] rw [← star_eq_conjTranspose, Matrix.UnitaryGroup.star_mul_self ⟨U, hU⟩] simp - refine' Polynomial.funext fun t => _; - convert h_det t using 1 <;> simp [ Matrix.charpoly, Matrix.det_apply' ]; - · simp [ Polynomial.eval_finsetSum, Polynomial.eval_mul, Polynomial.eval_prod, Matrix.one_apply ]; + refine Polynomial.funext fun t => ?_; + convert h_det t using 1 <;> simp? [ Matrix.charpoly, Matrix.det_apply' ]; + · simp? [ Polynomial.eval_finsetSum, Polynomial.eval_mul, Polynomial.eval_prod, + Matrix.one_apply ]; exact Finset.sum_congr rfl fun _ _ => by congr; ext; aesop; - · simp [ Polynomial.eval_finsetSum, Polynomial.eval_mul, Polynomial.eval_prod, Matrix.one_apply ]; + · simp? [ Polynomial.eval_finsetSum, Polynomial.eval_mul, Polynomial.eval_prod, + Matrix.one_apply ]; exact Finset.sum_congr rfl fun _ _ => by congr; ext; aesop; rw [ h, h_char_poly ]; - -- The roots of the characteristic polynomial of A are its eigenvalues (by `IsHermitian.charpoly_roots_eq_eigenvalues`). - have h_eigenvalues : (Matrix.charpoly A).roots = Multiset.map (RCLike.ofReal ∘ hA.eigenvalues) Finset.univ.val := by + -- The roots of the characteristic polynomial of A are its eigenvalues (by + -- `IsHermitian.charpoly_roots_eq_eigenvalues`). + have h_eigenvalues : (Matrix.charpoly A).roots = Multiset.map (RCLike.ofReal ∘ hA.eigenvalues) + Finset.univ.val := by exact Matrix.IsHermitian.roots_charpoly_eq_eigenvalues hA; -- The roots of the characteristic polynomial of D are the diagonal entries f. - have h_diag_roots : (Matrix.charpoly (Matrix.diagonal fun i => (f i : 𝕜))).roots = Multiset.map (fun i => (f i : 𝕜)) Finset.univ.val := by - simp [ Matrix.charpoly, Matrix.det_diagonal ]; + have h_diag_roots : (Matrix.charpoly (Matrix.diagonal fun i => (f i : 𝕜))).roots = + Multiset.map (fun i => (f i : 𝕜)) Finset.univ.val := by + simp? [ Matrix.charpoly, Matrix.det_diagonal ]; rw [ Polynomial.roots_prod ]; · aesop; · exact Finset.prod_ne_zero_iff.mpr fun i _ => Polynomial.X_sub_C_ne_zero _; have := Multiset.map_univ_eq_iff ( RCLike.ofReal ∘ hA.eigenvalues ) f subst h simp_all only [Function.comp_apply, RCLike.ofReal_real_eq_id, id_eq, CompTriple.comp_eq] - refine' this.mp _ |> fun ⟨ e, he ⟩ => ⟨ e.symm, _ ⟩ - · simpa [ Function.comp ] using congr_arg ( Multiset.map ( RCLike.re : 𝕜 → ℝ ) ) h_eigenvalues.symm - · exact funext fun x => by simpa using congr_fun he ( e.symm x ) ; + refine this.mp ?_ |> fun ⟨ e, he ⟩ => ⟨ e.symm, ?_ ⟩ + · simpa [ Function.comp ] using congr_arg ( Multiset.map ( RCLike.re : 𝕜 → ℝ ) ) + h_eigenvalues.symm + · exact funext fun x => by simpa using congr_fun he ( e.symm x ); end eigenvalues @@ -843,7 +927,6 @@ section variable {α n : Type*} [RCLike α] [Fintype n] [DecidableEq n] -@[simp] theorem toEuclideanLin_one : Matrix.toEuclideanLin (1 : Matrix n n α) = .id := by ext1 x simp [Matrix.toEuclideanLin] @@ -865,7 +948,7 @@ theorem cfc_diagonal (g : d → ℝ) (f : ℝ → ℝ) : have hg (x : d) : g x ∈ spectrum ℝ (diagonal (fun y => (g y : 𝕜))) := by intro hc have h := congrArg (fun M : Matrix d d 𝕜 => M x x) hc.exists_left_inv.choose_spec - simpa [algebraMap_eq_diagonal, diagonal_apply] using h + simp [algebraMap_eq_diagonal] at h let φ : C(spectrum ℝ (diagonal (fun x => (g x : 𝕜))), ℝ) →⋆ₐ[ℝ] Matrix d d 𝕜 := { toFun := fun h => diagonal fun x => RCLike.ofReal (h ⟨g x, hg x⟩) map_zero' := by simp @@ -902,7 +985,7 @@ theorem cfc_diagonal (g : d → ℝ) (f : ℝ → ℝ) : continuousOn_iff_continuous_domRestrict.mpr (by fun_prop)⟩] rw [cfcHom_eq_of_continuous_of_map_id h_self_adjoint φ hφ (by ext i j - by_cases h : i = j <;> simp [φ, diagonal_apply, h])] + by_cases h : i = j <;> simp [φ, h])] rfl theorem PosSemidef.pos_of_mem_spectrum {A : Matrix d d 𝕜} (hA : A.PosSemidef) (r : ℝ) : @@ -960,25 +1043,34 @@ private lemma spectrum_prod_complex {d d₂ : Type*} congr! 1; ext ⟨ i, j ⟩ ⟨ i', j' ⟩; simp [ Algebra.smul_def ] - -- Since $A$ and $B$ are Hermitian, they are diagonalizable. Let $P$ and $Q$ be unitary matrices such that $P^*AP$ and $Q^*BQ$ are diagonal. - obtain ⟨P, hP₁, ⟨D, hD⟩⟩ : ∃ P : Matrix d d 𝕜, P.det ≠ 0 ∧ ∃ D : Matrix d d 𝕜, D.IsDiag ∧ P⁻¹ * A * P = D := by - refine' ⟨ hA.eigenvectorUnitary, _, Matrix.diagonal ( RCLike.ofReal ∘ hA.eigenvalues ), _, _ ⟩; + -- Since $A$ and $B$ are Hermitian, they are diagonalizable. Let $P$ and $Q$ be unitary + -- matrices such that $P^*AP$ and $Q^*BQ$ are diagonal. + obtain ⟨P, hP₁, ⟨D, hD⟩⟩ : ∃ P : Matrix d d 𝕜, P.det ≠ 0 ∧ ∃ D : Matrix d d 𝕜, D.IsDiag ∧ P⁻¹ + * A * P = D := by + refine ⟨ hA.eigenvectorUnitary, ?_, Matrix.diagonal ( RCLike.ofReal ∘ hA.eigenvalues ), ?_, + ?_ ⟩; · intro h_det_zero; - exact absurd h_det_zero <| isUnit_iff_ne_zero.mp <| UnitaryGroup.det_isUnit hA.eigenvectorUnitary + exact absurd h_det_zero <| isUnit_iff_ne_zero.mp <| UnitaryGroup.det_isUnit + hA.eigenvectorUnitary · exact isDiag_diagonal (RCLike.ofReal ∘ hA.eigenvalues); · -- Since $U$ is unitary, $U⁻¹ = U*$, and thus $U⁻¹ * U = I$. - have h_unitary : (hA.eigenvectorUnitary : Matrix d d 𝕜)⁻¹ = star (hA.eigenvectorUnitary : Matrix d d 𝕜) := by + have h_unitary : (hA.eigenvectorUnitary : Matrix d d 𝕜)⁻¹ = star (hA.eigenvectorUnitary : + Matrix d d 𝕜) := by rw [ Matrix.inv_eq_left_inv ]; simp -- Substitute h_unitary into the equation. rw [h_unitary]; convert Matrix.IsHermitian.conjStarAlgAut_star_eigenvectorUnitary hA using 1 simp - obtain ⟨Q, hQ₁, ⟨E, hE⟩⟩ : ∃ Q : Matrix d₂ d₂ 𝕜, Q.det ≠ 0 ∧ ∃ E : Matrix d₂ d₂ 𝕜, E.IsDiag ∧ Q⁻¹ * B * Q = E := by + obtain ⟨Q, hQ₁, ⟨E, hE⟩⟩ : ∃ Q : Matrix d₂ d₂ 𝕜, Q.det ≠ 0 ∧ ∃ E : Matrix d₂ d₂ 𝕜, E.IsDiag ∧ + Q⁻¹ * B * Q = E := by have := Matrix.IsHermitian.spectral_theorem hB; - -- By the spectral theorem, since B is Hermitian, there exists a unitary matrix Q and a diagonal matrix D such that B = Q * D * Q⁻¹. - obtain ⟨Q, hQ_unitary, D, hD_diag, hQ⟩ : ∃ Q : Matrix d₂ d₂ 𝕜, Q.det ≠ 0 ∧ ∃ D : Matrix d₂ d₂ 𝕜, D.IsDiag ∧ B = Q * D * Q⁻¹ := by - refine' ⟨ hB.eigenvectorUnitary, _, Matrix.diagonal ( RCLike.ofReal ∘ hB.eigenvalues ), _, _ ⟩; + -- By the spectral theorem, since B is Hermitian, there exists a unitary matrix Q and a + -- diagonal matrix D such that B = Q * D * Q⁻¹. + obtain ⟨Q, hQ_unitary, D, hD_diag, hQ⟩ : ∃ Q : Matrix d₂ d₂ 𝕜, Q.det ≠ 0 ∧ ∃ D : Matrix d₂ + d₂ 𝕜, D.IsDiag ∧ B = Q * D * Q⁻¹ := by + refine ⟨ hB.eigenvectorUnitary, ?_, Matrix.diagonal ( RCLike.ofReal ∘ hB.eigenvalues ), + ?_, ?_ ⟩; · intro h_det_zero; -- Since the eigenvector unitary matrix is unitary, its determinant is non-zero. have h_unitary_det : ∀ (U : Matrix d₂ d₂ 𝕜), U * star U = 1 → U.det ≠ 0 := @@ -992,29 +1084,38 @@ private lemma spectrum_prod_complex {d d₂ : Type*} · simp only [SetLike.coe_mem, Unitary.star_mul_self_of_mem] refine ⟨ Q, hQ_unitary, D, hD_diag, ?_ ⟩ simp [ hQ, mul_assoc, hQ_unitary, isUnit_iff_ne_zero ]; - -- Then $(P \otimes Q)^{-1}(A \otimes B)(P \otimes Q) = D \otimes E$, where $D$ and $E$ are diagonal matrices. + -- Then $(P \otimes Q)^{-1}(A \otimes B)(P \otimes Q) = D \otimes E$, where $D$ and $E$ are + -- diagonal matrices. have h_diag : (P.kronecker Q)⁻¹ * (A ⊗ₖ B) * (P.kronecker Q) = D ⊗ₖ E := by - -- Using the properties of the Kronecker product and the fact that $P$ and $Q$ are invertible, we can simplify the expression. - have h_kronecker : (P.kronecker Q)⁻¹ * (A.kronecker B) * (P.kronecker Q) = (P⁻¹ * A * P).kronecker (Q⁻¹ * B * Q) := by - have h_kronecker : ∀ (X Y : Matrix d d 𝕜) (Z W : Matrix d₂ d₂ 𝕜), (X.kronecker Z) * (Y.kronecker W) = (X * Y).kronecker (Z * W) := by - intro X Y Z W; ext i j; simp [ Matrix.mul_apply ] ; + -- Using the properties of the Kronecker product and the fact that $P$ and $Q$ are + -- invertible, we can simplify the expression. + have h_kronecker : (P.kronecker Q)⁻¹ * (A.kronecker B) * (P.kronecker Q) = (P⁻¹ * A * + P).kronecker (Q⁻¹ * B * Q) := by + have h_kronecker : ∀ (X Y : Matrix d d 𝕜) (Z W : Matrix d₂ d₂ 𝕜), (X.kronecker Z) * + (Y.kronecker W) = (X * Y).kronecker (Z * W) := by + intro X Y Z W; ext i j; simp? [ Matrix.mul_apply ]; simp only [mul_left_comm, mul_comm, Finset.mul_sum _ _ _]; exact Fintype.sum_prod_type_right _ rw [Matrix.inv_eq_right_inv, h_kronecker, h_kronecker]; convert h_kronecker P P⁻¹ Q Q⁻¹ using 1; simp [ hP₁, hQ₁, isUnit_iff_ne_zero ]; aesop; - -- Since $D$ and $E$ are diagonal matrices, the determinant of $(D \otimes E - xI)$ is the product of the determinants of $(D - xI)$ and $(E - xI)$. + -- Since $D$ and $E$ are diagonal matrices, the determinant of $(D \otimes E - xI)$ is the + -- product of the determinants of $(D - xI)$ and $(E - xI)$. have h_det_diag : Matrix.det (D ⊗ₖ E - x • 1) = 0 := by - have h_det_diag : Matrix.det ((P.kronecker Q)⁻¹ * (A ⊗ₖ B - x • 1) * (P.kronecker Q)) = Matrix.det (D ⊗ₖ E - x • 1) := by + have h_det_diag : Matrix.det ((P.kronecker Q)⁻¹ * (A ⊗ₖ B - x • 1) * (P.kronecker Q)) = + Matrix.det (D ⊗ₖ E - x • 1) := by simp [ ← h_diag, mul_sub, sub_mul ]; simp [ Matrix.det_kronecker, hP₁, hQ₁ ]; simp_all [ Matrix.det_mul ]; - -- Since $D$ and $E$ are diagonal matrices, the determinant of $(D \otimes E - xI)$ is the product of the determinants of $(D - xI)$ and $(E - xI)$. Therefore, there must be some $i$ and $j$ such that $D_{ii} * E_{jj} = x$. + -- Since $D$ and $E$ are diagonal matrices, the determinant of $(D \otimes E - xI)$ is the + -- product of the determinants of $(D - xI)$ and $(E - xI)$. Therefore, there must be some + -- $i$ and $j$ such that $D_{ii} * E_{jj} = x$. obtain ⟨i, j, hij⟩ : ∃ i : d, ∃ j : d₂, D i i * E j j = x := by contrapose! h_det_diag; have h_det_diag : Matrix.det (D ⊗ₖ E - x • 1) = ∏ i : d, ∏ j : d₂, (D i i * E j j - x) := by - have h_det_diag : Matrix.det (D ⊗ₖ E - x • 1) = Matrix.det (Matrix.diagonal (fun p : d × d₂ => D p.1 p.1 * E p.2 p.2 - x)) := by + have h_det_diag : Matrix.det (D ⊗ₖ E - x • 1) = Matrix.det (Matrix.diagonal (fun p : d × + d₂ => D p.1 p.1 * E p.2 p.2 - x)) := by congr with p q simp_all only [ne_eq, kronecker, sub_apply, kroneckerMap_apply, smul_apply, smul_eq_mul] @@ -1023,44 +1124,48 @@ private lemma spectrum_prod_complex {d d₂ : Type*} obtain ⟨left, rfl⟩ := hD obtain ⟨left_1, rfl⟩ := hE simp_all only - by_cases h : fst = fst_1 <;> by_cases h' : snd = snd_1 <;> simp [ h, h', Matrix.one_apply ]; + by_cases h : fst = fst_1 <;> by_cases h' : snd = snd_1 <;> simp? [ h, h', + Matrix.one_apply ]; · exact Or.inr ( left_1 ( by aesop ) ); · exact Or.inl ( left h ); · exact Or.inl ( left h ); - simp_all [ Matrix.det_diagonal ]; + simp_all? [ Matrix.det_diagonal ]; exact Fintype.prod_prod_type fun (x_2 : d × d₂) => D x_2.1 x_2.1 * E x_2.2 x_2.2 - x - exact h_det_diag.symm ▸ Finset.prod_ne_zero_iff.mpr fun i _ => Finset.prod_ne_zero_iff.mpr fun j _ => sub_ne_zero_of_ne <| by solve_by_elim; - refine' ⟨ D i i, _, E j j, _, _ ⟩ - · simp_all [ spectrum.mem_iff ]; - simp_all [ Matrix.isUnit_iff_isUnit_det ]; + exact h_det_diag.symm ▸ Finset.prod_ne_zero_iff.mpr fun i _ => Finset.prod_ne_zero_iff.mpr + fun j _ => sub_ne_zero_of_ne <| by solve_by_elim; + refine ⟨ D i i, ?_, E j j, ?_, ?_ ⟩ + · simp_all? [ spectrum.mem_iff ]; + simp_all? [ Matrix.isUnit_iff_isUnit_det ]; have h_det_diag : Matrix.det (P⁻¹ * (D i i • 1 - A) * P) = 0 := by - simp_all [ mul_sub, sub_mul, mul_assoc ]; + simp_all? [ mul_sub, sub_mul, mul_assoc ]; rw [ Matrix.det_eq_zero_of_row_eq_zero i ] intro j_1 subst hij simp_all only [map_mul, sub_apply, smul_apply, smul_eq_mul] obtain ⟨left, rfl⟩ := hD obtain ⟨left_1, rfl⟩ := hE - by_cases hij : i = j_1 <;> simp_all [ Matrix.one_apply ]; + by_cases hij : i = j_1 <;> simp_all? [ Matrix.one_apply ]; exact left hij; - simp_all [ Matrix.det_mul]; + simp_all? [ Matrix.det_mul]; convert h_det_diag using 1; - exact congr_arg Matrix.det ( by ext i j; by_cases hi : i = j <;> simp [ hi, Algebra.smul_def ] ); - · simp_all [ spectrum.mem_iff ]; + exact congr_arg Matrix.det ( by ext i j; by_cases hi : i = j <;> simp [ hi, Algebra.smul_def + ] ); + · simp_all? [ spectrum.mem_iff ]; -- Since $E$ is diagonal, $E j j - B$ is singular, hence not invertible. have h_singular : Matrix.det (E j j • 1 - B) = 0 := by have h_singular : Matrix.det (Q⁻¹ * (E j j • 1 - B) * Q) = 0 := by - simp [ mul_sub, sub_mul, hE.2 ]; + simp? [ mul_sub, sub_mul, hE.2 ]; rw [ Matrix.det_eq_zero_of_row_eq_zero j ] intro j_1 subst hij - simp_all only [map_mul, isUnit_iff_ne_zero, ne_eq, not_false_eq_true, nonsing_inv_mul, sub_apply, + simp_all only [map_mul, isUnit_iff_ne_zero, ne_eq, not_false_eq_true, nonsing_inv_mul, + sub_apply, smul_apply, smul_eq_mul] obtain ⟨left, rfl⟩ := hD obtain ⟨left_1, rfl⟩ := hE by_cases h : j = j_1 <;> aesop; simp_all [ Matrix.det_mul ]; - simp_all [ Matrix.isUnit_iff_isUnit_det ]; + simp_all? [ Matrix.isUnit_iff_isUnit_det ]; convert h_singular using 1; simp [ Algebra.smul_def ]; · simp_all [ spectrum.mem_iff ]; @@ -1097,52 +1202,61 @@ theorem spectrum_prod {d d₂ : Type*} apply subset_antisymm · exact spectrum_prod_le hA hB · rintro x ⟨ y, hy, z, hz, rfl ⟩; - -- Since $y$ is an eigenvalue of $A$ and $z$ is an eigenvalue of $B$, there exist eigenvectors $v$ and $w$ such that $A*v = y*v$ and $B*w = z*w$. + -- Since $y$ is an eigenvalue of $A$ and $z$ is an eigenvalue of $B$, there exist + -- eigenvectors $v$ and $w$ such that $A*v = y*v$ and $B*w = z*w$. obtain ⟨v, hv⟩ : ∃ v : d → 𝕜, v ≠ 0 ∧ A.mulVec v = y • v := by rw [ spectrum.mem_iff ] at hy; - simp_all [ Matrix.isUnit_iff_isUnit_det ]; + simp_all? [ Matrix.isUnit_iff_isUnit_det ]; have := Matrix.exists_mulVec_eq_zero_iff.mpr hy; - simp_all [ funext_iff, Matrix.mulVec, dotProduct ]; - simp_all [ sub_mul, Matrix.one_apply, Algebra.algebraMap_eq_smul_one ]; - exact ⟨ this.choose, this.choose_spec.1, fun x => by linear_combination -this.choose_spec.2 x ⟩ + simp_all? [ funext_iff, Matrix.mulVec, dotProduct ]; + simp_all? [ sub_mul, Matrix.one_apply, Algebra.algebraMap_eq_smul_one ]; + exact ⟨ this.choose, this.choose_spec.1, fun x => by + linear_combination -this.choose_spec.2 x ⟩ obtain ⟨w, hw⟩ : ∃ w : d₂ → 𝕜, w ≠ 0 ∧ B.mulVec w = z • w := by rw [ spectrum.mem_iff ] at hz; - simp_all [ Matrix.isUnit_iff_isUnit_det ]; + simp_all? [ Matrix.isUnit_iff_isUnit_det ]; have := Matrix.exists_mulVec_eq_zero_iff.mpr hz; - simp_all [ Matrix.sub_mulVec ]; - obtain ⟨ w, hw, hw' ⟩ := this; use w; simp_all [ sub_eq_zero, Algebra.algebraMap_eq_smul_one ] ; - simp_all [ funext_iff, Matrix.mulVec, dotProduct ]; + simp_all? [ Matrix.sub_mulVec ]; + obtain ⟨ w, hw, hw' ⟩ := this; use w; simp_all? [ sub_eq_zero, + Algebra.algebraMap_eq_smul_one ]; + simp_all? [ funext_iff, Matrix.mulVec, dotProduct ]; simp_all [ Matrix.one_apply]; - refine' spectrum.mem_iff.mpr _; + refine spectrum.mem_iff.mpr ?_; -- Consider the vector $v \otimes w$. set v_tensor_w : (d × d₂) → 𝕜 := fun p => v p.1 * w p.2; -- We need to show that $v \otimes w$ is an eigenvector of $A \otimes B$ with eigenvalue $yz$. - have h_eigenvector : (Matrix.kroneckerMap (· * ·) A B).mulVec v_tensor_w = (y * z) • v_tensor_w := by - ext ⟨ i, j ⟩ ; - simp [ Matrix.mulVec, dotProduct] at * - simp [ funext_iff, Matrix.mulVec, dotProduct ] at hv hw ⊢ + have h_eigenvector : (Matrix.kroneckerMap (· * ·) A B).mulVec v_tensor_w = (y * z) • + v_tensor_w := by + ext ⟨ i, j ⟩; + simp? [ Matrix.mulVec, dotProduct] at * + simp? [ funext_iff, Matrix.mulVec, dotProduct ] at hv hw ⊢ erw [ Finset.sum_product ] simp_all only [v_tensor_w] obtain ⟨left, right⟩ := hv obtain ⟨left_1, right_1⟩ := hw -- By separating the sums, we can apply the given equalities. - have h_separate : ∑ x, ∑ x_1, A i x * B j x_1 * (v x * w x_1) = (∑ x : d, A i x * v x) * (∑ x_1 : d₂, B j x_1 * w x_1) := by + have h_separate : ∑ x, ∑ x_1, A i x * B j x_1 * (v x * w x_1) = (∑ x : d, A i x * v x) * + (∑ x_1 : d₂, B j x_1 * w x_1) := by simp only [mul_left_comm, mul_comm, Finset.mul_sum _ _ _]; - exact Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ); + exact Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ + _ => by ring ); rw [ h_separate, right, right_1 ] simp [RCLike.real_smul_eq_coe_mul] ring_nf - -- Since $v \otimes w$ is an eigenvector of $A \otimes B$ with eigenvalue $yz$, we have $(A \otimes B - yzI)(v \otimes w) = 0$. + -- Since $v \otimes w$ is an eigenvector of $A \otimes B$ with eigenvalue $yz$, we have + -- $(A \otimes B - yzI)(v \otimes w) = 0$. have h_eigenvector_zero : ((A ⊗ₖ B) - (y * z) • 1) *ᵥ v_tensor_w = 0 := by simp [ h_eigenvector, Matrix.sub_mulVec ]; simp [ Matrix.mulVec, funext_iff ]; simp [ Matrix.one_apply, dotProduct ]; - -- Since $v \otimes w$ is non-zero, we have $(A \otimes B - yzI)(v \otimes w) = 0$ implies that $A \otimes B - yzI$ is not invertible. + -- Since $v \otimes w$ is non-zero, we have $(A \otimes B - yzI)(v \otimes w) = 0$ + -- implies that $A \otimes B - yzI$ is not invertible. have h_not_invertible : ¬IsUnit (A ⊗ₖ B - (y * z) • 1) := by - simp only [ne_eq, isUnit_iff_isUnit_det, isUnit_iff_ne_zero, Decidable.not_not, v_tensor_w] at * + simp only [ne_eq, isUnit_iff_isUnit_det, isUnit_iff_ne_zero, Decidable.not_not, + v_tensor_w] at * rw [ ← Matrix.exists_mulVec_eq_zero_iff ] - refine' ⟨ v_tensor_w, _, h_eigenvector_zero ⟩; - simp [ funext_iff ] at hv hw ⊢ + refine ⟨ v_tensor_w, ?_, h_eigenvector_zero ⟩; + simp? [ funext_iff ] at hv hw ⊢ obtain ⟨left, right⟩ := hv obtain ⟨left_1, right_1⟩ := hw exact ⟨left.choose, left_1.choose, mul_ne_zero left.choose_spec left_1.choose_spec⟩ @@ -1154,20 +1268,23 @@ end spectrum_kron open ComplexOrder in open MatrixOrder in -theorem PosDef.zero_lt {n : Type*} [Nonempty n] [Fintype n] {A : Matrix n n ℂ} (hA : A.PosDef) : 0 < A := by +theorem PosDef.zero_lt {n : Type*} [Nonempty n] {A : Matrix n n ℂ} (hA : A.PosDef) : 0 < A := by apply lt_of_le_of_ne · replace hA := hA.posSemidef rwa [Matrix.nonneg_iff_posSemidef] · rintro rfl; exact absurd (hA.diag_pos (i := Classical.arbitrary n)) (by simp) -lemma IsHermitian.spectrum_eq_image_eigenvalues [Fintype n] {A : Matrix n n ℂ} (hA : A.IsHermitian) : +lemma IsHermitian.spectrum_eq_image_eigenvalues [Fintype n] {A : Matrix n n ℂ} (hA : + A.IsHermitian) : spectrum ℝ A = Finset.univ.image hA.eigenvalues := by simpa using hA.spectrum_real_eq_range_eigenvalues -/- This lemma looks "wrong" in the sense that it's specifically about `Fintype.card foo = Finset.card bar`, +/- This lemma looks "wrong" in the sense that it's specifically about `Fintype.card foo = +Finset.card bar`, why not just use the underlying fact `foo = ↑bar`? It turns out this actually gives annoying issues -with dependent rewrites, given the necessary `Fintype` instance. Using the above theorem for example, +with dependent rewrites, given the necessary `Fintype` instance. Using the above theorem for + example, trying `rw [hA.spectrum_eq_image_eigenvalues]` fails because of dependent types. -/ lemma IsHermitian.card_spectrum_eq_image [Fintype n] {A : Matrix n n ℂ} (hA : A.IsHermitian) [Fintype (spectrum ℝ A)] : @@ -1193,49 +1310,57 @@ lemma sub_iInf_eignevalues (hA : A.IsHermitian) : · simpa [ Matrix.IsHermitian, sub_eq_add_neg ] using hA · intro x have h_eigenvalue : ∀ i, hA.eigenvalues i ≥ iInf hA.eigenvalues := by - -- By definition of infimum, for any eigenvalue $i$, we have $hA.eigenvalues i \geq iInf hA.eigenvalues$. + -- By definition of infimum, for any eigenvalue $i$, we have $hA.eigenvalues i \geq + -- iInf hA.eigenvalues$. intros i apply le_of_forall_le intro j a exact le_trans a (ciInf_le ( Finite.bddBelow_range hA.eigenvalues ) i ); - -- Since $A$ is Hermitian, we can diagonalize it as $A = Q \Lambda Q^*$, where $Q$ is unitary and $\Lambda$ is diagonal with the eigenvalues on the diagonal. - obtain ⟨Q, Λ, hQ, hΛ⟩ : ∃ Q : Matrix d d ℂ, ∃ Λ : d → ℂ, Q.conjTranspose * Q = 1 ∧ A = Q * Matrix.diagonal Λ * Q.conjTranspose ∧ ∀ i, Λ i = Matrix.IsHermitian.eigenvalues hA i := by - have := hA.spectral_theorem; - refine' ⟨ _, _, _, this, _ ⟩; - · simp [ ← Matrix.ext_iff ]; - intro i j; erw [ Matrix.mul_apply ] ; simp [ Matrix.one_apply ] ; - have := hA.eigenvectorBasis.orthonormal; - rw [ orthonormal_iff_ite ] at this; - rw [← this i j] - simp [PiLp.inner_apply, mul_comm] - · simp - -- Since $Q$ is unitary, we have $Q^* Q = I$, and thus $Q^* (A - \lambda_{\min} I) Q = \Lambda - \lambda_{\min} I$. - have h_diag : Q.conjTranspose * (A - (iInf (Matrix.IsHermitian.eigenvalues hA)) • 1) * Q = Matrix.diagonal (fun i => Λ i - (iInf (Matrix.IsHermitian.eigenvalues hA))) := by - simp [ hΛ, mul_sub, sub_mul, mul_assoc, hQ ]; - simp [ ← mul_assoc, hQ]; - ext i j ; by_cases hij : i = j <;> aesop; - -- Since $Q$ is unitary, we have $Q^* (A - \lambda_{\min} I) Q = \Lambda - \lambda_{\min} I$, and thus $x^* (A - \lambda_{\min} I) x = (Q^* x)^* (\Lambda - \lambda_{\min} I) (Q^* x)$. - have h_quad_form : Star.star x ⬝ᵥ (A - (iInf (Matrix.IsHermitian.eigenvalues hA)) • 1).mulVec x = Star.star (Q.conjTranspose.mulVec x) ⬝ᵥ (Matrix.diagonal (fun i => Λ i - (iInf (Matrix.IsHermitian.eigenvalues hA)))).mulVec (Q.conjTranspose.mulVec x) := by + -- Since $A$ is Hermitian, we can diagonalize it as $A = Q \Lambda Q^*$, where $Q$ is + -- unitary and $\Lambda$ is diagonal with the eigenvalues on the diagonal. + obtain ⟨Q, Λ, hQ, hΛ⟩ : ∃ Q : Matrix d d ℂ, ∃ Λ : d → ℂ, Q.conjTranspose * Q = 1 ∧ A = Q * + Matrix.diagonal Λ * Q.conjTranspose ∧ ∀ i, Λ i = Matrix.IsHermitian.eigenvalues hA i := by + refine ⟨hA.eigenvectorUnitary, fun i => (hA.eigenvalues i : ℂ), ?_, + hA.spectral_theorem, fun _ => rfl⟩ + exact Matrix.UnitaryGroup.star_mul_self hA.eigenvectorUnitary + -- Since $Q$ is unitary, we have $Q^* Q = I$, and thus $Q^* (A - \lambda_{\min} I) Q = + -- \Lambda - \lambda_{\min} I$. + have h_diag : Q.conjTranspose * (A - (iInf (Matrix.IsHermitian.eigenvalues hA)) • 1) * Q = + Matrix.diagonal (fun i => Λ i - (iInf (Matrix.IsHermitian.eigenvalues hA))) := by + simp? [ hΛ, mul_sub, sub_mul, mul_assoc, hQ ]; + simp? [ ← mul_assoc, hQ]; + ext i j; by_cases hij : i = j <;> aesop; + -- Since $Q$ is unitary, we have $Q^* (A - \lambda_{\min} I) Q = \Lambda - + -- \lambda_{\min} I$, and thus $x^* (A - \lambda_{\min} I) x = (Q^* x)^* (\Lambda - + -- \lambda_{\min} I) (Q^* x)$. + have h_quad_form : Star.star x ⬝ᵥ (A - (iInf (Matrix.IsHermitian.eigenvalues hA)) • + 1).mulVec x = Star.star (Q.conjTranspose.mulVec x) ⬝ᵥ (Matrix.diagonal (fun i => Λ i - + (iInf (Matrix.IsHermitian.eigenvalues hA)))).mulVec (Q.conjTranspose.mulVec x) := by rw [ ← h_diag ]; - simp [ Matrix.mul_assoc, Matrix.dotProduct_mulVec, mul_eq_one_comm.mp hQ]; + simp? [ Matrix.mul_assoc, Matrix.dotProduct_mulVec, mul_eq_one_comm.mp hQ]; simp only [mulVec_conjTranspose, star_star, vecMul_vecMul]; rw [ ← Matrix.mul_assoc, mul_eq_one_comm.mp hQ, one_mul ]; simp_all only [ge_iff_le, dotProduct, Pi.star_apply, RCLike.star_def, mulVec, sub_apply, smul_apply, Complex.real_smul, conjTranspose_apply, star_sum, star_mul', RingHomCompTriple.comp_apply, RingHom.id_apply]; - simp_all only [implies_true, and_self, diagonal_apply, ite_mul, zero_mul, Finset.sum_ite_eq, ↓reduceIte]; - -- Since the eigenvalues are real and the sums involving Q and x are complex, the product of a complex number and its conjugate is non-negative. + simp_all only [implies_true, and_self, diagonal_apply, ite_mul, zero_mul, Finset.sum_ite_eq, + ↓reduceIte]; + -- Since the eigenvalues are real and the sums involving Q and x are complex, the + -- product of a complex number and its conjugate is non-negative. have h_nonneg : ∀ i, 0 ≤ (∑ x_2, Q x_2 i * star (x x_2)) * (∑ x_2, star (Q x_2 i) * x x_2) := by intro i - have h_nonneg : 0 ≤ (∑ x_2, Q x_2 i * star (x x_2)) * star (∑ x_2, Q x_2 i * star (x x_2)) := by + have h_nonneg : 0 ≤ (∑ x_2, Q x_2 i * star (x x_2)) * star (∑ x_2, Q x_2 i * star (x x_2)) + := by exact mul_star_self_nonneg (∑ x_2, Q x_2 i * star (x x_2)) convert h_nonneg using 1; simp [ mul_comm, Finset.mul_sum _ _ _]; - -- Since each term in the sum is a product of a non-negative number and a non-negative eigenvalue difference, the entire sum is non-negative. - have h_sum_nonneg : ∀ i, 0 ≤ (∑ x_2, Q x_2 i * star (x x_2)) * (((↑(hA.eigenvalues i) : ℂ) - (↑(iInf hA.eigenvalues) : ℂ)) * ∑ x_2, star (Q x_2 i) * x x_2) := by + -- Since each term in the sum is a product of a non-negative number and a non-negative + -- eigenvalue difference, the entire sum is non-negative. + have h_sum_nonneg : ∀ i, 0 ≤ (∑ x_2, Q x_2 i * star (x x_2)) * (((↑(hA.eigenvalues i) : ℂ) - + (↑(iInf hA.eigenvalues) : ℂ)) * ∑ x_2, star (Q x_2 i) * x x_2) := by intro i specialize h_nonneg i - simp_all only [mul_assoc, mul_comm, mul_left_comm, RCLike.star_def] ; + simp_all only [mul_assoc, mul_comm, mul_left_comm, RCLike.star_def]; rw [ ← mul_assoc ]; exact mul_nonneg h_nonneg ( sub_nonneg_of_le <| mod_cast h_eigenvalue i ); convert! Finset.sum_nonneg fun i _ => h_sum_nonneg i; @@ -1295,11 +1420,19 @@ open MatrixOrder variable {d : Type*} [Fintype d] -lemma _shortcut_posSMulMono : PosSMulMono ℝ (Matrix d d ℂ) := - inferInstance +omit [Fintype d] in +lemma _shortcut_posSMulMono [Finite d] : PosSMulMono ℝ (Matrix d d ℂ) := by + classical + let := Fintype.ofFinite d + exact + inferInstance -lemma _shortcut_posSmulReflectLE : PosSMulReflectLE ℝ (Matrix d d ℂ) := - inferInstance +omit [Fintype d] in +lemma _shortcut_posSmulReflectLE [Finite d] : PosSMulReflectLE ℝ (Matrix d d ℂ) := by + classical + let := Fintype.ofFinite d + exact + inferInstance scoped[MatrixOrder] attribute [instance] Matrix._shortcut_posSMulMono scoped[MatrixOrder] attribute [instance] Matrix._shortcut_posSmulReflectLE @@ -1314,17 +1447,23 @@ theorem IsHermitian.spectrum_subset_Ici_of_sub {d 𝕜 : Type*} [Fintype d] [Dec intro μ hμ obtain ⟨v, hv₁, hv₂⟩ : ∃ v : d → 𝕜, v ≠ 0 ∧ x.mulVec v = μ • v := by have h_singular : ∃ v : d → 𝕜, v ≠ 0 ∧ (μ • 1 - x).mulVec v = 0 := by - simp only [spectrum.mem_iff, Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero, ne_eq, Decidable.not_not] at hμ + simp only [spectrum.mem_iff, Matrix.isUnit_iff_isUnit_det, isUnit_iff_ne_zero, ne_eq, + Decidable.not_not] at hμ convert Matrix.exists_mulVec_eq_zero_iff.mpr hμ; simp [Algebra.smul_def] refine h_singular.imp fun v h ↦ ⟨h.left, ?_⟩ - simp_all [Matrix.sub_mulVec, sub_eq_iff_eq_add, funext_iff, Matrix.mulVec, dotProduct, Matrix.one_apply] - -- Since $x - A$ is positive semidefinite, for any eigenvalue $\lambda$ of $x$, we have $\lambda \geq \min(\text{eigenvalues of } A)$. - have h_lower_bound : ∀ (v : d → 𝕜), v ≠ 0 → (star v ⬝ᵥ (x.mulVec v)) ≥ (⨅ i, (hA.eigenvalues i)) * (star v ⬝ᵥ v) := by + simp_all [Matrix.sub_mulVec, sub_eq_iff_eq_add, funext_iff, Matrix.mulVec, dotProduct, + Matrix.one_apply] + -- Since $x - A$ is positive semidefinite, for any eigenvalue $\lambda$ of $x$, we have + -- $\lambda \geq \min(\text{eigenvalues of } A)$. + have h_lower_bound : ∀ (v : d → 𝕜), v ≠ 0 → (star v ⬝ᵥ (x.mulVec v)) ≥ (⨅ i, (hA.eigenvalues + i)) * (star v ⬝ᵥ v) := by intro v hv_nonzero have h_eigenvalue : (star v ⬝ᵥ (A.mulVec v)) ≥ (⨅ i, (hA.eigenvalues i)) * (star v ⬝ᵥ v) := by - have h_expand : (star v ⬝ᵥ (A.mulVec v)) = ∑ i, (hA.eigenvalues i) * (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) := by - have h_decomp : A = ∑ i, (hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k)))) := by + have h_expand : (star v ⬝ᵥ (A.mulVec v)) = ∑ i, (hA.eigenvalues i) * (star + (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) := by + have h_decomp : A = ∑ i, (hA.eigenvalues i) • (Matrix.of (fun j k => + (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k)))) := by convert Matrix.IsHermitian.spectral_theorem hA using 1; ext i j simp only [RCLike.star_def, Matrix.smul_of, Matrix.sum_apply, Matrix.of_apply, @@ -1332,26 +1471,43 @@ theorem IsHermitian.spectrum_subset_Ici_of_sub {d 𝕜 : Type*} [Fintype d] [Dec Matrix.IsHermitian.eigenvectorUnitary_apply, mul_ite, mul_zero, Finset.sum_ite_eq', Finset.mem_univ, ↓reduceIte, Matrix.star_apply, Unitary.conjStarAlgAut_apply] - simp [ mul_comm, mul_left_comm, Algebra.smul_def ] + simp? [ mul_comm, mul_left_comm, Algebra.smul_def ] congr! 1 simp [Algebra.algebraMap_eq_smul_one] -- Substitute the decomposition of $A$ into the expression $(star v ⬝ᵥ (A.mulVec v))$. - have h_subst : (star v ⬝ᵥ (A.mulVec v)) = ∑ i, (hA.eigenvalues i) * (star v ⬝ᵥ (Matrix.mulVec (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k)))) v)) := by - -- Substitute the decomposition of $A$ into the expression $(star v ⬝ᵥ (A.mulVec v))$ and use the linearity of matrix multiplication. - have h_subst : (star v ⬝ᵥ (A.mulVec v)) = (star v ⬝ᵥ ((∑ i, (hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k))))).mulVec v)) := by + have h_subst : (star v ⬝ᵥ (A.mulVec v)) = ∑ i, (hA.eigenvalues i) * (star v ⬝ᵥ + (Matrix.mulVec (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star + (hA.eigenvectorBasis i k)))) v)) := by + -- Substitute the decomposition of $A$ into the expression $(star v ⬝ᵥ + -- (A.mulVec v))$ and use the linearity of matrix multiplication. + have h_subst : (star v ⬝ᵥ (A.mulVec v)) = (star v ⬝ᵥ ((∑ i, (hA.eigenvalues i) • + (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i + k))))).mulVec v)) := by rw [ ← h_decomp ]; - -- By the linearity of matrix multiplication and the dot product, we can distribute the sum over the dot product. - have h_distribute : (star v ⬝ᵥ (∑ i, (hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k))))).mulVec v) = ∑ i, (star v ⬝ᵥ ((hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k))))).mulVec v) := by - -- By the linearity of matrix multiplication and the dot product, we can distribute the sum over the dot product. This follows from the fact that matrix multiplication is linear. - have h_distribute : ∀ (M N : Matrix d d 𝕜) (v : d → 𝕜), Star.star v ⬝ᵥ (M + N).mulVec v = Star.star v ⬝ᵥ M.mulVec v + Star.star v ⬝ᵥ N.mulVec v := by + -- By the linearity of matrix multiplication and the dot product, we can + -- distribute the sum over the dot product. + have h_distribute : (star v ⬝ᵥ (∑ i, (hA.eigenvalues i) • (Matrix.of (fun j k => + (hA.eigenvectorBasis i j) * (star (hA.eigenvectorBasis i k))))).mulVec v) = ∑ i, + (star v ⬝ᵥ ((hA.eigenvalues i) • (Matrix.of (fun j k => (hA.eigenvectorBasis i j) + * (star (hA.eigenvectorBasis i k))))).mulVec v) := by + -- By the linearity of matrix multiplication and the dot product, we can + -- distribute the sum over the dot product. This follows from the fact + -- that matrix multiplication is linear. + have h_distribute : ∀ (M N : Matrix d d 𝕜) (v : d → 𝕜), Star.star v ⬝ᵥ (M + + N).mulVec v = Star.star v ⬝ᵥ M.mulVec v + Star.star v ⬝ᵥ N.mulVec v := by simp [ Matrix.add_mulVec, dotProduct_add ]; - -- By induction on the number of terms in the sum, we can apply the distributive property repeatedly. - have h_induction : ∀ (n : ℕ) (M : Fin n → Matrix d d 𝕜) (v : d → 𝕜), Star.star v ⬝ᵥ (∑ i, M i).mulVec v = ∑ i, Star.star v ⬝ᵥ (M i).mulVec v := by + -- By induction on the number of terms in the sum, we can apply the + -- distributive property repeatedly. + have h_induction : ∀ (n : ℕ) (M : Fin n → Matrix d d 𝕜) (v : d → 𝕜), Star.star v ⬝ᵥ + (∑ i, M i).mulVec v = ∑ i, Star.star v ⬝ᵥ (M i).mulVec v := by intro n M v induction n · simp [*] · simp [Fin.sum_univ_succ, *] - convert h_induction ( Fintype.card d ) ( fun i => Matrix.of ( hA.eigenvalues ( Fintype.equivFin d |>.symm i ) • fun j k => hA.eigenvectorBasis ( Fintype.equivFin d |>.symm i ) j * starRingEnd 𝕜 ( hA.eigenvectorBasis ( Fintype.equivFin d |>.symm i ) k ) ) ) v using 1; + convert h_induction ( Fintype.card d ) ( fun i => Matrix.of ( hA.eigenvalues ( + Fintype.equivFin d |>.symm i ) • fun j k => hA.eigenvectorBasis ( + Fintype.equivFin d |>.symm i ) j * starRingEnd 𝕜 ( hA.eigenvectorBasis ( + Fintype.equivFin d |>.symm i ) k ) ) ) v using 1; · rw [ ← Equiv.sum_comp ( Fintype.equivFin d ) ]; simp [ Fintype.equivFin ]; · rw [ ← Equiv.sum_comp ( Fintype.equivFin d ) ]; @@ -1365,22 +1521,32 @@ theorem IsHermitian.spectrum_subset_Ici_of_sub {d 𝕜 : Type*} [Fintype d] [Dec simp only [dotProduct, Pi.star_apply, RCLike.star_def, mul_comm, mul_assoc, Matrix.mulVec, Matrix.of_apply, mul_eq_mul_left_iff, map_eq_zero]; simp [ mul_comm, mul_left_comm, Finset.mul_sum _ _ _ ]; - -- Since $\lambda_i \geq \inf(\text{eigenvalues of } A)$ for all $i$, we can bound each term in the sum. - have h_bound : ∀ i, (hA.eigenvalues i) * (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) ≥ (⨅ i, (hA.eigenvalues i)) * (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) := by + -- Since $\lambda_i \geq \inf(\text{eigenvalues of } A)$ for all $i$, we can bound + -- each term in the sum. + have h_bound : ∀ i, (hA.eigenvalues i) * (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ + (hA.eigenvectorBasis i)) ≥ (⨅ i, (hA.eigenvalues i)) * (star (hA.eigenvectorBasis i) + ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) := by intro i have h_eigenvalue_bound : (hA.eigenvalues i) ≥ (⨅ i, (hA.eigenvalues i)) := ciInf_le (Set.finite_range _).bddBelow _ - -- Since the product of the inner products is real and non-negative, multiplying both sides of the inequality by this product preserves the inequality. - have h_nonneg : 0 ≤ (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) := by - -- Since the inner product is conjugate symmetric, we have star v ⬝ᵥ (hA.eigenvectorBasis i) = conjugate(star (hA.eigenvectorBasis i) ⬝ᵥ v). - have h_conj_symm : star v ⬝ᵥ (hA.eigenvectorBasis i) = star (star (hA.eigenvectorBasis i) ⬝ᵥ v) := by + -- Since the product of the inner products is real and non-negative, multiplying + -- both sides of the inequality by this product preserves the inequality. + have h_nonneg : 0 ≤ (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ + (hA.eigenvectorBasis i)) := by + -- Since the inner product is conjugate symmetric, we have star v ⬝ᵥ + -- (hA.eigenvectorBasis i) = conjugate(star (hA.eigenvectorBasis i) ⬝ᵥ v). + have h_conj_symm : star v ⬝ᵥ (hA.eigenvectorBasis i) = star (star (hA.eigenvectorBasis + i) ⬝ᵥ v) := by simp [ dotProduct, mul_comm]; rw [ h_conj_symm ]; exact mul_star_self_nonneg (star (hA.eigenvectorBasis i) ⬝ᵥ v); norm_num [ mul_assoc ]; exact mul_le_mul_of_nonneg_right ( mod_cast h_eigenvalue_bound ) h_nonneg; - -- Since $\sum_{i} (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) = star v ⬝ᵥ v$, we can factor out $(⨅ i, (hA.eigenvalues i))$ from the sum. - have h_sum : ∑ i, (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis i)) = star v ⬝ᵥ v := by + -- Since $\sum_{i} (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ + -- (hA.eigenvectorBasis i)) = star v ⬝ᵥ v$, we can factor out $(⨅ i, (hA.eigenvalues + -- i))$ from the sum. + have h_sum : ∑ i, (star (hA.eigenvectorBasis i) ⬝ᵥ v) * (star v ⬝ᵥ (hA.eigenvectorBasis + i)) = star v ⬝ᵥ v := by have h_sum : ∑ i, (star (hA.eigenvectorBasis i) ⬝ᵥ v) • (hA.eigenvectorBasis i) = v := by have := hA.eigenvectorBasis.sum_repr (WithLp.toLp 2 v); convert this using 1; @@ -1397,19 +1563,21 @@ theorem IsHermitian.spectrum_subset_Ici_of_sub {d 𝕜 : Type*} [Fintype d] [Dec · intro h; apply_fun WithLp.ofLp at h; simpa using h exact key _ _ _ -- Taking the inner product of both sides of h_sum with star v, we get the desired equality. - have h_inner : star v ⬝ᵥ (∑ i, (star (hA.eigenvectorBasis i) ⬝ᵥ v) • (hA.eigenvectorBasis i)) = star v ⬝ᵥ v := by + have h_inner : star v ⬝ᵥ (∑ i, (star (hA.eigenvectorBasis i) ⬝ᵥ v) • + (hA.eigenvectorBasis i)) = star v ⬝ᵥ v := by congr 1 simp_rw [← WithLp.ofLp_smul, ← WithLp.ofLp_sum, h_sum] convert h_inner using 1; - simp [ dotProduct, Finset.mul_sum _ _ _ ]; - exact Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ); + simp? [ dotProduct, Finset.mul_sum _ _ _ ]; + exact Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ + _ => by ring ); rw [ h_expand ]; - refine' le_trans _ ( Finset.sum_le_sum fun i _ => h_bound i ); + refine le_trans ?_ ( Finset.sum_le_sum fun i _ => h_bound i ); simp only [ mul_assoc]; rw [ ← Finset.mul_sum _ _ _, h_sum ]; rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hl have := hl.2 v - simp [Matrix.sub_mulVec] at this + simp? [Matrix.sub_mulVec] at this exact le_trans h_eigenvalue this; change (⨅ i, hA.eigenvalues i) ≤ μ have := h_lower_bound v hv₁ @@ -1417,13 +1585,16 @@ theorem IsHermitian.spectrum_subset_Ici_of_sub {d 𝕜 : Type*} [Fintype d] [Dec dotProduct_smul]; simp_all only [dotProduct, mul_comm, RCLike.mul_conj]; rw [ Algebra.smul_def ] at this; - -- Since the sum of the squares of the norms of v is positive, we can divide both sides of the inequality by it. + -- Since the sum of the squares of the norms of v is positive, we can divide both sides of + -- the inequality by it. have h_sum_pos : 0 < ∑ x : d, (‖v x‖ : ℝ) ^ 2 := by contrapose! hv₁; - simp_all only [funext_iff, Pi.zero_apply, not_forall, forall_exists_index, Matrix.mulVec, Pi.smul_apply] + simp_all only [funext_iff, Pi.zero_apply, not_forall, forall_exists_index, Matrix.mulVec, + Pi.smul_apply] intro i rw [← norm_eq_zero] - simpa [ sq_nonneg ] using le_antisymm ( le_trans ( Finset.single_le_sum ( fun a _ => sq_nonneg ( ‖v a‖ ) ) ( Finset.mem_univ i ) ) hv₁ ) ( sq_nonneg ( ‖v i‖ ) ) + simpa [ sq_nonneg ] using le_antisymm ( le_trans ( Finset.single_le_sum ( fun a _ => + sq_nonneg ( ‖v a‖ ) ) ( Finset.mem_univ i ) ) hv₁ ) ( sq_nonneg ( ‖v i‖ ) ) norm_cast at this; nlinarith @@ -1454,7 +1625,8 @@ theorem IsHermitian.spectrum_subset_of_mem_Icc {d 𝕜 : Type*} [Fintype d] [Dec exact Set.subset_inter (hA.spectrum_subset_Ici_of_sub hl) (hB.spectrum_subset_Iic_of_sub hr) /-- -The right partial trace of a matrix is equal to the left partial trace of the matrix reindexed by swapping the tensor factors. +The right partial trace of a matrix is equal to the left partial trace of the matrix reindexed + by swapping the tensor factors. -/ theorem traceRight_eq_traceLeft_reindex {n m R : Type*} [Fintype m] [AddCommMonoid R] (M : Matrix (n × m) (n × m) R) : @@ -1529,6 +1701,7 @@ section finprod variable {ι : Type*} {d : ι → Type*} [fι : Fintype ι] variable {R : Type*} +/-- The product of a family of matrices on the dependent product of their index types. -/ def piProd [CommMonoid R] (A : ∀ i, Matrix (d i) (d i) R) : Matrix (∀ i, d i) (∀ i, d i) R := Matrix.of (fun j k : (∀ i, d i) ↦ ∏ i, A i (j i) (k i)) @@ -1537,8 +1710,9 @@ variable {A : ∀ i, Matrix (d i) (d i) R} theorem IsHermitian.piProd [CommSemiring R] [StarRing R] (hA : ∀ i, (A i).IsHermitian) : (piProd A).IsHermitian := by ext j k - simp [Matrix.piProd] - exact Finset.prod_congr rfl fun i _ => by simpa using congr_fun ( congr_fun ( hA i ) ( j i ) ) ( k i ) ; + simp? [Matrix.piProd] + exact Finset.prod_congr rfl fun i _ => by + simpa using congr_fun (congr_fun (hA i) (j i)) (k i) variable [DecidableEq ι] [∀ i, Fintype (d i)] --[∀ i, DecidableEq (d i)] @@ -1547,12 +1721,18 @@ theorem trace_piProd [CommSemiring R] : symm simp [trace, piProd, Fintype.prod_sum] +omit [DecidableEq ι] [∀ i, Fintype (d i)] in open ComplexOrder MatrixOrder in -theorem PosSemidef.piProd [RCLike R] (hA : ∀ i, (A i).PosSemidef) : +theorem PosSemidef.piProd [∀ i, Finite (d i)] [RCLike R] (hA : ∀ i, (A i).PosSemidef) : (piProd A).PosSemidef := by - -- Let B i be the square root of A i. Let BigB be the pi-product of B i. Show that BigB.conjTranspose * BigB equals the pi-product of A i using Fintype.prod_sum. Then use Matrix.PosSemidef.conjTranspose_mul_self to conclude the proof. + classical + let (i : ι) := Fintype.ofFinite (d i) + -- Let B i be the square root of A i. Let BigB be the pi-product of B i. Show that + -- BigB.conjTranspose * BigB equals the pi-product of A i using Fintype.prod_sum. Then use + -- Matrix.PosSemidef.conjTranspose_mul_self to conclude the proof. obtain ⟨B, hB⟩ : ∃ B : ∀ i, Matrix (d i) (d i) R, ∀ i, (A i) = B i * star (B i) := by - -- By definition of positive semi-definite matrices, each $A_i$ can be written as $B_i^* B_i$ for some matrix $B_i$. + -- By definition of positive semi-definite matrices, each $A_i$ can be written as $B_i^* + -- B_i$ for some matrix $B_i$. have h_decomp : ∀ i, ∃ B : Matrix (d i) (d i) R, A i = B * star B := by intro i obtain ⟨B, hB⟩ : ∃ B : Matrix (d i) (d i) R, A i = B.conjTranspose * B := by @@ -1562,15 +1742,19 @@ theorem PosSemidef.piProd [RCLike R] (hA : ∀ i, (A i).PosSemidef) : use B.conjTranspose; convert hB using 1; simp [ Matrix.star_eq_conjTranspose ]; - exact ⟨ fun i => Classical.choose ( h_decomp i ), fun i => Classical.choose_spec ( h_decomp i ) ⟩; - have hBigB_conjTranspose_mul_BigB : Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i * star (B i)) (j i) (k i)) = Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i) (j i) (k i)) * star (Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i) (j i) (k i))) := by - ext j k; simp [ Matrix.mul_apply] + exact ⟨ fun i => Classical.choose ( h_decomp i ), fun i => Classical.choose_spec ( h_decomp + i ) ⟩; + have hBigB_conjTranspose_mul_BigB : Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i * star (B i)) + (j i) (k i)) = Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i) (j i) (k i)) * star + (Matrix.of (fun j k : (∀ i, d i) => ∏ i, (B i) (j i) (k i))) := by + ext j k; simp? [ Matrix.mul_apply] simp only [Finset.prod_sum, ← Finset.prod_mul_distrib]; - refine' Finset.sum_bij ( fun p hp => fun i => p i ( Finset.mem_univ i ) ) _ _ _ _ <;> simp +decide; + refine Finset.sum_bij ( fun p hp => fun i => p i ( Finset.mem_univ i ) ) ?_ ?_ ?_ ?_ <;> + simp? +decide; · simp [ funext_iff ]; · exact fun b => ⟨ fun i _ => b i, rfl ⟩; simp only [Matrix.posSemidef_iff_dotProduct_mulVec] at hA ⊢ - simp_all [Matrix.piProd] + simp_all? [Matrix.piProd] constructor · ext1 simp [Matrix.mul_apply, mul_comm] @@ -1583,20 +1767,23 @@ theorem PosSemidef.piProd [RCLike R] (hA : ∀ i, (A i).PosSemidef) : simp only [mul_apply, of_apply, star_apply, star_prod, RCLike.star_def, Finset.mul_sum _ _ _, mul_left_comm, mulVec, dotProduct, mul_comm, map_sum, map_mul, map_prod, RingHomCompTriple.comp_apply, RingHom.id_apply, mul_assoc]; - exact Finset.sum_congr rfl fun _ _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ) + exact Finset.sum_congr rfl fun _ _ => Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ + => Finset.sum_congr rfl fun _ _ => by ring ) end finprod --TODO: Can this be used for `Matrix.reindex_eq_conj` cleanup? theorem submatrix_eq_mul_mul {d d₂ d₃ R : Type*} [DecidableEq d] [Fintype d] [Semiring R] (A : Matrix d d R) (e : d₂ → d) (f : d₃ → d) : - A.submatrix e f = (submatrix (α := R) 1 e id : Matrix d₂ d R) * A * (submatrix (α := R) 1 id f) := by + A.submatrix e f = (submatrix (α := R) 1 e id : Matrix d₂ d R) * A * (submatrix (α := R) 1 id + f) := by rw [show id = Equiv.refl d by rfl, Matrix.mul_submatrix_one, Matrix.one_submatrix_mul] simp open scoped Matrix Kronecker in /-- -The conjugate of a Kronecker product by a Kronecker product is the Kronecker product of the conjugates (for matrices). +The conjugate of a Kronecker product by a Kronecker product is the Kronecker product of the + conjugates (for matrices). -/ lemma kronecker_conj_eq {m n p q α : Type*} [CommSemiring α] [StarRing α] [Fintype m] [Fintype n] (A : Matrix m m α) (B : Matrix n n α) (C : Matrix p m α) (D : Matrix q n α) : diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean index b9864512d5..b933a50ed9 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Misc.lean @@ -8,12 +8,15 @@ module public import Mathlib.Analysis.SpecialFunctions.Log.Basic public import Mathlib.Order.CompletePartialOrder +/-! Auxiliary finite-dimensional linear algebra and functional calculus lemmas. -/ + @[expose] public section --Can this be rewritten more generally? For `finiteness` to work, I don't know how. --PR'ed in #33105 @[aesop (rule_sets := [finiteness]) apply 50%] -theorem ite_eq_top {α : Type*} [Top α] (h : Prop) [Decidable h] {x y : α} (hx : x ≠ ⊤) (hy : y ≠ ⊤) : +theorem ite_eq_top {α : Type*} [Top α] (h : Prop) [Decidable h] {x y : α} (hx : x ≠ ⊤) (hy : y ≠ + ⊤) : (if h then x else y) ≠ ⊤ := by split <;> assumption @@ -34,7 +37,8 @@ theorem subtype_val_iSup' {ι α : Type*} [ConditionallyCompleteLattice α] {s : ⨆ i, (⟨f i, h i⟩ : ↑s) = ⟨⨆ i, f i, by sorry⟩ := by rw [Subtype.eq_iff, subtype_val_iSup] ``` -Sadly, though, there's a "diamond" and we need it with the other data (the one we specify more narrowly +Sadly, though, there's a "diamond" and we need it with the other data (the one we specify more +narrowly below). -/ variable {ι α : Type*} [i : Nonempty ι] [ConditionallyCompleteLattice α] @@ -102,8 +106,10 @@ theorem csInf_mul_nonneg {s t : Set ℝ} intro ε hε obtain ⟨x, hx₁, hx₂, y, hy₁, hy₂⟩ := h_eps ε hε exact ⟨x, hx₁, y, hy₁, by nlinarith [hs₁ x hx₁, ht₁ y hy₁]⟩ - have h_lim : Filter.Tendsto (fun ε => (a + ε) * (b + ε)) (nhdsWithin 0 (Set.Ioi 0)) (nhds (a * b)) := by - exact tendsto_nhdsWithin_of_tendsto_nhds (Continuous.tendsto' (by continuity) _ _ (by norm_num)) + have h_lim : Filter.Tendsto (fun ε => (a + ε) * (b + ε)) (nhdsWithin 0 (Set.Ioi 0)) (nhds (a + * b)) := by + exact tendsto_nhdsWithin_of_tendsto_nhds (Continuous.tendsto' (by continuity) _ _ (by + norm_num)) apply le_of_tendsto_of_tendsto tendsto_const_nhds h_lim filter_upwards [self_mem_nhdsWithin] with ε hε specialize h_prod_eps ε hε @@ -122,7 +128,8 @@ theorem csInf_mul_nonneg {s t : Set ℝ} · exact hs₁ x hx /-- -If two functions from finite types have the same multiset of values, there exists a bijection between the domains that commutes with the functions. +If two functions from finite types have the same multiset of values, there exists a bijection + between the domains that commutes with the functions. -/ lemma Multiset.map_univ_eq_iff {α β : Type*} [Fintype α] (f g : α → β) : Multiset.map f Finset.univ.val = Multiset.map g Finset.univ.val ↔ ∃ (e : α ≃ α), f = g ∘ e := by @@ -131,19 +138,21 @@ lemma Multiset.map_univ_eq_iff {α β : Type*} [Fintype α] (f g : α → β) : classical -- Since these two multisets are equal, their elements must be equal up to permutation. have h_perm : ∃ e : α ≃ α, ∀ x, f x = g (e x) := by - have h_count_eq : ∀ y : β, Finset.card (Finset.filter (fun x => f x = y) Finset.univ) = Finset.card (Finset.filter (fun x => g x = y) Finset.univ) := by + have h_count_eq : ∀ y : β, Finset.card (Finset.filter (fun x => f x = y) Finset.univ) = + Finset.card (Finset.filter (fun x => g x = y) Finset.univ) := by intro y; replace a := congr_arg ( fun m => m.count y ) a; - simp_all ( config := { decide := Bool.true } ) [ Multiset.count_map ]; - simp_all [ eq_comm ] + simp_all? ( config := { decide := Bool.true } ) [ Multiset.count_map ]; + simp_all? [ eq_comm ] exact a have h_perm : ∀ y : β, ∃ e : { x : α // f x = y } ≃ { x : α // g x = y }, True := by intro y simp_all only [exists_const_iff, and_true] exact ⟨ Fintype.equivOfCardEq <| by simpa [ Fintype.card_subtype ] using h_count_eq y ⟩; choose e he using h_perm; - refine' ⟨ _, _ ⟩; - exact ( Equiv.sigmaFiberEquiv f ).symm.trans ( Equiv.sigmaCongrRight e ) |> Equiv.trans <| Equiv.sigmaFiberEquiv g; + refine ⟨ ?_, ?_ ⟩; + · exact (Equiv.sigmaFiberEquiv f).symm.trans (Equiv.sigmaCongrRight e) |>.trans + (Equiv.sigmaFiberEquiv g) intro x specialize e ( f x ) rename_i e_1 @@ -155,26 +164,32 @@ lemma Multiset.map_univ_eq_iff {α β : Type*} [Fintype α] (f g : α → β) : obtain ⟨w, h⟩ := a subst h simp_all only [Function.comp_apply, Finset.univ] - -- Since $w$ is a bijection, the multiset of $w(x)$ for $x$ in the original multiset is just a permutation of the original multiset. - have h_perm : Multiset.map (fun x => w x) (Finset.val Fintype.elems) = Finset.val Fintype.elems := by + -- Since $w$ is a bijection, the multiset of $w(x)$ for $x$ in the original multiset is + -- just a permutation of the original multiset. + have h_perm : Multiset.map (fun x => w x) (Finset.val Fintype.elems) = Finset.val + Fintype.elems := by exact Multiset.map_univ_val_equiv w; conv_rhs => rw [ ← h_perm ]; simp +zetaDelta at * /-- -If two functions from finite types have the same multiset of values, there exists a bijection between the domains that commutes with the functions. +If two functions from finite types have the same multiset of values, there exists a bijection + between the domains that commutes with the functions. -/ -lemma exists_equiv_of_multiset_map_eq {α β γ : Type*} [Fintype α] [Fintype β] [DecidableEq γ] +lemma exists_equiv_of_multiset_map_eq {α β γ : Type*} [Fintype α] [Fintype β] (f : α → γ) (g : β → γ) (h : Multiset.map f Finset.univ.val = Multiset.map g Finset.univ.val) : ∃ e : α ≃ β, f = g ∘ e := by - -- Since the multisets of values are equal, the cardinalities of the domains must be equal (as the multiset size is the cardinality of the domain). Thus there exists a bijection `σ : α ≃ β`. - obtain ⟨σ, hσ⟩ : ∃ σ : α ≃ β, Multiset.map f Finset.univ.val = Multiset.map (g ∘ σ) Finset.univ.val := by + -- Since the multisets of values are equal, the cardinalities of the domains must be equal + -- (as the multiset size is the cardinality of the domain). Thus there exists a bijection `σ + -- : α ≃ β`. + obtain ⟨σ, hσ⟩ : ∃ σ : α ≃ β, Multiset.map f Finset.univ.val = Multiset.map (g ∘ σ) + Finset.univ.val := by have h_card : Fintype.card α = Fintype.card β := by simpa using congr_arg Multiset.card h; obtain σ := Fintype.equivOfCardEq h_card use σ have h_multiset_eq : Multiset.map g Finset.univ.val = Multiset.map (g ∘ σ) Finset.univ.val := by - rw [ ← Multiset.map_univ_val_equiv σ ] ; + rw [ ← Multiset.map_univ_val_equiv σ ]; rw [ Multiset.map_map ] exact h.trans h_multiset_eq; -- By `Multiset.map_univ_eq_iff`, there exists `e' : α ≃ α` such that `f = (g ∘ σ) ∘ e'`. diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean index e58eebf8a2..6b2d9a821e 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean @@ -9,5 +9,6 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes /-! # Tactic -Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. +Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the + trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. -/ diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean index 23e12d87fe..0ae846a846 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean @@ -17,7 +17,8 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner /-! # Non-vacuity of the Peirce layer, on the Hermitian-matrix carrier -`EuclideanJordan/Peirce.lean` and `EuclideanJordan/PeirceMul.lean` are stated over an abstract real commutative +`EuclideanJordan/Peirce.lean` and `EuclideanJordan/PeirceMul.lean` are stated over an abstract + real commutative Jordan algebra and are *conditional* throughout: every theorem assumes `c ∘ c = c`, and the sharper ones assume an element with `c ∘ y = ½ y`. Conditional theorems are worth exactly as much as their hypotheses are satisfiable. This file is the check that they are. @@ -34,18 +35,21 @@ they are interchangeable for a commutative product, and `IsScalarTower` is the o carrier supplies. **2. The `1/2`-eigenspace is not zero.** This is the part that could have gone wrong -silently. Every rule in `EuclideanJordan/PeirceMul.lean` mentioning `J_{1/2}` would be *vacuously true* +silently. Every rule in `EuclideanJordan/PeirceMul.lean` mentioning `J_{1/2}` would be + *vacuously true* on a carrier where the half-space is trivial — and the half-space **is** trivial for the two idempotents one reaches for first, `0` and `1`. So a witness is exhibited: `cWit` is the rank-one projection `diag(1,0)`, `xWit` is the off-diagonal `[[0,1],[1,0]]`, and `cWit_mul_xWit` proves `cWit ∘ xWit = ½ · xWit` with `xWit ≠ 0`. -**3. `HermitianMat` is formally real** (§4 below), so `EuclideanJordan/FormallyReal.lean`'s no-nilpotents +**3. `HermitianMat` is formally real** (§4 below), so `EuclideanJordan/FormallyReal.lean`'s + no-nilpotents theorem is live rather than conditional on an uninhabited hypothesis. **4. The diagonal matrix units are a complete Jordan frame** (§5 below), witnessing `IsOrthIdemFamily` — which had **no** carrier here until then, leaving every theorem of -`EuclideanJordan/Frame.lean` and `EuclideanJordan/Block.lean` conditional on a structure nothing was known to satisfy. +`EuclideanJordan/Frame.lean` and `EuclideanJordan/Block.lean` conditional on a structure nothing + was known to satisfy. ★ **Scope.** This is non-vacuity, not coverage: it shows the Peirce hypotheses have a model with all three components nonzero, and nothing more. The rank-two carrier is used because it @@ -93,14 +97,15 @@ theorem cWit_idem : cWit * cWit = cWit := by apply HermitianMat.ext rw [mul_eq_symmMul, HermitianMat.symmMul_toMat, cWit_mat] ext i j - fin_cases i <;> fin_cases j <;> simp <;> norm_num + fin_cases i <;> fin_cases j <;> simp + norm_num /-- **The half-space is inhabited by a nonzero element**, so every `J_{1/2}` rule in `EuclideanJordan/PeirceMul.lean` has content on this carrier. -/ theorem cWit_mul_xWit : cWit * xWit = (2 : ℝ)⁻¹ • xWit := by apply HermitianMat.ext rw [mul_eq_symmMul, HermitianMat.symmMul_toMat, cWit_mat, xWit_mat] - show _ = (2 : ℝ)⁻¹ • xWit.mat + change _ = (2 : ℝ)⁻¹ • xWit.mat rw [xWit_mat] ext i j fin_cases i <;> fin_cases j <;> simp [Matrix.vecMul_diagonal] @@ -138,7 +143,8 @@ theorem witness_half_attained : ∃ y : H2, y ≠ 0 ∧ cWit * y = (2 : ℝ)⁻ /-! ### 4. Formal reality on the Hermitian-matrix carrier -★★★ **This section closes the exposure `EuclideanJordan/FormallyReal.lean` declares in its own docstring**: +★★★ **This section closes the exposure `EuclideanJordan/FormallyReal.lean` declares in its own + docstring**: that `IsFormallyReal` had no carrier here, so every theorem depending on it was conditional on an uninhabited hypothesis. @@ -169,9 +175,11 @@ private theorem inner_mul_self_one (A : HermitianMat d 𝕜) : rw [HermitianMat.inner_def, HermitianMat.inner_def, mul_eq_symmMul, HermitianMat.symmMul_self] simp +omit [DecidableEq d] in /-- **`H_d(𝕜)` is formally real.** -/ instance instIsFormallyReal : EuclideanJordan.IsFormallyReal (HermitianMat d 𝕜) where eq_zero_of_sum_mul_self := by + let : DecidableEq d := Classical.decEq d intro ι s f hsum i hi have h0 : ∑ j ∈ s, inner ℝ (f j * f j) (1 : HermitianMat d 𝕜) = 0 := by rw [← inner_sum_left_one, hsum] @@ -201,12 +209,14 @@ end FormallyReal /-! ### 5. A Jordan frame on the carrier ★★ **This closes the last vacuity exposure in the EJA layer.** `IsOrthIdemFamily` -(`EuclideanJordan/Frame.lean`) had **no witness**, so every theorem of `EuclideanJordan/Frame.lean` and +(`EuclideanJordan/Frame.lean`) had **no witness**, so every theorem of + `EuclideanJordan/Frame.lean` and `EuclideanJordan/Block.lean` was conditional on a structure nothing was known to satisfy — the same exposure that section 4 closed for `IsFormallyReal`. The diagonal matrix units supply it. ★ Completeness (`∑ i, p i = 1`) is proved here even though **no theorem in the abstract layer -assumes it** — deliberately, per `EuclideanJordan/Frame.lean`'s docstring, since completeness is what the +assumes it** — deliberately, per `EuclideanJordan/Frame.lean`'s docstring, since completeness is + what the spectral theorem produces rather than what the Peirce theory needs. Having it on the carrier shows the abstract results are not being kept general by weakening past what the intended model satisfies. -/ @@ -232,13 +242,15 @@ theorem diagFrame_orthIdem : rw [mul_eq_symmMul, HermitianMat.symmMul_toMat, diagFrame_mat, Matrix.diagonal_mul_diagonal] ext a b - by_cases h : a = b <;> simp [Matrix.smul_apply, h] <;> split_ifs <;> norm_num + by_cases h : a = b <;> simp [Matrix.smul_apply, h] + split_ifs <;> norm_num orth i j hij := by apply HermitianMat.ext rw [mul_eq_symmMul, HermitianMat.symmMul_toMat, diagFrame_mat, diagFrame_mat, Matrix.diagonal_mul_diagonal, Matrix.diagonal_mul_diagonal] ext a b - by_cases h : a = b <;> simp [Matrix.smul_apply, h] <;> split_ifs with h1 h2 <;> simp_all + by_cases h : a = b <;> simp [Matrix.smul_apply, h] + split_ifs with h1 h2 <;> simp_all /-- The frame is **complete**: the matrix units sum to the identity. -/ theorem diagFrame_sum : (∑ i, diagFrame (d := d) i) = 1 := by diff --git a/LeanPool/EuclideanJordan/SpectralSolution.lean b/LeanPool/EuclideanJordan/SpectralSolution.lean index 71575b57e7..72af4ee7f8 100644 --- a/LeanPool/EuclideanJordan/SpectralSolution.lean +++ b/LeanPool/EuclideanJordan/SpectralSolution.lean @@ -12,7 +12,7 @@ import Mathlib.Analysis.InnerProductSpace.Basic import Mathlib.LinearAlgebra.FiniteDimensional.Defs import Mathlib.LinearAlgebra.BilinearMap import Mathlib.Algebra.BigOperators.Fin -import Mathlib.Data.Real.Basic +import Mathlib.Basic.Real.Basic import LeanPool.EuclideanJordan.EuclideanJordan.Spectral /-! diff --git a/LeanPool/EuclideanJordan/StructureSolution.lean b/LeanPool/EuclideanJordan/StructureSolution.lean index b387ab9137..6eb4c2c62c 100644 --- a/LeanPool/EuclideanJordan/StructureSolution.lean +++ b/LeanPool/EuclideanJordan/StructureSolution.lean @@ -12,7 +12,7 @@ import Mathlib.Algebra.Jordan.Basic import Mathlib.Algebra.BigOperators.Fin import Mathlib.Algebra.DirectSum.Module import Mathlib.Analysis.InnerProductSpace.Basic -import Mathlib.Data.Real.Basic +import Mathlib.Basic.Real.Basic import Mathlib.Data.Sym.Sym2 import Mathlib.LinearAlgebra.BilinearMap import Mathlib.LinearAlgebra.Dimension.Finrank @@ -284,7 +284,8 @@ Everything the two statements mention is defined below from Mathlib alone: the c ## This file -Repeats the definitions and the two theorem statements of Part II of `StructureChallenge.lean` verbatim, +Repeats the definitions and the two theorem statements of Part II of `StructureChallenge.lean` + verbatim, imports the reference library, and discharges them from `EuclideanJordan.frameBlock_isInternal` (`EuclideanJordan/FramePeirce.lean`) and `EuclideanJordan.finrank_frameBlock_diag` (`EuclideanJordan/FramePeirceMul.lean`). diff --git a/LeanPool/EuclideanJordan/TraceFormSolution.lean b/LeanPool/EuclideanJordan/TraceFormSolution.lean index b63675c632..5bb672569f 100644 --- a/LeanPool/EuclideanJordan/TraceFormSolution.lean +++ b/LeanPool/EuclideanJordan/TraceFormSolution.lean @@ -13,7 +13,7 @@ import Mathlib.Algebra.BigOperators.Fin import Mathlib.LinearAlgebra.BilinearMap import Mathlib.LinearAlgebra.Trace import Mathlib.LinearAlgebra.FiniteDimensional.Defs -import Mathlib.Data.Real.Basic +import Mathlib.Basic.Real.Basic import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm /-! diff --git a/LeanPool/projects.yml b/LeanPool/projects.yml index aef0034a4d..bb249c0eae 100644 --- a/LeanPool/projects.yml +++ b/LeanPool/projects.yml @@ -9967,8 +9967,7 @@ projects: - '90C35' - '05C21' - slug: euclidean-jordan - title: 'Euclidean Jordan algebras: power associativity, the spectral theorem, the trace - form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition' + title: Euclidean Jordan algebras and the frame Peirce decomposition summary: 'The frame Peirce decomposition: relative to a Jordan frame, a Euclidean Jordan algebra is the internal direct sum of the blocks V_ij over unordered pairs of frame indices, and in finite dimension the diagonal blocks are lines, dim V_ii = 1 - the Faraut-Koranyi @@ -9987,7 +9986,6 @@ projects: status: verified main_declarations: - EuclideanJordan.frameBlock_isInternal - - EuclideanJordan.finrank_frameBlock_diag main_results: - declaration: EuclideanJordan.frameBlock_isInternal informal: A Euclidean Jordan algebra is the internal direct sum of the Peirce blocks associated From 9d1e4de77efb83f052bc5a6151970171cd0ecbd7 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Fri, 25 Sep 2026 08:45:23 +0000 Subject: [PATCH 5/6] Expose Euclidean Jordan algebra modules --- LeanPool/EuclideanJordan.lean | 109 +++++++++--------- LeanPool/EuclideanJordan/EuclideanJordan.lean | 91 ++++++++------- .../EuclideanJordan/Block.lean | 7 +- .../EuclideanJordan/Bridge.lean | 7 +- .../EuclideanJordan/Class.lean | 9 +- .../EuclideanJordan/Connection.lean | 7 +- .../EuclideanJordan/FormallyReal.lean | 7 +- .../EuclideanJordan/Frame.lean | 9 +- .../EuclideanJordan/FrameExists.lean | 7 +- .../EuclideanJordan/FramePeirce.lean | 7 +- .../EuclideanJordan/FramePeirceMul.lean | 7 +- .../EuclideanJordan/HermitianBilin.lean | 9 +- .../EuclideanJordan/HermitianCarrier.lean | 11 +- .../EuclideanJordan/Order.lean | 11 +- .../EuclideanJordan/OrderAuto.lean | 9 +- .../EuclideanJordan/OrderUnitSpace.lean | 13 ++- .../EuclideanJordan/Orthogonal.lean | 7 +- .../EuclideanJordan/Pattern.lean | 7 +- .../EuclideanJordan/Peirce.lean | 13 ++- .../EuclideanJordan/PeirceMul.lean | 7 +- .../EuclideanJordan/PeirceSubalgebra.lean | 7 +- .../EuclideanJordan/Power.lean | 7 +- .../EuclideanJordan/PowerAssoc.lean | 7 +- .../EuclideanJordan/EuclideanJordan/Rank.lean | 7 +- .../EuclideanJordan/Spectral.lean | 19 +-- .../EuclideanJordan/Subalgebra.lean | 9 +- .../EuclideanJordan/TraceForm.lean | 11 +- .../EuclideanJordan/Vendor.lean | 21 ++-- .../EuclideanJordan/Vendor/HermitianMat.lean | 23 ++-- .../EuclideanJordan/Vendor/Tactic.lean | 7 +- .../EuclideanJordan/Witness.lean | 13 ++- .../EuclideanJordan/FramePeirceSolution.lean | 19 +-- LeanPool/EuclideanJordan/KoecherSolution.lean | 15 ++- .../EuclideanJordan/SpectralSolution.lean | 17 ++- .../EuclideanJordan/StructureSolution.lean | 29 +++-- .../EuclideanJordan/TraceFormSolution.lean | 19 +-- 36 files changed, 382 insertions(+), 202 deletions(-) diff --git a/LeanPool/EuclideanJordan.lean b/LeanPool/EuclideanJordan.lean index 050f563c9e..316f449a89 100644 --- a/LeanPool/EuclideanJordan.lean +++ b/LeanPool/EuclideanJordan.lean @@ -4,58 +4,61 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan -import LeanPool.EuclideanJordan.EuclideanJordan.Block -import LeanPool.EuclideanJordan.EuclideanJordan.Bridge -import LeanPool.EuclideanJordan.EuclideanJordan.Class -import LeanPool.EuclideanJordan.EuclideanJordan.Connection -import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal -import LeanPool.EuclideanJordan.EuclideanJordan.Frame -import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul -import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin -import LeanPool.EuclideanJordan.EuclideanJordan.HermitianCarrier -import LeanPool.EuclideanJordan.EuclideanJordan.Order -import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto -import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace -import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal -import LeanPool.EuclideanJordan.EuclideanJordan.Pattern -import LeanPool.EuclideanJordan.EuclideanJordan.Peirce -import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul -import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra -import LeanPool.EuclideanJordan.EuclideanJordan.Power -import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc -import LeanPool.EuclideanJordan.EuclideanJordan.Rank -import LeanPool.EuclideanJordan.EuclideanJordan.Spectral -import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra -import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes.Attribute -import LeanPool.EuclideanJordan.EuclideanJordan.Witness -import LeanPool.EuclideanJordan.FramePeirceSolution -import LeanPool.EuclideanJordan.KoecherSolution -import LeanPool.EuclideanJordan.SpectralSolution -import LeanPool.EuclideanJordan.StructureSolution -import LeanPool.EuclideanJordan.TraceFormSolution +module + +public import LeanPool.EuclideanJordan.EuclideanJordan +public import LeanPool.EuclideanJordan.EuclideanJordan.Block +public import LeanPool.EuclideanJordan.EuclideanJordan.Bridge +public import LeanPool.EuclideanJordan.EuclideanJordan.Class +public import LeanPool.EuclideanJordan.EuclideanJordan.Connection +public import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +public import LeanPool.EuclideanJordan.EuclideanJordan.Frame +public import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul +public import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin +public import LeanPool.EuclideanJordan.EuclideanJordan.HermitianCarrier +public import LeanPool.EuclideanJordan.EuclideanJordan.Order +public import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto +public import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace +public import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal +public import LeanPool.EuclideanJordan.EuclideanJordan.Pattern +public import LeanPool.EuclideanJordan.EuclideanJordan.Peirce +public import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul +public import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra +public import LeanPool.EuclideanJordan.EuclideanJordan.Power +public import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc +public import LeanPool.EuclideanJordan.EuclideanJordan.Rank +public import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +public import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra +public import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes.Attribute +public import LeanPool.EuclideanJordan.EuclideanJordan.Witness +public import LeanPool.EuclideanJordan.FramePeirceSolution +public import LeanPool.EuclideanJordan.KoecherSolution +public import LeanPool.EuclideanJordan.SpectralSolution +public import LeanPool.EuclideanJordan.StructureSolution +public import LeanPool.EuclideanJordan.TraceFormSolution + /-! # Euclidean Jordan algebras and the frame Peirce decomposition @@ -67,3 +70,5 @@ Main declarations: `EuclideanJordan.frameBlock_isInternal` Tags: nonassociative-algebra MSC: 17C20, 17C27, 17C37, 17C65, 17A15, 46L70 -/ + +@[expose] public section diff --git a/LeanPool/EuclideanJordan/EuclideanJordan.lean b/LeanPool/EuclideanJordan/EuclideanJordan.lean index 678c501812..2c77e6dfe7 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan.lean @@ -8,52 +8,57 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Block -import LeanPool.EuclideanJordan.EuclideanJordan.Bridge -import LeanPool.EuclideanJordan.EuclideanJordan.Class -import LeanPool.EuclideanJordan.EuclideanJordan.Connection -import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal -import LeanPool.EuclideanJordan.EuclideanJordan.Frame -import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul -import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin -import LeanPool.EuclideanJordan.EuclideanJordan.HermitianCarrier -import LeanPool.EuclideanJordan.EuclideanJordan.Order -import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto -import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace -import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal -import LeanPool.EuclideanJordan.EuclideanJordan.Pattern -import LeanPool.EuclideanJordan.EuclideanJordan.Peirce -import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul -import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra -import LeanPool.EuclideanJordan.EuclideanJordan.Power -import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc -import LeanPool.EuclideanJordan.EuclideanJordan.Rank -import LeanPool.EuclideanJordan.EuclideanJordan.Spectral -import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra -import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes.Attribute -import LeanPool.EuclideanJordan.EuclideanJordan.Witness +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Block +public import LeanPool.EuclideanJordan.EuclideanJordan.Bridge +public import LeanPool.EuclideanJordan.EuclideanJordan.Class +public import LeanPool.EuclideanJordan.EuclideanJordan.Connection +public import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +public import LeanPool.EuclideanJordan.EuclideanJordan.Frame +public import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul +public import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin +public import LeanPool.EuclideanJordan.EuclideanJordan.HermitianCarrier +public import LeanPool.EuclideanJordan.EuclideanJordan.Order +public import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto +public import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace +public import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal +public import LeanPool.EuclideanJordan.EuclideanJordan.Pattern +public import LeanPool.EuclideanJordan.EuclideanJordan.Peirce +public import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul +public import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra +public import LeanPool.EuclideanJordan.EuclideanJordan.Power +public import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc +public import LeanPool.EuclideanJordan.EuclideanJordan.Rank +public import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +public import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra +public import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes.Attribute +public import LeanPool.EuclideanJordan.EuclideanJordan.Witness + /-! # Euclidean Jordan algebras in Lean 4 Root import for the library. See `README.md` for the headline results. -/ + +@[expose] public section diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean index a0d0af7503..6f1e9392a7 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Frame +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Frame + /-! @@ -55,6 +58,8 @@ they just stop being a Peirce decomposition. Callers wanting the FK reading shou in hand from `IsOrthIdemFamily`; the theorem simply does not need to be told. -/ +@[expose] public section + namespace EuclideanJordan section Block diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean index 4872523b50..69baefe4cd 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Pattern +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Pattern + /-! @@ -43,6 +46,8 @@ alone cross over; anything whose statement needs the ring instance — `jpow`, a power-associativity theorem — cannot be bridged this way, because the instance would have to exist before the statement elaborates. -/ + +@[expose] public section namespace EuclideanJordan section Bridge diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean index 1ae49ec68e..a2e1675987 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean @@ -8,8 +8,11 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Order -import LeanPool.EuclideanJordan.EuclideanJordan.Pattern +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Order +public import LeanPool.EuclideanJordan.EuclideanJordan.Pattern + /-! @@ -107,6 +110,8 @@ if one is ever declared, that scoped instance and this class's `toMul` will both inside `open HermMul` sections and one of them has to give way. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean index 1185dd930b..6b4f0fb897 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul + /-! @@ -70,6 +73,8 @@ The statement "a simple `J` has every block nonzero" is **not proved anywhere in library**, which has no notion of a Jordan ideal to state simplicity with. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean index 23b99eb07d..64ae9625f3 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc + /-! @@ -51,6 +54,8 @@ conclusion whose connecting lemma was already in the tree (after row 35 and the that documents it.** -/ +@[expose] public section + namespace EuclideanJordan open Finset diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean index 60448e274e..32ef291243 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean @@ -8,8 +8,11 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal -import Mathlib.Algebra.BigOperators.Ring.Finset +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal +public import Mathlib.Algebra.BigOperators.Ring.Finset + /-! @@ -61,6 +64,8 @@ facts needs it; it is what the *spectral* theorem produces and what the rank arg consumes. Keeping it out makes visible which results are independent of the spectral theorem. -/ +@[expose] public section + namespace EuclideanJordan open Finset diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean index 82067738da..1413241237 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Rank +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Rank + /-! @@ -77,6 +80,8 @@ frame conjugacy. Nothing here is a step towards it. Do not read `exists_jordan "`J` has rank `n`". -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean index f709bd6311..e195815e8d 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists + /-! @@ -103,6 +106,8 @@ existentially quantified. Note what that does and does not settle: see the rank -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean index 9cedd8e206..4934fa97f5 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce + /-! @@ -107,6 +110,8 @@ bounds a frame's cardinality by the rank and by the dimension, and nothing anywh `dim V_{ii} = 1` into a statement about `rank J`. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean index 64a15fc5d0..323f296365 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean @@ -8,8 +8,11 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner + /-! @@ -27,6 +30,8 @@ than 𝕜-linearity) is the right statement because `HermitianMat n 𝕜` is onl a 𝕜-multiple of a Hermitian matrix need not be Hermitian. -/ +@[expose] public section + noncomputable section open scoped Matrix diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean index 30806efbc7..e01f03d604 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean @@ -8,9 +8,12 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce -import LeanPool.EuclideanJordan.EuclideanJordan.Witness -import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce +public import LeanPool.EuclideanJordan.EuclideanJordan.Witness +public import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin + /-! @@ -119,6 +122,8 @@ which is **not** proved to be the rank — `EuclideanJordan/Rank.lean` proves on `card ≤ finrank`. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean index 2c6f7f7fe5..c8d24ac0f4 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean @@ -8,9 +8,12 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace -import LeanPool.EuclideanJordan.EuclideanJordan.Spectral -import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace +public import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +public import LeanPool.EuclideanJordan.EuclideanJordan.HermitianBilin + /-! @@ -84,6 +87,8 @@ Two deliberate choices, both forced by diamonds. coefficient off. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean b/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean index f3f978a7bc..903c23a9a5 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean @@ -8,8 +8,11 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm -import LeanPool.EuclideanJordan.EuclideanJordan.Order +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +public import LeanPool.EuclideanJordan.EuclideanJordan.Order + /-! @@ -90,6 +93,8 @@ automorphism, and the fixing and cocycle properties (vdW Props 5.5, 5.7). **Onl proved here.** The other two are cited, not formalized, and nothing below establishes them. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean b/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean index 56ea228f8d..92c6b22bf2 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean @@ -8,10 +8,13 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import Mathlib.Basic.Real.Basic -import Mathlib.Analysis.Normed.Module.Basic -import Mathlib.Algebra.Order.Group.Defs -import Mathlib.Tactic.Linarith +module + +public import Mathlib.Basic.Real.Basic +public import Mathlib.Analysis.Normed.Module.Basic +public import Mathlib.Algebra.Order.Group.Defs +public import Mathlib.Tactic.Linarith + /-! @@ -35,6 +38,8 @@ structure, not derived here as the order-unit norm. * van de Wetering, arXiv:1803.11139 -/ +@[expose] public section + noncomputable section /-- An order unit space is a real vector space with a partial order compatible diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean index 23b4938c4e..c389339e59 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul + /-! @@ -41,6 +44,8 @@ and `EuclideanJordan/Frame.lean` assembles it (`opCommute_scalarOn_frame`). * Faraut and Korányi, *Analysis on Symmetric Cones*, Ch. IV. -/ +@[expose] public section + namespace EuclideanJordan section Orthogonal diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean index 3db3f48b8f..bd2b4f91e6 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Block +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Block + /-! @@ -43,6 +46,8 @@ one-line consequence is written out above so a reader can check it rather than t constraints say what the summands can be, not that every element decomposes into them. -/ +@[expose] public section + namespace EuclideanJordan open Finset diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean index 38738b6ae9..d55823e02b 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean @@ -8,10 +8,13 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import Mathlib.Algebra.Jordan.Basic -import Mathlib.Analysis.Normed.Module.Basic -import Mathlib.Basic.Real.Basic -import Mathlib.Tactic.LinearCombination +module + +public import Mathlib.Algebra.Jordan.Basic +public import Mathlib.Analysis.Normed.Module.Basic +public import Mathlib.Basic.Real.Basic +public import Mathlib.Tactic.LinearCombination + /-! @@ -76,6 +79,8 @@ searched them systematically. * McCrimmon, *A Taste of Jordan Algebras*, §II.8. -/ +@[expose] public section + namespace EuclideanJordan local notation "L" => AddMonoid.End.mulLeft diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean index 353b66601e..e0c1debecf 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Peirce +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Peirce + /-! @@ -61,6 +64,8 @@ characterisation of the rank-two block. * McCrimmon, *A Taste of Jordan Algebras*, §II.8. -/ +@[expose] public section + namespace EuclideanJordan local notation "L" => AddMonoid.End.mulLeft diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean index 2d7825ac73..3d36ab92e3 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Class +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Class + /-! @@ -105,6 +108,8 @@ zero. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean index cc72447f1f..4402744f2d 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Frame +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Frame + /-! @@ -56,6 +59,8 @@ this vocabulary, and because `commuteAt_two` is what made the general pattern vi above and nothing more. -/ +@[expose] public section + namespace EuclideanJordan section Pow diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean index fb460babb0..ef9f57689e 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Power +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Power + /-! @@ -56,6 +59,8 @@ across it is the upstream contribution. **Not done here** — that translation i this file should not be read as providing it. -/ +@[expose] public section + namespace EuclideanJordan section Albert diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean index 46fd59e9f0..847ed154ca 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean @@ -8,7 +8,10 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra + /-! @@ -57,6 +60,8 @@ without restating it. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean index 753ffbd57c..2e15ada394 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean @@ -8,13 +8,16 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra -import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal -import LeanPool.EuclideanJordan.EuclideanJordan.Witness -import LeanPool.EuclideanJordan.EuclideanJordan.Bridge -import Mathlib.LinearAlgebra.Lagrange -import Mathlib.Analysis.Complex.Polynomial.Basic -import Mathlib.RingTheory.PrincipalIdealDomain +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra +public import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +public import LeanPool.EuclideanJordan.EuclideanJordan.Witness +public import LeanPool.EuclideanJordan.EuclideanJordan.Bridge +public import Mathlib.LinearAlgebra.Lagrange +public import Mathlib.Analysis.Complex.Polynomial.Basic +public import Mathlib.RingTheory.PrincipalIdealDomain + /-! @@ -90,6 +93,8 @@ because of step 3. Multiplicities never enter, and neither does `Mathlib`'s `rad polynomial calculus is built on it. -/ +@[expose] public section + namespace EuclideanJordan open Polynomial diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean index 530d774506..80ca2d2827 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean @@ -8,8 +8,11 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc -import Mathlib.LinearAlgebra.Dimension.Finite +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc +public import Mathlib.LinearAlgebra.Dimension.Finite + /-! @@ -54,6 +57,8 @@ development deliberately does not assume. The two theorems below say exactly wha version would, without the bundling. -/ +@[expose] public section + namespace EuclideanJordan section Subalgebra diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean b/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean index eb3d408c28..8fd93b24d3 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean @@ -8,9 +8,12 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Spectral -import Mathlib.LinearAlgebra.Trace -import Mathlib.LinearAlgebra.Projection +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +public import Mathlib.LinearAlgebra.Trace +public import Mathlib.LinearAlgebra.Projection + /-! @@ -79,6 +82,8 @@ normalisation, and definite form rather than a normalised one, so `traceForm` is admissible there as it stands. -/ +@[expose] public section + noncomputable section namespace EuclideanJordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean index 5058ff7880..ca3417123d 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor.lean @@ -4,14 +4,17 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.ContinuousLinearMap +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.IsMaximalSelfAdjoint +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Isometry +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.LinearEquiv +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Matrix +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Misc +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic + /-! # Vendor @@ -19,3 +22,5 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. -/ + +@[expose] public section diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean index 0f81d0d942..85d602e36b 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/HermitianMat.lean @@ -4,15 +4,18 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Basic +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.CFC +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.NonSingular +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Order +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Proj +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Reindex +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace + /-! # HermitianMat @@ -20,3 +23,5 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Trace Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. -/ + +@[expose] public section diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean index 6b2d9a821e..d6d62fb313 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Vendor/Tactic.lean @@ -4,7 +4,10 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes + /-! # Tactic @@ -12,3 +15,5 @@ import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.Tactic.Commutes Supporting modules for Euclidean Jordan algebras: power associativity, the spectral theorem, the trace form, Koecher/Alfsen-Shultz, and the frame Peirce decomposition. -/ + +@[expose] public section diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean index 0ae846a846..3b4310ddc6 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean @@ -8,10 +8,13 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal -import LeanPool.EuclideanJordan.EuclideanJordan.Block -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan -import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner +module + +public import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal +public import LeanPool.EuclideanJordan.EuclideanJordan.Block +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan +public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Inner + /-! @@ -56,6 +59,8 @@ with all three components nonzero, and nothing more. The rank-two carrier is use is the smallest place where a `1/2`-eigenvector exists, not because rank two matters here. -/ +@[expose] public section + open HermMul EuclideanJordan namespace EuclideanJordan.Witness diff --git a/LeanPool/EuclideanJordan/FramePeirceSolution.lean b/LeanPool/EuclideanJordan/FramePeirceSolution.lean index 54f93eb76d..8cca8d89c1 100644 --- a/LeanPool/EuclideanJordan/FramePeirceSolution.lean +++ b/LeanPool/EuclideanJordan/FramePeirceSolution.lean @@ -8,13 +8,16 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import Mathlib.Algebra.BigOperators.Fin -import Mathlib.Algebra.DirectSum.Module -import Mathlib.Analysis.InnerProductSpace.Basic -import Mathlib.Data.Sym.Sym2 -import Mathlib.LinearAlgebra.FiniteDimensional.Defs -import Mathlib.LinearAlgebra.Dimension.Finrank -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul +module + +public import Mathlib.Algebra.BigOperators.Fin +public import Mathlib.Algebra.DirectSum.Module +public import Mathlib.Analysis.InnerProductSpace.Basic +public import Mathlib.Data.Sym.Sym2 +public import Mathlib.LinearAlgebra.FiniteDimensional.Defs +public import Mathlib.LinearAlgebra.Dimension.Finrank +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul + /-! @@ -137,6 +140,8 @@ challenge file states. The contract here is that the two files' declaration typ nose, so the extra instance is kept out of every statement. -/ +@[expose] public section + noncomputable section namespace JordanFramePeirce diff --git a/LeanPool/EuclideanJordan/KoecherSolution.lean b/LeanPool/EuclideanJordan/KoecherSolution.lean index 629e8ff4df..c69ede4071 100644 --- a/LeanPool/EuclideanJordan/KoecherSolution.lean +++ b/LeanPool/EuclideanJordan/KoecherSolution.lean @@ -8,11 +8,14 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import Mathlib.Algebra.BigOperators.Fin -import Mathlib.Algebra.Module.LinearMap.Defs -import Mathlib.Analysis.Normed.Module.Basic -import Mathlib.LinearAlgebra.FiniteDimensional.Defs -import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto +module + +public import Mathlib.Algebra.BigOperators.Fin +public import Mathlib.Algebra.Module.LinearMap.Defs +public import Mathlib.Analysis.Normed.Module.Basic +public import Mathlib.LinearAlgebra.FiniteDimensional.Defs +public import LeanPool.EuclideanJordan.EuclideanJordan.OrderAuto + /-! @@ -74,6 +77,8 @@ reference library, and discharges the theorem from `EuclideanJordan.orderIso_pre local `IsSoS` is the same existential as `EuclideanJordan.IsSoS`, so the bridge is definitional. -/ +@[expose] public section + namespace KoecherAlfsenShultz open Finset diff --git a/LeanPool/EuclideanJordan/SpectralSolution.lean b/LeanPool/EuclideanJordan/SpectralSolution.lean index 72af4ee7f8..9298d4956f 100644 --- a/LeanPool/EuclideanJordan/SpectralSolution.lean +++ b/LeanPool/EuclideanJordan/SpectralSolution.lean @@ -8,12 +8,15 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import Mathlib.Analysis.InnerProductSpace.Basic -import Mathlib.LinearAlgebra.FiniteDimensional.Defs -import Mathlib.LinearAlgebra.BilinearMap -import Mathlib.Algebra.BigOperators.Fin -import Mathlib.Basic.Real.Basic -import LeanPool.EuclideanJordan.EuclideanJordan.Spectral +module + +public import Mathlib.Analysis.InnerProductSpace.Basic +public import Mathlib.LinearAlgebra.FiniteDimensional.Defs +public import Mathlib.LinearAlgebra.BilinearMap +public import Mathlib.Algebra.BigOperators.Fin +public import Mathlib.Basic.Real.Basic +public import LeanPool.EuclideanJordan.EuclideanJordan.Spectral + /-! # Solution: the single-element spectral theorem for a Euclidean Jordan algebra @@ -28,6 +31,8 @@ ring on `J`, `IsCommJordan`, `IsScalarTower ℝ J J`, `IsFormallyReal J`) are bu library from `m`, `hcomm`, `hjordan` and `hfr`, and none of them escapes into the statement. -/ +@[expose] public section + namespace JordanSpectral variable {J : Type*} [NormedAddCommGroup J] [InnerProductSpace ℝ J] diff --git a/LeanPool/EuclideanJordan/StructureSolution.lean b/LeanPool/EuclideanJordan/StructureSolution.lean index 6eb4c2c62c..a98480816e 100644 --- a/LeanPool/EuclideanJordan/StructureSolution.lean +++ b/LeanPool/EuclideanJordan/StructureSolution.lean @@ -8,18 +8,21 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import Mathlib.Algebra.Jordan.Basic -import Mathlib.Algebra.BigOperators.Fin -import Mathlib.Algebra.DirectSum.Module -import Mathlib.Analysis.InnerProductSpace.Basic -import Mathlib.Basic.Real.Basic -import Mathlib.Data.Sym.Sym2 -import Mathlib.LinearAlgebra.BilinearMap -import Mathlib.LinearAlgebra.Dimension.Finrank -import Mathlib.LinearAlgebra.FiniteDimensional.Defs -import Mathlib.LinearAlgebra.Trace -import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm -import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul +module + +public import Mathlib.Algebra.Jordan.Basic +public import Mathlib.Algebra.BigOperators.Fin +public import Mathlib.Algebra.DirectSum.Module +public import Mathlib.Analysis.InnerProductSpace.Basic +public import Mathlib.Basic.Real.Basic +public import Mathlib.Data.Sym.Sym2 +public import Mathlib.LinearAlgebra.BilinearMap +public import Mathlib.LinearAlgebra.Dimension.Finrank +public import Mathlib.LinearAlgebra.FiniteDimensional.Defs +public import Mathlib.LinearAlgebra.Trace +public import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul + /-! @@ -38,6 +41,8 @@ proof of the other, and the binders of all six statements are exactly those of t file. The part-level docstrings below are the originals and describe each bridge in detail. -/ +@[expose] public section + /-! # Part I: the Jordan trace form -/ /-! diff --git a/LeanPool/EuclideanJordan/TraceFormSolution.lean b/LeanPool/EuclideanJordan/TraceFormSolution.lean index 5bb672569f..d1ed7afd3d 100644 --- a/LeanPool/EuclideanJordan/TraceFormSolution.lean +++ b/LeanPool/EuclideanJordan/TraceFormSolution.lean @@ -8,13 +8,16 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license. Authors: Bryan Ehrlich -/ -import Mathlib.Algebra.Jordan.Basic -import Mathlib.Algebra.BigOperators.Fin -import Mathlib.LinearAlgebra.BilinearMap -import Mathlib.LinearAlgebra.Trace -import Mathlib.LinearAlgebra.FiniteDimensional.Defs -import Mathlib.Basic.Real.Basic -import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm +module + +public import Mathlib.Algebra.Jordan.Basic +public import Mathlib.Algebra.BigOperators.Fin +public import Mathlib.LinearAlgebra.BilinearMap +public import Mathlib.LinearAlgebra.Trace +public import Mathlib.LinearAlgebra.FiniteDimensional.Defs +public import Mathlib.Basic.Real.Basic +public import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm + /-! # Solution: the Jordan trace form @@ -33,6 +36,8 @@ The one piece of real work is formal reality. The challenge states it as a hypot only by reindexing along `Finset.equivFin`, done inline in each of the two positivity proofs. -/ +@[expose] public section + namespace JordanTraceForm variable {J : Type*} [NonUnitalNonAssocCommRing J] [Module ℝ J] [IsScalarTower ℝ J J] From f2c77c6383fdefcf9f1f80345167dee581accb02 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Fri, 25 Sep 2026 19:20:41 +0000 Subject: [PATCH 6/6] Remove duplicate Euclidean Jordan license headers --- LeanPool/EuclideanJordan/EuclideanJordan.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Block.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Class.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean | 5 ----- .../EuclideanJordan/EuclideanJordan/HermitianCarrier.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Order.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean | 5 ----- .../EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Power.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean | 5 ----- LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean | 5 ----- LeanPool/EuclideanJordan/FramePeirceSolution.lean | 5 ----- LeanPool/EuclideanJordan/KoecherSolution.lean | 5 ----- LeanPool/EuclideanJordan/SpectralSolution.lean | 5 ----- LeanPool/EuclideanJordan/StructureSolution.lean | 5 ----- LeanPool/EuclideanJordan/TraceFormSolution.lean | 5 ----- 32 files changed, 160 deletions(-) diff --git a/LeanPool/EuclideanJordan/EuclideanJordan.lean b/LeanPool/EuclideanJordan/EuclideanJordan.lean index 2c77e6dfe7..29643674b3 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Block diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean index 6f1e9392a7..8909478ba0 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Block.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Frame diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean index 69baefe4cd..cd1deb405e 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Bridge.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Pattern diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean index a2e1675987..9d32b23702 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Class.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Order diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean index 6b4f0fb897..3525a02420 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Connection.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirceMul diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean index 64ae9625f3..83bb6c0647 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FormallyReal.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean index 32ef291243..59afbf4f9d 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Frame.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Orthogonal diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean index 1413241237..9793be3c06 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FrameExists.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Rank diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean index e195815e8d..a2ffbadd92 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirce.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.FrameExists diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean index 4934fa97f5..3e676a1067 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/FramePeirceMul.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean index 323f296365..27ec96bcfe 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianBilin.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Vendor.HermitianMat.Jordan diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean index e01f03d604..ad7716b139 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/HermitianCarrier.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.FramePeirce diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean index c8d24ac0f4..c5ed0ebf7e 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Order.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.OrderUnitSpace diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean b/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean index 903c23a9a5..e8468a076a 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/OrderAuto.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.TraceForm diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean b/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean index 92c6b22bf2..fa3525f5e9 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/OrderUnitSpace.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import Mathlib.Basic.Real.Basic diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean index c389339e59..5c6bcfd42b 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Orthogonal.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.PeirceMul diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean index bd2b4f91e6..1f35eceda7 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Pattern.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Block diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean index d55823e02b..bce4c9192b 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Peirce.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import Mathlib.Algebra.Jordan.Basic diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean index e0c1debecf..bfda7784a0 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceMul.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Peirce diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean index 3d36ab92e3..b6d6fe2217 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PeirceSubalgebra.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Class diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean index 4402744f2d..83dc9344d7 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Power.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Frame diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean b/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean index ef9f57689e..5f4ce471f3 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/PowerAssoc.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Power diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean index 847ed154ca..d1084fc882 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Rank.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.PeirceSubalgebra diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean index 2e15ada394..db1f230649 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Spectral.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Subalgebra diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean index 80ca2d2827..c5ace796eb 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Subalgebra.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.PowerAssoc diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean b/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean index 8fd93b24d3..ab9fc55b08 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/TraceForm.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.Spectral diff --git a/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean b/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean index 3b4310ddc6..a7cb7224c5 100644 --- a/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean +++ b/LeanPool/EuclideanJordan/EuclideanJordan/Witness.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import LeanPool.EuclideanJordan.EuclideanJordan.FormallyReal diff --git a/LeanPool/EuclideanJordan/FramePeirceSolution.lean b/LeanPool/EuclideanJordan/FramePeirceSolution.lean index 8cca8d89c1..c9233286e7 100644 --- a/LeanPool/EuclideanJordan/FramePeirceSolution.lean +++ b/LeanPool/EuclideanJordan/FramePeirceSolution.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import Mathlib.Algebra.BigOperators.Fin diff --git a/LeanPool/EuclideanJordan/KoecherSolution.lean b/LeanPool/EuclideanJordan/KoecherSolution.lean index c69ede4071..94846aef4f 100644 --- a/LeanPool/EuclideanJordan/KoecherSolution.lean +++ b/LeanPool/EuclideanJordan/KoecherSolution.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import Mathlib.Algebra.BigOperators.Fin diff --git a/LeanPool/EuclideanJordan/SpectralSolution.lean b/LeanPool/EuclideanJordan/SpectralSolution.lean index 9298d4956f..fc6c4120ba 100644 --- a/LeanPool/EuclideanJordan/SpectralSolution.lean +++ b/LeanPool/EuclideanJordan/SpectralSolution.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import Mathlib.Analysis.InnerProductSpace.Basic diff --git a/LeanPool/EuclideanJordan/StructureSolution.lean b/LeanPool/EuclideanJordan/StructureSolution.lean index a98480816e..4eab444b4c 100644 --- a/LeanPool/EuclideanJordan/StructureSolution.lean +++ b/LeanPool/EuclideanJordan/StructureSolution.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import Mathlib.Algebra.Jordan.Basic diff --git a/LeanPool/EuclideanJordan/TraceFormSolution.lean b/LeanPool/EuclideanJordan/TraceFormSolution.lean index d1ed7afd3d..c358a1f2af 100644 --- a/LeanPool/EuclideanJordan/TraceFormSolution.lean +++ b/LeanPool/EuclideanJordan/TraceFormSolution.lean @@ -3,11 +3,6 @@ Copyright (c) 2026 Bryan Ehrlich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Bryan Ehrlich -/ -/- -Copyright (c) 2026 Bryan Ehrlich. All rights reserved. -Released under Apache 2.0 license. -Authors: Bryan Ehrlich --/ module public import Mathlib.Algebra.Jordan.Basic