diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5fe1ccf..869d95d 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -9,7 +9,7 @@ RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhisto && echo "$SNIPPET" >> "/home/$USERNAME/.bashrc" RUN apt-get update && \ - apt install -y ripgrep + apt install -y ripgrep libgl1 libglib2.0-0 USER vscode WORKDIR /home/vscode diff --git a/QuantumSystem.lean b/QuantumSystem.lean index 67e0d6c..2f72535 100644 --- a/QuantumSystem.lean +++ b/QuantumSystem.lean @@ -9,19 +9,49 @@ public import QuantumSystem.Algebra.CStarAlgebra.PureState public import QuantumSystem.Algebra.CStarAlgebra.QuasiState public import QuantumSystem.Algebra.CStarAlgebra.State public import QuantumSystem.Algebra.CStarAlgebra.State.Continuity +public import QuantumSystem.Algebra.CStarAlgebra.State.Faithful +public import QuantumSystem.Algebra.LocalNet public import QuantumSystem.Algebra.Star.DoubleCommutant.SOTClosedSubAlgebra public import QuantumSystem.Algebra.Star.DoubleCommutant.WOTClosedSubAlgebra -public import QuantumSystem.Algebra.VonNeumannAlgebra.Basic +public import QuantumSystem.Analysis.CFC.Diagonal +public import QuantumSystem.Analysis.Entropy.KroneckerProduct +public import QuantumSystem.Analysis.Entropy.Regularize +public import QuantumSystem.Analysis.Entropy.RelativeEntropy +public import QuantumSystem.Analysis.Entropy.SSA +public import QuantumSystem.Analysis.Entropy.VonNeumannEntropy +public import QuantumSystem.Analysis.Matrix.Effros +public import QuantumSystem.Analysis.Matrix.HermitianFunctionalCalculus +public import QuantumSystem.Analysis.Matrix.LiebConcavity +public import QuantumSystem.Analysis.Matrix.Order +public import QuantumSystem.Analysis.Matrix.PartialTrace +public import QuantumSystem.Analysis.Matrix.Pinching +public import QuantumSystem.Analysis.Matrix.PosDef +public import QuantumSystem.Channel +public import QuantumSystem.ForMathlib.Analysis.Calculus.Deriv.Sign public import QuantumSystem.ForMathlib.Analysis.Complex.Basic public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.HilbertSpace public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.Ideal public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.NonUnital public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.Unital public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.WeakDual +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.Adjoint public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.DiagonalAmplification public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.InvariantSubspace +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.l2Space +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.TensorProduct public import QuantumSystem.ForMathlib.Analysis.LocallyConvex.StrongOperatorTopology public import QuantumSystem.ForMathlib.Analysis.LocallyConvex.WeakOperatorTopology +public import QuantumSystem.ForMathlib.Analysis.Matrix.Basic +public import QuantumSystem.ForMathlib.Analysis.Matrix.Hermitian +public import QuantumSystem.ForMathlib.Analysis.Matrix.Order +public import QuantumSystem.ForMathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.Abs +public import QuantumSystem.ForMathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.PartialIsometry +public import QuantumSystem.ForMathlib.Analysis.SpecialFunctions.Pow.Real +public import QuantumSystem.ForMathlib.InformationTheory.KullbackLeibler.KLFun +public import QuantumSystem.ForMathlib.LinearAlgebra.Matrix.StarAlgEquiv public import QuantumSystem.ForMathlib.LinearAlgebra.Span.Def +public import QuantumSystem.ForMathlib.RingTheory.RootsOfUnity.Complex public import QuantumSystem.ForMathlib.Topology.DenseLinear public import QuantumSystem.ForMathlib.Topology.MetricSpace.Completion +public import QuantumSystem.Notation +public import QuantumSystem.State diff --git a/QuantumSystem/Algebra/CStarAlgebra/GNS/Construction.lean b/QuantumSystem/Algebra/CStarAlgebra/GNS/Construction.lean index 1191886..672bf21 100644 --- a/QuantumSystem/Algebra/CStarAlgebra/GNS/Construction.lean +++ b/QuantumSystem/Algebra/CStarAlgebra/GNS/Construction.lean @@ -5,6 +5,7 @@ public import Mathlib.Analysis.Normed.Operator.Extend public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.HilbertSpace public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.Ideal public import QuantumSystem.Algebra.CStarAlgebra.State.Continuity +public import QuantumSystem.Algebra.CStarAlgebra.State.Faithful @[expose] public section @@ -765,6 +766,84 @@ lemma approxUnit_eval_tendsto_one : rw [inner_self_eq_norm_sq_to_K (𝕜 := ℂ), h_norm] norm_num +/-- If ω is faithful, then the GNS kernel Nω is trivial. -/ +lemma IsFaithful.kernel_eq_bot (hω : ω.IsFaithful) : (Nω).carrier = {0} := by + ext a + simp only [Set.mem_singleton_iff] + constructor + · intro ha + exact hω a ha + · intro ha + simp [ha] + +/-- The GNS cyclic vector ξω is nonzero (has norm 1). + +Note: This is a basic property of the GNS construction, not the main theorem about +faithful states and separating vectors. -/ +lemma ξω_ne_zero : ξω ω ≠ 0 := by + have h := ξω_norm (ω := ω) + intro h0 + rw [h0, norm_zero] at h + exact one_ne_zero h.symm + +/-- If ω is faithful, then πω(a) ξω = 0 implies a = 0 (and hence πω(a) = 0). + +This is the key separating property: the cyclic vector ξω separates elements of A +via the GNS representation when ω is faithful. -/ +lemma IsFaithful.ξω_separating (hω : ω.IsFaithful) (a : A) (h : πω ω a (ξω ω) = 0) : a = 0 := by + rw [πω_cyclic_identity] at h + -- h : ↑(Quotient.mk'' a : A ⧸ Nω ω) = 0 in Hω + have h_inj := UniformSpace.Completion.coe_injective (α := A ⧸ Nω) + have h0 : (↑(Quotient.mk'' 0 : A ⧸ Nω) : Hω) = 0 := + UniformSpace.Completion.coe_zero (α := A ⧸ Nω) + have h_quot_zero : (Quotient.mk'' a : A ⧸ Nω) = Quotient.mk'' 0 := h_inj (h.trans h0.symm) + -- From quotient being zero, a ∈ Nω + have h_eq := Quotient.exact' h_quot_zero + rw [CStarAlgebraIdeal.leftRel, QuotientAddGroup.leftRel_apply] at h_eq + have h_neg_in : -a ∈ (Nω).toAddSubgroup := by simpa using h_eq + have h_neg_neg : - -a ∈ (Nω).carrier := (Nω).neg_mem' h_neg_in + have h_in_kernel : a ∈ (Nω).carrier := by simp only [neg_neg] at h_neg_neg; exact h_neg_neg + -- Apply faithfulness: Nω = {0} + rw [IsFaithful.kernel_eq_bot (ω := ω) hω, Set.mem_singleton_iff] at h_in_kernel + exact h_in_kernel + +/-- The fundamental equivalence: ω is faithful if and only if the map a ↦ πω(a) ξω +is injective from A to Hω. + +This connects faithfulness (an algebraic property of the state) with the separating +property (a geometric property of the cyclic vector). -/ +theorem isFaithful_iff_separating : + ω.IsFaithful ↔ Function.Injective (fun a : A => πω ω a (ξω ω)) := by + constructor + · intro hω a b hab + have h : πω ω (a - b) (ξω ω) = 0 := by + simp only [πω_sub, ContinuousLinearMap.sub_apply, hab, sub_self] + exact sub_eq_zero.mp (IsFaithful.ξω_separating (ω := ω) hω (a - b) h) + · intro h_inj a ha + -- ha : ω(a*a) = 0, i.e., a ∈ Nω + -- Need to show a = 0 + have h_cyclic : πω ω a (ξω ω) = 0 := by + rw [πω_cyclic_identity] + have h_quot : (Quotient.mk'' a : A ⧸ Nω) = Quotient.mk'' 0 := by + apply Quotient.sound' + rw [CStarAlgebraIdeal.leftRel, QuotientAddGroup.leftRel_apply] + simpa using (Nω).neg_mem' ha + rw [h_quot] + exact UniformSpace.Completion.coe_zero (α := A ⧸ Nω) + have h0 : πω ω 0 (ξω ω) = 0 := by rw [πω_zero]; simp + exact h_inj (h_cyclic.trans h0.symm) + +/-- The GNS representation πω is injective when ω is faithful. + +This is a corollary of the fundamental equivalence `isFaithful_iff_separating`: +the cyclic vector ξω separates elements via the representation, which implies +that the representation itself is injective. -/ +lemma IsFaithful.injective_πω (hω : ω.IsFaithful) : Function.Injective (πω ω) := by + intro a b hab + have h_sep := (isFaithful_iff_separating (ω := ω)).mp hω + apply h_sep + simp only [hab] + end Construction end GNS diff --git a/QuantumSystem/Algebra/CStarAlgebra/State/Faithful.lean b/QuantumSystem/Algebra/CStarAlgebra/State/Faithful.lean new file mode 100644 index 0000000..3aa1d7d --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/State/Faithful.lean @@ -0,0 +1,45 @@ +module + +public import QuantumSystem.Algebra.CStarAlgebra.State + +@[expose] public section + +namespace State + +variable {𝕜 : Type*} [RCLike 𝕜] +variable {A : Type*} [NonUnitalCStarAlgebra A] [Module 𝕜 A] + +/-- A state ω on a C*-algebra A is faithful if ω(a*a) = 0 implies a = 0. + +This is equivalent to saying the GNS kernel Nω is trivial, i.e., the GNS representation +is injective. -/ +def IsFaithful (ω : State 𝕜 A) : Prop := + ∀ a : A, ω (star a * a) = 0 → a = 0 + +/-- Alternative characterization: a state is faithful iff a*a is not in the kernel +unless a = 0. -/ +lemma isFaithful_iff (ω : State 𝕜 A) : + ω.IsFaithful ↔ ∀ a : A, a ≠ 0 → ω (star a * a) ≠ 0 := by + constructor + · intro hf a ha h0 + exact ha (hf a h0) + · intro h a h0 + by_contra ha + exact h a ha h0 + +/-- Faithful states are positive definite on positive elements. -/ +lemma IsFaithful.pos_of_nonzero {ω : State ℂ A} (hω : ω.IsFaithful) {a : A} (ha : a ≠ 0) : + 0 < (ω (star a * a)).re := by + obtain ⟨r, hr⟩ := ω.positive a + have hr' : ω (star a * a) = (r : ℂ) := by + simpa [State.toLinearMap_apply] using hr + rw [hr'] + simp only [Complex.ofReal_re] + by_contra h_not_pos + push_neg at h_not_pos + have hr_nonneg : (0 : ℝ) ≤ r := r.property + have hr_zero : (r : ℝ) = 0 := le_antisymm h_not_pos hr_nonneg + have h0 : ω (star a * a) = 0 := by rw [hr', hr_zero]; simp + exact ha (hω a h0) + +end State diff --git a/QuantumSystem/Algebra/LocalNet.lean b/QuantumSystem/Algebra/LocalNet.lean new file mode 100644 index 0000000..c2594ae --- /dev/null +++ b/QuantumSystem/Algebra/LocalNet.lean @@ -0,0 +1,718 @@ +module + +public import QuantumSystem.Channel + +/-! +# Local Net of Matrix Algebras (finite-dim) + +This file defines the data of a **local net of matrix algebras** on a finite lattice. +An AQFT system assigns to each spacetime / lattice region `Λ` an +algebra `𝔄(Λ)` of observables, with **isotony** (`Λ₁ ⊆ Λ₂ ⟹ 𝔄(Λ₁) ⊆ 𝔄(Λ₂)`), **locality** +(disjoint regions commute), and—in the spacetime version—**covariance**. + +For finite-dimensional quantum spin systems, the construction specialises to: + +- a finite set of **sites** `L`, +- a local index type `ℂ^{n_x}` at each site `x ∈ L`, +- regions `Λ ∈ 𝒫(L)` (`Finset L.sites`), +- local algebra `𝔄(Λ) = ⊗_{x ∈ Λ} M_{n_x}(ℂ)` realised concretely as + `Matrix (Π s ∈ Λ, idx s) (Π s ∈ Λ, idx s) ℂ`. + +This file provides: + +1. the structure carrying the lattice + per-site Hilbert-space data, the derived region index + types, and the index-combiner equivalence relating `regionIdx Λ_total` to the product + `regionIdx Λ × regionIdx (Λ_total \ Λ)`; +2. the **restriction** (Schrödinger-picture partial trace): given regions `Λ ⊆ Λ_total`, + the restriction of a state on `𝔄(Λ_total)` to `𝔄(Λ)`. In density-matrix language this is + exactly the partial trace over the complementary region `Λ_total \ Λ`. + The restriction is the Schrödinger-picture dual of the algebra + inclusion `𝔄(Λ) ↪ 𝔄(Λ_total)`. There is no positional ("left/right") concept — the + operation is parameterised by the region itself. + +## Main definitions + +* `LocalNet` — data carrier: sites + per-site finite index types +* `LocalNet.regionIdx` — index type of a region (dependent product) +* `LocalNet.localAlgebra` — the matrix algebra at a region +* `LocalNet.densityMatrix` — density matrices at a region +* `LocalNet.combineIdx` — `regionIdx Λ × regionIdx (Λ_total \ Λ) ≃ regionIdx Λ_total` +* `LocalNet.includeAlgebra` — isotony embedding `𝔄(Λ) ↪ 𝔄(Λ_total)` +* `LocalNet.regionIdxInsertEquiv` — recursive split: `regionIdx (insert s Λ) ≃ localIdx s × regionIdx Λ` +* `LocalNet.regionIdxPairEquiv` / `regionIdxTripleEquiv` / `regionIdxTripleEquiv'` — + factorisation of `n`-element regions into per-site product types +* `LocalNet.regionIdxComplLeftSite` / `regionIdxComplRightSite` — + `regionIdx ({a, b} \ {a}) ≃ localIdx b` and its right-site dual + +The generic primitives above subsume any partite count; the bipartite / tripartite +factorisation specialisations are exposed below as `regionIdxPairEquiv`, +`regionIdxTripleEquiv`, and `regionIdxTripleEquiv'`. + +The partial-trace / restriction operations (`Matrix.restrict`, `Matrix.restrictKraus`, +`Matrix.QuantumChannel.restrict`, `DensityMatrix.restrict`, and the paper notation +`ρ ↾ Λ`) are defined in `QuantumSystem/Analysis/Matrix/PartialTrace.lean`. + +## References + +* Verch 2025 (`https://arxiv.org/abs/2507.00900`) +* Naaijkens 2012 (`https://repository.ubn.ru.nl/handle/2066/92737`) +-/ + +@[expose] public section + +/-- Data for a finite-dimensional **local net of matrix algebras** on a finite lattice. + Each site `s : sites` carries a finite index type `localIdx s` whose cardinality is the + local Hilbert-space dimension. The local algebra at a region `Λ ⊆ sites` is then the + matrix algebra on the dependent product `Π s ∈ Λ, localIdx s`. -/ +structure LocalNet where + /-- Lattice of sites — `Fintype` for the finite-dim project scope. -/ + sites : Type* + [sitesFintype : Fintype sites] + [sitesDecEq : DecidableEq sites] + /-- Local Hilbert-space index type at each site. -/ + localIdx : sites → Type* + [localFintype : ∀ s, Fintype (localIdx s)] + [localDecEq : ∀ s, DecidableEq (localIdx s)] + +namespace LocalNet + +attribute [instance] sitesFintype sitesDecEq localFintype localDecEq + +variable (L : LocalNet) + +/-- Index type of a region: dependent product of local indices over the sites in `Λ`. -/ +abbrev regionIdx (Λ : Finset L.sites) : Type _ := ∀ s : Λ, L.localIdx s.val + +/-- A region's index type stays nonempty when restricting to a sub-region: any element of + `regionIdx Λ_total` restricts to an element of `regionIdx Λ` along `h : Λ ⊆ Λ_total`. -/ +lemma regionIdx_nonempty_of_subset {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + [hne : Nonempty (L.regionIdx Λ_total)] : Nonempty (L.regionIdx Λ) := + hne.elim fun f => ⟨fun s => f ⟨s.val, h s.property⟩⟩ + +/-- Local algebra `𝔄(Λ)` at a region — concrete matrix algebra over `ℂ`. -/ +abbrev localAlgebra (Λ : Finset L.sites) : Type _ := + Matrix (L.regionIdx Λ) (L.regionIdx Λ) ℂ + +/-- Density matrices on a region (positive semi-definite, trace 1). -/ +abbrev densityMatrix (Λ : Finset L.sites) : Type _ := + DensityMatrix (L.regionIdx Λ) + +/-! ### Combining region indices via disjoint union -/ + +/-- For `Λ ⊆ Λ_total`, the index type of the larger region splits as a product: + `regionIdx Λ × regionIdx (Λ_total \ Λ) ≃ regionIdx Λ_total`. This realises the tensor + factorisation `ℋ_Λ_total = ℋ_Λ ⊗ ℋ_{Λ_total \ Λ}` underlying isotony and partial trace. -/ +def combineIdx {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + (L.regionIdx Λ × L.regionIdx (Λ_total \ Λ)) ≃ L.regionIdx Λ_total where + toFun ab s := + if hs : s.val ∈ Λ then ab.1 ⟨s.val, hs⟩ + else ab.2 ⟨s.val, Finset.mem_sdiff.mpr ⟨s.property, hs⟩⟩ + invFun f := + (fun s => f ⟨s.val, h s.property⟩, + fun s => f ⟨s.val, (Finset.mem_sdiff.mp s.property).1⟩) + left_inv := by + rintro ⟨a, b⟩ + ext1 + · funext s + have hs : s.val ∈ Λ := s.property + simp [hs] + · funext s + have hns : s.val ∉ Λ := (Finset.mem_sdiff.mp s.property).2 + simp [hns] + right_inv := by + intro f + funext s + by_cases hs : s.val ∈ Λ <;> simp [hs] + +/-! ### Pointwise behaviour of `combineIdx` -/ + +variable {L} in +@[simp] lemma combineIdx_apply_mem + {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (a : L.regionIdx Λ) (b : L.regionIdx (Λ_total \ Λ)) + (s : ↥Λ_total) (hs : s.val ∈ Λ) : + (L.combineIdx h (a, b)) s = a ⟨s.val, hs⟩ := by + simp only [combineIdx, Equiv.coe_fn_mk] + rw [dif_pos hs] + +variable {L} in +@[simp] lemma combineIdx_apply_not_mem + {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (a : L.regionIdx Λ) (b : L.regionIdx (Λ_total \ Λ)) + (s : ↥Λ_total) (hs : s.val ∉ Λ) : + (L.combineIdx h (a, b)) s + = b ⟨s.val, Finset.mem_sdiff.mpr ⟨s.property, hs⟩⟩ := by + simp only [combineIdx, Equiv.coe_fn_mk] + rw [dif_neg hs] + +/-- Cardinality factorisation for region indices induced by `combineIdx`. -/ +theorem card_regionIdx_total {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + Fintype.card (L.regionIdx Λ_total) = + Fintype.card (L.regionIdx Λ) * Fintype.card (L.regionIdx (Λ_total \ Λ)) := by + rw [← Fintype.card_prod] + exact Fintype.card_congr (L.combineIdx h).symm + +/-! ### Isotony embedding (algebra inclusion) + +The isotony embedding `𝔄(Λ) ↪ 𝔄(Λ_total)` is realised concretely as the tensor with +identity on the complement, `A ↦ A ⊗ I_{Λ_total \ Λ}`. We bundle it as a unital +`*`-algebra homomorphism (`StarAlgHom`) so that the AQFT axioms (Naaijkens 2012 §1.3 +line 211, Verch 2025 §1.2 axiom (i), Bratteli–Robinson Vol.2 §6.2) — preservation of +unit, product, and adjoint — are guaranteed at the type level. + +Pipeline: entry-wise underlying function `includeAlgebraFun` → algebraic identities +`includeAlgebraFun_{one,mul,star,...}` → bundled `includeAlgebra : _ →⋆ₐ[ℂ] _`. -/ + +/-- Entry-wise underlying function for `includeAlgebra`, defined separately so the + structural simp lemmas (`includeAlgebraFun_apply`, `..._apply_combineIdx`) reduce + by `rfl`/`simp` without going through the `StarAlgHom` coercion. -/ +noncomputable def includeAlgebraFun {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (X : L.localAlgebra Λ) : L.localAlgebra Λ_total := + Matrix.of fun s s' => + if ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 then + X ((L.combineIdx h).symm s).1 ((L.combineIdx h).symm s').1 + else 0 + +@[simp] lemma includeAlgebraFun_apply {Λ Λ_total : Finset L.sites} + (h : Λ ⊆ Λ_total) (X : L.localAlgebra Λ) (s s' : L.regionIdx Λ_total) : + L.includeAlgebraFun h X s s' = + if ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 then + X ((L.combineIdx h).symm s).1 ((L.combineIdx h).symm s').1 + else 0 := rfl + +/-- Entry-wise behaviour of `includeAlgebraFun` at combined indices: the off-diagonal + components in the complementary region vanish, leaving `X a a'` on the diagonal. -/ +@[simp] lemma includeAlgebraFun_apply_combineIdx + {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) (X : L.localAlgebra Λ) + (a a' : L.regionIdx Λ) (b b' : L.regionIdx (Λ_total \ Λ)) : + L.includeAlgebraFun h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b')) = + if b = b' then X a a' else 0 := by + simp [includeAlgebraFun, Equiv.symm_apply_apply] + +lemma includeAlgebraFun_zero {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + L.includeAlgebraFun h 0 = 0 := by + ext s s' + simp only [includeAlgebraFun_apply, Matrix.zero_apply] + split_ifs <;> rfl + +lemma includeAlgebraFun_add {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (X Y : L.localAlgebra Λ) : + L.includeAlgebraFun h (X + Y) = + L.includeAlgebraFun h X + L.includeAlgebraFun h Y := by + ext s s' + simp only [includeAlgebraFun_apply, Matrix.add_apply] + split_ifs with hbb + · rfl + · rw [add_zero] + +lemma includeAlgebraFun_smul {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (c : ℂ) (X : L.localAlgebra Λ) : + L.includeAlgebraFun h (c • X) = c • L.includeAlgebraFun h X := by + ext s s' + simp only [includeAlgebraFun_apply, Matrix.smul_apply, smul_eq_mul] + split_ifs with hbb + · rfl + · rw [mul_zero] + +lemma includeAlgebraFun_one {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + L.includeAlgebraFun h 1 = 1 := by + ext s s' + by_cases hss : s = s' + · subst hss + rw [includeAlgebraFun_apply, if_pos rfl, Matrix.one_apply_eq, Matrix.one_apply_eq] + · rw [includeAlgebraFun_apply, Matrix.one_apply_ne hss] + -- Translate `s ≠ s'` to a disjunction on the two coordinates of `(combineIdx h).symm`. + have hne : (L.combineIdx h).symm s ≠ (L.combineIdx h).symm s' := fun heq => + hss ((L.combineIdx h).symm.injective heq) + rw [Ne, Prod.ext_iff, not_and_or] at hne + by_cases h2 : ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 + · rw [if_pos h2] + rcases hne with h1 | h2' + · rw [Matrix.one_apply_ne h1] + · exact absurd h2 h2' + · rw [if_neg h2] + +lemma includeAlgebraFun_star {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (X : L.localAlgebra Λ) : + L.includeAlgebraFun h (star X) = star (L.includeAlgebraFun h X) := by + ext s s' + simp only [includeAlgebraFun_apply, Matrix.star_apply] + by_cases h2 : ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 + · rw [if_pos h2, if_pos h2.symm] + · rw [if_neg h2, if_neg (fun hh => h2 hh.symm), star_zero] + +lemma includeAlgebraFun_mul {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (X Y : L.localAlgebra Λ) : + L.includeAlgebraFun h (X * Y) = + L.includeAlgebraFun h X * L.includeAlgebraFun h Y := by + ext s s' + -- Express both rows/columns through `combineIdx` so the `_apply_combineIdx` simp lemma fires. + set sa := ((L.combineIdx h).symm s).1 with hsa + set sb := ((L.combineIdx h).symm s).2 with hsb + set s'a := ((L.combineIdx h).symm s').1 with hs'a + set s'b := ((L.combineIdx h).symm s').2 with hs'b + have hs : s = L.combineIdx h (sa, sb) := by + simp [sa, sb, Equiv.apply_symm_apply] + have hs' : s' = L.combineIdx h (s'a, s'b) := by + simp [s'a, s'b, Equiv.apply_symm_apply] + rw [hs, hs', includeAlgebraFun_apply_combineIdx, Matrix.mul_apply] + -- Reindex the RHS sum (over `regionIdx Λ_total`) via `combineIdx`. + rw [show ((L.includeAlgebraFun h X * L.includeAlgebraFun h Y) + (L.combineIdx h (sa, sb)) (L.combineIdx h (s'a, s'b))) = + ∑ p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ), + L.includeAlgebraFun h X (L.combineIdx h (sa, sb)) (L.combineIdx h p) * + L.includeAlgebraFun h Y (L.combineIdx h p) (L.combineIdx h (s'a, s'b)) from by + rw [Matrix.mul_apply] + exact ((L.combineIdx h).sum_comp _).symm] + rw [Fintype.sum_prod_type] + simp_rw [includeAlgebraFun_apply_combineIdx] + -- Goal: + -- (if sb = s'b then ∑ a'', X sa a'' * Y a'' s'a else 0) + -- = ∑ a'', ∑ b'', (if sb = b'' then X sa a'' else 0) * (if b'' = s'b then Y a'' s'a else 0) + by_cases hbb : sb = s'b + · rw [if_pos hbb] + refine Finset.sum_congr rfl fun a'' _ => ?_ + rw [Finset.sum_eq_single sb + (fun b'' _ hb'' => by rw [if_neg fun heq => hb'' heq.symm, zero_mul]) + (fun h_not_mem => absurd (Finset.mem_univ sb) h_not_mem)] + rw [if_pos rfl, ← hbb, if_pos rfl] + · rw [if_neg hbb] + refine (Finset.sum_eq_zero fun a'' _ => ?_).symm + refine Finset.sum_eq_zero fun b'' _ => ?_ + by_cases hb_sb : sb = b'' + · subst hb_sb + rw [if_neg hbb, mul_zero] + · rw [if_neg hb_sb, zero_mul] + +lemma includeAlgebraFun_algebraMap {Λ Λ_total : Finset L.sites} + (h : Λ ⊆ Λ_total) (c : ℂ) : + L.includeAlgebraFun h ((algebraMap ℂ (L.localAlgebra Λ)) c) = + (algebraMap ℂ (L.localAlgebra Λ_total)) c := by + rw [Algebra.algebraMap_eq_smul_one, Algebra.algebraMap_eq_smul_one, + includeAlgebraFun_smul, includeAlgebraFun_one] + +/-- **Isotony embedding** `𝔄(Λ) ↪ 𝔄(Λ_total)`: tensor a local matrix with the identity on + the complementary region. Realises the inclusion `A ↦ A ⊗ I_{Λ_total \ Λ}` from + Naaijkens 2012 §1.3 line 211, Verch 2025 §1.2 axiom (i), Bratteli–Robinson Vol.2 §6.2. + Bundled as a unital `*`-algebra homomorphism so that `map_one`, `map_mul`, `map_star` + are available via the `StarAlgHom` API. Entry-wise: + `(includeAlgebra h X) s s' = X (combineIdx⁻¹ s).1 (combineIdx⁻¹ s').1` when the + complementary indices match, else `0`. -/ +noncomputable def includeAlgebra {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + L.localAlgebra Λ →⋆ₐ[ℂ] L.localAlgebra Λ_total where + toFun := L.includeAlgebraFun h + map_zero' := L.includeAlgebraFun_zero h + map_add' := L.includeAlgebraFun_add h + map_one' := L.includeAlgebraFun_one h + map_mul' := L.includeAlgebraFun_mul h + commutes' := L.includeAlgebraFun_algebraMap h + map_star' := L.includeAlgebraFun_star h + +/-- Entry-wise unfolding of `includeAlgebra h X`: at indices `(s, s')` of the larger + region, the embedded matrix equals `X` on the diagonal (in the complementary index) + and zero off-diagonal. -/ +@[simp] lemma includeAlgebra_apply {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (X : L.localAlgebra Λ) (s s' : L.regionIdx Λ_total) : + L.includeAlgebra h X s s' = + if ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 then + X ((L.combineIdx h).symm s).1 ((L.combineIdx h).symm s').1 + else 0 := rfl + +/-- **Injectivity of the isotony embedding** (the `↪` of `𝔄(Λ) ↪ 𝔄(Λ_total)`): under + the standing AQFT non-degeneracy assumption that the complementary region has a + non-empty index type, `includeAlgebra h` is injective as a map of `*`-algebras. -/ +theorem includeAlgebra_injective {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + [hne : Nonempty (L.regionIdx (Λ_total \ Λ))] : + Function.Injective (L.includeAlgebra h) := by + rw [injective_iff_map_eq_zero] + intro X hX + ext a a' + obtain ⟨b⟩ := hne + have heq : L.includeAlgebra h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) = + 0 := by rw [hX]; rfl + have key : L.includeAlgebra h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) = + X a a' := by + change L.includeAlgebraFun h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) = X a a' + rw [includeAlgebraFun_apply_combineIdx, if_pos rfl] + rw [key] at heq + simpa using heq + +/-! ### Region equivalences + +Generic equivalences over arbitrary `Fintype` site sets — used both directly (for any +finite site set) and as building blocks for the bipartite (`regionIdxPairEquiv`) and +tripartite (`regionIdxTripleEquiv`, `regionIdxTripleEquiv'`) factorisations below. -/ + +variable (L : LocalNet) + +/-- Transport `regionIdx` along a Finset equality. -/ +def regionIdxCongr {Λ Λ' : Finset L.sites} (h : Λ = Λ') : + L.regionIdx Λ ≃ L.regionIdx Λ' := + h ▸ Equiv.refl _ + +@[simp] lemma regionIdxCongr_apply + {Λ Λ' : Finset L.sites} (h : Λ = Λ') (x : L.regionIdx Λ) + {s : L.sites} (hs : s ∈ Λ) (hs' : s ∈ Λ') : + (L.regionIdxCongr h x) ⟨s, hs'⟩ = x ⟨s, hs⟩ := by + subst h + rfl + +/-- Singleton region: `regionIdx {s} ≃ localIdx s`. -/ +def singletonRegionIdxEquiv (s : L.sites) : + L.regionIdx ({s} : Finset L.sites) ≃ L.localIdx s where + toFun f := f ⟨s, Finset.mem_singleton.mpr rfl⟩ + invFun x := fun ⟨v, hv⟩ => + (Finset.mem_singleton.mp hv).symm ▸ x + left_inv f := by + funext ⟨v, hv⟩ + have hvs : v = s := Finset.mem_singleton.mp hv + subst hvs + rfl + right_inv x := rfl + +@[simp] private lemma singletonRegionIdxEquiv_apply (s : L.sites) + (f : L.regionIdx ({s} : Finset L.sites)) : + L.singletonRegionIdxEquiv s f = f ⟨s, Finset.mem_singleton.mpr rfl⟩ := rfl + +/-! ### Generic n-partite primitives + +Building blocks for any finite site set: `regionIdx ∅ ≃ PUnit`, an `insert`-based +recursive split, and the universal product form `regionIdx Finset.univ ≃ Π s, localIdx s`. +Two- and three-element factor equivs are derived from the recursive split — adding more +partite counts (4, 5, ...) is now a one-liner with no new boilerplate. -/ + +/-- The empty region: `regionIdx ∅ ≃ PUnit`. The dependent product over the empty + subtype has a unique element. -/ +def regionIdxEmptyEquiv : L.regionIdx (∅ : Finset L.sites) ≃ PUnit where + toFun _ := PUnit.unit + invFun _ := fun s => absurd s.property (Finset.notMem_empty _) + left_inv f := by + funext s + exact absurd s.property (Finset.notMem_empty _) + right_inv _ := rfl + +/-- **Recursive split (region composition rule)**: for `s ∉ Λ`, + `regionIdx (insert s Λ) ≃ localIdx s × regionIdx Λ`. + + This is the core composition primitive — repeated application gives factorisation + of any finitely-enumerated region into per-site factors. Built from `combineIdx` + applied to the singleton `{s} ⊆ insert s Λ`, with the complementary region + `insert s Λ \ {s}` reducing to `Λ`. -/ +def regionIdxInsertEquiv {s : L.sites} {Λ : Finset L.sites} (hs : s ∉ Λ) : + L.regionIdx (insert s Λ) ≃ L.localIdx s × L.regionIdx Λ := + have h_sub : ({s} : Finset L.sites) ⊆ insert s Λ := + Finset.singleton_subset_iff.mpr (Finset.mem_insert_self s Λ) + have h_compl_eq : insert s Λ \ {s} = Λ := by + rw [Finset.insert_sdiff_of_mem _ (Finset.mem_singleton_self s), + Finset.sdiff_eq_self_iff_disjoint.mpr (Finset.disjoint_singleton_right.mpr hs)] + (L.combineIdx h_sub).symm.trans + (Equiv.prodCongr (L.singletonRegionIdxEquiv s) (L.regionIdxCongr h_compl_eq)) + +/-- The universal region: `regionIdx Finset.univ ≃ Π s : sites, localIdx s`. + Collapses the `Finset.univ`-subtype back to the underlying type. -/ +def regionIdxUnivEquiv : L.regionIdx (Finset.univ : Finset L.sites) ≃ + ∀ s : L.sites, L.localIdx s where + toFun f s := f ⟨s, Finset.mem_univ s⟩ + invFun g := fun ⟨s, _⟩ => g s + left_inv f := by funext ⟨s, _⟩; rfl + right_inv _ := rfl + +/-- **2-element factorisation**: `regionIdx {a, b} ≃ localIdx a × localIdx b` when + `a ≠ b`. Direct definition with concrete `toFun` so both projections evaluate by `rfl`. -/ +def regionIdxPairEquiv {a b : L.sites} (hab : a ≠ b) : + L.regionIdx ({a, b} : Finset L.sites) ≃ L.localIdx a × L.localIdx b where + toFun f := + (f ⟨a, Finset.mem_insert_self a {b}⟩, + f ⟨b, Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)⟩) + invFun ab := fun ⟨s, hs⟩ => + if h : s = a then h ▸ ab.1 + else + have hsb : s = b := by + rcases Finset.mem_insert.mp hs with h' | h' + · exact absurd h' h + · exact Finset.mem_singleton.mp h' + hsb ▸ ab.2 + left_inv f := by + funext ⟨s, hs⟩ + by_cases hsa : s = a + · subst hsa + simp + · have hsb : s = b := by + rcases Finset.mem_insert.mp hs with h' | h' + · exact absurd h' hsa + · exact Finset.mem_singleton.mp h' + subst hsb + simp [hsa] + right_inv ab := by + have hba : b ≠ a := fun h_eq => hab h_eq.symm + ext1 + · simp + · simp [hba] + +/-- Closed-form unfolding of `regionIdxPairEquiv` as a pair. -/ +@[simp] lemma regionIdxPairEquiv_apply {a b : L.sites} (hab : a ≠ b) + (f : L.regionIdx ({a, b} : Finset L.sites)) : + L.regionIdxPairEquiv hab f = + (f ⟨a, Finset.mem_insert_self a {b}⟩, + f ⟨b, Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)⟩) := rfl + +/-- Closed-form first projection of `regionIdxPairEquiv` — picks out the value at site `a`. -/ +@[simp] private lemma regionIdxPairEquiv_apply_fst {a b : L.sites} (hab : a ≠ b) + (f : L.regionIdx ({a, b} : Finset L.sites)) : + (L.regionIdxPairEquiv hab f).1 = f ⟨a, Finset.mem_insert_self a {b}⟩ := rfl + +/-- Closed-form second projection of `regionIdxPairEquiv` — picks out the value at site `b`. -/ +@[simp] private lemma regionIdxPairEquiv_apply_snd {a b : L.sites} (hab : a ≠ b) + (f : L.regionIdx ({a, b} : Finset L.sites)) : + (L.regionIdxPairEquiv hab f).2 = + f ⟨b, Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)⟩ := rfl + +/-- **3-element factorisation (right-associated)**: + `regionIdx {a, b, c} ≃ localIdx a × localIdx b × localIdx c` when the sites are + pairwise distinct. Direct definition with concrete `toFun` so all three projections + evaluate by `rfl`. -/ +def regionIdxTripleEquiv {a b c : L.sites} (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) : + L.regionIdx ({a, b, c} : Finset L.sites) ≃ + L.localIdx a × L.localIdx b × L.localIdx c where + toFun f := + (f ⟨a, Finset.mem_insert_self a {b, c}⟩, + f ⟨b, Finset.mem_insert_of_mem (Finset.mem_insert_self b {c})⟩, + f ⟨c, Finset.mem_insert_of_mem + (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl))⟩) + invFun abc := fun ⟨s, hs⟩ => + if h : s = a then h ▸ abc.1 + else if h' : s = b then h' ▸ abc.2.1 + else + have hsc : s = c := by + rcases Finset.mem_insert.mp hs with hh | hh + · exact absurd hh h + · rcases Finset.mem_insert.mp hh with hh | hh + · exact absurd hh h' + · exact Finset.mem_singleton.mp hh + hsc ▸ abc.2.2 + left_inv f := by + funext ⟨s, hs⟩ + by_cases hsa : s = a + · subst hsa; simp + by_cases hsb : s = b + · subst hsb; simp [hsa] + have hsc : s = c := by + rcases Finset.mem_insert.mp hs with hh | hh + · exact absurd hh hsa + · rcases Finset.mem_insert.mp hh with hh | hh + · exact absurd hh hsb + · exact Finset.mem_singleton.mp hh + subst hsc; simp [hsa, hsb] + right_inv abc := by + have hba : b ≠ a := fun h => hab h.symm + have hca : c ≠ a := fun h => hac h.symm + have hcb : c ≠ b := fun h => hbc h.symm + ext1 + · simp + · ext1 + · simp [hba] + · simp [hca, hcb] + +/-- Closed-form first projection of `regionIdxTripleEquiv` — value at site `a`. -/ +@[simp] private lemma regionIdxTripleEquiv_apply_fst {a b c : L.sites} + (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) + (f : L.regionIdx ({a, b, c} : Finset L.sites)) : + (L.regionIdxTripleEquiv hab hbc hac f).1 = f ⟨a, Finset.mem_insert_self a {b, c}⟩ := rfl + +/-- Second projection of `regionIdxTripleEquiv` — value at site `b`. -/ +@[simp] private lemma regionIdxTripleEquiv_apply_snd_fst {a b c : L.sites} + (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) + (f : L.regionIdx ({a, b, c} : Finset L.sites)) : + (L.regionIdxTripleEquiv hab hbc hac f).2.1 = + f ⟨b, Finset.mem_insert_of_mem (Finset.mem_insert_self b {c})⟩ := rfl + +/-- Third projection of `regionIdxTripleEquiv` — value at site `c`. -/ +@[simp] private lemma regionIdxTripleEquiv_apply_snd_snd {a b c : L.sites} + (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) + (f : L.regionIdx ({a, b, c} : Finset L.sites)) : + (L.regionIdxTripleEquiv hab hbc hac f).2.2 = + f ⟨c, Finset.mem_insert_of_mem + (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl))⟩ := rfl + +/-- **3-element factorisation (left-associated)**: the alternate + `regionIdx {a, b, c} ≃ (localIdx a × localIdx b) × localIdx c` view, used when the + bipartite split sees the pair `(a, b)` together against `c`. -/ +def regionIdxTripleEquiv' {a b c : L.sites} (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) : + L.regionIdx ({a, b, c} : Finset L.sites) ≃ + (L.localIdx a × L.localIdx b) × L.localIdx c := + (L.regionIdxTripleEquiv hab hbc hac).trans (Equiv.prodAssoc _ _ _).symm + +/-- **Pair-complement on the left site**: for the two-element region `{a, b}` with + `a ≠ b`, the index type of the complement of `{a}` reduces to `localIdx b`. + Direct realisation of "evaluate at the unique remaining site `b`". -/ +def regionIdxComplLeftSite {a b : L.sites} (hab : a ≠ b) : + L.regionIdx (({a, b} : Finset L.sites) \ {a}) ≃ L.localIdx b where + toFun f := f ⟨b, Finset.mem_sdiff.mpr + ⟨Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl), + Finset.notMem_singleton.mpr (fun h => hab h.symm)⟩⟩ + invFun y := fun ⟨s, hs⟩ => + have hsb : s = b := by + simp only [Finset.mem_sdiff, Finset.mem_insert, Finset.mem_singleton] at hs + rcases hs.1 with h | h + · exact absurd h hs.2 + · exact h + hsb ▸ y + left_inv f := by + funext ⟨s, hs⟩ + have hsb : s = b := by + simp only [Finset.mem_sdiff, Finset.mem_insert, Finset.mem_singleton] at hs + rcases hs.1 with h | h + · exact absurd h hs.2 + · exact h + subst hsb + rfl + right_inv y := rfl + +/-- **Pair-complement on the right site**: dual of `regionIdxComplLeftSite` — the + complement of `{b}` in `{a, b}` reduces to `localIdx a`. -/ +def regionIdxComplRightSite {a b : L.sites} (hab : a ≠ b) : + L.regionIdx (({a, b} : Finset L.sites) \ {b}) ≃ L.localIdx a where + toFun f := f ⟨a, Finset.mem_sdiff.mpr + ⟨Finset.mem_insert_self a {b}, Finset.notMem_singleton.mpr hab⟩⟩ + invFun y := fun ⟨s, hs⟩ => + have hsa : s = a := by + simp only [Finset.mem_sdiff, Finset.mem_insert, Finset.mem_singleton] at hs + rcases hs.1 with h | h + · exact h + · exact absurd h hs.2 + hsa ▸ y + left_inv f := by + funext ⟨s, hs⟩ + have hsa : s = a := by + simp only [Finset.mem_sdiff, Finset.mem_insert, Finset.mem_singleton] at hs + rcases hs.1 with h | h + · exact h + · exact absurd h hs.2 + subst hsa + rfl + right_inv y := rfl + +/-- **Triple-complement, first site**: for pairwise-distinct `a, b, c : L.sites`, + `regionIdx ({a, b, c} \ {a}) ≃ localIdx b × localIdx c`. Direct construction + (no `regionIdxCongr` transport) so pointwise evaluation reduces by computation, + enabling the marginal-compatibility helpers used by SSA-style proofs. -/ +noncomputable def regionIdxComplFirst {a b c : L.sites} + (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) : + L.regionIdx (({a, b, c} : Finset L.sites) \ ({a} : Finset _)) ≃ + L.localIdx b × L.localIdx c where + toFun f := + (f ⟨b, Finset.mem_sdiff.mpr ⟨ + Finset.mem_insert_of_mem (Finset.mem_insert_self _ _), + Finset.notMem_singleton.mpr hab.symm⟩⟩, + f ⟨c, Finset.mem_sdiff.mpr ⟨ + Finset.mem_insert_of_mem (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)), + Finset.notMem_singleton.mpr hac.symm⟩⟩) + invFun xbc := fun ⟨v, hv⟩ => + if h : v = b then h ▸ xbc.1 + else + have hvc : v = c := by + rw [Finset.mem_sdiff] at hv + rcases Finset.mem_insert.mp hv.1 with rfl | h2 + · exact absurd (Finset.mem_singleton_self _) hv.2 + · rcases Finset.mem_insert.mp h2 with rfl | h3 + · exact absurd rfl h + · exact Finset.mem_singleton.mp h3 + hvc ▸ xbc.2 + left_inv f := by + funext ⟨v, hv⟩ + rw [Finset.mem_sdiff] at hv + by_cases hvb : v = b + · subst hvb; simp + · have hvc : v = c := by + rcases Finset.mem_insert.mp hv.1 with rfl | h2 + · exact absurd (Finset.mem_singleton_self _) hv.2 + · rcases Finset.mem_insert.mp h2 with rfl | h3 + · exact absurd rfl hvb + · exact Finset.mem_singleton.mp h3 + subst hvc; simp [hvb] + right_inv := by + rintro ⟨xb, xc⟩ + have hcb : c ≠ b := hbc.symm + ext1 <;> simp [hcb] + +/-- **Triple-complement, first two sites**: for pairwise-distinct + `a, b, c : L.sites`, `regionIdx ({a, b, c} \ {a, b}) ≃ localIdx c`. Direct + construction (no `regionIdxCongr` transport) so pointwise evaluation reduces + by computation. -/ +noncomputable def regionIdxComplPairFirstTwo {a b c : L.sites} + (hac : a ≠ c) (hbc : b ≠ c) : + L.regionIdx (({a, b, c} : Finset L.sites) \ ({a, b} : Finset _)) ≃ L.localIdx c where + toFun f := f ⟨c, Finset.mem_sdiff.mpr ⟨ + Finset.mem_insert_of_mem (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)), + by simp only [Finset.mem_insert, Finset.mem_singleton, not_or] + exact ⟨hac.symm, hbc.symm⟩⟩⟩ + invFun xc := fun ⟨v, hv⟩ => + have hvc : v = c := by + rw [Finset.mem_sdiff] at hv + rcases Finset.mem_insert.mp hv.1 with rfl | h2 + · exact absurd (Finset.mem_insert_self _ _) hv.2 + · rcases Finset.mem_insert.mp h2 with rfl | h3 + · exact absurd + (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)) hv.2 + · exact Finset.mem_singleton.mp h3 + hvc ▸ xc + left_inv f := by + funext ⟨v, hv⟩ + rw [Finset.mem_sdiff] at hv + have hvc : v = c := by + rcases Finset.mem_insert.mp hv.1 with rfl | h2 + · exact absurd (Finset.mem_insert_self _ _) hv.2 + · rcases Finset.mem_insert.mp h2 with rfl | h3 + · exact absurd + (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)) hv.2 + · exact Finset.mem_singleton.mp h3 + subst hvc; rfl + right_inv := by intro xc; rfl + +/-- **Triple-complement, last two sites**: for pairwise-distinct + `a, b, c : L.sites`, `regionIdx ({a, b, c} \ {b, c}) ≃ localIdx a`. Direct + construction (no `regionIdxCongr` transport) so pointwise evaluation reduces + by computation. -/ +noncomputable def regionIdxComplPairLastTwo {a b c : L.sites} + (hab : a ≠ b) (hac : a ≠ c) : + L.regionIdx (({a, b, c} : Finset L.sites) \ ({b, c} : Finset _)) ≃ L.localIdx a where + toFun f := f ⟨a, Finset.mem_sdiff.mpr ⟨ + Finset.mem_insert_self _ _, + by simp only [Finset.mem_insert, Finset.mem_singleton, not_or] + exact ⟨hab, hac⟩⟩⟩ + invFun xa := fun ⟨v, hv⟩ => + have hva : v = a := by + rw [Finset.mem_sdiff] at hv + rcases Finset.mem_insert.mp hv.1 with rfl | h2 + · rfl + · rcases Finset.mem_insert.mp h2 with rfl | h3 + · exact absurd (Finset.mem_insert_self _ _) hv.2 + · have hvc : v = c := Finset.mem_singleton.mp h3 + subst hvc + exact absurd + (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)) hv.2 + hva ▸ xa + left_inv f := by + funext ⟨v, hv⟩ + rw [Finset.mem_sdiff] at hv + have hva : v = a := by + rcases Finset.mem_insert.mp hv.1 with rfl | h2 + · rfl + · rcases Finset.mem_insert.mp h2 with rfl | h3 + · exact absurd (Finset.mem_insert_self _ _) hv.2 + · have hvc : v = c := Finset.mem_singleton.mp h3 + subst hvc + exact absurd + (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)) hv.2 + subst hva; rfl + right_inv := by intro xa; rfl + +/-- `{b} ⊆ {a, b, c}` for any sites `a, b, c`. Pure Finset membership; the + arguments `a` and `c` are kept positional so call sites can pass them + explicitly when convenient. -/ +lemma singleton_b_subset_triple (a b c : L.sites) : + ({b} : Finset L.sites) ⊆ ({a, b, c} : Finset L.sites) := + Finset.singleton_subset_iff.mpr + (Finset.mem_insert_of_mem (Finset.mem_insert_self b _)) + +end LocalNet diff --git a/QuantumSystem/Algebra/Star/DoubleCommutant/WOTClosedSubAlgebra.lean b/QuantumSystem/Algebra/Star/DoubleCommutant/WOTClosedSubAlgebra.lean index 7f6cd6b..36de546 100644 --- a/QuantumSystem/Algebra/Star/DoubleCommutant/WOTClosedSubAlgebra.lean +++ b/QuantumSystem/Algebra/Star/DoubleCommutant/WOTClosedSubAlgebra.lean @@ -19,7 +19,7 @@ is equivalent to being closed in the weak operator topology (WOT). The "hard half" is the direction -*WOT-closed `*-`subalgebra* $\Rightarrow$ *equals its double commutant*. +*WOT-closed `*-`subalgebra* ⇒ *equals its double commutant*. We state and prove this as an equality of sets of operators `Set.centralizer (Set.centralizer (A : Set B)) = (A : Set B)`. diff --git a/QuantumSystem/Algebra/VonNeumannAlgebra/Basic.lean b/QuantumSystem/Algebra/VonNeumannAlgebra/Basic.lean deleted file mode 100644 index d128976..0000000 --- a/QuantumSystem/Algebra/VonNeumannAlgebra/Basic.lean +++ /dev/null @@ -1,270 +0,0 @@ -module - -public import Mathlib.Analysis.VonNeumannAlgebra.Basic -public import QuantumSystem.Algebra.CStarAlgebra.GNS.Construction -public import QuantumSystem.Algebra.Star.DoubleCommutant.SOTClosedSubAlgebra - -@[expose] public section - -/-! -# Weak-operator-topology and strong-operator-topology von Neumann algebras - -Mathlib defines the concrete notion `VonNeumannAlgebra H` via the double commutant. - -For many arguments, one instead wants the alternative characterization: a *-subalgebra of -`𝓑(H)` that is **closed in the weak operator topology (WOT)** or the **strong operator -topology (SOT)**. The equivalence between these characterizations and the double commutant -is the von Neumann double commutant theorem. - -This file sets up: - -## Main definitions - -* `VonNeumannAlgebraWOT`: a WOT-closed `StarSubalgebra` of `H →L[ℂ] H`. -* `VonNeumannAlgebraSOT`: a SOT-closed `StarSubalgebra` of `H →L[ℂ] H`. -* `GNS.Construction.vonNeumannAlgebraGenerated`: the double commutant of a set of operators. -* `GNS.Construction.gnsVonNeumannAlgebra`: the von Neumann algebra generated by the GNS - representation. - -## Main results - -* `VonNeumannAlgebra.isWOTClosed`: any `VonNeumannAlgebra` (double-commutant) is WOT-closed. -* `VonNeumannAlgebra.isSOTClosed`: any `VonNeumannAlgebra` (double-commutant) is SOT-closed. -* `isWOTClosed_iff_doubleCommutant`: a *-subalgebra is WOT-closed iff it equals its double - commutant. -* `isSOTClosed_iff_doubleCommutant`: a *-subalgebra is SOT-closed iff it equals its double - commutant. -* `VonNeumannAlgebraWOT.coe_coe_eq`: the two notions of von Neumann algebra (WOT and - double-commutant) are carrier-equivalent. -* `VonNeumannAlgebraSOT.coe_coe_eq`: the two notions of von Neumann algebra (SOT and - double-commutant) are carrier-equivalent. -* `VonNeumannAlgebra.coe_coe_eq_wot`, `VonNeumannAlgebra.coe_coe_eq_sot`: round-trip - equivalences. -* `GNS.Construction.gnsVonNeumannAlgebra_contains_πω`: the GNS operators lie in the generated - von Neumann algebra. --/ - -section WOTClosed - -open WeakOperatorTopology - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] - -/-- A concrete von Neumann algebra defined as a WOT-closed *-subalgebra of `𝓑(H)`. - -This is the standard alternative definition in functional analysis. --/ -structure VonNeumannAlgebraWOT (H : Type*) [NormedAddCommGroup H] [InnerProductSpace ℂ H] - [CompleteSpace H] extends StarSubalgebra ℂ (H →L[ℂ] H) where - /-- The carrier is closed in the weak operator topology (via the WOT type-copy). -/ - wot_closed' : IsWOTClosed (H := H) (carrier : Set (H →L[ℂ] H)) - -namespace VonNeumannAlgebraWOT - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] - -/-- Coercion to `VonNeumannAlgebra` via double commutant. Always valid (no hard half needed). -/ -noncomputable instance : CoeOut (VonNeumannAlgebraWOT H) (VonNeumannAlgebra H) where - coe S := { - toStarSubalgebra := - StarSubalgebra.centralizer ℂ (Set.centralizer (S.toStarSubalgebra : Set (H →L[ℂ] H))) - centralizer_centralizer' := by - classical - let sset : Set (H →L[ℂ] H) := (S.toStarSubalgebra : Set (H →L[ℂ] H)) - have hsstar : star sset = sset := by simp [sset] - have hcommstar : star (Set.centralizer sset) = Set.centralizer sset := by - simp [Set.star_centralizer, hsstar] - simp [StarSubalgebra.coe_centralizer, sset, hcommstar] - } - -end VonNeumannAlgebraWOT - -/-- Any `VonNeumannAlgebra` (double-commutant) is WOT-closed. -/ -theorem VonNeumannAlgebra.isWOTClosed (S : VonNeumannAlgebra H) : - IsWOTClosed (H := H) (S : Set (H →L[ℂ] H)) := by - simpa [S.centralizer_centralizer] using - isWOTClosed_centralizer_centralizer (H := H) (S := (S : Set (H →L[ℂ] H))) - -/-- Any `VonNeumannAlgebra` can be viewed as a WOT-closed *-subalgebra. -/ -noncomputable instance : CoeOut (VonNeumannAlgebra H) (VonNeumannAlgebraWOT H) where - coe S := ⟨S.toStarSubalgebra, S.isWOTClosed⟩ - -/-! ### Equivalence of the two definitions -/ - -/-- The double commutant theorem for WOT-closed *-subalgebras: closedness ↔ equals double commutant. --/ -theorem isWOTClosed_iff_doubleCommutant (S : StarSubalgebra ℂ (H →L[ℂ] H)) : - IsWOTClosed (H := H) (S : Set (H →L[ℂ] H)) ↔ - Set.centralizer (Set.centralizer (S : Set (H →L[ℂ] H))) = (S : Set (H →L[ℂ] H)) := - ⟨WOTClosedSubalgebra.doubleCommutant_eq_of_isWOTClosed (H := H) S, fun hcc => by - simpa [hcc] using isWOTClosed_centralizer_centralizer (H := H) (S := (S : Set (H →L[ℂ] H)))⟩ - -/-- The two notions of von Neumann algebra are carrier-equivalent. -/ -theorem VonNeumannAlgebraWOT.coe_coe_eq (S : VonNeumannAlgebraWOT H) : - ((S : VonNeumannAlgebra H) : Set (H →L[ℂ] H)) = (S.toStarSubalgebra : Set (H →L[ℂ] H)) := by - classical - let sset : Set (H →L[ℂ] H) := (S.toStarSubalgebra : Set (H →L[ℂ] H)) - have hsstar : star sset = sset := by simp [sset] - have hcommstar : star (Set.centralizer sset) = Set.centralizer sset := by - simp [Set.star_centralizer, hsstar] - have hcc := (isWOTClosed_iff_doubleCommutant S.toStarSubalgebra).1 S.wot_closed' - simp [StarSubalgebra.coe_centralizer, hcommstar, hcc, sset] - -/-- Round-trip: `VonNeumannAlgebra → WOT → VonNeumannAlgebra` gives carrier equality. -/ -theorem VonNeumannAlgebra.coe_coe_eq_wot (S : VonNeumannAlgebra H) : - ((S : VonNeumannAlgebraWOT H) : VonNeumannAlgebra H) = S := by - have h : ((S : VonNeumannAlgebraWOT H) : VonNeumannAlgebra H).toStarSubalgebra.carrier = - S.toStarSubalgebra.carrier := by - have := VonNeumannAlgebraWOT.coe_coe_eq (S : VonNeumannAlgebraWOT H) - simp only [VonNeumannAlgebra.coe_toStarSubalgebra] at this - exact this - ext x - exact Set.ext_iff.1 h x - -end WOTClosed - -/-! -## Strong-operator-topology von Neumann algebras - -Parallel to the WOT characterization, we define SOT-closed *-subalgebras and prove that -double commutants are SOT-closed. --/ - -section SOTClosed - -open StrongOperatorTopology WeakOperatorTopology - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] - -/-- A concrete von Neumann algebra defined as a SOT-closed *-subalgebra of `𝓑(H)`. - -This is equivalent to WOT-closedness for *-subalgebras (by the double commutant theorem). --/ -structure VonNeumannAlgebraSOT (H : Type*) [NormedAddCommGroup H] [InnerProductSpace ℂ H] - [CompleteSpace H] extends StarSubalgebra ℂ (H →L[ℂ] H) where - /-- The carrier is closed in the strong operator topology. -/ - sot_closed' : IsSOTClosed (H := H) (carrier : Set (H →L[ℂ] H)) - -namespace VonNeumannAlgebraSOT - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] - -/-- Coercion to `VonNeumannAlgebra` via double commutant. Always valid (no hard half needed). -/ -noncomputable instance : CoeOut (VonNeumannAlgebraSOT H) (VonNeumannAlgebra H) where - coe S := { - toStarSubalgebra := - StarSubalgebra.centralizer ℂ (Set.centralizer (S.toStarSubalgebra : Set (H →L[ℂ] H))) - centralizer_centralizer' := by - classical - let sset : Set (H →L[ℂ] H) := (S.toStarSubalgebra : Set (H →L[ℂ] H)) - have hsstar : star sset = sset := by simp [sset] - have hcommstar : star (Set.centralizer sset) = Set.centralizer sset := by - simp [Set.star_centralizer, hsstar] - simp [StarSubalgebra.coe_centralizer, sset, hcommstar] - } - -end VonNeumannAlgebraSOT - -/-- Any `VonNeumannAlgebra` (double-commutant) is SOT-closed. -/ -theorem VonNeumannAlgebra.isSOTClosed (S : VonNeumannAlgebra H) : - IsSOTClosed (H := H) (S : Set (H →L[ℂ] H)) := by - simpa [S.centralizer_centralizer] using - isSOTClosed_centralizer_centralizer (H := H) (S := (S : Set (H →L[ℂ] H))) - -/-- Any `VonNeumannAlgebra` can be viewed as a SOT-closed *-subalgebra. -/ -noncomputable instance : CoeOut (VonNeumannAlgebra H) (VonNeumannAlgebraSOT H) where - coe S := ⟨S.toStarSubalgebra, S.isSOTClosed⟩ - -/-- The double commutant theorem for SOT-closed *-subalgebras. - -A SOT-closed *-subalgebra equals its double commutant. --/ -theorem isSOTClosed_iff_doubleCommutant (S : StarSubalgebra ℂ (H →L[ℂ] H)) : - IsSOTClosed (H := H) (S : Set (H →L[ℂ] H)) ↔ - Set.centralizer (Set.centralizer (S : Set (H →L[ℂ] H))) = (S : Set (H →L[ℂ] H)) := - ⟨SOTClosedSubalgebra.doubleCommutant_eq_of_isSOTClosed S, fun hcc => by - -- The reverse direction: A'' = A implies A is SOT-closed (easy half). - simpa [hcc] using isSOTClosed_centralizer_centralizer (H := H) (S := (S : Set (H →L[ℂ] H)))⟩ - -/-- The two notions of SOT von Neumann algebra are carrier-equivalent. -/ -theorem VonNeumannAlgebraSOT.coe_coe_eq (S : VonNeumannAlgebraSOT H) : - ((S : VonNeumannAlgebra H) : Set (H →L[ℂ] H)) = (S.toStarSubalgebra : Set (H →L[ℂ] H)) := by - classical - let sset : Set (H →L[ℂ] H) := (S.toStarSubalgebra : Set (H →L[ℂ] H)) - have hsstar : star sset = sset := by simp [sset] - have hcommstar : star (Set.centralizer sset) = Set.centralizer sset := by - simp [Set.star_centralizer, hsstar] - have hcc := (isSOTClosed_iff_doubleCommutant S.toStarSubalgebra).1 S.sot_closed' - simp [StarSubalgebra.coe_centralizer, hcommstar, hcc, sset] - -/-- Round-trip: `VonNeumannAlgebra → SOT → VonNeumannAlgebra` gives carrier equality. -/ -theorem VonNeumannAlgebra.coe_coe_eq_sot (S : VonNeumannAlgebra H) : - ((S : VonNeumannAlgebraSOT H) : VonNeumannAlgebra H) = S := by - have h : ((S : VonNeumannAlgebraSOT H) : VonNeumannAlgebra H).toStarSubalgebra.carrier = - S.toStarSubalgebra.carrier := by - have := VonNeumannAlgebraSOT.coe_coe_eq (S : VonNeumannAlgebraSOT H) - simp only [VonNeumannAlgebra.coe_toStarSubalgebra] at this - exact this - ext x - exact Set.ext_iff.1 h x - -end SOTClosed - -namespace GNS -namespace Construction - -open WeakOperatorTopology - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] -variable {A : Type*} [NonUnitalCStarAlgebra A] -variable (ω : State ℂ A) - -/-- The (concrete) von Neumann algebra generated by a set of operators: the double commutant. - -This definition does *not* require any topology: it is purely algebraic. -In classical functional analysis, by the von Neumann double commutant theorem, this coincides -with the weak operator closure of the unital *-algebra generated by the set. --/ -noncomputable def vonNeumannAlgebraGenerated (S : Set (H →L[ℂ] H)) : VonNeumannAlgebra H where - -- We take the commutant of the commutant (as `StarSubalgebra.centralizer`). - -- Unfolding shows the underlying set is `(S ∪ star S).centralizer.centralizer`. - toStarSubalgebra := StarSubalgebra.centralizer ℂ (StarSubalgebra.centralizer ℂ S : Set (H →L[ℂ] H)) - centralizer_centralizer' := by - -- A commutant is fixed by the double-commutant operation. - simp - -@[simp] -theorem coe_vonNeumannAlgebraGenerated (S : Set (H →L[ℂ] H)) : - (vonNeumannAlgebraGenerated (H := H) S : Set (H →L[ℂ] H)) = - (S ∪ star S).centralizer.centralizer := by - -- This is exactly `StarSubalgebra.coe_centralizer_centralizer` specialized to `A = H →L[ℂ] H`. - simpa [vonNeumannAlgebraGenerated] using - (StarSubalgebra.coe_centralizer_centralizer (R := ℂ) (A := H →L[ℂ] H) (s := S)) - -/-- The generating set is contained in its double commutant. -/ -theorem subset_vonNeumannAlgebraGenerated (S : Set (H →L[ℂ] H)) : - S ⊆ (vonNeumannAlgebraGenerated (H := H) S : Set (H →L[ℂ] H)) := by - intro x hx - -- Work in the explicit set `(S ∪ star S)''`. - have : x ∈ (S ∪ star S).centralizer.centralizer := by - intro y hy - -- `hy x` gives `x * y = y * x`, so flip sides. - exact (hy x (Or.inl hx)).symm - simpa [coe_vonNeumannAlgebraGenerated (H := H) (S := S)] using this - -/-- The concrete von Neumann algebra on the GNS Hilbert space generated by the GNS operators -`πω(a) : Hω →L[ℂ] Hω`. - -By definition this is the double commutant of the range of `πω`. --/ -noncomputable def gnsVonNeumannAlgebra : VonNeumannAlgebra (Hω (ω := ω)) := - vonNeumannAlgebraGenerated (H := Hω (ω := ω)) (Set.range fun a : A => (πω ω a)) - -/-- The GNS operators lie in the von Neumann algebra generated by the GNS representation. -/ -theorem gnsVonNeumannAlgebra_contains_πω (a : A) : - (πω ω a) ∈ (gnsVonNeumannAlgebra (ω := ω) : Set (Hω (ω := ω) →L[ℂ] Hω (ω := ω))) := - subset_vonNeumannAlgebraGenerated (H := Hω (ω := ω)) - (S := Set.range fun a : A => (πω ω a)) ⟨a, rfl⟩ - -end Construction -end GNS diff --git a/QuantumSystem/Analysis/CFC/Diagonal.lean b/QuantumSystem/Analysis/CFC/Diagonal.lean new file mode 100644 index 0000000..4cefc70 --- /dev/null +++ b/QuantumSystem/Analysis/CFC/Diagonal.lean @@ -0,0 +1,159 @@ +module + +public import Mathlib.Analysis.CStarAlgebra.CStarMatrix +public import Mathlib.Analysis.Matrix.Order +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +public import Mathlib.Topology.Algebra.Module.FiniteDimension +public import QuantumSystem.Analysis.Matrix.HermitianFunctionalCalculus + +/-! +# Continuous functional calculus on diagonal matrices + +For a diagonal matrix `diagonal (d : m → ℂ)` with strictly positive real entries, +the continuous functional calculus `cfc Real.log` reduces to the diagonal of the +entrywise logarithm. This is the `Real.log` analogue of `Matrix.diagonal_rpow` +in `QuantumSystem/ForMathlib/Analysis/Matrix/Basic.lean`. + +## Main results + +* `Matrix.cfc_log_diagonal_pos` — `cfc Real.log (diagonal d) = diagonal (Real.log ∘ d)` + for `d : m → ℝ` with `0 < d i`. +* `Matrix.cfc_log_unitary_conj_diagonal` — `cfc Real.log` commutes with unitary + conjugation of a positive real diagonal. +-/ + +@[expose] public section + +namespace Matrix + +open scoped ComplexOrder + +variable {m : Type*} [Fintype m] [DecidableEq m] + +/-- `cfc Real.log` of a diagonal matrix with strictly positive real entries equals +the diagonal of the entrywise `Real.log`. + +Proof outline: +1. `diagonal : (m → ℂ) →⋆ₐ[ℝ] Matrix m m ℂ` is a continuous star algebra + homomorphism (constructed inline), so `StarAlgHomClass.map_cfc` moves the CFC + inside: `cfc Real.log (diagonal dc) = diagonal (cfc Real.log dc)`. +2. In the commutative Pi C*-algebra `m → ℂ`, CFC is pointwise (`cfc_map_pi`), + and each entry `(d i : ℂ) = algebraMap ℝ ℂ (d i)` gives + `cfc Real.log (d i : ℂ) = (Real.log (d i) : ℝ) : ℂ` via `cfc_algebraMap`. -/ +lemma cfc_log_diagonal_pos + (d : m → ℝ) (hd : ∀ i, 0 < d i) : + cfc Real.log (diagonal (fun i => (d i : ℂ)) : Matrix m m ℂ) = + diagonal (fun i => ((Real.log (d i) : ℝ) : ℂ)) := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + -- Pi self-adjoint + let dc : m → ℂ := fun i => (d i : ℂ) + have hdc_sa : IsSelfAdjoint dc := by + rw [IsSelfAdjoint, Pi.star_def]; ext i; simp [dc, Complex.conj_ofReal] + -- `diagonal` as star algebra hom (m → ℂ) →⋆ₐ[ℝ] Matrix m m ℂ + let φ : (m → ℂ) →⋆ₐ[ℝ] Matrix m m ℂ := + { Matrix.diagonalAlgHom (R := ℝ) with + map_star' := fun v => by + change diagonal (star v) = (diagonal v)ᴴ + rw [diagonal_conjTranspose] } + have hφ_cont : Continuous φ := + φ.toAlgHom.toLinearMap.continuous_of_finiteDimensional + have hφ_dc : φ dc = diagonal dc := rfl + have hφdc_sa : IsSelfAdjoint (φ dc) := by + rw [IsSelfAdjoint, ← map_star φ]; exact congr_arg φ hdc_sa.star_eq + -- Each component is self-adjoint (real-valued in ℂ). + have hdc_i_sa : ∀ i, IsSelfAdjoint (dc i) := by + intro i; simp [dc, IsSelfAdjoint, Complex.conj_ofReal] + -- spectrum ℝ (dc i) ⊆ {d i} by CFC.spectrum_algebraMap_subset. + have hspec_i : ∀ i, spectrum ℝ (dc i) ⊆ {d i} := by + intro i + change spectrum ℝ ((d i : ℂ)) ⊆ {d i} + rw [show ((d i : ℂ) : ℂ) = algebraMap ℝ ℂ (d i) from rfl] + exact CFC.spectrum_algebraMap_subset (d i) + -- Continuity of Real.log on ⋃ spectrum of each component. + have hcont_union : ContinuousOn Real.log (⋃ i, spectrum ℝ (dc i)) := by + refine Real.continuousOn_log.mono ?_ + intro x hx + rcases Set.mem_iUnion.mp hx with ⟨i, hxi⟩ + have : x = d i := (hspec_i i) hxi + rw [this] + simp [ne_of_gt (hd i)] + -- cfc on Pi computed componentwise. + have h_pi_cfc : cfc Real.log dc = fun i : m => ((Real.log (d i) : ℝ) : ℂ) := by + rw [cfc_map_pi (S := ℝ) Real.log dc hcont_union hdc_sa hdc_i_sa] + funext i + simp only [dc] + rw [show (d i : ℂ) = algebraMap ℝ ℂ (d i) from rfl, cfc_algebraMap (A := ℂ) (d i) Real.log] + rfl + -- Apply StarAlgHom.map_cfc. Need ContinuousOn Real.log (spectrum ℝ dc). + have hspec_dc : spectrum ℝ dc ⊆ ⋃ i, spectrum ℝ (dc i) := by + rw [Pi.spectrum_eq] + have hcont_dc : ContinuousOn Real.log (spectrum ℝ dc) := hcont_union.mono hspec_dc + have h_map := StarAlgHomClass.map_cfc (R := ℝ) (S := ℝ) + φ Real.log dc hcont_dc hφ_cont hdc_sa hφdc_sa + rw [← hφ_dc, ← h_map, h_pi_cfc] + rfl + +/-- `cfc Real.log` commutes with unitary conjugation of a diagonal matrix with +strictly positive real entries. -/ +lemma cfc_log_unitary_conj_diagonal + {k : Type*} [Fintype k] [DecidableEq k] + (W : unitary (Matrix k k ℂ)) (d : k → ℝ) (hd : ∀ i, 0 < d i) : + cfc Real.log + ((W : Matrix k k ℂ) * + diagonal (fun i => ((d i : ℝ) : ℂ)) * (W : Matrix k k ℂ)ᴴ) = + (W : Matrix k k ℂ) * + diagonal (fun i => ((Real.log (d i) : ℝ) : ℂ)) * (W : Matrix k k ℂ)ᴴ := by + letI : NormedRing (Matrix k k ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix k k ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix k k ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix k k ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := k) (A := ℂ) + have h_diag_sa : IsSelfAdjoint (diagonal (fun i => ((d i : ℝ) : ℂ))) := by + rw [IsSelfAdjoint, star_eq_conjTranspose, diagonal_conjTranspose] + congr 1 + funext i + simp [Complex.conj_ofReal] + have h_spec_sub : spectrum ℝ (diagonal (fun i => ((d i : ℝ) : ℂ)) : Matrix k k ℂ) ⊆ + {x : ℝ | x ≠ 0} := by + intro x hx + rw [← spectrum.preimage_algebraMap ℂ] at hx + rw [Set.mem_preimage, _root_.spectrum_diagonal] at hx + rcases hx with ⟨i, hxi⟩ + have hx_eq : (x : ℂ) = ((d i : ℝ) : ℂ) := by + change (algebraMap ℝ ℂ x : ℂ) = ((d i : ℝ) : ℂ) + exact hxi.symm + have : x = d i := by + exact_mod_cast hx_eq + rw [this] + simp [ne_of_gt (hd i)] + have h_cont : ContinuousOn Real.log + (spectrum ℝ (diagonal (fun i => ((d i : ℝ) : ℂ)))) := + Real.continuousOn_log.mono h_spec_sub + have h_diag_conj_sa : IsSelfAdjoint + ((Unitary.conjStarAlgAut ℝ (Matrix k k ℂ) W) + (diagonal (fun i => ((d i : ℝ) : ℂ)))) := by + rw [IsSelfAdjoint, ← map_star (Unitary.conjStarAlgAut ℝ (Matrix k k ℂ) W)] + exact congr_arg (Unitary.conjStarAlgAut ℝ (Matrix k k ℂ) W) h_diag_sa.star_eq + have h_cont_conj : Continuous (Unitary.conjStarAlgAut ℝ (Matrix k k ℂ) W) := + LinearMap.continuous_of_finiteDimensional + (Unitary.conjStarAlgAut ℝ (Matrix k k ℂ) W).toAlgEquiv.toLinearMap + have h_map := StarAlgHomClass.map_cfc (R := ℝ) (S := ℝ) + (Unitary.conjStarAlgAut ℝ (Matrix k k ℂ) W) Real.log + (diagonal (fun i => ((d i : ℝ) : ℂ))) h_cont h_cont_conj h_diag_sa h_diag_conj_sa + rw [Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] at h_map + calc + cfc Real.log + ((W : Matrix k k ℂ) * + diagonal (fun i => ((d i : ℝ) : ℂ)) * (W : Matrix k k ℂ)ᴴ) + = (W : Matrix k k ℂ) * + cfc Real.log (diagonal (fun i => ((d i : ℝ) : ℂ))) * (W : Matrix k k ℂ)ᴴ := + h_map.symm + _ = (W : Matrix k k ℂ) * + diagonal (fun i => ((Real.log (d i) : ℝ) : ℂ)) * (W : Matrix k k ℂ)ᴴ := by + rw [cfc_log_diagonal_pos d hd] + +end Matrix diff --git a/QuantumSystem/Analysis/Entropy/KroneckerProduct.lean b/QuantumSystem/Analysis/Entropy/KroneckerProduct.lean new file mode 100644 index 0000000..599a15f --- /dev/null +++ b/QuantumSystem/Analysis/Entropy/KroneckerProduct.lean @@ -0,0 +1,468 @@ +module + +public import QuantumSystem.Analysis.CFC.Diagonal +public import QuantumSystem.Analysis.Matrix.PartialTrace +public import QuantumSystem.State + +/-! +# Tensor product (Kronecker) of density matrices and bipartite Kronecker calculus + +Given density matrices `ρ : DensityMatrix n` and `σ : DensityMatrix m`, we form +their tensor product `ρ ⊗ σ : DensityMatrix (n × m)` whose underlying matrix is +the Kronecker product of the underlying matrices. This is the bipartite product +state (independent-systems product state). + +This file is the hub for **Kronecker-product calculus on bipartite matrices**: + +* preservation of Hermitian / unitary structure under `⊗ₖ`, +* Kronecker spectral decomposition, +* the **log-tensor identity** + `matrixLog (A ⊗ₖ B) = matrixLog A ⊗ₖ 1 + 1 ⊗ₖ matrixLog B` for PosDef `A`, `B`, +* the **equivalence-indexed partial trace** `partialTrace` + (for `e : X ≃ A × B`, retain `A` and sum over `B`), +* and the **Heisenberg duality at product type** + `Tr(ρ · (X ⊗ 1)) = Tr((partialTrace (Equiv.refl (n × m)) ρ) · X)` together with + the symmetric `(1 ⊗ Y)` version. + +The retained subsystem is determined by the codomain of the chosen equivalence +`e : X ≃ A × B`. For native product types, `partialTrace (Equiv.refl (n × m))` +retains the `n` factor, while `partialTrace (Equiv.prodComm n m)` retains the +`m` factor. LocalNet-facing theorems should instead expose the split subset +`Λ ⊆ Λ_total` (and its complement) explicitly, and use these matrix-level lemmas +only after reindexing by `LocalNet.combineIdx`. + +The proof of the log-tensor identity uses the spectral decomposition of `A ⊗ B` +constructed explicitly from spectral decompositions of `A` and `B`, combined +with **spectral invariance** of `matrixFunction` (derived from +`Matrix.matrixFunction_eq_cfc` and `StarAlgHomClass.map_cfc` on the ⋆-algebra +automorphism given by conjugation by a unitary). Working via `matrixFunction` +(spectral, bare `Matrix n n ℂ`) instead of the `CStarMatrix` wrapper avoids the +instance diamond that blocked a previous `cfc`-only approach. + +## Main definitions + +* `DensityMatrix.kronecker` — tensor product of density matrices. +* `Matrix.partialTrace` — partial trace specified by an explicit bipartite equivalence. + +## Main results + +* `DensityMatrix.kronecker_toMatrix` — underlying-matrix unfolding. +* `Matrix.IsHermitian.kronecker` — Kronecker of Hermitian matrices is Hermitian. +* `Matrix.kronecker_eq_unitary_conj_diagonal` — Kronecker spectral decomposition. +* `Matrix.matrixLog_kronecker_posDef` — the log-tensor identity. +* `Matrix.partialTrace_apply` — entrywise unfolding of the equivalence-indexed partial trace. +* `Matrix.restrict_eq_partialTrace_combineIdx` / + `Matrix.restrict_compl_eq_partialTrace_combineIdx` — LocalNet restriction as an + equivalence-indexed partial trace. +* `Matrix.trace_mul_kronecker_one_right` — + `Tr(ρ · (X ⊗ 1)) = Tr((partialTrace (Equiv.refl (n × m)) ρ) · X)`. +* `Matrix.trace_mul_kronecker_one_left` — + `Tr(ρ · (1 ⊗ Y)) = Tr((partialTrace (Equiv.prodComm n m) ρ) · Y)`. +-/ + +@[expose] public section + +namespace Matrix + +open scoped Kronecker MatrixOrder ComplexOrder + +variable {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + +/-! ### Kronecker preserves Hermitian / unitary -/ + +omit [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] in +/-- The Kronecker product of two Hermitian matrices is Hermitian. -/ +theorem IsHermitian.kronecker {A : Matrix n n ℂ} {B : Matrix m m ℂ} + (hA : A.IsHermitian) (hB : B.IsHermitian) : (A ⊗ₖ B).IsHermitian := by + unfold IsHermitian + rw [conjTranspose_kronecker, hA.eq, hB.eq] + +/-- If `U` and `V` are unitary (i.e. `Uᴴ * U = 1` and `Vᴴ * V = 1`), then so is +`U ⊗ₖ V`. This is the statement used internally; the `unitaryGroup`-membership +version is `Matrix.kronecker_mem_unitary` in Mathlib. -/ +private lemma kronecker_conjTranspose_mul_self + {U : Matrix n n ℂ} {V : Matrix m m ℂ} + (hU : Uᴴ * U = 1) (hV : Vᴴ * V = 1) : + (U ⊗ₖ V)ᴴ * (U ⊗ₖ V) = 1 := by + rw [conjTranspose_kronecker, ← mul_kronecker_mul, hU, hV, ← one_kronecker_one] + +/-- Dual version: `(U ⊗ V)(U ⊗ V)ᴴ = 1`. -/ +private lemma kronecker_mul_conjTranspose_self + {U : Matrix n n ℂ} {V : Matrix m m ℂ} + (hU : U * Uᴴ = 1) (hV : V * Vᴴ = 1) : + (U ⊗ₖ V) * (U ⊗ₖ V)ᴴ = 1 := by + rw [conjTranspose_kronecker, ← mul_kronecker_mul, hU, hV, ← one_kronecker_one] + +/-! ### Kronecker spectral decomposition + +Given spectral decompositions `A = U_A * diag λ * U_Aᴴ` and `B = U_B * diag μ * U_Bᴴ`, +the Kronecker product satisfies + `A ⊗ₖ B = (U_A ⊗ U_B) * diag ((i,j) ↦ λ i * μ j) * (U_A ⊗ U_B)ᴴ`, +exhibiting `U_A ⊗ U_B` as a valid unitary diagonaliser of `A ⊗ B`. -/ + +/-- **Kronecker spectral decomposition.** If `A = U_A * D_A * U_Aᴴ` and +`B = U_B * D_B * U_Bᴴ` with `D_A = diagonal dA`, `D_B = diagonal dB`, then +`A ⊗ₖ B = (U_A ⊗ U_B) * diagonal (fun (i,j) => dA i * dB j) * (U_A ⊗ U_B)ᴴ`. -/ +theorem kronecker_eq_unitary_conj_diagonal + {U_A : Matrix n n ℂ} {U_B : Matrix m m ℂ} + {dA : n → ℂ} {dB : m → ℂ} + {A : Matrix n n ℂ} {B : Matrix m m ℂ} + (hA : A = U_A * diagonal dA * U_Aᴴ) + (hB : B = U_B * diagonal dB * U_Bᴴ) : + A ⊗ₖ B = + (U_A ⊗ₖ U_B) * + diagonal (fun ij : n × m => dA ij.1 * dB ij.2) * + (U_A ⊗ₖ U_B)ᴴ := by + rw [hA, hB, conjTranspose_kronecker] + -- Apply mul_kronecker_mul twice (forward) and diagonal_kronecker_diagonal + rw [mul_kronecker_mul, mul_kronecker_mul, diagonal_kronecker_diagonal] + +/-! ### Log-tensor identity -/ + +section LogTensor + +/-- `matrixLog` of a unitary conjugate of a positive real diagonal is the same +unitary conjugation of the diagonal logarithm. Auxiliary for the log-tensor identity. -/ +private lemma matrixLog_unitary_conj_diagonal + {k : Type*} [Fintype k] [DecidableEq k] + (W : unitary (Matrix k k ℂ)) (d : k → ℝ) (hd : ∀ i, 0 < d i) + (hM : ((W : Matrix k k ℂ) * diagonal (fun i => ((d i : ℝ) : ℂ)) * + (W : Matrix k k ℂ)ᴴ).IsHermitian) : + matrixLog ((W : Matrix k k ℂ) * diagonal (fun i => ((d i : ℝ) : ℂ)) * + (W : Matrix k k ℂ)ᴴ) hM = + (W : Matrix k k ℂ) * + diagonal (fun i => ((Real.log (d i) : ℝ) : ℂ)) * (W : Matrix k k ℂ)ᴴ := by + unfold matrixLog + rw [matrixFunction_eq_cfc] + exact cfc_log_unitary_conj_diagonal W d hd + +/-- **Log-tensor identity.** For positive-definite matrices `A` and `B`, the matrix +logarithm of the Kronecker product decomposes as the sum of tensor-embedded logs: +`matrixLog (A ⊗ₖ B) = matrixLog A ⊗ₖ 1 + 1 ⊗ₖ matrixLog B`. -/ +theorem matrixLog_kronecker_posDef + {A : Matrix n n ℂ} (hA : A.PosDef) + {B : Matrix m m ℂ} (hB : B.PosDef) : + matrixLog (A ⊗ₖ B) (IsHermitian.kronecker hA.1 hB.1) = + matrixLog A hA.1 ⊗ₖ (1 : Matrix m m ℂ) + + (1 : Matrix n n ℂ) ⊗ₖ matrixLog B hB.1 := by + -- Spectral data + set U_A := (hA.1.eigenvectorUnitary : Matrix n n ℂ) with hU_A_def + set U_B := (hB.1.eigenvectorUnitary : Matrix m m ℂ) with hU_B_def + set dA := hA.1.eigenvalues with hdA_def + set dB := hB.1.eigenvalues with hdB_def + -- PosDef → eigenvalues positive + have hdA_pos : ∀ i, 0 < dA i := fun i => hA.eigenvalues_pos i + have hdB_pos : ∀ j, 0 < dB j := fun j => hB.eigenvalues_pos j + -- Unitarity + have hUA_self : U_Aᴴ * U_A = 1 := by + have := Unitary.coe_star_mul_self hA.1.eigenvectorUnitary + simpa [star_eq_conjTranspose, hU_A_def] using this + have hUA_self' : U_A * U_Aᴴ = 1 := by + have := Unitary.coe_mul_star_self hA.1.eigenvectorUnitary + simpa [star_eq_conjTranspose, hU_A_def] using this + have hUB_self : U_Bᴴ * U_B = 1 := by + have := Unitary.coe_star_mul_self hB.1.eigenvectorUnitary + simpa [star_eq_conjTranspose, hU_B_def] using this + have hUB_self' : U_B * U_Bᴴ = 1 := by + have := Unitary.coe_mul_star_self hB.1.eigenvectorUnitary + simpa [star_eq_conjTranspose, hU_B_def] using this + -- Spectral decompositions + have hA_decomp : A = U_A * diagonal (fun i => (dA i : ℂ)) * U_Aᴴ := by + have h := hA.1.spectral_theorem (𝕜 := ℂ) + rw [Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] at h + exact h + have hB_decomp : B = U_B * diagonal (fun j => (dB j : ℂ)) * U_Bᴴ := by + have h := hB.1.spectral_theorem (𝕜 := ℂ) + rw [Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] at h + exact h + -- Construct Kronecker unitary + let W : unitary (Matrix (n × m) (n × m) ℂ) := + ⟨U_A ⊗ₖ U_B, by + rw [Unitary.mem_iff, star_eq_conjTranspose] + exact ⟨kronecker_conjTranspose_mul_self hUA_self hUB_self, + kronecker_mul_conjTranspose_self hUA_self' hUB_self'⟩⟩ + have hW_val : (W : Matrix (n × m) (n × m) ℂ) = U_A ⊗ₖ U_B := rfl + -- Kronecker spectral decomposition + have hAB_decomp : A ⊗ₖ B = (W : Matrix (n × m) (n × m) ℂ) * + diagonal (fun ij : n × m => (((dA ij.1 * dB ij.2 : ℝ) : ℂ))) * + (W : Matrix (n × m) (n × m) ℂ)ᴴ := by + rw [hW_val] + have h := kronecker_eq_unitary_conj_diagonal hA_decomp hB_decomp + rw [h] + congr 1; congr 1 + funext ij; push_cast; ring + -- Hermitianness of Kronecker + have hAB_herm : (A ⊗ₖ B).IsHermitian := IsHermitian.kronecker hA.1 hB.1 + -- Positivity of the diagonal product + have h_dA_dB_pos : ∀ ij : n × m, 0 < (dA ij.1 * dB ij.2 : ℝ) := + fun ij => mul_pos (hdA_pos ij.1) (hdB_pos ij.2) + -- matrixLog of A ⊗ B via aux lemma + have hAB_herm' : ((W : Matrix (n × m) (n × m) ℂ) * + diagonal (fun ij : n × m => (((dA ij.1 * dB ij.2 : ℝ) : ℂ))) * + (W : Matrix (n × m) (n × m) ℂ)ᴴ).IsHermitian := by + rw [← hAB_decomp]; exact hAB_herm + have h_matrixLog_AB : + matrixLog (A ⊗ₖ B) hAB_herm = + (W : Matrix (n × m) (n × m) ℂ) * + diagonal (fun ij : n × m => ((Real.log (dA ij.1 * dB ij.2) : ℝ) : ℂ)) * + (W : Matrix (n × m) (n × m) ℂ)ᴴ := by + rw [show matrixLog (A ⊗ₖ B) hAB_herm = + matrixLog ((W : Matrix (n × m) (n × m) ℂ) * + diagonal (fun ij : n × m => (((dA ij.1 * dB ij.2 : ℝ) : ℂ))) * + (W : Matrix (n × m) (n × m) ℂ)ᴴ) hAB_herm' from ?_] + · exact matrixLog_unitary_conj_diagonal W + (fun ij : n × m => (dA ij.1 * dB ij.2 : ℝ)) h_dA_dB_pos hAB_herm' + · congr 1 + rw [h_matrixLog_AB] + -- Split log(dA*dB) = log dA + log dB + have h_log_split : + diagonal (fun ij : n × m => ((Real.log (dA ij.1 * dB ij.2) : ℝ) : ℂ)) = + diagonal (fun ij : n × m => ((Real.log (dA ij.1) : ℝ) : ℂ)) + + diagonal (fun ij : n × m => ((Real.log (dB ij.2) : ℝ) : ℂ)) := by + ext ij ij' + by_cases h : ij = ij' + · subst h + simp only [diagonal_apply_eq, Matrix.add_apply, diagonal_apply_eq] + rw [Real.log_mul (ne_of_gt (hdA_pos ij.1)) (ne_of_gt (hdB_pos ij.2))] + push_cast; ring + · simp only [Matrix.add_apply, diagonal_apply_ne _ h, add_zero] + rw [h_log_split] + -- Split each diagonal as Kronecker product + have h_left_as_kronecker : + diagonal (fun ij : n × m => ((Real.log (dA ij.1) : ℝ) : ℂ)) = + diagonal (fun i => ((Real.log (dA i) : ℝ) : ℂ)) ⊗ₖ (1 : Matrix m m ℂ) := by + rw [show (1 : Matrix m m ℂ) = diagonal (fun _ : m => (1 : ℂ)) from (diagonal_one).symm, + diagonal_kronecker_diagonal] + congr 1; funext ij; ring + have h_right_as_kronecker : + diagonal (fun ij : n × m => ((Real.log (dB ij.2) : ℝ) : ℂ)) = + (1 : Matrix n n ℂ) ⊗ₖ diagonal (fun j => ((Real.log (dB j) : ℝ) : ℂ)) := by + rw [show (1 : Matrix n n ℂ) = diagonal (fun _ : n => (1 : ℂ)) from (diagonal_one).symm, + diagonal_kronecker_diagonal] + congr 1; funext ij; ring + rw [h_left_as_kronecker, h_right_as_kronecker] + -- Distribute the conjugation over the sum + rw [Matrix.mul_add, Matrix.add_mul, hW_val, conjTranspose_kronecker] + -- Each term: (X ⊗ Y) * (P ⊗ Q) * (X' ⊗ Y') = (X * P * X') ⊗ (Y * Q * Y') + -- Using ← mul_kronecker_mul twice per term + rw [← mul_kronecker_mul, ← mul_kronecker_mul, + ← mul_kronecker_mul, ← mul_kronecker_mul] + -- Clean up: U_A * 1 * U_Aᴴ = 1, U_B * 1 * U_Bᴴ = 1 + rw [Matrix.mul_one U_A, hUA_self', Matrix.mul_one U_B, hUB_self'] + -- Now unfold matrixLog of A and B via their spectral decomposition + rw [matrixLog_spectral_eq hA.1, matrixLog_spectral_eq hB.1] + +end LogTensor + +/-! ### Equivalence-indexed partial trace + +For an explicit bipartite decomposition `e : X ≃ A × B`, `partialTrace e ρ` +retains the `A` factor and sums over the `B` factor. This makes the retained +subsystem part of the type of the decomposition, rather than something inferred +from names such as `A/B` or from left/right position. -/ + +section PartialTrace + +variable {X A B : Type*} [Fintype B] + +/-- **Partial trace along an explicit bipartite equivalence.** If `e : X ≃ A × B`, then +`partialTrace e ρ` is the matrix on the retained subsystem `A` obtained by summing out +the `B` factor. -/ +noncomputable def partialTrace (e : X ≃ A × B) (ρ : Matrix X X ℂ) : Matrix A A ℂ := + Matrix.of fun a a' => ∑ b : B, ρ (e.symm (a, b)) (e.symm (a', b)) + +@[simp] lemma partialTrace_apply (e : X ≃ A × B) (ρ : Matrix X X ℂ) (a a' : A) : + partialTrace e ρ a a' = ∑ b : B, ρ (e.symm (a, b)) (e.symm (a', b)) := rfl + +end PartialTrace + +omit [Fintype n] [DecidableEq n] [DecidableEq m] in +@[simp] lemma partialTrace_refl_apply (ρ : Matrix (n × m) (n × m) ℂ) (a a' : n) : + partialTrace (A := n) (B := m) (Equiv.refl (n × m)) ρ a a' = + ∑ b : m, ρ (a, b) (a', b) := rfl + +omit [Fintype m] [DecidableEq n] [DecidableEq m] in +@[simp] lemma partialTrace_prodComm_apply (ρ : Matrix (n × m) (n × m) ℂ) (b b' : m) : + partialTrace (A := m) (B := n) (Equiv.prodComm n m) ρ b b' = + ∑ a : n, ρ (a, b) (a, b') := rfl + +/-! ### LocalNet bridge + +These lemmas identify `Matrix.restrict` on a `LocalNet` with the equivalence-indexed +partial trace of the reindexed matrix induced by `LocalNet.combineIdx`. -/ + +section LocalNetBridge + +variable {L : LocalNet} + +/-- Combining via `h : Λ ⊆ Λ_total` agrees with combining via the complementary split, +after transporting the remaining factor along `Λ_total \ (Λ_total \ Λ) = Λ`. -/ +private lemma combineIdx_swap_apply + {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (x : L.regionIdx Λ) (y : L.regionIdx (Λ_total \ Λ)) : + L.combineIdx h (x, y) = + L.combineIdx Finset.sdiff_subset + (y, L.regionIdxCongr (sdiff_sdiff_eq_self h).symm x) := by + have h_eq : Λ_total \ (Λ_total \ Λ) = Λ := sdiff_sdiff_eq_self h + funext ⟨s, hs⟩ + by_cases hsΛ : s ∈ Λ + · have hns_compl : s ∉ Λ_total \ Λ := fun h_in => (Finset.mem_sdiff.mp h_in).2 hsΛ + have hs_recast : s ∈ Λ_total \ (Λ_total \ Λ) := by + rw [h_eq] + exact hsΛ + rw [LocalNet.combineIdx_apply_mem h _ _ ⟨s, hs⟩ hsΛ, + LocalNet.combineIdx_apply_not_mem Finset.sdiff_subset _ _ ⟨s, hs⟩ hns_compl, + LocalNet.regionIdxCongr_apply (L := L) h_eq.symm x hsΛ hs_recast] + · have hs_compl : s ∈ Λ_total \ Λ := Finset.mem_sdiff.mpr ⟨hs, hsΛ⟩ + rw [LocalNet.combineIdx_apply_not_mem h _ _ ⟨s, hs⟩ hsΛ, + LocalNet.combineIdx_apply_mem Finset.sdiff_subset _ _ ⟨s, hs⟩ hs_compl] + +/-- Restriction to `Λ` equals the partial trace of the reindexed matrix induced by +`combineIdx h`, retaining the `Λ` factor. -/ +theorem restrict_eq_partialTrace_combineIdx + {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (ρ : L.localAlgebra Λ_total) (x x' : L.regionIdx Λ) : + Matrix.restrict h ρ x x' = + Matrix.partialTrace + (A := L.regionIdx Λ) (B := L.regionIdx (Λ_total \ Λ)) + (Equiv.refl (L.regionIdx Λ × L.regionIdx (Λ_total \ Λ))) + (ρ.submatrix (L.combineIdx h) (L.combineIdx h)) x x' := by + rw [Matrix.partialTrace_refl_apply, Matrix.restrict_apply] + simp [Matrix.submatrix_apply] + +/-- Restriction to the complement of `Λ` equals the partial trace of the reindexed matrix +induced by `combineIdx h`, retaining the complementary factor. -/ +theorem restrict_compl_eq_partialTrace_combineIdx + {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (ρ : L.localAlgebra Λ_total) (y y' : L.regionIdx (Λ_total \ Λ)) : + Matrix.restrict Finset.sdiff_subset ρ y y' = + Matrix.partialTrace + (A := L.regionIdx (Λ_total \ Λ)) (B := L.regionIdx Λ) + (Equiv.prodComm (L.regionIdx Λ) (L.regionIdx (Λ_total \ Λ))) + (ρ.submatrix (L.combineIdx h) (L.combineIdx h)) y y' := by + rw [Matrix.partialTrace_prodComm_apply, Matrix.restrict_apply] + rw [← (L.regionIdxCongr (sdiff_sdiff_eq_self h).symm).sum_comp + (fun z => ρ (L.combineIdx Finset.sdiff_subset (y, z)) + (L.combineIdx Finset.sdiff_subset (y', z)))] + refine Finset.sum_congr rfl fun x _ => ?_ + rw [Matrix.submatrix_apply, combineIdx_swap_apply h x y, combineIdx_swap_apply h x y'] + +end LocalNetBridge + +/-! ### Heisenberg duality at product type + +`Tr(ρ · (X ⊗ 1)) = Tr((partialTrace (Equiv.refl (n × m)) ρ) · X)` and the symmetric +`(1 ⊗ Y)` version. -/ + +omit [DecidableEq n] in +/-- **Right-factor Heisenberg dual**: tracing `ρ` against the embedded observable +`X ⊗ 1` reduces to the trace against the partial trace that retains the first factor. -/ +theorem trace_mul_kronecker_one_right + (ρ : Matrix (n × m) (n × m) ℂ) (X : Matrix n n ℂ) : + Tr (ρ * (X ⊗ₖ (1 : Matrix m m ℂ))) = + Tr (partialTrace (A := n) (B := m) (Equiv.refl (n × m)) ρ * X) := by + classical + unfold Matrix.trace + simp_rw [Matrix.diag_apply, Matrix.mul_apply, partialTrace_refl_apply] + rw [Fintype.sum_prod_type] + simp_rw [Fintype.sum_prod_type, Matrix.kronecker_apply, Matrix.one_apply] + -- Goal: ∑ a, ∑ b, ∑ a', ∑ b', ρ (a, b) (a', b') * (X a' a * (if b' = b then 1 else 0)) + -- = ∑ a, ∑ a', (∑ b, ρ (a, b) (a', b)) * X a' a + have inner : ∀ (a : n) (b : m) (a' : n), + (∑ b' : m, ρ (a, b) (a', b') * (X a' a * (if b' = b then (1 : ℂ) else 0))) = + ρ (a, b) (a', b) * X a' a := by + intro a b a' + rw [Finset.sum_eq_single b] + · simp + · intro b' _ hb'; rw [if_neg hb']; ring + · simp + simp_rw [inner] + refine Finset.sum_congr rfl fun a _ => ?_ + rw [Finset.sum_comm] + refine Finset.sum_congr rfl fun a' _ => ?_ + rw [Finset.sum_mul] + +omit [DecidableEq m] in +/-- **Left-factor Heisenberg dual**: `Tr(ρ · (1 ⊗ Y))` reduces to the trace against the +partial trace that retains the second factor. -/ +theorem trace_mul_kronecker_one_left + (ρ : Matrix (n × m) (n × m) ℂ) (Y : Matrix m m ℂ) : + Tr (ρ * ((1 : Matrix n n ℂ) ⊗ₖ Y)) = + Tr (partialTrace (A := m) (B := n) (Equiv.prodComm n m) ρ * Y) := by + classical + unfold Matrix.trace + simp_rw [Matrix.diag_apply, Matrix.mul_apply, partialTrace_prodComm_apply] + rw [Fintype.sum_prod_type] + simp_rw [Fintype.sum_prod_type, Matrix.kronecker_apply, Matrix.one_apply] + -- Goal: ∑ a, ∑ b, ∑ a', ∑ b', ρ (a, b) (a', b') * ((if a' = a then 1 else 0) * Y b' b) + -- = ∑ b, ∑ b', (∑ a, ρ (a, b) (a, b')) * Y b' b + have inner : ∀ (a : n) (b : m), + (∑ a' : n, ∑ b' : m, ρ (a, b) (a', b') * ((if a' = a then (1 : ℂ) else 0) * Y b' b)) = + ∑ b' : m, ρ (a, b) (a, b') * Y b' b := by + intro a b + rw [Finset.sum_comm] + refine Finset.sum_congr rfl fun b' _ => ?_ + rw [Finset.sum_eq_single a] + · simp + · intro a' _ ha'; rw [if_neg ha']; ring + · simp + simp_rw [inner] + -- Goal: ∑ a, ∑ b, ∑ b', ρ (a, b) (a, b') * Y b' b + -- = ∑ b, ∑ b', (∑ a, ρ (a, b) (a, b')) * Y b' b + rw [Finset.sum_comm] + refine Finset.sum_congr rfl fun b _ => ?_ + rw [Finset.sum_comm] + refine Finset.sum_congr rfl fun b' _ => ?_ + rw [Finset.sum_mul] + +/-! ## Paper notation: `tr₁(ρ)` / `tr₂(ρ)` + +Subscript convention follows Nielsen–Chuang §2.4: `trᵢ(ρ)` traces *out* factor +`i` and retains the other. For a bipartite density matrix `ρ` on `n × m`: + +* `tr₂(ρ) = partialTrace (Equiv.refl (n × m)) ρ` — traces out the second + factor `m`, retaining `n`. +* `tr₁(ρ) = partialTrace (Equiv.prodComm n m) ρ` — traces out the first + factor `n`, retaining `m`. + +The macros use `Equiv.refl _` / `Equiv.prodComm _ _`; the underscores are +solved from the matrix-typed argument. -/ + +namespace QuantumInfo + +scoped syntax:max "tr₁(" term ")" : term +scoped syntax:max "tr₂(" term ")" : term + +scoped macro_rules + | `(tr₁($ρ)) => `(Matrix.partialTrace (Equiv.prodComm _ _) $ρ) + | `(tr₂($ρ)) => `(Matrix.partialTrace (Equiv.refl _) $ρ) + +end QuantumInfo + +end Matrix + +/-! ### Tensor product of density matrices -/ + +namespace DensityMatrix + +open scoped Kronecker +open Matrix + +variable {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + +/-- **Tensor product (Kronecker) of density matrices.** Represents the +independent-systems product state on the joint system `n × m`. -/ +noncomputable def kronecker (ρ : DensityMatrix n) (σ : DensityMatrix m) : + DensityMatrix (n × m) where + toMatrix := ρ.toMatrix ⊗ₖ σ.toMatrix + posSemidef := ρ.posSemidef.kronecker σ.posSemidef + trace_eq_one := by + rw [trace_kronecker, ρ.trace_eq_one, σ.trace_eq_one, mul_one] + +@[inherit_doc DensityMatrix.kronecker] +scoped[Kronecker] infixl:100 " ⊗ " => DensityMatrix.kronecker + +@[simp] lemma kronecker_toMatrix (ρ : DensityMatrix n) (σ : DensityMatrix m) : + (ρ ⊗ σ).toMatrix = ρ.toMatrix ⊗ₖ σ.toMatrix := rfl + +end DensityMatrix diff --git a/QuantumSystem/Analysis/Entropy/Regularize.lean b/QuantumSystem/Analysis/Entropy/Regularize.lean new file mode 100644 index 0000000..b9f4760 --- /dev/null +++ b/QuantumSystem/Analysis/Entropy/Regularize.lean @@ -0,0 +1,89 @@ +module + +public import QuantumSystem.Analysis.Entropy.VonNeumannEntropy +public import QuantumSystem.Analysis.Matrix.PartialTrace + +/-! +# Regularization on a local net: compatibility with restriction + +The structural definitions (`maximallyMixed`, `regularize`, `regularize_eq_cfc`, +`regularize_mapEquiv`) and the entropy identities for regularized states +(`vonNeumannEntropy_regularize_eq_negMulLog_sum`, `tendsto_negMulLog_regularize_sum_zero`, +`vonNeumannEntropy_mapEquiv`) live in `State.lean` and `VonNeumannEntropy.lean`. + +This file specialises that infrastructure to a `LocalNet`. It proves compatibility of +regularization with restriction (partial trace), which is the key fact behind extending +PosDef-only theorems to PosSemidef. The cardinality factorisation for region indices now +lives next to `LocalNet.combineIdx` in `LocalNet.lean`. +-/ + +@[expose] public section + +open scoped ComplexOrder + +/-! ### Regularization commutes with restriction + +The marginal of a regularized state equals the regularized marginal: +`restrict h (regularize ρ ε) = regularize (restrict h ρ) ε`. + +This is the key fact behind the "regularization respects partial trace" property +used in extending PosDef-only theorems to PosSemidef. -/ + +namespace Matrix + +/-- `Matrix.restrict h` of the maximally-mixed state at `Λ_total` equals the + maximally-mixed state at `Λ`, after dimension cancellation. -/ +theorem restrict_maximallyMixed {L : LocalNet} {Λ Λ_total : Finset L.sites} + (h : Λ ⊆ Λ_total) [Nonempty (L.regionIdx Λ)] [Nonempty (L.regionIdx Λ_total)] : + Matrix.restrict h (DensityMatrix.maximallyMixed (n := L.regionIdx Λ_total)).toMatrix = + (DensityMatrix.maximallyMixed (n := L.regionIdx Λ)).toMatrix := by + have hcomp_nonempty : Nonempty (L.regionIdx (Λ_total \ Λ)) := by + obtain ⟨x⟩ := ‹Nonempty (L.regionIdx Λ_total)› + exact ⟨((L.combineIdx h).symm x).2⟩ + rw [DensityMatrix.maximallyMixed_toMatrix, + DensityMatrix.maximallyMixed_toMatrix] + rw [Matrix.restrict_smul, Matrix.restrict_one] + rw [smul_smul] + congr 1 + have hdΛ : (Fintype.card (L.regionIdx Λ) : ℂ) ≠ 0 := by + exact_mod_cast (Fintype.card_pos (α := L.regionIdx Λ)).ne' + have hdComp : (Fintype.card (L.regionIdx (Λ_total \ Λ)) : ℂ) ≠ 0 := by + exact_mod_cast (Fintype.card_pos (α := L.regionIdx (Λ_total \ Λ))).ne' + rw [LocalNet.card_regionIdx_total (L := L) h] + have h_inv_mul : + ((((Fintype.card (L.regionIdx Λ) : ℂ) * + (Fintype.card (L.regionIdx (Λ_total \ Λ)) : ℂ)))⁻¹) * + (Fintype.card (L.regionIdx (Λ_total \ Λ)) : ℂ) = + (Fintype.card (L.regionIdx Λ) : ℂ)⁻¹ := by + field_simp [hdΛ, hdComp] + simpa [Nat.cast_mul] using h_inv_mul + +end Matrix + +namespace DensityMatrix + +/-- The regularization commutes with `restrict` (Matrix-level): + `restrict h (regularize ρ ε).toMatrix = (regularize (restrict h ρ) ε).toMatrix`. -/ +theorem regularize_restrict_toMatrix {L : LocalNet} {Λ Λ_total : Finset L.sites} + (h : Λ ⊆ Λ_total) [Nonempty (L.regionIdx Λ)] [Nonempty (L.regionIdx Λ_total)] + (ρ : L.densityMatrix Λ_total) {ε : ℝ} (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + Matrix.restrict h (regularize ρ hε hε').toMatrix = + (regularize (restrict h ρ) hε hε').toMatrix := by + rw [regularize_toMatrix, regularize_toMatrix] + rw [DensityMatrix.restrict_toMatrix] + rw [(Matrix.restrict h).map_add] + rw [(Matrix.restrict h).map_smul, (Matrix.restrict h).map_smul] + rw [Matrix.restrict_maximallyMixed h] + +/-- The regularization commutes with `restrict` (DensityMatrix-level): + `(regularize ρ ε).restrict h = regularize (restrict h ρ) ε`. -/ +theorem regularize_restrict {L : LocalNet} {Λ Λ_total : Finset L.sites} + (h : Λ ⊆ Λ_total) [Nonempty (L.regionIdx Λ)] [Nonempty (L.regionIdx Λ_total)] + (ρ : L.densityMatrix Λ_total) {ε : ℝ} (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + restrict h (regularize ρ hε hε') = + regularize (restrict h ρ) hε hε' := by + apply ext + rw [DensityMatrix.restrict_toMatrix] + exact regularize_restrict_toMatrix h ρ hε hε' + +end DensityMatrix diff --git a/QuantumSystem/Analysis/Entropy/RelativeEntropy.lean b/QuantumSystem/Analysis/Entropy/RelativeEntropy.lean new file mode 100644 index 0000000..01ce6e3 --- /dev/null +++ b/QuantumSystem/Analysis/Entropy/RelativeEntropy.lean @@ -0,0 +1,1717 @@ +module + +public import QuantumSystem.Analysis.Matrix.LiebConcavity +public import QuantumSystem.Analysis.Matrix.Pinching +public import QuantumSystem.Channel +public import QuantumSystem.ForMathlib.Analysis.Calculus.Deriv.Sign +public import QuantumSystem.ForMathlib.InformationTheory.KullbackLeibler.KLFun + +/-! +# Entropy Inequalities for Quantum Channels + +This file collects fundamental entropy inequalities for quantum channels. + +## Main Results + +* `relativeEntropy_channel_le`: Monotonicity of relative entropy — quantum channels do not + increase relative entropy: S(Φ(ρ) ‖ Φ(σ)) ≤ S(ρ ‖ σ). +* `relativeEntropy_channel_eq_iff_recoverable`: Equality in monotonicity holds when a Petz + recovery channel exists: if R(Φ(ρ)) = ρ and R(Φ(σ)) = σ, then + S(Φ(ρ) ‖ Φ(σ)) = S(ρ ‖ σ). +* `relativeEntropy_jointly_convex`: Relative entropy is jointly convex. + +## Mathematical Background + +### Monotonicity of Relative Entropy +For a quantum channel Φ : Mₙ(ℂ) → Mₘ(ℂ) and positive definite +density matrices ρ, σ: + S(Φ(ρ) ‖ Φ(σ)) ≤ S(ρ ‖ σ) + +**Proof strategy** (Lindblad): +1. Use Stinespring dilation: Φ(ρ) = Tr_E(U(ρ ⊗ |0⟩⟨0|)U†) +2. Relative entropy is additive: S(ρ ⊗ |0⟩⟨0| ‖ σ ⊗ |0⟩⟨0|) = S(ρ ‖ σ) +3. Relative entropy is unitarily invariant +4. Partial trace only decreases relative entropy + (monotonicity under partial trace; equivalent to strong subadditivity) + +### Petz Recovery Map +Equality in monotonicity holds iff there exists a recovery channel R such that +R(Φ(ρ)) = ρ and R(Φ(σ)) = σ. The explicit form is: + R(·) = σ^(1/2) Φ*(Φ(σ)^(-1/2) · Φ(σ)^(-1/2)) σ^(1/2) +where Φ* is the adjoint of Φ with respect to the Hilbert-Schmidt inner product. + +## References + +* Lindblad, *Completely positive maps and entropy inequalities* +* Petz, *Monotonicity of quantum relative entropy revisited* +* Ruskai, *Inequalities for quantum entropy: A review with conditions for equality* +-/ + +@[expose] public section + +namespace Matrix + +open scoped MatrixOrder ComplexOrder QuantumInfo + +variable {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + +/-! ### Relative Entropy -/ + +/-- Support inclusion: the kernel of σ is contained in the kernel of ρ, +i.e., supp(ρ) ⊆ supp(σ). This is the condition for D(ρ‖σ) to be finite. -/ +def suppSubset (ρ σ : Matrix n n ℂ) : Prop := + ∀ v : n → ℂ, σ.mulVec v = 0 → ρ.mulVec v = 0 + +/-- Quantum relative entropy D(ρ‖σ) = Tr (ρ(log ρ - log σ)) (Umegaki 1962). + +This is the physically correct definition, following Umegaki (1962): + + D(ρ ‖ σ) = Tr (ρ(log ρ − log σ)) if supp(ρ) ⊆ supp(σ) + = +∞ otherwise + +- The return type is `EReal` to accommodate the +∞ case. +- The matrix logarithms `log ρ` and `log σ` are computed via the spectral theorem. + This is the operator-algebraic definition used throughout the codebase. + When `σ` has zero eigenvalues, `Real.log 0 = 0` (Mathlib junk value) is used; + those directions contribute 0 to `Tr (ρ log σ)` because supp(ρ) ⊆ supp(σ). +-/ +noncomputable def relativeEntropy (ρ σ : DensityMatrix n) : EReal := + letI := Classical.propDecidable (suppSubset ρ.toMatrix σ.toMatrix) + if suppSubset ρ.toMatrix σ.toMatrix then + -- Tr (ρ (log ρ - log σ)) + ↑((Tr (ρ * (log ρ - log σ))).re) + else ⊤ + +namespace QuantumInfo +scoped notation "D(" ρ " ∥ " σ ")" => Matrix.relativeEntropy ρ σ +end QuantumInfo + +/-! ### Helper Lemmas for Relative Entropy -/ + +/-- Change-of-basis unitary between eigenvector bases of ρ and σ. +W = Vᴴ * U where V = eigenvectors of σ, U = eigenvectors of ρ. -/ +private noncomputable def eigW (ρ σ : DensityMatrix n) : Matrix n n ℂ := + (σ.isHermitian.eigenvectorUnitary : Matrix n n ℂ)ᴴ * + (ρ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + +/-- W * Wᴴ = 1 for the change-of-basis unitary. -/ +private lemma eigW_WWH (ρ σ : DensityMatrix n) : + eigW ρ σ * (eigW ρ σ)ᴴ = 1 := by + unfold eigW + set V := (σ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + set U := (ρ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + rw [conjTranspose_mul, conjTranspose_conjTranspose] + calc (Vᴴ * U) * (Uᴴ * V) + = Vᴴ * (U * Uᴴ) * V := by simp only [Matrix.mul_assoc] + _ = Vᴴ * V := by rw [UUH_eq_one _ ρ.isHermitian, Matrix.mul_one] + _ = 1 := UHU_eq_one _ σ.isHermitian + +/-- Wᴴ * W = 1 for the change-of-basis unitary. -/ +private lemma eigW_WHW (ρ σ : DensityMatrix n) : + (eigW ρ σ)ᴴ * eigW ρ σ = 1 := by + unfold eigW + set V := (σ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + set U := (ρ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + rw [conjTranspose_mul, conjTranspose_conjTranspose] + calc (Uᴴ * V) * (Vᴴ * U) + = Uᴴ * (V * Vᴴ) * U := by simp only [Matrix.mul_assoc] + _ = Uᴴ * U := by rw [UUH_eq_one _ σ.isHermitian, Matrix.mul_one] + _ = 1 := UHU_eq_one _ ρ.isHermitian + +/-- Column sums of |W_{ji}|² equal 1. Follows from W * Wᴴ = 1 (W is unitary). -/ +private lemma eigW_unitary_colsum (ρ σ : DensityMatrix n) (i : n) : + ∑ j : n, Complex.normSq (eigW ρ σ j i) = 1 := by + have h1 := congr_fun (congr_fun (eigW_WHW ρ σ) i) i + simp only [mul_apply, conjTranspose_apply, one_apply_eq] at h1 + have h2 : (∑ j : n, (Complex.normSq (eigW ρ σ j i) : ℂ)) = 1 := by + simp_rw [show ∀ j, (Complex.normSq (eigW ρ σ j i) : ℂ) = + star (eigW ρ σ j i) * eigW ρ σ j i from fun j => by + rw [Complex.normSq_eq_conj_mul_self]; simp [RCLike.star_def]] + exact h1 + exact_mod_cast h2 + +/-- Row sums of |W_{ji}|² equal 1. Follows from Wᴴ * W = 1 (W is unitary). -/ +private lemma eigW_unitary_rowsum (ρ σ : DensityMatrix n) (j : n) : + ∑ i : n, Complex.normSq (eigW ρ σ j i) = 1 := by + have h1 := congr_fun (congr_fun (eigW_WWH ρ σ) j) j + simp only [mul_apply, conjTranspose_apply, one_apply_eq] at h1 + have h2 : (∑ i : n, (Complex.normSq (eigW ρ σ j i) : ℂ)) = 1 := by + simp_rw [show ∀ i, (Complex.normSq (eigW ρ σ j i) : ℂ) = + eigW ρ σ j i * star (eigW ρ σ j i) from fun i => by + rw [Complex.normSq_eq_conj_mul_self]; simp [RCLike.star_def, mul_comm]] + exact h1 + exact_mod_cast h2 + +/-- Support subset condition implies: |W_{ji}|² · ev_ρᵢ = 0 when ev_σⱼ = 0. +Here ev_ρᵢ are eigenvalues of ρ, ev_σⱼ are eigenvalues of σ. +Proof: vⱼ = col j of V ∈ ker(σ), suppSubset gives vⱼ ∈ ker(ρ), +injectivity of U gives diag(ev_ρ) · (Uᴴvⱼ) = 0, and (Uᴴvⱼ)ᵢ = conj(Wji). -/ +private lemma suppSubset_normSq_ev_zero (ρ σ : DensityMatrix n) + (h : suppSubset ρ.toMatrix σ.toMatrix) (j : n) + (hev_σj : σ.isHermitian.eigenvalues j = 0) (i : n) : + Complex.normSq (eigW ρ σ j i) * ρ.isHermitian.eigenvalues i = 0 := by + set V := (σ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) with hV_def + set U := (ρ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) with hU_def + set W := eigW ρ σ with hW_def + set ev_ρ := ρ.isHermitian.eigenvalues with hev_ρ_def + set colV_j : n → ℂ := fun k => V k j with hcolV_def + -- Column j of V is in ker(σ) since eigenvalue j is 0 + have hσcol : σ.toMatrix.mulVec colV_j = 0 := by + have h1 := mulVec_eigenvector_col σ.toMatrix σ.isHermitian j + ext k; rw [congr_fun h1 k, hev_σj, Complex.ofReal_zero, zero_mul, Pi.zero_apply] + -- By suppSubset, col j of V is also in ker(ρ) + have hρcol : ρ.toMatrix.mulVec colV_j = 0 := h colV_j hσcol + -- Compute Uᴴ · colV_j + set Uh_colV : n → ℂ := Uᴴ.mulVec colV_j with hUh_colV_def + -- Key: Uh_colV i = star(W j i) + have hUh_eq_starW : Uh_colV i = star (W j i) := by + simp only [hUh_colV_def, hW_def, eigW, hcolV_def, mulVec, dotProduct, + conjTranspose_apply, mul_apply, star_sum] + apply Finset.sum_congr rfl + intro k _ + rw [star_mul', star_star, mul_comm] + -- From spectral decomposition ρ = U diag(ev_ρ) Uᴴ, we have: + -- ρ · colV_j = U · diag(ev_ρ) · (Uᴴ · colV_j) + have hspec := spectral_expand ρ.toMatrix ρ.isHermitian + -- Since ρ · colV_j = 0, we have U · diag(ev_ρ) · Uh_colV = 0 + have h_diag_eq : (U * diagonal (fun k => (ev_ρ k : ℂ))).mulVec Uh_colV = 0 := by + calc (U * diagonal (fun k => (ev_ρ k : ℂ))).mulVec Uh_colV + = (U * diagonal (fun k => (ev_ρ k : ℂ))).mulVec (Uᴴ.mulVec colV_j) := rfl + _ = (U * diagonal (fun k => (ev_ρ k : ℂ)) * Uᴴ).mulVec colV_j := by + rw [Matrix.mulVec_mulVec] + _ = ρ.toMatrix.mulVec colV_j := by rw [← hspec] + _ = 0 := hρcol + -- Extract the i-th component: ev_ρ i * (Uh_colV i) = 0 + have h_ev_Uh_zero : (ev_ρ i : ℂ) * Uh_colV i = 0 := by + -- From h_diag_eq, we know (U * diag) * Uh_colV = 0 + -- Multiplying by Uᴴ on left: Uᴴ * (U * diag) * Uh_colV = 0 + -- Since Uᴴ * U = 1, this gives diag * Uh_colV = 0 + have h_UhU := UHU_eq_one _ ρ.isHermitian + have h1 : (diagonal (fun k => (ev_ρ k : ℂ))).mulVec Uh_colV = 0 := by + have h2 : Uᴴ.mulVec ((U * diagonal (fun k => (ev_ρ k : ℂ))).mulVec Uh_colV) = 0 := by + rw [h_diag_eq, mulVec_zero] + simp only [Matrix.mulVec_mulVec] at h2 + have h3 : (Uᴴ * U) * diagonal (fun k => (ev_ρ k : ℂ)) = diagonal (fun k => (ev_ρ k : ℂ)) := by + rw [h_UhU, Matrix.one_mul] + rw [← Matrix.mul_assoc] at h2 + rw [h3] at h2 + exact h2 + have h2 := congr_fun h1 i + simp only [mulVec, dotProduct, diagonal_apply, Pi.zero_apply] at h2 + -- h2 : ∑ x, (if i = x then ev_ρ i else 0) * Uh_colV x = 0 + -- Simplify the sum: only x = i contributes + have h3 : ∑ x, (if i = x then (ev_ρ i : ℂ) else 0) * Uh_colV x = (ev_ρ i : ℂ) * Uh_colV i := by + rw [Finset.sum_eq_single i] + · simp only [ite_true] + · intro b _ hb + have hne : i ≠ b := Ne.symm hb + simp only [hne, ite_false, zero_mul] + · intro hi; exact absurd (Finset.mem_univ i) hi + rw [h3] at h2 + exact h2 + -- From ev_ρ i * star(W j i) = 0, derive normSq(W j i) * ev_ρ i = 0 + rw [hUh_eq_starW] at h_ev_Uh_zero + rcases mul_eq_zero.mp h_ev_Uh_zero with hev_zero | hstar_zero + · -- Case: ev_ρ i = 0 + simp only [Complex.ofReal_eq_zero] at hev_zero + simp [hev_zero] + · -- Case: star(W j i) = 0, hence W j i = 0 + rw [star_eq_zero] at hstar_zero + simp [hstar_zero] + +/-- Trace of ρ log ρ equals the eigenvalue sum ∑ᵢ ev_{ρ,i} log ev_{ρ,i}. -/ +private lemma trace_ρlogρ_eq (ρ : DensityMatrix n) : + (Tr (ρ.toMatrix * log ρ)).re = + ∑ i, ρ.isHermitian.eigenvalues i * Real.log (ρ.isHermitian.eigenvalues i) := by + set U := (ρ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + set ev_ρ := ρ.isHermitian.eigenvalues + have hUHU : Uᴴ * U = 1 := UHU_eq_one _ ρ.isHermitian + -- ρ = U * diag(ev) * Uᴴ + have hρ_spec := spectral_expand ρ.toMatrix ρ.isHermitian + -- log(ρ) = U * diag(log ev) * Uᴴ + have hlogρ_spec : log ρ = U * diagonal (fun i => (Real.log (ev_ρ i) : ℂ)) * Uᴴ := by + unfold DensityMatrix.log matrixLog matrixFunction + rfl + -- ρ * log(ρ) = U * diag(ev) * Uᴴ * U * diag(log ev) * Uᴴ = U * diag(ev * log ev) * Uᴴ + -- First rewrite log, then ρ + have h1 : (ρ.toMatrix * log ρ).trace.re = + (ρ.toMatrix * (U * diagonal (fun i => (Real.log (ev_ρ i) : ℂ)) * Uᴴ)).trace.re := by + rw [hlogρ_spec] + rw [h1, hρ_spec] + have h2 : (U * diagonal (fun i => (ev_ρ i : ℂ)) * Uᴴ * + (U * diagonal (fun i => (Real.log (ev_ρ i) : ℂ)) * Uᴴ)) = + U * (diagonal (fun i => (ev_ρ i : ℂ)) * + diagonal (fun i => (Real.log (ev_ρ i) : ℂ))) * Uᴴ := calc + _ = U * diagonal (fun i => (ev_ρ i : ℂ)) * + (Uᴴ * (U * diagonal (fun i => (Real.log (ev_ρ i) : ℂ)) * Uᴴ)) := by + simp only [Matrix.mul_assoc] + _ = U * diagonal (fun i => (ev_ρ i : ℂ)) * + ((Uᴴ * U) * diagonal (fun i => (Real.log (ev_ρ i) : ℂ)) * Uᴴ) := by + conv_lhs => rw [← Matrix.mul_assoc Uᴴ (U * _) Uᴴ, ← Matrix.mul_assoc Uᴴ U] + _ = U * diagonal (fun i => (ev_ρ i : ℂ)) * + (diagonal (fun i => (Real.log (ev_ρ i) : ℂ)) * Uᴴ) := by + rw [hUHU, Matrix.one_mul] + _ = U * (diagonal (fun i => (ev_ρ i : ℂ)) * + diagonal (fun i => (Real.log (ev_ρ i) : ℂ))) * Uᴴ := by + simp only [Matrix.mul_assoc] + rw [h2] + simp only [Matrix.mul_assoc] + rw [trace_mul_cycle'] + -- Goal: ((diag_log * Uᴴ) * (U * diag_ev)).trace.re = ... + -- left associate and expose Uᴴ * U + conv_lhs => rw [← Matrix.mul_assoc, Matrix.mul_assoc (diagonal _) Uᴴ U] + rw [hUHU, Matrix.mul_one, diagonal_mul_diagonal, trace_diagonal] + simp only [← Complex.ofReal_mul] + rw [Complex.re_sum] + simp only [Complex.ofReal_re, mul_comm] + +/-- Trace of ρ log σ expressed as double sum over eigenvalues via eigW. -/ +private lemma trace_ρlogσ_eq (ρ σ : DensityMatrix n) : + (Tr (ρ.toMatrix * log σ)).re = + ∑ i, ∑ j, Complex.normSq (eigW ρ σ j i) * + ρ.isHermitian.eigenvalues i * + Real.log (σ.isHermitian.eigenvalues j) := by + set V := (σ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + set U := (ρ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + set W := eigW ρ σ + set ev_ρ := ρ.isHermitian.eigenvalues + set ev_σ := σ.isHermitian.eigenvalues + have hρ : ρ.toMatrix = U * diagonal (fun i => (ev_ρ i : ℂ)) * Uᴴ := by + have h := (matrixFunction_id ρ.isHermitian).symm + unfold matrixFunction at h + simpa [Function.comp] using h + have hlogσ : log σ = V * diagonal (fun i => (Real.log (ev_σ i) : ℂ)) * Vᴴ := by + unfold DensityMatrix.log matrixLog matrixFunction; rfl + have hUHV : Uᴴ * V = Wᴴ := by + calc Uᴴ * V = Uᴴ * (Vᴴ)ᴴ := by rw [conjTranspose_conjTranspose] + _ = (Vᴴ * U)ᴴ := by rw [conjTranspose_mul] + _ = Wᴴ := rfl + rw [hρ, hlogσ] + have hconv : U * diagonal (fun i => (ev_ρ i : ℂ)) * Uᴴ * + (V * diagonal (fun j => (Real.log (ev_σ j) : ℂ)) * Vᴴ) = + U * (diagonal (fun i => (ev_ρ i : ℂ)) * Wᴴ * + diagonal (fun j => (Real.log (ev_σ j) : ℂ))) * Vᴴ := by + simp only [Matrix.mul_assoc, ← hUHV] + rw [hconv] + have hVHU : Vᴴ * U = W := rfl + -- Apply trace_mul_cycle to bring Vᴴ adjacent to U + have hstep1 : (U * ((diagonal (fun i => (ev_ρ i : ℂ))) * Wᴴ * + (diagonal (fun j => (Real.log (ev_σ j) : ℂ)))) * Vᴴ).trace = + (Vᴴ * (U * ((diagonal (fun i => (ev_ρ i : ℂ))) * Wᴴ * + (diagonal (fun j => (Real.log (ev_σ j) : ℂ)))))).trace := by + rw [trace_mul_comm] + rw [hstep1] + simp only [Matrix.mul_assoc] + -- Goal: (Vᴴ * (U * (diag_ev * (Wᴴ * diag_log)))).trace.re + -- Use ← mul_assoc to get (Vᴴ * U) * ... + conv_lhs => rw [← Matrix.mul_assoc Vᴴ U, hVHU] + -- Now: (W * (diag_ev * (Wᴴ * diag_log))).trace.re + rw [← Matrix.mul_assoc W (diagonal _)] + -- Now: (W * diag_ev * (Wᴴ * diag_log)).trace.re + -- Use extensionality to compare summands + simp only [Matrix.trace, Matrix.diag, mul_apply, conjTranspose_apply, diagonal_apply] + rw [Complex.re_sum] + -- RHS is ∑ i, ∑ j, ... ; swap to ∑ j, ∑ i, ... to match LHS structure + conv_rhs => rw [Finset.sum_comm] + congr 1; ext j + -- Each summand has nested sums with if-then-else that simplify to single terms + have h1 : ∀ x, (∑ x_1, W j x_1 * if x_1 = x then ↑(ev_ρ x_1) else 0) = W j x * ↑(ev_ρ x) := by + intro x + rw [Finset.sum_eq_single x] + · simp only [if_true] + · intro b _ hb + simp only [if_neg hb, mul_zero] + · intro h; exact absurd (Finset.mem_univ x) h + have h2 : ∀ x, (∑ x_1, star (W x_1 x) * if x_1 = j then ↑(Real.log (ev_σ x_1)) else 0) = + star (W j x) * ↑(Real.log (ev_σ j)) := by + intro x + rw [Finset.sum_eq_single j] + · simp only [if_true] + · intro b _ hb + simp only [if_neg hb, mul_zero] + · intro h; exact absurd (Finset.mem_univ j) h + simp only [h1, h2] + rw [Complex.re_sum] + congr 1; ext x + -- Goal: (W j x * ↑(ev_ρ x) * (star (W j x) * ↑(Real.log (ev_σ j)))).re = + -- Complex.normSq (W j x) * ev_ρ x * Real.log (ev_σ j) + have hstar : star (W j x) * W j x = ↑(Complex.normSq (W j x)) := by + simp only [RCLike.star_def, Complex.normSq_eq_conj_mul_self] + have hrearrange : W j x * ↑(ev_ρ x) * (star (W j x) * ↑(Real.log (ev_σ j))) = + star (W j x) * W j x * ↑(ev_ρ x) * ↑(Real.log (ev_σ j)) := by ring + rw [hrearrange, hstar] + simp only [Complex.mul_re, Complex.ofReal_re, Complex.ofReal_im, zero_mul, sub_zero, mul_assoc] + +/-- Quantum relative entropy is non-negative (Klein's inequality). +D(ρ‖σ) ∈ [0, +∞] with D(ρ‖σ) = 0 iff ρ = σ. + +**Proof sketch**: When D = ⊤ the bound is trivial. When supp(ρ) ⊆ supp(σ), +apply Klein's operator inequality to f(x) = -log x (operator-convex on (0,∞)). -/ +theorem relativeEntropy_nonneg (ρ σ : DensityMatrix n) : + 0 ≤ D(ρ ∥ σ) := by + unfold relativeEntropy + split_ifs with h + · simp only [EReal.coe_nonneg] + change 0 ≤ (ρ.toMatrix * (log ρ - log σ)).trace.re + set ev_ρ := ρ.isHermitian.eigenvalues + set ev_σ := σ.isHermitian.eigenvalues + set W := eigW ρ σ + rw [Matrix.mul_sub, trace_sub, Complex.sub_re, trace_ρlogρ_eq ρ, trace_ρlogσ_eq ρ σ] + rw [show ∑ i, ev_ρ i * Real.log (ev_ρ i) = + ∑ i, ∑ j, Complex.normSq (W j i) * ev_ρ i * Real.log (ev_ρ i) by + congr 1; ext i + rw [← Finset.sum_mul, ← Finset.sum_mul, eigW_unitary_colsum ρ σ, one_mul], + ← Finset.sum_sub_distrib] + apply le_trans (b := ∑ i : n, ∑ j : n, Complex.normSq (W j i) * (ev_ρ i - ev_σ j)) + · -- Lower bound: Σᵢⱼ |Wji|² (ev_ρᵢ - ev_σⱼ) = 0 (by unitarity + trace = 1) + simp only [mul_sub, Finset.sum_sub_distrib] + rw [show ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_ρ i = + ∑ i : n, ev_ρ i * ∑ j : n, Complex.normSq (W j i) by + congr 1; ext i; rw [Finset.mul_sum]; congr 1; ext j; ring, + show ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_σ j = + ∑ j : n, ev_σ j * ∑ i : n, Complex.normSq (W j i) by + rw [Finset.sum_comm]; congr 1; ext j; rw [Finset.mul_sum]; congr 1; ext i; ring] + simp_rw [show ∀ i, ∑ j : n, Complex.normSq (W j i) = 1 from eigW_unitary_colsum ρ σ, + show ∀ j, ∑ i : n, Complex.normSq (W j i) = 1 from eigW_unitary_rowsum ρ σ, + mul_one, + show ∑ i, ev_ρ i = 1 from ρ.sum_eigenvalues, + show ∑ j, ev_σ j = 1 from σ.sum_eigenvalues, sub_self, le_refl] + · -- Term-by-term: |Wji|²(ev_ρᵢ-ev_σⱼ) ≤ |Wji|²(ev_ρᵢ log ev_ρᵢ - ev_ρᵢ log ev_σⱼ) + apply Finset.sum_le_sum; intro i _ + rw [← Finset.sum_sub_distrib] + apply Finset.sum_le_sum; intro j _ + -- Normalize goal: eigW ρ σ j i = W j i, eigenvalues = ev_ρ/ev_σ + change Complex.normSq (W j i) * (ev_ρ i - ev_σ j) ≤ + Complex.normSq (W j i) * ev_ρ i * Real.log (ev_ρ i) - + Complex.normSq (W j i) * ev_ρ i * Real.log (ev_σ j) + rw [← mul_sub] + -- Goal: normSq * (ev_ρ - ev_σ) ≤ normSq * ev_ρ * (log ev_ρ - log ev_σ) + rw [show Complex.normSq (W j i) * ev_ρ i * (Real.log (ev_ρ i) - Real.log (ev_σ j)) = + Complex.normSq (W j i) * (ev_ρ i * (Real.log (ev_ρ i) - Real.log (ev_σ j))) by ring] + -- Check if normSq = 0 first (in which case both sides are 0) + by_cases hnormSq : Complex.normSq (W j i) = 0 + · simp [hnormSq] + · apply mul_le_mul_of_nonneg_left _ (Complex.normSq_nonneg _) + rcases (σ.eigenvalues_nonneg j).lt_or_eq with hevσpos | hevσzero + · rcases (ρ.eigenvalues_nonneg i).lt_or_eq with hevρpos | hevρzero + · have := mul_log_div_ge_sub' hevρpos hevσpos + rw [Real.log_div (ne_of_gt hevρpos) (ne_of_gt hevσpos)] at this; linarith + · -- ev_ρ i = 0, ev_σ j > 0 + -- Goal: ev_ρ i - ev_σ j ≤ ev_ρ i * (log ev_ρ i - log ev_σ j) + -- With ev_ρ i = 0: -ev_σ j ≤ 0, which follows from ev_σ j > 0 + have hρeq : ev_ρ i = 0 := hevρzero.symm + simp only [hρeq, zero_mul, zero_sub] + linarith + · -- ev_σ j = 0, but normSq ≠ 0 + -- suppSubset implies normSq * ev_ρ = 0, and since normSq ≠ 0, we get ev_ρ = 0 + have hzero := suppSubset_normSq_ev_zero ρ σ h j hevσzero.symm i + have hprod : Complex.normSq (W j i) * ev_ρ i = 0 := + le_antisymm + (by nlinarith [Complex.normSq_nonneg (W j i), ρ.eigenvalues_nonneg i, + mul_nonneg (Complex.normSq_nonneg (W j i)) (ρ.eigenvalues_nonneg i)]) + (by nlinarith [mul_nonneg (Complex.normSq_nonneg (W j i)) + (ρ.eigenvalues_nonneg i)]) + have hevρ : ev_ρ i = 0 := by + rcases (mul_eq_zero.mp hprod) with h | h + · exact absurd h hnormSq + · exact h + -- Goal: 0 - ev_σ j ≤ 0 * (log 0 - log ev_σ j) + -- With ev_σ j = 0: 0 - 0 ≤ 0 * (...) = 0, so 0 ≤ 0 + have hσeq : ev_σ j = 0 := hevσzero.symm + simp [hevρ, hσeq] + · exact le_top + +/-- Quantum relative entropy is zero iff ρ = σ (faithfulness / quantum Pinsker). +D(ρ‖σ) = 0 if and only if ρ = σ. + +This is a consequence of Klein's inequality plus the strict convexity of x ↦ x log x. +Note: D(ρ‖σ) = ⊤ ≠ 0 when supp(ρ) ⊄ supp(σ). -/ +theorem relativeEntropy_eq_zero_iff (ρ σ : DensityMatrix n) : + D(ρ ∥ σ) = 0 ↔ ρ = σ := by + constructor + · -- → direction: D(ρ‖σ) = 0 → ρ = σ + intro hD + -- First: D = ⊤ would give ⊤ = 0, contradiction, so we must be in the suppSubset case. + unfold relativeEntropy at hD + by_cases h : suppSubset ρ.toMatrix σ.toMatrix + · simp only [h, ↓reduceIte] at hD + rw [EReal.coe_eq_zero] at hD + change (ρ.toMatrix * (log ρ - log σ)).trace.re = 0 at hD + -- Extract eigenvalue data + set V := (σ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) with hV_def + set U := (ρ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) with hU_def + set W := eigW ρ σ with hW_def + set ev_ρ := ρ.isHermitian.eigenvalues + set ev_σ := σ.isHermitian.eigenvalues + have hVW : V * W = U := by + simp only [W, eigW] + rw [← Matrix.mul_assoc, UUH_eq_one _ σ.isHermitian, Matrix.one_mul] + have hWW : W * Wᴴ = 1 := eigW_WWH ρ σ + have hVVH : V * Vᴴ = 1 := UUH_eq_one _ σ.isHermitian + -- D expressed as eigenvalue sum + have hD_sum : ∑ i : n, ∑ j : n, Complex.normSq (W j i) * + (ev_ρ i * Real.log (ev_ρ i) - ev_ρ i * Real.log (ev_σ j)) = 0 := by + rw [Matrix.mul_sub, trace_sub, Complex.sub_re] at hD + rw [trace_ρlogρ_eq ρ, trace_ρlogσ_eq ρ σ] at hD + rw [show ∑ i, ev_ρ i * Real.log (ev_ρ i) = + ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_ρ i * Real.log (ev_ρ i) by + congr 1; ext i + rw [← Finset.sum_mul, ← Finset.sum_mul, eigW_unitary_colsum ρ σ, one_mul]] at hD + linarith [show ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_ρ i * Real.log (ev_ρ i) - + ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_ρ i * Real.log (ev_σ j) = + ∑ i : n, ∑ j : n, Complex.normSq (W j i) * + (ev_ρ i * Real.log (ev_ρ i) - ev_ρ i * Real.log (ev_σ j)) by + simp only [← Finset.sum_sub_distrib]; congr 1; ext i; congr 1; ext j; ring] + -- KL form: D = Σᵢⱼ |Wji|² ev_σⱼ klFun(ev_ρᵢ/ev_σⱼ) + have hklform : ∑ i : n, ∑ j : n, + Complex.normSq (W j i) * ev_σ j * InformationTheory.klFun (ev_ρ i / ev_σ j) = 0 := by + have hD_eq : ∑ i : n, ∑ j : n, Complex.normSq (W j i) * + (ev_ρ i * Real.log (ev_ρ i) - ev_ρ i * Real.log (ev_σ j)) = + ∑ i : n, ∑ j : n, + (Complex.normSq (W j i) * ev_σ j * InformationTheory.klFun (ev_ρ i / ev_σ j) + + Complex.normSq (W j i) * (ev_ρ i - ev_σ j)) := by + congr 1; ext i; congr 1; ext j + unfold InformationTheory.klFun + rcases (σ.eigenvalues_nonneg j).lt_or_eq with hμpos | hμzero + · rcases (ρ.eigenvalues_nonneg i).lt_or_eq with hevρpos | hevρzero + · have hevρne : ev_ρ i ≠ 0 := ne_of_gt hevρpos + have hevσne : ev_σ j ≠ 0 := ne_of_gt hμpos + field_simp; rw [Real.log_div hevρne hevσne]; ring + · have hev_ρ_zero : ev_ρ i = 0 := hevρzero.symm + simp [hev_ρ_zero, Real.log_zero] + · have hsupp' := suppSubset_normSq_ev_zero ρ σ h j hμzero.symm i + rcases mul_eq_zero.mp hsupp' with hw0 | hev0 + · have hW0 : Complex.normSq (W j i) = 0 := hw0 + simp [hW0] + · have hev0' : ev_ρ i = 0 := by exact_mod_cast hev0 + simp [hev0', Real.log_zero] + rw [hD_eq] at hD_sum + simp_rw [Finset.sum_add_distrib] at hD_sum + have hzero : ∑ i : n, ∑ j : n, Complex.normSq (W j i) * (ev_ρ i - ev_σ j) = 0 := by + simp only [mul_sub, Finset.sum_sub_distrib] + rw [show ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_ρ i = ∑ i : n, ev_ρ i by + congr 1; ext i; rw [← Finset.sum_mul, eigW_unitary_colsum ρ σ, one_mul], + show ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_σ j = ∑ j : n, ev_σ j by + rw [Finset.sum_comm]; congr 1; ext j; rw [← Finset.sum_mul, eigW_unitary_rowsum ρ σ, one_mul]] + linarith [ρ.sum_eigenvalues, σ.sum_eigenvalues] + linarith + -- Each KL term = 0 + have hterms : ∀ (i j : n), + Complex.normSq (W j i) * ev_σ j * InformationTheory.klFun (ev_ρ i / ev_σ j) = 0 := by + have hterm_nn : ∀ (i j : n), + 0 ≤ Complex.normSq (W j i) * ev_σ j * InformationTheory.klFun (ev_ρ i / ev_σ j) := + fun i j => mul_nonneg (mul_nonneg (Complex.normSq_nonneg _) + (σ.eigenvalues_nonneg j)) + (InformationTheory.klFun_nonneg (div_nonneg (ρ.eigenvalues_nonneg i) + (σ.eigenvalues_nonneg j))) + have hnn_sum : ∀ (i : n), 0 ≤ ∑ j : n, + Complex.normSq (W j i) * ev_σ j * InformationTheory.klFun (ev_ρ i / ev_σ j) := + fun i => Finset.sum_nonneg fun j _ => hterm_nn i j + intro i j + have houter := (Finset.sum_eq_zero_iff_of_nonneg (fun i _ => hnn_sum i)).mp + hklform i (Finset.mem_univ _) + exact (Finset.sum_eq_zero_iff_of_nonneg (fun j _ => hterm_nn i j)).mp + houter j (Finset.mem_univ _) + -- Derive normSq(Wji) * (ev_ρᵢ - ev_σⱼ) = 0 + have hterm_diff : ∀ (i j : n), Complex.normSq (W j i) * (ev_ρ i - ev_σ j) = 0 := by + intro i j + rcases (σ.eigenvalues_nonneg j).lt_or_eq with hμpos | hμzero + · rcases mul_eq_zero.mp (hterms i j) with h1 | h2 + · rcases mul_eq_zero.mp h1 with h3 | h4 + · rw [h3, zero_mul] + · exact absurd h4 (ne_of_gt hμpos) + · have hkl := (InformationTheory.klFun_eq_zero_iff + (div_nonneg (ρ.eigenvalues_nonneg i) (σ.eigenvalues_nonneg j))).mp h2 + have hev_eq : ev_ρ i = ev_σ j := by + have := div_eq_one_iff_eq (ne_of_gt hμpos) |>.mp hkl + exact_mod_cast this + rw [hev_eq, sub_self, mul_zero] + · have hev_zero : ev_σ j = 0 := hμzero.symm + rw [hev_zero, sub_zero] + exact suppSubset_normSq_ev_zero ρ σ h j hμzero.symm i + -- Derive W_{ji} * ev_ρᵢ = W_{ji} * ev_σⱼ + have hstep : ∀ (i j : n), W j i * (ev_ρ i : ℂ) = W j i * (ev_σ j : ℂ) := fun i j => by + rcases mul_eq_zero.mp (hterm_diff i j) with h1 | h2 + · rw [Complex.normSq_eq_zero] at h1; simp [h1] + · congr 1; exact_mod_cast sub_eq_zero.mp h2 + -- W * diag(ev_ρ) = diag(ev_σ) * W + have hcommute : W * diagonal (fun i => (ev_ρ i : ℂ)) = diagonal (fun j => (ev_σ j : ℂ)) * W := by + ext j i + simp only [mul_apply, diagonal_apply, ite_mul, zero_mul, mul_ite, mul_zero] + rw [Finset.sum_ite_eq', Finset.sum_ite_eq] + simp only [Finset.mem_univ, ite_true] + calc W j i * (ev_ρ i : ℂ) = W j i * (ev_σ j : ℂ) := hstep i j + _ = (ev_σ j : ℂ) * W j i := by ring + -- W * diag(ev_ρ) * W† = diag(ev_σ) + have hWdiag : W * diagonal (fun i => (ev_ρ i : ℂ)) * Wᴴ = diagonal (fun j => (ev_σ j : ℂ)) := by + calc W * diagonal (fun i => (ev_ρ i : ℂ)) * Wᴴ + = diagonal (fun j => (ev_σ j : ℂ)) * W * Wᴴ := by rw [hcommute] + _ = diagonal (fun j => (ev_σ j : ℂ)) * (W * Wᴴ) := by rw [Matrix.mul_assoc] + _ = diagonal (fun j => (ev_σ j : ℂ)) := by rw [hWW, Matrix.mul_one] + -- ρ.toMatrix = U diag(ev_ρ) Uᴴ + have hρ_spec : ρ.toMatrix = U * diagonal (fun i => (ev_ρ i : ℂ)) * Uᴴ := + spectral_expand ρ.toMatrix ρ.isHermitian + -- σ.toMatrix = V diag(ev_σ) Vᴴ + have hσ_spec : σ.toMatrix = V * diagonal (fun j => (ev_σ j : ℂ)) * Vᴴ := + spectral_expand σ.toMatrix σ.isHermitian + -- ρ.toMatrix = σ.toMatrix via: U diag(ev_ρ) Uᴴ = VW diag(ev_ρ)Wᴴ Vᴴ = V diag(ev_σ) Vᴴ + apply DensityMatrix.ext + rw [hρ_spec, hσ_spec, ← hVW, conjTranspose_mul] + calc V * W * diagonal (fun i => (ev_ρ i : ℂ)) * (Wᴴ * Vᴴ) + = V * (W * diagonal (fun i => (ev_ρ i : ℂ)) * Wᴴ) * Vᴴ := by + simp only [Matrix.mul_assoc] + _ = V * diagonal (fun j => (ev_σ j : ℂ)) * Vᴴ := by rw [hWdiag] + · -- h : ¬ suppSubset ρ.toMatrix σ.toMatrix, so relativeEntropy = ⊤ ≠ 0, contradiction + simp only [h, ↓reduceIte, EReal.top_ne_zero] at hD + · -- ← direction: ρ = σ → D(ρ‖σ) = 0 + intro h + subst h + unfold relativeEntropy + simp only [show suppSubset ρ.toMatrix ρ.toMatrix from fun _ h => h, ↓reduceIte] + change (↑(ρ.toMatrix * (log ρ - log ρ)).trace.re : EReal) = 0 + rw [sub_self, Matrix.mul_zero, Matrix.trace_zero, Complex.zero_re, EReal.coe_zero] + +/-! ### Support Subset Preservation for Channels (needed before monotonicity) -/ + +omit [DecidableEq n] in +/-- For a positive semidefinite matrix B, if Re[v† B v] = 0 then B v = 0. -/ +private lemma mulVec_eq_zero_of_re_inner_zero' + {B : Matrix n n ℂ} (hB : B.PosSemidef) + (v : n → ℂ) (hv : (star v ⬝ᵥ B.mulVec v).re = 0) : + B.mulVec v = 0 := by + rw [← hB.dotProduct_mulVec_zero_iff] + apply Complex.ext + · exact hv + · exact hB.1.im_star_dotProduct_mulVec_self v + +omit [DecidableEq n] [DecidableEq m] in +/-- Support subset is preserved by a single Kraus conjugation K ρ K†. -/ +private lemma suppSubset_kraus_single' + (K : Matrix m n ℂ) {ρ σ : Matrix n n ℂ} + (hσ : σ.PosSemidef) (h : suppSubset ρ σ) : + suppSubset (K * ρ * Kᴴ) (K * σ * Kᴴ) := by + intro v hv + have hKHv_ker : σ.mulVec (Kᴴ.mulVec v) = 0 := by + apply mulVec_eq_zero_of_re_inner_zero' hσ + have h_eq : (star (Kᴴ.mulVec v) ⬝ᵥ σ.mulVec (Kᴴ.mulVec v)).re = + (star v ⬝ᵥ (K * σ * Kᴴ).mulVec v).re := by + congr 1 + conv_rhs => rw [show (K * σ * Kᴴ).mulVec v = K.mulVec (σ.mulVec (Kᴴ.mulVec v)) from by + simp only [← Matrix.mulVec_mulVec]] + rw [star_mulVec, Matrix.conjTranspose_conjTranspose, ← dotProduct_mulVec] + rw [h_eq, hv]; simp + have hρKHv_zero : ρ.mulVec (Kᴴ.mulVec v) = 0 := h _ hKHv_ker + simp only [show (K * ρ * Kᴴ).mulVec v = K.mulVec (ρ.mulVec (Kᴴ.mulVec v)) from by + simp only [← Matrix.mulVec_mulVec], hρKHv_zero, Matrix.mulVec_zero] + +omit [DecidableEq n] [DecidableEq m] in +/-- Support subset is preserved under finite sums of Kraus-conjugated pairs. -/ +private lemma suppSubset_sum' {r : ℕ} {A B : Fin r → Matrix m m ℂ} + (hB : ∀ k, (B k).PosSemidef) + (h : ∀ k, suppSubset (A k) (B k)) : + suppSubset (∑ k, A k) (∑ k, B k) := by + intro v hv + simp only [Matrix.sum_mulVec] at hv ⊢ + have hB_nonneg : ∀ k, 0 ≤ (star v ⬝ᵥ (B k).mulVec v).re := + fun k => (hB k).re_dotProduct_nonneg v + have hsum_zero : ∑ k : Fin r, (star v ⬝ᵥ (B k).mulVec v).re = 0 := by + have heq : (star v ⬝ᵥ ∑ k : Fin r, (B k).mulVec v).re = 0 := by rw [hv]; simp + rw [dotProduct_sum] at heq + simpa [Complex.re_sum] using heq + have hB_each : ∀ k, (star v ⬝ᵥ (B k).mulVec v).re = 0 := fun k => + le_antisymm (by + calc (star v ⬝ᵥ (B k).mulVec v).re + ≤ ∑ i : Fin r, (star v ⬝ᵥ (B i).mulVec v).re := + Finset.single_le_sum (fun i _ => hB_nonneg i) (Finset.mem_univ k) + _ = 0 := hsum_zero) (hB_nonneg k) + have hBv_zero : ∀ k, (B k).mulVec v = 0 := fun k => by + rw [← (hB k).dotProduct_mulVec_zero_iff] + apply Complex.ext + · exact hB_each k + · exact (hB k).1.im_star_dotProduct_mulVec_self v + have hAv_zero : ∀ k, (A k).mulVec v = 0 := fun k => h k v (hBv_zero k) + simp only [hAv_zero, Finset.sum_const_zero] + +omit [DecidableEq n] [DecidableEq m] in +/-- Support subset is preserved by quantum channels. -/ +private lemma suppSubset_channel' + (Φ : QuantumChannel n m) + {ρ σ : Matrix n n ℂ} (hσ : σ.PosSemidef) (h : suppSubset ρ σ) : + suppSubset (Φ.val ρ) (Φ.val σ) := by + obtain ⟨r, K, hK⟩ := Φ.property.completelyPositive + rw [hK, hK] + exact suppSubset_sum' + (fun k => hσ.mul_mul_conjTranspose_same (K k)) + (fun k => suppSubset_kraus_single' (K k) hσ h) + +/-! ### Monotonicity of Relative Entropy -/ + +/-! #### Pinching method for the partial trace inequality + +The proof of trace_rpow_mul_channel_le uses the pinching method (root-of-unity +unitary averaging). The proof chain is: + +1. **Step 1**: F_s(VρV†, VσV†) = F_s(ρ, σ) by `rpow_conj_isometry` +2. **Stage A**: F_s(ω, τ) ≤ F_s(P(ω), P(τ)) by pinching inequality +3. **Stage B**: F_s(P(ω), P(τ)) ≤ F_s(Φρ, Φσ) by super-additivity + +where P is the block-diagonal pinching map and ω = VρV†. -/ + +/-- Tr (ρˢ σ¹⁻ˢ) equals a double sum over eigenvalues via the +change-of-basis unitary W = U_σ† U_ρ: + Tr (ρˢ σ¹⁻ˢ) = ∑_{i,j} |W_{ji}|² λᵢˢ μⱼ¹⁻ˢ -/ +private lemma trace_rpow_mul_double_sum (ρ σ : DensityMatrix n) (s : ℝ) : + (Tr (ρ ^ s * σ ^ (1 - s))).re = + ∑ i : n, ∑ j : n, + Complex.normSq (eigW ρ σ j i) * + ρ.isHermitian.eigenvalues i ^ s * + σ.isHermitian.eigenvalues j ^ (1 - s) := by + change (ρ.toMatrix ^ s * σ.toMatrix ^ (1 - s)).trace.re = _ + set U := (ρ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + set V := (σ.isHermitian.eigenvectorUnitary : Matrix n n ℂ) + set W := eigW ρ σ + set ev_ρ := ρ.isHermitian.eigenvalues + set ev_σ := σ.isHermitian.eigenvalues + have hpsdρ := ρ.posSemidef + have hpsdσ := σ.posSemidef + have hρs : ρ.toMatrix ^ s = U * diagonal (fun i => ((ev_ρ i ^ s : ℝ) : ℂ)) * Uᴴ := by + rw [← matrixFunction_rpow_eq hpsdρ]; unfold matrixFunction; rfl + have hσs : σ.toMatrix ^ (1 - s) = V * diagonal (fun j => ((ev_σ j ^ (1 - s) : ℝ) : ℂ)) * Vᴴ := by + rw [← matrixFunction_rpow_eq hpsdσ]; unfold matrixFunction; rfl + have hVU : Vᴴ * U = W := rfl + rw [hρs, hσs] + -- Use cyclic trace property and W = Vᴴ * U to reduce to W D_ρ Wᴴ D_σ + have hWH : Wᴴ = Uᴴ * V := by rw [← hVU, conjTranspose_mul, conjTranspose_conjTranspose] + have htrace : (U * diagonal (fun i => ((ev_ρ i ^ s : ℝ) : ℂ)) * Uᴴ * + (V * diagonal (fun j => ((ev_σ j ^ (1 - s) : ℝ) : ℂ)) * Vᴴ)).trace = + (W * diagonal (fun i => ((ev_ρ i ^ s : ℝ) : ℂ)) * Wᴴ * + diagonal (fun j => ((ev_σ j ^ (1 - s) : ℝ) : ℂ))).trace := by + set D1 := diagonal (fun i => ((ev_ρ i ^ s : ℝ) : ℂ)) + set D2 := diagonal (fun j => ((ev_σ j ^ (1 - s) : ℝ) : ℂ)) + -- Cyclic permutation: Tr (U D1 Uᴴ V D2 Vᴴ) = Tr (Vᴴ U D1 Uᴴ V D2) + rw [show U * D1 * Uᴴ * (V * D2 * Vᴴ) = + (U * D1 * Uᴴ * V * D2) * Vᴴ from by + simp [Matrix.mul_assoc]] + rw [Matrix.trace_mul_comm] + rw [show Vᴴ * (U * D1 * Uᴴ * V * D2) = W * D1 * Wᴴ * D2 from by + rw [show Vᴴ * (U * D1 * Uᴴ * V * D2) = (Vᴴ * U) * D1 * (Uᴴ * V) * D2 from by + simp [Matrix.mul_assoc]] + rw [hVU, ← hWH]] + rw [htrace] + -- Expand trace elementwise and reduce diagonal selections + simp only [Matrix.trace, Matrix.diag, Matrix.mul_apply, conjTranspose_apply, diagonal_apply, + mul_ite, mul_zero, Finset.sum_ite_eq', Finset.mem_univ, if_true, + Complex.star_def, Complex.normSq_apply, Complex.re_sum, Complex.mul_re, + Complex.mul_im, Complex.conj_re, Complex.conj_im, + Complex.ofReal_re, Complex.ofReal_im, Finset.sum_mul] + rw [Finset.sum_comm] + apply Finset.sum_congr rfl; intro i _ + apply Finset.sum_congr rfl; intro j _ + ring + +omit [DecidableEq n] in +/-- HasDerivAt for the double sum ∑_{i,j} w_{ij} λᵢˢ μⱼ¹⁻ˢ at s=1. -/ +private lemma hasDerivAt_double_rpow_sum + (w : n → n → ℝ) + (ev1 ev2 : n → ℝ) (hev1 : ∀ i, 0 ≤ ev1 i) (hev2 : ∀ j, 0 ≤ ev2 j) + (hsupp : ∀ j i, ev2 j = 0 → w i j * ev1 i = 0) : + HasDerivAt (fun s : ℝ => ∑ i : n, ∑ j : n, w i j * ev1 i ^ s * ev2 j ^ (1 - s)) + (∑ i : n, ∑ j : n, w i j * ev1 i * (Real.log (ev1 i) - Real.log (ev2 j))) 1 := by + have inner : ∀ i : n, ∀ j : n, HasDerivAt + (fun s : ℝ => w i j * ev1 i ^ s * ev2 j ^ (1 - s)) + (w i j * ev1 i * (Real.log (ev1 i) - Real.log (ev2 j))) 1 := by + intro i j + rcases (hev1 i).lt_or_eq with hev1pos | hev1zero + · rcases (hev2 j).lt_or_eq with hev2pos | hev2zero + · have hd1 : HasDerivAt (fun s : ℝ => ev1 i ^ s) (ev1 i * Real.log (ev1 i)) 1 := by + have := (hasDerivAt_id (𝕜 := ℝ) 1).mul_const (Real.log (ev1 i)) |>.exp + simp only [id] at this + have heq : (fun x => Real.exp (x * Real.log (ev1 i))) = (fun x => ev1 i ^ x) := by + ext x; rw [Real.rpow_def_of_pos hev1pos, mul_comm] + rw [heq] at this + convert this using 1 + rw [one_mul, Real.exp_log hev1pos] + have hd2 : HasDerivAt (fun s : ℝ => ev2 j ^ (1 - s)) (-(Real.log (ev2 j))) 1 := by + have := ((hasDerivAt_const (𝕜 := ℝ) 1 (Real.log (ev2 j))).sub + ((hasDerivAt_id (𝕜 := ℝ) 1).mul_const (Real.log (ev2 j)))).exp + simp only [id, Pi.sub_apply] at this + have heq : (fun x => Real.exp (Real.log (ev2 j) - x * Real.log (ev2 j))) = + (fun x => ev2 j ^ (1 - x)) := by + ext x; rw [Real.rpow_def_of_pos hev2pos]; ring_nf + rw [heq] at this + convert this using 1 + simp only [one_mul, sub_self, Real.exp_zero, zero_sub] + have h12 := HasDerivAt.mul hd1 hd2 + have h12c := h12.const_mul (w i j) + simp only [Pi.mul_apply] at h12c + convert h12c using 1 + · funext s; ring + · simp only [Real.rpow_one, sub_self, Real.rpow_zero]; ring + · rcases mul_eq_zero.mp (hsupp j i hev2zero.symm) with hw0 | hev10 + · simp only [hw0, zero_mul]; exact hasDerivAt_const _ _ + · linarith + · simp only [← hev1zero, mul_zero, zero_mul] + apply (hasDerivAt_const (𝕜 := ℝ) (1:ℝ) (0:ℝ)).congr_of_eventuallyEq + apply Filter.eventually_of_mem (Ioi_mem_nhds (show (0:ℝ) < 1 from by norm_num)) + intro s hs + simp only [Set.mem_Ioi] at hs + simp only [Real.zero_rpow (ne_of_gt hs), mul_zero, zero_mul] + have outer : ∀ i : n, HasDerivAt + (fun s : ℝ => ∑ j : n, w i j * ev1 i ^ s * ev2 j ^ (1 - s)) + (∑ j : n, w i j * ev1 i * (Real.log (ev1 i) - Real.log (ev2 j))) 1 := by + intro i + have h := HasDerivAt.sum (u := Finset.univ) (fun j (_ : j ∈ Finset.univ) => inner i j) + have heq : (∑ j ∈ Finset.univ, fun s : ℝ => w i j * ev1 i ^ s * ev2 j ^ (1 - s)) = + (fun s : ℝ => ∑ j : n, w i j * ev1 i ^ s * ev2 j ^ (1 - s)) := + funext (fun s => Finset.sum_apply _ _ _) + rwa [heq] at h + have h_final := HasDerivAt.sum (u := Finset.univ) (fun i (_ : i ∈ Finset.univ) => outer i) + have heq : (∑ i ∈ Finset.univ, fun s : ℝ => ∑ j : n, w i j * ev1 i ^ s * ev2 j ^ (1 - s)) = + (fun s : ℝ => ∑ i : n, ∑ j : n, w i j * ev1 i ^ s * ev2 j ^ (1 - s)) := + funext (fun s => Finset.sum_apply _ _ _) + rwa [heq] at h_final + +/-- HasDerivAt of Re[Tr (ρˢ σ¹⁻ˢ)] at s=1 equals D(ρ ‖ σ). + +When supp(ρ) ⊆ supp(σ): + (d/ds)|_{s=1} Tr (ρˢ σ¹⁻ˢ) = Tr (ρ(log ρ − log σ)) = D(ρ ‖ σ) -/ +private lemma hasDerivAt_trace_rpow_mul (ρ σ : DensityMatrix n) (h : suppSubset ρ.toMatrix σ.toMatrix) : + HasDerivAt (fun s : ℝ => (Tr (ρ ^ s * σ ^ (1 - s))).re) + ((Tr (ρ * (log ρ - log σ))).re) 1 := by + change HasDerivAt (fun s : ℝ => (ρ.toMatrix ^ s * σ.toMatrix ^ (1 - s)).trace.re) + ((ρ.toMatrix * (log ρ - log σ)).trace.re) 1 + set ev_ρ := ρ.isHermitian.eigenvalues + set ev_σ := σ.isHermitian.eigenvalues + set W := eigW ρ σ + have hconv : (fun s : ℝ => (ρ.toMatrix ^ s * σ.toMatrix ^ (1 - s)).trace.re) = fun s => + ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_ρ i ^ s * ev_σ j ^ (1 - s) := + funext (trace_rpow_mul_double_sum ρ σ) + rw [hconv] + have hderiv := hasDerivAt_double_rpow_sum + (fun i j => Complex.normSq (W j i)) + ev_ρ ev_σ ρ.eigenvalues_nonneg σ.eigenvalues_nonneg + (fun j i hμ => by + have := suppSubset_normSq_ev_zero ρ σ h j hμ i + linarith [mul_nonneg (Complex.normSq_nonneg (W j i)) (ρ.eigenvalues_nonneg i)]) + convert hderiv using 1 + simp only [] -- beta-reduce lambda in hderiv's derivative form + -- Relate derivative to D(ρ‖σ) = Tr (ρ(log ρ)) - Tr (ρ(log σ)) + rw [Matrix.mul_sub, trace_sub, Complex.sub_re, trace_ρlogρ_eq ρ, trace_ρlogσ_eq ρ σ] + -- Rewrite Σᵢ evᵢ log evᵢ as Σᵢⱼ |Wji|² evᵢ log evᵢ (using column sum = 1) + have h1 : ∑ i : n, ev_ρ i * Real.log (ev_ρ i) = + ∑ i : n, ∑ j : n, Complex.normSq (W j i) * ev_ρ i * Real.log (ev_ρ i) := by + congr 1; ext i + rw [← Finset.sum_mul, ← Finset.sum_mul, + show ∑ j : n, Complex.normSq (W j i) = 1 from eigW_unitary_colsum ρ σ i] + ring + rw [h1, ← Finset.sum_sub_distrib]; congr 1; ext i + rw [← Finset.sum_sub_distrib]; congr 1; ext j; ring + +/-! #### Isometry and unitary invariance of F_s -/ + +/-- Unitary invariance of F_s: Tr ((UAU†)ˢ (UBU†)¹⁻ˢ) = Tr (Aˢ B¹⁻ˢ) for unitary U. + +Proof: By `rpow_unitary_conj`, (UAU†)ˢ = U Aˢ U†. +Then trace cyclicity gives Tr (U Aˢ U† U B¹⁻ˢ U†) = +Tr (Aˢ (U†U) B¹⁻ˢ (U†U)) = Tr (Aˢ B¹⁻ˢ). -/ +private lemma trace_rpow_mul_unitary_conj_eq + {α : Type*} [Fintype α] [DecidableEq α] + (U : Matrix α α ℂ) (hU : U ∈ Matrix.unitaryGroup α ℂ) + (A B : Matrix α α ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) + (s : ℝ) (hs0 : 0 < s) (hs1 : s < 1) : + (Tr ((U * A * Uᴴ) ^ s * (U * B * Uᴴ) ^ (1 - s))).re = + (Tr (A ^ s * B ^ (1 - s))).re := by + have h1s_pos : 0 < 1 - s := by linarith + have hA' : (0 : Matrix α α ℂ) ≤ A := by rw [Matrix.le_iff, sub_zero]; exact hA + have hB' : (0 : Matrix α α ℂ) ≤ B := by rw [Matrix.le_iff, sub_zero]; exact hB + have hUA' : (0 : Matrix α α ℂ) ≤ U * A * Uᴴ := by + rw [Matrix.le_iff, sub_zero]; exact hA.mul_mul_conjTranspose_same U + have hUB' : (0 : Matrix α α ℂ) ≤ U * B * Uᴴ := by + rw [Matrix.le_iff, sub_zero]; exact hB.mul_mul_conjTranspose_same U + rw [rpow_unitary_conj hU hs0.le hA' hUA', + rpow_unitary_conj hU h1s_pos.le hB' hUB'] + -- Now: Tr (U A^s Uᴴ * U B^{1-s} Uᴴ) + have hUHU : Uᴴ * U = 1 := by + rw [← star_eq_conjTranspose] + exact Matrix.mem_unitaryGroup_iff'.mp hU + have heq : U * A ^ s * Uᴴ * (U * B ^ (1 - s) * Uᴴ) = + U * (A ^ s * B ^ (1 - s)) * Uᴴ := by + simp only [Matrix.mul_assoc] + rw [← Matrix.mul_assoc Uᴴ U _, hUHU, Matrix.one_mul] + rw [heq] + rw [Matrix.trace_mul_comm (U * (A ^ s * B ^ (1 - s))) Uᴴ, + show Uᴴ * (U * (A ^ s * B ^ (1 - s))) = A ^ s * B ^ (1 - s) by + rw [← Matrix.mul_assoc, hUHU, Matrix.one_mul]] + +/-- Isometry invariance of F_s: F_s(VρV†, VσV†) = F_s(ρ, σ) +when V†V = I. + +Proof: By `rpow_conj_isometry`, (VAV†)ˢ = V Aˢ V†. +Then trace cyclicity and V†V = I yield the result. -/ +private lemma trace_rpow_mul_isometry_conj_eq + {α β : Type*} [Fintype α] [Fintype β] [DecidableEq α] [DecidableEq β] + (V : Matrix β α ℂ) (hV : Vᴴ * V = 1) + (A B : Matrix α α ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) + (s : ℝ) (hs0 : 0 < s) (hs1 : s < 1) : + (Tr ((V * A * Vᴴ) ^ s * (V * B * Vᴴ) ^ (1 - s))).re = + (Tr (A ^ s * B ^ (1 - s))).re := by + have h1s_pos : 0 < 1 - s := by linarith + have hVA_psd := hA.mul_mul_conjTranspose_same V + have hVB_psd := hB.mul_mul_conjTranspose_same V + rw [rpow_conj_isometry V hV A hA s hs0, + rpow_conj_isometry V hV B hB (1 - s) h1s_pos] + -- Now: Tr (V A^s Vᴴ * V B^{1-s} Vᴴ) + have heq : V * A ^ s * Vᴴ * (V * B ^ (1 - s) * Vᴴ) = + V * (A ^ s * B ^ (1 - s)) * Vᴴ := by + simp only [Matrix.mul_assoc] + rw [← Matrix.mul_assoc Vᴴ V _, hV, Matrix.one_mul] + rw [heq] + rw [Matrix.trace_mul_comm (V * (A ^ s * B ^ (1 - s))) Vᴴ, + show Vᴴ * (V * (A ^ s * B ^ (1 - s))) = A ^ s * B ^ (1 - s) by + rw [← Matrix.mul_assoc, hV, Matrix.one_mul]] + +/-! #### Block-diagonal decomposition of F_s -/ + +/-- For `Fin 1 × m`, rpow of a PSD matrix commutes with submatrix extraction at block 0. +Uses `StarAlgHomClass.map_cfc` to show CFC commutes with reindexing. -/ +private lemma rpow_submatrix_fin_one + {A : Matrix (Fin 1 × m) (Fin 1 × m) ℂ} (hA : A.PosSemidef) + {p : ℝ} (hp : 0 ≤ p) : + (A ^ p).submatrix (Prod.mk (0 : Fin 1)) (Prod.mk 0) = + (A.submatrix (Prod.mk 0) (Prod.mk 0)) ^ p := by + classical + -- Set up normed algebra and C*-algebra instances + letI : NormedRing (Matrix (Fin 1 × m) (Fin 1 × m) ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (Fin 1 × m) (Fin 1 × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix (Fin 1 × m) (Fin 1 × m) ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := Fin 1 × m) (A := ℂ) + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + -- Equivalence e : Fin 1 × m ≃ m (canonical) + let e := Equiv.uniqueProd m (Fin 1) + -- Construct StarAlgEquiv from reindexAlgEquiv + let ψ : Matrix (Fin 1 × m) (Fin 1 × m) ℂ ≃⋆ₐ[ℝ] Matrix m m ℂ := + StarAlgEquiv.ofAlgEquiv (Matrix.reindexAlgEquiv ℝ ℂ e) (fun M => by + ext i j + simp only [star_eq_conjTranspose, Matrix.conjTranspose_apply, + Matrix.reindexAlgEquiv_apply, Matrix.reindex_apply, Matrix.submatrix_apply]) + -- ψ acts as submatrix extraction at block 0 + have hψ_apply : ∀ (M : Matrix (Fin 1 × m) (Fin 1 × m) ℂ), + ψ M = M.submatrix (Prod.mk (0 : Fin 1)) (Prod.mk 0) := by + intro M; ext i j + simp only [ψ, StarAlgEquiv.ofAlgEquiv_apply, Matrix.reindexAlgEquiv_apply, + Matrix.reindex_apply, Matrix.submatrix_apply, e, Equiv.uniqueProd_symm_apply, + Fin.default_eq_zero] + -- Self-adjointness for CFC + have hA_sa : IsSelfAdjoint A := hA.1.isSelfAdjoint + have hψA_sa : IsSelfAdjoint (ψ A) := by + rw [IsSelfAdjoint, ← map_star ψ]; exact congr_arg ψ hA_sa.star_eq + -- Positivity + have hA_le : (0 : Matrix (Fin 1 × m) (Fin 1 × m) ℂ) ≤ A := by + simpa [Matrix.le_iff] using hA + have hψA_le : 0 ≤ ψ A := by + rw [hψ_apply]; simpa [Matrix.le_iff] using hA.submatrix (Prod.mk (0 : Fin 1)) + -- Continuity of ψ (finite-dimensional) + have hψ_cont : Continuous ψ := + ψ.toAlgEquiv.toLinearMap.continuous_of_finiteDimensional + -- Convert LHS rpow to CFC + rw [CFC.rpow_eq_cfc_real (a := A) (ha := hA_le)] + -- Rewrite: (cfc f A).sub = ψ (cfc f A) + conv_lhs => rw [show (cfc (HPow.hPow · p) A).submatrix (Prod.mk (0 : Fin 1)) (Prod.mk 0) + = ψ (cfc (HPow.hPow · p) A) from (hψ_apply _).symm] + -- Apply map_cfc: ψ (cfc f A) = cfc f (ψ A) + rw [StarAlgHomClass.map_cfc (R := ℝ) (S := ℝ) ψ (HPow.hPow · p) A + ((Real.continuous_rpow_const hp).continuousOn) hψ_cont hA_sa hψA_sa] + -- Goal: cfc (· ^ p) (ψ A) = (A.sub ...) ^ p + rw [hψ_apply A] + -- Goal: cfc (· ^ p) (A.sub) = (A.sub) ^ p + exact (CFC.rpow_eq_cfc_real (a := A.submatrix (Prod.mk (0 : Fin 1)) (Prod.mk 0)) + (ha := by simpa [Matrix.le_iff] using hA.submatrix (Prod.mk (0 : Fin 1)))).symm + +/-- **Pinching inequality for F_s**: For PSD matrices ω, τ on (Fin r × m), + F_s(ω, τ) ≤ ∑ᵢ F_s(ωᵢᵢ, τᵢᵢ) +where ωᵢᵢ denotes the i-th diagonal block. + +Proved via joint concavity of F_s under uniform averaging + unitary invariance + block decomposition. -/ +private lemma pinching_inequality_Fs {r : ℕ} [NeZero r] + (ω τ : Matrix (Fin r × m) (Fin r × m) ℂ) (hω : ω.PosSemidef) (hτ : τ.PosSemidef) + (s : ℝ) (hs0 : 0 < s) (hs1 : s < 1) : + (Tr (ω ^ s * τ ^ (1 - s))).re ≤ + ∑ k : Fin r, (Tr ((ω.submatrix (Prod.mk k) (Prod.mk k)) ^ s * + (τ.submatrix (Prod.mk k) (Prod.mk k)) ^ (1 - s))).re := by + -- The pinching inequality follows from joint concavity of F_s. + -- Define P(ω) = (1/r) Σ_k U_k ω U_k† where U_k are the pinching unitaries. + -- Step 1: P(ω) is block-diagonal with blocks ω_ii + -- Step 2: F_s(P(ω), P(τ)) ≥ (1/r) Σ_k F_s(U_k ω U_k†, U_k τ U_k†) by joint concavity + -- Step 3: F_s(U_k ω U_k†, U_k τ U_k†) = F_s(ω, τ) by unitary invariance + -- Step 4: F_s(P(ω), P(τ)) ≥ F_s(ω, τ) + -- Step 5: For block-diagonal P(ω), F_s(P(ω), P(τ)) = Σ_i F_s(ω_ii, τ_ii) + -- Combining: F_s(ω, τ) ≤ Σ_i F_s(ω_ii, τ_ii) + have h1s_pos : 0 < 1 - s := by linarith + -- The diagonal blocks are PSD + have hω_block_psd : ∀ k : Fin r, (ω.submatrix (Prod.mk k) (Prod.mk k)).PosSemidef := + fun k => hω.submatrix _ + have hτ_block_psd : ∀ k : Fin r, (τ.submatrix (Prod.mk k) (Prod.mk k)).PosSemidef := + fun k => hτ.submatrix _ + -- The trace on (Fin r × m) decomposes as sum over blocks + -- Tr (A) = Σ_i Σ_a A_{(i,a)(i,a)} = Σ_i Tr_m[A.submatrix (Prod.mk i) (Prod.mk i)] + have htrace_decomp : ∀ A : Matrix (Fin r × m) (Fin r × m) ℂ, + A.trace = ∑ i : Fin r, (A.submatrix (Prod.mk i) (Prod.mk i)).trace := by + intro A + simp only [Matrix.trace, Matrix.diag, Matrix.submatrix_apply] + rw [← Finset.sum_product'] + rfl + -- Induction on r, using 2-block fromBlocks decomposition at each step + induction r using Nat.strong_induction_on with + | _ r ih => + rcases r with _ | _ | r + · -- r = 0: vacuous + simp only [Finset.univ_eq_empty, Finset.sum_empty] + have hempty : IsEmpty (Fin 0 × m) := by infer_instance + rw [Matrix.trace_eq_zero_of_isEmpty, Complex.zero_re] + · -- r = 1: single block, equality + rw [Fin.sum_univ_one, htrace_decomp (ω ^ s * τ ^ (1 - s)), Fin.sum_univ_one] + apply le_of_eq; congr 1; congr 1 + -- submatrix of product = product of submatrices via reindexAlgEquiv + let e : Fin (0 + 1) × m ≃ m := + { toFun := fun p => p.2 + invFun := fun a => (0, a) + left_inv := fun ⟨i, a⟩ => Prod.ext (Fin.eq_zero i).symm rfl + right_inv := fun _ => rfl } + have hsub_eq : ∀ (M : Matrix (Fin (0 + 1) × m) (Fin (0 + 1) × m) ℂ), + M.submatrix (Prod.mk (0 : Fin (0 + 1))) (Prod.mk 0) = + Matrix.reindexAlgEquiv ℝ ℂ e M := by + intro M; ext i j; rfl + rw [hsub_eq, map_mul, ← hsub_eq, ← hsub_eq] + -- rpow commutes with submatrix + congr 1 + · exact rpow_submatrix_fin_one hω (le_of_lt hs0) + · exact rpow_submatrix_fin_one hτ (le_of_lt h1s_pos) + · -- r ≥ 2: use 2-block splitting and induction + haveI : NeZero (r + 2) := ⟨by omega⟩ + -- Lieb concavity with (r+2)-block pinching gives F_s(ω,τ) ≤ F_s(P(ω),P(τ)) + have hr_pos : (0 : ℝ) < r + 2 := by positivity + have hw_sum : ∑ k : Fin (r + 2), (1 / (r + 2 : ℝ)) = 1 := by + simp only [Finset.sum_const, Finset.card_fin, nsmul_eq_mul] + rw [show ((r + 2 : ℕ) : ℝ) = (r : ℝ) + 2 from Nat.cast_add r 2] + field_simp + have hw_nn : ∀ k : Fin (r + 2), 0 ≤ (1 / (r + 2 : ℝ)) := fun _ => by positivity + let U := pinchingUnitary (m := m) (r + 2) + let A := fun k => U k * ω * (U k)ᴴ + let B := fun k => U k * τ * (U k)ᴴ + have hA_psd : ∀ k, (A k).PosSemidef := fun k => hω.mul_mul_conjTranspose_same (U k) + have hB_psd : ∀ k, (B k).PosSemidef := fun k => hτ.mul_mul_conjTranspose_same (U k) + have hconc := lieb_concavity_weighted A B hA_psd hB_psd + (fun _ => 1 / (r + 2 : ℝ)) hw_nn hw_sum s hs0.le hs1.le + have hunitary : ∀ k, ((A k) ^ s * (B k) ^ (1 - s)).trace.re = + (ω ^ s * τ ^ (1 - s)).trace.re := fun k => + trace_rpow_mul_unitary_conj_eq (U k) (pinchingUnitary_isUnitary k) ω τ hω hτ s hs0 hs1 + simp only [hunitary] at hconc + simp only [Finset.sum_const, Finset.card_fin, nsmul_eq_mul] at hconc + rw [show ((r + 2 : ℕ) : ℝ) = (r : ℝ) + 2 from Nat.cast_add r 2, ← mul_assoc] at hconc + have hmul_cancel : ((r : ℝ) + 2) * (1 / ((r : ℝ) + 2)) = 1 := by field_simp + rw [hmul_cancel, one_mul] at hconc + -- Set abbreviations for pinching averages + set Pω := ∑ k : Fin (r + 2), (1 / (r + 2 : ℝ)) • A k with hPω_def + set Pτ := ∑ k : Fin (r + 2), (1 / (r + 2 : ℝ)) • B k with hPτ_def + -- P(ω) is block-diagonal + have hPω_entry : ∀ i j : Fin (r + 2), ∀ a b : m, + Pω (i, a) (j, b) = if i = j then ω (i, a) (j, b) else 0 := by + intro i j a b + change (∑ k, (1 / (r + 2 : ℝ)) • A k) (i, a) (j, b) = _ + simp_rw [Matrix.sum_apply, Matrix.smul_apply, Complex.real_smul, ← Finset.mul_sum] + convert pinching_average_eq_blockDiag ω i j a b using 2 + push_cast; ring + have hPτ_entry : ∀ i j : Fin (r + 2), ∀ a b : m, + Pτ (i, a) (j, b) = if i = j then τ (i, a) (j, b) else 0 := by + intro i j a b + change (∑ k, (1 / (r + 2 : ℝ)) • B k) (i, a) (j, b) = _ + simp_rw [Matrix.sum_apply, Matrix.smul_apply, Complex.real_smul, ← Finset.mul_sum] + convert pinching_average_eq_blockDiag τ i j a b using 2 + push_cast; ring + -- Pinching averages are PSD + have hPω_psd : Pω.PosSemidef := by + apply posSemidef_sum + intro k _ + exact (hA_psd k).smul (hw_nn k) + have hPτ_psd : Pτ.PosSemidef := by + apply posSemidef_sum + intro k _ + exact (hB_psd k).smul (hw_nn k) + -- Restriction to blocks 1..r+1 + let embed : Fin (r + 1) × m → Fin (r + 2) × m := fun ⟨i, a⟩ => (i.succ, a) + let Qω := Pω.submatrix embed embed + let Qτ := Pτ.submatrix embed embed + have hQω_psd : Qω.PosSemidef := hPω_psd.submatrix _ + have hQτ_psd : Qτ.PosSemidef := hPτ_psd.submatrix _ + -- Q blocks match ω blocks + have hQ_block_ω : ∀ i : Fin (r + 1), + Qω.submatrix (Prod.mk i) (Prod.mk i) = + ω.submatrix (Prod.mk (i.succ)) (Prod.mk i.succ) := by + intro i; ext a b + change Pω (i.succ, a) (i.succ, b) = ω (i.succ, a) (i.succ, b) + rw [hPω_entry]; simp + have hQ_block_τ : ∀ i : Fin (r + 1), + Qτ.submatrix (Prod.mk i) (Prod.mk i) = + τ.submatrix (Prod.mk (i.succ)) (Prod.mk i.succ) := by + intro i; ext a b + change Pτ (i.succ, a) (i.succ, b) = τ (i.succ, a) (i.succ, b) + rw [hPτ_entry]; simp + -- Block 0 matrices + let ω₀₀ := ω.submatrix (Prod.mk (0 : Fin (r + 2))) (Prod.mk 0) + let τ₀₀ := τ.submatrix (Prod.mk (0 : Fin (r + 2))) (Prod.mk 0) + -- Reindex via ψ to show P(ω) = fromBlocks ω₀₀ 0 0 Qω + classical + letI : NormedRing (Matrix (Fin (r + 2) × m) (Fin (r + 2) × m) ℂ) := + Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (Fin (r + 2) × m) (Fin (r + 2) × m) ℂ) := + Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix (Fin (r + 2) × m) (Fin (r + 2) × m) ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := Fin (r + 2) × m) (A := ℂ) + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + letI : NormedRing (Matrix (Fin (r + 1) × m) (Fin (r + 1) × m) ℂ) := + Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (Fin (r + 1) × m) (Fin (r + 1) × m) ℂ) := + Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix (Fin (r + 1) × m) (Fin (r + 1) × m) ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := Fin (r + 1) × m) (A := ℂ) + -- Reindex equivalence + let e := splitFinSuccProdEquiv (r + 1) m + -- Build star algebra equivalence ψ + let ψ : Matrix (Fin (r + 2) × m) (Fin (r + 2) × m) ℂ ≃⋆ₐ[ℝ] + Matrix (m ⊕ (Fin (r + 1) × m)) (m ⊕ (Fin (r + 1) × m)) ℂ := + StarAlgEquiv.ofAlgEquiv (Matrix.reindexAlgEquiv ℝ ℂ e) (fun M => by + ext i j + simp only [star_eq_conjTranspose, Matrix.conjTranspose_apply, + Matrix.reindexAlgEquiv_apply, Matrix.reindex_apply, Matrix.submatrix_apply]) + -- Key: ψ M = M.submatrix e.symm e.symm + have hψ_eq : ∀ M : Matrix (Fin (r + 2) × m) (Fin (r + 2) × m) ℂ, + ψ M = M.submatrix e.symm e.symm := by + intro M; rfl + -- e.symm on Sum.inl and Sum.inr + have he_inl : ∀ a : m, e.symm (Sum.inl a) = (⟨0, by omega⟩, a) := by + intro a; rfl + have he_inr : ∀ (i : Fin (r + 1)) (a : m), e.symm (Sum.inr (i, a)) = (i.succ, a) := by + intro ⟨i, hi⟩ a; rfl + -- ψ(Pω) = fromBlocks ω₀₀ 0 0 Qω + have hψPω : ψ Pω = Matrix.fromBlocks ω₀₀ 0 0 Qω := by + ext (a | ⟨i, a⟩) (b | ⟨j, b⟩) + · simp only [hψ_eq, Matrix.submatrix_apply, he_inl, Matrix.fromBlocks_apply₁₁] + exact hPω_entry 0 0 a b |>.trans (if_pos rfl) + · simp only [hψ_eq, Matrix.submatrix_apply, he_inl, he_inr, Matrix.fromBlocks_apply₁₂] + exact hPω_entry 0 j.succ a b |>.trans (if_neg (Fin.succ_ne_zero j).symm) + · simp only [hψ_eq, Matrix.submatrix_apply, he_inl, he_inr, Matrix.fromBlocks_apply₂₁] + exact hPω_entry i.succ 0 a b |>.trans (if_neg (Fin.succ_ne_zero i)) + · simp only [hψ_eq, Matrix.submatrix_apply, he_inr, + Matrix.fromBlocks_apply₂₂, Qω, embed] + have hψPτ : ψ Pτ = Matrix.fromBlocks τ₀₀ 0 0 Qτ := by + ext (a | ⟨i, a⟩) (b | ⟨j, b⟩) + · simp only [hψ_eq, Matrix.submatrix_apply, he_inl, Matrix.fromBlocks_apply₁₁] + exact hPτ_entry 0 0 a b |>.trans (if_pos rfl) + · simp only [hψ_eq, Matrix.submatrix_apply, he_inl, he_inr, Matrix.fromBlocks_apply₁₂] + exact hPτ_entry 0 j.succ a b |>.trans (if_neg (Fin.succ_ne_zero j).symm) + · simp only [hψ_eq, Matrix.submatrix_apply, he_inl, he_inr, Matrix.fromBlocks_apply₂₁] + exact hPτ_entry i.succ 0 a b |>.trans (if_neg (Fin.succ_ne_zero i)) + · simp only [hψ_eq, Matrix.submatrix_apply, he_inr, + Matrix.fromBlocks_apply₂₂, Qτ, embed] + -- ψ preserves trace + have hψ_trace : ∀ M : Matrix (Fin (r + 2) × m) (Fin (r + 2) × m) ℂ, + (ψ M).trace = M.trace := by + intro M + simp only [hψ_eq, Matrix.trace, Matrix.diag, Matrix.submatrix_apply] + exact Fintype.sum_equiv e.symm _ _ (fun i => rfl) + -- ψ preserves rpow (via CFC) + have hψ_rpow : ∀ (M : Matrix (Fin (r + 2) × m) (Fin (r + 2) × m) ℂ), + M.PosSemidef → ∀ p : ℝ, 0 < p → ψ (M ^ p) = (ψ M) ^ p := by + intro M hM p hp + have hM_sa := hM.1.isSelfAdjoint + have hM_le : (0 : Matrix _ _ ℂ) ≤ M := by rw [Matrix.le_iff, sub_zero]; exact hM + have hψM_sa : IsSelfAdjoint (ψ M) := by + rw [IsSelfAdjoint, ← map_star ψ]; exact congr_arg ψ hM_sa.star_eq + have hψM_le : (0 : Matrix _ _ ℂ) ≤ ψ M := by + rw [Matrix.le_iff, sub_zero] + exact hM.submatrix e.symm + rw [CFC.rpow_eq_cfc_real (ha := hM_le), CFC.rpow_eq_cfc_real (ha := hψM_le)] + rw [StarAlgHomClass.map_cfc (R := ℝ) (S := ℝ) ψ _ M + ((Real.continuous_rpow_const (by linarith)).continuousOn) + (ψ.toAlgEquiv.toLinearMap.continuous_of_finiteDimensional) + hM_sa hψM_sa] + -- Trace splits into block 0 + remaining blocks + have htrace_split : + (Pω ^ s * Pτ ^ (1 - s)).trace.re = + (ω₀₀ ^ s * τ₀₀ ^ (1 - s)).trace.re + (Qω ^ s * Qτ ^ (1 - s)).trace.re := by + conv_lhs => rw [show (Pω ^ s * Pτ ^ (1 - s)).trace = + (ψ (Pω ^ s * Pτ ^ (1 - s))).trace from (hψ_trace _).symm] + rw [map_mul, hψ_rpow Pω hPω_psd s hs0, hψ_rpow Pτ hPτ_psd (1 - s) h1s_pos, + hψPω, hψPτ] + rw [fromBlocks_diag_rpow (hω_block_psd 0) hQω_psd (p := s) hs0, + fromBlocks_diag_rpow (hτ_block_psd 0) hQτ_psd (p := 1 - s) h1s_pos] + simp only [fromBlocks_multiply, Matrix.mul_zero, Matrix.zero_mul, add_zero, zero_add] + rw [trace_fromBlocks] + simp only [Complex.add_re] + rfl + -- Apply IH to Q (r+1 blocks) + haveI : NeZero (r + 1) := ⟨by omega⟩ + have hQ_block_psd_ω : ∀ k : Fin (r + 1), + (Qω.submatrix (Prod.mk k) (Prod.mk k)).PosSemidef := by + intro k; rw [hQ_block_ω k]; exact hω_block_psd k.succ + have hQ_block_psd_τ : ∀ k : Fin (r + 1), + (Qτ.submatrix (Prod.mk k) (Prod.mk k)).PosSemidef := by + intro k; rw [hQ_block_τ k]; exact hτ_block_psd k.succ + have hIH : (Qω ^ s * Qτ ^ (1 - s)).trace.re ≤ + ∑ k : Fin (r + 1), ((Qω.submatrix (Prod.mk k) (Prod.mk k)) ^ s * + (Qτ.submatrix (Prod.mk k) (Prod.mk k)) ^ (1 - s)).trace.re := by + exact ih (r + 1) (by omega) Qω Qτ hQω_psd hQτ_psd hQ_block_psd_ω hQ_block_psd_τ + (fun A' => by simp [Matrix.trace, Matrix.diag, Matrix.submatrix_apply, + ← Finset.sum_product']) + calc (ω ^ s * τ ^ (1 - s)).trace.re + ≤ (Pω ^ s * Pτ ^ (1 - s)).trace.re := hconc + _ = (ω₀₀ ^ s * τ₀₀ ^ (1 - s)).trace.re + + (Qω ^ s * Qτ ^ (1 - s)).trace.re := htrace_split + _ ≤ (ω₀₀ ^ s * τ₀₀ ^ (1 - s)).trace.re + + ∑ k : Fin (r + 1), ((Qω.submatrix (Prod.mk k) (Prod.mk k)) ^ s * + (Qτ.submatrix (Prod.mk k) (Prod.mk k)) ^ (1 - s)).trace.re := by + linarith [hIH] + _ = ∑ i : Fin (r + 2), ((ω.submatrix (Prod.mk i) (Prod.mk i)) ^ s * + (τ.submatrix (Prod.mk i) (Prod.mk i)) ^ (1 - s)).trace.re := by + -- Rewrite as sum over Fin (r+2) = {0} ∪ {1,..,r+1} + conv_rhs => rw [Fin.sum_univ_succ] + dsimp only [ω₀₀, τ₀₀] + simp only [hQ_block_ω, hQ_block_τ] + +/-- **Channel inequality for F_s**: Quantum channels increase Tr (ρˢσ¹⁻ˢ). + +For a quantum channel Φ and density matrices ρ, σ, for s ∈ (0,1]: + Tr (ρˢ σ¹⁻ˢ) ≤ Tr ((Φρ)ˢ (Φσ)¹⁻ˢ) + +**Proof** (Stinespring + pinching + weighted Lieb concavity): +1. F_s(VρV†, VσV†) = F_s(ρ, σ) by `rpow_conj_isometry` +2. **Stage A**: F_s(ω, τ) ≤ F_s(P(ω), P(τ)) — the pinching inequality, + proved via root-of-unity unitary averaging + concavity. +3. **Stage B**: F_s(P(ω), P(τ)) ≤ F_s(Φρ, Φσ) — from weighted + Lieb concavity with w_k = 1/r. -/ +private lemma trace_rpow_mul_channel_le + (Φ : QuantumChannel n m) + (ρ σ : DensityMatrix n) (s : ℝ) (hs0 : 0 < s) (hs1 : s ≤ 1) : + (Tr (ρ ^ s * σ ^ (1 - s))).re ≤ + (Tr ((Φ.val ↑ρ) ^ s * (Φ.val ↑σ) ^ (1 - s))).re := by + change (ρ.toMatrix ^ s * σ.toMatrix ^ (1 - s)).trace.re ≤ + ((Φ.val ρ.toMatrix) ^ s * (Φ.val σ.toMatrix) ^ (1 - s)).trace.re + -- s = 1: both sides equal Tr (ρ) by trace-preservation + by_cases hs1_eq : s = 1 + · subst hs1_eq + simp only [sub_self] + have hρ_psd := ρ.posSemidef + have hσ_psd := σ.posSemidef + have hΦρ_psd := (Φ ρ).posSemidef + have hΦσ_psd := (Φ σ).posSemidef + rw [CFC.rpow_zero σ.toMatrix (by rw [Matrix.le_iff, sub_zero]; exact hσ_psd), + CFC.rpow_zero (Φ.val σ.toMatrix) (by rw [Matrix.le_iff, sub_zero]; exact hΦσ_psd), + CFC.rpow_one ρ.toMatrix (by rw [Matrix.le_iff, sub_zero]; exact hρ_psd), + CFC.rpow_one (Φ.val ρ.toMatrix) (by rw [Matrix.le_iff, sub_zero]; exact hΦρ_psd), + Matrix.mul_one, Matrix.mul_one] + have hTP := Φ.property.tracePreserving ρ.toMatrix + rw [hTP] + · -- s < 1: Stinespring isometry + pinching + super-additivity + have hs1_lt : s < 1 := lt_of_le_of_ne hs1 hs1_eq + have h1s_pos : 0 < 1 - s := by linarith + obtain ⟨r, K, hKraus⟩ := Φ.property.completelyPositive + have hKsum := Φ.kraus_sum_eq_one hKraus + have hK_ρ_psd : ∀ i, (K i * ρ.toMatrix * (K i)ᴴ).PosSemidef := + fun i => ρ.posSemidef.mul_mul_conjTranspose_same (K i) + have hK_σ_psd : ∀ i, (K i * σ.toMatrix * (K i)ᴴ).PosSemidef := + fun i => σ.posSemidef.mul_mul_conjTranspose_same (K i) + have hΦρ_psd := (Φ ρ).posSemidef + have hΦσ_psd := (Φ σ).posSemidef + -- Stage B: super-additivity Σᵢ F_s(KᵢρKᵢ†, KᵢσKᵢ†) ≤ F_s(Φρ, Φσ) + have hsum_le := lieb_concavity_sum + (fun i => K i * ρ.toMatrix * (K i)ᴴ) + (fun i => K i * σ.toMatrix * (K i)ᴴ) + hK_ρ_psd hK_σ_psd s hs0.le hs1 + have hΦρ_eq : ∑ i : Fin r, K i * ρ.toMatrix * (K i)ᴴ = Φ.val ρ.toMatrix := (hKraus ρ.toMatrix).symm + have hΦσ_eq : ∑ i : Fin r, K i * σ.toMatrix * (K i)ᴴ = Φ.val σ.toMatrix := (hKraus σ.toMatrix).symm + rw [hΦρ_eq, hΦσ_eq] at hsum_le + -- Stage A: pinching inequality F_s(ρ, σ) ≤ Σᵢ F_s(KᵢρKᵢ†, KᵢσKᵢ†) + rcases r with _ | r + · -- r = 0: contradicts trace-preservation + simp only [Finset.univ_eq_empty, Finset.sum_empty] at hKsum + haveI : Nonempty n := by + by_contra h + haveI := not_nonempty_iff.mp h + have := ρ.trace_eq_one + rw [Matrix.trace_eq_zero_of_isEmpty] at this + exact zero_ne_one this + exfalso + have h01 := congrFun (congrFun hKsum (Classical.arbitrary n)) (Classical.arbitrary n) + simp only [Matrix.zero_apply, Matrix.one_apply_eq] at h01 + exact zero_ne_one h01 + · -- r ≥ 1: Stinespring dilation + pinching + haveI : NeZero (r + 1) := ⟨Nat.succ_ne_zero r⟩ + set V := stinespringIsometry K with hV_def + have hVV : Vᴴ * V = 1 := stinespringIsometry_conjTranspose_mul hKsum + set ω := V * ρ.toMatrix * Vᴴ with hω_def + set τ := V * σ.toMatrix * Vᴴ with hτ_def + have hω_psd : ω.PosSemidef := ρ.posSemidef.mul_mul_conjTranspose_same V + have hτ_psd : τ.PosSemidef := σ.posSemidef.mul_mul_conjTranspose_same V + -- Isometry invariance: F_s(ρ, σ) = F_s(VρV†, VσV†) + have h_iso := trace_rpow_mul_isometry_conj_eq V hVV ρ.toMatrix σ.toMatrix + ρ.posSemidef σ.posSemidef s hs0 hs1_lt + rw [← h_iso] + -- Chain: F_s(ω, τ) ≤ Σᵢ F_s(KᵢρKᵢ†, KᵢσKᵢ†) ≤ F_s(Φρ, Φσ) + calc (ω ^ s * τ ^ (1 - s)).trace.re + ≤ ∑ i : Fin (r + 1), ((K i * ρ.toMatrix * (K i)ᴴ) ^ s * + (K i * σ.toMatrix * (K i)ᴴ) ^ (1 - s)).trace.re := by + -- Diagonal blocks of ω = VρV† + have hω_block : ∀ i j : Fin (r + 1), ∀ a b : m, + ω (i, a) (j, b) = (K i * ρ.toMatrix * (K j)ᴴ) a b := by + intro i j a b + simp only [hω_def, Matrix.mul_apply, Matrix.conjTranspose_apply] + simp only [hV_def, stinespringIsometry, Matrix.of_apply] + have hτ_block : ∀ i j : Fin (r + 1), ∀ a b : m, + τ (i, a) (j, b) = (K i * σ.toMatrix * (K j)ᴴ) a b := by + intro i j a b + simp only [hτ_def, Matrix.mul_apply, Matrix.conjTranspose_apply] + simp only [hV_def, stinespringIsometry, Matrix.of_apply] + have hω_diag_block : ∀ i : Fin (r + 1), + ω.submatrix (Prod.mk i) (Prod.mk i) = K i * ρ.toMatrix * (K i)ᴴ := by + intro i; ext a b + simp only [Matrix.submatrix_apply, hω_block i i] + have hτ_diag_block : ∀ i : Fin (r + 1), + τ.submatrix (Prod.mk i) (Prod.mk i) = K i * σ.toMatrix * (K i)ᴴ := by + intro i; ext a b + simp only [Matrix.submatrix_apply, hτ_block i i] + -- Pinching inequality: F_s(ω, τ) ≤ Σᵢ F_s(ωᵢᵢ, τᵢᵢ) + have hpinching := pinching_inequality_Fs ω τ hω_psd hτ_psd s hs0 hs1_lt + simp_rw [hω_diag_block, hτ_diag_block] at hpinching + exact hpinching + _ ≤ ((Φ.val ρ.toMatrix) ^ s * (Φ.val σ.toMatrix) ^ (1 - s)).trace.re := hsum_le + +/-- **Monotonicity of Relative Entropy**: Quantum channels do not increase relative entropy. + +For a quantum channel Φ and positive definite density matrices ρ, σ: + S(Φ(ρ) || Φ(σ)) ≤ S(ρ || σ) + +**Proof**: Uses derivative argument on g(s) = F_s(Φρ, Φσ) - F_s(ρ, σ) where +F_s(A, B) = Tr (Aˢ B¹⁻ˢ). Since g(s) ≥ 0 on (0,1] and g(1) = 0, we get g'(1) ≤ 0, +which is D(Φρ‖Φσ) ≤ D(ρ‖σ). -/ +theorem relativeEntropy_channel_le + (Φ : QuantumChannel n m) + (ρ σ : DensityMatrix n) : + D(Φ ρ ∥ Φ σ) ≤ + D(ρ ∥ σ) := by + -- Case split: if D(ρ‖σ) = ⊤, trivially true + by_cases hsupp : suppSubset ρ.toMatrix σ.toMatrix + · -- Finite case: supp(ρ) ⊆ supp(σ) + -- suppSubset is preserved by channels + have hsupp_ch := suppSubset_channel' Φ σ.posSemidef hsupp + set ρ' : DensityMatrix m := Φ ρ + set σ' : DensityMatrix m := Φ σ + -- Both relative entropies are finite + have hDch : relativeEntropy ρ' σ' = + ↑(ρ'.toMatrix * (log ρ' - log σ')).trace.re := by + unfold relativeEntropy + split_ifs with h + · rfl + · exact absurd hsupp_ch h + have hD : relativeEntropy ρ σ = + ↑(ρ.toMatrix * (log ρ - log σ)).trace.re := by + unfold relativeEntropy + simp only [if_pos hsupp]; rfl + rw [hDch, hD, EReal.coe_le_coe_iff] + -- Use derivative argument: define g(s) = F_s(Φρ, Φσ) - F_s(ρ, σ) + let g : ℝ → ℝ := fun s => + (ρ'.toMatrix ^ s * σ'.toMatrix ^ (1 - s)).trace.re - + (ρ.toMatrix ^ s * σ.toMatrix ^ (1 - s)).trace.re + -- (a) g(s) ≥ 0 for s ∈ (0,1] by trace_rpow_mul_channel_le + have g_nonneg : ∀ s ∈ Set.Ioc (0 : ℝ) 1, 0 ≤ g s := by + intro s hs + exact sub_nonneg.mpr (trace_rpow_mul_channel_le Φ ρ σ s hs.1 hs.2) + -- (b) g(1) = 0: at s=1, Tr (ρ¹ σ⁰) = Tr (ρ) = 1 and Tr ((Φρ)¹(Φσ)⁰) = Tr (Φρ) = 1 + have hg_one : g 1 = 0 := by + simp only [g] + rw [show (1 : ℝ) - 1 = 0 from by ring] + rw [CFC.rpow_one _ (by rw [Matrix.le_iff, sub_zero]; exact ρ'.posSemidef), + CFC.rpow_one _ (by rw [Matrix.le_iff, sub_zero]; exact ρ.posSemidef), + CFC.rpow_zero _ (by rw [Matrix.le_iff, sub_zero]; exact σ'.posSemidef), + CFC.rpow_zero _ (by rw [Matrix.le_iff, sub_zero]; exact σ.posSemidef)] + simp only [Matrix.mul_one] + rw [ρ'.trace_eq_one, ρ.trace_eq_one] + simp [Complex.one_re, sub_self] + -- (c) HasDerivAt of g at s=1 + have hderiv_ch : HasDerivAt (fun s => (ρ'.toMatrix ^ s * σ'.toMatrix ^ (1 - s)).trace.re) + ((ρ'.toMatrix * (log ρ' - log σ')).trace.re) (1 : ℝ) := + hasDerivAt_trace_rpow_mul ρ' σ' hsupp_ch + have hderiv_orig : HasDerivAt (fun s => (ρ.toMatrix ^ s * σ.toMatrix ^ (1 - s)).trace.re) + ((ρ.toMatrix * (log ρ - log σ)).trace.re) (1 : ℝ) := + hasDerivAt_trace_rpow_mul ρ σ hsupp + have hderiv_g : HasDerivAt g + ((ρ'.toMatrix * (log ρ' - log σ')).trace.re - + (ρ.toMatrix * (log ρ - log σ)).trace.re) (1 : ℝ) := + hderiv_ch.sub hderiv_orig + -- (d) g'(1) ≤ 0 since g has minimum at s=1 from the left + have hmin : ∀ y ∈ Set.Ioo (1 - (1:ℝ)/2) (1:ℝ), g (1:ℝ) ≤ g y := by + intro y hy; rw [hg_one]; exact g_nonneg y ⟨by linarith [hy.1], le_of_lt hy.2⟩ + have hderiv_nonpos : + (ρ'.toMatrix * (log ρ' - log σ')).trace.re - + (ρ.toMatrix * (log ρ - log σ)).trace.re ≤ 0 := + deriv_nonpos_of_forall_lt_min g _ (1:ℝ) (1/2) (by norm_num) hderiv_g hmin + -- (e) D(Φρ‖Φσ) - D(ρ‖σ) ≤ 0 + linarith + · -- Infinite case: supp(ρ) ⊄ supp(σ) → D(ρ‖σ) = ⊤ + have hD_top : relativeEntropy ρ σ = ⊤ := by + simp [relativeEntropy, hsupp] + rw [hD_top] + exact le_top + +/-! ### Characterization of Equality -/ + +/-- **Sufficiency of recovery for equality in DPI.** + +If a quantum channel R recovers both ρ and σ from Φ, i.e., + R(Φ(ρ)) = ρ and R(Φ(σ)) = σ, +then equality holds in the data-processing inequality: + S(Φ(ρ) ‖ Φ(σ)) = S(ρ ‖ σ). + +**Proof.** Applying DPI to Φ gives S(Φ(ρ)‖Φ(σ)) ≤ S(ρ‖σ). +For the reverse, applying DPI to R and using the recovery conditions gives +S(ρ‖σ) = S(R(Φ(ρ))‖R(Φ(σ))) ≤ S(Φ(ρ)‖Φ(σ)). +-/ +theorem relativeEntropy_channel_eq_iff_recoverable + (Φ : QuantumChannel n m) + (ρ σ : DensityMatrix n) + (R : QuantumChannel m n) (hRρ : R (Φ ρ) = ρ) (hRσ : R (Φ σ) = σ) : + D(Φ ρ ∥ Φ σ) = + D(ρ ∥ σ) := by + apply le_antisymm + · -- S(Φ(ρ) || Φ(σ)) ≤ S(ρ || σ) by DPI for Φ + exact relativeEntropy_channel_le Φ ρ σ + · -- S(ρ || σ) ≤ S(Φ(ρ) || Φ(σ)) by applying DPI to R and using recovery + -- DPI for R: S(R(Φρ) || R(Φσ)) ≤ S(Φρ || Φσ) + have hle := relativeEntropy_channel_le R (Φ ρ) (Φ σ) + rw [hRρ, hRσ] at hle + exact hle + +/-! ### Joint Convexity of Relative Entropy -/ + +omit [DecidableEq n] in +/-- For a positive semidefinite matrix B, if Re[v† B v] = 0 then B v = 0. -/ +private lemma mulVec_eq_zero_of_re_inner_zero + {B : Matrix n n ℂ} (hB : B.PosSemidef) + (v : n → ℂ) (hv : (star v ⬝ᵥ B.mulVec v).re = 0) : + B.mulVec v = 0 := by + rw [← hB.dotProduct_mulVec_zero_iff] + apply Complex.ext + · exact hv + · exact hB.1.im_star_dotProduct_mulVec_self v + +omit [DecidableEq n] in +/-- The support subset condition is preserved under convex combinations of positive semidefinite pairs. +If supp(Aᵢ) ⊆ supp(Bᵢ) for i=1,2 and p, 1−p ≥ 0, then +supp(p A₁ + (1−p) A₂) ⊆ supp(p B₁ + (1−p) B₂). -/ +private lemma suppSubset_mix + {A₁ A₂ B₁ B₂ : Matrix n n ℂ} + (hB₁ : B₁.PosSemidef) (hB₂ : B₂.PosSemidef) + (hsup₁ : suppSubset A₁ B₁) (hsup₂ : suppSubset A₂ B₂) + (p : ℝ) (hp : 0 ≤ p) (hp1 : 0 ≤ 1 - p) : + suppSubset (p • A₁ + (1 - p) • A₂) (p • B₁ + (1 - p) • B₂) := by + intro v hv + simp only [Matrix.add_mulVec, Matrix.smul_mulVec] at hv + have h₁ : 0 ≤ (star v ⬝ᵥ B₁.mulVec v).re := hB₁.re_dotProduct_nonneg v + have h₂ : 0 ≤ (star v ⬝ᵥ B₂.mulVec v).re := hB₂.re_dotProduct_nonneg v + have hinner_sum : p * (star v ⬝ᵥ B₁.mulVec v).re + (1 - p) * (star v ⬝ᵥ B₂.mulVec v).re = 0 := by + have h : p * (star v ⬝ᵥ B₁.mulVec v).re + (1 - p) * (star v ⬝ᵥ B₂.mulVec v).re = + (star v ⬝ᵥ (p • B₁.mulVec v + (1 - p) • B₂.mulVec v)).re := by + simp [dotProduct_add, dotProduct_smul] + rw [h, hv]; simp + have hpB₁ : p * (star v ⬝ᵥ B₁.mulVec v).re = 0 := by + nlinarith [mul_nonneg hp h₁, mul_nonneg hp1 h₂] + have h1pB₂ : (1 - p) * (star v ⬝ᵥ B₂.mulVec v).re = 0 := by + nlinarith [mul_nonneg hp h₁, mul_nonneg hp1 h₂] + have hpA₁ : p • A₁.mulVec v = 0 := by + rcases mul_eq_zero.mp hpB₁ with hp0 | h₁0 + · simp [hp0] + · simp [hsup₁ v (mulVec_eq_zero_of_re_inner_zero hB₁ v h₁0)] + have h1pA₂ : (1 - p) • A₂.mulVec v = 0 := by + rcases mul_eq_zero.mp h1pB₂ with hp10 | h₂0 + · simp [hp10] + · simp [hsup₂ v (mulVec_eq_zero_of_re_inner_zero hB₂ v h₂0)] + simp [Matrix.add_mulVec, Matrix.smul_mulVec, hpA₁, h1pA₂] + +omit [DecidableEq n] [DecidableEq m] in +/-- Support subset is preserved by a single Kraus conjugation K ρ K†. +If supp(ρ) ⊆ supp(σ) then +supp(K ρ K†) ⊆ supp(K σ K†). -/ +private lemma suppSubset_kraus_single + (K : Matrix m n ℂ) {ρ σ : Matrix n n ℂ} + (hσ : σ.PosSemidef) (h : suppSubset ρ σ) : + suppSubset (K * ρ * Kᴴ) (K * σ * Kᴴ) := by + intro v hv + -- From (K σ Kᴴ) v = 0 and PSD, deduce σ (Kᴴ v) = 0 + have hKHv_ker : σ.mulVec (Kᴴ.mulVec v) = 0 := by + apply mulVec_eq_zero_of_re_inner_zero hσ + -- re⟨Kᴴv, σ(Kᴴv)⟩ = re⟨v, (KσKᴴ)v⟩ = 0 + -- Key: ⟨Kᴴv, w⟩ = ⟨v, Kw⟩ (adjoint identity) + have h_eq : (star (Kᴴ.mulVec v) ⬝ᵥ σ.mulVec (Kᴴ.mulVec v)).re = + (star v ⬝ᵥ (K * σ * Kᴴ).mulVec v).re := by + congr 1 + conv_rhs => rw [show (K * σ * Kᴴ).mulVec v = K.mulVec (σ.mulVec (Kᴴ.mulVec v)) from by + simp only [← Matrix.mulVec_mulVec]] + -- star (Kᴴ v) ⬝ᵥ w = star v ⬝ᵥ K w (adjoint identity) + rw [star_mulVec, Matrix.conjTranspose_conjTranspose, ← dotProduct_mulVec] + rw [h_eq, hv]; simp + -- suppSubset gives ρ (Kᴴ v) = 0 + have hρKHv_zero : ρ.mulVec (Kᴴ.mulVec v) = 0 := h _ hKHv_ker + -- Therefore (K ρ Kᴴ) v = K (ρ (Kᴴ v)) = 0 + simp only [show (K * ρ * Kᴴ).mulVec v = K.mulVec (ρ.mulVec (Kᴴ.mulVec v)) from by + simp only [← Matrix.mulVec_mulVec], hρKHv_zero, Matrix.mulVec_zero] + +omit [DecidableEq n] [DecidableEq m] in +/-- Support subset is preserved under finite sums of Kraus-conjugated pairs. +If supp(A_k) ⊆ supp(B_k) for all k and each B_k is +positive semidefinite, then supp(∑_k A_k) ⊆ supp(∑_k B_k). -/ +private lemma suppSubset_sum {r : ℕ} {A B : Fin r → Matrix m m ℂ} + (hB : ∀ k, (B k).PosSemidef) + (h : ∀ k, suppSubset (A k) (B k)) : + suppSubset (∑ k, A k) (∑ k, B k) := by + intro v hv + simp only [Matrix.sum_mulVec] at hv ⊢ + -- Each ⟨v, B_k v⟩.re ≥ 0 (PSD) and their sum = 0 + have hB_nonneg : ∀ k, 0 ≤ (star v ⬝ᵥ (B k).mulVec v).re := + fun k => (hB k).re_dotProduct_nonneg v + have hsum_zero : ∑ k : Fin r, (star v ⬝ᵥ (B k).mulVec v).re = 0 := by + have heq : (star v ⬝ᵥ ∑ k : Fin r, (B k).mulVec v).re = 0 := by rw [hv]; simp + rw [dotProduct_sum] at heq + simpa [Complex.re_sum] using heq + -- Each term is 0 (nonneg terms sum to 0) + have hB_each : ∀ k, (star v ⬝ᵥ (B k).mulVec v).re = 0 := fun k => + le_antisymm (by + calc (star v ⬝ᵥ (B k).mulVec v).re + ≤ ∑ i : Fin r, (star v ⬝ᵥ (B i).mulVec v).re := + Finset.single_le_sum (fun i _ => hB_nonneg i) (Finset.mem_univ k) + _ = 0 := hsum_zero) (hB_nonneg k) + -- Each (B k) v = 0 via PosSemidef.dotProduct_mulVec_zero_iff + have hBv_zero : ∀ k, (B k).mulVec v = 0 := fun k => by + rw [← (hB k).dotProduct_mulVec_zero_iff] + apply Complex.ext + · exact hB_each k + · exact (hB k).1.im_star_dotProduct_mulVec_self v + -- Each (A k) v = 0 + have hAv_zero : ∀ k, (A k).mulVec v = 0 := fun k => h k v (hBv_zero k) + simp only [hAv_zero, Finset.sum_const_zero] + +omit [DecidableEq n] [DecidableEq m] in +/-- Support subset is preserved by quantum channels. +If supp(ρ) ⊆ supp(σ) then +supp(Φ(ρ)) ⊆ supp(Φ(σ)). -/ +private lemma suppSubset_channel + (Φ : QuantumChannel n m) + {ρ σ : Matrix n n ℂ} (hσ : σ.PosSemidef) (h : suppSubset ρ σ) : + suppSubset (Φ.val ρ) (Φ.val σ) := by + obtain ⟨r, K, hK⟩ := Φ.property.completelyPositive + rw [hK, hK] + exact suppSubset_sum + (fun k => hσ.mul_mul_conjTranspose_same (K k)) + (fun k => suppSubset_kraus_single (K k) hσ h) + +/-- Joint concavity of Tr (ρˢ σ¹⁻ˢ) for positive semidefinite matrices. + p ⋅ Tr (ρ₁ˢ σ₁¹⁻ˢ) + (1−p) ⋅ Tr (ρ₂ˢ σ₂¹⁻ˢ) + ≤ Tr ((pρ₁ + (1−p)ρ₂)ˢ (pσ₁ + (1−p)σ₂)¹⁻ˢ) -/ +private lemma trace_rpow_mul_jointly_concave + (ρ₁ ρ₂ σ₁ σ₂ : DensityMatrix n) (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) (s : ℝ) + (hs0 : 0 ≤ s) (hs1 : s ≤ 1) : + p * (Tr (ρ₁ ^ s * σ₁ ^ (1 - s))).re + + (1 - p) * (Tr (ρ₂ ^ s * σ₂ ^ (1 - s))).re ≤ + (Tr ((p • ρ₁.toMatrix + (1 - p) • ρ₂.toMatrix) ^ s * (p • σ₁.toMatrix + (1 - p) • σ₂.toMatrix) ^ (1 - s))).re := by + have hpsd₁ := ρ₁.posSemidef + have hpsd₂ := ρ₂.posSemidef + have hpsdσ₁ := σ₁.posSemidef + have hpsdσ₂ := σ₂.posSemidef + -- This is lieb_joint_concavity_semidef with K = 1 + have key := lieb_joint_concavity_semidef ρ₁.toMatrix ρ₂.toMatrix σ₁.toMatrix σ₂.toMatrix + hpsd₁ hpsd₂ hpsdσ₁ hpsdσ₂ + (1 : Matrix n n ℂ) s hs0 hs1 p (1 - p) hp (by linarith) (by ring) + simp only [liebJointFunction, conjTranspose_one, Matrix.mul_one] at key + exact key + + +theorem relativeEntropy_jointly_convex + (ρ₁ ρ₂ σ₁ σ₂ : DensityMatrix n) + (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) : + D(DensityMatrix.mix ρ₁ ρ₂ p hp hp1 ∥ DensityMatrix.mix σ₁ σ₂ p hp hp1) ≤ + p * D(ρ₁ ∥ σ₁) + (1 - p) * D(ρ₂ ∥ σ₂) := by + set ρ_mix := DensityMatrix.mix ρ₁ ρ₂ p hp hp1 + set σ_mix := DensityMatrix.mix σ₁ σ₂ p hp hp1 + -- Handle boundary cases p = 0 and p = 1 + rcases hp.eq_or_lt' with rfl | hp0 + · -- p = 0: mixture = ρ₂, σ₂; RHS = 0 + D₂ = D₂ + have hρ_eq : ρ_mix = ρ₂ := DensityMatrix.ext (by simp [ρ_mix, DensityMatrix.mix]) + have hσ_eq : σ_mix = σ₂ := DensityMatrix.ext (by simp [σ_mix, DensityMatrix.mix]) + rw [hρ_eq, hσ_eq]; simp [relativeEntropy] + rcases hp1.lt_or_eq with hp1' | rfl + · -- 0 < p < 1: handle the three sub-cases + -- Case split on whether suppSubsets hold + by_cases h₂ : suppSubset ρ₂.toMatrix σ₂.toMatrix + · by_cases h₁ : suppSubset ρ₁.toMatrix σ₁.toMatrix + · -- Both finite: use derivative argument + -- Step 1: suppSubset for the mixture + have hsup_mix : suppSubset (p • ρ₁.toMatrix + (1 - p) • ρ₂.toMatrix) + (p • σ₁.toMatrix + (1 - p) • σ₂.toMatrix) := + suppSubset_mix σ₁.posSemidef σ₂.posSemidef + h₁ h₂ p hp (by linarith) + -- Step 2: Abbreviate the real-valued relative entropies + set r₁ := (ρ₁.toMatrix * (log ρ₁ - log σ₁)).trace.re + set r₂ := (ρ₂.toMatrix * (log ρ₂ - log σ₂)).trace.re + -- Simplify relativeEntropy using the definitions + have hD₁_eq : relativeEntropy ρ₁ σ₁ = ↑r₁ := by + simp only [relativeEntropy, h₁, ↓reduceIte]; rfl + have hD₂_eq : relativeEntropy ρ₂ σ₂ = ↑r₂ := by + simp only [relativeEntropy, h₂, ↓reduceIte]; rfl + have hsup_mix' : suppSubset ρ_mix.toMatrix σ_mix.toMatrix := hsup_mix + have hD_mix_eq : relativeEntropy ρ_mix σ_mix = + ↑(ρ_mix.toMatrix * + (log ρ_mix - log σ_mix)).trace.re := by + simp only [relativeEntropy, hsup_mix', ↓reduceIte]; rfl + rw [hD_mix_eq, hD₁_eq, hD₂_eq] + -- Step 3: Convert to real comparison + rw [show (↑p : EReal) * ↑r₁ + (1 - ↑p) * ↑r₂ = ↑(p * r₁ + (1 - p) * r₂) from by + push_cast; ring_nf] + rw [EReal.coe_le_coe_iff] + -- Step 4: Derivative argument + -- Define h(s) = Re[Tr (ρ_mix^s σ_mix^{1-s})] - p Re[Tr (ρ₁^s σ₁^{1-s})] - (1-p) Re[Tr (ρ₂^s σ₂^{1-s})] + let g : ℝ → ℝ := fun s => + (ρ_mix.toMatrix ^ s * σ_mix.toMatrix ^ (1 - s)).trace.re - + (p * (ρ₁.toMatrix ^ s * σ₁.toMatrix ^ (1 - s)).trace.re + + (1 - p) * (ρ₂.toMatrix ^ s * σ₂.toMatrix ^ (1 - s)).trace.re) + -- (a) g(s) ≥ 0 for s ∈ (0,1] by joint concavity (Lieb) + have g_nonneg : ∀ s ∈ Set.Ioc (0 : ℝ) 1, 0 ≤ g s := by + intro s hs + simp only [g, ρ_mix, σ_mix, DensityMatrix.mix_toMatrix] + have h := trace_rpow_mul_jointly_concave ρ₁ ρ₂ σ₁ σ₂ p hp hp1 s (le_of_lt hs.1) hs.2 + change p * (ρ₁.toMatrix ^ s * σ₁.toMatrix ^ (1 - s)).trace.re + + (1 - p) * (ρ₂.toMatrix ^ s * σ₂.toMatrix ^ (1 - s)).trace.re ≤ _ at h + linarith + -- (b) g(1) = 0 + have hg_one : g 1 = 0 := by + simp only [g] + rw [show (1 : ℝ) - 1 = 0 from by ring] + rw [CFC.rpow_one _ (by rw [Matrix.le_iff, sub_zero]; exact ρ_mix.posSemidef), + CFC.rpow_one _ (by rw [Matrix.le_iff, sub_zero]; exact ρ₁.posSemidef), + CFC.rpow_one _ (by rw [Matrix.le_iff, sub_zero]; exact ρ₂.posSemidef), + CFC.rpow_zero _ (by rw [Matrix.le_iff, sub_zero]; exact σ_mix.posSemidef), + CFC.rpow_zero _ (by rw [Matrix.le_iff, sub_zero]; exact σ₁.posSemidef), + CFC.rpow_zero _ (by rw [Matrix.le_iff, sub_zero]; exact σ₂.posSemidef)] + simp only [Matrix.mul_one] + rw [ρ_mix.trace_eq_one, ρ₁.trace_eq_one, ρ₂.trace_eq_one] + simp only [Complex.one_re]; linarith + -- (c) HasDerivAt of g at s=1 + have hderiv_mix : HasDerivAt (fun s : ℝ => (ρ_mix.toMatrix ^ s * σ_mix.toMatrix ^ (1 - s)).trace.re) + ((ρ_mix.toMatrix * (log ρ_mix - log σ_mix)).trace.re) 1 := + hasDerivAt_trace_rpow_mul ρ_mix σ_mix hsup_mix + have hderiv₁ : HasDerivAt (fun s : ℝ => (ρ₁.toMatrix ^ s * σ₁.toMatrix ^ (1 - s)).trace.re) + ((ρ₁.toMatrix * (log ρ₁ - log σ₁)).trace.re) 1 := + hasDerivAt_trace_rpow_mul ρ₁ σ₁ h₁ + have hderiv₂ : HasDerivAt (fun s : ℝ => (ρ₂.toMatrix ^ s * σ₂.toMatrix ^ (1 - s)).trace.re) + ((ρ₂.toMatrix * (log ρ₂ - log σ₂)).trace.re) 1 := + hasDerivAt_trace_rpow_mul ρ₂ σ₂ h₂ + have hderiv_g : HasDerivAt g + ((ρ_mix.toMatrix * (log ρ_mix - log σ_mix)).trace.re - + (p * (ρ₁.toMatrix * (log ρ₁ - log σ₁)).trace.re + + (1 - p) * (ρ₂.toMatrix * (log ρ₂ - log σ₂)).trace.re)) 1 := by + exact hderiv_mix.sub (hderiv₁.const_mul p |>.add (hderiv₂.const_mul (1 - p))) + -- (d) g'(1) ≤ 0 since g has a minimum at s=1 from the left + have hmin : ∀ y ∈ Set.Ioo (1 - (1:ℝ)/2) 1, g 1 ≤ g y := by + intro y hy; rw [hg_one]; exact g_nonneg y ⟨by linarith [hy.1], le_of_lt hy.2⟩ + have hderiv_nonpos : + (ρ_mix.toMatrix * (log ρ_mix - log σ_mix)).trace.re - + (p * (ρ₁.toMatrix * (log ρ₁ - log σ₁)).trace.re + + (1 - p) * (ρ₂.toMatrix * (log ρ₂ - log σ₂)).trace.re) ≤ 0 := + deriv_nonpos_of_forall_lt_min g _ 1 (1/2) (by norm_num) hderiv_g hmin + linarith + · -- D₁ = ⊤: RHS = p * ⊤ + ... = ⊤, LHS ≤ ⊤ + have hD₁ : relativeEntropy ρ₁ σ₁ = ⊤ := by + simp [relativeEntropy, h₁] + rw [hD₁, EReal.mul_top_of_pos (by exact_mod_cast hp0), + EReal.top_add_of_ne_bot (ne_bot_of_gt (lt_of_lt_of_le EReal.bot_lt_zero + (EReal.mul_nonneg (by norm_cast; linarith) (relativeEntropy_nonneg ρ₂ σ₂))))] + exact le_top + · -- D₂ = ⊤: RHS = ... + (1-p) * ⊤ = ⊤ + have hD₂ : relativeEntropy ρ₂ σ₂ = ⊤ := by + simp [relativeEntropy, h₂] + rw [hD₂, EReal.mul_top_of_pos (by exact_mod_cast (by linarith : 0 < 1 - p)), + EReal.add_top_of_ne_bot (ne_bot_of_gt (lt_of_lt_of_le EReal.bot_lt_zero + (EReal.mul_nonneg (by norm_cast) (relativeEntropy_nonneg ρ₁ σ₁))))] + exact le_top + · -- p = 1: mixture = ρ₁, σ₁; RHS = D₁ + 0 = D₁ + have hρ_eq : ρ_mix = ρ₁ := DensityMatrix.ext (by simp [ρ_mix, DensityMatrix.mix]) + have hσ_eq : σ_mix = σ₁ := DensityMatrix.ext (by simp [σ_mix, DensityMatrix.mix]) + rw [hρ_eq, hσ_eq] + have h1sub1 : (1 : EReal) - 1 = 0 := + EReal.sub_self (EReal.coe_ne_top 1) (EReal.coe_ne_bot 1) + simp only [EReal.coe_one, one_mul, h1sub1, EReal.zero_mul, add_zero] + exact le_refl _ + +/-! ### Isomorphism invariance + +For a `*-`algebra equivalence `φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ` that preserves trace, +quantum relative entropy is invariant: `D(ρ.map φ ‖ σ.map φ) = D(ρ ‖ σ)`. In quantum +information literature this is the **isometric invariance of relative entropy**, a +special case of Lindblad–Uhlmann monotonicity restricted to invertible CPTP maps. The +reindex specialisation uses `Matrix.reindexStarAlgEquiv e`, which preserves trace +automatically (`Matrix.trace_reindexStarAlgEquiv`). -/ + +variable {m : Type*} [Fintype m] [DecidableEq m] + +/-- **Quantum relative entropy is invariant under trace-preserving `*-`algebra +equivalence** (PosDef case). -/ +theorem relativeEntropy_map_starAlgEquiv_posDef + (ρ σ : DensityMatrix m) (hρ : ρ.toMatrix.PosDef) (hσ : σ.toMatrix.PosDef) + (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) + (hφ : ∀ A, (φ A).trace = A.trace) : + D(ρ.map φ hφ ∥ σ.map φ hφ) = D(ρ ∥ σ) := by + have hρ_map : (ρ.map φ hφ).toMatrix.PosDef := hρ.map_starAlgEquiv φ + have hσ_map : (σ.map φ hφ).toMatrix.PosDef := hσ.map_starAlgEquiv φ + unfold relativeEntropy + have h_supp_map : suppSubset (ρ.map φ hφ).toMatrix (σ.map φ hφ).toMatrix := by + intro v hv + have hinj : Function.Injective (σ.map φ hφ).toMatrix.mulVec := + Matrix.mulVec_injective_iff_isUnit.mpr hσ_map.isUnit + have h0 : (σ.map φ hφ).toMatrix.mulVec 0 = 0 := by simp + have hv_zero : v = 0 := hinj (hv.trans h0.symm) + rw [hv_zero]; simp + have h_supp : suppSubset ρ.toMatrix σ.toMatrix := by + intro v hv + have hinj : Function.Injective σ.toMatrix.mulVec := + Matrix.mulVec_injective_iff_isUnit.mpr hσ.isUnit + have h0 : σ.toMatrix.mulVec 0 = 0 := by simp + have hv_zero : v = 0 := hinj (hv.trans h0.symm) + rw [hv_zero]; simp + simp only [h_supp_map, h_supp, if_true] + congr 1 + change (Tr ((ρ.map φ hφ).toMatrix * + (matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian - + matrixLog (σ.map φ hφ).toMatrix (σ.map φ hφ).isHermitian))).re = + (Tr (ρ.toMatrix * + (matrixLog ρ.toMatrix ρ.isHermitian - + matrixLog σ.toMatrix σ.isHermitian))).re + have h_log_ρ : matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian = + φ (matrixLog ρ.toMatrix ρ.isHermitian) := by + change matrixLog (φ ρ.toMatrix) _ = _ + exact matrixLog_map_starAlgEquiv hρ φ + have h_log_σ : matrixLog (σ.map φ hφ).toMatrix (σ.map φ hφ).isHermitian = + φ (matrixLog σ.toMatrix σ.isHermitian) := by + change matrixLog (φ σ.toMatrix) _ = _ + exact matrixLog_map_starAlgEquiv hσ φ + rw [h_log_ρ, h_log_σ, DensityMatrix.map_toMatrix, ← map_sub, ← map_mul, hφ] + +/-- Specialisation of `relativeEntropy_map_starAlgEquiv_posDef` to reindexing. -/ +theorem relativeEntropy_mapEquiv_posDef + (ρ σ : DensityMatrix m) (hρ : ρ.toMatrix.PosDef) (hσ : σ.toMatrix.PosDef) (e : n ≃ m) : + D(ρ.mapEquiv e ∥ σ.mapEquiv e) = D(ρ ∥ σ) := + relativeEntropy_map_starAlgEquiv_posDef ρ σ hρ hσ _ _ + +end Matrix diff --git a/QuantumSystem/Analysis/Entropy/SSA.lean b/QuantumSystem/Analysis/Entropy/SSA.lean new file mode 100644 index 0000000..d03306f --- /dev/null +++ b/QuantumSystem/Analysis/Entropy/SSA.lean @@ -0,0 +1,605 @@ +module + +public import QuantumSystem.Analysis.Entropy.KroneckerProduct +public import QuantumSystem.Analysis.Entropy.RelativeEntropy +public import QuantumSystem.Analysis.Entropy.Regularize +public import QuantumSystem.Analysis.Matrix.PartialTrace + +/-! +# Strong subadditivity of the von Neumann entropy (LocalNet form) + +For a quantum system on a `LocalNet L`, the public theorem in this file is stated +in a **common-region-explicit** form. Given regions + +- `ΛAB ⊆ ΛABC`, +- `ΛBC ⊆ ΛABC`, +- `ΛB ⊆ ΛABC`, +- `ΛA ⊆ ΛAB`, +- `ΛAB \ ΛA = ΛB`, +- `ΛABC \ ΛA = ΛBC`, + +the **strong subadditivity** inequality states: + + `S(ρ ↾ ΛAB) + S(ρ ↾ ΛBC) ≥ S(ρ) + S(ρ ↾ ΛB)` + +This avoids encoding the theorem through positional subsystem names such as +`A/B/C` or a distinguished three-site tuple. The familiar three-site statement + + `S(ρ ↾ {a, b}) + S(ρ ↾ {b, c}) ≥ S(ρ) + S(ρ ↾ {b})` + +is recovered by instantiating + +- `ΛABC = {a, b, c}`, +- `ΛAB = {a, b}`, +- `ΛBC = {b, c}`, +- `ΛB = {b}`, +- `ΛA = {a}`. + +This file first establishes the **mutual-information identity** in product-type form, + + `D(ρ_AB ‖ ρ_A ⊗ ρ_B) = -S(ρ_AB) + S(ρ_A) + S(ρ_B)`, + +for a PosDef bipartite density matrix `ρ_AB : DensityMatrix (n × m)` whose canonical +partial traces coincide with PosDef factor states `ρ_A` and `ρ_B`. It then uses this +identity together with the data-processing inequality to prove SSA in the +AQFT-natural form: + +1. The bipartite **mutual-information identity** (`relativeEntropy_kronecker_marginals_product`) + applied twice — once for the `(ΛA vs ΛABC \ ΛA)` bipartition of the full system, + once for the `(ΛA vs ΛAB \ ΛA)` bipartition of the `ΛAB`-marginal. +2. The **data-processing inequality** for relative entropy + (`Matrix.relativeEntropy_channel_le`) applied to the LocalNet + `Matrix.QuantumChannel.restrict` channel for the inclusion `ΛAB ⊆ ΛABC`. + +## Main results + +* `Matrix.relativeEntropy_kronecker_marginals_product` — product-type mutual-information + identity. +* `DensityMatrix.vonNeumannEntropy_SSA` — SSA for arbitrary states on a + `LocalNet`, with the common region and split equalities explicit. + +## References + +* Nielsen, Chuang, *Quantum Computation and Quantum Information* §11.3 — quantum mutual + information `I(A:B) = S(ρ_A) + S(ρ_B) − S(ρ_AB)` and the relative-entropy identity + `D(ρ_AB ‖ ρ_A ⊗ ρ_B) = I(A:B)`. +-/ + +@[expose] public section + +namespace Matrix + +open scoped Kronecker MatrixOrder ComplexOrder QuantumInfo + +variable {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + +/-! ### Product-type relative-entropy identity -/ + +/-- **Mutual-information identity (product-type form)**: for a bipartite density +matrix `ρ_AB : DensityMatrix (n × m)` whose canonical partial traces coincide with PosDef +factor states `ρ_A` and `ρ_B`, the relative entropy w.r.t. the product `ρ_A ⊗ ρ_B` +equals `-S(ρ_AB) + S(ρ_A) + S(ρ_B)`. -/ +theorem relativeEntropy_kronecker_marginals_product + (ρ_AB : DensityMatrix (n × m)) + (ρ_A : DensityMatrix n) (hρ_A : ρ_A.toMatrix.PosDef) + (ρ_B : DensityMatrix m) (hρ_B : ρ_B.toMatrix.PosDef) + (h_A_partialTrace : tr₂(ρ_AB.toMatrix) = ρ_A.toMatrix) + (h_B_partialTrace : tr₁(ρ_AB.toMatrix) = ρ_B.toMatrix) : + D(ρ_AB ∥ ρ_A ⊗ ρ_B) = -S(ρ_AB) + S(ρ_A) + S(ρ_B) := by + classical + have hρ_A_kron_pos : (ρ_A ⊗ ρ_B).toMatrix.PosDef := by + rw [DensityMatrix.kronecker_toMatrix]; exact hρ_A.kronecker hρ_B + -- supp(ρ) ⊆ supp(ρ_A ⊗ ρ_B) holds for PosDef σ. + have h_supp : suppSubset ρ_AB.toMatrix (ρ_A ⊗ ρ_B).toMatrix := by + intro v hv + have hinj : Function.Injective (ρ_A ⊗ ρ_B).toMatrix.mulVec := + Matrix.mulVec_injective_iff_isUnit.mpr hρ_A_kron_pos.isUnit + have h0 : (ρ_A ⊗ ρ_B).toMatrix.mulVec 0 = 0 := by simp + have hv_zero : v = 0 := hinj (hv.trans h0.symm) + rw [hv_zero]; simp + unfold relativeEntropy + simp only [h_supp, if_true] + -- log of ρ_A ⊗ ρ_B decomposes via matrixLog_kronecker_posDef. + -- The two `IsHermitian` proofs differ proof-wise but match by Prop irrelevance. + have h_log_kron : matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian = + matrixLog ρ_A.toMatrix hρ_A.1 ⊗ₖ (1 : Matrix m m ℂ) + + (1 : Matrix n n ℂ) ⊗ₖ matrixLog ρ_B.toMatrix hρ_B.1 := + matrixLog_kronecker_posDef hρ_A hρ_B + -- The trace identity after substitution. + have h_trace_log_kron : + Tr (ρ_AB.toMatrix * matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian) = + Tr (ρ_A.toMatrix * matrixLog ρ_A.toMatrix hρ_A.1) + + Tr (ρ_B.toMatrix * matrixLog ρ_B.toMatrix hρ_B.1) := by + rw [h_log_kron, Matrix.mul_add, Matrix.trace_add, trace_mul_kronecker_one_right, + trace_mul_kronecker_one_left, h_A_partialTrace, h_B_partialTrace] + -- Split (log ρ - log(ρ_A⊗ρ_B)) and reduce trace. + have h_split : Tr (ρ_AB.toMatrix * (matrixLog ρ_AB.toMatrix ρ_AB.isHermitian - + matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian)) = + Tr (ρ_AB.toMatrix * matrixLog ρ_AB.toMatrix ρ_AB.isHermitian) - + Tr (ρ_AB.toMatrix * matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian) := by + rw [Matrix.mul_sub, Matrix.trace_sub] + -- Translate to the goal in EReal. + change (↑(Tr (ρ_AB.toMatrix * (matrixLog ρ_AB.toMatrix ρ_AB.isHermitian - + matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian))).re : EReal) = + -S(ρ_AB) + S(ρ_A) + S(ρ_B) + rw [h_split, Complex.sub_re, h_trace_log_kron, Complex.add_re] + -- Now: ↑((Tr(ρ · log ρ)).re - ((Tr(ρ_A · log ρ_A)).re + (Tr(ρ_B · log ρ_B)).re)) + -- = -S(ρ) + S(ρ_A) + S(ρ_B) + -- Express the LHS Real value: + set α : ℝ := (Tr (ρ_AB.toMatrix * matrixLog ρ_AB.toMatrix ρ_AB.isHermitian)).re with hα + set β : ℝ := (Tr (ρ_A.toMatrix * matrixLog ρ_A.toMatrix hρ_A.1)).re with hβ + set γ : ℝ := (Tr (ρ_B.toMatrix * matrixLog ρ_B.toMatrix hρ_B.1)).re with hγ + -- And the S values: + change (↑(α - (β + γ)) : EReal) = -S(ρ_AB) + S(ρ_A) + S(ρ_B) + have hSρ : S(ρ_AB) = -α := by + change -(Tr (ρ_AB.toMatrix * DensityMatrix.log ρ_AB)).re = -α + rfl + have hSρ_A : S(ρ_A) = -β := by + change -(Tr (ρ_A.toMatrix * DensityMatrix.log ρ_A)).re = -β + rfl + have hSρ_B : S(ρ_B) = -γ := by + change -(Tr (ρ_B.toMatrix * DensityMatrix.log ρ_B)).re = -γ + rfl + rw [hSρ, hSρ_A, hSρ_B] + -- Goal in EReal: ↑(α - (β + γ)) = -↑(-α) + ↑(-β) + ↑(-γ) + -- Equivalent Real identity: + have h_real : α - (β + γ) = -(-α) + (-β) + (-γ) := by ring + exact_mod_cast h_real + +end Matrix + +/-! ### Strong subadditivity (LocalNet form, PosDef case) + +The main theorem. We use the bipartite mutual-information identity twice +(for `(A vs BC)` and for `(A vs B)` within `ρ ↾ {A, B}`) and the data-processing +inequality on the LocalNet `restrict` channel for `{A, B} ⊆ univ`. -/ + +namespace DensityMatrix + +open scoped Kronecker MatrixOrder ComplexOrder +open scoped Matrix.QuantumInfo +open scoped LocalNet.QuantumInfo + +variable {L : LocalNet} + +/-! #### Split-explicit nested-region SSA + +The next theorem is the region-level core of the three-site wrapper below. It is +parameterised by nested inclusions `ΛA ⊆ ΛAB ⊆ ΛABC`; the common/middle region is the +explicit complement `ΛAB \ ΛA`, and the other side is `ΛABC \ ΛA`. +-/ + +/-- **Strong subadditivity (PosDef case, split-explicit nested-region form).** + +Given nested regions `ΛA ⊆ ΛAB ⊆ ΛABC`, write the middle/common region as +`ΛAB \ ΛA` and the complementary side as `ΛABC \ ΛA`. For a PosDef state `ρ_ABC` on +`ΛABC`, assuming the relevant marginals are PosDef, strong subadditivity is + +`S(ρ_ABC ↾ ΛAB) + S(ρ_ABC ↾ (ΛABC \ ΛA)) ≥ S(ρ_ABC) + S(ρ_ABC ↾ (ΛAB \ ΛA))`. + +This statement contains the split subset in the hypotheses and does not rely on +site names such as `a b c` or product-factor names such as `A/B`. -/ +private lemma vonNeumannEntropy_SSA_posDef_nested + {L : LocalNet} {ΛA ΛAB ΛABC : Finset L.sites} + (h_AB : ΛAB ⊆ ΛABC) (h_A : ΛA ⊆ ΛAB) + (ρ_ABC : L.densityMatrix ΛABC) + (h_ABC_pos : ρ_ABC.toMatrix.PosDef) + (h_A_pos : (ρ_ABC ↾[h_A.trans h_AB]).toMatrix.PosDef) + (h_AB_pos : (ρ_ABC ↾[h_AB]).toMatrix.PosDef) + (h_BC_pos : (ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)]).toMatrix.PosDef) + (h_B_pos : (ρ_ABC ↾[(Finset.sdiff_subset_sdiff h_AB (le_refl ΛA)).trans Finset.sdiff_subset]).toMatrix.PosDef) : + S(ρ_ABC ↾ ΛAB) + + S(ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)]) ≥ + S(ρ_ABC) + + S(ρ_ABC ↾[(Finset.sdiff_subset_sdiff h_AB (le_refl ΛA)).trans Finset.sdiff_subset]) := by + classical + set h_B_in_BC : (ΛAB \ ΛA) ⊆ (ΛABC \ ΛA) := + Finset.sdiff_subset_sdiff h_AB (le_refl ΛA) with hh_B_in_BC + set ρ_A : DensityMatrix (L.regionIdx ΛA) := + ρ_ABC ↾[h_A.trans h_AB] with hh_ρ_A + set ρ_AB : DensityMatrix (L.regionIdx ΛAB) := + ρ_ABC ↾[h_AB] with hh_ρ_AB + set ρ_BC : DensityMatrix (L.regionIdx (ΛABC \ ΛA)) := + ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)] with hh_ρ_BC + set ρ_B : DensityMatrix (L.regionIdx (ΛAB \ ΛA)) := + ρ_ABC ↾[h_B_in_BC.trans Finset.sdiff_subset] with hh_ρ_B + have hρ_A : ρ_A.toMatrix.PosDef := h_A_pos + have hρ_AB : ρ_AB.toMatrix.PosDef := h_AB_pos + have hρ_BC : ρ_BC.toMatrix.PosDef := h_BC_pos + have hρ_B : ρ_B.toMatrix.PosDef := h_B_pos + -- Bipartite views of `ρ` and `ρ_AB` via the explicit split subset. + set ρ_pt : DensityMatrix (L.regionIdx ΛA × L.regionIdx (ΛABC \ ΛA)) := + ρ_ABC.mapEquiv (L.combineIdx (h_A.trans h_AB)) with hρ_pt_def + have hρ_pt : ρ_pt.toMatrix.PosDef := h_ABC_pos.mapEquiv _ + set ρ_AB_pt : DensityMatrix (L.regionIdx ΛA × L.regionIdx (ΛAB \ ΛA)) := + ρ_AB.mapEquiv (L.combineIdx h_A) with hρ_AB_pt_def + have hρ_AB_pt : ρ_AB_pt.toMatrix.PosDef := hρ_AB.mapEquiv _ + have hρ_pt_sub : ρ_pt.toMatrix = + ρ_ABC.toMatrix.submatrix (L.combineIdx (h_A.trans h_AB)) + (L.combineIdx (h_A.trans h_AB)) := by + simp [hρ_pt_def, DensityMatrix.mapEquiv_toMatrix] + have hρ_AB_pt_sub : ρ_AB_pt.toMatrix = + ρ_AB.toMatrix.submatrix (L.combineIdx h_A) (L.combineIdx h_A) := by + simp [hρ_AB_pt_def, DensityMatrix.mapEquiv_toMatrix] + -- Product reference state for the full split and its lift back to `ΛABC`. + set σ_pt : DensityMatrix (L.regionIdx ΛA × L.regionIdx (ΛABC \ ΛA)) := + ρ_A ⊗ ρ_BC with hσ_pt_def + have hσ_pt : σ_pt.toMatrix.PosDef := by + rw [hσ_pt_def, DensityMatrix.kronecker_toMatrix] + exact hρ_A.kronecker hρ_BC + set σ_full : L.densityMatrix ΛABC := + σ_pt.mapEquiv (L.combineIdx (h_A.trans h_AB)).symm with hσ_full_def + have hσ_full : σ_full.toMatrix.PosDef := hσ_pt.mapEquiv _ + -- Mutual-information identity for the full split. + have h_ptA_ρ_pt : tr₂(ρ_pt.toMatrix) = ρ_A.toMatrix := by + ext x x' + rw [hρ_pt_sub, ← Matrix.restrict_eq_partialTrace_combineIdx] + rfl + have h_ptBC_ρ_pt : tr₁(ρ_pt.toMatrix) = ρ_BC.toMatrix := by + ext y y' + rw [hρ_pt_sub, ← Matrix.restrict_compl_eq_partialTrace_combineIdx + (h_A.trans h_AB)] + rfl + have h_mut_full : + D(ρ_pt ∥ σ_pt) = -S(ρ_pt) + S(ρ_A) + S(ρ_BC) := + Matrix.relativeEntropy_kronecker_marginals_product ρ_pt ρ_A hρ_A ρ_BC hρ_BC + h_ptA_ρ_pt h_ptBC_ρ_pt + -- Mutual-information identity for the `ΛA ⊆ ΛAB` split. + have h_ptA_AB : tr₂(ρ_AB_pt.toMatrix) = ρ_A.toMatrix := by + ext x x' + rw [hρ_AB_pt_sub, ← Matrix.restrict_eq_partialTrace_combineIdx] + change ((ρ_ABC ↾[h_AB]) ↾[h_A]).toMatrix x x' = + (ρ_ABC ↾[h_A.trans h_AB]).toMatrix x x' + rw [DensityMatrix.restrict_restrict] + have h_ptB_AB : tr₁(ρ_AB_pt.toMatrix) = ρ_B.toMatrix := by + ext y y' + rw [hρ_AB_pt_sub, ← Matrix.restrict_compl_eq_partialTrace_combineIdx h_A] + change ((ρ_ABC ↾[h_AB]) ↾[(Finset.sdiff_subset : + ΛAB \ ΛA ⊆ ΛAB)]).toMatrix y y' = + (ρ_ABC ↾[h_B_in_BC.trans Finset.sdiff_subset]).toMatrix y y' + rw [DensityMatrix.restrict_restrict] + have h_mut_AB : + D(ρ_AB_pt ∥ ρ_A ⊗ ρ_B) = -S(ρ_AB_pt) + S(ρ_A) + S(ρ_B) := + Matrix.relativeEntropy_kronecker_marginals_product ρ_AB_pt ρ_A hρ_A ρ_B hρ_B + h_ptA_AB h_ptB_AB + -- DPI for restriction from `ΛABC` to `ΛAB`. + set Φ : Matrix.QuantumChannel (L.regionIdx ΛABC) (L.regionIdx ΛAB) := + Matrix.QuantumChannel.restrict h_AB with hΦ_def + have h_Φρ_mat : ((Φ : Matrix.QuantumChannel _ _) ρ_ABC).toMatrix = ρ_AB.toMatrix := by + change (ρ_ABC ↾[h_AB]).toMatrix = ρ_AB.toMatrix + rfl + have h_Φρ_pos : ((Φ : Matrix.QuantumChannel _ _) ρ_ABC).toMatrix.PosDef := h_Φρ_mat ▸ hρ_AB + -- Restricting the lifted product state gives the product of the restricted factors. + have h_Φσ_mat : + ((Φ : Matrix.QuantumChannel _ _) σ_full).toMatrix = + (ρ_A ⊗ ρ_B).toMatrix.submatrix + (L.combineIdx h_A).symm (L.combineIdx h_A).symm := by + change Matrix.restrict h_AB σ_full.toMatrix = _ + ext s s' + rw [show s = (L.combineIdx h_A) + ((L.combineIdx h_A).symm s) from + ((L.combineIdx h_A).apply_symm_apply s).symm, + show s' = (L.combineIdx h_A) + ((L.combineIdx h_A).symm s') from + ((L.combineIdx h_A).apply_symm_apply s').symm] + set p := (L.combineIdx h_A).symm s with hp + set p' := (L.combineIdx h_A).symm s' with hp' + rw [Matrix.restrict_apply, Matrix.submatrix_apply] + simp only [Equiv.symm_apply_apply] + simp_rw [Matrix.combineIdx_assoc_eq h_AB h_A] + have h_σ_full_apply : ∀ (x x' : L.regionIdx ΛA) + (y y' : L.regionIdx (ΛABC \ ΛA)), + σ_full.toMatrix + (L.combineIdx (h_A.trans h_AB) (x, y)) + (L.combineIdx (h_A.trans h_AB) (x', y')) = + σ_pt.toMatrix (x, y) (x', y') := by + intro x x' y y' + simp [hσ_full_def, DensityMatrix.mapEquiv_toMatrix, + Matrix.submatrix_apply, Equiv.symm_apply_apply] + simp_rw [h_σ_full_apply] + rw [hσ_pt_def, DensityMatrix.kronecker_toMatrix] + simp only [Matrix.kronecker_apply] + rw [← Finset.mul_sum] + rw [DensityMatrix.kronecker_toMatrix, Matrix.kronecker_apply] + congr 1 + rw [show ρ_B.toMatrix p.2 p'.2 = Matrix.restrict h_B_in_BC ρ_BC.toMatrix p.2 p'.2 + from by + change Matrix.restrict (h_B_in_BC.trans Finset.sdiff_subset) ρ_ABC.toMatrix p.2 p'.2 = + Matrix.restrict h_B_in_BC (Matrix.restrict Finset.sdiff_subset ρ_ABC.toMatrix) p.2 p'.2 + rw [Matrix.restrict_restrict], + Matrix.restrict_eq_partialTrace_combineIdx h_B_in_BC, + Matrix.partialTrace_refl_apply] + have h_compl_eq : (ΛABC \ ΛA) \ (ΛAB \ ΛA) = ΛABC \ ΛAB := by + ext x + simp only [Finset.mem_sdiff] + constructor + · rintro ⟨⟨hxABC, hxA⟩, hx_not_BminusA⟩ + exact ⟨hxABC, fun hxAB => hx_not_BminusA ⟨hxAB, hxA⟩⟩ + · rintro ⟨hxABC, hxAB⟩ + exact ⟨⟨hxABC, fun hxA => hxAB (h_A hxA)⟩, + fun hxBminusA => hxAB hxBminusA.1⟩ + rw [← (L.regionIdxCongr h_compl_eq.symm).sum_comp + (fun b => ρ_BC.toMatrix.submatrix (L.combineIdx h_B_in_BC) (L.combineIdx h_B_in_BC) + (p.2, b) (p'.2, b))] + refine Finset.sum_congr rfl fun γ _ => ?_ + have hR_eq : ∀ (z : L.regionIdx (ΛAB \ ΛA)), + Matrix.restrictAssocEquiv h_AB h_A (z, γ) = + L.combineIdx h_B_in_BC (z, L.regionIdxCongr h_compl_eq.symm γ) := by + intro z + funext ⟨v, hv⟩ + by_cases hv_in_AB : v ∈ ΛAB + · have hv_in_combine : v ∈ ΛAB \ ΛA := + Finset.mem_sdiff.mpr ⟨hv_in_AB, (Finset.mem_sdiff.mp hv).2⟩ + rw [LocalNet.combineIdx_apply_mem h_B_in_BC _ _ ⟨v, hv⟩ hv_in_combine] + exact dif_pos hv_in_AB + · have hv_not_in_combine : v ∉ ΛAB \ ΛA := fun h_in => + hv_in_AB (Finset.mem_sdiff.mp h_in).1 + have hv_compl : v ∈ ΛABC \ ΛAB := + Finset.mem_sdiff.mpr ⟨(Finset.mem_sdiff.mp hv).1, hv_in_AB⟩ + have hv_compl_compl : v ∈ (ΛABC \ ΛA) \ (ΛAB \ ΛA) := + Finset.mem_sdiff.mpr ⟨hv, hv_not_in_combine⟩ + rw [LocalNet.combineIdx_apply_not_mem h_B_in_BC _ _ ⟨v, hv⟩ hv_not_in_combine, + show L.regionIdxCongr h_compl_eq.symm γ ⟨v, hv_compl_compl⟩ = γ ⟨v, hv_compl⟩ + from LocalNet.regionIdxCongr_apply (L := L) h_compl_eq.symm γ hv_compl hv_compl_compl] + exact dif_neg hv_in_AB + rw [hR_eq p.2, hR_eq p'.2] + rfl + have h_Φσ_pos : ((Φ : Matrix.QuantumChannel _ _) σ_full).toMatrix.PosDef := by + rw [h_Φσ_mat] + have h_kron_pos : (ρ_A ⊗ ρ_B).toMatrix.PosDef := by + rw [DensityMatrix.kronecker_toMatrix] + exact hρ_A.kronecker hρ_B + exact h_kron_pos.mapEquiv _ + have h_dpi : + D((Φ : Matrix.QuantumChannel _ _) ρ_ABC ∥ (Φ : Matrix.QuantumChannel _ _) σ_full) ≤ + D(ρ_ABC ∥ σ_full) := + Matrix.relativeEntropy_channel_le Φ ρ_ABC σ_full + have h_dpi_lhs : + D((Φ : Matrix.QuantumChannel _ _) ρ_ABC ∥ (Φ : Matrix.QuantumChannel _ _) σ_full) = + D(ρ_AB_pt ∥ ρ_A ⊗ ρ_B) := by + have h_Φρ_dm : (Φ : Matrix.QuantumChannel _ _) ρ_ABC = + ρ_AB_pt.mapEquiv (L.combineIdx h_A).symm := by + apply DensityMatrix.ext + rw [h_Φρ_mat, DensityMatrix.mapEquiv_toMatrix, hρ_AB_pt_sub, + Matrix.submatrix_submatrix] + simp + have h_Φσ_dm : (Φ : Matrix.QuantumChannel _ _) σ_full = + (ρ_A ⊗ ρ_B).mapEquiv (L.combineIdx h_A).symm := by + apply DensityMatrix.ext + exact h_Φσ_mat + rw [h_Φρ_dm, h_Φσ_dm] + have h_kron_pos : (ρ_A ⊗ ρ_B).toMatrix.PosDef := by + rw [DensityMatrix.kronecker_toMatrix] + exact hρ_A.kronecker hρ_B + exact Matrix.relativeEntropy_mapEquiv_posDef _ _ hρ_AB_pt h_kron_pos _ + have h_ρ_eq_pt : ρ_ABC = ρ_pt.mapEquiv (L.combineIdx (h_A.trans h_AB)).symm := by + apply DensityMatrix.ext + rw [DensityMatrix.mapEquiv_toMatrix, hρ_pt_sub, Matrix.submatrix_submatrix] + simp + have h_σ_full_eq_pt : + σ_full = σ_pt.mapEquiv (L.combineIdx (h_A.trans h_AB)).symm := rfl + have h_dpi_rhs : D(ρ_ABC ∥ σ_full) = D(ρ_pt ∥ σ_pt) := by + rw [h_ρ_eq_pt, h_σ_full_eq_pt] + exact Matrix.relativeEntropy_mapEquiv_posDef _ _ hρ_pt hσ_pt _ + have h_Sρ_eq : S(ρ_ABC) = S(ρ_pt) := by + rw [h_ρ_eq_pt] + exact Matrix.vonNeumannEntropy_mapEquiv_posDef _ hρ_pt _ + have h_SρAB_eq : S(ρ_AB) = S(ρ_AB_pt) := by + rw [show ρ_AB = ρ_AB_pt.mapEquiv (L.combineIdx h_A).symm from by + apply DensityMatrix.ext + rw [DensityMatrix.mapEquiv_toMatrix, hρ_AB_pt_sub, Matrix.submatrix_submatrix] + simp] + exact Matrix.vonNeumannEntropy_mapEquiv_posDef _ hρ_AB_pt _ + rw [h_dpi_lhs, h_dpi_rhs] at h_dpi + rw [h_mut_AB, h_mut_full] at h_dpi + rw [h_Sρ_eq, h_SρAB_eq] + have h_real : + -S(ρ_AB_pt) + S(ρ_A) + S(ρ_B) ≤ + -S(ρ_pt) + S(ρ_A) + S(ρ_BC) := by + exact_mod_cast h_dpi + linarith + +/-! #### Finset-equality bridges for the user-facing wrapper + +For two `Λ ⊆ Λ_total` and `Λ' ⊆ Λ_total` Subset proofs whose Finsets are propositionally +equal (`Λ = Λ'`), the partial-trace `restrict h ρ` and `restrict h' ρ` are essentially +identical: their entropies and PosDef status agree. Discharged by `subst h_eq` plus +proof-irrelevance for `Subset`. Used in `vonNeumannEntropy_SSA_posDef` to bridge +user-facing `S(restrict h_BC ρ)` (with `{b,c}`) to the bipartite-natural form +`S(restrict sdiff_subset ρ)` (with `{a,b,c} \ {a}`). -/ + +private lemma vonNeumannEntropy_restrict_finset_eq + {L : LocalNet} {Λ Λ' Λ_total : Finset L.sites} (h_eq : Λ = Λ') + (h : Λ ⊆ Λ_total) (h' : Λ' ⊆ Λ_total) (ρ : L.densityMatrix Λ_total) : + S(ρ ↾[h]) = S(ρ ↾[h']) := by + subst h_eq + rfl + +private lemma posDef_restrict_finset_eq + {L : LocalNet} {Λ Λ' Λ_total : Finset L.sites} (h_eq : Λ = Λ') + (h : Λ ⊆ Λ_total) (h' : Λ' ⊆ Λ_total) (ρ : L.densityMatrix Λ_total) : + (ρ ↾[h]).toMatrix.PosDef ↔ (ρ ↾[h']).toMatrix.PosDef := by + subst h_eq + rfl + +/-- **Strong subadditivity (PosDef case, common-region-explicit form).** + +This is the public region-level wrapper around +`vonNeumannEntropy_SSA_posDef_nested`. The theorem does not infer the common region from +the names `a b c` or from a product-factor order. Instead it receives explicit data: + +* `ΛAB ⊆ ΛABC`, the first two-block marginal, +* `ΛA ⊆ ΛAB`, the split used inside `ΛAB`, +* `ΛBC ⊆ ΛABC`, the other two-block marginal, +* `ΛB ⊆ ΛABC`, the common/middle marginal, +* `ΛAB \ ΛA = ΛB`, identifying the common region, +* `ΛABC \ ΛA = ΛBC`, identifying the side obtained by tracing out `ΛA`. + +Under PosDef hypotheses for the displayed marginals, the conclusion is exactly +`S(ρ_ABC ↾ ΛAB) + S(ρ_ABC ↾ ΛBC) ≥ S(ρ_ABC) + S(ρ_ABC ↾ ΛB)`. -/ +private lemma vonNeumannEntropy_SSA_posDef + {L : LocalNet} {ΛA ΛB ΛAB ΛBC ΛABC : Finset L.sites} + (h_AB_total : ΛAB ⊆ ΛABC) (h_A_in_AB : ΛA ⊆ ΛAB) + (h_BC_total : ΛBC ⊆ ΛABC) (h_B_total : ΛB ⊆ ΛABC) + (h_B_eq : ΛAB \ ΛA = ΛB) (h_BC_eq : ΛABC \ ΛA = ΛBC) + (ρ_ABC : L.densityMatrix ΛABC) + (h_ABC_pos : ρ_ABC.toMatrix.PosDef) + (h_A_pos : (ρ_ABC ↾[h_A_in_AB.trans h_AB_total]).toMatrix.PosDef) + (h_AB_pos : (ρ_ABC ↾[h_AB_total]).toMatrix.PosDef) + (h_BC_pos : (ρ_ABC ↾[h_BC_total]).toMatrix.PosDef) + (h_B_pos : (ρ_ABC ↾[h_B_total]).toMatrix.PosDef) : + S(ρ_ABC ↾[h_AB_total]) + S(ρ_ABC ↾[h_BC_total]) ≥ + S(ρ_ABC) + S(ρ_ABC ↾[h_B_total]) := by + classical + let h_B_nested : (ΛAB \ ΛA) ⊆ ΛABC := + (Finset.sdiff_subset_sdiff h_AB_total (le_refl ΛA)).trans Finset.sdiff_subset + have h_BC_pos_nested : + (ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)]).toMatrix.PosDef := + posDef_restrict_finset_eq h_BC_eq.symm h_BC_total Finset.sdiff_subset ρ_ABC |>.mp h_BC_pos + have h_B_pos_nested : (ρ_ABC ↾[h_B_nested]).toMatrix.PosDef := + posDef_restrict_finset_eq h_B_eq.symm h_B_total h_B_nested ρ_ABC |>.mp h_B_pos + have h_nested := vonNeumannEntropy_SSA_posDef_nested h_AB_total h_A_in_AB ρ_ABC h_ABC_pos + h_A_pos h_AB_pos h_BC_pos_nested h_B_pos_nested + have h_S_BC : S(ρ_ABC ↾[h_BC_total]) = + S(ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)]) := + vonNeumannEntropy_restrict_finset_eq h_BC_eq.symm + h_BC_total Finset.sdiff_subset ρ_ABC + have h_S_B : S(ρ_ABC ↾[h_B_total]) = S(ρ_ABC ↾[h_B_nested]) := + vonNeumannEntropy_restrict_finset_eq h_B_eq.symm h_B_total h_B_nested ρ_ABC + rwa [← h_S_BC, ← h_S_B] at h_nested + + +/-- **Strong subadditivity (common-region-explicit form, no PosDef hypothesis).** + +PosDef-free version of `vonNeumannEntropy_SSA_posDef`. Given the geometric data of a +common-region split + +* `ΛAB ⊆ ΛABC`, `ΛA ⊆ ΛAB`, `ΛBC ⊆ ΛABC`, `ΛB ⊆ ΛABC`, +* `ΛAB \ ΛA = ΛB`, `ΛABC \ ΛA = ΛBC`, + +and `[Nonempty (L.regionIdx ΛABC)]` (which propagates to every sub-region by +`regionIdx_nonempty_of_subset`), strong subadditivity holds for every density matrix +`ρ_ABC : L.densityMatrix ΛABC`: + + `S(ρ_ABC ↾ ΛAB) + S(ρ_ABC ↾ ΛBC) ≥ S(ρ_ABC) + S(ρ_ABC ↾ ΛB)`. + +The proof regularises `ρ` to the PosDef state `(1 - ε) ρ + ε · π_ΛABC` for `ε ∈ (0, 1]`, +applies `vonNeumannEntropy_SSA_posDef`, and passes to the limit `ε → 0⁺` +via the eigenvalue continuity formulas in `Regularize.lean`. -/ +theorem vonNeumannEntropy_SSA + {L : LocalNet} {ΛA ΛB ΛAB ΛBC ΛABC : Finset L.sites} + (h_AB : ΛAB ⊆ ΛABC) (h_A : ΛA ⊆ ΛAB) + (h_BC : ΛBC ⊆ ΛABC) (h_B : ΛB ⊆ ΛABC) + (h_B_eq : ΛAB \ ΛA = ΛB) (h_BC_eq : ΛABC \ ΛA = ΛBC) + [Nonempty (L.regionIdx ΛABC)] + (ρ_ABC : L.densityMatrix ΛABC) : + S(ρ_ABC ↾[h_AB]) + S(ρ_ABC ↾[h_BC]) ≥ + S(ρ_ABC) + S(ρ_ABC ↾[h_B]) := by + -- Sub-region Nonempty instances, derived from `Nonempty (regionIdx ΛABC)`. + haveI : Nonempty (L.regionIdx ΛAB) := L.regionIdx_nonempty_of_subset h_AB + haveI : Nonempty (L.regionIdx ΛBC) := L.regionIdx_nonempty_of_subset h_BC + haveI : Nonempty (L.regionIdx ΛB) := L.regionIdx_nonempty_of_subset h_B + haveI : Nonempty (L.regionIdx ΛA) := + L.regionIdx_nonempty_of_subset (h_A.trans h_AB) + -- Marginals and eigenvalue-formula functions. + set ρ_AB := ρ_ABC ↾[h_AB] with hρ_AB + set ρ_BC := ρ_ABC ↾[h_BC] with hρ_BC + set ρ_B := ρ_ABC ↾[h_B] with hρ_B + let f_AB : ℝ → ℝ := fun ε => + ∑ i, Real.negMulLog ((1 - ε) * ρ_AB.isHermitian.eigenvalues i + + ε / Fintype.card (L.regionIdx ΛAB)) + let f_BC : ℝ → ℝ := fun ε => + ∑ i, Real.negMulLog ((1 - ε) * ρ_BC.isHermitian.eigenvalues i + + ε / Fintype.card (L.regionIdx ΛBC)) + let f_full : ℝ → ℝ := fun ε => + ∑ i, Real.negMulLog ((1 - ε) * ρ_ABC.isHermitian.eigenvalues i + + ε / Fintype.card (L.regionIdx ΛABC)) + let f_B : ℝ → ℝ := fun ε => + ∑ i, Real.negMulLog ((1 - ε) * ρ_B.isHermitian.eigenvalues i + + ε / Fintype.card (L.regionIdx ΛB)) + -- For ε ∈ (0, 1]: regularised state and its 4 marginals are PosDef, so the PosDef + -- common-region SSA applies. + have h_ineq_pos : ∀ ε : ℝ, 0 < ε → ε ≤ 1 → + f_full ε + f_B ε ≤ f_AB ε + f_BC ε := by + intro ε hε_pos hε_le + have hρ_reg_pos : + (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix.PosDef := + DensityMatrix.regularize_posDef ρ_ABC hε_pos hε_le + have h_AB_eq_reg := + regularize_restrict_toMatrix h_AB ρ_ABC hε_pos.le hε_le + have h_BC_eq_reg := + regularize_restrict_toMatrix h_BC ρ_ABC hε_pos.le hε_le + have h_A_eq_reg := + regularize_restrict_toMatrix (h_A.trans h_AB) ρ_ABC hε_pos.le hε_le + have h_B_eq_reg := + regularize_restrict_toMatrix h_B ρ_ABC hε_pos.le hε_le + have hAB_reg_pos : + ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) ↾[h_AB]).toMatrix.PosDef + := by + change (Matrix.restrict h_AB + (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix).PosDef + rw [h_AB_eq_reg]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le + have hBC_reg_pos : + ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) ↾[h_BC]).toMatrix.PosDef + := by + change (Matrix.restrict h_BC + (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix).PosDef + rw [h_BC_eq_reg]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le + have hA_reg_pos : + ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) + ↾[h_A.trans h_AB]).toMatrix.PosDef := by + change (Matrix.restrict (h_A.trans h_AB) + (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix).PosDef + rw [h_A_eq_reg]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le + have hB_reg_pos : + ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) ↾[h_B]).toMatrix.PosDef + := by + change (Matrix.restrict h_B + (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix).PosDef + rw [h_B_eq_reg]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le + have h_ssa := vonNeumannEntropy_SSA_posDef + h_AB h_A h_BC h_B h_B_eq h_BC_eq + (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) + hρ_reg_pos hA_reg_pos hAB_reg_pos hBC_reg_pos hB_reg_pos + have h_AB_dm := regularize_restrict h_AB ρ_ABC hε_pos.le hε_le + have h_BC_dm := regularize_restrict h_BC ρ_ABC hε_pos.le hε_le + have h_B_dm := regularize_restrict h_B ρ_ABC hε_pos.le hε_le + rw [h_AB_dm, h_BC_dm, h_B_dm] at h_ssa + have ef_AB := + Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_AB hε_pos.le hε_le + have ef_BC := + Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_BC hε_pos.le hε_le + have ef_full := + Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_ABC hε_pos.le hε_le + have ef_B := + Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_B hε_pos.le hε_le + rw [ef_AB, ef_BC, ef_full, ef_B] at h_ssa + exact h_ssa + have h_cont_AB : Filter.Tendsto f_AB (nhds 0) (nhds S(ρ_AB)) := + Matrix.tendsto_negMulLog_regularize_sum_zero ρ_AB + have h_cont_BC : Filter.Tendsto f_BC (nhds 0) (nhds S(ρ_BC)) := + Matrix.tendsto_negMulLog_regularize_sum_zero ρ_BC + have h_cont_full : Filter.Tendsto f_full (nhds 0) (nhds S(ρ_ABC)) := + Matrix.tendsto_negMulLog_regularize_sum_zero ρ_ABC + have h_cont_B : Filter.Tendsto f_B (nhds 0) (nhds S(ρ_B)) := + Matrix.tendsto_negMulLog_regularize_sum_zero ρ_B + have h_within : ∀ᶠ ε in nhdsWithin (0 : ℝ) (Set.Ioi 0), + f_full ε + f_B ε ≤ f_AB ε + f_BC ε := by + rw [eventually_nhdsWithin_iff] + have h_le_one : ∀ᶠ ε in nhds (0 : ℝ), ε ≤ 1 := + Filter.eventually_of_mem (IsOpen.mem_nhds isOpen_Iio (by norm_num : (0 : ℝ) < 1)) <| by + intros ε hε + exact le_of_lt hε + filter_upwards [h_le_one] with ε hε_le_one hε_pos + exact h_ineq_pos ε hε_pos hε_le_one + have h_LHS_lim : + Filter.Tendsto (fun ε => f_AB ε + f_BC ε) (nhdsWithin (0 : ℝ) (Set.Ioi 0)) + (nhds (S(ρ_AB) + S(ρ_BC))) := + (h_cont_AB.add h_cont_BC).mono_left nhdsWithin_le_nhds + have h_RHS_lim : + Filter.Tendsto (fun ε => f_full ε + f_B ε) (nhdsWithin (0 : ℝ) (Set.Ioi 0)) + (nhds (S(ρ_ABC) + S(ρ_B))) := + (h_cont_full.add h_cont_B).mono_left nhdsWithin_le_nhds + exact le_of_tendsto_of_tendsto h_RHS_lim h_LHS_lim h_within + +end DensityMatrix diff --git a/QuantumSystem/Analysis/Entropy/VonNeumannEntropy.lean b/QuantumSystem/Analysis/Entropy/VonNeumannEntropy.lean new file mode 100644 index 0000000..552666a --- /dev/null +++ b/QuantumSystem/Analysis/Entropy/VonNeumannEntropy.lean @@ -0,0 +1,500 @@ +module + +public import QuantumSystem.Analysis.Matrix.Order +public import QuantumSystem.ForMathlib.Analysis.Calculus.Deriv.Sign +public import QuantumSystem.ForMathlib.InformationTheory.KullbackLeibler.KLFun +public import QuantumSystem.State + +/-! +# Von Neumann Entropy + +This file contains definitions and core properties of von Neumann entropy. + +## Main Results + +* `vonNeumannEntropy_concave`: Von Neumann entropy is concave. +-/ + +@[expose] public section + +namespace Matrix + +open scoped MatrixOrder ComplexOrder QuantumInfo + +variable {n : Type*} [Fintype n] [DecidableEq n] + +/-- The entropy function η(x) = -x log x, extended by continuity to η(0) = 0. +This is concave on [0, ∞). -/ +noncomputable def entropyFun (x : ℝ) : ℝ := + if x ≤ 0 then 0 else -x * Real.log x + +theorem entropyFun_nonneg {x : ℝ} (hx : 0 ≤ x) (hx1 : x ≤ 1) : 0 ≤ entropyFun x := by + unfold entropyFun + split_ifs with hle + · exact le_refl 0 + · push_neg at hle + have hlog : Real.log x ≤ 0 := Real.log_nonpos hx hx1 + nlinarith [hle, hlog] + +/-- Von Neumann entropy of a density matrix: S(ρ) = −Tr (ρ log ρ). +Since ρ log ρ is Hermitian (see `DensityMatrix.mul_log_isHermitian`), +its trace is real, so `.re` is lossless (see `vonNeumannEntropy_ofReal`). -/ +noncomputable def vonNeumannEntropy (ρ : DensityMatrix n) : ℝ := + -(Tr (ρ * log ρ)).re + +namespace QuantumInfo +scoped notation "S(" ρ ")" => Matrix.vonNeumannEntropy ρ +end QuantumInfo + +/-- Casting `S(ρ)` back to ℂ recovers −Tr(ρ log ρ) exactly, confirming the trace is real. -/ +@[simp] +theorem vonNeumannEntropy_ofReal (ρ : DensityMatrix n) : + (S(ρ) : ℂ) = -(Tr (ρ * log ρ)) := by + unfold vonNeumannEntropy + rw [Complex.ofReal_neg] + congr 1 + exact ρ.mul_log_isHermitian.trace_ofReal_re + +/-- Von Neumann entropy equals the eigenvalue sum S(ρ) = ∑ᵢ (−λᵢ log λᵢ). -/ +theorem vonNeumannEntropy_eq_sum (ρ : DensityMatrix n) : + S(ρ) = ∑ i, entropyFun (ρ.isHermitian.eigenvalues i) := by + unfold vonNeumannEntropy DensityMatrix.log matrixLog + change -(Tr (ρ.toMatrix * matrixFunction _ ρ.toMatrix ρ.isHermitian)).re = _ + rw [trace_mul_matrixFunction, Complex.re_sum] + simp_rw [← Complex.ofReal_mul, Complex.ofReal_re, ← Finset.sum_neg_distrib] + congr 1 + ext i + unfold entropyFun + split_ifs with hle + · have h0 := le_antisymm hle (ρ.eigenvalues_nonneg i) + simp [h0] + · ring + +/-- For `x ≥ 0`, `entropyFun x = Real.negMulLog x`. -/ +theorem entropyFun_eq_negMulLog_of_nonneg {x : ℝ} (hx : 0 ≤ x) : + entropyFun x = Real.negMulLog x := by + unfold entropyFun Real.negMulLog + split_ifs with h + · have h0 : x = 0 := le_antisymm h hx + rw [h0]; ring + · rfl + +/-- `vonNeumannEntropy ρ = ∑ᵢ Real.negMulLog (ρ.eigenvalues i)`. + +Since `ρ` is PSD, its eigenvalues are non-negative, so `entropyFun = Real.negMulLog` +on the sum range. -/ +theorem vonNeumannEntropy_eq_negMulLog_sum (ρ : DensityMatrix n) : + vonNeumannEntropy ρ = ∑ i, Real.negMulLog (ρ.isHermitian.eigenvalues i) := by + rw [vonNeumannEntropy_eq_sum] + refine Finset.sum_congr rfl fun i _ => ?_ + exact entropyFun_eq_negMulLog_of_nonneg (ρ.eigenvalues_nonneg i) + +/-- `vonNeumannEntropy` expressed via the spectral matrix function `matrixFunction` + applied to `Real.negMulLog`. This bridges to Mathlib's continuous functional + calculus, enabling continuity arguments. -/ +theorem vonNeumannEntropy_eq_matrixFunction_re (ρ : DensityMatrix n) : + vonNeumannEntropy ρ = + (Tr (matrixFunction (fun x : ℝ => (Real.negMulLog x : ℂ)) + ρ.toMatrix ρ.isHermitian)).re := by + rw [matrixFunction_trace, vonNeumannEntropy_eq_negMulLog_sum] + rw [Complex.re_sum] + simp_rw [Complex.ofReal_re] + +/-- Von Neumann entropy is non-negative. -/ +theorem vonNeumannEntropy_nonneg (ρ : DensityMatrix n) : + 0 ≤ S(ρ) := by + rw [vonNeumannEntropy_eq_sum] + apply Finset.sum_nonneg + intro i _ + exact entropyFun_nonneg (ρ.eigenvalues_nonneg i) (ρ.eigenvalue_le_one i) + +/-- Von Neumann entropy is at most log(dim), achieved for the maximally mixed state. +This follows from Jensen's inequality applied to the concave function -x log x. -/ +theorem vonNeumannEntropy_le_log_dim [Nonempty n] (ρ : DensityMatrix n) : + S(ρ) ≤ Real.log (Fintype.card n) := by + rw [vonNeumannEntropy_eq_sum] + have heq : ∀ i, entropyFun (ρ.isHermitian.eigenvalues i) = + Real.negMulLog (ρ.isHermitian.eigenvalues i) := by + intro i + unfold entropyFun Real.negMulLog + split_ifs with hle + · have h0 : ρ.isHermitian.eigenvalues i = 0 := le_antisymm hle (ρ.eigenvalues_nonneg i) + simp [h0] + · rfl + simp_rw [heq] + have hlog_inv : Real.log (1 / Fintype.card n) = -Real.log (Fintype.card n) := by + rw [one_div, Real.log_inv] + have hunif_pos : ∀ i : n, 0 < 1 / (Fintype.card n : ℝ) := fun _ => by positivity + have hunif_sum : ∑ _ : n, 1 / (Fintype.card n : ℝ) = 1 := by + rw [Finset.sum_const, Finset.card_univ] + simp + have hKL : 0 ≤ ∑ i, ρ.isHermitian.eigenvalues i * + (Real.log (ρ.isHermitian.eigenvalues i) - Real.log (1 / Fintype.card n)) := by + have hsum_lower : ∑ i, ρ.isHermitian.eigenvalues i * + (Real.log (ρ.isHermitian.eigenvalues i) - Real.log (1 / Fintype.card n)) ≥ + ∑ i, (ρ.isHermitian.eigenvalues i - 1 / Fintype.card n) := by + apply Finset.sum_le_sum + intro i _ + by_cases hp : ρ.isHermitian.eigenvalues i = 0 + · simp [hp] + · have hpi_pos : 0 < ρ.isHermitian.eigenvalues i := + lt_of_le_of_ne (ρ.eigenvalues_nonneg i) (ne_comm.mp hp) + have hmul := mul_log_div_ge_sub' hpi_pos (hunif_pos i) + rw [Real.log_div (ne_of_gt hpi_pos) (ne_of_gt (hunif_pos i))] at hmul + linarith + have hsum_zero : ∑ i, (ρ.isHermitian.eigenvalues i - 1 / Fintype.card n) = 0 := by + rw [Finset.sum_sub_distrib, ρ.sum_eigenvalues, hunif_sum, sub_self] + linarith + simp only [hlog_inv, sub_neg_eq_add] at hKL + have hexpand : ∑ i, ρ.isHermitian.eigenvalues i * + (Real.log (ρ.isHermitian.eigenvalues i) + Real.log (Fintype.card n)) = + ∑ i, ρ.isHermitian.eigenvalues i * Real.log (ρ.isHermitian.eigenvalues i) + + Real.log (Fintype.card n) := by + simp_rw [mul_add] + rw [Finset.sum_add_distrib, ← Finset.sum_mul, ρ.sum_eigenvalues, one_mul] + rw [hexpand] at hKL + have hneg : ∑ i, Real.negMulLog (ρ.isHermitian.eigenvalues i) = + -∑ i, ρ.isHermitian.eigenvalues i * Real.log (ρ.isHermitian.eigenvalues i) := by + rw [← Finset.sum_neg_distrib] + congr 1 + ext i + unfold Real.negMulLog + ring + rw [hneg] + linarith + +/-- For PosSemidef ρ: Re(Tr (ρ^s)) = ∑ i, eigenvalue_i ^ s. +This follows from the spectral theorem: ρ^s = U diag(λᵢ^s) U†, +and trace cyclicity Tr (U D U†) = Tr (D) = ∑ Dᵢᵢ. -/ +lemma trace_rpow_eq_sum_pow (ρ : Matrix n n ℂ) (hρ : ρ.PosSemidef) (s : ℝ) : + (Tr (ρ ^ s)).re = ∑ i, hρ.1.eigenvalues i ^ s := by + rw [← matrixFunction_rpow_eq hρ s, matrixFunction_trace] + simp [Complex.ofReal_re] + +/-- HasDerivAt of eigenvalue rpow sum. +d/ds (∑ i, λᵢ ^ s)|_{s=1} = ∑ i, λᵢ * log(λᵢ). +This follows from HasStrictDerivAt of x^s in s at s=1 for each term. -/ +lemma hasDerivAt_sum_rpow {α : Type*} [Fintype α] (evs : α → ℝ) (hev : ∀ i, 0 ≤ evs i) : + HasDerivAt (fun (s : ℝ) => ∑ i, evs i ^ s) (∑ i, evs i * Real.log (evs i)) 1 := by + let F : α → ℝ → ℝ := fun i s => evs i ^ s + have hF : ∀ i ∈ Finset.univ, HasDerivAt (F i) (evs i * Real.log (evs i)) 1 := by + intro i _ + simp only [F] + rcases (hev i).lt_or_eq with hpos | hzero + · have h := HasDerivAt.exp ((hasDerivAt_id (𝕜 := ℝ) 1).mul_const (Real.log (evs i))) + simp only [id] at h + convert h using 1 + · ext s + rw [Real.rpow_def_of_pos hpos, mul_comm (Real.log (evs i))] + · rw [one_mul, Real.exp_log hpos] + · rw [← hzero, Real.log_zero, mul_zero] + exact (hasDerivAt_const (𝕜 := ℝ) 1 0).congr_of_eventuallyEq + (Filter.Eventually.mono (Ioi_mem_nhds (by norm_num : (0 : ℝ) < 1)) + (fun x hx => by simp [Real.zero_rpow (ne_of_gt hx)])) + have hsum : HasDerivAt (∑ i : α, F i) (∑ i : α, evs i * Real.log (evs i)) (1 : ℝ) := + HasDerivAt.sum (𝕜 := ℝ) (u := Finset.univ) hF + have heq : (fun s : ℝ => ∑ i : α, evs i ^ s) = ∑ i : α, F i := by + ext s + simp [F] + rw [heq] + exact hsum + +/-- Trace-rpow concavity: for 0 < s ≤ 1 and positive semidefinite A, B, + p ⋅ Tr (Aˢ) + (1−p) ⋅ Tr (Bˢ) ≤ Tr ((pA + (1−p)B)ˢ). + This follows from Löwner-order concavity (`rpow_isLownerConcave`) plus the + trace-monotonicity of the Hermitian order. -/ +lemma trace_rpow_concave (A B : Matrix n n ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) + (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) + (s : ℝ) (hs0 : 0 < s) (hs1 : s ≤ 1) : + p * (Tr (A ^ s)).re + (1 - p) * (Tr (B ^ s)).re ≤ (Tr ((p • A + (1 - p) • B) ^ s)).re := by + have hpsd_mix : (p • A + (1 - p) • B).PosSemidef := (hA.smul hp).add (hB.smul (by linarith)) + have hlowner := rpow_isLownerConcave hs0 hs1 n A B hA hB p hp hp1 hpsd_mix.1 + simp only [] at hlowner + have hfunc_eq : (fun x : ℝ => ((-x ^ s : ℝ) : ℂ)) = (fun x : ℝ => -(((x ^ s : ℝ) : ℂ))) := by + ext x + exact Complex.ofReal_neg _ + rw [hfunc_eq] at hlowner + rw [matrixFunction_neg hA.1, matrixFunction_neg hB.1, matrixFunction_neg hpsd_mix.1, + matrixFunction_rpow_eq hA, matrixFunction_rpow_eq hB, matrixFunction_rpow_eq hpsd_mix] at hlowner + have hlowner' : p • A ^ s + (1 - p) • B ^ s ≤ (p • A + (1 - p) • B) ^ s := by + have heq : p • -A ^ s + (1 - p) • -B ^ s = -(p • A ^ s + (1 - p) • B ^ s) := by + simp [smul_neg] + abel + rw [heq] at hlowner + rwa [neg_le_neg_iff] at hlowner + rw [Matrix.le_iff] at hlowner' + have htrace := (Complex.nonneg_iff.mp hlowner'.trace_nonneg).1 + rw [Matrix.trace_sub, Matrix.trace_add, Matrix.trace_smul, Matrix.trace_smul] at htrace + simp only [Complex.sub_re, Complex.add_re, Complex.real_smul, Complex.mul_re, + Complex.ofReal_re, Complex.ofReal_im] at htrace + linarith + +/-- **Von Neumann entropy is concave**. + +S(∑ᵢ pᵢ ρᵢ) ≥ ∑ᵢ pᵢ S(ρᵢ) + +**Proof**: We use the Löwner-order concavity of A ↦ Aˢ for 0 < s ≤ 1 +(from `rpow_isLownerConcave`). Define g(s) := Tr (ρ_mixˢ) +− p Tr (ρ₁ˢ) − (1−p) Tr (ρ₂ˢ). + +- **Non-negativity**: For s ∈ (0,1], Löwner concavity gives + p ρ₁ˢ + (1−p) ρ₂ˢ ≤ ρ_mixˢ in Löwner order, + so taking traces gives g(s) ≥ 0. +- **Boundary**: g(1) = 0 since all density matrices have trace 1. +- **Derivative sign**: Since g(1) = 0 ≤ g(s) for nearby s < 1, we have g'(1) ≤ 0. +- **Derivative formula**: g'(1) = −S(ρ_mix) + p S(ρ₁) + (1−p) S(ρ₂) + via (d/ds)|_{s=1} ∑ᵢ λᵢˢ = ∑ᵢ λᵢ log λᵢ = −S(ρ). +- **Conclusion**: g'(1) ≤ 0 gives the desired concavity inequality. +-/ +theorem vonNeumannEntropy_concave (ρ₁ ρ₂ : DensityMatrix n) (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) : + S(DensityMatrix.mix ρ₁ ρ₂ p hp hp1) ≥ + p * S(ρ₁) + (1 - p) * S(ρ₂) := by + set ρ_mix : DensityMatrix n := DensityMatrix.mix ρ₁ ρ₂ p hp hp1 + have hpsd₁ : ρ₁.toMatrix.PosSemidef := ρ₁.posSemidef + have hpsd₂ : ρ₂.toMatrix.PosSemidef := ρ₂.posSemidef + have hpsd_mix : ρ_mix.toMatrix.PosSemidef := ρ_mix.posSemidef + let g : ℝ → ℝ := fun s => + (ρ_mix.toMatrix ^ s).trace.re - + (p * (ρ₁.toMatrix ^ s).trace.re + (1 - p) * (ρ₂.toMatrix ^ s).trace.re) + have g_nonneg : ∀ s ∈ Set.Ioc (0 : ℝ) 1, 0 ≤ g s := by + intro s hs + exact sub_nonneg.mpr (trace_rpow_concave ρ₁.toMatrix ρ₂.toMatrix hpsd₁ hpsd₂ p hp hp1 s hs.1 hs.2) + have hg_one : g 1 = 0 := by + simp only [g] + rw [CFC.rpow_one _ (by simpa [Matrix.le_iff, sub_zero] using hpsd_mix), + CFC.rpow_one _ (by simpa [Matrix.le_iff, sub_zero] using hpsd₁), + CFC.rpow_one _ (by simpa [Matrix.le_iff, sub_zero] using hpsd₂)] + rw [ρ_mix.trace_eq_one, ρ₁.trace_eq_one, ρ₂.trace_eq_one] + simp [Complex.one_re] + have hderiv_mix : HasDerivAt (fun (s : ℝ) => (ρ_mix.toMatrix ^ s).trace.re) + (∑ i, ρ_mix.isHermitian.eigenvalues i * Real.log (ρ_mix.isHermitian.eigenvalues i)) 1 := by + convert hasDerivAt_sum_rpow hpsd_mix.1.eigenvalues ρ_mix.eigenvalues_nonneg using 1 + funext s + exact trace_rpow_eq_sum_pow _ hpsd_mix s + have hderiv₁ : HasDerivAt (fun (s : ℝ) => (ρ₁.toMatrix ^ s).trace.re) + (∑ i, ρ₁.isHermitian.eigenvalues i * Real.log (ρ₁.isHermitian.eigenvalues i)) 1 := by + convert hasDerivAt_sum_rpow hpsd₁.1.eigenvalues ρ₁.eigenvalues_nonneg using 1 + funext s + exact trace_rpow_eq_sum_pow _ hpsd₁ s + have hderiv₂ : HasDerivAt (fun (s : ℝ) => (ρ₂.toMatrix ^ s).trace.re) + (∑ i, ρ₂.isHermitian.eigenvalues i * Real.log (ρ₂.isHermitian.eigenvalues i)) 1 := by + convert hasDerivAt_sum_rpow hpsd₂.1.eigenvalues ρ₂.eigenvalues_nonneg using 1 + funext s + exact trace_rpow_eq_sum_pow _ hpsd₂ s + have hderiv_g : HasDerivAt g + ((∑ i, ρ_mix.isHermitian.eigenvalues i * Real.log (ρ_mix.isHermitian.eigenvalues i)) - + (p * (∑ i, ρ₁.isHermitian.eigenvalues i * Real.log (ρ₁.isHermitian.eigenvalues i)) + + (1 - p) * (∑ i, ρ₂.isHermitian.eigenvalues i * Real.log (ρ₂.isHermitian.eigenvalues i)))) 1 := by + change HasDerivAt (fun s => + (ρ_mix.toMatrix ^ s).trace.re - + (p * (ρ₁.toMatrix ^ s).trace.re + (1 - p) * (ρ₂.toMatrix ^ s).trace.re)) _ _ + exact hderiv_mix.sub (hderiv₁.const_mul p |>.add (hderiv₂.const_mul (1 - p))) + have hmin : ∀ y ∈ Set.Ioo (1 - (1 : ℝ) / 2) 1, g 1 ≤ g y := fun y hy => by + rw [hg_one] + exact g_nonneg y ⟨by linarith [hy.1], le_of_lt hy.2⟩ + have hderiv_g_nonpos : + (∑ i, ρ_mix.isHermitian.eigenvalues i * Real.log (ρ_mix.isHermitian.eigenvalues i)) - + (p * (∑ i, ρ₁.isHermitian.eigenvalues i * Real.log (ρ₁.isHermitian.eigenvalues i)) + + (1 - p) * (∑ i, ρ₂.isHermitian.eigenvalues i * Real.log (ρ₂.isHermitian.eigenvalues i))) ≤ 0 := + deriv_nonpos_of_forall_lt_min g _ 1 (1 / 2) (by norm_num) hderiv_g hmin + have hmix_eq : ∑ i, ρ_mix.isHermitian.eigenvalues i * Real.log (ρ_mix.isHermitian.eigenvalues i) = + -vonNeumannEntropy ρ_mix := by + rw [vonNeumannEntropy_eq_sum, ← Finset.sum_neg_distrib] + congr 1 + ext i + unfold entropyFun + split_ifs with h + · simp [le_antisymm h (ρ_mix.eigenvalues_nonneg i)] + · push_neg at h + ring + have h₁_eq : ∑ i, ρ₁.isHermitian.eigenvalues i * Real.log (ρ₁.isHermitian.eigenvalues i) = + -vonNeumannEntropy ρ₁ := by + rw [vonNeumannEntropy_eq_sum, ← Finset.sum_neg_distrib] + congr 1 + ext i + unfold entropyFun + split_ifs with h + · simp [le_antisymm h (ρ₁.eigenvalues_nonneg i)] + · push_neg at h + ring + have h₂_eq : ∑ i, ρ₂.isHermitian.eigenvalues i * Real.log (ρ₂.isHermitian.eigenvalues i) = + -vonNeumannEntropy ρ₂ := by + rw [vonNeumannEntropy_eq_sum, ← Finset.sum_neg_distrib] + congr 1 + ext i + unfold entropyFun + split_ifs with h + · simp [le_antisymm h (ρ₂.eigenvalues_nonneg i)] + · push_neg at h + ring + rw [hmix_eq, h₁_eq, h₂_eq] at hderiv_g_nonpos + linarith + +/-! ### Isomorphism invariance + +For a `*-`algebra equivalence `φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ` that preserves trace, +von Neumann entropy is invariant: `S(ρ.map φ) = S(ρ)`. In quantum-information literature +this is the **unitary invariance of von Neumann entropy** (Nielsen–Chuang Thm 11.1, +Wilde §11.1.3): a special case of Lindblad–Uhlmann monotonicity restricted to invertible +CPTP maps. The reindex specialisation uses `Matrix.reindexStarAlgEquiv e` which preserves +trace automatically (`Matrix.trace_reindexStarAlgEquiv`). + +TODO (Mathlib upstream): for matrix algebras over a field, +`Matrix m m R ≃⋆ₐ[R] Matrix n n R` forces `Fintype.card m = Fintype.card n` (Skolem–Noether +/ structure theorem for type `I_n` factors); once formalised, any cardinality hypothesis +in derived statements becomes automatic. -/ + +section IsomorphismInvariance + +variable {m : Type*} [Fintype m] [DecidableEq m] + +/-- **Von Neumann entropy is invariant under trace-preserving `*-`algebra equivalence** +(PosDef case). -/ +theorem vonNeumannEntropy_map_starAlgEquiv_posDef + (ρ : DensityMatrix m) (hρ : ρ.toMatrix.PosDef) + (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) + (hφ : ∀ A, (φ A).trace = A.trace) : + S(ρ.map φ hφ) = S(ρ) := by + unfold vonNeumannEntropy + have h_log_eq : matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian = + φ (matrixLog ρ.toMatrix ρ.isHermitian) := by + change matrixLog (φ ρ.toMatrix) _ = _ + exact matrixLog_map_starAlgEquiv hρ φ + have h_tr : Tr ((ρ.map φ hφ).toMatrix * + matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian) = + Tr (ρ.toMatrix * matrixLog ρ.toMatrix ρ.isHermitian) := by + rw [h_log_eq, DensityMatrix.map_toMatrix, ← map_mul, hφ] + change -(Tr ((ρ.map φ hφ).toMatrix * + matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian)).re = + -(Tr (ρ.toMatrix * matrixLog ρ.toMatrix ρ.isHermitian)).re + rw [h_tr] + +/-- Specialisation of `vonNeumannEntropy_map_starAlgEquiv_posDef` to reindexing. -/ +theorem vonNeumannEntropy_mapEquiv_posDef + (ρ : DensityMatrix m) (hρ : ρ.toMatrix.PosDef) (e : n ≃ m) : + S(ρ.mapEquiv e) = S(ρ) := + vonNeumannEntropy_map_starAlgEquiv_posDef ρ hρ _ _ + +end IsomorphismInvariance + +/-! ### Regularization and entropy + +Using the eigenvalue formula and continuity of `Real.negMulLog`, the entropy of a +regularised state `S(ρ_ε) = ∑ᵢ Real.negMulLog ((1-ε) λᵢ + ε/d)` is continuous in `ε`, +which yields `S(ρ_ε) → S(ρ)` as `ε → 0+`. This is the foundation for extending +PosDef-only theorems to PosSemidef. -/ + +section Regularization + +variable {m : Type*} [Fintype m] [DecidableEq m] + +/-- **Eigenvalue formula for the entropy of a regularization**: + `S(regularize ρ ε) = ∑ᵢ Real.negMulLog ((1-ε) λᵢ + ε/d)` where `λᵢ = ρ.eigenvalues i`. -/ +theorem vonNeumannEntropy_regularize_eq_negMulLog_sum [Nonempty n] + (ρ : DensityMatrix n) {ε : ℝ} (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + vonNeumannEntropy (DensityMatrix.regularize ρ hε hε') = + ∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + ε / Fintype.card n) := by + rw [vonNeumannEntropy_eq_matrixFunction_re] + rw [matrixFunction_eq_cfc] + rw [DensityMatrix.regularize_eq_cfc ρ hε hε'] + have hρ_sa : IsSelfAdjoint ρ.toMatrix := ρ.isHermitian + rw [← cfc_comp (R := ℝ) Real.negMulLog + (fun x => (1 - ε) * x + ε / Fintype.card n) ρ.toMatrix + hρ_sa Real.continuous_negMulLog.continuousOn (by fun_prop)] + rw [show (Real.negMulLog ∘ (fun x : ℝ => (1 - ε) * x + ε / (Fintype.card n : ℝ))) = + (fun x : ℝ => Real.negMulLog ((1 - ε) * x + ε / (Fintype.card n : ℝ))) from rfl] + rw [← matrixFunction_eq_cfc ρ.isHermitian + (fun x : ℝ => Real.negMulLog ((1 - ε) * x + ε / Fintype.card n))] + rw [matrixFunction_trace, Complex.re_sum] + simp_rw [Complex.ofReal_re] + +/-- The eigenvalue-formula function `ε ↦ ∑ᵢ Real.negMulLog ((1-ε) λᵢ + ε/d)` is + continuous in `ε ∈ ℝ`. -/ +theorem continuous_negMulLog_regularize_sum (ρ : DensityMatrix n) : + Continuous (fun ε : ℝ => + ∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + ε / Fintype.card n)) := by + refine continuous_finset_sum _ fun i _ => ?_ + exact Real.continuous_negMulLog.comp + ((continuous_const.sub continuous_id).mul continuous_const |>.add + (continuous_id.mul continuous_const)) + +/-- **Limit of regularization eigenvalue sum at `ε = 0`**: + `lim_{ε → 0} ∑ᵢ Real.negMulLog ((1-ε) λᵢ + ε/d) = S(ρ)`. + +This is the key continuity result. Combined with `vonNeumannEntropy_regularize_eq_negMulLog_sum`, +it gives `lim_{ε → 0+} S(regularize ρ ε) = S(ρ)`, the foundation for extending PosDef-only +theorems to PosSemidef. -/ +theorem tendsto_negMulLog_regularize_sum_zero (ρ : DensityMatrix n) : + Filter.Tendsto + (fun ε : ℝ => + ∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + ε / Fintype.card n)) + (nhds 0) + (nhds (vonNeumannEntropy ρ)) := by + have h_at_zero : (∑ i, Real.negMulLog ((1 - (0 : ℝ)) * ρ.isHermitian.eigenvalues i + + (0 : ℝ) / Fintype.card n)) = vonNeumannEntropy ρ := by + rw [vonNeumannEntropy_eq_negMulLog_sum] + refine Finset.sum_congr rfl fun i _ => ?_ + ring_nf + rw [← h_at_zero] + exact (continuous_negMulLog_regularize_sum ρ).tendsto 0 + +/-- **`vonNeumannEntropy` is invariant under reindex** (PSD case, no PosDef required). + +For any density matrix `ρ` and equivalence `e`, the entropy is preserved: +`S(ρ.mapEquiv e) = S(ρ)`. Proven via regularization + limit. -/ +theorem vonNeumannEntropy_mapEquiv [Nonempty n] [Nonempty m] + (ρ : DensityMatrix m) (e : n ≃ m) : + vonNeumannEntropy (DensityMatrix.mapEquiv ρ e) = vonNeumannEntropy ρ := by + have h_eq : ∀ ε : ℝ, ∀ (hε_pos : 0 < ε) (hε_le : ε ≤ 1), + vonNeumannEntropy (DensityMatrix.regularize + (DensityMatrix.mapEquiv ρ e) hε_pos.le hε_le) = + vonNeumannEntropy (DensityMatrix.regularize ρ hε_pos.le hε_le) := by + intro ε hε_pos hε_le + rw [DensityMatrix.regularize_mapEquiv ρ e hε_pos.le hε_le] + exact vonNeumannEntropy_mapEquiv_posDef + (DensityMatrix.regularize ρ hε_pos.le hε_le) + (DensityMatrix.regularize_posDef ρ hε_pos hε_le) e + have h_LHS_lim : Filter.Tendsto + (fun ε : ℝ => + ∑ i, Real.negMulLog ((1 - ε) * + (DensityMatrix.mapEquiv ρ e).isHermitian.eigenvalues i + + ε / Fintype.card n)) + (nhds 0) (nhds (vonNeumannEntropy (DensityMatrix.mapEquiv ρ e))) := + tendsto_negMulLog_regularize_sum_zero (DensityMatrix.mapEquiv ρ e) + have h_RHS_lim : Filter.Tendsto + (fun ε : ℝ => + ∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + + ε / Fintype.card m)) + (nhds 0) (nhds (vonNeumannEntropy ρ)) := + tendsto_negMulLog_regularize_sum_zero ρ + have h_funeq : ∀ ε : ℝ, 0 < ε → ε ≤ 1 → + (∑ i, Real.negMulLog ((1 - ε) * + (DensityMatrix.mapEquiv ρ e).isHermitian.eigenvalues i + + ε / Fintype.card n)) = + (∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + + ε / Fintype.card m)) := by + intro ε hε_pos hε_le + rw [← vonNeumannEntropy_regularize_eq_negMulLog_sum + (DensityMatrix.mapEquiv ρ e) hε_pos.le hε_le, + ← vonNeumannEntropy_regularize_eq_negMulLog_sum ρ hε_pos.le hε_le] + exact h_eq ε hε_pos hε_le + have h_within : ∀ᶠ ε in nhdsWithin (0 : ℝ) (Set.Ioi 0), + (∑ i, Real.negMulLog ((1 - ε) * + (DensityMatrix.mapEquiv ρ e).isHermitian.eigenvalues i + + ε / Fintype.card n)) = + (∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + + ε / Fintype.card m)) := by + rw [eventually_nhdsWithin_iff] + have h_le_one : ∀ᶠ ε in nhds (0 : ℝ), ε ≤ 1 := + Filter.eventually_of_mem (IsOpen.mem_nhds isOpen_Iio (by norm_num : (0 : ℝ) < 1)) <| by + intros ε hε + exact le_of_lt hε + filter_upwards [h_le_one] with ε hε_le_one hε_pos + exact h_funeq ε hε_pos hε_le_one + have hLHS_within := h_LHS_lim.mono_left (nhdsWithin_le_nhds (s := Set.Ioi (0 : ℝ))) + have hRHS_within := h_RHS_lim.mono_left (nhdsWithin_le_nhds (s := Set.Ioi (0 : ℝ))) + exact tendsto_nhds_unique (hLHS_within.congr' h_within) hRHS_within + +end Regularization + +end Matrix diff --git a/QuantumSystem/Analysis/Matrix/Effros.lean b/QuantumSystem/Analysis/Matrix/Effros.lean new file mode 100644 index 0000000..dfbb6d4 --- /dev/null +++ b/QuantumSystem/Analysis/Matrix/Effros.lean @@ -0,0 +1,1158 @@ +module + +public import Mathlib.Data.Matrix.Bilinear +public import QuantumSystem.Analysis.Matrix.Order + +/-! +# Effros's Matrix Convexity Approach + +This file formalises the Effros (2008) machinery used to prove Lieb's joint concavity theorem. + +## Contents + +1. **Compression lemmas** – `compression_pow_eq`, `compression_aeval_eq`, + `eigenvalues_compression_subset`, and `matrixFunction_compression_of_commuting`: + the map `X ↦ V† X V` (sandwiching) interacts well with polynomial/functional calculus + when `V†V = I` and `M` commutes with `VV†`. +2. **Block diagonal** – `compression_of_fromBlocks_cfc` and related CFC lemmas. +3. **`lownerConvex_compression_le`** – the fundamental inequality + `f(V†TV) ≤ V†f(T)V` when `V†V ≤ I`, `f` is Löwner convex, and `f(0) ≤ 0`. +4. **`isJensenConvex_of_isLownerConvex`** – Löwner convexity implies Jensen/HPJ + convexity (Effros 2008, Theorem 3.1; Hansen–Pedersen 1981). +5. **`neg_rpow_isJensenConvex`** – `f(t) = −tˢ` is Jensen convex for `0 < s ≤ 1`. +6. **`hpj_subhomogeneous`**, **`hpj_affine`** – concrete HPJ inequality instances. + +## References + +* Effros, *A Matrix Convexity Approach to Some Celebrated Quantum Inequalities* (2008) +* Hansen, Pedersen, *Jensen's operator inequality* (2003) +-/ +@[expose] public section + +namespace Matrix + +open Real NNReal MeasureTheory Set +open scoped MatrixOrder ComplexOrder Kronecker + +/-- Left multiplication operator on matrices. -/ +noncomputable def leftMul {m : Type*} [Fintype m] + (A : Matrix m m ℂ) : Matrix m m ℂ →ₗ[ℂ] Matrix m m ℂ := + mulLeftLinearMap m ℂ A + +/-- Right multiplication operator on matrices. -/ +noncomputable def rightMul {m : Type*} [Fintype m] + (B : Matrix m m ℂ) : Matrix m m ℂ →ₗ[ℂ] Matrix m m ℂ := + mulRightLinearMap m ℂ B + +/-- `leftMul A` applied to a matrix `X` yields `A * X`. -/ +@[simp] lemma leftMul_apply {m : Type*} [Fintype m] + (A X : Matrix m m ℂ) : leftMul A X = A * X := by + simp [leftMul] + +/-- `rightMul B` applied to a matrix `X` yields `X * B`. -/ +@[simp] lemma rightMul_apply {m : Type*} [Fintype m] + (B X : Matrix m m ℂ) : rightMul B X = X * B := by + simp [rightMul] + +/-- Left and right multiplication operators commute as linear maps. -/ +lemma leftMul_rightMul_commute {m : Type*} [Fintype m] + (A B : Matrix m m ℂ) : + leftMul A ∘ₗ rightMul B = rightMul B ∘ₗ leftMul A := by + simpa [leftMul, rightMul] using + (commute_mulLeftLinearMap_mulRightLinearMap (R := ℂ) (a := A) (b := B)) + +/-- Standard basis on `Matrix m m ℂ`, used to represent linear maps as matrices. -/ +noncomputable def matrixBasis (m : Type*) [Fintype m] [DecidableEq m] : + Module.Basis (m × m) ℂ (Matrix m m ℂ) := + Matrix.stdBasis ℂ m m + +/-- Matrix representation of left multiplication with respect to the standard basis. -/ +noncomputable def leftMulMatrix {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) : Matrix (m × m) (m × m) ℂ := + LinearMap.toMatrix (matrixBasis m) (matrixBasis m) (leftMul A) + +/-- Matrix representation of right multiplication with respect to the standard basis. -/ +noncomputable def rightMulMatrix {m : Type*} [Fintype m] [DecidableEq m] + (B : Matrix m m ℂ) : Matrix (m × m) (m × m) ℂ := + LinearMap.toMatrix (matrixBasis m) (matrixBasis m) (rightMul B) + +/-- Shorthand for `leftMulMatrix`. Corresponds to L_A in Effros (2008). -/ +notation "𝐋" => leftMulMatrix + +/-- Shorthand for `rightMulMatrix`. Corresponds to R_B in Effros (2008). -/ +notation "𝐑" => rightMulMatrix + +/-- Matrix representations of left and right multiplication commute. -/ +lemma leftMulMatrix_rightMulMatrix_commute {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) : + 𝐋 A * 𝐑 B = 𝐑 B * 𝐋 A := by + classical + have hcomp_left : + LinearMap.toMatrix (matrixBasis m) (matrixBasis m) (leftMul A ∘ₗ rightMul B) = + 𝐋 A * 𝐑 B := by + simpa [leftMulMatrix, rightMulMatrix] using + (LinearMap.toMatrix_comp (v₁ := matrixBasis m) (v₂ := matrixBasis m) + (v₃ := matrixBasis m) (f := leftMul A) (g := rightMul B)) + have hcomp_right : + LinearMap.toMatrix (matrixBasis m) (matrixBasis m) (rightMul B ∘ₗ leftMul A) = + 𝐑 B * 𝐋 A := by + simpa [leftMulMatrix, rightMulMatrix] using + (LinearMap.toMatrix_comp (v₁ := matrixBasis m) (v₂ := matrixBasis m) + (v₃ := matrixBasis m) (f := rightMul B) (g := leftMul A)) + have hcomm := congrArg + (fun f => LinearMap.toMatrix (matrixBasis m) (matrixBasis m) f) + (leftMul_rightMul_commute (A := A) (B := B)) + simpa [hcomp_left, hcomp_right] using hcomm + +/-- leftMulMatrix is additive: leftMulMatrix (A + B) = leftMulMatrix A + leftMulMatrix B -/ +theorem leftMulMatrix_add {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) : + 𝐋 (A + B) = 𝐋 A + 𝐋 B := by + simp only [leftMulMatrix] + have h : leftMul (A + B) = leftMul A + leftMul B := by + ext X; simp [leftMul, add_mul] + rw [h] + exact (LinearMap.toMatrix (matrixBasis m) (matrixBasis m)).map_add (leftMul A) (leftMul B) + +/-- leftMulMatrix is homogeneous: leftMulMatrix (c • A) = c • leftMulMatrix A -/ +lemma leftMulMatrix_smul {m : Type*} [Fintype m] [DecidableEq m] + (c : ℂ) (A : Matrix m m ℂ) : + 𝐋 (c • A) = c • 𝐋 A := by + simp only [leftMulMatrix] + have h : leftMul (c • A) = c • leftMul A := by + ext X; simp [leftMul] + rw [h] + exact (LinearMap.toMatrix (matrixBasis m) (matrixBasis m)).map_smul c (leftMul A) + +/-- rightMulMatrix is additive: rightMulMatrix (A + B) = rightMulMatrix A + rightMulMatrix B -/ +theorem rightMulMatrix_add {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) : + 𝐑 (A + B) = 𝐑 A + 𝐑 B := by + simp only [rightMulMatrix] + have h : rightMul (A + B) = rightMul A + rightMul B := by + ext X; simp [rightMul, mul_add] + rw [h] + exact (LinearMap.toMatrix (matrixBasis m) (matrixBasis m)).map_add (rightMul A) (rightMul B) + +/-- rightMulMatrix is homogeneous: rightMulMatrix (c • A) = c • rightMulMatrix A -/ +lemma rightMulMatrix_smul {m : Type*} [Fintype m] [DecidableEq m] + (c : ℂ) (A : Matrix m m ℂ) : + 𝐑 (c • A) = c • 𝐑 A := by + simp only [rightMulMatrix] + have h : rightMul (c • A) = c • rightMul A := by + ext X; simp [rightMul] + rw [h] + exact (LinearMap.toMatrix (matrixBasis m) (matrixBasis m)).map_smul c (rightMul A) + +/-- leftMulMatrix is homogeneous for real scalars -/ +theorem leftMulMatrix_smul_real {m : Type*} [Fintype m] [DecidableEq m] + (r : ℝ) (A : Matrix m m ℂ) : + 𝐋 (r • A) = r • 𝐋 A := by + have h : (r : ℂ) • A = r • A := by + ext i j + simp [Complex.real_smul] + rw [← h, leftMulMatrix_smul] + ext i j + simp [Complex.real_smul] + +/-- rightMulMatrix is homogeneous for real scalars -/ +theorem rightMulMatrix_smul_real {m : Type*} [Fintype m] [DecidableEq m] + (r : ℝ) (A : Matrix m m ℂ) : + 𝐑 (r • A) = r • 𝐑 A := by + have h : (r : ℂ) • A = r • A := by + ext i j + simp [Complex.real_smul] + rw [← h, rightMulMatrix_smul] + ext i j + simp [Complex.real_smul] + +/-- leftMulMatrix is multiplicative: leftMulMatrix (A * B) = leftMulMatrix A * leftMulMatrix B -/ +lemma leftMulMatrix_mul {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) : + 𝐋 (A * B) = 𝐋 A * 𝐋 B := by + simp only [leftMulMatrix] + have h : leftMul (A * B) = (leftMul A).comp (leftMul B) := by + ext X; simp [leftMul] + rw [h, LinearMap.toMatrix_comp (matrixBasis m) (matrixBasis m) (matrixBasis m)] + +/-- leftMulMatrix maps identity to identity -/ +lemma leftMulMatrix_one {m : Type*} [Fintype m] [DecidableEq m] : + 𝐋 (1 : Matrix m m ℂ) = (1 : Matrix (m × m) (m × m) ℂ) := by + simp only [leftMulMatrix] + have h : leftMul (1 : Matrix m m ℂ) = LinearMap.id := by + ext X; simp [leftMul] + rw [h, LinearMap.toMatrix_id (matrixBasis m)] + +/-- rightMulMatrix is anti-multiplicative: + rightMulMatrix (A * B) = rightMulMatrix B * rightMulMatrix A -/ +lemma rightMulMatrix_mul {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) : + 𝐑 (A * B) = 𝐑 B * 𝐑 A := by + simp only [rightMulMatrix] + have h : rightMul (A * B) = (rightMul B).comp (rightMul A) := by + ext X; simp [rightMul, Matrix.mul_assoc] + rw [h, LinearMap.toMatrix_comp (matrixBasis m) (matrixBasis m) (matrixBasis m)] + +/-- rightMulMatrix maps identity to identity -/ +lemma rightMulMatrix_one {m : Type*} [Fintype m] [DecidableEq m] : + 𝐑 (1 : Matrix m m ℂ) = (1 : Matrix (m × m) (m × m) ℂ) := by + simp only [rightMulMatrix] + have h : rightMul (1 : Matrix m m ℂ) = LinearMap.id := by + ext X; simp [rightMul] + rw [h, LinearMap.toMatrix_id (matrixBasis m)] + + +/-- rightMulMatrix preserves powers: rightMulMatrix (B ^ n) = (rightMulMatrix B) ^ n -/ +lemma rightMulMatrix_pow {m : Type*} [Fintype m] [DecidableEq m] + (B : Matrix m m ℂ) (n : ℕ) : + 𝐑 (B ^ n) = (𝐑 B) ^ n := by + induction n with + | zero => simp [rightMulMatrix_one] + | succ n ih => + rw [pow_succ, rightMulMatrix_mul, ih, ← pow_succ'] + +/-- leftMulMatrix preserves powers: leftMulMatrix (A ^ n) = (leftMulMatrix A) ^ n -/ +lemma leftMulMatrix_pow {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (n : ℕ) : + 𝐋 (A ^ n) = (𝐋 A) ^ n := by + induction n with + | zero => simp [leftMulMatrix_one] + | succ n ih => + rw [pow_succ, leftMulMatrix_mul, ih] + rw [pow_succ] + +/-- The standard basis element at index `(i, j)` is the matrix with `1` at `(i, j)` and `0` elsewhere. -/ +lemma matrixBasis_apply_eq_single {m : Type*} [Fintype m] [DecidableEq m] (ij : m × m) : + matrixBasis m ij = Matrix.single ij.1 ij.2 (1 : ℂ) := by + cases ij with + | mk a b => + simp [matrixBasis, Matrix.stdBasis_eq_single] + +/-- The basis representation of a matrix `M` at index `(i, j)` is `M i j`. -/ +lemma matrixBasis_repr_apply {m : Type*} [Fintype m] [DecidableEq m] + (M : Matrix m m ℂ) (i j : m) : + (matrixBasis m).repr M (i, j) = M i j := by + classical + have hsum := congrArg (fun N => N i j) ((matrixBasis m).sum_repr M) + have hsum' : + (∑ ij : m × m, + (matrixBasis m).repr M ij * + (if ij.1 = i ∧ ij.2 = j then (1 : ℂ) else 0)) = M i j := by + simpa [Matrix.sum_apply, Matrix.smul_apply, matrixBasis_apply_eq_single, + Matrix.single, Matrix.of_apply, mul_comm, mul_left_comm, mul_assoc] using hsum + have hcoeff : + (∑ ij : m × m, + (matrixBasis m).repr M ij * + (if ij.1 = i ∧ ij.2 = j then (1 : ℂ) else 0)) = + (matrixBasis m).repr M (i, j) := by + classical + let f : m × m → ℂ := fun ij => + (matrixBasis m).repr M ij * (if ij.1 = i ∧ ij.2 = j then (1 : ℂ) else 0) + have hsumf : (∑ ij, f ij) = f (i, j) := by + refine Fintype.sum_eq_single (i, j) ?_ + intro ij hij + have hne : ¬ (ij.1 = i ∧ ij.2 = j) := by + intro h + apply hij + cases ij with + | mk a b => + cases h with + | intro h1 h2 => + subst h1 + subst h2 + rfl + simp [f, hne] + simpa [f] using hsumf + calc + (matrixBasis m).repr M (i, j) = + ∑ ij : m × m, + (matrixBasis m).repr M ij * + (if ij.1 = i ∧ ij.2 = j then (1 : ℂ) else 0) := by + symm + exact hcoeff + _ = M i j := hsum' + +/-- Entry `(i, j), (k, l)` of `leftMulMatrix A` equals `A i k` if `j = l`, else `0`. -/ +theorem leftMulMatrix_apply {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (i j k l : m) : + 𝐋 A (i, j) (k, l) = if j = l then A i k else 0 := by + classical + have hrepr : + 𝐋 A (i, j) (k, l) = (A * Matrix.single k l (1 : ℂ)) i j := by + simp only [leftMulMatrix, leftMul, LinearMap.toMatrix_apply, matrixBasis_apply_eq_single] + exact matrixBasis_repr_apply .. + rw [hrepr] + by_cases hjl : j = l + · subst hjl; simp + · simp [hjl] + +/-- Entry `(i, j), (k, l)` of `rightMulMatrix B` equals `B l j` if `i = k`, else `0`. -/ +theorem rightMulMatrix_apply {m : Type*} [Fintype m] [DecidableEq m] + (B : Matrix m m ℂ) (i j k l : m) : + 𝐑 B (i, j) (k, l) = if i = k then B l j else 0 := by + classical + have hrepr : + 𝐑 B (i, j) (k, l) = (Matrix.single k l (1 : ℂ) * B) i j := by + simp only [rightMulMatrix, rightMul, LinearMap.toMatrix_apply, matrixBasis_apply_eq_single] + exact matrixBasis_repr_apply .. + rw [hrepr] + by_cases hik : i = k + · subst hik + simp + · simp [hik] + +/-- Action of `leftMulMatrix A` on the vectorized form of `Kᴴ` yields `(A * Kᴴ) i j`. -/ +lemma leftMulMatrix_mulVec_vecConjTranspose {m : Type*} [Fintype m] [DecidableEq m] + (A K : Matrix m m ℂ) (i j : m) : + (𝐋 A *ᵥ (fun x : m × m => Kᴴ x.1 x.2)) (i, j) = (A * Kᴴ) i j := by + classical + simp [Matrix.mulVec, dotProduct, leftMulMatrix_apply, Matrix.mul_apply, Fintype.sum_prod_type] + +/-- Action of `rightMulMatrix B` on the vectorized form of `Kᴴ` yields `(Kᴴ * B) i j`. -/ +lemma rightMulMatrix_mulVec_vecConjTranspose {m : Type*} [Fintype m] [DecidableEq m] + (B K : Matrix m m ℂ) (i j : m) : + (𝐑 B *ᵥ (fun x : m × m => Kᴴ x.1 x.2)) (i, j) = (Kᴴ * B) i j := by + classical + simp [Matrix.mulVec, dotProduct, rightMulMatrix_apply, Matrix.mul_apply, Fintype.sum_prod_type, mul_comm] + +/-- Composite mulVec: (leftMulMatrix X * rightMulMatrix Y) *ᵥ vec(K†) = vec(X * K† * Y). -/ +lemma leftRightMul_mulVec_vecConjTranspose {m : Type*} [Fintype m] [DecidableEq m] + (X Y K : Matrix m m ℂ) : + (𝐋 X * 𝐑 Y) *ᵥ (fun x : m × m => Kᴴ x.1 x.2) = + fun x : m × m => (X * Kᴴ * Y) x.1 x.2 := by + rw [← Matrix.mulVec_mulVec] + have hR : 𝐑 Y *ᵥ (fun x : m × m => Kᴴ x.1 x.2) = + fun x : m × m => (Kᴴ * Y) x.1 x.2 := by + ext ⟨i, j⟩; exact rightMulMatrix_mulVec_vecConjTranspose Y K i j + rw [hR] + ext ⟨i, j⟩ + classical + simp only [Matrix.mulVec, dotProduct, Fintype.sum_prod_type, leftMulMatrix_apply] + simp only [ite_mul, zero_mul, Finset.sum_ite_eq, Finset.mem_univ, ite_true] + rw [Matrix.mul_assoc]; rfl + +/-- The quadratic form star(vec(K†)) ⬝ᵥ (leftMulMatrix X * rightMulMatrix Y) *ᵥ vec(K†) +equals Tr(K * X * K† * Y). -/ +theorem quadForm_leftRightMul_eq_trace {m : Type*} [Fintype m] [DecidableEq m] + (X Y K : Matrix m m ℂ) : + let v : (m × m) → ℂ := fun x => Kᴴ x.1 x.2 + star v ⬝ᵥ ((leftMulMatrix X * rightMulMatrix Y) *ᵥ v) = + (K * X * Kᴴ * Y).trace := by + dsimp only + rw [leftRightMul_mulVec_vecConjTranspose] + simp only [dotProduct, Fintype.sum_prod_type, Pi.star_apply, + Matrix.conjTranspose_apply, star_star] + rw [Finset.sum_comm] + conv_rhs => rw [Matrix.mul_assoc K X Kᴴ, Matrix.mul_assoc K (X * Kᴴ) Y] + simp only [Matrix.trace, Matrix.diag, Matrix.mul_apply] + +/-- `leftMulMatrix A` equals the Kronecker product `A ⊗ₖ I`. -/ +lemma leftMulMatrix_eq_kronecker_one {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) : + 𝐋 A = A ⊗ₖ (1 : Matrix m m ℂ) := by + classical + ext ⟨i, j⟩ ⟨k, l⟩ + by_cases hjl : j = l + · subst hjl + simp [leftMulMatrix_apply, Matrix.kroneckerMap_apply] + · simp [leftMulMatrix_apply, Matrix.kroneckerMap_apply, hjl] + +/-- `rightMulMatrix B` equals the Kronecker product `I ⊗ₖ Bᵀ`. -/ +lemma rightMulMatrix_eq_one_kronecker_transpose {m : Type*} [Fintype m] [DecidableEq m] + (B : Matrix m m ℂ) : + 𝐑 B = (1 : Matrix m m ℂ) ⊗ₖ Bᵀ := by + classical + ext ⟨i, j⟩ ⟨k, l⟩ + by_cases hik : i = k + · subst hik + simp [rightMulMatrix_apply, Matrix.kroneckerMap_apply, Matrix.transpose_apply] + · simp [rightMulMatrix_apply, Matrix.kroneckerMap_apply, Matrix.transpose_apply, hik] + +/-- `leftMulMatrix` commutes with conjugate transpose: `leftMulMatrix (Aᴴ) = (leftMulMatrix A)ᴴ`. -/ +lemma leftMulMatrix_conjTranspose {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) : + 𝐋 (Aᴴ) = (𝐋 A)ᴴ := by + classical + ext ⟨i, j⟩ ⟨k, l⟩ + simp only [leftMulMatrix_apply, Matrix.conjTranspose_apply] + by_cases hjl : j = l + · subst hjl; simp + · have hlj : ¬ l = j := Ne.symm hjl + simp [hjl, hlj] + +/-- `rightMulMatrix` commutes with conjugate transpose: `rightMulMatrix (Bᴴ) = (rightMulMatrix B)ᴴ`. -/ +lemma rightMulMatrix_conjTranspose {m : Type*} [Fintype m] [DecidableEq m] + (B : Matrix m m ℂ) : + 𝐑 (Bᴴ) = (𝐑 B)ᴴ := by + classical + ext ⟨i, j⟩ ⟨k, l⟩ + simp only [rightMulMatrix_apply, Matrix.conjTranspose_apply] + by_cases hik : i = k + · subst hik; simp + · have hki : ¬ k = i := Ne.symm hik + simp [hik, hki] + +/-- `leftMulMatrix` preserves positive semidefiniteness. -/ +theorem leftMulMatrix_posSemidef {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) : + (𝐋 A).PosSemidef := by + classical + simpa [leftMulMatrix_eq_kronecker_one] using + (Matrix.PosSemidef.kronecker (m := m) (x := A) (y := (1 : Matrix m m ℂ)) hA posSemidef_one) + +/-- `rightMulMatrix` preserves positive semidefiniteness. -/ +lemma rightMulMatrix_posSemidef {m : Type*} [Fintype m] [DecidableEq m] + {B : Matrix m m ℂ} (hB : B.PosSemidef) : + (𝐑 B).PosSemidef := by + classical + have hB' : Bᵀ.PosSemidef := hB.transpose + simpa [rightMulMatrix_eq_one_kronecker_transpose] using + (Matrix.PosSemidef.kronecker (m := m) (x := (1 : Matrix m m ℂ)) (y := Bᵀ) posSemidef_one hB') + +/-- `rightMulMatrix` preserves positive definiteness. -/ +theorem rightMulMatrix_posDef {m : Type*} [Fintype m] [DecidableEq m] + {B : Matrix m m ℂ} (hB : B.PosDef) : + (𝐑 B).PosDef := by + classical + have hB' : Bᵀ.PosDef := hB.transpose + simpa [rightMulMatrix_eq_one_kronecker_transpose] using + (Matrix.PosDef.kronecker (m := m) (x := (1 : Matrix m m ℂ)) (y := Bᵀ) posDef_one hB') + +/-- leftMulMatrix as a star algebra homomorphism over ℝ. + This allows using the CFC infrastructure to relate + leftMulMatrix (f(A)) = f(leftMulMatrix(A)) for continuous f. -/ +noncomputable def leftMulStarAlgHom {m : Type*} [Fintype m] [DecidableEq m] : + Matrix m m ℂ →⋆ₐ[ℝ] Matrix (m × m) (m × m) ℂ where + toFun := leftMulMatrix + map_one' := leftMulMatrix_one + map_mul' := leftMulMatrix_mul + map_zero' := by + change 𝐋 0 = 0 + have h : leftMul (0 : Matrix m m ℂ) = 0 := by ext X; simp [leftMul] + simp only [leftMulMatrix, h, map_zero] + map_add' := leftMulMatrix_add + commutes' r := by + simp only [Algebra.algebraMap_eq_smul_one] + rw [leftMulMatrix_smul_real, leftMulMatrix_one] + map_star' a := by + simp only [star_eq_conjTranspose] + exact leftMulMatrix_conjTranspose a + +/-- The leftMulMatrix homomorphism is continuous (finite dimensional). -/ +lemma leftMulStarAlgHom_continuous {m : Type*} [Fintype m] [DecidableEq m] : + Continuous (leftMulStarAlgHom : Matrix m m ℂ →⋆ₐ[ℝ] Matrix (m × m) (m × m) ℂ) := by + -- leftMulMatrix is a linear map between finite-dimensional normed spaces, hence continuous + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + change Continuous (leftMulStarAlgHom.toAlgHom.toLinearMap : Matrix m m ℂ →ₗ[ℝ] Matrix (m × m) (m × m) ℂ) + exact leftMulStarAlgHom.toAlgHom.toLinearMap.continuous_of_finiteDimensional + +/-- CFC commutes with leftMulMatrix: for self-adjoint A and continuous f, + leftMulMatrix (cfc f A) = cfc f (leftMulMatrix A). -/ +lemma leftMulMatrix_cfc {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : IsSelfAdjoint A) (f : ℝ → ℝ) + (hf : ContinuousOn f (spectrum ℝ A) := by cfc_cont_tac) : + 𝐋 (cfc f A) = cfc f (𝐋 A) := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix (m × m) (m × m) ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m × m) (A := ℂ) + exact StarAlgHom.map_cfc leftMulStarAlgHom f A hf + leftMulStarAlgHom_continuous hA + +/-- leftMulMatrix preserves rpow: leftMulMatrix (A ^ s) = (leftMulMatrix A) ^ s + for positive semidefinite A and real s. -/ +lemma leftMulMatrix_rpow {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) {s : ℝ} (hs : 0 ≤ s) : + 𝐋 (A ^ s) = (𝐋 A) ^ s := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix (m × m) (m × m) ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m × m) (A := ℂ) + have hA0 : (0 : Matrix m m ℂ) ≤ A := by simpa [Matrix.le_iff] using hA + have hLA0 : (0 : Matrix (m × m) (m × m) ℂ) ≤ 𝐋 A := by + simpa [Matrix.le_iff] using leftMulMatrix_posSemidef hA + have hcont_rpow : ContinuousOn (fun x : ℝ => x ^ s) (spectrum ℝ A) := + (Real.continuous_rpow_const hs).continuousOn + rw [CFC.rpow_eq_cfc_real (a := A) (ha := hA0), + CFC.rpow_eq_cfc_real (a := 𝐋 A) (ha := hLA0)] + exact leftMulMatrix_cfc A hA.1.isSelfAdjoint (· ^ s) hcont_rpow + +/-- B ↦ rightMulMatrix(Bᴴ) as a star algebra homomorphism over ℝ. +This composes the anti-homomorphism `rightMulMatrix` with the anti-involution +conjTranspose, yielding a genuine homomorphism. -/ +noncomputable def rightMulConjTransposeStarAlgHom {m : Type*} [Fintype m] [DecidableEq m] : + Matrix m m ℂ →⋆ₐ[ℝ] Matrix (m × m) (m × m) ℂ where + toFun B := 𝐑 (Bᴴ) + map_one' := by simp [conjTranspose_one, rightMulMatrix_one] + map_mul' A B := by + change 𝐑 ((A * B)ᴴ) = 𝐑 (Aᴴ) * 𝐑 (Bᴴ) + rw [conjTranspose_mul, rightMulMatrix_mul] + map_zero' := by + change 𝐑 (0ᴴ) = 0 + rw [conjTranspose_zero] + have h : rightMul (0 : Matrix m m ℂ) = 0 := by ext X; simp [rightMul] + simp only [rightMulMatrix, h, map_zero] + map_add' A B := by + change 𝐑 ((A + B)ᴴ) = 𝐑 (Aᴴ) + 𝐑 (Bᴴ) + rw [conjTranspose_add, rightMulMatrix_add] + commutes' r := by + change 𝐑 ((algebraMap ℝ (Matrix m m ℂ) r)ᴴ) = algebraMap ℝ _ r + simp only [Algebra.algebraMap_eq_smul_one] + rw [conjTranspose_smul, conjTranspose_one, star_trivial, + rightMulMatrix_smul_real, rightMulMatrix_one] + map_star' A := by + simp only [star_eq_conjTranspose, conjTranspose_conjTranspose, + rightMulMatrix_conjTranspose] + +/-- The `rightMulConjTransposeStarAlgHom` is continuous (finite dimensional). -/ +lemma rightMulConjTransposeStarAlgHom_continuous {m : Type*} [Fintype m] [DecidableEq m] : + Continuous (rightMulConjTransposeStarAlgHom : + Matrix m m ℂ →⋆ₐ[ℝ] Matrix (m × m) (m × m) ℂ) := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + exact rightMulConjTransposeStarAlgHom.toAlgHom.toLinearMap.continuous_of_finiteDimensional + +/-- CFC commutes with rightMulMatrix for self-adjoint (Hermitian) matrices. +Uses the `rightMulConjTransposeStarAlgHom` to transport CFC via `StarAlgHom.map_cfc`. +Key insight: For Hermitian B, `Bᴴ = B`, so `Ψ(B) = rightMulMatrix(B)`, +and for self-adjoint `cfc f B`, `Ψ(cfc f B) = rightMulMatrix(cfc f B)`. -/ +lemma rightMulMatrix_cfc {m : Type*} [Fintype m] [DecidableEq m] + (B : Matrix m m ℂ) (hB : IsSelfAdjoint B) (f : ℝ → ℝ) + (hf : ContinuousOn f (spectrum ℝ B) := by cfc_cont_tac) : + 𝐑 (cfc f B) = cfc f (𝐑 B) := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix (m × m) (m × m) ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m × m) (A := ℂ) + -- Ψ = rightMulConjTransposeStarAlgHom: B ↦ rightMulMatrix(Bᴴ) + -- StarAlgHom.map_cfc gives: Ψ(cfc f B) = cfc f (Ψ B) + have h_map := StarAlgHom.map_cfc rightMulConjTransposeStarAlgHom f B hf + rightMulConjTransposeStarAlgHom_continuous hB + -- Ψ(B) = rightMulMatrix(Bᴴ) = rightMulMatrix(B) since B is Hermitian + have h_psi_B : (rightMulConjTransposeStarAlgHom : Matrix m m ℂ →⋆ₐ[ℝ] _) B = + 𝐑 B := by + dsimp [rightMulConjTransposeStarAlgHom] + rw [← star_eq_conjTranspose, hB.star_eq] + -- Ψ(cfc f B) = rightMulMatrix((cfc f B)ᴴ) = rightMulMatrix(cfc f B) + -- since cfc f B is self-adjoint + have h_psi_cfc : (rightMulConjTransposeStarAlgHom : Matrix m m ℂ →⋆ₐ[ℝ] _) (cfc f B) = + 𝐑 (cfc f B) := by + dsimp [rightMulConjTransposeStarAlgHom] + rw [← star_eq_conjTranspose, (cfc_predicate f B : IsSelfAdjoint (cfc f B)).star_eq] + rw [h_psi_B, h_psi_cfc] at h_map + exact h_map + +/-- `rightMulMatrix` preserves rpow: `rightMulMatrix (B ^ s) = (rightMulMatrix B) ^ s` +for positive semidefinite `B` and real `s`. -/ +lemma rightMulMatrix_rpow {m : Type*} [Fintype m] [DecidableEq m] + {B : Matrix m m ℂ} (hB : B.PosSemidef) {s : ℝ} (hs : 0 ≤ s) : + 𝐑 (B ^ s) = (𝐑 B) ^ s := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix (m × m) (m × m) ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m × m) (A := ℂ) + have hB0 : (0 : Matrix m m ℂ) ≤ B := by simpa [Matrix.le_iff] using hB + have hRB0 : (0 : Matrix (m × m) (m × m) ℂ) ≤ 𝐑 B := by + simpa [Matrix.le_iff] using rightMulMatrix_posSemidef hB + have hcont_rpow : ContinuousOn (fun x : ℝ => x ^ s) (spectrum ℝ B) := + (Real.continuous_rpow_const hs).continuousOn + rw [CFC.rpow_eq_cfc_real (a := B) (ha := hB0), + CFC.rpow_eq_cfc_real (a := 𝐑 B) (ha := hRB0)] + exact rightMulMatrix_cfc B hB.1.isSelfAdjoint (· ^ s) hcont_rpow + +/-- Matrix perspective of a function `f` using the Kubo-Ando style formula. +Defined for PSD `L` and PD `R`. -/ +noncomputable def matrixPerspective {m : Type*} [Fintype m] [DecidableEq m] + (f : ℝ → ℝ) (L R : Matrix m m ℂ) (hL : L.PosSemidef) (hR : R.PosDef) : Matrix m m ℂ := + let Rinv := matrixInvSqrt R hR + let inner := Rinvᴴ * L * Rinv + let hinner : inner.IsHermitian := + isHermitian_conjTranspose_mul_mul (B := Rinv) (A := L) hL.1 + let fInner := matrixFunction (fun x => (f x : ℂ)) inner hinner + let Rhalf := matrixSqrt R hR.posSemidef + Rhalf * fInner * Rhalf + +/-- The matrix perspective of a function preserves Hermiticity. -/ +lemma matrixPerspective_isHermitian {m : Type*} [Fintype m] [DecidableEq m] + (f : ℝ → ℝ) (L R : Matrix m m ℂ) (hL : L.PosSemidef) (hR : R.PosDef) : + (matrixPerspective f L R hL hR).IsHermitian := by + classical + unfold matrixPerspective + dsimp + set Rinv := matrixInvSqrt R hR + set inner := Rinvᴴ * L * Rinv + have hinner : inner.IsHermitian := + isHermitian_conjTranspose_mul_mul (B := Rinv) (A := L) hL.1 + set fInner := matrixFunction (fun x => (f x : ℂ)) inner hinner + set Rhalf := matrixSqrt R hR.posSemidef + have hRhalf : Rhalf.IsHermitian := matrixSqrt_isHermitian hR.posSemidef + have hfin : fInner.IsHermitian := + matrixFunction_isHermitian hinner (fun x => f x) + simpa [hRhalf.eq] using + (isHermitian_mul_mul_conjTranspose (B := Rhalf) (A := fInner) hfin) + +/-- Congruence lemma for matrixPerspective: equal matrices give equal results + regardless of the proof terms. -/ +theorem matrixPerspective_congr {m : Type*} [Fintype m] [DecidableEq m] + (f : ℝ → ℝ) (L₁ L₂ R₁ R₂ : Matrix m m ℂ) + (hL₁ : L₁.PosSemidef) (hL₂ : L₂.PosSemidef) (hR₁ : R₁.PosDef) (hR₂ : R₂.PosDef) + (hL : L₁ = L₂) (hR : R₁ = R₂) : + matrixPerspective f L₁ R₁ hL₁ hR₁ = matrixPerspective f L₂ R₂ hL₂ hR₂ := by + cases hL; cases hR; rfl + +/-- Perspective for left/right multiplication matrices. -/ +noncomputable def leftRightMatrixPerspective {m : Type*} [Fintype m] [DecidableEq m] + (f : ℝ → ℝ) (A B : Matrix m m ℂ) + (hA : (𝐋 A).PosSemidef) (hB : (𝐑 B).PosDef) : + Matrix (m × m) (m × m) ℂ := + matrixPerspective f (𝐋 A) (𝐑 B) hA hB + +/-- The left-right matrix perspective preserves Hermiticity. -/ +lemma leftRightMatrixPerspective_isHermitian {m : Type*} [Fintype m] [DecidableEq m] + (f : ℝ → ℝ) (A B : Matrix m m ℂ) + (hA : (𝐋 A).PosSemidef) (hB : (𝐑 B).PosDef) : + (leftRightMatrixPerspective f A B hA hB).IsHermitian := by + simpa [leftRightMatrixPerspective] using + (matrixPerspective_isHermitian (m := m × m) f (𝐋 A) (𝐑 B) hA hB) + +/-- Cancellation for (c · (S · P))† (c · (S · P)) = c² · (P · R · P) when S² = R. -/ +lemma perspective_AA_cancel {n : Type*} [Fintype n] + (c w : ℝ) (P S R : Matrix n n ℂ) + (hP : P.IsHermitian) (hS : S.IsHermitian) (hSS : S * S = R) + (hcsq : c * c = w) : + ((c : ℂ) • (S * P))ᴴ * ((c : ℂ) • (S * P)) = (w : ℂ) • (P * R * P) := by + simp only [conjTranspose_smul, RCLike.star_def, Complex.conj_ofReal, + conjTranspose_mul, hP.eq, hS.eq, smul_mul_assoc, mul_smul_comm, smul_smul, + show (↑c * ↑c : ℂ) = (↑w : ℂ) from by rw [← Complex.ofReal_mul]; exact congrArg _ hcsq, + mul_assoc] + congr 1 + rw [← mul_assoc S S P, hSS, ← mul_assoc P R P] + +/-- Cancellation with triple product: +(c · (S · P))† (S⁻¹† · L · S⁻¹) (c · (S · P)) = c² · (P · L · P) +when S · S⁻¹ = 1 and S⁻¹ · S = 1. -/ +lemma perspective_ATA_cancel {n : Type*} [Fintype n] [DecidableEq n] + (c w : ℝ) (P S Sinv L : Matrix n n ℂ) + (hP : P.IsHermitian) (hS : S.IsHermitian) (hSinv : Sinv.IsHermitian) + (hSSinv : S * Sinv = 1) (hSinvS : Sinv * S = 1) + (hcsq : c * c = w) : + ((c : ℂ) • (S * P))ᴴ * (Sinvᴴ * L * Sinv) * ((c : ℂ) • (S * P)) = + (w : ℂ) • (P * L * P) := by + simp only [conjTranspose_smul, RCLike.star_def, Complex.conj_ofReal, + conjTranspose_mul, hP.eq, hS.eq, hSinv.eq, smul_mul_assoc, mul_smul_comm, smul_smul, + show (↑c * ↑c : ℂ) = (↑w : ℂ) from by rw [← Complex.ofReal_mul]; exact congrArg _ hcsq] + congr 1 + simp only [mul_assoc] + rw [← mul_assoc Sinv S P, hSinvS, one_mul, + ← mul_assoc S Sinv (L * P), hSSinv, one_mul] + +/-- Sandwich distribution for the perspective proof: +P(X₁ + X₂ - Z)P = Y₁ + Y₂ - PZ'P +where PXᵢP = Yᵢ and Z = Z'. -/ +lemma perspective_sandwich_eq {n : Type*} [Fintype n] + {P S₁ S₂ : Matrix n n ℂ} + {A₁adj A₁ A₂adj A₂ M₁ M₂ Z Z' : Matrix n n ℂ} + {c₁ c₂ : ℂ} {w₁ w₂ : ℝ} + (hPA₁ : P * A₁adj = c₁ • S₁) (hA₁P : A₁ * P = c₁ • S₁) + (hPA₂ : P * A₂adj = c₂ • S₂) (hA₂P : A₂ * P = c₂ • S₂) + (hc₁ : c₁ * c₁ = (w₁ : ℂ)) (hc₂ : c₂ * c₂ = (w₂ : ℂ)) + (hZ : Z = Z') : + P * (A₁adj * M₁ * A₁ + A₂adj * M₂ * A₂ - Z) * P = + w₁ • (S₁ * M₁ * S₁) + w₂ • (S₂ * M₂ * S₂) - P * Z' * P := by + have h₁ : P * (A₁adj * M₁ * A₁) * P = w₁ • (S₁ * M₁ * S₁) := by + have ha : P * (A₁adj * M₁ * A₁) * P = (P * A₁adj) * M₁ * (A₁ * P) := by + simp only [mul_assoc] + rw [ha, hPA₁, hA₁P] + simp only [smul_mul_assoc, mul_smul_comm, smul_smul, hc₁, mul_assoc] + exact (IsScalarTower.algebraMap_smul ℂ w₁ _).symm + have h₂ : P * (A₂adj * M₂ * A₂) * P = w₂ • (S₂ * M₂ * S₂) := by + have ha : P * (A₂adj * M₂ * A₂) * P = (P * A₂adj) * M₂ * (A₂ * P) := by + simp only [mul_assoc] + rw [ha, hPA₂, hA₂P] + simp only [smul_mul_assoc, mul_smul_comm, smul_smul, hc₂, mul_assoc] + exact (IsScalarTower.algebraMap_smul ℂ w₂ _).symm + rw [mul_sub, sub_mul, mul_add, add_mul, h₁, h₂, hZ] + +/-- Joint convexity of the matrix perspective for Löwner convex `f`. -/ +theorem matrixPerspective_joint_convex.{v} {m : Type v} [Fintype m] [DecidableEq m] + {f : ℝ → ℝ} (hconv : IsJensenConvex.{v} f) + {L₁ L₂ R₁ R₂ : Matrix m m ℂ} + (hL₁ : L₁.PosSemidef) (hL₂ : L₂.PosSemidef) + (hR₁ : R₁.PosDef) (hR₂ : R₂.PosDef) + {w₁ w₂ : ℝ} (hw₁ : 0 ≤ w₁) (hw₂ : 0 ≤ w₂) (hw : w₁ + w₂ = 1) : + matrixPerspective f (w₁ • L₁ + w₂ • L₂) (w₁ • R₁ + w₂ • R₂) + ((hL₁.smul hw₁).add (hL₂.smul hw₂)) + (PosDef.convex_comb_nonneg hR₁ hR₂ hw₁ hw₂ hw) ≤ + w₁ • matrixPerspective f L₁ R₁ hL₁ hR₁ + + w₂ • matrixPerspective f L₂ R₂ hL₂ hR₂ := by + classical + set L : Matrix m m ℂ := w₁ • L₁ + w₂ • L₂ + set R : Matrix m m ℂ := w₁ • R₁ + w₂ • R₂ + have hR : R.PosDef := PosDef.convex_comb_nonneg hR₁ hR₂ hw₁ hw₂ hw + set Rinv : Matrix m m ℂ := matrixInvSqrt R hR + set Rhalf : Matrix m m ℂ := matrixSqrt R hR.posSemidef + set R₁inv : Matrix m m ℂ := matrixInvSqrt R₁ hR₁ + set R₂inv : Matrix m m ℂ := matrixInvSqrt R₂ hR₂ + set R₁half : Matrix m m ℂ := matrixSqrt R₁ hR₁.posSemidef + set R₂half : Matrix m m ℂ := matrixSqrt R₂ hR₂.posSemidef + set A₁ : Matrix m m ℂ := (Real.sqrt w₁ : ℂ) • (R₁half * Rinv) + set A₂ : Matrix m m ℂ := (Real.sqrt w₂ : ℂ) • (R₂half * Rinv) + set T₁ : Matrix m m ℂ := R₁invᴴ * L₁ * R₁inv + set T₂ : Matrix m m ℂ := R₂invᴴ * L₂ * R₂inv + have hT₁ : T₁.PosSemidef := by + simpa [T₁] using hL₁.conjTranspose_mul_mul_same R₁inv + have hT₂ : T₂.PosSemidef := by + simpa [T₂] using hL₂.conjTranspose_mul_mul_same R₂inv + have hRinv_herm : Rinv.IsHermitian := matrixInvSqrt_isHermitian hR + have hR₁half_herm : R₁half.IsHermitian := matrixSqrt_isHermitian hR₁.posSemidef + have hR₂half_herm : R₂half.IsHermitian := matrixSqrt_isHermitian hR₂.posSemidef + have hRhalf_herm : Rhalf.IsHermitian := matrixSqrt_isHermitian hR.posSemidef + have hA₁_adj : A₁ᴴ = (Real.sqrt w₁ : ℂ) • (Rinv * R₁half) := by + simp [A₁, hRinv_herm.eq, hR₁half_herm.eq, Matrix.conjTranspose_mul] + have hA₂_adj : A₂ᴴ = (Real.sqrt w₂ : ℂ) • (Rinv * R₂half) := by + simp [A₂, hRinv_herm.eq, hR₂half_herm.eq, Matrix.conjTranspose_mul] + have hsqrt₁_real : Real.sqrt w₁ * Real.sqrt w₁ = w₁ := Real.mul_self_sqrt hw₁ + have hsqrt₂_real : Real.sqrt w₂ * Real.sqrt w₂ = w₂ := Real.mul_self_sqrt hw₂ + have hsqrt₁ : (Real.sqrt w₁ : ℂ) * (Real.sqrt w₁ : ℂ) = (w₁ : ℂ) := by + rw [← Complex.ofReal_mul]; exact congrArg _ hsqrt₁_real + have hsqrt₂ : (Real.sqrt w₂ : ℂ) * (Real.sqrt w₂ : ℂ) = (w₂ : ℂ) := by + rw [← Complex.ofReal_mul]; exact congrArg _ hsqrt₂_real + -- A†A sum ≤ 1 (using AA cancellation helper) + have hA_sum : A₁ᴴ * A₁ + A₂ᴴ * A₂ = (Rinv * R * Rinv) := by + have hA₁A₁ : A₁ᴴ * A₁ = (w₁ : ℂ) • (Rinv * R₁ * Rinv) := + perspective_AA_cancel (Real.sqrt w₁) w₁ Rinv R₁half R₁ + hRinv_herm hR₁half_herm (matrixSqrt_mul_self hR₁) hsqrt₁_real + have hA₂A₂ : A₂ᴴ * A₂ = (w₂ : ℂ) • (Rinv * R₂ * Rinv) := + perspective_AA_cancel (Real.sqrt w₂) w₂ Rinv R₂half R₂ + hRinv_herm hR₂half_herm (matrixSqrt_mul_self hR₂) hsqrt₂_real + calc + A₁ᴴ * A₁ + A₂ᴴ * A₂ = + (w₁ : ℂ) • (Rinv * R₁ * Rinv) + (w₂ : ℂ) • (Rinv * R₂ * Rinv) := by + rw [hA₁A₁, hA₂A₂] + _ = Rinv * ((w₁ : ℂ) • R₁ + (w₂ : ℂ) • R₂) * Rinv := by + simp [mul_add, add_mul, mul_assoc] + _ = Rinv * R * Rinv := by + simp [R] + have hAB : A₁ᴴ * A₁ + A₂ᴴ * A₂ ≤ (1 : Matrix m m ℂ) := by + have hRinv_mul : Rinv * R * Rinv = (1 : Matrix m m ℂ) := by + simpa [Rinv, R] using matrixInvSqrt_mul_self hR + simp [hA_sum, hRinv_mul] + have hT₁_herm : T₁.IsHermitian := + isHermitian_conjTranspose_mul_mul (B := R₁inv) (A := L₁) hL₁.1 + have hT₂_herm : T₂.IsHermitian := + isHermitian_conjTranspose_mul_mul (B := R₂inv) (A := L₂) hL₂.1 + have hC : (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂).IsHermitian := + IsHermitian.add_isHermitian + (isHermitian_conjTranspose_mul_mul (B := A₁) (A := T₁) hT₁_herm) + (isHermitian_conjTranspose_mul_mul (B := A₂) (A := T₂) hT₂_herm) + have hconv' := hconv (m := m) (A := A₁) (B := A₂) (T₁ := T₁) (T₂ := T₂) hT₁ hT₂ hAB hC + have hpsd : + (A₁ᴴ * + matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A₁ + + A₂ᴴ * + matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * A₂ - + matrixFunction (fun x => (f x : ℂ)) (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂) hC + ).PosSemidef := by + simpa [Matrix.le_iff] using hconv' + have hpsd' : + (Rhalfᴴ * + (A₁ᴴ * + matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A₁ + + A₂ᴴ * + matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * A₂ - + matrixFunction (fun x => (f x : ℂ)) (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂) hC) * Rhalf + ).PosSemidef := + hpsd.conjTranspose_mul_mul_same Rhalf + have hRhalf_eq : Rhalfᴴ = Rhalf := hRhalf_herm.eq + -- A†TA sum = Rinv† * L * Rinv (using ATA cancellation helper) + have hinner : A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂ = Rinvᴴ * L * Rinv := by + have hA₁TA₁ : A₁ᴴ * T₁ * A₁ = (w₁ : ℂ) • (Rinv * L₁ * Rinv) := + perspective_ATA_cancel (Real.sqrt w₁) w₁ Rinv R₁half R₁inv L₁ + hRinv_herm hR₁half_herm (matrixInvSqrt_isHermitian hR₁) + (matrixSqrt_mul_matrixInvSqrt hR₁) (matrixInvSqrt_mul_matrixSqrt hR₁) hsqrt₁_real + have hA₂TA₂ : A₂ᴴ * T₂ * A₂ = (w₂ : ℂ) • (Rinv * L₂ * Rinv) := + perspective_ATA_cancel (Real.sqrt w₂) w₂ Rinv R₂half R₂inv L₂ + hRinv_herm hR₂half_herm (matrixInvSqrt_isHermitian hR₂) + (matrixSqrt_mul_matrixInvSqrt hR₂) (matrixInvSqrt_mul_matrixSqrt hR₂) hsqrt₂_real + calc + A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂ = + (w₁ : ℂ) • (Rinv * L₁ * Rinv) + (w₂ : ℂ) • (Rinv * L₂ * Rinv) := by + rw [hA₁TA₁, hA₂TA₂] + _ = Rinv * ((w₁ : ℂ) • L₁ + (w₂ : ℂ) • L₂) * Rinv := by + simp [mul_add, add_mul, mul_assoc] + _ = Rinvᴴ * L * Rinv := by + simp [L, hRinv_herm.eq] + -- Cancellation lemmas for Rhalf and Rinv + have hRhalf_Rinv : Rhalf * Rinv = 1 := by + simpa [Rhalf, Rinv] using matrixSqrt_mul_matrixInvSqrt hR + have hRinv_Rhalf : Rinv * Rhalf = 1 := by + simpa [Rhalf, Rinv] using matrixInvSqrt_mul_matrixSqrt hR + -- Sandwich helper lemmas (outside hfinal for performance) + have hRhalf_A₁_adj : Rhalf * A₁ᴴ = (Real.sqrt w₁ : ℂ) • R₁half := by + rw [hA₁_adj, mul_smul_comm]; congr 1; rw [← mul_assoc, hRhalf_Rinv, one_mul] + have hA₁_Rhalf : A₁ * Rhalf = (Real.sqrt w₁ : ℂ) • R₁half := by + simp only [A₁, smul_mul_assoc, mul_assoc, hRinv_Rhalf, mul_one] + have hRhalf_A₂_adj : Rhalf * A₂ᴴ = (Real.sqrt w₂ : ℂ) • R₂half := by + rw [hA₂_adj, mul_smul_comm]; congr 1; rw [← mul_assoc, hRhalf_Rinv, one_mul] + have hA₂_Rhalf : A₂ * Rhalf = (Real.sqrt w₂ : ℂ) • R₂half := by + simp only [A₂, smul_mul_assoc, mul_assoc, hRinv_Rhalf, mul_one] + -- matrixFunction_congr for the f(C) term (precomputed for performance) + have hmfC : matrixFunction (fun x => (f x : ℂ)) + (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂) hC = + matrixFunction (fun x => (f x : ℂ)) (Rinvᴴ * L * Rinv) + (isHermitian_conjTranspose_mul_mul (B := Rinv) (A := L) + ((hL₁.smul hw₁).add (hL₂.smul hw₂)).1) := + matrixFunction_congr (fun x => (f x : ℂ)) hC _ hinner + -- Final step: apply sandwich equation and conclude + have hfinal : + matrixPerspective f L R ((hL₁.smul hw₁).add (hL₂.smul hw₂)) hR ≤ + w₁ • matrixPerspective f L₁ R₁ hL₁ hR₁ + + w₂ • matrixPerspective f L₂ R₂ hL₂ hR₂ := by + rw [Matrix.le_iff] + rw [hRhalf_eq] at hpsd' + rw [perspective_sandwich_eq hRhalf_A₁_adj hA₁_Rhalf + hRhalf_A₂_adj hA₂_Rhalf hsqrt₁ hsqrt₂ hmfC] at hpsd' + exact hpsd' + simpa [L, R] using hfinal + +/-- The sign matrix Σ = I ⊕ (-I) on m ⊕ m is unitary. -/ +lemma signMatrix_mem_unitary {m : Type*} [Fintype m] [DecidableEq m] : + fromBlocks (1 : Matrix m m ℂ) 0 0 (-1 : Matrix m m ℂ) ∈ + unitary (Matrix (m ⊕ m) (m ⊕ m) ℂ) := by + rw [Unitary.mem_iff] + constructor <;> (simp [star_eq_conjTranspose, fromBlocks_conjTranspose, + fromBlocks_multiply, fromBlocks_one]) + +/-- The sign matrix is self-adjoint: Σ* = Σ. -/ +lemma signMatrix_star_eq {m : Type*} [Fintype m] [DecidableEq m] : + star (fromBlocks (1 : Matrix m m ℂ) 0 0 (-1 : Matrix m m ℂ)) = + fromBlocks (1 : Matrix m m ℂ) 0 0 (-1 : Matrix m m ℂ) := by + simp [star_eq_conjTranspose, fromBlocks_conjTranspose] + +/-! ### Kronecker Product Powers and Perspective Identity -/ + +/-- Kronecker product of natural number powers: `(A ⊗ₖ M)^n = A^n ⊗ₖ M^n`. -/ +lemma kronecker_npow {m : Type*} [Fintype m] [DecidableEq m] + (A M : Matrix m m ℂ) (n : ℕ) : + (A ⊗ₖ M) ^ n = (A ^ n) ⊗ₖ (M ^ n) := by + induction n with + | zero => simp + | succ n ih => + -- Work with the explicit Kronecker product type + set K : Matrix (m × m) (m × m) ℂ := A ⊗ₖ M with hK + change K ^ (n + 1) = _ + rw [pow_succ, ih, hK, ← mul_kronecker_mul, ← pow_succ, ← pow_succ] + +/-- The matrixFunction `x ↦ x^(p:ℂ)` equals rpow for PosSemidef matrices with non-negative + eigenvalues and real exponent p. -/ +lemma matrixFunction_cpow_eq_rpow {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) (p : ℝ) : + matrixFunction (fun x => x ^ (p : ℂ)) A hA.1 = + matrixFunction (fun x => ((x ^ p : ℝ) : ℂ)) A hA.1 := by + unfold matrixFunction + have h_diag : (fun i => (hA.1.eigenvalues i : ℂ) ^ (p : ℂ)) = + (fun i => ((hA.1.eigenvalues i ^ p : ℝ) : ℂ)) := by + funext i + exact (Complex.ofReal_cpow (hA.eigenvalues_nonneg i) p).symm + simp_rw [h_diag] + +/-- For commuting PSD L and PD R, the perspective inner matrix simplifies: +Rinv† * L * Rinv = L * R^{-1}. +Since matrixInvSqrt is Hermitian (self-adjoint), Rinv† = Rinv, +and since Rinv commutes with L, the product is L * Rinv * Rinv = L * R^{-1}. -/ +lemma perspective_inner_eq_commuting {n : Type*} [Fintype n] [DecidableEq n] + {L R : Matrix n n ℂ} (hL : L.PosSemidef) (hR : R.PosDef) + (hcomm : L * R = R * L) : + (matrixInvSqrt R hR)ᴴ * L * matrixInvSqrt R hR = L * matrixInvSqrt R hR * matrixInvSqrt R hR := by + have hRinv_herm := matrixInvSqrt_isHermitian hR + rw [hRinv_herm.eq] -- Rinv† = Rinv + rw [← matrixInvSqrt_commute_of_commute hL hR hcomm] + +/-- For commuting PSD L and PD R, the perspective inner matrix equals L * R^{-1}. +This combines the commutativity simplification with the fact that Rinv * Rinv = R^{-1}. -/ +lemma perspective_inner_eq_mul_inv {n : Type*} [Fintype n] [DecidableEq n] + {L R : Matrix n n ℂ} (hL : L.PosSemidef) (hR : R.PosDef) + (hcomm : L * R = R * L) : + (matrixInvSqrt R hR)ᴴ * L * matrixInvSqrt R hR = L * R⁻¹ := by + rw [perspective_inner_eq_commuting hL hR hcomm] + have hRinv_eq : matrixInvSqrt R hR = R ^ (-1 / 2 : ℝ) := by + simpa [matrixInvSqrt] using matrixFunction_rpow_eq hR.posSemidef (-1 / 2 : ℝ) + rw [hRinv_eq] + letI : NormedRing (Matrix n n ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix n n ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := n) (A := ℂ) + have hRunit := hR.isUnit + have hR0 : (0 : Matrix n n ℂ) ≤ R := by simpa [Matrix.le_iff] using hR.posSemidef + -- R^{-1/2} * R^{-1/2} = R^{-1} + have hRhalf_sq : R ^ (-1 / 2 : ℝ) * R ^ (-1 / 2 : ℝ) = R⁻¹ := by + -- Show (R^{-1/2})^2 * R = 1, hence (R^{-1/2})^2 = R⁻¹ + have h1 : R ^ (-1 / 2 : ℝ) * R ^ (-1 / 2 : ℝ) * R = 1 := by + rw [← CFC.rpow_add hRunit (x := (-1 / 2 : ℝ)) (y := (-1 / 2 : ℝ))] + norm_num + -- goal: R ^ (-1 : ℝ) * R = 1 + have := CFC.rpow_neg_mul_rpow (1 : ℝ) hRunit hR0 + rwa [CFC.rpow_one R hR0] at this + have hdet : IsUnit R.det := + (Matrix.isUnit_iff_isUnit_det R).mp hRunit + have h2 : R⁻¹ * R = 1 := Matrix.nonsing_inv_mul R hdet + exact hRunit.mul_right_cancel (h1.trans h2.symm) + -- L * (R^{-1/2} * R^{-1/2}) = L * R⁻¹ + rw [mul_assoc, hRhalf_sq] -- mul_assoc: (L * R^{-1/2}) * R^{-1/2} → L * (R^{-1/2} * R^{-1/2}) + +/-- Kronecker product distributes over rpow for PSD matrices: +`(X ⊗ₖ Y) ^ p = (X ^ p) ⊗ₖ (Y ^ p)` when `p ≥ 0`. -/ +lemma kronecker_rpow_psd {m n : Type*} [Fintype m] [DecidableEq m] [Fintype n] [DecidableEq n] + {X : Matrix m m ℂ} {Y : Matrix n n ℂ} (hX : X.PosSemidef) (hY : Y.PosSemidef) + (p : ℝ) (hp : 0 ≤ p) : + (X ⊗ₖ Y) ^ p = (X ^ p) ⊗ₖ (Y ^ p) := by + -- Spectral decomposition data + let UX := hX.1.eigenvectorUnitary + let dX := hX.1.eigenvalues + let UY := hY.1.eigenvectorUnitary + let dY := hY.1.eigenvalues + -- Eigenvalue nonnegativity + have hdX : ∀ i, 0 ≤ dX i := hX.eigenvalues_nonneg + have hdY : ∀ i, 0 ≤ dY i := hY.eigenvalues_nonneg + -- Spectral decompositions: X = UX * diag(dX) * UX†, etc. + have hX_eq : X = (UX : Matrix m m ℂ) * diagonal (RCLike.ofReal ∘ dX) * + (UX : Matrix m m ℂ)ᴴ := by + rw [hX.1.spectral_theorem (𝕜 := ℂ), Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] + have hY_eq : Y = (UY : Matrix n n ℂ) * diagonal (RCLike.ofReal ∘ dY) * + (UY : Matrix n n ℂ)ᴴ := by + rw [hY.1.spectral_theorem (𝕜 := ℂ), Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] + -- Diagonal matrices are PSD / nonneg + have hDX_psd : (diagonal (RCLike.ofReal ∘ dX) : Matrix m m ℂ).PosSemidef := + posSemidef_diagonal_iff.mpr fun i => RCLike.ofReal_nonneg.mpr (hdX i) + have hDY_psd : (diagonal (RCLike.ofReal ∘ dY) : Matrix n n ℂ).PosSemidef := + posSemidef_diagonal_iff.mpr fun j => RCLike.ofReal_nonneg.mpr (hdY j) + have hDX_nonneg : (0 : Matrix m m ℂ) ≤ diagonal (RCLike.ofReal ∘ dX) := hDX_psd.nonneg + have hDY_nonneg : (0 : Matrix n n ℂ) ≤ diagonal (RCLike.ofReal ∘ dY) := hDY_psd.nonneg + -- The conjugated forms are nonneg (needed for rpow_unitary_conj auto-param) + have hX_nonneg : 0 ≤ (UX : Matrix m m ℂ) * diagonal (RCLike.ofReal ∘ dX) * + (UX : Matrix m m ℂ)ᴴ := by + rw [← hX_eq]; exact hX.nonneg + have hY_nonneg : 0 ≤ (UY : Matrix n n ℂ) * diagonal (RCLike.ofReal ∘ dY) * + (UY : Matrix n n ℂ)ᴴ := by + rw [← hY_eq]; exact hY.nonneg + -- Diagonal rpow + have hDX_rpow : diagonal (RCLike.ofReal ∘ dX) ^ p = + diagonal (fun i => ((dX i ^ p : ℝ) : ℂ)) := by + change diagonal (fun i => (dX i : ℂ)) ^ p = _ + exact diagonal_rpow dX hdX p hp + have hDY_rpow : diagonal (RCLike.ofReal ∘ dY) ^ p = + diagonal (fun j => ((dY j ^ p : ℝ) : ℂ)) := by + change diagonal (fun j => (dY j : ℂ)) ^ p = _ + exact diagonal_rpow dY hdY p hp + -- CFC rpow via spectral: X^p = UX * diag(dX^p) * UX† + have hX_rpow : X ^ p = (UX : Matrix m m ℂ) * diagonal (fun i => ((dX i ^ p : ℝ) : ℂ)) * + (UX : Matrix m m ℂ)ᴴ := by + conv_lhs => rw [hX_eq] + rw [rpow_unitary_conj UX.2 hp hDX_nonneg (hM' := hX_nonneg), hDX_rpow] + have hY_rpow : Y ^ p = (UY : Matrix n n ℂ) * diagonal (fun j => ((dY j ^ p : ℝ) : ℂ)) * + (UY : Matrix n n ℂ)ᴴ := by + conv_lhs => rw [hY_eq] + rw [rpow_unitary_conj UY.2 hp hDY_nonneg (hM' := hY_nonneg), hDY_rpow] + -- Kronecker: X ⊗ₖ Y = (UX ⊗ₖ UY) * diag(dX ⊗ dY) * (UX ⊗ₖ UY)† + have hXY_eq : X ⊗ₖ Y = ((UX : Matrix m m ℂ) ⊗ₖ (UY : Matrix n n ℂ)) * + (diagonal (RCLike.ofReal ∘ dX) ⊗ₖ diagonal (RCLike.ofReal ∘ dY)) * + ((UX : Matrix m m ℂ) ⊗ₖ (UY : Matrix n n ℂ))ᴴ := by + rw [hX_eq, hY_eq, conjTranspose_kronecker, ← mul_kronecker_mul, ← mul_kronecker_mul] + -- UX ⊗ₖ UY is in unitaryGroup + have hUXY : ((UX : Matrix m m ℂ) ⊗ₖ (UY : Matrix n n ℂ)) ∈ + Matrix.unitaryGroup (m × n) ℂ := by + rw [Matrix.mem_unitaryGroup_iff'] + have h1 := Matrix.mem_unitaryGroup_iff'.mp UX.2 + have h2 := Matrix.mem_unitaryGroup_iff'.mp UY.2 + rw [star_eq_conjTranspose, conjTranspose_kronecker, ← mul_kronecker_mul] + simp only [← star_eq_conjTranspose] + rw [h1, h2, one_kronecker_one] + -- DXY = diag(dX) ⊗ₖ diag(dY) is nonneg + have hDXY_nonneg : 0 ≤ diagonal (RCLike.ofReal ∘ dX) ⊗ₖ + diagonal (RCLike.ofReal ∘ dY) := + (hDX_psd.kronecker hDY_psd).nonneg + -- The conjugated Kronecker form is nonneg + have hXY_nonneg : 0 ≤ ((UX : Matrix m m ℂ) ⊗ₖ (UY : Matrix n n ℂ)) * + (diagonal (RCLike.ofReal ∘ dX) ⊗ₖ diagonal (RCLike.ofReal ∘ dY)) * + ((UX : Matrix m m ℂ) ⊗ₖ (UY : Matrix n n ℂ))ᴴ := by + rw [← hXY_eq]; exact (hX.kronecker hY).nonneg + -- CFC rpow on the Kronecker product + have hXY_rpow : (X ⊗ₖ Y) ^ p = ((UX : Matrix m m ℂ) ⊗ₖ (UY : Matrix n n ℂ)) * + ((diagonal (RCLike.ofReal ∘ dX) ⊗ₖ diagonal (RCLike.ofReal ∘ dY)) ^ p) * + ((UX : Matrix m m ℂ) ⊗ₖ (UY : Matrix n n ℂ))ᴴ := by + conv_lhs => rw [hXY_eq] + exact rpow_unitary_conj hUXY hp hDXY_nonneg (hM' := hXY_nonneg) + -- Diagonal Kronecker rpow: (DX ⊗ₖ DY)^p = DX^p ⊗ₖ DY^p + have hDXY_rpow : (diagonal (RCLike.ofReal ∘ dX) ⊗ₖ + diagonal (RCLike.ofReal ∘ dY)) ^ p = + diagonal (fun i => ((dX i ^ p : ℝ) : ℂ)) ⊗ₖ + diagonal (fun j => ((dY j ^ p : ℝ) : ℂ)) := by + change (diagonal (fun i => (dX i : ℂ)) ⊗ₖ diagonal (fun j => (dY j : ℂ))) ^ p = _ + -- Convert LHS Kronecker to single diagonal + have hkron : diagonal (fun i => (dX i : ℂ)) ⊗ₖ diagonal (fun j => (dY j : ℂ)) = + diagonal (fun mn : m × n => ((dX mn.fst * dY mn.snd : ℝ) : ℂ)) := by + rw [diagonal_kronecker_diagonal]; congr 1; ext ⟨a, b⟩; push_cast; ring + rw [hkron, diagonal_rpow _ (fun ⟨a, b⟩ => mul_nonneg (hdX a) (hdY b)) p hp] + -- Convert back to Kronecker + rw [diagonal_kronecker_diagonal]; congr 1; ext ⟨a, b⟩ + push_cast [Real.mul_rpow (hdX a) (hdY b)]; ring + -- Combine everything + rw [hXY_rpow, hDXY_rpow, hX_rpow, hY_rpow] + rw [conjTranspose_kronecker, ← mul_kronecker_mul, ← mul_kronecker_mul] + +/-- The inner matrix of the perspective, raised to the power `p` and multiplied by `R`, +equals `L_{A^p} · R_{B^{1-p}}` for PD matrices `A`, `B` and `p ≥ 0`. +Here `L = L_A`, `R = R_B` are left/right multiplication operators, and +`S = R^{-1/2}` so that `Sᴴ * L * S` is the inner matrix of the perspective. -/ +lemma perspective_inner_rpow_mul_eq_leftRight {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hA : A.PosDef) (hB : B.PosDef) (p : ℝ) (hp : 0 ≤ p) : + let L := 𝐋 A + let R := 𝐑 B + let hR_pd := rightMulMatrix_posDef hB + let S := matrixInvSqrt R hR_pd + (Sᴴ * L * S) ^ p * R = 𝐋 (A ^ p) * 𝐑 (B ^ (1 - p)) := by + intro L R hR_pd S + have hL_psd : L.PosSemidef := leftMulMatrix_posSemidef hA.posSemidef + have hcomm : L * R = R * L := leftMulMatrix_rightMulMatrix_commute A B + have hinner_eq : Sᴴ * L * S = L * R⁻¹ := + perspective_inner_eq_mul_inv hL_psd hR_pd hcomm + have hB_unit : IsUnit B := hB.isUnit + have hB_det : IsUnit B.det := (Matrix.isUnit_iff_isUnit_det B).mp hB_unit + have hR_unit : IsUnit R := hR_pd.isUnit + have hRinv_eq_rm : R⁻¹ = 𝐑 (B⁻¹) := by + have h1 : R * 𝐑 (B⁻¹) = 1 := by + change 𝐑 B * 𝐑 (B⁻¹) = 1 + rw [← rightMulMatrix_mul, Matrix.nonsing_inv_mul B hB_det, rightMulMatrix_one] + have hR_detU : IsUnit R.det := (Matrix.isUnit_iff_isUnit_det R).mp hR_unit + have h2 : R * R⁻¹ = 1 := Matrix.mul_nonsing_inv R hR_detU + exact (hR_unit.mul_left_cancel (h1.trans h2.symm)).symm + have hLRinv_kron : L * R⁻¹ = A ⊗ₖ (B⁻¹)ᵀ := by + change 𝐋 A * R⁻¹ = A ⊗ₖ (B⁻¹)ᵀ + rw [hRinv_eq_rm, leftMulMatrix_eq_kronecker_one, + rightMulMatrix_eq_one_kronecker_transpose, + ← mul_kronecker_mul, Matrix.mul_one, Matrix.one_mul] + have hBinv_psd : (B⁻¹).PosSemidef := hB.posSemidef.inv + have hBinvT_psd : ((B⁻¹)ᵀ).PosSemidef := hBinv_psd.transpose + have hLRinv_rpow : (L * R⁻¹) ^ p = (A ^ p) ⊗ₖ (((B⁻¹)ᵀ) ^ p) := by + rw [hLRinv_kron] + exact kronecker_rpow_psd hA.posSemidef hBinvT_psd p hp + have hBinvT_rpow_mul : ((B⁻¹)ᵀ) ^ p * Bᵀ = (B ^ (1 - p))ᵀ := + inv_transpose_rpow_mul_transpose_eq B hB p hp + rw [hinner_eq, hLRinv_rpow] + change ((A ^ p) ⊗ₖ (((B⁻¹)ᵀ) ^ p)) * 𝐑 B = + 𝐋 (A ^ p) * 𝐑 (B ^ (1 - p)) + rw [rightMulMatrix_eq_one_kronecker_transpose B] + rw [← mul_kronecker_mul, Matrix.mul_one, hBinvT_rpow_mul] + rw [leftMulMatrix_eq_kronecker_one, rightMulMatrix_eq_one_kronecker_transpose] + rw [← mul_kronecker_mul, Matrix.mul_one, Matrix.one_mul] + +/-- The p-th power of the inner matrix of the perspective commutes with `R^{1/2}`. +This follows from the fact that `L ⊗ I` and `I ⊗ Bᵀ` commute. -/ +lemma perspective_inner_rpow_comm_sqrt_leftRight {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hA : A.PosDef) (hB : B.PosDef) (p : ℝ) (_hp : 0 ≤ p) : + let L := 𝐋 A + let R := 𝐑 B + let hR_pd := rightMulMatrix_posDef hB + let S := matrixInvSqrt R hR_pd + let T := matrixSqrt R hR_pd.posSemidef + (Sᴴ * L * S) ^ p * T = T * (Sᴴ * L * S) ^ p := by + intro L R hR_pd S T + have hL_psd : L.PosSemidef := leftMulMatrix_posSemidef hA.posSemidef + have hcomm : L * R = R * L := leftMulMatrix_rightMulMatrix_commute A B + have hinner_eq : Sᴴ * L * S = L * R⁻¹ := + perspective_inner_eq_mul_inv hL_psd hR_pd hcomm + have hR_nonneg : (0 : Matrix (m × m) (m × m) ℂ) ≤ R := by + simpa [Matrix.le_iff] using hR_pd.posSemidef + have hR_unit : IsUnit R := hR_pd.isUnit + have hRhalf_eq : T = R ^ (1 / 2 : ℝ) := by + change matrixSqrt R hR_pd.posSemidef = R ^ (1 / 2 : ℝ) + simpa [matrixSqrt] using matrixFunction_rpow_eq hR_pd.posSemidef (1 / 2 : ℝ) + have hR_det : IsUnit R.det := (Matrix.isUnit_iff_isUnit_det R).mp hR_unit + have hLRinv_comm_R : Commute R (L * R⁻¹) := by + rw [Commute, SemiconjBy] + have h1 : R * (L * R⁻¹) = L := by + rw [← mul_assoc, hcomm.symm, mul_assoc, + Matrix.mul_nonsing_inv R hR_det, mul_one] + have h2 : L * R⁻¹ * R = L := by + rw [mul_assoc, Matrix.nonsing_inv_mul R hR_det, mul_one] + rw [h1, h2] + have hinner_psd : (Sᴴ * L * S).PosSemidef := + hL_psd.conjTranspose_mul_mul_same S + have hinner_psd_nonneg : (0 : Matrix (m × m) (m × m) ℂ) ≤ L * R⁻¹ := by + rw [← hinner_eq] + exact hinner_psd.nonneg + rw [hinner_eq, hRhalf_eq, + CFC.rpow_eq_cfc_real (a := L * R⁻¹) (ha := hinner_psd_nonneg), + CFC.rpow_eq_cfc_real (a := R) (ha := hR_nonneg) (y := 1 / 2)] + simpa [L, R, hR_pd] using + (hLRinv_comm_R.symm.cfc_real (· ^ p) |>.symm.cfc_real (· ^ (1 / 2 : ℝ))).eq.symm + +/-- The matrix perspective with f(x) = −xᵖ on left/right multiplication matrices +equals −(L_{Aᵖ} · R_{B¹⁻ᵖ}) for PD matrices A, B and p ≥ 0. +Here L = L_A, R = R_B, S = R^(⁻¹⁄₂), T = R^(¹⁄₂), and the perspective is +T · f(S* L S) · T. -/ +theorem matrixPerspective_neg_leftRight_eq {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hA : A.PosDef) (hB : B.PosDef) (p : ℝ) (hp : 0 ≤ p) + (hL_psd : (𝐋 A).PosSemidef) (hR_pd : (𝐑 B).PosDef) : + matrixPerspective (fun x => -(x ^ p)) (𝐋 A) (𝐑 B) hL_psd hR_pd = + -(𝐋 (A ^ p) * 𝐑 (B ^ (1 - p))) := by + set L := 𝐋 A + set R := 𝐑 B + set S := matrixInvSqrt R hR_pd + set T := matrixSqrt R hR_pd.posSemidef + have hinner_psd : (Sᴴ * L * S).PosSemidef := + hL_psd.conjTranspose_mul_mul_same S + have hfun_neg : matrixFunction (fun x => ((-(x ^ p) : ℝ) : ℂ)) + (Sᴴ * L * S) hinner_psd.1 = -((Sᴴ * L * S) ^ p) := by + have h1 : (fun x : ℝ => ((-(x ^ p) : ℝ) : ℂ)) = (fun x : ℝ => -((x ^ p : ℝ) : ℂ)) := by + ext x + push_cast + ring + rw [h1, matrixFunction_neg hinner_psd.1, matrixFunction_rpow_eq hinner_psd p] + have hRhalf_sq : T * T = R := matrixSqrt_mul_self_posSemidef hR_pd.posSemidef + have hinnerp_comm_Rhalf : (Sᴴ * L * S) ^ p * T = T * (Sᴴ * L * S) ^ p := by + simpa [L, R, S, T] using + perspective_inner_rpow_comm_sqrt_leftRight A B hA hB p hp + have hpersp_simp : T * ((Sᴴ * L * S) ^ p) * T = (Sᴴ * L * S) ^ p * R := by + rw [hinnerp_comm_Rhalf.symm, mul_assoc, hRhalf_sq] + have hinnerp_R_eq : (Sᴴ * L * S) ^ p * R = + 𝐋 (A ^ p) * 𝐑 (B ^ (1 - p)) := by + simpa [L, R, S] using + perspective_inner_rpow_mul_eq_leftRight A B hA hB p hp + unfold matrixPerspective + dsimp only + rw [hfun_neg, Matrix.mul_neg, Matrix.neg_mul] + congr 1 + rw [hpersp_simp, hinnerp_R_eq] + +end Matrix diff --git a/QuantumSystem/Analysis/Matrix/HermitianFunctionalCalculus.lean b/QuantumSystem/Analysis/Matrix/HermitianFunctionalCalculus.lean new file mode 100644 index 0000000..4c6b5d7 --- /dev/null +++ b/QuantumSystem/Analysis/Matrix/HermitianFunctionalCalculus.lean @@ -0,0 +1,1330 @@ +module + +public import Mathlib.Analysis.CStarAlgebra.ContinuousFunctionalCalculus.Commute +public import Mathlib.Analysis.CStarAlgebra.CStarMatrix +public import Mathlib.LinearAlgebra.Lagrange +public import QuantumSystem.ForMathlib.Analysis.Matrix.Basic +public import QuantumSystem.ForMathlib.Analysis.Matrix.Hermitian +public import QuantumSystem.ForMathlib.LinearAlgebra.Matrix.StarAlgEquiv + +/-! +# Matrix Functional Calculus and Foundational Inequalities + +This file develops the core tools for matrix analysis. Foundational lemmas about Hermitian +matrices, positive semidefiniteness, block-matrix identities, and the Löwner order are in +`QuantumSystem.ForMathlib.Analysis.Matrix.*`. + +## Main results + +### Functional Calculus +- `matrixFunction f A hA`: spectral decomposition f(A) = U diag(f(λᵢ)) U* + for Hermitian A with eigendecomposition A = UΛ U*. +- Algebraic properties: `matrixFunction_id`, `matrixFunction_neg`, `matrixFunction_add`, + `matrixFunction_smul`, `matrixFunction_const`, `matrixFunction_add_const`, etc. +- Complex power instances: `matrixFunction_cpow_zero`, `matrixFunction_cpow_one`. +- Compatibility with Mathlib's CFC: `matrixFunction_eq_cfc`. +- Special functions: `matrixExp`, `matrixLog`, `matrixSqrt` (via `matrixFunction`). + +### Hermitian and PSD Structure +- `matrixFunction_isHermitian`: f(A) is Hermitian when f maps ℝ to ℝ. +- `matrixFunction_posSemidef`: f(A) ≥ 0 when f(λᵢ) ≥ 0 on eigenvalues. +- `matrixFunction_inv_add_const`: (A + tI)⁻¹ from `matrixFunction`. +- `matrixFunction_rpow_eq`: `matrixFunction` agrees with `CFC.rpow` on PSD matrices. +- `matrixSqrt`: the matrix square root A¹⁄² for PSD A. +- `matrixInvSqrt_commute_of_commute`: R⁻¹⁄² commutes with L when L and R commute + (for PSD L, PD R). + +### Spectral Decomposition Identities +- `Matrix.UHU_eq_one`: Uᴴ * U = 1 for the eigenvector unitary. +- `Matrix.UUH_eq_one`: U * Uᴴ = 1 for the eigenvector unitary. +- `Matrix.spectral_expand`: A = U * diag(eigenvalues) * Uᴴ. +- `Matrix.mulVec_eigenvector_col`: column j of U is an eigenvector with eigenvalue j. +- `Matrix.fromBlocks_diag_rpow`: (A ⊕ D)ᵖ = Aᵖ ⊕ Dᵖ for PSD A, D with p > 0. + +## References + +* Bhatia, *Matrix Analysis* (1997) +-/ + +@[expose] public section + +namespace Matrix + +open scoped MatrixOrder ComplexOrder + +/-- Functional calculus for Hermitian matrices via spectral decomposition. +Given f : ℝ → ℂ and a Hermitian matrix A = U Λ U*, we define f(A) = U f(Λ) U* +where f(Λ) applies f to each diagonal entry (eigenvalue). -/ +noncomputable def matrixFunction {m : Type*} [Fintype m] [DecidableEq m] + (f : ℝ → ℂ) (A : Matrix m m ℂ) (hA : A.IsHermitian) : Matrix m m ℂ := + let U : Matrix m m ℂ := hA.eigenvectorUnitary + let Λ := diagonal (fun i => f (hA.eigenvalues i)) + U * Λ * Uᴴ + +/-- Trace of f(A) equals sum of f(λ_i) by cyclicity of trace and unitarity of U. -/ +lemma matrixFunction_trace {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) (f : ℝ → ℂ) : + (matrixFunction f A hA).trace = ∑ i, f (hA.eigenvalues i) := by + unfold matrixFunction + rw [trace_mul_cycle] + have h := Unitary.coe_star_mul_self hA.eigenvectorUnitary + simp only [star_eq_conjTranspose] at h + rw [h, Matrix.one_mul] + exact trace_diagonal _ + +/-- matrixFunction of a real-valued function produces a Hermitian matrix. -/ +lemma matrixFunction_isHermitian {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) : + (matrixFunction (fun x => (f x : ℂ)) A hA).IsHermitian := by + unfold matrixFunction + -- U * D * Uᴴ is Hermitian when D is Hermitian and U is unitary + have hD : (diagonal (fun i => (f (hA.eigenvalues i) : ℂ))).IsHermitian := by + rw [isHermitian_diagonal_iff] + intro i + exact Complex.conj_ofReal _ + -- (U D Uᴴ)ᴴ = U Dᴴ Uᴴ = U D Uᴴ since D is Hermitian + rw [IsHermitian] + simp only [conjTranspose_mul, conjTranspose_conjTranspose] + conv_rhs => rw [mul_assoc] + rw [hD] + +/-- Spectral lemma: matrixFunction(id) = A. -/ +lemma matrixFunction_id {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) : + matrixFunction (fun x => (x : ℂ)) A hA = A := by + unfold matrixFunction + simp only + conv_rhs => rw [hA.spectral_theorem] + unfold Unitary.conjStarAlgAut + simp only [MonoidHom.coe_mk, OneHom.coe_mk] + rfl + +/-- Negation distributes through matrixFunction. -/ +lemma matrixFunction_neg {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℂ) : + matrixFunction (fun x => -f x) A hA = -matrixFunction f A hA := by + unfold matrixFunction + simp only + have hdiag : diagonal (fun i => -f (hA.eigenvalues i)) = + -diagonal (fun i => f (hA.eigenvalues i)) := by + ext i j + simp only [diagonal_apply, neg_apply] + split_ifs <;> ring + rw [hdiag, mul_neg, neg_mul] + +/-- matrixFunction distributes over addition. -/ +lemma matrixFunction_add {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f g : ℝ → ℂ) : + matrixFunction (fun x => f x + g x) A hA = + matrixFunction f A hA + matrixFunction g A hA := by + unfold matrixFunction + simp only + have hdiag : + diagonal (fun i => f (hA.eigenvalues i) + g (hA.eigenvalues i)) = + diagonal (fun i => f (hA.eigenvalues i)) + + diagonal (fun i => g (hA.eigenvalues i)) := by + ext i j + by_cases h : i = j + · subst h + simp + · simp [h] + rw [hdiag] + rw [Matrix.mul_add, Matrix.add_mul] + +/-- matrixFunction commutes with scalar multiplication. -/ +lemma matrixFunction_smul {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (c : ℂ) (f : ℝ → ℂ) : + matrixFunction (fun x => c * f x) A hA = c • matrixFunction f A hA := by + unfold matrixFunction + simp only + have hdiag : + diagonal (fun i => c * f (hA.eigenvalues i)) = + c • diagonal (fun i => f (hA.eigenvalues i)) := by + ext i j + by_cases h : i = j + · subst h + simp + · simp [h] + rw [hdiag] + rw [Matrix.mul_smul, Matrix.smul_mul] + +/-- matrixFunction of a constant function is a scalar multiple of the identity. -/ +lemma matrixFunction_const {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (c : ℂ) : + matrixFunction (fun _ => c) A hA = c • (1 : Matrix m m ℂ) := by + classical + unfold matrixFunction + simp only + have hdiag : diagonal (fun _ => c) = c • (1 : Matrix m m ℂ) := by + ext i j + by_cases h : i = j + · subst h + simp + · simp [h] + rw [hdiag] + have hU : (hA.eigenvectorUnitary : Matrix m m ℂ) * + (hA.eigenvectorUnitary : Matrix m m ℂ)ᴴ = 1 := by + simpa [star_eq_conjTranspose] using Unitary.coe_mul_star_self hA.eigenvectorUnitary + simp [hU] + +/-- matrixFunction of negation is negation of A. -/ +lemma matrixFunction_neg_id {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) : + matrixFunction (fun x => -(x : ℂ)) A hA = -A := by + have h : (fun x : ℝ => -(x : ℂ)) = (fun x => -((fun y : ℝ => (y : ℂ)) x)) := rfl + rw [h, matrixFunction_neg, matrixFunction_id] + +/-- matrixFunction depends only on the matrix value, not on the specific proof term. + If two matrices are equal, their matrixFunctions are equal. -/ +lemma matrixFunction_congr {m : Type*} [Fintype m] [DecidableEq m] + {A B : Matrix m m ℂ} (f : ℝ → ℂ) (hA : A.IsHermitian) (hB : B.IsHermitian) + (hAB : A = B) : matrixFunction f A hA = matrixFunction f B hB := by + subst hAB + rfl + +/-- matrixFunction equals Mathlib's IsHermitian.cfc for real-valued functions. +This connects our spectral decomposition definition to Mathlib's CFC infrastructure. -/ +lemma matrixFunction_eq_cfc {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) : + matrixFunction (fun x => (f x : ℂ)) A hA = cfc f A := by + have h : matrixFunction (fun x => (f x : ℂ)) A hA = hA.cfc f := by + unfold matrixFunction Matrix.IsHermitian.cfc + rw [Unitary.conjStarAlgAut_apply] + simp only [Function.comp_def, star_eq_conjTranspose] + rfl + calc + matrixFunction (fun x => (f x : ℂ)) A hA = hA.cfc f := h + _ = cfc f A := by + simpa using (Matrix.IsHermitian.cfc_eq (A := A) (hA := hA) (f := f)).symm + +/-- `matrixFunction` for the affine function `x ↦ x + t` adds `t • I`. -/ +lemma matrixFunction_add_const {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (t : ℝ) : + matrixFunction (fun x => ((x + t : ℝ) : ℂ)) A hA = A + (t : ℂ) • 1 := by + classical + unfold matrixFunction + set U : Matrix m m ℂ := (hA.eigenvectorUnitary : Matrix m m ℂ) + have hdiag : + diagonal (fun i => ((hA.eigenvalues i + t : ℝ) : ℂ)) = + diagonal (fun i => (hA.eigenvalues i : ℂ)) + (t : ℂ) • 1 := by + ext i j + by_cases h : i = j + · subst h + simp + · simp [h] + rw [hdiag] + simp only [Matrix.mul_add, Matrix.add_mul] + have hU : U * Uᴴ = 1 := by + simpa [U, star_eq_conjTranspose] using Unitary.coe_mul_star_self hA.eigenvectorUnitary + have hU1 : U * (1 : Matrix m m ℂ) * Uᴴ = 1 := by + simp [hU] + have hspec := hA.spectral_theorem + rw [Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] at hspec + have hspec' : U * diagonal (fun i => (hA.eigenvalues i : ℂ)) * Uᴴ = A := by + simpa [U, Function.comp] using hspec.symm + -- Rewrite the two diagonal terms using the spectral theorem and unitarity. + calc + U * diagonal (fun i => (hA.eigenvalues i : ℂ)) * Uᴴ + + U * ((t : ℂ) • (1 : Matrix m m ℂ)) * Uᴴ = + A + (t : ℂ) • 1 := by + rw [hspec'] + have hUt : U * ((t : ℂ) • (1 : Matrix m m ℂ)) * Uᴴ = + (t : ℂ) • (U * (1 : Matrix m m ℂ) * Uᴴ) := by + calc + U * ((t : ℂ) • (1 : Matrix m m ℂ)) * Uᴴ = + ((t : ℂ) • (U * (1 : Matrix m m ℂ))) * Uᴴ := by + simp + _ = (t : ℂ) • (U * (1 : Matrix m m ℂ) * Uᴴ) := by + simp + calc + A + U * ((t : ℂ) • (1 : Matrix m m ℂ)) * Uᴴ = + A + (t : ℂ) • (U * (1 : Matrix m m ℂ) * Uᴴ) := by + rw [hUt] + _ = A + (t : ℂ) • 1 := by + calc + A + (t : ℂ) • (U * (1 : Matrix m m ℂ) * Uᴴ) = + A + (t : ℂ) • (U * Uᴴ) := by + simp + _ = A + (t : ℂ) • 1 := by + simp [hU] + +/-- `matrixFunction` for `x ↦ (x + t)⁻¹` equals `(A + t•I)⁻¹` when `t > 0` and `A` is PSD. -/ +lemma matrixFunction_inv_add_const {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) {t : ℝ} (ht : 0 < t) : + matrixFunction (fun x => ((x + t : ℝ) : ℂ)⁻¹) A hA.1 = + (A + (t : ℂ) • 1)⁻¹ := by + classical + have hA' : A.IsHermitian := hA.1 + have hneq : ∀ x ∈ spectrum ℝ A, (x + t) ≠ 0 := by + intro x hx + have hx' : x ∈ Set.range hA'.eigenvalues := by + simpa [hA'.spectrum_real_eq_range_eigenvalues] using hx + rcases hx' with ⟨i, rfl⟩ + have hx_nonneg : 0 ≤ hA.1.eigenvalues i := hA.eigenvalues_nonneg i + linarith + have hcfcinv : + cfc (fun x : ℝ => (x + t)⁻¹) A = Ring.inverse (cfc (fun x : ℝ => x + t) A) := by + simpa using (cfc_inv (A := Matrix m m ℂ) (f := fun x : ℝ => x + t) (a := A) hneq) + have hcfcaff : cfc (fun x : ℝ => x + t) A = A + (t : ℂ) • 1 := by + have h := matrixFunction_add_const (m := m) hA' t + calc + cfc (fun x : ℝ => x + t) A = + matrixFunction (fun x => ((x + t : ℝ) : ℂ)) A hA' := by + simpa using (matrixFunction_eq_cfc hA' (fun x => x + t)).symm + _ = A + (t : ℂ) • 1 := h + have hposdef : (A + (t : ℂ) • 1).PosDef := PosSemidef.add_smul_one_posDef hA ht + have hunit : IsUnit (A + (t : ℂ) • 1) := hposdef.isUnit + let _ := hunit.invertible + have hcfcaff_inv : Ring.inverse (cfc (fun x : ℝ => x + t) A) = (A + (t : ℂ) • 1)⁻¹ := by + simpa [hcfcaff] using (Ring.inverse_unit hunit.unit) + have hmf : matrixFunction (fun x => ((x + t : ℝ) : ℂ)⁻¹) A hA.1 = + cfc (fun x : ℝ => (x + t)⁻¹) A := by + simpa using (matrixFunction_eq_cfc hA' (fun x => (x + t)⁻¹)) + rw [hmf, hcfcinv, hcfcaff_inv] + +/-- `matrixFunction` agrees with `CFC.rpow` for PSD matrices. -/ +lemma matrixFunction_rpow_eq {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) (s : ℝ) : + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 = A ^ s := by + have hA0 : 0 ≤ A := by + simpa [Matrix.le_iff] using hA + calc + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 = cfc (fun x : ℝ => x ^ s) A := by + simpa using (matrixFunction_eq_cfc hA.1 (fun x => x ^ s)) + _ = A ^ s := by + symm + exact CFC.rpow_eq_cfc_real (A := Matrix m m ℂ) (a := A) (y := s) (ha := hA0) + +/-- Resolvent form for `matrixFunction` on PSD matrices. -/ +lemma matrixFunction_resolvent {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) {r : ℝ} (hr : 0 < r) : + matrixFunction (fun x => ((1 - r * (x + r)⁻¹ : ℝ) : ℂ)) A hA.1 = + (1 : Matrix m m ℂ) - (r : ℂ) • (A + (r : ℂ) • 1)⁻¹ := by + have hfun_inv : (fun x : ℝ => ((x : ℂ) + (r : ℂ))⁻¹) = + (fun x : ℝ => ((x + r : ℝ) : ℂ)⁻¹) := by + funext x + simp + have hfun : (fun x : ℝ => ((1 - r * (x + r)⁻¹ : ℝ) : ℂ)) = + (fun x : ℝ => (1 : ℂ) + -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) := by + funext x + push_cast + ring + have hconst : matrixFunction (fun _ => (1 : ℂ)) A hA.1 = (1 : Matrix m m ℂ) := by + simpa using (matrixFunction_const (m := m) hA.1 (1 : ℂ)) + have hinv : matrixFunction (fun x => ((x + r : ℝ) : ℂ)⁻¹) A hA.1 = + (A + (r : ℂ) • 1)⁻¹ := by + exact matrixFunction_inv_add_const (m := m) hA hr + calc + matrixFunction (fun x => ((1 - r * (x + r)⁻¹ : ℝ) : ℂ)) A hA.1 = + matrixFunction (fun x => (1 : ℂ) + -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) A hA.1 := by + rw [hfun] + _ = matrixFunction (fun _ => (1 : ℂ)) A hA.1 + + matrixFunction (fun x => -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) A hA.1 := by + simpa using (matrixFunction_add hA.1 (fun _ => (1 : ℂ)) + (fun x => -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹))) + _ = (1 : Matrix m m ℂ) + + -((r : ℂ) • matrixFunction (fun x => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1) := by + have hsmul : + matrixFunction (fun x => (r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹) A hA.1 = + (r : ℂ) • matrixFunction (fun x => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1 := by + simpa using + (matrixFunction_smul hA.1 (r : ℂ) (fun x => ((x : ℂ) + (r : ℂ))⁻¹)) + have hneg : + matrixFunction (fun x => -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) A hA.1 = + -((r : ℂ) • matrixFunction (fun x => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1) := by + simpa [hsmul] using + (matrixFunction_neg hA.1 (fun x => (r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) + simp [hconst, hneg] + _ = (1 : Matrix m m ℂ) - (r : ℂ) • (A + (r : ℂ) • 1)⁻¹ := by + calc + (1 : Matrix m m ℂ) + + -((r : ℂ) • matrixFunction (fun x => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1) = + (1 : Matrix m m ℂ) + -((r : ℂ) • (A + (r : ℂ) • 1)⁻¹) := by + have hinv' : + matrixFunction (fun x : ℝ => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1 = + (A + (r : ℂ) • 1)⁻¹ := by + simpa [hfun_inv] using hinv + simp [hinv'] + _ = (1 : Matrix m m ℂ) - (r : ℂ) • (A + (r : ℂ) • 1)⁻¹ := by + simp [sub_eq_add_neg] + +/-- matrixFunction preserves positive semidefiniteness when f maps nonneg eigenvalues to nonneg. -/ +lemma matrixFunction_posSemidef {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) + (f : ℝ → ℝ) (hf : ∀ i, 0 ≤ f (hA.1.eigenvalues i)) : + (matrixFunction (fun x => (f x : ℂ)) A hA.1).PosSemidef := by + unfold matrixFunction + have hD : (diagonal (fun i => (f (hA.1.eigenvalues i) : ℂ))).PosSemidef := + PosSemidef.diagonal_ofReal hf + have key := hD.conjTranspose_mul_mul_same ((hA.1.eigenvectorUnitary : Matrix m m ℂ)ᴴ) + simp only [conjTranspose_conjTranspose] at key + exact key + +/-- matrixFunction (f - c) = matrixFunction f - c • I -/ +lemma matrixFunction_sub_const {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) (c : ℝ) : + matrixFunction (fun x => (f x - c : ℂ)) A hA = matrixFunction (fun x => (f x : ℂ)) A hA - (c : ℂ) • 1 := by + classical + let U : Matrix m m ℂ := hA.eigenvectorUnitary + have hU : U * Uᴴ = 1 := by + simpa [U, star_eq_conjTranspose] using Unitary.coe_mul_star_self hA.eigenvectorUnitary + have hdiag : + diagonal (fun i => (f (hA.eigenvalues i) - c : ℂ)) = + diagonal (fun i => (f (hA.eigenvalues i) : ℂ)) - diagonal (fun _ => (c : ℂ)) := by + ext i j + by_cases h : i = j + · subst h + simp + · simp [h] + have hdiagc : diagonal (fun _ => (c : ℂ)) = (c : ℂ) • (1 : Matrix m m ℂ) := by + ext i j + by_cases h : i = j + · subst h + simp + · simp [h] + unfold matrixFunction + rw [hdiag] + simp only [Matrix.mul_sub, Matrix.sub_mul] + rw [hdiagc] + simp only [Matrix.mul_smul, Matrix.smul_mul, Matrix.mul_one] + rw [hU] + +/-- Product of two matrixFunctions is the matrixFunction of the pointwise product. +Since both share the eigenbasis U, f(A) g(A) = U diag(f(λ) · g(λ)) U*. -/ +lemma matrixFunction_mul {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f g : ℝ → ℂ) : + matrixFunction f A hA * matrixFunction g A hA = + matrixFunction (fun x => f x * g x) A hA := by + unfold matrixFunction + set U : Matrix m m ℂ := (hA.eigenvectorUnitary : Matrix m m ℂ) with hU_def + have hUU : Uᴴ * U = 1 := by + simpa [star_eq_conjTranspose] using Unitary.coe_star_mul_self hA.eigenvectorUnitary + simp only [hU_def, Matrix.mul_assoc] + congr 1 + rw [← Matrix.mul_assoc Uᴴ U, hUU, Matrix.one_mul, ← Matrix.mul_assoc, diagonal_mul_diagonal] + +/-- Tr(A · f(A)) = ∑ᵢ λᵢ · f(λᵢ). +We first rewrite A as id(A) via `matrixFunction_id`, then apply +`matrixFunction_mul` and `matrixFunction_trace`. -/ +lemma trace_mul_matrixFunction {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) (f : ℝ → ℂ) : + (A * matrixFunction f A hA).trace = + ∑ i, ((hA.eigenvalues i : ℂ) * f (hA.eigenvalues i)) := by + suffices h : (matrixFunction (fun x => (x : ℂ)) A hA * + matrixFunction f A hA).trace = + ∑ i, ((hA.eigenvalues i : ℂ) * f (hA.eigenvalues i)) by + rwa [matrixFunction_id] at h + rw [matrixFunction_mul, matrixFunction_trace] + +/-- A Hermitian matrix commutes with any matrixFunction of itself. +This follows from `matrixFunction_id` (A = id(A)) and `matrixFunction_mul` +(id(A) · f(A) = f(A) · id(A) by pointwise commutativity of multiplication). -/ +lemma commute_matrixFunction_self {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℂ) : + Commute A (matrixFunction f A hA) := by + have hlhs : A * matrixFunction f A hA = + matrixFunction (fun x => (x : ℂ) * f x) A hA := by + have := matrixFunction_mul hA (fun x => (x : ℂ)) f + rwa [matrixFunction_id] at this + have hrhs : matrixFunction f A hA * A = + matrixFunction (fun x => f x * (x : ℂ)) A hA := by + have := matrixFunction_mul hA f (fun x => (x : ℂ)) + rwa [matrixFunction_id] at this + change A * matrixFunction f A hA = matrixFunction f A hA * A + rw [hlhs, hrhs] + congr 1; ext x; ring + +/-- The product A · f(A) is Hermitian when A is Hermitian and f : ℝ → ℝ. +Since A and f(A) share the same eigenbasis, they commute; both are Hermitian, +so their product is Hermitian by `IsHermitian.commute_iff`. -/ +lemma mul_matrixFunction_isHermitian {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) : + (A * matrixFunction (fun x => (f x : ℂ)) A hA).IsHermitian := + (hA.commute_iff (matrixFunction_isHermitian hA f)).mp + (commute_matrixFunction_self hA _) + +/-- The trace of a Hermitian matrix is real: casting its real part back to ℂ recovers the trace. +Proof: Aᴴ = A implies star(Tr A) = Tr(Aᴴ) = Tr A, so Tr A is self-adjoint, +hence equal to its real part cast to ℂ. -/ +lemma IsHermitian.trace_ofReal_re {m : Type*} [Fintype m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) : + (A.trace.re : ℂ) = A.trace := by + have h : (starRingEnd ℂ) A.trace = A.trace := by + change star A.trace = A.trace + rw [← trace_conjTranspose, hA.eq] + exact (RCLike.conj_eq_iff_re (K := ℂ)).mp h + +/-- Matrix exponential for Hermitian matrices via spectral decomposition. -/ +noncomputable def matrixExp {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) : Matrix m m ℂ := + matrixFunction (fun x => Real.exp x) A hA + +/-- Matrix logarithm for positive definite matrices via spectral decomposition. -/ +noncomputable def matrixLog {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) : Matrix m m ℂ := + matrixFunction (fun x => Real.log x) A hA + +/-- `matrixLog` expands into the spectral decomposition attached to the chosen +Hermitian proof. -/ +lemma matrixLog_spectral_eq {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) : + matrixLog A hA = + (hA.eigenvectorUnitary : Matrix m m ℂ) * + diagonal (fun i => ((Real.log (hA.eigenvalues i) : ℝ) : ℂ)) * + (hA.eigenvectorUnitary : Matrix m m ℂ)ᴴ := by + unfold matrixLog matrixFunction + rfl + +/-- Trace of matrix exponential equals sum of exp of eigenvalues. -/ +lemma matrixExp_trace {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) : + (matrixExp A hA).trace = ∑ i, (Real.exp (hA.eigenvalues i) : ℂ) := by + unfold matrixExp + rw [matrixFunction_trace] + +/-- Trace of matrix logarithm equals sum of log of eigenvalues. -/ +lemma matrixLog_trace {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) : + (matrixLog A hA).trace = ∑ i, (Real.log (hA.eigenvalues i) : ℂ) := by + unfold matrixLog + rw [matrixFunction_trace] + +/-- Matrix logarithm of a Hermitian matrix is Hermitian. -/ +lemma matrixLog_isHermitian {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) : + (matrixLog A hA).IsHermitian := by + unfold matrixLog matrixFunction IsHermitian + simp only [conjTranspose_mul, conjTranspose_conjTranspose] + have hDiag := IsHermitian.diagonal_real (fun i => Real.log (hA.eigenvalues i)) + rw [IsHermitian] at hDiag + rw [hDiag, Matrix.mul_assoc] + +/-- The matrix logarithm commutes with any `*-`algebra equivalence between complex matrix +algebras on PosDef matrices. Continuity is automatic in finite dimensions. -/ +theorem matrixLog_map_starAlgEquiv {m n : Type*} [Fintype m] [DecidableEq m] + [Fintype n] [DecidableEq n] {M : Matrix m m ℂ} (hM : M.PosDef) + (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) : + matrixLog (φ M) ((hM.posSemidef.map_starAlgEquiv φ).isHermitian) = + φ (matrixLog M hM.1) := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + letI : NormedRing (Matrix n n ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix n n ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := n) (A := ℂ) + unfold matrixLog + rw [matrixFunction_eq_cfc, matrixFunction_eq_cfc] + -- View `φ` as an ℝ-`StarAlgHom` to apply `StarAlgHomClass.map_cfc`. + let ψ : Matrix m m ℂ →⋆ₐ[ℝ] Matrix n n ℂ := + { toAlgHom := (φ.toAlgEquiv.restrictScalars ℝ).toAlgHom + map_star' := fun X => map_star φ X } + have h_ψ_apply : ∀ X, ψ X = φ X := fun _ => rfl + have hψ_cont : Continuous ψ := + ψ.toAlgHom.toLinearMap.continuous_of_finiteDimensional + have hM_sa : IsSelfAdjoint M := hM.1 + have hψM_sa : IsSelfAdjoint (ψ M) := by + rw [IsSelfAdjoint, ← map_star ψ] + exact congr_arg ψ hM_sa.star_eq + have h_cont : ContinuousOn Real.log (spectrum ℝ M) := by + refine Real.continuousOn_log.mono ?_ + intro x hx + rw [hM.1.spectrum_real_eq_range_eigenvalues] at hx + rcases hx with ⟨i, rfl⟩ + exact ne_of_gt (hM.eigenvalues_pos i) + have h_map := StarAlgHomClass.map_cfc (R := ℝ) (S := ℝ) ψ Real.log M + h_cont hψ_cont hM_sa hψM_sa + rw [h_ψ_apply, h_ψ_apply] at h_map + exact h_map.symm + +/-- Matrix inverse square root via functional calculus for PD matrices. -/ +noncomputable def matrixInvSqrt {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.PosDef) : Matrix m m ℂ := + matrixFunction (fun x => (Real.rpow x (-1 / 2 : ℝ) : ℂ)) A hA.1 + +/-- The matrix inverse square root of a PD matrix is Hermitian. -/ +lemma matrixInvSqrt_isHermitian {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosDef) : + (matrixInvSqrt A hA).IsHermitian := by + unfold matrixInvSqrt + exact matrixFunction_isHermitian hA.1 (fun x => Real.rpow x (-1 / 2 : ℝ)) + +/-- For a positive definite matrix `A`, `A^{-1/2} * A * A^{-1/2} = I`. -/ +lemma matrixInvSqrt_mul_self {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosDef) : + matrixInvSqrt A hA * A * matrixInvSqrt A hA = 1 := by + have hS : matrixInvSqrt A hA = A ^ (-1 / 2 : ℝ) := by + simpa [matrixInvSqrt] using (matrixFunction_rpow_eq hA.posSemidef (-1 / 2 : ℝ)) + have hAunit : IsUnit A := hA.isUnit + have hnonneg : 0 ≤ A := by + simpa [Matrix.le_iff] using hA.posSemidef + calc + matrixInvSqrt A hA * A * matrixInvSqrt A hA = + A ^ (-1 / 2 : ℝ) * A * A ^ (-1 / 2 : ℝ) := by + simp [hS] + _ = A ^ (-1 / 2 : ℝ) * A ^ (1 : ℝ) * A ^ (-1 / 2 : ℝ) := by + simp [CFC.rpow_one (a := A) hnonneg] + _ = A ^ ((-1 / 2 : ℝ) + (1 : ℝ)) * A ^ (-1 / 2 : ℝ) := by + simp [CFC.rpow_add (a := A) (x := (-1 / 2 : ℝ)) (y := (1 : ℝ)) hAunit, mul_assoc] + _ = A ^ (1 / 2 : ℝ) * A ^ (-1 / 2 : ℝ) := by + ring_nf + _ = 1 := by + calc + A ^ (1 / 2 : ℝ) * A ^ (-1 / 2 : ℝ) = + A ^ ((1 / 2 : ℝ) + (-1 / 2 : ℝ)) := by + symm + simpa using (CFC.rpow_add (a := A) (x := (1 / 2 : ℝ)) (y := (-1 / 2 : ℝ)) hAunit) + _ = 1 := by + ring_nf + simpa using (CFC.rpow_zero (a := A) hnonneg) + +/-- Matrix square root via functional calculus for PSD matrices. -/ +noncomputable def matrixSqrt {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.PosSemidef) : Matrix m m ℂ := + matrixFunction (fun x => (Real.rpow x (1 / 2 : ℝ) : ℂ)) A hA.1 + +/-- The matrix square root of a PSD matrix is Hermitian. -/ +lemma matrixSqrt_isHermitian {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) : + (matrixSqrt A hA).IsHermitian := by + unfold matrixSqrt + exact matrixFunction_isHermitian hA.1 (fun x => Real.rpow x (1 / 2 : ℝ)) + +/-- For a positive semidefinite matrix `A`, `A^{1/2} * A^{1/2} = A`. -/ +lemma matrixSqrt_mul_self_posSemidef {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) : + matrixSqrt A hA * matrixSqrt A hA = A := by + classical + -- Use the spectral decomposition and diagonal computation. + unfold matrixSqrt matrixFunction + set U : Matrix m m ℂ := (hA.1.eigenvectorUnitary : Matrix m m ℂ) + set D : Matrix m m ℂ := + diagonal (fun i => (Real.rpow (hA.1.eigenvalues i) (1 / 2 : ℝ) : ℂ)) + have hU : Uᴴ * U = (1 : Matrix m m ℂ) := by + simpa [U, star_eq_conjTranspose] using Unitary.coe_star_mul_self hA.1.eigenvectorUnitary + have hD_mul : D * D = diagonal (fun i => (hA.1.eigenvalues i : ℂ)) := by + ext i j + by_cases h : i = j + · subst h + have hnonneg : 0 ≤ hA.1.eigenvalues i := hA.eigenvalues_nonneg i + simp only [D, mul_diagonal, diagonal_apply_eq] + norm_cast + simp only [Real.rpow_eq_pow] + rw [← Real.sqrt_eq_rpow, Real.mul_self_sqrt hnonneg] + · simp only [D, mul_diagonal] + simp [h] + calc + U * D * Uᴴ * (U * D * Uᴴ) + = U * (D * D) * Uᴴ := by + simp only [Matrix.mul_assoc] + congr 1 + rw [← Matrix.mul_assoc Uᴴ U, hU, Matrix.one_mul] + _ = U * diagonal (fun i => (hA.1.eigenvalues i : ℂ)) * Uᴴ := by + simp [hD_mul] + _ = A := by + simpa [U] using (hA.1.spectral_theorem).symm + +/-- For a positive definite matrix `A`, `A^{1/2} * A^{1/2} = A`. -/ +lemma matrixSqrt_mul_self {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosDef) : + matrixSqrt A hA.posSemidef * matrixSqrt A hA.posSemidef = A := by + have hS : matrixSqrt A hA.posSemidef = A ^ (1 / 2 : ℝ) := by + simpa [matrixSqrt] using (matrixFunction_rpow_eq hA.posSemidef (1 / 2 : ℝ)) + have hAunit : IsUnit A := hA.isUnit + calc + matrixSqrt A hA.posSemidef * matrixSqrt A hA.posSemidef = + A ^ (1 / 2 : ℝ) * A ^ (1 / 2 : ℝ) := by + simp [hS] + _ = A ^ ((1 / 2 : ℝ) + (1 / 2 : ℝ)) := by + symm + simpa using (CFC.rpow_add (a := A) (x := (1 / 2 : ℝ)) (y := (1 / 2 : ℝ)) hAunit) + _ = A := by + have hnonneg : 0 ≤ A := by + simpa [Matrix.le_iff] using hA.posSemidef + ring_nf + simpa using (CFC.rpow_one (a := A) hnonneg) + +/-- For a positive definite matrix `A`, `A^{1/2} * A^{-1/2} = I`. -/ +lemma matrixSqrt_mul_matrixInvSqrt {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosDef) : + matrixSqrt A hA.posSemidef * matrixInvSqrt A hA = 1 := by + have hS : matrixSqrt A hA.posSemidef = A ^ (1 / 2 : ℝ) := by + simpa [matrixSqrt] using (matrixFunction_rpow_eq hA.posSemidef (1 / 2 : ℝ)) + have hSi : matrixInvSqrt A hA = A ^ (-1 / 2 : ℝ) := by + simpa [matrixInvSqrt] using (matrixFunction_rpow_eq hA.posSemidef (-1 / 2 : ℝ)) + have hAunit : IsUnit A := hA.isUnit + have hnonneg : 0 ≤ A := by + simpa [Matrix.le_iff] using hA.posSemidef + calc + matrixSqrt A hA.posSemidef * matrixInvSqrt A hA = + A ^ (1 / 2 : ℝ) * A ^ (-1 / 2 : ℝ) := by + simp [hS, hSi] + _ = A ^ ((1 / 2 : ℝ) + (-1 / 2 : ℝ)) := by + symm + simpa using (CFC.rpow_add (a := A) (x := (1 / 2 : ℝ)) (y := (-1 / 2 : ℝ)) hAunit) + _ = 1 := by + ring_nf + simpa using (CFC.rpow_zero (a := A) hnonneg) + +/-- For a positive definite matrix `A`, `A^{-1/2} * A^{1/2} = I`. -/ +lemma matrixInvSqrt_mul_matrixSqrt {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosDef) : + matrixInvSqrt A hA * matrixSqrt A hA.posSemidef = 1 := by + have hS : matrixSqrt A hA.posSemidef = A ^ (1 / 2 : ℝ) := by + simpa [matrixSqrt] using (matrixFunction_rpow_eq hA.posSemidef (1 / 2 : ℝ)) + have hSi : matrixInvSqrt A hA = A ^ (-1 / 2 : ℝ) := by + simpa [matrixInvSqrt] using (matrixFunction_rpow_eq hA.posSemidef (-1 / 2 : ℝ)) + have hAunit : IsUnit A := hA.isUnit + have hnonneg : 0 ≤ A := by + simpa [Matrix.le_iff] using hA.posSemidef + calc + matrixInvSqrt A hA * matrixSqrt A hA.posSemidef = + A ^ (-1 / 2 : ℝ) * A ^ (1 / 2 : ℝ) := by + simp [hS, hSi] + _ = A ^ ((-1 / 2 : ℝ) + (1 / 2 : ℝ)) := by + symm + simpa using (CFC.rpow_add (a := A) (x := (-1 / 2 : ℝ)) (y := (1 / 2 : ℝ)) hAunit) + _ = 1 := by + ring_nf + simpa using (CFC.rpow_zero (a := A) hnonneg) + +/-- For commuting PSD L and PD R, matrixInvSqrt R commutes with L. +This follows from the fact that L commutes with R, and CFC (hence rpow) preserves +commutativity. Since matrixInvSqrt R = R^{-1/2} (by matrixFunction_rpow_eq), and +Commute.cfc_real gives that cfc g R commutes with L when L commutes with R, +the result follows. -/ +lemma matrixInvSqrt_commute_of_commute {n : Type*} [Fintype n] [DecidableEq n] + {L R : Matrix n n ℂ} (_hL : L.PosSemidef) (hR : R.PosDef) + (hcomm : L * R = R * L) : + matrixInvSqrt R hR * L = L * matrixInvSqrt R hR := by + letI : NormedRing (Matrix n n ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix n n ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := n) (A := ℂ) + have hRinv_eq : matrixInvSqrt R hR = R ^ (-1 / 2 : ℝ) := by + simpa [matrixInvSqrt] using matrixFunction_rpow_eq hR.posSemidef (-1 / 2 : ℝ) + rw [hRinv_eq] + -- R^{-1/2} = cfc(x^{-1/2}, R), so it commutes with L since L commutes with R + have hR0 : (0 : Matrix n n ℂ) ≤ R := by simpa [Matrix.le_iff] using hR.posSemidef + rw [CFC.rpow_eq_cfc_real (a := R) (ha := hR0)] + have hcommute : Commute R L := hcomm.symm + exact Commute.cfc_real hcommute _ + +/-- For a PSD matrix `A`, `(A^{1/2})ᴴ * A^{1/2} = A` (since the square root is Hermitian). -/ +lemma matrixSqrt_conjTranspose_mul_self_posSemidef {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) : + (matrixSqrt A hA)ᴴ * matrixSqrt A hA = A := by + have hherm : (matrixSqrt A hA).IsHermitian := matrixSqrt_isHermitian hA + calc + (matrixSqrt A hA)ᴴ * matrixSqrt A hA = matrixSqrt A hA * matrixSqrt A hA := by + simp [hherm.eq] + _ = A := matrixSqrt_mul_self_posSemidef hA + +/-- At p = 0: `matrixFunction (fun x => x ^ 0) A = I`. -/ +lemma matrixFunction_cpow_zero {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) : + matrixFunction (fun x => x ^ (0 : ℂ)) A hA = 1 := by + unfold matrixFunction + have h_pow : (fun i => ((hA.eigenvalues i : ℝ) : ℂ) ^ (0 : ℂ)) = (fun _ => 1) := by + ext i + simp [Complex.cpow_zero] + simp only [h_pow, Matrix.diagonal_one, Matrix.mul_one] + have h := Unitary.coe_mul_star_self hA.eigenvectorUnitary + simp only [Unitary.coe_star, star_eq_conjTranspose] at h + exact h + +/-- `matrixFunction (fun x => x ^ 1) A = A`. -/ +lemma matrixFunction_cpow_one {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) (hA : A.IsHermitian) : + matrixFunction (fun x => x ^ (1 : ℂ)) A hA = A := by + unfold matrixFunction + have h_pow : (fun i => ((hA.eigenvalues i : ℝ) : ℂ) ^ (1 : ℂ)) = + (fun i => ((hA.eigenvalues i : ℝ) : ℂ)) := by + ext i + simp [Complex.cpow_one] + have h_diag : diagonal (fun i => ((hA.eigenvalues i : ℝ) : ℂ)) = + diagonal (RCLike.ofReal ∘ hA.eigenvalues) := rfl + simp only [h_pow, h_diag] + have h_spec := hA.spectral_theorem + rw [Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] at h_spec + exact h_spec.symm + +/-- CFC commutes with unitary conjugation using `Unitary.conjStarAlgAut`. -/ +lemma cfc_unitary_conjugation' {m : Type*} [Fintype m] [DecidableEq m] + (U : unitary (Matrix m m ℂ)) (M : Matrix m m ℂ) + (hM : IsSelfAdjoint M) (f : ℝ → ℝ) (hf : ContinuousOn f (spectrum ℝ M)) : + (U : Matrix m m ℂ) * cfc f M * star (U : Matrix m m ℂ) = + cfc f ((U : Matrix m m ℂ) * M * star (U : Matrix m m ℂ)) := by + change (Unitary.conjStarAlgAut ℝ _ U) (cfc f M) = + cfc f ((Unitary.conjStarAlgAut ℝ _ U) M) + have hcont : Continuous (Unitary.conjStarAlgAut ℝ (Matrix m m ℂ) U) := + (Unitary.conjStarAlgAut ℝ (Matrix m m ℂ) U).toAlgEquiv.toLinearMap.continuous_of_finiteDimensional + exact StarAlgHomClass.map_cfc (Unitary.conjStarAlgAut ℝ _ U) f M hf hcont hM + +/-- Block diagonal embedding as a star algebra homomorphism. +Maps (A, D) ↦ fromBlocks(A, 0, 0, D). -/ +noncomputable def blockDiagEmbed (m : Type*) [Fintype m] [DecidableEq m] : + (Matrix m m ℂ × Matrix m m ℂ) →⋆ₐ[ℝ] Matrix (m ⊕ m) (m ⊕ m) ℂ where + toFun p := fromBlocks p.1 0 0 p.2 + map_one' := fromBlocks_one + map_mul' p q := by simp [fromBlocks_multiply] + map_zero' := by simp [fromBlocks_zero] + map_add' p q := by simp [fromBlocks_add] + commutes' r := by + simp only [Algebra.algebraMap_eq_smul_one] + ext (i | i) (j | j) <;> simp [fromBlocks, Matrix.one_apply, Sum.inl.injEq, Sum.inr.injEq] + map_star' p := by + simp [star_eq_conjTranspose, fromBlocks_conjTranspose, Prod.star_def] + +/-- CFC of a block diagonal matrix equals the block diagonal of CFC of the blocks. + f(A ⊕ D) = f(A) ⊕ f(D) -/ +lemma cfc_fromBlocks_diag {m : Type*} [Fintype m] [DecidableEq m] + (A D : Matrix m m ℂ) (hA : IsSelfAdjoint A) + (hD : IsSelfAdjoint D) (f : ℝ → ℝ) + (hf : ContinuousOn f (spectrum ℝ A ∪ spectrum ℝ D)) : + cfc f (fromBlocks A 0 0 D) = fromBlocks (cfc f A) 0 0 (cfc f D) := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + have hcont : Continuous (blockDiagEmbed m) := by + change Continuous fun p : Matrix m m ℂ × Matrix m m ℂ => fromBlocks p.1 0 0 p.2 + fun_prop + have hAD : IsSelfAdjoint (A, D) := by + rw [IsSelfAdjoint, Prod.star_def] + exact Prod.ext hA.star_eq hD.star_eq + have h_map := StarAlgHom.map_cfc (blockDiagEmbed m) f (A, D) (by + rwa [Prod.spectrum_eq]) hcont hAD + have h_prod := cfc_map_prod (S := ℝ) f A D hf hAD hA hD + rw [h_prod] at h_map + exact h_map.symm + +/-- Block diagonal embedding for different-dimension blocks as a star algebra homomorphism. +Maps (A, D) ↦ fromBlocks(A, 0, 0, D) where A : n×n and D : m×m. -/ +noncomputable def blockDiagEmbed' (n m : Type*) [Fintype n] [DecidableEq n] [Fintype m] [DecidableEq m] : + (Matrix n n ℂ × Matrix m m ℂ) →⋆ₐ[ℝ] Matrix (n ⊕ m) (n ⊕ m) ℂ where + toFun p := fromBlocks p.1 0 0 p.2 + map_one' := fromBlocks_one + map_mul' p q := by simp [fromBlocks_multiply] + map_zero' := by simp [fromBlocks_zero] + map_add' p q := by simp [fromBlocks_add] + commutes' r := by + simp only [Algebra.algebraMap_eq_smul_one] + ext (i | i) (j | j) <;> simp [fromBlocks, Matrix.one_apply, Sum.inl.injEq, Sum.inr.injEq] + map_star' p := by + simp [star_eq_conjTranspose, fromBlocks_conjTranspose, Prod.star_def] + +/-- CFC of a block diagonal matrix (different dimensions) equals the block diagonal of CFC. + f(A ⊕ D) = f(A) ⊕ f(D) where A : n×n and D : m×m. -/ +lemma cfc_fromBlocks_diag' {n m : Type*} [Fintype n] [DecidableEq n] [Fintype m] [DecidableEq m] + (A : Matrix n n ℂ) (D : Matrix m m ℂ) (hA : IsSelfAdjoint A) + (hD : IsSelfAdjoint D) (f : ℝ → ℝ) + (hf : ContinuousOn f (spectrum ℝ A ∪ spectrum ℝ D)) : + cfc f (fromBlocks A 0 0 D) = fromBlocks (cfc f A) 0 0 (cfc f D) := by + letI : NormedRing (Matrix n n ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix n n ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := n) (A := ℂ) + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + have hcont : Continuous (blockDiagEmbed' n m) := by + change Continuous fun p : Matrix n n ℂ × Matrix m m ℂ => fromBlocks p.1 0 0 p.2 + fun_prop + have hAD : IsSelfAdjoint (A, D) := by + rw [IsSelfAdjoint, Prod.star_def] + exact Prod.ext hA.star_eq hD.star_eq + have h_map := StarAlgHom.map_cfc (blockDiagEmbed' n m) f (A, D) (by + rwa [Prod.spectrum_eq]) hcont hAD + have h_prod := cfc_map_prod (S := ℝ) f A D hf hAD hA hD + rw [h_prod] at h_map + exact h_map.symm + +/-! ### Matrix Convexity Implies Jensen Convexity + +The equivalence between Löwner convexity and Löwner convexity (HPJ form) is a +classical result in matrix analysis. The standard proof uses the block diagonal +technique: embed the 2-term HPJ problem into a larger space using block matrices. + +Reference: Hansen-Pedersen (2003), "Jensen's Operator Inequality" -/ + +section JensenConvexity + +-- Helper: V†M^k V = (V†MV)^k when PM = MP and V†V = I +-- where P = VV†. +lemma compression_pow_eq {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + (V : Matrix n m ℂ) (M : Matrix n n ℂ) + (hVV : Vᴴ * V = (1 : Matrix m m ℂ)) + (hcomm : M * (V * Vᴴ) = V * Vᴴ * M) (k : ℕ) : + Vᴴ * M ^ k * V = (Vᴴ * M * V) ^ k := by + -- First establish: M^j commutes with VV† for all j + have hcommk : ∀ j : ℕ, M ^ j * (V * Vᴴ) = V * Vᴴ * M ^ j := by + intro j; induction j with + | zero => simp [pow_zero] + | succ j ihj => + rw [pow_succ, Matrix.mul_assoc, hcomm, ← Matrix.mul_assoc, ihj, + Matrix.mul_assoc, ← pow_succ] + -- Key: V†Q = 0 where Q = I - VV† + have hVQ : Vᴴ * ((1 : Matrix n n ℂ) - V * Vᴴ) = 0 := by + rw [Matrix.mul_sub, Matrix.mul_one] + rw [show Vᴴ * (V * Vᴴ) = (Vᴴ * V) * Vᴴ from (Matrix.mul_assoc _ _ _).symm] + rw [hVV, Matrix.one_mul, sub_self] + -- V†M^k(I - VV†) = 0 + have hV_Mk_Q : ∀ j : ℕ, Vᴴ * M ^ j * ((1 : Matrix n n ℂ) - V * Vᴴ) = 0 := by + intro j + -- M^j(I - VV†) = (I - VV†)M^j (since M^j commutes with VV†) + have h_comm_q : M ^ j * ((1 : Matrix n n ℂ) - V * Vᴴ) = + ((1 : Matrix n n ℂ) - V * Vᴴ) * M ^ j := by + rw [Matrix.mul_sub, Matrix.sub_mul, Matrix.mul_one, Matrix.one_mul, hcommk j] + rw [Matrix.mul_assoc, h_comm_q, ← Matrix.mul_assoc, hVQ, Matrix.zero_mul] + -- Main proof by induction + induction k with + | zero => simp [pow_zero, Matrix.mul_one, hVV] + | succ k ih => + -- pow_succ: M^{k+1} = M^k * M + rw [pow_succ, pow_succ] + -- V†(M^k * M)V = (V†M^kV)(V†MV) = (V†MV)^k * (V†MV) + -- Key: V†M^k = V†M^k(VV†) because V†M^k(I-VV†) = 0 + -- So V†M^k * M * V = V†M^k * VV† * M * V = (V†M^kV)(V†MV) + have hstep : Vᴴ * M ^ k * (V * Vᴴ) = Vᴴ * M ^ k := by + have := hV_Mk_Q k + rw [Matrix.mul_sub, Matrix.mul_one] at this + exact (sub_eq_zero.mp this).symm + calc + Vᴴ * (M ^ k * M) * V + = Vᴴ * M ^ k * (M * V) := by simp only [Matrix.mul_assoc] + _ = Vᴴ * M ^ k * (V * Vᴴ) * (M * V) := by rw [hstep] + _ = Vᴴ * M ^ k * V * (Vᴴ * M * V) := by simp only [Matrix.mul_assoc] + _ = (Vᴴ * M * V) ^ k * (Vᴴ * M * V) := by rw [ih] + +-- Helper: matrixFunction f M can be expressed as a polynomial in M +-- (specifically, the Lagrange interpolant at the eigenvalues). +-- Hence V†f(M)V = f(V†MV) when V†M^kV = (V†MV)^k. + +/-- Compression commutes with polynomial evaluation when V†V = 1 and M commutes with VV†. -/ +lemma compression_aeval_eq {n m : Type*} + [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + (V : Matrix n m ℂ) (M : Matrix n n ℂ) + (hVV : Vᴴ * V = (1 : Matrix m m ℂ)) + (hcomm : M * (V * Vᴴ) = V * Vᴴ * M) (p : Polynomial ℂ) : + Vᴴ * (Polynomial.aeval M p) * V = Polynomial.aeval (Vᴴ * M * V) p := by + classical + induction p using Polynomial.induction_on' with + | add p q hp hq => + simp only [Polynomial.aeval_add, Matrix.mul_add, Matrix.add_mul] + rw [hp, hq] + | monomial k c => + simp only [Polynomial.aeval_monomial] + -- V†((algebraMap c) * M^k)V = (algebraMap c) * (V†MV)^k + have h : Vᴴ * ((algebraMap ℂ (Matrix n n ℂ)) c * M ^ k) * V = + (algebraMap ℂ (Matrix m m ℂ)) c * (Vᴴ * M * V) ^ k := by + rw [Algebra.algebraMap_eq_smul_one, Algebra.algebraMap_eq_smul_one] + simp only [Matrix.smul_mul, Matrix.mul_smul, Matrix.one_mul] + congr 1 + exact compression_pow_eq V M hVV hcomm k + exact h + +/-- Eigenvalues of V†MV are contained in eigenvalues of M when M commutes with VV†. +This follows from the spectrum inclusion spectrum(V†MV) ⊆ spectrum(M). -/ +lemma eigenvalues_compression_subset {n m : Type*} + [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + (V : Matrix n m ℂ) (M : Matrix n n ℂ) (hM : M.IsHermitian) + (hVV : Vᴴ * V = (1 : Matrix m m ℂ)) + (hcomm : M * (V * Vᴴ) = V * Vᴴ * M) + (hVM : (Vᴴ * M * V).IsHermitian) : + Set.range hVM.eigenvalues ⊆ Set.range hM.eigenvalues := by + rintro _ ⟨i, rfl⟩ + -- Use spectrum inclusion: spectrum(V†MV) ⊆ spectrum(M) + -- This holds because if (λI - M) is invertible, then (λI - V†MV) = V†(λI - M)V + -- is also invertible with inverse V†(λI - M)⁻¹V (using commutativity) + have h_spec_subset : spectrum ℂ (Vᴴ * M * V) ⊆ spectrum ℂ M := by + intro lam hlam + by_contra h_not_spec + rw [spectrum.mem_iff] at h_not_spec hlam + push_neg at h_not_spec + -- h_not_spec : IsUnit (algebraMap ℂ (Matrix n n ℂ) lam - M) + -- The compression V†(λI - M)V = (λI - V†MV) and inverse transfers + -- This is standard linear algebra: V†AV invertible iff A restricted to range(V) is invertible + -- For matrices with V†V = I and M commuting with VV†, invertibility transfers + -- Technical proof uses: (V†AV)⁻¹ = V†A⁻¹V when A commutes with VV† + apply hlam + -- Construct the inverse for V†MV + set A := algebraMap ℂ (Matrix n n ℂ) lam - M with hA_def + -- h_not_spec : IsUnit A + -- Build the unit for the compression + refine ⟨⟨Vᴴ * A * V, Vᴴ * A⁻¹ * V, ?_, ?_⟩, ?_⟩ + · -- mul_inv: (V†AV)(V†A⁻¹V) = 1 + have hcomm_A : A * (V * Vᴴ) = (V * Vᴴ) * A := by + simp only [hA_def, Algebra.algebraMap_eq_smul_one, sub_mul, mul_sub, + Matrix.smul_mul, Matrix.mul_smul, Matrix.one_mul, Matrix.mul_one, hcomm] + have h_inv : A * A⁻¹ = 1 := Matrix.mul_nonsing_inv A (A.isUnit_iff_isUnit_det.mp h_not_spec) + have h_inv' : A⁻¹ * A = 1 := Matrix.nonsing_inv_mul A (A.isUnit_iff_isUnit_det.mp h_not_spec) + have hcomm_inv : A⁻¹ * (V * Vᴴ) = (V * Vᴴ) * A⁻¹ := by + calc A⁻¹ * (V * Vᴴ) + = A⁻¹ * (V * Vᴴ) * (A * A⁻¹) := by rw [h_inv, Matrix.mul_one] + _ = (A⁻¹ * (V * Vᴴ) * A) * A⁻¹ := by simp only [Matrix.mul_assoc] + _ = (A⁻¹ * (A * (V * Vᴴ))) * A⁻¹ := by rw [hcomm_A]; simp only [Matrix.mul_assoc] + _ = ((A⁻¹ * A) * (V * Vᴴ)) * A⁻¹ := by simp only [Matrix.mul_assoc] + _ = (V * Vᴴ) * A⁻¹ := by rw [h_inv', Matrix.one_mul] + calc Vᴴ * A * V * (Vᴴ * A⁻¹ * V) + = Vᴴ * (A * (V * Vᴴ) * A⁻¹) * V := by simp only [Matrix.mul_assoc] + _ = Vᴴ * ((V * Vᴴ) * A * A⁻¹) * V := by rw [hcomm_A] + _ = Vᴴ * (V * Vᴴ) * V := by rw [Matrix.mul_assoc (V * Vᴴ), h_inv, Matrix.mul_one] + _ = (Vᴴ * V) * (Vᴴ * V) := by simp only [Matrix.mul_assoc] + _ = 1 := by rw [hVV, Matrix.mul_one] + · -- inv_mul: (V†A⁻¹V)(V†AV) = 1 + have hcomm_A : A * (V * Vᴴ) = (V * Vᴴ) * A := by + simp only [hA_def, Algebra.algebraMap_eq_smul_one, sub_mul, mul_sub, + Matrix.smul_mul, Matrix.mul_smul, Matrix.one_mul, Matrix.mul_one, hcomm] + have h_inv' : A⁻¹ * A = 1 := Matrix.nonsing_inv_mul A (A.isUnit_iff_isUnit_det.mp h_not_spec) + have h_inv : A * A⁻¹ = 1 := Matrix.mul_nonsing_inv A (A.isUnit_iff_isUnit_det.mp h_not_spec) + have hcomm_inv : A⁻¹ * (V * Vᴴ) = (V * Vᴴ) * A⁻¹ := by + calc A⁻¹ * (V * Vᴴ) + = A⁻¹ * (V * Vᴴ) * (A * A⁻¹) := by rw [h_inv, Matrix.mul_one] + _ = (A⁻¹ * (V * Vᴴ) * A) * A⁻¹ := by simp only [Matrix.mul_assoc] + _ = (A⁻¹ * (A * (V * Vᴴ))) * A⁻¹ := by rw [hcomm_A]; simp only [Matrix.mul_assoc] + _ = ((A⁻¹ * A) * (V * Vᴴ)) * A⁻¹ := by simp only [Matrix.mul_assoc] + _ = (V * Vᴴ) * A⁻¹ := by rw [h_inv', Matrix.one_mul] + calc Vᴴ * A⁻¹ * V * (Vᴴ * A * V) + = Vᴴ * (A⁻¹ * (V * Vᴴ) * A) * V := by simp only [Matrix.mul_assoc] + _ = Vᴴ * ((V * Vᴴ) * A⁻¹ * A) * V := by rw [hcomm_inv] + _ = Vᴴ * (V * Vᴴ) * V := by rw [Matrix.mul_assoc (V * Vᴴ), h_inv', Matrix.mul_one] + _ = (Vᴴ * V) * (Vᴴ * V) := by simp only [Matrix.mul_assoc] + _ = 1 := by rw [hVV, Matrix.mul_one] + · -- Show val equals the compression + simp only [Algebra.algebraMap_eq_smul_one, hA_def] + -- Goal: Vᴴ * (lam • 1 - M) * V = lam • 1 - Vᴴ * M * V + rw [Matrix.mul_sub, Matrix.sub_mul] + -- Goal: Vᴴ * (lam • 1) * V - Vᴴ * M * V = lam • 1 - Vᴴ * M * V + congr 1 + -- Goal: Vᴴ * (lam • 1) * V = lam • 1 + simp only [Matrix.mul_smul, Matrix.smul_mul, Matrix.mul_one, hVV] + -- Now show that eigenvalue of V†MV is an eigenvalue of M + have h_eigenvalue_in_spec : (hVM.eigenvalues i : ℂ) ∈ spectrum ℂ (Vᴴ * M * V) := + spectrum.of_algebraMap_mem ℂ (hVM.eigenvalues_mem_spectrum_real i) + have h_in_M_spec := h_spec_subset h_eigenvalue_in_spec + -- Use that spectrum ℂ M = Set.image (↑·) (Set.range hM.eigenvalues) + rw [hM.spectrum_eq_image_range] at h_in_M_spec + -- Extract the real eigenvalue from the image + simp only [Set.mem_image, Set.mem_range] at h_in_M_spec + obtain ⟨r, ⟨j, rfl⟩, hr⟩ := h_in_M_spec + use j + exact Complex.ofReal_injective hr + +lemma matrixFunction_compression_of_commuting {n m : Type*} + [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + (V : Matrix n m ℂ) (M : Matrix n n ℂ) (hM : M.IsHermitian) + (hVV : Vᴴ * V = (1 : Matrix m m ℂ)) + (hcomm : M * (V * Vᴴ) = V * Vᴴ * M) (f : ℝ → ℝ) + (hVM : (Vᴴ * M * V).IsHermitian) : + Vᴴ * matrixFunction (fun x => (f x : ℂ)) M hM * V = + matrixFunction (fun x => (f x : ℂ)) (Vᴴ * M * V) hVM := by + classical + -- The key insight: eigenvalues of V†MV are among eigenvalues of M + have h_eig_subset := eigenvalues_compression_subset V M hM hVV hcomm hVM + -- Construct the DISTINCT eigenvalues of M as a Finset + let distinct_eigs_M : Finset ℝ := Finset.image hM.eigenvalues Finset.univ + -- Construct a polynomial that interpolates f on DISTINCT eigenvalues of M + let p : Polynomial ℝ := Lagrange.interpolate distinct_eigs_M id (fun x => f x) + -- Key property: p evaluates to f at each eigenvalue of M + have hp_interp_M : ∀ i : n, p.eval (hM.eigenvalues i) = f (hM.eigenvalues i) := by + intro i + have h_mem : hM.eigenvalues i ∈ distinct_eigs_M := Finset.mem_image.mpr ⟨i, Finset.mem_univ i, rfl⟩ + have h_inj : Set.InjOn id (distinct_eigs_M : Set ℝ) := fun _ _ _ _ h => h + have := Lagrange.eval_interpolate_at_node (fun x => f x) h_inj h_mem + simp only [id] at this + exact this + -- p also evaluates to f at each eigenvalue of V†MV (since they're in range of M's eigenvalues) + have hp_interp_VM : ∀ i : m, p.eval (hVM.eigenvalues i) = f (hVM.eigenvalues i) := by + intro i + have h_in_range : hVM.eigenvalues i ∈ Set.range hM.eigenvalues := by + apply h_eig_subset + exact Set.mem_range_self i + obtain ⟨j, hj⟩ := h_in_range + rw [← hj] + exact hp_interp_M j + -- The complex version of the polynomial (mapping coefficients ℝ → ℂ) + let p_complex : Polynomial ℂ := p.map (algebraMap ℝ ℂ) + -- Show that p_complex.eval agrees with f on eigenvalues (lifted to ℂ) + have hp_eval_M : ∀ i : n, p_complex.eval (hM.eigenvalues i : ℂ) = (f (hM.eigenvalues i) : ℂ) := by + intro i + simp only [p_complex, Polynomial.eval_map] + have h1 : (hM.eigenvalues i : ℂ) = algebraMap ℝ ℂ (hM.eigenvalues i) := rfl + rw [h1, ← Polynomial.aeval_def, Polynomial.aeval_algebraMap_apply_eq_algebraMap_eval, + hp_interp_M i] + rfl + have hp_eval_VM : ∀ i : m, p_complex.eval (hVM.eigenvalues i : ℂ) = (f (hVM.eigenvalues i) : ℂ) := by + intro i + simp only [p_complex, Polynomial.eval_map] + have h1 : (hVM.eigenvalues i : ℂ) = algebraMap ℝ ℂ (hVM.eigenvalues i) := rfl + rw [h1, ← Polynomial.aeval_def, Polynomial.aeval_algebraMap_apply_eq_algebraMap_eval, + hp_interp_VM i] + rfl + unfold matrixFunction + have h_mf_M_eq_cfc : matrixFunction (fun x => (f x : ℂ)) M hM = cfc f M := + matrixFunction_eq_cfc hM f + have h_mf_VM_eq_cfc : matrixFunction (fun x => (f x : ℂ)) (Vᴴ * M * V) hVM = cfc f (Vᴴ * M * V) := + matrixFunction_eq_cfc hVM f + have h_cfc_M : cfc f M = hM.cfc f := Matrix.IsHermitian.cfc_eq hM f + have h_cfc_VM : cfc f (Vᴴ * M * V) = hVM.cfc f := Matrix.IsHermitian.cfc_eq hVM f + have h_cfc_f_eq_p_M : hM.cfc f = hM.cfc (fun x => p.eval x) := by + unfold Matrix.IsHermitian.cfc + congr 1 + ext i j + simp only [diagonal_apply] + split_ifs with h + · subst h + simp only [Function.comp_apply] + rw [hp_interp_M i] + · rfl + have h_cfc_f_eq_p_VM : hVM.cfc f = hVM.cfc (fun x => p.eval x) := by + unfold Matrix.IsHermitian.cfc + congr 1 + ext i j + simp only [diagonal_apply] + split_ifs with h + · subst h + simp only [Function.comp_apply] + rw [hp_interp_VM i] + · rfl + have h_cfc_p_eq_aeval_M : cfc (fun x => p.eval x) M = Polynomial.aeval M p := by + have hM' : IsSelfAdjoint M := hM + exact cfc_polynomial p M + have h_cfc_p_eq_aeval_VM : cfc (fun x => p.eval x) (Vᴴ * M * V) = Polynomial.aeval (Vᴴ * M * V) p := by + have hVM' : IsSelfAdjoint (Vᴴ * M * V) := hVM + exact cfc_polynomial p (Vᴴ * M * V) + have h_compress_aeval : Vᴴ * Polynomial.aeval M p * V = Polynomial.aeval (Vᴴ * M * V) p := by + have h1 : Polynomial.aeval M p = Polynomial.aeval M p_complex := by + simp only [p_complex, Polynomial.aeval_map_algebraMap] + have h2 : Polynomial.aeval (Vᴴ * M * V) p = Polynomial.aeval (Vᴴ * M * V) p_complex := by + simp only [p_complex, Polynomial.aeval_map_algebraMap] + rw [h1, h2] + exact compression_aeval_eq V M hVV hcomm p_complex + calc Vᴴ * matrixFunction (fun x => (f x : ℂ)) M hM * V + = Vᴴ * cfc f M * V := by rw [h_mf_M_eq_cfc] + _ = Vᴴ * hM.cfc f * V := by rw [h_cfc_M] + _ = Vᴴ * hM.cfc (fun x => p.eval x) * V := by rw [h_cfc_f_eq_p_M] + _ = Vᴴ * cfc (fun x => p.eval x) M * V := by rw [← Matrix.IsHermitian.cfc_eq hM] + _ = Vᴴ * Polynomial.aeval M p * V := by rw [h_cfc_p_eq_aeval_M] + _ = Polynomial.aeval (Vᴴ * M * V) p := h_compress_aeval + _ = cfc (fun x => p.eval x) (Vᴴ * M * V) := by rw [← h_cfc_p_eq_aeval_VM] + _ = hVM.cfc (fun x => p.eval x) := by rw [Matrix.IsHermitian.cfc_eq hVM] + _ = hVM.cfc f := by rw [← h_cfc_f_eq_p_VM] + _ = cfc f (Vᴴ * M * V) := by rw [← h_cfc_VM] + _ = matrixFunction (fun x => (f x : ℂ)) (Vᴴ * M * V) hVM := by rw [← h_mf_VM_eq_cfc] + +/-- For an isometry V (V†V = I), PSD A, and s > 0: (VAV†)^s = V A^s V†. + +**Proof**: Uses `matrixFunction_compression_of_commuting` to get V†(VAV†)^s V = A^s, +then shows (VAV†)^s annihilates the complement (1 - VV†) via kernel preservation. -/ +lemma rpow_conj_isometry {n m : Type*} [Fintype n] [Fintype m] + [DecidableEq n] [DecidableEq m] + (V : Matrix m n ℂ) (hV : Vᴴ * V = 1) + (A : Matrix n n ℂ) (hA : A.PosSemidef) (s : ℝ) (hs : 0 < s) : + (V * A * Vᴴ) ^ s = V * (A ^ s) * Vᴴ := by + set M := V * A * Vᴴ with hM_def + set P := V * Vᴴ with hP_def + -- Step 1: V†MV = A + have hstep1 : Vᴴ * M * V = A := by + rw [hM_def, Matrix.mul_assoc V A Vᴴ, + ← Matrix.mul_assoc Vᴴ V (A * Vᴴ), hV, Matrix.one_mul, + Matrix.mul_assoc, hV, Matrix.mul_one] + have hM_psd : M.PosSemidef := by + rw [hM_def]; exact hA.mul_mul_conjTranspose_same V + -- Step 2: M commutes with P = VV† + have hcomm : M * P = P * M := by + rw [hM_def, hP_def] + conv_lhs => + rw [Matrix.mul_assoc (V * A) Vᴴ (V * Vᴴ), + ← Matrix.mul_assoc Vᴴ V Vᴴ, hV, Matrix.one_mul] + conv_rhs => + rw [← Matrix.mul_assoc (V * Vᴴ) (V * A) Vᴴ, + Matrix.mul_assoc V Vᴴ (V * A), + ← Matrix.mul_assoc Vᴴ V A, hV, Matrix.one_mul] + -- Step 3: V†(M^s)V = A^s + have hVM_herm : (Vᴴ * M * V).IsHermitian := by rw [hstep1]; exact hA.1 + have hVMA_rpow : Vᴴ * (M ^ s) * V = A ^ s := by + have h1 := matrixFunction_rpow_eq hM_psd s + have h2 := matrixFunction_compression_of_commuting V M hM_psd.1 hV hcomm (· ^ s) hVM_herm + rw [h1] at h2 + rw [h2] + have h3 : matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) (Vᴴ * M * V) hVM_herm = + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 := by + congr 1 + rw [h3, matrixFunction_rpow_eq hA] + -- Step 4: M * (1 - P) = 0 + have hM_annihilate : M * (1 - P) = 0 := by + rw [mul_sub, mul_one, hM_def, hP_def, + Matrix.mul_assoc (V * A) Vᴴ (V * Vᴴ), + ← Matrix.mul_assoc Vᴴ V Vᴴ, hV, Matrix.one_mul, sub_self] + -- Step 5: M^s * (1 - P) = 0 via kernel preservation + have hM_herm := hM_psd.1 + set U := hM_herm.eigenvectorUnitary with hU_def + set ev := hM_herm.eigenvalues with hev_def + have hev_nonneg : ∀ i, 0 ≤ ev i := hM_psd.eigenvalues_nonneg + have hMs_annihilate : M ^ s * (1 - P) = 0 := by + -- Spectral decomposition: M = U diag(ev) U† + have hspec : M = (U : Matrix m m ℂ) * + diagonal (fun i => (ev i : ℂ)) * (U : Matrix m m ℂ)ᴴ := by + rw [hM_herm.spectral_theorem (𝕜 := ℂ), Unitary.conjStarAlgAut_apply, + star_eq_conjTranspose]; rfl + have hev_nneg_cast : (0 : Matrix m m ℂ) ≤ diagonal (fun i => (ev i : ℂ)) := by + simpa [Matrix.le_iff] using (posSemidef_diagonal_iff.mpr + (fun i => Complex.zero_le_real.mpr (mod_cast hev_nonneg i))) + -- M^s = U diag(ev^s) U† + have hMs_spec : M ^ s = (U : Matrix m m ℂ) * + diagonal (fun i => ((ev i ^ s : ℝ) : ℂ)) * (U : Matrix m m ℂ)ᴴ := by + have hM'_nonneg : (0 : Matrix m m ℂ) ≤ + (U : Matrix m m ℂ) * diagonal (fun i => (ev i : ℂ)) * (U : Matrix m m ℂ)ᴴ := by + rw [← hspec]; simpa [Matrix.le_iff] using hM_psd + conv_lhs => rw [hspec] + rw [rpow_unitary_conj U.2 hs.le hev_nneg_cast hM'_nonneg, + diagonal_rpow ev hev_nonneg s hs.le] + have hUstarU : (U : Matrix m m ℂ)ᴴ * U = 1 := by + have := Unitary.coe_star_mul_self U + simp only [star_eq_conjTranspose] at this + exact this + -- Set Q := U† * (1 - P) + set Q := (U : Matrix m m ℂ)ᴴ * (1 - P) with hQ_def + -- From M * (1-P) = 0: U * D * U† * (1-P) = 0 + -- Left-multiply by U†: D * Q = 0 + have hDQ : diagonal (fun i => (ev i : ℂ)) * Q = 0 := by + have h1 : (U : Matrix m m ℂ) * (diagonal (fun i => (ev i : ℂ)) * Q) = 0 := by + simp only [hQ_def, ← Matrix.mul_assoc] + rw [show (U : Matrix m m ℂ) * diagonal (fun i => (ev i : ℂ)) * + (U : Matrix m m ℂ)ᴴ = M from hspec.symm] + exact hM_annihilate + have h2 := congr_arg ((U : Matrix m m ℂ)ᴴ * ·) h1 + simp only [← Matrix.mul_assoc, hUstarU, Matrix.one_mul, Matrix.mul_zero] at h2 + exact h2 + -- Entry-wise: ev_i * Q_{i,j} = 0 + have hDQ_entry : ∀ i j, (ev i : ℂ) * Q i j = 0 := by + intro i j + have := congr_fun (congr_fun hDQ i) j + simp only [Matrix.mul_apply, diagonal_apply, ite_mul, zero_mul, + Matrix.zero_apply] at this + simpa using this + -- diag(ev^s) * Q = 0 (entry-wise: ev_i^s * Q_{i,j} = 0) + have hDsQ : diagonal (fun i => ((ev i ^ s : ℝ) : ℂ)) * Q = 0 := by + ext i j + have : (∑ x : m, if i = x then ↑(ev i ^ s) * Q x j else 0) = ↑(ev i ^ s) * Q i j := by + simp + simp only [Matrix.mul_apply, diagonal_apply, ite_mul, zero_mul, Matrix.zero_apply] + rw [this] + rcases mul_eq_zero.mp (hDQ_entry i j) with h | h + · -- ev_i = 0 → ev_i^s = 0^s = 0 + have hevi_zero : ev i = 0 := by exact_mod_cast h + simp [hevi_zero, Real.zero_rpow (ne_of_gt hs)] + · -- Q_{i,j} = 0 + simp [h] + -- M^s * (1-P) = U * D_s * U† * (1-P) = U * (D_s * Q) = U * 0 = 0 + calc M ^ s * (1 - P) + = (U : Matrix m m ℂ) * (diagonal (fun i => ((ev i ^ s : ℝ) : ℂ)) * Q) := by + rw [hMs_spec, hQ_def]; simp only [Matrix.mul_assoc] + _ = (U : Matrix m m ℂ) * 0 := by rw [hDsQ] + _ = 0 := Matrix.mul_zero _ + -- Step 6: M^s = V * A^s * V† + have hP_herm : Pᴴ = P := by + simp [hP_def, Matrix.conjTranspose_mul, conjTranspose_conjTranspose] + have hMs_herm : (M ^ s).IsHermitian := by + rw [← matrixFunction_rpow_eq hM_psd] + exact matrixFunction_isHermitian hM_psd.1 (· ^ s) + -- M^s = M^s * P (from M^s*(1-P)=0) + have hMsP_eq : M ^ s = M ^ s * P := by + have h := hMs_annihilate + rw [mul_sub, mul_one] at h + exact sub_eq_zero.mp h + -- P * M^s = M^s (from (1-P)*M^s = 0 via adjoint) + have hPMs : P * M ^ s = M ^ s := by + have h1 : (1 - P) * M ^ s = 0 := by + have h2 : ((M ^ s) * (1 - P))ᴴ = (0 : Matrix m m ℂ)ᴴ := congr_arg _ hMs_annihilate + rw [Matrix.conjTranspose_mul, Matrix.conjTranspose_sub, Matrix.conjTranspose_one] at h2 + simp only [Matrix.conjTranspose_zero] at h2 + rw [hP_herm, hMs_herm.eq] at h2; exact h2 + rw [sub_mul, one_mul] at h1 + exact (sub_eq_zero.mp h1).symm + -- M^s * V = V * A^s + have hMsV : M ^ s * V = V * A ^ s := by + conv_lhs => rw [← hPMs, show P = V * Vᴴ from hP_def] + simp only [Matrix.mul_assoc] + congr 1 + rw [← Matrix.mul_assoc] + exact hVMA_rpow + -- Conclusion: M^s = M^s * V * V† = V * A^s * V† + rw [hMsP_eq, show P = V * Vᴴ from hP_def, ← Matrix.mul_assoc, hMsV] + +end JensenConvexity + +/-! ### Spectral Decomposition Identities -/ + +section SpectralIdentities + +variable {n : Type*} [Fintype n] [DecidableEq n] + +/-- Uᴴ * U = 1 for the eigenvector unitary of a Hermitian matrix. -/ +lemma UHU_eq_one (A : Matrix n n ℂ) (hA : A.IsHermitian) : + (hA.eigenvectorUnitary : Matrix n n ℂ)ᴴ * + (hA.eigenvectorUnitary : Matrix n n ℂ) = 1 := by + have := Unitary.coe_star_mul_self hA.eigenvectorUnitary + simpa [star_eq_conjTranspose] using this + +/-- U * Uᴴ = 1 for the eigenvector unitary of a Hermitian matrix. -/ +lemma UUH_eq_one (A : Matrix n n ℂ) (hA : A.IsHermitian) : + (hA.eigenvectorUnitary : Matrix n n ℂ) * + (hA.eigenvectorUnitary : Matrix n n ℂ)ᴴ = 1 := by + have := Unitary.coe_mul_star_self hA.eigenvectorUnitary + simpa [star_eq_conjTranspose] using this + +/-- Spectral decomposition: A = U * diag(eigenvalues) * Uᴴ. -/ +lemma spectral_expand (A : Matrix n n ℂ) (hA : A.IsHermitian) : + A = (hA.eigenvectorUnitary : Matrix n n ℂ) * + diagonal (fun i => (hA.eigenvalues i : ℂ)) * + (hA.eigenvectorUnitary : Matrix n n ℂ)ᴴ := by + have h := (matrixFunction_id hA).symm + unfold matrixFunction at h + simpa [Function.comp] using h + +/-- The j-th column of the eigenvector unitary satisfies the eigenvalue equation: +A · (column j of U) = eigenvalue j · (column j of U). -/ +lemma mulVec_eigenvector_col (A : Matrix n n ℂ) (hA : A.IsHermitian) (j : n) : + A.mulVec (fun k => (hA.eigenvectorUnitary : Matrix n n ℂ) k j) = + fun k => (hA.eigenvalues j : ℂ) * (hA.eigenvectorUnitary : Matrix n n ℂ) k j := by + have h := hA.mulVec_eigenvectorBasis j + have hconv : (fun k => (hA.eigenvectorUnitary : Matrix n n ℂ) k j) = + (⇑(hA.eigenvectorBasis j) : n → ℂ) := by ext l; simp + rw [hconv] + ext k + have hk := congr_fun h k + simp only [Pi.smul_apply] at hk + rw [hk] + simp [Complex.real_smul] + +/-- For PSD `A`, `D` with `0 < p`, `(A ⊕ D)ᵖ = Aᵖ ⊕ Dᵖ`. -/ +lemma fromBlocks_diag_rpow {n₁ n₂ : Type*} + [Fintype n₁] [DecidableEq n₁] [Fintype n₂] [DecidableEq n₂] + {A : Matrix n₁ n₁ ℂ} (hA : A.PosSemidef) + {D : Matrix n₂ n₂ ℂ} (hD : D.PosSemidef) + {p : ℝ} (hp : 0 < p) : + (Matrix.fromBlocks A 0 0 D) ^ p = Matrix.fromBlocks (A ^ p) 0 0 (D ^ p) := by + have hha := fromBlocks_diag_posSemidef hA hD + rw [CFC.rpow_eq_cfc_real (a := fromBlocks A 0 0 D) + (ha := by rw [Matrix.le_iff, sub_zero]; exact hha)] + have hcfc : cfc (fun x : ℝ => x ^ p) (fromBlocks A 0 0 D) = + fromBlocks (cfc (fun x : ℝ => x ^ p) A) 0 0 (cfc (fun x : ℝ => x ^ p) D) := + cfc_fromBlocks_diag' A D hA.1 hD.1 _ + ((continuousOn_id.rpow_const fun _ _ => Or.inr hp.le)) + rw [hcfc, + ← CFC.rpow_eq_cfc_real (a := A) (ha := by rw [Matrix.le_iff, sub_zero]; exact hA), + ← CFC.rpow_eq_cfc_real (a := D) (ha := by rw [Matrix.le_iff, sub_zero]; exact hD)] + +end SpectralIdentities + +end Matrix diff --git a/QuantumSystem/Analysis/Matrix/LiebConcavity.lean b/QuantumSystem/Analysis/Matrix/LiebConcavity.lean new file mode 100644 index 0000000..5820f07 --- /dev/null +++ b/QuantumSystem/Analysis/Matrix/LiebConcavity.lean @@ -0,0 +1,967 @@ +module + +public import QuantumSystem.Analysis.Matrix.Effros +public import QuantumSystem.Notation +public import Mathlib.Analysis.CStarAlgebra.ContinuousFunctionalCalculus.Continuity + +/-! +# Lieb's Concavity Theorem via Effros's Matrix Convexity Approach + +This file provides definitions related to Lieb's concavity theorem and establishes it +via the Effros Löwner convexity approach (2008), which avoids complex interpolation. + +## Mathematical Background + +### Lieb's Joint Concavity Theorem (1973) +For 0 ≤ p ≤ 1 and a fixed matrix K, the map + (A, B) ↦ Tr(A^p K† B^{1-p} K) +is jointly concave on pairs of positive definite matrices. + +### Proof Strategy (Effros 2008) +The proof proceeds via: +1. **Matrix concavity of t^s**: For 0 < s ≤ 1, the map A ↦ A^s is Löwner concave + (equivalently, f(t) = -t^s is Löwner convex). This follows from Löwner-Heinz. +2. **Hansen-Pedersen-Jensen (HPJ) inequality**: For Löwner convex f and matrices + A, B with A*A + B*B = I: f(A*T₁A + B*T₂B) ≤ A*f(T₁)A + B*f(T₂)B. +3. **Matrix perspective**: The perspective g(L,R) = f(L/R)R for commuting positive + operators L, R is jointly convex when f is Löwner convex. +4. **Left/right multiplication**: For L(X) = AX and R(X) = XB (operators on M_n), + apply the perspective with f(t) = -t^s to obtain joint concavity of + (A,B) ↦ Tr(A^s K† B^{1-s} K). + +## References + +* Effros, *A Matrix Convexity Approach to Some Celebrated Quantum Inequalities* (2008) +* Lieb, *Convex trace functions and the Wigner-Yanase-Dyson conjecture* (1973) +-/ +@[expose] public section + +namespace Matrix + +open scoped MatrixOrder ComplexOrder + +/-- The Lieb joint function: Tr(Aᵖ K† B¹⁻ᵖ K) +for a (possibly rectangular) matrix K : m × n, A : n × n PSD, B : m × m PSD. +Uses CFC rpow (`A ^ p`) for positive semidefinite matrices. -/ +noncomputable def liebJointFunction {n m : Type*} [Fintype n] [DecidableEq n] + [Fintype m] [DecidableEq m] + (K : Matrix m n ℂ) (p : ℝ) + (A : Matrix n n ℂ) (_hA : A.PosSemidef) + (B : Matrix m m ℂ) (_hB : B.PosSemidef) : ℂ := + Tr ((A ^ p) * Kᴴ * (B ^ (1 - p)) * K) + +/-- liebJointFunction at p = 0 equals Tr(K†BK). -/ +lemma liebJointFunction_zero_eq {n m : Type*} [Fintype n] [DecidableEq n] + [Fintype m] [DecidableEq m] + (K : Matrix m n ℂ) + (A : Matrix n n ℂ) (hA : A.PosSemidef) + (B : Matrix m m ℂ) (hB : B.PosSemidef) : + liebJointFunction K 0 A hA B hB = Tr (Kᴴ * B * K) := by + simp only [liebJointFunction, sub_zero] + rw [CFC.rpow_zero A (by simpa [Matrix.le_iff] using hA), + CFC.rpow_one B (by simpa [Matrix.le_iff] using hB), Matrix.one_mul] + +/-- liebJointFunction at p = 1 equals Tr(AK†K). -/ +lemma liebJointFunction_one_eq {n m : Type*} [Fintype n] [DecidableEq n] + [Fintype m] [DecidableEq m] + (K : Matrix m n ℂ) + (A : Matrix n n ℂ) (hA : A.PosSemidef) + (B : Matrix m m ℂ) (hB : B.PosSemidef) : + liebJointFunction K 1 A hA B hB = Tr (A * Kᴴ * K) := by + simp only [liebJointFunction, sub_self] + rw [CFC.rpow_one A (by simpa [Matrix.le_iff] using hA), + CFC.rpow_zero B (by simpa [Matrix.le_iff] using hB), Matrix.mul_one] + +/-- Hilbert-Schmidt inner product: ⟨X, Y⟩_HS = Tr(X† Y). +Note: We use the physics convention ⟨X, Y⟩ = Tr(X† Y), which is conjugate-linear +in the first argument and linear in the second. -/ +noncomputable def hsInnerProduct {m : Type*} [Fintype m] + (X Y : Matrix m m ℂ) : ℂ := + (Xᴴ * Y).trace + +namespace QuantumInfo +scoped notation "⟪" X ", " Y "⟫_HS" => Matrix.hsInnerProduct X Y +end QuantumInfo + +/-- Hilbert-Schmidt inner product is related to liebJointFunction via left/right multiplication. +For positive semidefinite A, B and real p: + ⟨A^p · K† · B^{1-p}, K†⟩_HS = Tr(A^p · K† · B^{1-p} · K) +This connects the operator-level perspective to the trace-level Lieb function. -/ +private lemma hsInnerProduct_leftMul_rightMul {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) + (K : Matrix m m ℂ) (p : ℝ) : + hsInnerProduct ((A ^ p) * Kᴴ * (B ^ (1 - p))) Kᴴ = liebJointFunction K p A hA B hB := by + simp only [hsInnerProduct, liebJointFunction] + -- (A^p * K† * B^{1-p})† = B^{1-p}† * K * (A^p)† + -- Since A^p and B^{1-p} are Hermitian (rpow of PSD is PSD hence Hermitian): + have hAp_herm : (A ^ p)ᴴ = A ^ p := by + rw [← matrixFunction_rpow_eq hA p] + exact matrixFunction_isHermitian hA.1 (fun x => x ^ p) + have hBp_herm : (B ^ (1 - p))ᴴ = B ^ (1 - p) := by + rw [← matrixFunction_rpow_eq hB (1 - p)] + exact matrixFunction_isHermitian hB.1 (fun x => x ^ (1 - p)) + simp only [Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose, + hAp_herm, hBp_herm, Matrix.mul_assoc] + -- LHS: Tr(B^{1-p} * K * A^p * K†), RHS: Tr(A^p * K† * B^{1-p} * K) + -- By trace cyclicity (two shifts) these are equal + simp only [← Matrix.mul_assoc] + rw [trace_mul_cycle, trace_mul_cycle] + simp only [Matrix.mul_assoc] + +/-- The HS inner product ⟨v, matrixPerspective(f, L_A, R_B) v⟩ for f(t) = -t^p +and v = vec(K†) equals -Tr(A^p K† B^{1-p} K). + +This is the key spectral identity connecting the matrix perspective +on left/right multiplication operators to the Lieb joint function. +See Effros (2008), Corollary 2.4 (proof). + +The proof is technical but the key insight is: +- For commuting L = leftMulMatrix A and R = rightMulMatrix B, + the perspective matrixPerspective(f, L, R) with f(t) = -t^p + simplifies to -(L^p R^{1-p}) = -leftMulMatrix(A^p) * rightMulMatrix(B^{1-p}) +- The quadratic form ⟨vec(K†), L_X R_Y vec(K†)⟩ = Tr(X K† Y K) +- Combining: ⟨v, (-L^p R^{1-p}) v⟩ = -Tr(A^p K† B^{1-p} K) = -liebJointFunction + +For full generality this requires functional calculus on Kronecker products, +but the result follows from the trace identity hsInnerProduct_leftMul_rightMul +and the perspective structure. -/ +private lemma matrixPerspective_inner_eq_neg_liebJointFunction {m : Type*} [Fintype m] [DecidableEq m] + (K : Matrix m m ℂ) (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) + (A B : Matrix m m ℂ) (hA : A.PosDef) (hB : B.PosDef) + (hL_psd : (𝐋 A).PosSemidef) (hR_pd : (𝐑 B).PosDef) : + let v : (m × m) → ℂ := fun x => Kᴴ x.1 x.2 + (star v ⬝ᵥ (matrixPerspective (fun x => -(x ^ p)) + (𝐋 A) (𝐑 B) hL_psd hR_pd *ᵥ v)).re = + -(liebJointFunction K p A hA.posSemidef B hB.posSemidef).re := by + intro v + have hp1' : 0 ≤ 1 - p := by linarith + have h_persp : + matrixPerspective (fun x => -(x ^ p)) (𝐋 A) (𝐑 B) hL_psd hR_pd = + -(𝐋 (A ^ p) * 𝐑 (B ^ (1 - p))) := + Matrix.matrixPerspective_neg_leftRight_eq A B hA hB p hp hL_psd hR_pd + -- Compute the quadratic form + rw [h_persp, Matrix.neg_mulVec, dotProduct_neg, Complex.neg_re] + congr 1 + -- Use quadForm_leftRightMul_eq_trace + have h_quad := quadForm_leftRightMul_eq_trace (A ^ p) (B ^ (1 - p)) K + rw [h_quad] + -- Trace cyclicity: + -- quadForm gives trace(K * A^p * K† * B^{1-p}), which after h_quad becomes + -- trace(B^{1-p} * (K * A^p) * K†), need to show equals + -- trace(A^p * K† * B^{1-p} * K) from liebJointFunction. + congr 1 + -- Goal: (K * A^p * K† * B^{1-p}).trace = (A^p * K† * B^{1-p} * K).trace + rw [Matrix.mul_assoc (K * A ^ p) Kᴴ (B ^ (1 - p)), + Matrix.mul_assoc K (A ^ p) (Kᴴ * B ^ (1 - p)), + trace_mul_cycle' K (A ^ p) (Kᴴ * B ^ (1 - p))] + -- Now: (K† * B^{1-p} * (K * A^p)).trace = (A^p * K† * B^{1-p} * K).trace + rw [← Matrix.mul_assoc (Kᴴ * B ^ (1 - p)) K (A ^ p)] + -- Now: ((K†*B^{1-p}*K) * A^p).trace = (A^p * K†*B^{1-p}*K).trace + rw [trace_mul_comm (Kᴴ * B ^ (1 - p) * K) (A ^ p)] + -- Now: (A^p * (K†*B^{1-p}*K)).trace = (A^p * K†*B^{1-p}*K).trace + congr 1; rw [Matrix.mul_assoc (A ^ p) Kᴴ, Matrix.mul_assoc (A ^ p)] + +/-- **Lieb's Joint Concavity Theorem (Interior Case, Effros Proof)** + +The key is that the Lieb function Tr(A^p K† B^{1-p} K) equals the HS inner product +⟨A^p K† B^{1-p}, K†⟩, and operator concavity of x^p implies joint concavity via +the left/right multiplication operator structure. + +This proof uses the Effros approach: for Löwner convex f(t) = -t^p, the perspective +function g(L,R) = f(L/R)R is jointly convex for commuting operators L, R. +Applied to left/right multiplication operators L_A and R_B (which commute), the +HS inner product ⟨g(L_A, R_B)(K†), K†⟩ = -Tr(A^p K† B^{1-p} K) is jointly convex, +hence Tr(A^p K† B^{1-p} K) is jointly concave. -/ +private lemma lieb_concavity_effros {m : Type*} [Fintype m] [DecidableEq m] + (A₁ A₂ B₁ B₂ : Matrix m m ℂ) + (hA₁ : A₁.PosDef) (hA₂ : A₂.PosDef) (hB₁ : B₁.PosDef) (hB₂ : B₂.PosDef) + (K : Matrix m m ℂ) (p : ℝ) (hp0 : 0 < p) (hp1 : p < 1) + (w₁ w₂ : ℝ) (hw₁ : 0 ≤ w₁) (hw₂ : 0 ≤ w₂) (hw : w₁ + w₂ = 1) : + w₁ * (liebJointFunction K p A₁ hA₁.posSemidef B₁ hB₁.posSemidef).re + + w₂ * (liebJointFunction K p A₂ hA₂.posSemidef B₂ hB₂.posSemidef).re ≤ + (liebJointFunction K p + (w₁ • A₁ + w₂ • A₂) ((hA₁.posSemidef.smul hw₁).add (hA₂.posSemidef.smul hw₂)) + (w₁ • B₁ + w₂ • B₂) ((hB₁.posSemidef.smul hw₁).add (hB₂.posSemidef.smul hw₂))).re := by + classical + /- Proof by Effros's Matrix Perspective Approach -/ + -- 1. Setup the function f(x) = -x^p, which is Matrix Convex. + let f : ℝ → ℝ := fun x => -(x ^ p) + have hconv : IsJensenConvex f := neg_rpow_isJensenConvex hp0 (le_of_lt hp1) + -- 2. Define Left and Right multiplication operators + let L₁ := 𝐋 A₁ + let L₂ := 𝐋 A₂ + let R₁ := 𝐑 B₁ + let R₂ := 𝐑 B₂ + let L := w₁ • L₁ + w₂ • L₂ + let R := w₁ • R₁ + w₂ • R₂ + -- Properties of L and R + have hL₁_psd : L₁.PosSemidef := leftMulMatrix_posSemidef hA₁.posSemidef + have hL₂_psd : L₂.PosSemidef := leftMulMatrix_posSemidef hA₂.posSemidef + have hR₁_pd : R₁.PosDef := rightMulMatrix_posDef hB₁ + have hR₂_pd : R₂.PosDef := rightMulMatrix_posDef hB₂ + have hR_pd : R.PosDef := PosDef.convex_comb_nonneg hR₁_pd hR₂_pd hw₁ hw₂ hw + -- 3. Apply Joint Convexity of matrixPerspective + have h_jconv := matrixPerspective_joint_convex hconv hL₁_psd hL₂_psd hR₁_pd hR₂_pd hw₁ hw₂ hw + -- 4. Relate matrixPerspective to Lieb Function + -- g(L, R) = f(L R⁻¹) R = -(L R⁻¹)^p R = -L^p R^{1-p} (for commuting L, R) + -- Lieb(A, B) = Tr(A^p K† B^{1-p} K) = ⟨L_{A^p} R_{B^{1-p}} K†, K†⟩ + + -- Helper: matrixPerspective f L R = -L^p R^(1-p) + -- This requires commutativity L R = R L, which holds. + -- And functional calculus property on Kronecker product. + -- We assume the identity: ⟨matrixPerspective f L R K†, K†⟩_HS = -Lieb(A, B). + let term1 := matrixPerspective f L₁ R₁ hL₁_psd hR₁_pd + let term2 := matrixPerspective f L₂ R₂ hL₂_psd hR₂_pd + let term_comb := matrixPerspective f L R ((hL₁_psd.smul hw₁).add (hL₂_psd.smul hw₂)) hR_pd + -- The inequality is term_comb ≤ w₁ term1 + w₂ term2 + -- Apply ⟨· K†, K†⟩ which preserves order. + let v : (m × m) → ℂ := fun x => Kᴴ x.1 x.2 + have h_jconv_le := h_jconv + rw [Matrix.le_iff] at h_jconv_le + -- Use that (RHS - LHS) is PSD => ⟨v, (RHS - LHS) v⟩ ≥ 0 + have h_vec_nonneg := h_jconv_le.dotProduct_mulVec_nonneg v + -- Expand LHS linearity + simp only [Matrix.sub_mulVec, Matrix.add_mulVec, Matrix.smul_mulVec] at h_vec_nonneg + simp only [dotProduct_sub, dotProduct_add, dotProduct_smul] at h_vec_nonneg + -- Connect to Lieb function + -- Use the spectral identity: ⟨v, matrixPerspective(f, L_A, R_B) v⟩ = -liebJointFunction(K, p, A, B) + have h_ident1 : (star v ⬝ᵥ (term1 *ᵥ v)).re = -(liebJointFunction K p A₁ hA₁.posSemidef B₁ hB₁.posSemidef).re := by + simpa [term1, f] using + matrixPerspective_inner_eq_neg_liebJointFunction K p (le_of_lt hp0) (le_of_lt hp1) A₁ B₁ hA₁ hB₁ hL₁_psd hR₁_pd + have h_ident2 : (star v ⬝ᵥ (term2 *ᵥ v)).re = -(liebJointFunction K p A₂ hA₂.posSemidef B₂ hB₂.posSemidef).re := by + simpa [term2, f] using + matrixPerspective_inner_eq_neg_liebJointFunction K p (le_of_lt hp0) (le_of_lt hp1) A₂ B₂ hA₂ hB₂ hL₂_psd hR₂_pd + have hA_comb : (w₁ • A₁ + w₂ • A₂).PosDef := PosDef.convex_comb_nonneg hA₁ hA₂ hw₁ hw₂ hw + have hB_comb : (w₁ • B₁ + w₂ • B₂).PosDef := PosDef.convex_comb_nonneg hB₁ hB₂ hw₁ hw₂ hw + -- The combined identity follows from matrixPerspective_inner_eq_neg_liebJointFunction + -- applied to the convex combinations, after identifying L = leftMulMatrix(w₁A₁+w₂A₂) + -- and R = rightMulMatrix(w₁B₁+w₂B₂) via linearity of leftMulMatrix/rightMulMatrix. + have h_ident_comb : (star v ⬝ᵥ (term_comb *ᵥ v)).re = -(liebJointFunction K p + (w₁ • A₁ + w₂ • A₂) ((hA₁.posSemidef.smul hw₁).add (hA₂.posSemidef.smul hw₂)) + (w₁ • B₁ + w₂ • B₂) ((hB₁.posSemidef.smul hw₁).add (hB₂.posSemidef.smul hw₂))).re := by + have hLlin : L = 𝐋 (w₁ • A₁ + w₂ • A₂) := by + ext ij kl + rcases ij with ⟨i, j⟩ + rcases kl with ⟨k, l⟩ + by_cases h : j = l + · subst h + simp [L, L₁, L₂, leftMulMatrix_apply] + · simp [L, L₁, L₂, leftMulMatrix_apply, h] + have hRlin : R = 𝐑 (w₁ • B₁ + w₂ • B₂) := by + ext ij kl + rcases ij with ⟨i, j⟩ + rcases kl with ⟨k, l⟩ + by_cases h : i = k + · subst h + simp [R, R₁, R₂, rightMulMatrix_apply] + · simp [R, R₁, R₂, rightMulMatrix_apply, h] + have hR_pd' : (𝐑 (w₁ • B₁ + w₂ • B₂)).PosDef := by rw [← hRlin]; exact hR_pd + have hL_psd' : (𝐋 (w₁ • A₁ + w₂ • A₂)).PosSemidef := by + rw [← hLlin]; exact (hL₁_psd.smul hw₁).add (hL₂_psd.smul hw₂) + -- Apply matrixPerspective_inner_eq_neg_liebJointFunction to the convex combination + -- The key identity relates the HS inner product to the Lieb function. + -- After establishing that term_comb = matrixPerspective f (leftMulMatrix (w₁•A₁+w₂•A₂)) + -- (rightMulMatrix (w₁•B₁+w₂•B₂)) via the linearity hLlin and hRlin, + -- the result follows from the same spectral identity as h_ident1 and h_ident2. + -- The connection: L = leftMulMatrix (w₁ • A₁ + w₂ • A₂) and + -- R = rightMulMatrix (w₁ • B₁ + w₂ • B₂), so term_comb is the matrixPerspective + -- applied to these operators with the convex combination matrices. + -- The matrixPerspective_inner_eq_neg_liebJointFunction lemma gives us + -- the result for general positive definite matrices. + have h_apply := matrixPerspective_inner_eq_neg_liebJointFunction K p + (le_of_lt hp0) (le_of_lt hp1) + (w₁ • A₁ + w₂ • A₂) (w₁ • B₁ + w₂ • B₂) hA_comb hB_comb hL_psd' hR_pd' + -- The term_comb uses L and R which equal leftMulMatrix/rightMulMatrix of convex combs. + -- After substitution, the goal matches h_apply. + -- The matrices are definitionally equal after applying hLlin and hRlin. + -- term_comb = matrixPerspective f L R _ _ + -- = matrixPerspective f (leftMulMatrix (w₁•A₁+w₂•A₂)) (rightMulMatrix (w₁•B₁+w₂•B₂)) _ _ + -- The proof terms may differ but the matrices are equal by proof irrelevance. + -- Since both sides compute the same quadratic form value, they are equal. + -- We establish this by showing the matrixPerspective matrices are equal. + have hpersp_eq : term_comb = matrixPerspective f + (𝐋 (w₁ • A₁ + w₂ • A₂)) + (𝐑 (w₁ • B₁ + w₂ • B₂)) hL_psd' hR_pd' := by + simp only [term_comb, L, R, L₁, L₂, R₁, R₂] + exact matrixPerspective_congr f _ _ _ _ + _ hL_psd' _ hR_pd' + (by rw [leftMulMatrix_add, ← leftMulMatrix_smul_real, ← leftMulMatrix_smul_real]) + (by rw [rightMulMatrix_add, ← rightMulMatrix_smul_real, ← rightMulMatrix_smul_real]) + rw [hpersp_eq] + exact h_apply + -- Substitute identities into the nonnegativity inequality + have h_vec_re : + 0 ≤ + (w₁ • (star v ⬝ᵥ (term1 *ᵥ v)) + w₂ • (star v ⬝ᵥ (term2 *ᵥ v)) - + (star v ⬝ᵥ (term_comb *ᵥ v))).re := by + exact (Complex.nonneg_iff.mp h_vec_nonneg).1 + have h_vec_re' : + 0 ≤ + (w₁ * (star v ⬝ᵥ (term1 *ᵥ v)).re + w₂ * (star v ⬝ᵥ (term2 *ᵥ v)).re - + (star v ⬝ᵥ (term_comb *ᵥ v)).re) := by + simpa [Complex.add_re, Complex.sub_re, Complex.real_smul] using h_vec_re + rw [h_ident1, h_ident2, h_ident_comb] at h_vec_re' + linarith + + +/-- **Lieb's Joint Concavity Theorem (General Case)** -/ +private lemma lieb_joint_concavity {m : Type*} [Fintype m] [DecidableEq m] + (A₁ A₂ B₁ B₂ : Matrix m m ℂ) + (hA₁ : A₁.PosDef) (hA₂ : A₂.PosDef) (hB₁ : B₁.PosDef) (hB₂ : B₂.PosDef) + (K : Matrix m m ℂ) (p : ℝ) (hp0 : 0 ≤ p) (hp1 : p ≤ 1) + (w₁ w₂ : ℝ) (hw₁ : 0 ≤ w₁) (hw₂ : 0 ≤ w₂) (hw : w₁ + w₂ = 1) : + w₁ * (liebJointFunction K p A₁ hA₁.posSemidef B₁ hB₁.posSemidef).re + + w₂ * (liebJointFunction K p A₂ hA₂.posSemidef B₂ hB₂.posSemidef).re ≤ + (liebJointFunction K p + (w₁ • A₁ + w₂ • A₂) ((hA₁.posSemidef.smul hw₁).add (hA₂.posSemidef.smul hw₂)) + (w₁ • B₁ + w₂ • B₂) ((hB₁.posSemidef.smul hw₁).add (hB₂.posSemidef.smul hw₂))).re := by + -- Handle boundary cases p = 0 and p = 1 separately + rcases eq_or_lt_of_le hp0 with rfl | hp0' + · -- p = 0: Tr(K†BK) is linear in B, so equality holds + rw [liebJointFunction_zero_eq, liebJointFunction_zero_eq, liebJointFunction_zero_eq] + have h_linear : (Kᴴ * (w₁ • B₁ + w₂ • B₂) * K).trace = + (w₁ : ℂ) * (Kᴴ * B₁ * K).trace + (w₂ : ℂ) * (Kᴴ * B₂ * K).trace := by + rw [Matrix.mul_add, Matrix.add_mul] + rw [Matrix.mul_smul, Matrix.smul_mul, Matrix.mul_smul, Matrix.smul_mul] + rw [trace_add, trace_smul, trace_smul] + simp only [Complex.real_smul] + rw [h_linear] + simp only [Complex.add_re, Complex.mul_re, Complex.ofReal_re, Complex.ofReal_im, zero_mul, + sub_zero] + exact le_refl _ + rcases eq_or_lt_of_le hp1 with rfl | hp1' + · -- p = 1: Tr(AK†K) is linear in A, so equality holds + rw [liebJointFunction_one_eq, liebJointFunction_one_eq, liebJointFunction_one_eq] + have h_linear : ((w₁ • A₁ + w₂ • A₂) * Kᴴ * K).trace = + (w₁ : ℂ) * (A₁ * Kᴴ * K).trace + (w₂ : ℂ) * (A₂ * Kᴴ * K).trace := by + rw [Matrix.add_mul, Matrix.add_mul] + rw [Matrix.smul_mul, Matrix.smul_mul, Matrix.smul_mul, Matrix.smul_mul] + rw [trace_add, trace_smul, trace_smul] + simp only [Complex.real_smul] + rw [h_linear] + simp only [Complex.add_re, Complex.mul_re, Complex.ofReal_re, Complex.ofReal_im, zero_mul, + sub_zero] + exact le_refl _ + -- For 0 < p < 1, apply Effros's Löwner convexity approach + exact lieb_concavity_effros A₁ A₂ B₁ B₂ hA₁ hA₂ hB₁ hB₂ K p hp0' hp1' w₁ w₂ hw₁ hw₂ hw + +/-- For a positive semidefinite matrix A and real p, the map + ε ↦ (A + ε I)ᵖ converges to Aᵖ as ε → 0⁺. -/ +private lemma rpow_tendsto_smul_one {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) (p : ℝ) (hp : 0 ≤ p) : + Filter.Tendsto (fun ε : ℝ => (A + (ε : ℂ) • (1 : Matrix m m ℂ)) ^ p) + (nhdsWithin 0 (Set.Ioi 0)) (nhds (A ^ p)) := by + -- Express A^p and (A + ε•1)^p via the continuous functional calculus. + -- Using matrixFunction and cfc, reduce to pointwise convergence of x^p as ε → 0+. + have hA_eq : A ^ p = cfc (fun x : ℝ => x ^ p) A := by + rw [← matrixFunction_rpow_eq hA, matrixFunction_eq_cfc hA.1] + have hshift_eq : ∀ ε : ℝ, 0 < ε → (A + (ε : ℂ) • (1 : Matrix m m ℂ)) ^ p = + cfc (fun x : ℝ => (x + ε) ^ p) A := by + intro ε hε + have hcfc_shift : cfc (fun x : ℝ => x + ε) A = A + (ε : ℂ) • (1 : Matrix m m ℂ) := by + rw [← matrixFunction_eq_cfc hA.1]; exact matrixFunction_add_const hA.1 ε + have hcont_p : ContinuousOn (fun x : ℝ => x ^ p) ((fun x : ℝ => x + ε) '' spectrum ℝ A) := by + apply ContinuousOn.rpow_const continuousOn_id + rintro x ⟨_, hy_spec, rfl⟩ + rw [hA.1.spectrum_real_eq_range_eigenvalues] at hy_spec + obtain ⟨i, rfl⟩ := hy_spec + left; simp only [id, ne_eq]; linarith [hA.eigenvalues_nonneg i] + rw [CFC.rpow_eq_cfc_real (a := A + (ε : ℂ) • 1) + (ha := by simpa [Matrix.le_iff] using (hA.add_smul_one_posDef hε).posSemidef), + ← hcfc_shift, ← cfc_comp' (fun x => x ^ p) (fun x => x + ε) A hcont_p] + -- Build the tendsto for the cfc version via uniform convergence on the finite spectrum. + have htend_cfc : Filter.Tendsto (fun ε : ℝ => cfc (fun x : ℝ => (x + ε) ^ p) A) + (nhdsWithin 0 (Set.Ioi 0)) (nhds (cfc (fun x : ℝ => x ^ p) A)) := by + apply tendsto_cfc_fun + · -- TendstoUniformlyOn: reduce to pointwise convergence on the finite spectrum. + have hspec_finite : (spectrum ℝ A).Finite := by + rw [hA.1.spectrum_real_eq_range_eigenvalues]; exact Set.finite_range _ + rw [Metric.tendstoUniformlyOn_iff] + intro ε₀ hε₀ + have hptwise : ∀ x ∈ spectrum ℝ A, ∀ᶠ ε in nhdsWithin 0 (Set.Ioi 0), + dist (x ^ p) ((x + ε) ^ p) < ε₀ := by + intro x hx + have hx_nn : 0 ≤ x := by + rw [hA.1.spectrum_real_eq_range_eigenvalues] at hx + obtain ⟨i, rfl⟩ := hx; exact hA.eigenvalues_nonneg i + have hcont : ContinuousAt (fun ε : ℝ => (x + ε) ^ p) 0 := by + apply ContinuousAt.rpow_const (continuousAt_const.add continuousAt_id) + rcases hx_nn.eq_or_lt' with rfl | hx_pos + · right; simpa using hp + · left; simp; linarith + have key : Filter.Tendsto (fun ε : ℝ => (x + ε) ^ p) + (nhdsWithin 0 (Set.Ioi 0)) (nhds (x ^ p)) := by + have h := hcont.tendsto; simp only [add_zero] at h + exact h.mono_left nhdsWithin_le_nhds + exact (Metric.tendsto_nhds.mp key ε₀ hε₀).mono + (fun ε hε => by rw [dist_comm]; exact hε) + have key := (Filter.eventually_all_finset hspec_finite.toFinset).mpr + (fun x hx => hptwise x (hspec_finite.mem_toFinset.mp hx)) + exact key.mono (fun ε hε x hx => hε x (hspec_finite.mem_toFinset.mpr hx)) + · -- ContinuousOn (fun x => (x + ε)^p) (spectrum ℝ A) for all ε > 0. + apply eventually_nhdsWithin_of_forall + intro ε hε_pos + apply ContinuousOn.rpow_const (by fun_prop) + rintro x hx; left + rw [hA.1.spectrum_real_eq_range_eigenvalues] at hx + obtain ⟨i, rfl⟩ := hx + intro h + linarith [hA.eigenvalues_nonneg i, Set.mem_Ioi.mp hε_pos] + rw [hA_eq] + exact htend_cfc.congr' (eventually_nhdsWithin_of_forall (fun ε hε => (hshift_eq ε hε).symm)) + +/-- **Lieb's Joint Concavity Theorem (PosSemidef extension)** + +Extension of `lieb_joint_concavity` from positive definite to positive semidefinite matrices, +via an ε-regularization argument. + +For positive semidefinite matrices A₁, A₂, B₁, B₂ and any matrix K, the map +(A, B) ↦ Tr(Aᵖ K† B¹⁻ᵖ K) is jointly concave: + w₁ · Tr(A₁ᵖ K† B₁¹⁻ᵖ K) + w₂ · Tr(A₂ᵖ K† B₂¹⁻ᵖ K) + ≤ Tr((w₁ A₁ + w₂ A₂)ᵖ K† (w₁ B₁ + w₂ B₂)¹⁻ᵖ K) + +**Proof**: For each ε > 0, apply `lieb_joint_concavity` to (Aᵢ + ε I, Bᵢ + ε I) +which are positive definite (by `PosSemidef.add_smul_one_posDef`). The inequality is preserved in the +limit ε → 0⁺ by CFC continuity (`rpow_tendsto_smul_one`). -/ +theorem lieb_joint_concavity_semidef {m : Type*} [Fintype m] [DecidableEq m] + (A₁ A₂ B₁ B₂ : Matrix m m ℂ) + (hA₁ : A₁.PosSemidef) (hA₂ : A₂.PosSemidef) + (hB₁ : B₁.PosSemidef) (hB₂ : B₂.PosSemidef) + (K : Matrix m m ℂ) (p : ℝ) (hp0 : 0 ≤ p) (hp1 : p ≤ 1) + (w₁ w₂ : ℝ) (hw₁ : 0 ≤ w₁) (hw₂ : 0 ≤ w₂) (hw : w₁ + w₂ = 1) : + w₁ * (liebJointFunction K p A₁ hA₁ B₁ hB₁).re + + w₂ * (liebJointFunction K p A₂ hA₂ B₂ hB₂).re ≤ + (liebJointFunction K p + (w₁ • A₁ + w₂ • A₂) ((hA₁.smul hw₁).add (hA₂.smul hw₂)) + (w₁ • B₁ + w₂ • B₂) ((hB₁.smul hw₁).add (hB₂.smul hw₂))).re := by + -- For each ε > 0, Aᵢ + ε•1 is PosDef; the inequality holds by lieb_joint_concavity + -- Use unfolded form to avoid PosSemidef proof dependencies in the type + have hε_ineq : ∀ ε : ℝ, 0 < ε → + w₁ * ((A₁ + (ε:ℂ) • 1) ^ p * Kᴴ * (B₁ + (ε:ℂ) • 1) ^ (1 - p) * K).trace.re + + w₂ * ((A₂ + (ε:ℂ) • 1) ^ p * Kᴴ * (B₂ + (ε:ℂ) • 1) ^ (1 - p) * K).trace.re ≤ + ((w₁ • A₁ + w₂ • A₂ + (ε:ℂ) • 1) ^ p * Kᴴ * + (w₁ • B₁ + w₂ • B₂ + (ε:ℂ) • 1) ^ (1 - p) * K).trace.re := by + intro ε hε + -- Key: w₁•(Aᵢ + ε•1) + w₂•(Aᵢ + ε•1) = w₁•Aᵢ + w₂•Aᵢ + ε•1 (using w₁ + w₂ = 1) + have hcomb_A : w₁ • (A₁ + (ε:ℂ) • 1) + w₂ • (A₂ + (ε:ℂ) • 1) = w₁ • A₁ + w₂ • A₂ + (ε:ℂ) • 1 := + calc w₁ • (A₁ + (ε:ℂ) • 1) + w₂ • (A₂ + (ε:ℂ) • 1) + = w₁ • A₁ + w₁ • ((ε:ℂ) • 1) + (w₂ • A₂ + w₂ • ((ε:ℂ) • 1)) := by + simp [smul_add] + _ = w₁ • A₁ + w₂ • A₂ + (w₁ • ((ε:ℂ) • 1) + w₂ • ((ε:ℂ) • 1)) := by abel + _ = w₁ • A₁ + w₂ • A₂ + (w₁ + w₂) • ((ε:ℂ) • 1) := by rw [← add_smul] + _ = w₁ • A₁ + w₂ • A₂ + (ε:ℂ) • 1 := by rw [hw, one_smul] + have hcomb_B : w₁ • (B₁ + (ε:ℂ) • 1) + w₂ • (B₂ + (ε:ℂ) • 1) = w₁ • B₁ + w₂ • B₂ + (ε:ℂ) • 1 := + calc w₁ • (B₁ + (ε:ℂ) • 1) + w₂ • (B₂ + (ε:ℂ) • 1) + = w₁ • B₁ + w₁ • ((ε:ℂ) • 1) + (w₂ • B₂ + w₂ • ((ε:ℂ) • 1)) := by + simp [smul_add] + _ = w₁ • B₁ + w₂ • B₂ + (w₁ • ((ε:ℂ) • 1) + w₂ • ((ε:ℂ) • 1)) := by abel + _ = w₁ • B₁ + w₂ • B₂ + (w₁ + w₂) • ((ε:ℂ) • 1) := by rw [← add_smul] + _ = w₁ • B₁ + w₂ • B₂ + (ε:ℂ) • 1 := by rw [hw, one_smul] + have h := lieb_joint_concavity (A₁ + (ε:ℂ) • 1) (A₂ + (ε:ℂ) • 1) + (B₁ + (ε:ℂ) • 1) (B₂ + (ε:ℂ) • 1) + (hA₁.add_smul_one_posDef hε) (hA₂.add_smul_one_posDef hε) + (hB₁.add_smul_one_posDef hε) (hB₂.add_smul_one_posDef hε) + K p hp0 hp1 w₁ w₂ hw₁ hw₂ hw + simp only [liebJointFunction] at h ⊢ + rw [← hcomb_A, ← hcomb_B] + exact h + -- Convergence: as ε → 0⁺, liebJointFunction converges for each pair + -- Unfold liebJointFunction to avoid PosSemidef proof dependencies in the type + have hconv_lhs : Filter.Tendsto (fun ε : ℝ => + w₁ * ((A₁ + (ε:ℂ) • 1) ^ p * Kᴴ * (B₁ + (ε:ℂ) • 1) ^ (1 - p) * K).trace.re + + w₂ * ((A₂ + (ε:ℂ) • 1) ^ p * Kᴴ * (B₂ + (ε:ℂ) • 1) ^ (1 - p) * K).trace.re) + (nhdsWithin 0 (Set.Ioi 0)) + (nhds (w₁ * (liebJointFunction K p A₁ hA₁ B₁ hB₁).re + + w₂ * (liebJointFunction K p A₂ hA₂ B₂ hB₂).re)) := by + simp only [liebJointFunction] + apply Filter.Tendsto.add + · apply Filter.Tendsto.const_mul + apply (Complex.continuous_re.comp continuous_id.matrix_trace).continuousAt.tendsto.comp + exact ((rpow_tendsto_smul_one hA₁ p hp0).mul_const Kᴴ).mul + (rpow_tendsto_smul_one hB₁ (1 - p) (by linarith)) |>.mul_const K + · apply Filter.Tendsto.const_mul + apply (Complex.continuous_re.comp continuous_id.matrix_trace).continuousAt.tendsto.comp + exact ((rpow_tendsto_smul_one hA₂ p hp0).mul_const Kᴴ).mul + (rpow_tendsto_smul_one hB₂ (1 - p) (by linarith)) |>.mul_const K + have hconv_rhs : Filter.Tendsto (fun ε : ℝ => + ((w₁ • A₁ + w₂ • A₂ + (ε:ℂ) • 1) ^ p * Kᴴ * + (w₁ • B₁ + w₂ • B₂ + (ε:ℂ) • 1) ^ (1 - p) * K).trace.re) + (nhdsWithin 0 (Set.Ioi 0)) + (nhds (liebJointFunction K p (w₁ • A₁ + w₂ • A₂) ((hA₁.smul hw₁).add (hA₂.smul hw₂)) + (w₁ • B₁ + w₂ • B₂) ((hB₁.smul hw₁).add (hB₂.smul hw₂))).re) := by + simp only [liebJointFunction] + apply (Complex.continuous_re.comp continuous_id.matrix_trace).continuousAt.tendsto.comp + exact ((rpow_tendsto_smul_one ((hA₁.smul hw₁).add (hA₂.smul hw₂)) p hp0).mul_const Kᴴ).mul + (rpow_tendsto_smul_one ((hB₁.smul hw₁).add (hB₂.smul hw₂)) (1 - p) (by linarith)) |>.mul_const K + -- Combine: lhs_limit ≤ rhs_limit via the ε-pointwise inequality + apply le_of_tendsto_of_tendsto hconv_lhs hconv_rhs + filter_upwards [self_mem_nhdsWithin (s := Set.Ioi (0:ℝ))] with ε (hε : ε ∈ Set.Ioi 0) + exact hε_ineq ε (Set.mem_Ioi.mp hε) + +/-- Block diagonal matrix `fromBlocks A 0 0 0` is positive semidefinite when `A` is. -/ +private lemma fromBlocks_top_posSemidef {n m : Type*} [Fintype n] [Fintype m] + {A : Matrix n n ℂ} (hA : A.PosSemidef) : + (Matrix.fromBlocks A 0 0 (0 : Matrix m m ℂ)).PosSemidef := by + refine PosSemidef.of_dotProduct_mulVec_nonneg + (by simpa using Matrix.IsHermitian.fromBlocks hA.1 (by simp) Matrix.isHermitian_zero) ?_ + intro v + have heq : star v ⬝ᵥ (Matrix.fromBlocks A 0 0 0 *ᵥ v) = + star (fun i => v (Sum.inl i)) ⬝ᵥ (A *ᵥ fun i => v (Sum.inl i)) := by + simp [dotProduct, Fintype.sum_sum_type, fromBlocks_mulVec_inl, fromBlocks_mulVec_inr, + Matrix.zero_mulVec] + rw [heq] + exact hA.dotProduct_mulVec_nonneg _ + +/-- Block diagonal matrix `fromBlocks 0 0 0 B` is positive semidefinite when `B` is. -/ +private lemma fromBlocks_bot_posSemidef {n m : Type*} [Fintype n] [Fintype m] + {B : Matrix m m ℂ} (hB : B.PosSemidef) : + (Matrix.fromBlocks (0 : Matrix n n ℂ) 0 0 B).PosSemidef := by + refine PosSemidef.of_dotProduct_mulVec_nonneg + (by simpa using Matrix.IsHermitian.fromBlocks Matrix.isHermitian_zero (by simp) hB.1) ?_ + intro v + have heq : star v ⬝ᵥ (Matrix.fromBlocks 0 0 0 B *ᵥ v) = + star (fun i => v (Sum.inr i)) ⬝ᵥ (B *ᵥ fun i => v (Sum.inr i)) := by + simp [dotProduct, Fintype.sum_sum_type, fromBlocks_mulVec_inl, fromBlocks_mulVec_inr, + Matrix.zero_mulVec] + rw [heq] + exact hB.dotProduct_mulVec_nonneg _ + +/-- For `0 < p`, the rpow of `fromBlocks A 0 0 0` equals `fromBlocks (A^p) 0 0 0`. -/ +private lemma fromBlocks_top_rpow {n m : Type*} [Fintype n] [DecidableEq n] + [Fintype m] [DecidableEq m] + {A : Matrix n n ℂ} (hA : A.PosSemidef) (p : ℝ) (hp : 0 < p) : + (Matrix.fromBlocks A 0 0 (0 : Matrix m m ℂ)) ^ p = Matrix.fromBlocks (A ^ p) 0 0 0 := by + have hAsa : IsSelfAdjoint A := hA.1 + have h0sa : IsSelfAdjoint (0 : Matrix m m ℂ) := Matrix.isHermitian_zero + have hcont : ContinuousOn (fun x : ℝ => x ^ p) + (spectrum ℝ A ∪ spectrum ℝ (0 : Matrix m m ℂ)) := + (continuousOn_id.rpow_const fun _ _ => Or.inr hp.le) + have h0m : cfc (fun x : ℝ => x ^ p) (0 : Matrix m m ℂ) = 0 := by + simp [Real.zero_rpow hp.ne'] + rw [CFC.rpow_eq_cfc_real (a := fromBlocks A 0 0 0) + (ha := by rw [Matrix.le_iff, sub_zero]; exact fromBlocks_top_posSemidef hA), + cfc_fromBlocks_diag' A 0 hAsa h0sa (fun x => x ^ p) hcont, + ← CFC.rpow_eq_cfc_real (a := A) + (ha := by rw [Matrix.le_iff, sub_zero]; exact hA), + h0m] + +/-- For `0 < p`, the rpow of `fromBlocks 0 0 0 B` equals `fromBlocks 0 0 0 (B^p)`. -/ +private lemma fromBlocks_bot_rpow {n m : Type*} [Fintype n] [DecidableEq n] + [Fintype m] [DecidableEq m] + {B : Matrix m m ℂ} (hB : B.PosSemidef) (p : ℝ) (hp : 0 < p) : + (Matrix.fromBlocks (0 : Matrix n n ℂ) 0 0 B) ^ p = Matrix.fromBlocks 0 0 0 (B ^ p) := by + have hBsa : IsSelfAdjoint B := hB.1 + have h0sa : IsSelfAdjoint (0 : Matrix n n ℂ) := Matrix.isHermitian_zero + have hcont : ContinuousOn (fun x : ℝ => x ^ p) + (spectrum ℝ (0 : Matrix n n ℂ) ∪ spectrum ℝ B) := + (continuousOn_id.rpow_const fun _ _ => Or.inr hp.le) + have h0n : cfc (fun x : ℝ => x ^ p) (0 : Matrix n n ℂ) = 0 := by + simp [Real.zero_rpow hp.ne'] + rw [CFC.rpow_eq_cfc_real (a := fromBlocks 0 0 0 B) + (ha := by rw [Matrix.le_iff, sub_zero]; exact fromBlocks_bot_posSemidef hB), + cfc_fromBlocks_diag' 0 B h0sa hBsa (fun x => x ^ p) hcont, + ← CFC.rpow_eq_cfc_real (a := B) + (ha := by rw [Matrix.le_iff, sub_zero]; exact hB), + h0n] + +/-- The block Lieb identity: the rectangular Lieb function for the original matrices +equals the (square) Lieb function applied with block-embedded matrices. +For the block square matrix +K̃ = [0, K†; K, 0], +à = [A, 0; 0, 0], +B̃ = [0, 0; 0, B]: + liebJointFunction(K̃, p, Ã, B̃) = liebJointFunction(K, p, A, B) + +This identity is proved by direct block matrix computation. -/ +private lemma liebJointFunction_eq_block {n m : Type*} [Fintype n] [DecidableEq n] + [Fintype m] [DecidableEq m] + (K : Matrix m n ℂ) (p : ℝ) (hp : 0 < p) (hp1 : p < 1) + (A : Matrix n n ℂ) (hA : A.PosSemidef) + (B : Matrix m m ℂ) (hB : B.PosSemidef) : + liebJointFunction (fromBlocks 0 Kᴴ K 0) p + (fromBlocks A 0 0 0) (fromBlocks_top_posSemidef hA) + (fromBlocks 0 0 0 B) (fromBlocks_bot_posSemidef hB) = + liebJointFunction K p A hA B hB := by + simp only [liebJointFunction] + -- Compute (fromBlocks A 0 0 0)^p = fromBlocks (A^p) 0 0 0 + have hAp : (fromBlocks A 0 0 (0 : Matrix m m ℂ)) ^ p = fromBlocks (A ^ p) 0 0 0 := + fromBlocks_top_rpow hA p hp + -- Compute (fromBlocks 0 0 0 B)^{1-p} = fromBlocks 0 0 0 (B^{1-p}) + have hB1p : (fromBlocks (0 : Matrix n n ℂ) 0 0 B) ^ (1 - p) = + fromBlocks 0 0 0 (B ^ (1 - p)) := + fromBlocks_bot_rpow hB (1 - p) (by linarith) + -- (fromBlocks 0 Kᴴ K 0)ᴴ = fromBlocks 0 Kᴴ K 0 + have hKH : (fromBlocks 0 Kᴴ K 0 : Matrix (n ⊕ m) (n ⊕ m) ℂ)ᴴ = fromBlocks 0 Kᴴ K 0 := by + rw [Matrix.fromBlocks_conjTranspose] + simp + rw [hAp, hB1p, hKH] + simp only [fromBlocks_multiply] + simp [Matrix.trace, Fintype.sum_sum_type] + +private lemma fromBlocks_smul_top {n m : Type*} [Fintype n] [Fintype m] + (A : Matrix n n ℂ) (w : ℝ) : + w • (fromBlocks A 0 0 (0 : Matrix m m ℂ)) = fromBlocks (w • A) 0 0 0 := by + ext i j + rcases i with i | i <;> rcases j with j | j <;> simp [fromBlocks, smul_zero] + +private lemma fromBlocks_smul_bot {n m : Type*} [Fintype n] [Fintype m] + (B : Matrix m m ℂ) (w : ℝ) : + w • (fromBlocks (0 : Matrix n n ℂ) 0 0 B) = fromBlocks 0 0 0 (w • B) := by + ext i j + rcases i with i | i <;> rcases j with j | j <;> simp [fromBlocks, smul_zero] + +/-- **Lieb's Joint Concavity Theorem for Rectangular Matrices (PosSemidef extension)** + +For K : m × n rectangular and positive semidefinite A₁, A₂ : n × n, +B₁, B₂ : m × m, the map (A, B) ↦ Tr(Aᵖ K† B¹⁻ᵖ K) +is jointly concave: + w₁ · Tr(A₁ᵖ K† B₁¹⁻ᵖ K) + w₂ · Tr(A₂ᵖ K† B₂¹⁻ᵖ K) + ≤ Tr((w₁ A₁ + w₂ A₂)ᵖ K† (w₁ B₁ + w₂ B₂)¹⁻ᵖ K) + +**Proof**: Embed in the block space n ⊕ m using +K̃ = [0, K†; K, 0], +Ãᵢ = [Aᵢ, 0; 0, 0], +B̃ᵢ = [0, 0; 0, Bᵢ], +and apply `lieb_joint_concavity_semidef`. -/ +theorem lieb_joint_concavity_rect_semidef {n m : Type*} [Fintype n] [DecidableEq n] + [Fintype m] [DecidableEq m] + (A₁ A₂ : Matrix n n ℂ) (hA₁ : A₁.PosSemidef) (hA₂ : A₂.PosSemidef) + (B₁ B₂ : Matrix m m ℂ) (hB₁ : B₁.PosSemidef) (hB₂ : B₂.PosSemidef) + (K : Matrix m n ℂ) (p : ℝ) (hp0 : 0 ≤ p) (hp1 : p ≤ 1) + (w₁ w₂ : ℝ) (hw₁ : 0 ≤ w₁) (hw₂ : 0 ≤ w₂) (hw : w₁ + w₂ = 1) : + w₁ * (liebJointFunction K p A₁ hA₁ B₁ hB₁).re + + w₂ * (liebJointFunction K p A₂ hA₂ B₂ hB₂).re ≤ + (liebJointFunction K p + (w₁ • A₁ + w₂ • A₂) ((hA₁.smul hw₁).add (hA₂.smul hw₂)) + (w₁ • B₁ + w₂ • B₂) ((hB₁.smul hw₁).add (hB₂.smul hw₂))).re := by + -- Boundary cases p = 0 and p = 1: both sides are linear, giving equality + rcases eq_or_lt_of_le hp0 with rfl | hp0' + · -- p = 0: Tr(K†BK) is linear in B + simp only [liebJointFunction_zero_eq] + have h_linear : (Kᴴ * (w₁ • B₁ + w₂ • B₂) * K).trace = + (w₁ : ℂ) * (Kᴴ * B₁ * K).trace + (w₂ : ℂ) * (Kᴴ * B₂ * K).trace := by + rw [Matrix.mul_add, Matrix.add_mul, Matrix.mul_smul, Matrix.smul_mul, + Matrix.mul_smul, Matrix.smul_mul, trace_add, trace_smul, trace_smul] + simp [Complex.real_smul] + rw [h_linear] + simp only [Complex.add_re, Complex.mul_re, Complex.ofReal_re, Complex.ofReal_im, + zero_mul, sub_zero] + exact le_refl _ + rcases eq_or_lt_of_le hp1 with rfl | hp1' + · -- p = 1: Tr(AK†K) is linear in A + simp only [liebJointFunction_one_eq] + have h_linear : ((w₁ • A₁ + w₂ • A₂) * Kᴴ * K).trace = + (w₁ : ℂ) * (A₁ * Kᴴ * K).trace + (w₂ : ℂ) * (A₂ * Kᴴ * K).trace := by + rw [Matrix.add_mul, Matrix.add_mul, Matrix.smul_mul, Matrix.smul_mul, + Matrix.smul_mul, Matrix.smul_mul, trace_add, trace_smul, trace_smul] + simp [Complex.real_smul] + rw [h_linear] + simp only [Complex.add_re, Complex.mul_re, Complex.ofReal_re, Complex.ofReal_im, + zero_mul, sub_zero] + exact le_refl _ + -- Interior case: embed in (n ⊕ m) block space and apply lieb_joint_concavity_semidef + -- Define block matrices in (n ⊕ m) × (n ⊕ m) space + let Kblock : Matrix (n ⊕ m) (n ⊕ m) ℂ := fromBlocks 0 Kᴴ K 0 + let Ablock1 : Matrix (n ⊕ m) (n ⊕ m) ℂ := fromBlocks A₁ 0 0 0 + let Ablock2 : Matrix (n ⊕ m) (n ⊕ m) ℂ := fromBlocks A₂ 0 0 0 + let Bblock1 : Matrix (n ⊕ m) (n ⊕ m) ℂ := fromBlocks 0 0 0 B₁ + let Bblock2 : Matrix (n ⊕ m) (n ⊕ m) ℂ := fromBlocks 0 0 0 B₂ + -- PSD properties + have hAb1 : Ablock1.PosSemidef := fromBlocks_top_posSemidef hA₁ + have hAb2 : Ablock2.PosSemidef := fromBlocks_top_posSemidef hA₂ + have hBb1 : Bblock1.PosSemidef := fromBlocks_bot_posSemidef hB₁ + have hBb2 : Bblock2.PosSemidef := fromBlocks_bot_posSemidef hB₂ + -- Apply lieb_joint_concavity_semidef in (n⊕m) space + have key := lieb_joint_concavity_semidef Ablock1 Ablock2 Bblock1 Bblock2 + hAb1 hAb2 hBb1 hBb2 Kblock p hp0 hp1 w₁ w₂ hw₁ hw₂ hw + -- Rewrite using the block identity + have hid₁ := liebJointFunction_eq_block K p hp0' hp1' A₁ hA₁ B₁ hB₁ + have hid₂ := liebJointFunction_eq_block K p hp0' hp1' A₂ hA₂ B₂ hB₂ + -- Identify convex combinations of blocks + have hAcomb : w₁ • Ablock1 + w₂ • Ablock2 = fromBlocks (w₁ • A₁ + w₂ • A₂) 0 0 0 := by + simp only [Ablock1, Ablock2, fromBlocks_smul_top, fromBlocks_add, add_zero] + have hBcomb : w₁ • Bblock1 + w₂ • Bblock2 = fromBlocks 0 0 0 (w₁ • B₁ + w₂ • B₂) := by + simp only [Bblock1, Bblock2, fromBlocks_smul_bot, fromBlocks_add, zero_add] + have hAcomb_psd : (w₁ • Ablock1 + w₂ • Ablock2).PosSemidef := (hAb1.smul hw₁).add (hAb2.smul hw₂) + have hBcomb_psd : (w₁ • Bblock1 + w₂ • Bblock2).PosSemidef := (hBb1.smul hw₁).add (hBb2.smul hw₂) + have hidcomb : liebJointFunction Kblock p (w₁ • Ablock1 + w₂ • Ablock2) hAcomb_psd + (w₁ • Bblock1 + w₂ • Bblock2) hBcomb_psd = + liebJointFunction K p (w₁ • A₁ + w₂ • A₂) ((hA₁.smul hw₁).add (hA₂.smul hw₂)) + (w₁ • B₁ + w₂ • B₂) ((hB₁.smul hw₁).add (hB₂.smul hw₂)) := by + simp only [liebJointFunction] + conv_lhs => rw [hAcomb, hBcomb] + have key2 := liebJointFunction_eq_block K p hp0' hp1' + (w₁ • A₁ + w₂ • A₂) ((hA₁.smul hw₁).add (hA₂.smul hw₂)) + (w₁ • B₁ + w₂ • B₂) ((hB₁.smul hw₁).add (hB₂.smul hw₂)) + simp only [liebJointFunction] at key2 + exact key2 + -- Rewrite key inequality using the block identities + rw [← hid₁, ← hid₂, ← hidcomb] at * + exact key + +/-! ### Extensions: homogeneity, weighted, and super-additive Lieb concavity -/ + +open scoped QuantumInfo + +/-- Degree-1 homogeneity of rpow: (c ⋅ A)ˢ = cˢ ⋅ Aˢ for c ≥ 0, A PSD, s ≥ 0. +Proved via spectral decomposition + `rpow_unitary_conj` + `diagonal_rpow` + `Real.mul_rpow`. -/ +lemma rpow_nonneg_smul {α : Type*} [Fintype α] [DecidableEq α] + (c : ℝ) (hc : 0 ≤ c) (A : Matrix α α ℂ) (hA : A.PosSemidef) + (s : ℝ) (hs : 0 ≤ s) : + (c • A) ^ s = (c ^ s : ℝ) • A ^ s := by + set U := hA.1.eigenvectorUnitary.1 + set ev := hA.1.eigenvalues + have hev_nn : ∀ i, 0 ≤ ev i := hA.eigenvalues_nonneg + have hU_mem : U ∈ Matrix.unitaryGroup α ℂ := hA.1.eigenvectorUnitary.2 + -- Spectral decomposition: A = U * diag(ev) * U† + have hspec : A = U * diagonal (fun i => (ev i : ℂ)) * Uᴴ := + hA.1.spectral_theorem (𝕜 := ℂ) + -- PSD of diagonal matrices + have hD_le : (0 : Matrix α α ℂ) ≤ diagonal (fun i => (ev i : ℂ)) := by + simp only [Matrix.le_iff, sub_zero] + exact posSemidef_diagonal_iff.mpr (fun i => Complex.zero_le_real.mpr (mod_cast hev_nn i)) + have hcev_nn : ∀ i, 0 ≤ c * ev i := fun i => mul_nonneg hc (hev_nn i) + have hcD_le : (0 : Matrix α α ℂ) ≤ diagonal (fun i => ((c * ev i : ℝ) : ℂ)) := by + simp only [Matrix.le_iff, sub_zero] + exact posSemidef_diagonal_iff.mpr (fun i => Complex.zero_le_real.mpr (mod_cast hcev_nn i)) + -- c • diag(ev) = diag(c * ev) + have hsmul_diag : c • diagonal (fun i => (ev i : ℂ)) = + diagonal (fun i => ((c * ev i : ℝ) : ℂ)) := by + ext i j; simp only [Matrix.smul_apply, diagonal_apply, smul_ite, smul_zero] + split_ifs <;> [simp [Complex.ofReal_mul]; rfl] + -- c • A = U * diag(c * ev) * U† + have hcA_spec : c • A = U * diagonal (fun i => ((c * ev i : ℝ) : ℂ)) * Uᴴ := by + conv_lhs => rw [hspec] + -- c • ((U * D) * Uᴴ) = (c • (U * D)) * Uᴴ = (U * (c • D)) * Uᴴ = (U * D') * Uᴴ + rw [← smul_mul_assoc, ← mul_smul_comm, hsmul_diag] + -- (c • A)^s = U * diag((c*ev)^s) * U† + have hcA_nonneg : 0 ≤ c • A := by rw [Matrix.le_iff, sub_zero]; exact hA.smul hc + have h_lhs : (c • A) ^ s = + U * diagonal (fun i => (((c * ev i) ^ s : ℝ) : ℂ)) * Uᴴ := by + conv_lhs => rw [hcA_spec] + rw [rpow_unitary_conj hU_mem hs hcD_le (hM' := by simpa [Matrix.le_iff, hcA_spec] using hcA_nonneg), + diagonal_rpow _ hcev_nn s hs] + -- A^s = U * diag(ev^s) * U† + have h_rhs : A ^ s = + U * diagonal (fun i => ((ev i ^ s : ℝ) : ℂ)) * Uᴴ := by + conv_lhs => rw [hspec] + rw [rpow_unitary_conj hU_mem hs hD_le (hM' := by rw [← hspec]; rw [Matrix.le_iff, sub_zero]; exact hA), + diagonal_rpow _ hev_nn s hs] + -- (c * ev_i)^s = c^s * ev_i^s by Real.mul_rpow + rw [h_lhs, h_rhs] + -- Goal: (U * diag((c*ev)^s) * U†) = c^s • (U * diag(ev^s) * U†) + -- Use (c * ev_i)^s = c^s * ev_i^s by Real.mul_rpow + rw [← smul_mul_assoc, ← mul_smul_comm] + -- Goal: (U * diag((c*ev)^s)) * U† = (U * (c^s • diag(ev^s))) * U† + congr 1 + -- Goal: U * diag((c*ev)^s) = U * (c^s • diag(ev^s)) + congr 1 + -- Goal: diag((c*ev)^s) = c^s • diag(ev^s) + ext i j + simp only [diagonal, Matrix.of_apply, Matrix.smul_apply] + by_cases hij : i = j + · subst hij + simp only [if_true, Complex.real_smul] + rw [Real.mul_rpow hc (hev_nn i)] + simp only [Complex.ofReal_mul] + · simp only [hij, if_false, smul_zero] + +/-- Degree-1 homogeneity of F_s: F_s(cA, cB) = c ⋅ F_s(A, B). -/ +lemma Fs_homogeneous {α : Type*} [Fintype α] [DecidableEq α] + (c : ℝ) (hc : 0 ≤ c) + (A B : Matrix α α ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) + (s : ℝ) (hs0 : 0 ≤ s) (hs1 : s ≤ 1) : + (Tr ((c • A) ^ s * (c • B) ^ (1 - s))).re = + c * (Tr (A ^ s * B ^ (1 - s))).re := by + have h1s : 0 ≤ 1 - s := by linarith + rw [rpow_nonneg_smul c hc A hA s hs0, + rpow_nonneg_smul c hc B hB (1 - s) h1s] + -- (c^s • A^s) * (c^{1-s} • B^{1-s}) = c^s * c^{1-s} • (A^s * B^{1-s}) + rw [show (c ^ s : ℝ) • A ^ s * ((c ^ (1 - s) : ℝ) • B ^ (1 - s)) = + ((c ^ s * c ^ (1 - s) : ℝ) : ℝ) • (A ^ s * B ^ (1 - s)) from by + rw [smul_mul_smul_comm]] + -- c^s * c^{1-s} = c^1 = c + have : c ^ s * c ^ (1 - s) = c := by + by_cases hc0 : c = 0 + · by_cases hs0' : s = 0 + · simp only [hs0', Real.rpow_zero, one_mul, sub_zero, Real.rpow_one] + · simp only [hc0, Real.zero_rpow hs0', zero_mul] + · have hc_pos : 0 < c := lt_of_le_of_ne hc (Ne.symm hc0) + rw [← Real.rpow_add hc_pos, show s + (1 - s) = 1 by ring, Real.rpow_one] + rw [this] + -- Goal: (c • (A^s * B^{1-s})).trace.re = c * (A^s * B^{1-s}).trace.re + simp only [Matrix.trace_smul, Complex.real_smul, Complex.mul_re, + Complex.ofReal_re, Complex.ofReal_im, zero_mul, sub_zero] + +/-- Weighted multi-term Lieb concavity (K = I, square matrices): + ∑ᵢ wᵢ Tr (Aᵢˢ Bᵢ¹⁻ˢ) ≤ Tr ((∑ᵢ wᵢ Aᵢ)ˢ (∑ᵢ wᵢ Bᵢ)¹⁻ˢ) +for wᵢ ≥ 0 with ∑ᵢ wᵢ = 1, proved by induction using the 2-term +`lieb_joint_concavity_semidef`. -/ +lemma lieb_concavity_weighted {r : ℕ} {α : Type*} [Fintype α] [DecidableEq α] + (A B : Fin r → Matrix α α ℂ) (hA : ∀ i, (A i).PosSemidef) (hB : ∀ i, (B i).PosSemidef) + (w : Fin r → ℝ) (hw_nn : ∀ i, 0 ≤ w i) (hw_sum : ∑ i, w i = 1) + (s : ℝ) (hs0 : 0 ≤ s) (hs1 : s ≤ 1) : + ∑ i : Fin r, w i * (Tr ((A i) ^ s * (B i) ^ (1 - s))).re ≤ + (Tr ((∑ i : Fin r, w i • A i) ^ s * (∑ i : Fin r, w i • B i) ^ (1 - s))).re := by + induction r with + | zero => simp at hw_sum + | succ r ih => + rw [Fin.sum_univ_castSucc, Fin.sum_univ_castSucc, Fin.sum_univ_castSucc] + set w' := fun i : Fin r => w (Fin.castSucc i) + set A' := fun i : Fin r => A (Fin.castSucc i) + set B' := fun i : Fin r => B (Fin.castSucc i) + set wr := w (Fin.last r) + set Ar := A (Fin.last r) + set Br := B (Fin.last r) + set W := ∑ i : Fin r, w' i -- = 1 - wr + have hW_eq : W + wr = 1 := by + have : ∑ i : Fin (r + 1), w i = 1 := hw_sum + rw [Fin.sum_univ_castSucc] at this; exact this + have hW_nn : 0 ≤ W := Finset.sum_nonneg fun i _ => hw_nn (Fin.castSucc i) + have hwr_nn : 0 ≤ wr := hw_nn (Fin.last r) + have hwr_le : wr ≤ 1 := by linarith + -- PSD of weighted sums + have hSA_psd : (∑ i : Fin r, w' i • A' i).PosSemidef := + posSemidef_sum Finset.univ fun i _ => (hA (Fin.castSucc i)).smul (hw_nn (Fin.castSucc i)) + have hSB_psd : (∑ i : Fin r, w' i • B' i).PosSemidef := + posSemidef_sum Finset.univ fun i _ => (hB (Fin.castSucc i)).smul (hw_nn (Fin.castSucc i)) + have hAr_psd : Ar.PosSemidef := hA (Fin.last r) + have hBr_psd : Br.PosSemidef := hB (Fin.last r) + -- Use the 2-term Lieb concavity (K = 1) with weights W and wr + have h2term := lieb_joint_concavity_semidef + (∑ i : Fin r, w' i • A' i) (wr • Ar) + (∑ i : Fin r, w' i • B' i) (wr • Br) + hSA_psd ((hA (Fin.last r)).smul hwr_nn) + hSB_psd ((hB (Fin.last r)).smul hwr_nn) + 1 s hs0 hs1 W wr hW_nn hwr_nn hW_eq + simp only [liebJointFunction, conjTranspose_one, Matrix.mul_one] at h2term + -- Case split: W = 0 → trivial; W > 0 → IH with wᵢ/W then 2-term concavity + clear h2term + by_cases hW : W = 0 + · -- All w'ᵢ = 0, wr = 1 + have hw'_zero : ∀ i, w' i = 0 := by + intro i + have := Finset.sum_eq_zero_iff_of_nonneg (fun j _ => hw_nn (Fin.castSucc j)) |>.mp hW + exact this i (Finset.mem_univ _) + have hwr_one : wr = 1 := by linarith + have hA_zero : ∑ i : Fin r, w' i • A' i = 0 := by + apply Finset.sum_eq_zero; intro i _; simp [hw'_zero i] + have hB_zero : ∑ i : Fin r, w' i • B' i = 0 := by + apply Finset.sum_eq_zero; intro i _; simp [hw'_zero i] + have hF_zero : ∑ i : Fin r, w' i * ((A' i) ^ s * (B' i) ^ (1 - s)).trace.re = 0 := by + apply Finset.sum_eq_zero; intro i _; simp [hw'_zero i] + -- Unfold the set definitions so simp can match + simp only [w', A', B', Ar, Br, wr] at hwr_one hA_zero hB_zero hF_zero ⊢ + simp only [hwr_one, one_mul, one_smul, hA_zero, hB_zero, hF_zero, zero_add, le_refl] + · -- W > 0 + have hW_pos : 0 < W := lt_of_le_of_ne hW_nn (Ne.symm hW) + -- Divide weights by W for IH + have hw'_sum : ∑ i : Fin r, w' i / W = 1 := by + rw [← Finset.sum_div, div_eq_one_iff_eq (ne_of_gt hW_pos)] + have hw'_nn : ∀ i, 0 ≤ w' i / W := fun i => div_nonneg (hw_nn (Fin.castSucc i)) hW_nn + -- IH with normalized weights + have ih' := ih A' B' (fun i => hA (Fin.castSucc i)) (fun i => hB (Fin.castSucc i)) + (fun i => w' i / W) hw'_nn hw'_sum + -- Factor out 1/W from weighted sums + have hSA_div : ∑ i : Fin r, (w' i / W) • A' i = (1 / W) • ∑ i : Fin r, w' i • A' i := by + rw [Finset.smul_sum] + congr 1 + funext i + rw [show (w' i / W) = (1 / W) * w' i from by ring] + rw [smul_smul] + have hSB_div : ∑ i : Fin r, (w' i / W) • B' i = (1 / W) • ∑ i : Fin r, w' i • B' i := by + rw [Finset.smul_sum] + congr 1 + funext i + rw [show (w' i / W) = (1 / W) * w' i from by ring] + rw [smul_smul] + -- 2-term Lieb concavity with X = (1/W)•Σw'A, Y = (1/W)•Σw'B + have hX_psd : ((1 / W) • ∑ i : Fin r, w' i • A' i).PosSemidef := + hSA_psd.smul (div_nonneg zero_le_one hW_nn) + have hY_psd : ((1 / W) • ∑ i : Fin r, w' i • B' i).PosSemidef := + hSB_psd.smul (div_nonneg zero_le_one hW_nn) + have h2 := lieb_joint_concavity_semidef + ((1 / W) • ∑ i : Fin r, w' i • A' i) Ar + ((1 / W) • ∑ i : Fin r, w' i • B' i) Br + hX_psd hAr_psd hY_psd hBr_psd + 1 s hs0 hs1 W wr hW_nn hwr_nn hW_eq + simp only [liebJointFunction, conjTranspose_one, Matrix.mul_one] at h2 + -- Simplify W • (1/W • X) = X + have hWX_A : W • ((1 / W) • ∑ i : Fin r, w' i • A' i) = ∑ i : Fin r, w' i • A' i := by + rw [smul_smul, mul_one_div_cancel (ne_of_gt hW_pos), one_smul] + have hWX_B : W • ((1 / W) • ∑ i : Fin r, w' i • B' i) = ∑ i : Fin r, w' i • B' i := by + rw [smul_smul, mul_one_div_cancel (ne_of_gt hW_pos), one_smul] + rw [hWX_A, hWX_B] at h2 + -- Combine h2 (2-term concavity) with IH (normalized weights) + have ih_simple : ∑ i : Fin r, (w' i / W) * ((A' i) ^ s * (B' i) ^ (1 - s)).trace.re ≤ + ((∑ i : Fin r, (w' i / W) • A' i) ^ s * + (∑ i : Fin r, (w' i / W) • B' i) ^ (1 - s)).trace.re := by + convert ih' using 2 + rw [hSA_div, hSB_div] at ih_simple + -- Scale IH by W + have ih_scaled : ∑ i : Fin r, w' i * ((A' i) ^ s * (B' i) ^ (1 - s)).trace.re ≤ + W * (((1 / W) • ∑ i : Fin r, w' i • A' i) ^ s * + ((1 / W) • ∑ i : Fin r, w' i • B' i) ^ (1 - s)).trace.re := by + have hmul := mul_le_mul_of_nonneg_left ih_simple hW_nn + have hsum_eq : W * ∑ i : Fin r, (w' i / W) * ((A' i) ^ s * (B' i) ^ (1 - s)).trace.re = + ∑ i : Fin r, w' i * ((A' i) ^ s * (B' i) ^ (1 - s)).trace.re := by + rw [Finset.mul_sum] + congr 1 + funext i + field_simp + rwa [hsum_eq] at hmul + -- Combine ih_scaled and h2 + linarith [ih_scaled, h2] + +/-- **Unweighted super-additivity** of F_s(A, B) = Tr (Aˢ B¹⁻ˢ): + ∑ᵢ F_s(Aᵢ, Bᵢ) ≤ F_s(∑ᵢ Aᵢ, ∑ᵢ Bᵢ) +Proved from `lieb_concavity_weighted` (uniform weights 1/r) plus degree-1 homogeneity. -/ +lemma lieb_concavity_sum {r : ℕ} {α : Type*} [Fintype α] [DecidableEq α] + (A B : Fin r → Matrix α α ℂ) (hA : ∀ i, (A i).PosSemidef) (hB : ∀ i, (B i).PosSemidef) + (s : ℝ) (hs0 : 0 ≤ s) (hs1 : s ≤ 1) : + ∑ i : Fin r, (Tr ((A i) ^ s * (B i) ^ (1 - s))).re ≤ + (Tr ((∑ i : Fin r, A i) ^ s * (∑ i : Fin r, B i) ^ (1 - s))).re := by + rcases r with _ | r + · -- Empty case: sums are empty, so 0 ≤ (0^s * 0^{1-s}).trace.re + simp only [Finset.univ_eq_empty, Finset.sum_empty] + -- For 0 ≤ s ≤ 1, the trace of 0^s * 0^{1-s} has nonneg real part + -- since both 0^s and 0^{1-s} are PSD (continuous functional calculus) + -- and trace(AB) has nonneg real part for PSD A, B. + -- Simplest: if s > 0, 0^s = cfc f(0) where f(x) = x^s applied to 0 gives 0. + -- Similarly for 1-s > 0. At least one of s, 1-s is positive (unless s = 0 or s = 1). + -- In all cases, 0^s * 0^{1-s} ∈ PSD, and PSD have nonneg trace.re. + have h_nonneg : 0 ≤ (0 : Matrix α α ℂ) := by rw [Matrix.le_iff, sub_zero]; exact PosSemidef.zero + by_cases hs0' : s = 0 + · simp only [hs0', sub_zero] + rw [CFC.rpow_zero (0 : Matrix α α ℂ) h_nonneg, CFC.rpow_one (0 : Matrix α α ℂ) h_nonneg] + simp + · -- s > 0, so 0^s = 0 + -- For s > 0, f(x) = x^s has f(0) = 0, so cfc f 0 = 0 • 1 = 0 + have hs_pos : 0 < s := lt_of_le_of_ne hs0 (Ne.symm hs0') + -- Need to show: 0 ≤ (0^s * 0^{1-s}).trace.re + -- We compute 0^s = 0 for s > 0 via CFC + have h0s : (0 : Matrix α α ℂ) ^ s = 0 := by + rw [CFC.rpow_eq_cfc_real (a := (0 : Matrix α α ℂ)) (ha := h_nonneg), cfc_apply_zero] + simp [Real.zero_rpow (ne_of_gt hs_pos)] + simp only [h0s, Matrix.zero_mul, Matrix.trace_zero, Complex.zero_re, le_refl] + set rr := (r + 1 : ℝ) + have hr_pos : (0 : ℝ) < rr := by simp only [rr]; positivity + -- Weighted concavity with w_i = 1/rr + have hw_sum : ∑ i : Fin (r + 1), (1 / rr) = 1 := by + simp only [Finset.sum_const, Finset.card_fin, nsmul_eq_mul, rr] + field_simp; push_cast; ring + have hw_nn : ∀ i : Fin (r + 1), (0 : ℝ) ≤ 1 / rr := fun _ => by positivity + have hw := lieb_concavity_weighted A B hA hB (fun _ => 1 / rr) hw_nn hw_sum s hs0 hs1 + -- LHS: (1/rr) * Σ Fᵢ + rw [← Finset.mul_sum] at hw + -- RHS: F(Σ (1/rr) • A, Σ (1/rr) • B) = F((1/rr) • ΣA, (1/rr) • ΣB) = (1/rr) * F(ΣA, ΣB) + have hSA : (∑ i, A i).PosSemidef := posSemidef_sum Finset.univ fun i _ => hA i + have hSB : (∑ i, B i).PosSemidef := posSemidef_sum Finset.univ fun i _ => hB i + rw [show ∑ i : Fin (r + 1), (1 / rr) • A i = (1 / rr) • ∑ i, A i from Finset.smul_sum.symm, + show ∑ i : Fin (r + 1), (1 / rr) • B i = (1 / rr) • ∑ i, B i from Finset.smul_sum.symm] + at hw + rw [Fs_homogeneous (1 / rr) (by positivity) _ _ hSA hSB s hs0 hs1] at hw + -- hw: (1/rr) * Σ Fᵢ ≤ (1/rr) * F(ΣA, ΣB) + exact le_of_mul_le_mul_left hw (by positivity : (0 : ℝ) < 1 / rr) + +end Matrix diff --git a/QuantumSystem/Analysis/Matrix/Order.lean b/QuantumSystem/Analysis/Matrix/Order.lean new file mode 100644 index 0000000..a7dd4d5 --- /dev/null +++ b/QuantumSystem/Analysis/Matrix/Order.lean @@ -0,0 +1,1022 @@ +module + +public import Mathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.Rpow.IntegralRepresentation +public import QuantumSystem.Analysis.Matrix.HermitianFunctionalCalculus +public import QuantumSystem.Analysis.Matrix.PosDef +public import QuantumSystem.ForMathlib.Analysis.Matrix.Basic +public import QuantumSystem.ForMathlib.Analysis.Matrix.Order + +/-! +# Effros's Matrix Convexity Approach + +This file formalises the Effros (2008) machinery used to prove Lieb's joint concavity theorem +and related operator-convexity results. + +## Main definitions + +- `Matrix.IsLownerMonotone f`: A ≤ B ⇒ f(A) ≤ f(B) in the Löwner order. +- `Matrix.IsLownerConvex f`: f(tA + (1-t)B) ≤ t f(A) + (1-t)f(B) in the Löwner order. +- `Matrix.IsLownerConcave f`: −f is Löwner convex. +- `Matrix.IsJensenConvex f`: for Löwner convex f and A†A + B†B ≤ I, + f(A† T₁ A + B† T₂ B) ≤ A† f(T₁) A + B† f(T₂) B. +- `Matrix.IsJensenConcave f`: −f is Jensen convex. + +## Main results + +- `Matrix.isJensenConvex_of_isLownerConvex`: Löwner convexity with f(0) ≤ 0 implies + Jensen (HPJ) convexity. Follows the defect-matrix proof of Hansen-Pedersen 1981. +- `Matrix.rpow_isLownerConcave`: the power function tˢ (0 < s ≤ 1) is Löwner concave. + Proved via the Stieltjes integral representation of xˢ and pointwise resolvent concavity. +- `Matrix.neg_rpow_isLownerConvex`: −tˢ is Löwner convex. +- `Matrix.neg_rpow_isJensenConvex`: −tˢ is Jensen convex. +- `Matrix.hpj_subhomogeneous`: HPJ inequality for A†A + B†B ≤ I. +- `Matrix.hpj_affine`: HPJ inequality for A†A + B†B = I. + +## References + +* Effros, *A Matrix Convexity Approach to Some Celebrated Quantum Inequalities* (2008) +* Hansen, Pedersen, *Jensen's operator inequality* (1981) +* Bhatia, *Matrix Analysis*, Theorem V.2.5 (1997) +-/ +@[expose] public section + +namespace Matrix + +open Real NNReal MeasureTheory Set +open scoped MatrixOrder ComplexOrder + +/-- A real function f is Löwner monotone on positive semidefinite matrices if +A ≤ B (in the Löwner order) implies f(A) ≤ f(B). -/ +def IsLownerMonotone (f : ℝ → ℝ) : Prop := + ∀ (m : Type*) [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef), + A ≤ B → + let fA := matrixFunction (fun x => (f x : ℂ)) A hA.1 + let fB := matrixFunction (fun x => (f x : ℂ)) B hB.1 + fA ≤ fB + +/-- A real function f is Löwner convex if +f(tA + (1-t)B) ≤ t · f(A) + (1-t) · f(B) in the Löwner order for all t ∈ [0,1]. -/ +def IsLownerConvex (f : ℝ → ℝ) : Prop := + ∀ (m : Type*) [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) (t : ℝ), + 0 ≤ t → t ≤ 1 → + ∀ (hC : (t • A + (1 - t) • B).IsHermitian), + let fA := matrixFunction (fun x => (f x : ℂ)) A hA.1 + let fB := matrixFunction (fun x => (f x : ℂ)) B hB.1 + let fC := matrixFunction (fun x => (f x : ℂ)) (t • A + (1 - t) • B) hC + fC ≤ t • fA + (1 - t) • fB + +/-- A real function f is Löwner concave if −f is Löwner convex. -/ +def IsLownerConcave (f : ℝ → ℝ) : Prop := + ∀ (m : Type*) [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) (t : ℝ), + 0 ≤ t → t ≤ 1 → + ∀ (hC : (t • A + (1 - t) • B).IsHermitian), + let fA := matrixFunction (fun x => Complex.ofReal (-f x)) A hA.1 + let fB := matrixFunction (fun x => Complex.ofReal (-f x)) B hB.1 + let fC := matrixFunction (fun x => Complex.ofReal (-f x)) (t • A + (1 - t) • B) hC + fC ≤ t • fA + (1 - t) • fB + +/-- Jensen convexity (HPJ sense): compression inequality for two terms. +For A†A + B†B ≤ I and PSD T₁, T₂: +f(A† T₁ A + B† T₂ B) ≤ A† f(T₁) A + B† f(T₂) B. -/ +def IsJensenConvex (f : ℝ → ℝ) : Prop := + ∀ (m : Type*) [Fintype m] [DecidableEq m] + (A B T₁ T₂ : Matrix m m ℂ) + (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) + (_hAB : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ)) + (hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian), + let fT₁ := matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 + let fT₂ := matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 + let fC := matrixFunction (fun x => (f x : ℂ)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + fC ≤ Aᴴ * fT₁ * A + Bᴴ * fT₂ * B + +/-- Jensen concavity in the HPJ sense: −f is Jensen convex. -/ +def IsJensenConcave (f : ℝ → ℝ) : Prop := + ∀ (m : Type*) [Fintype m] [DecidableEq m] + (A B T₁ T₂ : Matrix m m ℂ) + (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) + (_hAB : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ)) + (hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian), + let fT₁ := matrixFunction (fun x => Complex.ofReal (-f x)) T₁ hT₁.1 + let fT₂ := matrixFunction (fun x => Complex.ofReal (-f x)) T₂ hT₂.1 + let fC := matrixFunction (fun x => Complex.ofReal (-f x)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + fC ≤ Aᴴ * fT₁ * A + Bᴴ * fT₂ * B + +/-- Block diagonal matrix is positive semidefinite if blocks are positive semidefinite. -/ +private lemma fromBlocks_posSemidef_diag {m n : Type*} [Fintype m] [Fintype n] + {A : Matrix m m ℂ} {D : Matrix n n ℂ} + (hA : A.PosSemidef) (hD : D.PosSemidef) : + (Matrix.fromBlocks A 0 0 D).PosSemidef := by + classical + refine PosSemidef.of_dotProduct_mulVec_nonneg ?_ ?_ + · -- Hermitian + simpa using (Matrix.IsHermitian.fromBlocks (A := A) (B := (0 : Matrix m n ℂ)) + (C := (0 : Matrix n m ℂ)) (D := D) hA.1 (by simp) hD.1) + · intro v + -- Split the vector into left/right blocks. + let v₁ : m → ℂ := fun i => v (Sum.inl i) + let v₂ : n → ℂ := fun i => v (Sum.inr i) + have hleft : + (star v ⬝ᵥ (Matrix.fromBlocks A 0 0 D *ᵥ v)).re = + (star v₁ ⬝ᵥ (A *ᵥ v₁)).re + (star v₂ ⬝ᵥ (D *ᵥ v₂)).re := by + -- Compute dotProduct with block structure. + classical + simp [dotProduct, Fintype.sum_sum_type, fromBlocks_mulVec_inl, fromBlocks_mulVec_inr, + v₁, v₂, Finset.sum_add_distrib, Complex.add_re] + have hA_nonneg : 0 ≤ (star v₁ ⬝ᵥ (A *ᵥ v₁)).re := hA.re_dotProduct_nonneg v₁ + have hD_nonneg : 0 ≤ (star v₂ ⬝ᵥ (D *ᵥ v₂)).re := hD.re_dotProduct_nonneg v₂ + have hsum_nonneg : + 0 ≤ (star v₁ ⬝ᵥ (A *ᵥ v₁)).re + (star v₂ ⬝ᵥ (D *ᵥ v₂)).re := + add_nonneg hA_nonneg hD_nonneg + have hreal : 0 ≤ (star v ⬝ᵥ (Matrix.fromBlocks A 0 0 D *ᵥ v)).re := by + simpa [hleft] using hsum_nonneg + have him : (star v ⬝ᵥ (Matrix.fromBlocks A 0 0 D *ᵥ v)).im = 0 := by + apply IsHermitian.quadForm_im_eq_zero + simpa using (Matrix.IsHermitian.fromBlocks (A := A) (B := (0 : Matrix m n ℂ)) + (C := (0 : Matrix n m ℂ)) (D := D) hA.1 (by simp) hD.1) + exact (Complex.nonneg_iff).2 ⟨hreal, him.symm⟩ + +/-- Fundamental compression inequality for Löwner convex functions. +For Löwner convex f with f(0) ≤ 0, and V with V†V ≤ I (contraction), +the compression satisfies f(V†TV) ≤ V†f(T)V. + +The proof uses the defect technique: let D = √(I - V†V), W = [V; D], T' = T ⊕ 0. +Then W is an isometry (W†W = I), and: +- W†T'W = V†TV (the compression) +- W†f(T')W = V†f(T)V + f(0)·D†D = V†f(T)V + f(0)·(I - V†V) + +The matrix Jensen inequality gives f(W†T'W) ≤ W†f(T')W for Löwner convex f. +Since f(0) ≤ 0 and I - V†V ≥ 0, we have f(0)·(I - V†V) ≤ 0. +Thus f(V†TV) ≤ V†f(T)V + f(0)·(I - V†V) ≤ V†f(T)V. -/ +lemma lownerConvex_compression_le.{v} {n : Type v} {m : Type v} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + {f : ℝ → ℝ} (hconv : IsLownerConvex.{v} f) (hf0 : f 0 ≤ 0) + (V : Matrix n m ℂ) (hVV : Vᴴ * V ≤ 1) + (T : Matrix n n ℂ) (hT : T.PosSemidef) : + matrixFunction (fun x => (f x : ℂ)) (Vᴴ * T * V) + (isHermitian_conjTranspose_mul_mul (B := V) (A := T) hT.1) ≤ + Vᴴ * matrixFunction (fun x => (f x : ℂ)) T hT.1 * V := by + -- The proof uses the defect technique and the block diagonal CFC formula. + classical + -- Step 1: Setup the defect matrix D = √(I - V†V) + have hΔ : ((1 : Matrix m m ℂ) - Vᴴ * V).PosSemidef := by + simpa [Matrix.le_iff] using hVV + let D := matrixSqrt ((1 : Matrix m m ℂ) - Vᴴ * V) hΔ + have hD_herm : D.IsHermitian := matrixSqrt_isHermitian hΔ + have hDD : D * D = (1 : Matrix m m ℂ) - Vᴴ * V := matrixSqrt_mul_self_posSemidef hΔ + -- D†D = DD since D is Hermitian (D† = D) + have hDhD : Dᴴ * D = (1 : Matrix m m ℂ) - Vᴴ * V := by + rw [hD_herm.eq, hDD] + -- V†V + D†D = I + have hsum : Vᴴ * V + Dᴴ * D = (1 : Matrix m m ℂ) := by + rw [hDhD]; simp + -- Step 2: Create the extended block diagonal matrix T' = T ⊕ 0 + let T' := Matrix.fromBlocks T 0 0 (0 : Matrix m m ℂ) + have hT'_psd : T'.PosSemidef := by + have h0_psd : (0 : Matrix m m ℂ).PosSemidef := Matrix.PosSemidef.zero + exact fromBlocks_posSemidef_diag hT h0_psd + have hT'_herm : T'.IsHermitian := hT'_psd.1 + -- Step 3: Create the extended contraction W = [V; D] : (n ⊕ m) → m + -- Here V : n → m and D : m → m, stacked vertically + let W : Matrix (n ⊕ m) m ℂ := Matrix.fromRows V D + -- W†W = V†V + D†D = I (isometry property) + have hWW : Wᴴ * W = (1 : Matrix m m ℂ) := by + simp only [W, fromRows_conjTranspose_mul_self, hsum] + -- Step 4: Compute W†T'W = V†TV + have hWTW : Wᴴ * T' * W = Vᴴ * T * V := by + have h := fromRows_compress_blockDiag V D T (0 : Matrix m m ℂ) + simp only [W, T'] at h ⊢ + rw [h] + simp only [Matrix.mul_zero, Matrix.zero_mul, add_zero] + -- Step 5: Relate matrixFunction to CFC + have hfT_eq : matrixFunction (fun x => (f x : ℂ)) T hT.1 = cfc f T := + matrixFunction_eq_cfc hT.1 f + -- Step 6-7: W†f(T')W = V†f(T)V + f(0)·D†D + have hWfTW : Wᴴ * cfc f T' * W = + Vᴴ * cfc f T * V + (f 0 : ℂ) • (Dᴴ * D) := by + have hT_sa : IsSelfAdjoint T := by + simpa [IsSelfAdjoint, star_eq_conjTranspose] using hT.1 + have h0_sa : IsSelfAdjoint (0 : Matrix m m ℂ) := by + simp [IsSelfAdjoint] + have hfinite : (spectrum ℝ T ∪ spectrum ℝ (0 : Matrix m m ℂ)).Finite := + (Matrix.finite_real_spectrum (A := T)).union + (Matrix.finite_real_spectrum (A := (0 : Matrix m m ℂ))) + have hcont : ContinuousOn f (spectrum ℝ T ∪ spectrum ℝ (0 : Matrix m m ℂ)) := + Set.Finite.continuousOn hfinite f + have hfT' : cfc f T' = Matrix.fromBlocks (cfc f T) 0 0 (cfc f (0 : Matrix m m ℂ)) := + cfc_fromBlocks_diag' T (0 : Matrix m m ℂ) hT_sa h0_sa f hcont + have hf0_mat : cfc f (0 : Matrix m m ℂ) = (f 0 : ℂ) • (1 : Matrix m m ℂ) := by + rw [cfc_apply_zero] + simp only [Algebra.algebraMap_eq_smul_one] + ext i j + simp only [smul_apply, smul_eq_mul, one_apply, Complex.real_smul] + have hfT'_expanded : cfc f T' = Matrix.fromBlocks (cfc f T) 0 0 ((f 0 : ℂ) • 1) := by + rw [hfT', hf0_mat] + rw [hfT'_expanded] + have h := fromRows_compress_blockDiag V D (cfc f T) ((f 0 : ℂ) • (1 : Matrix m m ℂ)) + simp only [W] at h ⊢ + rw [h] + simp only [Matrix.mul_smul, Matrix.smul_mul, Matrix.mul_one] + -- Step 8: Apply the matrix Jensen inequality + have hVTV_herm := isHermitian_conjTranspose_mul_mul (B := V) (A := T) hT.1 + have hDD_psd : (Dᴴ * D).PosSemidef := by + rw [hDhD]; exact hΔ + have hf0_term_le : (f 0 : ℂ) • (Dᴴ * D) ≤ (0 : Matrix m m ℂ) := by + have h := Matrix.PosSemidef.smul_nonpos hf0 hDD_psd + have heq : (f 0 : ℂ) • (Dᴴ * D) = (f 0 : ℝ) • (Dᴴ * D) := by + ext i j; simp only [smul_apply, Complex.real_smul, smul_eq_mul] + rw [heq] + exact h + have hWfTW' : Wᴴ * cfc f T' * W = Vᴴ * cfc f T * V + (f 0 : ℂ) • (Dᴴ * D) := hWfTW + rw [hfT_eq] + have hfVTV_eq : matrixFunction (fun x => (f x : ℂ)) (Vᴴ * T * V) hVTV_herm = + cfc f (Vᴴ * T * V) := matrixFunction_eq_cfc hVTV_herm f + rw [hfVTV_eq] + have h_jensen : cfc f (Wᴴ * T' * W) ≤ Wᴴ * cfc f T' * W := by + set P : Matrix (n ⊕ m) (n ⊕ m) ℂ := W * Wᴴ with hP_def + have hP_sq : P * P = P := by + change W * Wᴴ * (W * Wᴴ) = W * Wᴴ + rw [Matrix.mul_assoc W Wᴴ (W * Wᴴ), + show Wᴴ * (W * Wᴴ) = (Wᴴ * W) * Wᴴ from (Matrix.mul_assoc _ _ _).symm, + hWW, Matrix.one_mul] + have hP_herm : Pᴴ = P := by + change (W * Wᴴ)ᴴ = W * Wᴴ + rw [conjTranspose_mul, conjTranspose_conjTranspose] + set S : Matrix (n ⊕ m) (n ⊕ m) ℂ := (2 : ℝ) • P - 1 with hS_def + have h2P : (2 : ℝ) • P = P + P := two_smul ℝ P + have hS_herm : Sᴴ = S := by + rw [hS_def, h2P, conjTranspose_sub, conjTranspose_one, conjTranspose_add, + hP_herm] + have hS_sq : S * S = 1 := by + rw [hS_def, h2P] + have hPstep : P * (P + P - 1) = P := by + rw [mul_sub, mul_add, hP_sq, mul_one, add_sub_cancel_right] + rw [sub_mul, one_mul, add_mul, hPstep] + abel + have hS_star_eq : star S = S := by + rw [star_eq_conjTranspose, hS_herm] + have hS_mem_unitary : S ∈ unitary (Matrix (n ⊕ m) (n ⊕ m) ℂ) := by + rw [Unitary.mem_iff]; exact ⟨by rw [hS_star_eq, hS_sq], by rw [hS_star_eq, hS_sq]⟩ + let S_unit : unitary (Matrix (n ⊕ m) (n ⊕ m) ℂ) := ⟨S, hS_mem_unitary⟩ + have hPW : P * W = W := by + change W * Wᴴ * W = W + rw [Matrix.mul_assoc, hWW, Matrix.mul_one] + have hSP : S * P = P := by + rw [hS_def, h2P, sub_mul, one_mul, add_mul, hP_sq, add_sub_cancel_right] + have hPS : P * S = P := by + rw [hS_def, h2P, mul_sub, mul_one, mul_add, hP_sq, add_sub_cancel_right] + have hSW : S * W = W := by + have h : (S * P) * W = P * W := by rw [hSP] + rw [Matrix.mul_assoc] at h; rwa [hPW] at h + have hWhS : Wᴴ * S = Wᴴ := by + have h := congr_arg Matrix.conjTranspose hSW + rwa [conjTranspose_mul, hS_herm] at h + have hST'S_psd : (S * T' * S).PosSemidef := by + have h := hT'_psd.conjTranspose_mul_mul_same S + rwa [hS_herm] at h + have hST'S_herm : (S * T' * S).IsHermitian := hST'S_psd.1 + have hM_herm : ((1/2 : ℝ) • T' + (1 - 1/2 : ℝ) • (S * T' * S)).IsHermitian := + IsHermitian.add_isHermitian (IsHermitian.smul_real hT'_herm (1/2)) + (IsHermitian.smul_real hST'S_herm (1 - 1/2)) + set M : Matrix (n ⊕ m) (n ⊕ m) ℂ := (1/2 : ℝ) • T' + (1/2 : ℝ) • (S * T' * S) with hM_def + have hM_eq : M = (1/2 : ℝ) • T' + (1 - 1/2 : ℝ) • (S * T' * S) := by + simp only [hM_def]; congr 1; congr 1; norm_num + have hM_herm' : M.IsHermitian := by rw [hM_eq]; exact hM_herm + have hconv_app := hconv (n ⊕ m) T' (S * T' * S) hT'_psd hST'S_psd (1/2) + (by norm_num) (by norm_num) hM_herm + have hfT'_eq : matrixFunction (fun x => (f x : ℂ)) T' hT'_herm = cfc f T' := + matrixFunction_eq_cfc hT'_herm f + have hfST'S_eq : matrixFunction (fun x => (f x : ℂ)) (S * T' * S) hST'S_herm = + cfc f (S * T' * S) := matrixFunction_eq_cfc hST'S_herm f + rw [hfT'_eq, hfST'S_eq] at hconv_app + have hfM_conv : matrixFunction (fun x => (f x : ℂ)) + ((1 / 2 : ℝ) • T' + (1 - 1 / 2 : ℝ) • (S * T' * S)) hM_herm = cfc f M := + (matrixFunction_congr _ hM_herm hM_herm' hM_eq.symm).trans + (matrixFunction_eq_cfc hM_herm' f) + rw [hfM_conv] at hconv_app + have hT'_sa : IsSelfAdjoint T' := by + rwa [IsSelfAdjoint, star_eq_conjTranspose] + have hcfc_conj : S * cfc f T' * S = cfc f (S * T' * S) := by + have h : S * cfc f T' * star S = cfc f (S * T' * star S) := + cfc_unitary_conjugation' S_unit T' hT'_sa f + (Set.Finite.continuousOn (Matrix.finite_real_spectrum) f) + rwa [star_eq_conjTranspose, hS_herm] at h + have hM_comm : M * (W * Wᴴ) = (W * Wᴴ) * M := by + rw [← hP_def] + suffices h : M * P = P * M from h + rw [hM_def] + rw [Matrix.add_mul, Matrix.mul_add, Matrix.smul_mul, Matrix.smul_mul, + Matrix.mul_smul, Matrix.mul_smul, + show S * T' * S * P = S * T' * (S * P) from by + simp only [Matrix.mul_assoc], hSP, + show P * (S * T' * S) = (P * S) * T' * S from by + simp only [Matrix.mul_assoc], hPS, + ← smul_add, ← smul_add] + congr 1 + have hST'P : S * T' * P = P * T' * P + P * T' * P - T' * P := by + rw [hS_def, h2P, sub_mul, one_mul, add_mul, sub_mul, add_mul] + have hPT'S : P * T' * S = P * T' * P + P * T' * P - P * T' := by + rw [hS_def, h2P, mul_sub, mul_one, mul_add] + rw [hST'P, hPT'S]; abel + have hWMW : Wᴴ * M * W = Wᴴ * T' * W := by + rw [hM_def, + Matrix.mul_add, Matrix.add_mul, Matrix.mul_smul, Matrix.smul_mul, + Matrix.mul_smul, Matrix.smul_mul, + show Wᴴ * (S * T' * S) * W = (Wᴴ * S) * T' * (S * W) from by + simp only [Matrix.mul_assoc], + hWhS, hSW, ← smul_add, ← two_smul ℝ (Wᴴ * T' * W), smul_smul, + show (1 / 2 : ℝ) * 2 = 1 from by norm_num, one_smul] + have hWMW_herm : (Wᴴ * M * W).IsHermitian := + isHermitian_conjTranspose_mul_mul (B := W) (A := M) hM_herm' + have h_comp := matrixFunction_compression_of_commuting W M hM_herm' hWW hM_comm f hWMW_herm + rw [matrixFunction_eq_cfc hM_herm' f, matrixFunction_eq_cfc hWMW_herm f, hWMW] at h_comp + have h_compress := compression_le hconv_app W + rw [h_comp] at h_compress + have h_half : (1 - 1 / 2 : ℝ) = (1 / 2 : ℝ) := by norm_num + calc cfc f (Wᴴ * T' * W) + ≤ Wᴴ * ((1 / 2 : ℝ) • cfc f T' + (1 - 1 / 2 : ℝ) • cfc f (S * T' * S)) * W := + h_compress + _ = Wᴴ * cfc f T' * W := by + rw [h_half, ← hcfc_conj, + Matrix.mul_add, Matrix.add_mul, Matrix.mul_smul, Matrix.smul_mul, + Matrix.mul_smul, Matrix.smul_mul, + show Wᴴ * (S * cfc f T' * S) * W = (Wᴴ * S) * cfc f T' * (S * W) from by + simp only [Matrix.mul_assoc], + hWhS, hSW, ← smul_add, + show Wᴴ * cfc f T' * W + Wᴴ * cfc f T' * W = (2 : ℝ) • (Wᴴ * cfc f T' * W) from + (two_smul ℝ _).symm, + smul_smul, show (1 / 2 : ℝ) * 2 = 1 from by norm_num, + one_smul] + rw [hWTW] at h_jensen + rw [hWfTW'] at h_jensen + calc cfc f (Vᴴ * T * V) + ≤ Vᴴ * cfc f T * V + (f 0 : ℂ) • (Dᴴ * D) := h_jensen + _ ≤ Vᴴ * cfc f T * V + 0 := add_le_add (le_refl _) hf0_term_le + _ = Vᴴ * cfc f T * V := by simp + +private lemma fromRows_defect_sqrt {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hAB : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ)) : + let V := Matrix.fromRows A B + let Δ := (1 : Matrix m m ℂ) - Vᴴ * V + let D := matrixSqrt Δ (Matrix.PosSemidef.one_sub_fromRows (A := A) (B := B) hAB) + Dᴴ * D = Δ := by + intro V Δ D + have hΔ : Δ.PosSemidef := by + simpa [Δ, V] using Matrix.PosSemidef.one_sub_fromRows (A := A) (B := B) hAB + calc + Dᴴ * D = D * D := by + have hherm : D.IsHermitian := by + simpa [D, Δ, V] using matrixSqrt_isHermitian hΔ + simp [hherm.eq] + _ = Δ := by + simpa [D] using matrixSqrt_mul_self_posSemidef hΔ + +/-- The compression V†f(T)V for block diagonal T equals + A†f(T₁)A + B†f(T₂)B when V = [A; B] and T = T₁ ⊕ T₂. -/ +private lemma compression_of_fromBlocks_cfc {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (T₁ T₂ : Matrix m m ℂ) + (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) (f : ℝ → ℝ) : + let V := Matrix.fromRows A B + let T := Matrix.fromBlocks T₁ 0 0 T₂ + let hT_herm : T.IsHermitian := by + simpa using Matrix.IsHermitian.fromBlocks hT₁.1 (by simp : (0 : Matrix m m ℂ).IsHermitian) hT₂.1 + Vᴴ * matrixFunction (fun x => (f x : ℂ)) T hT_herm * V = + Aᴴ * matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A + + Bᴴ * matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * B := by + classical + intro V T hT_herm + -- Use the CFC block diagonal formula. + have hT_cfc : + matrixFunction (fun x => (f x : ℂ)) T hT_herm = + Matrix.fromBlocks + (matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1) 0 0 + (matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1) := by + have hT_cfc' : matrixFunction (fun x => (f x : ℂ)) T hT_herm = cfc f T := + matrixFunction_eq_cfc hT_herm f + have hT₁_cfc : matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 = cfc f T₁ := + matrixFunction_eq_cfc hT₁.1 f + have hT₂_cfc : matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 = cfc f T₂ := + matrixFunction_eq_cfc hT₂.1 f + have hT_sa : IsSelfAdjoint T := by + simpa [IsSelfAdjoint, Matrix.IsHermitian, star_eq_conjTranspose] using hT_herm + have hT₁_sa : IsSelfAdjoint T₁ := by + simpa [IsSelfAdjoint, Matrix.IsHermitian, star_eq_conjTranspose] using hT₁.1 + have hT₂_sa : IsSelfAdjoint T₂ := by + simpa [IsSelfAdjoint, Matrix.IsHermitian, star_eq_conjTranspose] using hT₂.1 + have hfinite : (spectrum ℝ T₁ ∪ spectrum ℝ T₂).Finite := + (Matrix.finite_real_spectrum (A := T₁)).union (Matrix.finite_real_spectrum (A := T₂)) + have hcont : ContinuousOn f (spectrum ℝ T₁ ∪ spectrum ℝ T₂) := + Set.Finite.continuousOn hfinite f + have hblock := cfc_fromBlocks_diag (m := m) (A := T₁) (D := T₂) hT₁_sa hT₂_sa f hcont + calc + matrixFunction (fun x => (f x : ℂ)) T hT_herm = cfc f T := hT_cfc' + _ = Matrix.fromBlocks (cfc f T₁) 0 0 (cfc f T₂) := by simpa [T] using hblock + _ = Matrix.fromBlocks + (matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1) 0 0 + (matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1) := by simp [hT₁_cfc, hT₂_cfc] + rw [hT_cfc] + simpa [V] using fromRows_compress_blockDiag + (A := A) (B := B) + (T₁ := matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1) + (T₂ := matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1) + +/-- IsLownerConvex + f(0) ≤ 0 implies HPJ inequality (Matrix Convexity). +Theorem 3.1 in Effros 2008, originally Hansen-Pedersen 1981 Theorem 2.1 (i)⟹(iii). + +The proof reduces the 2-term subhomogeneous case to: +1. A single-term compression inequality: f(V†TV) ≤ V†f(T)V when V†V ≤ I +2. The block diagonal CFC identity: V†f(T₁⊕T₂)V = A†f(T₁)A + B†f(T₂)B + +Step 1 uses the defect matrix D = √(I - V†V) and f(0) ≤ 0 to absorb the defect term. +Step 2 is compression_of_fromBlocks_cfc (already proved). -/ +lemma isJensenConvex_of_isLownerConvex.{v} + {f : ℝ → ℝ} (hconv : IsLownerConvex.{v} f) (hf0 : f 0 ≤ 0) : + IsJensenConvex.{v} f := by + classical + intro m _ _ A B T₁ T₂ hT₁ hT₂ hAB hC + -- Step 1: Set up block diagonal T = T₁ ⊕ T₂ and V = fromRows A B + let V := Matrix.fromRows A B + let T := Matrix.fromBlocks T₁ 0 0 T₂ + have hT_psd : T.PosSemidef := fromBlocks_posSemidef_diag hT₁ hT₂ + -- Step 2: V†TV = A†T₁A + B†T₂B (block multiplication) + have hVTV : Vᴴ * T * V = Aᴴ * T₁ * A + Bᴴ * T₂ * B := + fromRows_compress_blockDiag A B T₁ T₂ + -- Step 3: V†f(T)V = A†f(T₁)A + B†f(T₂)B (block diagonal CFC) + have hVfTV : Vᴴ * matrixFunction (fun x => (f x : ℂ)) T + (by simpa using Matrix.IsHermitian.fromBlocks hT₁.1 (by simp : (0 : Matrix m m ℂ).IsHermitian) hT₂.1) * V = + Aᴴ * matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A + + Bᴴ * matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * B := + compression_of_fromBlocks_cfc A B T₁ T₂ hT₁ hT₂ f + have hΔ := Matrix.PosSemidef.one_sub_fromRows A B hAB + let Δ := (1 : Matrix m m ℂ) - Vᴴ * V + let D := matrixSqrt Δ hΔ + have hDD : Dᴴ * D = Δ := fromRows_defect_sqrt A B hAB + have hsum : Vᴴ * V + Dᴴ * D = (1 : Matrix m m ℂ) := by + rw [hDD]; simp [Δ] + have hf0_neg : f 0 • (Dᴴ * D) ≤ (0 : Matrix m m ℂ) := by + have : (Dᴴ * D).PosSemidef := by + rw [hDD]; exact hΔ + exact Matrix.PosSemidef.smul_nonpos hf0 this + have hfC := matrixFunction_congr (fun x => (f x : ℂ)) hC + (isHermitian_conjTranspose_mul_mul (B := V) (A := T) hT_psd.1) hVTV.symm + have hT_herm : T.IsHermitian := by + simpa using Matrix.IsHermitian.fromBlocks hT₁.1 + (by simp : (0 : Matrix m m ℂ).IsHermitian) hT₂.1 + calc matrixFunction (fun x => (f x : ℂ)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + = matrixFunction (fun x => (f x : ℂ)) (Vᴴ * T * V) + (isHermitian_conjTranspose_mul_mul (B := V) (A := T) hT_psd.1) := hfC + _ ≤ Vᴴ * matrixFunction (fun x => (f x : ℂ)) T hT_herm * V := by + have hVV : Vᴴ * V ≤ 1 := by simpa [V, fromRows_conjTranspose_mul_self] using hAB + exact lownerConvex_compression_le hconv hf0 V hVV T hT_psd + _ = Aᴴ * matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A + + Bᴴ * matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * B := hVfTV + +/-- Matrix convexity of matrix inverse in the Löwner order. -/ +private lemma inv_lowner_convex_le {m : Type*} [Fintype m] [DecidableEq m] + {A B : Matrix m m ℂ} (hA : A.PosDef) (hB : B.PosDef) + {t : ℝ} (ht0 : 0 ≤ t) (ht1 : t ≤ 1) : + (t • A + (1 - t) • B)⁻¹ ≤ t • A⁻¹ + (1 - t) • B⁻¹ := by + classical + by_cases ht_zero : t = 0 + · subst ht_zero + simp + by_cases ht_one : t = 1 + · subst ht_one + simp + have ht_pos : 0 < t := lt_of_le_of_ne ht0 (Ne.symm ht_zero) + have h1t_pos : 0 < 1 - t := by + have ht1' : t < 1 := lt_of_le_of_ne ht1 ht_one + linarith + set C : Matrix m m ℂ := t • A + (1 - t) • B + have hC_pos : C.PosDef := hA.convex_comb hB ht_pos h1t_pos + let _ := hC_pos.isUnit.invertible + have hA_blk : (Matrix.fromBlocks A 1 1 A⁻¹).PosSemidef := fromBlocks_inv_posSemidef hA + have hB_blk : (Matrix.fromBlocks B 1 1 B⁻¹).PosSemidef := fromBlocks_inv_posSemidef hB + have hsum : + (t • Matrix.fromBlocks A 1 1 A⁻¹ + (1 - t) • Matrix.fromBlocks B 1 1 B⁻¹).PosSemidef := by + exact (Matrix.PosSemidef.add (Matrix.PosSemidef.smul hA_blk ht0) + (Matrix.PosSemidef.smul hB_blk (by linarith))) + have hblocks_eq : + t • Matrix.fromBlocks A 1 1 A⁻¹ + (1 - t) • Matrix.fromBlocks B 1 1 B⁻¹ = + Matrix.fromBlocks C 1 1 (t • A⁻¹ + (1 - t) • B⁻¹) := by + ext i j + cases i with + | inl i => + cases j with + | inl j => + by_cases h : i = j <;> + simp [C, h, fromBlocks_apply₁₁, Matrix.add_apply, Matrix.smul_apply] + | inr j => + by_cases h : i = j + · have hsum : (t : ℂ) + (1 - t) = (1 : ℂ) := by ring + simp [C, h, fromBlocks_apply₁₂, Matrix.add_apply, Matrix.smul_apply, hsum] + · simp [C, h, fromBlocks_apply₁₂, Matrix.add_apply, Matrix.smul_apply] + | inr i => + cases j with + | inl j => + by_cases h : i = j + · have hsum : (t : ℂ) + (1 - t) = (1 : ℂ) := by ring + simp [C, h, fromBlocks_apply₂₁, Matrix.add_apply, Matrix.smul_apply, hsum] + · simp [C, h, fromBlocks_apply₂₁, Matrix.add_apply, Matrix.smul_apply] + | inr j => + by_cases h : i = j <;> + simp [C, h, fromBlocks_apply₂₂, Matrix.add_apply, Matrix.smul_apply] + have hsum' : (Matrix.fromBlocks C 1 1 (t • A⁻¹ + (1 - t) • B⁻¹)).PosSemidef := by + simpa [hblocks_eq] using hsum + have hsum'' : + (Matrix.fromBlocks C 1 (1 : Matrix m m ℂ)ᴴ (t • A⁻¹ + (1 - t) • B⁻¹)).PosSemidef := by + simpa using hsum' + have hSchur : + (t • A⁻¹ + (1 - t) • B⁻¹ - (1 : Matrix m m ℂ)ᴴ * C⁻¹ * (1 : Matrix m m ℂ)).PosSemidef := + (Matrix.PosDef.fromBlocks₁₁ (B := (1 : Matrix m m ℂ)) (D := t • A⁻¹ + (1 - t) • B⁻¹) hC_pos).1 + hsum'' + rw [Matrix.le_iff] + simpa [C] using hSchur + +/-- Matrix concavity of `X ↦ 1 - r * (X + rI)⁻¹` for `r > 0`. -/ +private lemma resolvent_lowner_concave_le {m : Type*} [Fintype m] [DecidableEq m] + {A B : Matrix m m ℂ} (hA : A.PosSemidef) (hB : B.PosSemidef) + {t : ℝ} (ht0 : 0 ≤ t) (ht1 : t ≤ 1) {r : ℝ} (hr : 0 < r) : + t • (1 - r • (A + r • (1 : Matrix m m ℂ))⁻¹) + + (1 - t) • (1 - r • (B + r • (1 : Matrix m m ℂ))⁻¹) + ≤ 1 - r • ((t • A + (1 - t) • B) + r • (1 : Matrix m m ℂ))⁻¹ := by + classical + set A' : Matrix m m ℂ := A + r • (1 : Matrix m m ℂ) + set B' : Matrix m m ℂ := B + r • (1 : Matrix m m ℂ) + set C : Matrix m m ℂ := t • A + (1 - t) • B + set C' : Matrix m m ℂ := C + r • (1 : Matrix m m ℂ) + have hA' : A'.PosDef := PosSemidef.add_smul_one_posDef hA hr + have hB' : B'.PosDef := PosSemidef.add_smul_one_posDef hB hr + have hconv : C'⁻¹ ≤ t • A'⁻¹ + (1 - t) • B'⁻¹ := by + have hA'' : A'.PosDef := hA' + have hB'' : B'.PosDef := hB' + have hC' : C' = t • A' + (1 - t) • B' := by + dsimp [A', B', C', C] + module + simpa [hC'] using inv_lowner_convex_le hA'' hB'' ht0 ht1 + have hconv_psd : (t • A'⁻¹ + (1 - t) • B'⁻¹ - C'⁻¹).PosSemidef := by + simpa [Matrix.le_iff] using hconv + have hconv_psd' : (r • (t • A'⁻¹ + (1 - t) • B'⁻¹ - C'⁻¹)).PosSemidef := by + exact hconv_psd.smul (by linarith : 0 ≤ r) + rw [Matrix.le_iff] + -- Reduce to the PSD of the inverse convexity difference. + have hcalc : + (1 - r • C'⁻¹) - (t • (1 - r • A'⁻¹) + (1 - t) • (1 - r • B'⁻¹)) = + r • (t • A'⁻¹ + (1 - t) • B'⁻¹ - C'⁻¹) := by + module + simpa [hcalc, A', B', C', C] using hconv_psd' + +/-- Core operator concavity lemma for matrices. +Uses the integral representation of xˢ and resolvent operator concavity. + +The key mathematical fact: For 0 < s ≤ 1, the function x ↦ x^s is operator +concave on positive semidefinite matrices. This means: + (tA + (1-t)B)^s ≥ t·A^s + (1-t)·B^s +for any PSD matrices A, B and t ∈ [0,1]. + +**Proof Strategy**: +1. Use the integral representation of xˢ via `exists_measure_nnrpow_eq_integral_cfcₙ_rpowIntegrand₀₁`. +2. Identify the integrand with the resolvent form `1 - u * (x + u)⁻¹` using CFC. +3. Apply the resolvent operator concavity inequality pointwise in u. +4. Integrate and rewrite with `matrixFunction_rpow_eq` to conclude the inequality. -/ +private lemma rpow_operator_concave_le {m : Type*} [Fintype m] [DecidableEq m] [Fintype (m × m)] + {s : ℝ} (hs0 : 0 < s) (hs1 : s ≤ 1) + (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) + (t : ℝ) (ht0 : 0 ≤ t) (ht1 : t ≤ 1) + (hC : (t • A + (1 - t) • B).IsHermitian) : + t • matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 + + (1 - t) • matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) B hB.1 ≤ + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) (t • A + (1 - t) • B) hC := by + classical + by_cases hs_eq : s = 1 + · subst hs_eq + simp [Real.rpow_one, matrixFunction_id] + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CompleteSpace (Matrix m m ℂ) := by infer_instance + letI : NonUnitalCStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using + (CStarMatrix.instNonUnitalCStarAlgebra (n := m) (A := ℂ)) + letI : NonUnitalContinuousFunctionalCalculus ℝ (Matrix m m ℂ) IsSelfAdjoint := by + infer_instance + have hs_lt : s < 1 := lt_of_le_of_ne hs1 hs_eq + let q : ℝ≥0 := ⟨s, le_of_lt hs0⟩ + have hq : (q : ℝ) ∈ Set.Ioo (0 : ℝ) 1 := ⟨hs0, hs_lt⟩ + obtain ⟨μ, hμ⟩ := + CFC.exists_measure_nnrpow_eq_integral_cfcₙ_rpowIntegrand₀₁ + (A := Matrix m m ℂ) hq + set C : Matrix m m ℂ := t • A + (1 - t) • B + have hA0 : (0 : Matrix m m ℂ) ≤ A := by + simpa [Matrix.le_iff] using hA + have hB0 : (0 : Matrix m m ℂ) ≤ B := by + simpa [Matrix.le_iff] using hB + have hCpsd : C.PosSemidef := (hA.smul ht0).add (hB.smul (by linarith)) + have hC0 : (0 : Matrix m m ℂ) ≤ C := by + simpa [Matrix.le_iff, C] using hCpsd + have hA_int : IntegrableOn (fun u => cfcₙ (rpowIntegrand₀₁ q u) A) (Ioi 0) μ := + (hμ A hA0).1 + have hB_int : IntegrableOn (fun u => cfcₙ (rpowIntegrand₀₁ q u) B) (Ioi 0) μ := + (hμ B hB0).1 + have hC_int : IntegrableOn (fun u => cfcₙ (rpowIntegrand₀₁ q u) C) (Ioi 0) μ := + (hμ C hC0).1 + have h_integrand_le : + (fun u => t • cfcₙ (rpowIntegrand₀₁ q u) A + + (1 - t) • cfcₙ (rpowIntegrand₀₁ q u) B) ≤ᵐ[μ.restrict (Ioi 0)] + fun u => cfcₙ (rpowIntegrand₀₁ q u) C := by + filter_upwards [ae_restrict_mem measurableSet_Ioi] with u hu + have hu' : 0 < u := hu + -- Express the integrand via the resolvent concavity lemma. + have hcont_Ici : ContinuousOn (fun x => rpowIntegrand₀₁ (q : ℝ) u x) (Ici 0) := by + have hcont_add : ContinuousOn (fun x => u + x) (Ici 0) := by + fun_prop + have hne : ∀ x ∈ Ici 0, u + x ≠ 0 := by + intro x hx + have hx' : 0 ≤ x := hx + linarith + have hcont_inv : ContinuousOn (fun x => (u + x)⁻¹) (Ici 0) := + ContinuousOn.inv₀ hcont_add hne + have hcont_sub : ContinuousOn (fun x => u⁻¹ - (u + x)⁻¹) (Ici 0) := by + simpa using (ContinuousOn.sub continuousOn_const hcont_inv) + simpa [Real.rpowIntegrand₀₁] using (ContinuousOn.mul continuousOn_const hcont_sub) + have hA_spec : quasispectrum ℝ A ⊆ Ici 0 := by + intro x hx + exact (StarOrderedRing.nonneg_iff_quasispectrum_nonneg (A := Matrix m m ℂ) A).1 hA0 x hx + have hB_spec : quasispectrum ℝ B ⊆ Ici 0 := by + intro x hx + exact (StarOrderedRing.nonneg_iff_quasispectrum_nonneg (A := Matrix m m ℂ) B).1 hB0 x hx + have hC_spec : quasispectrum ℝ C ⊆ Ici 0 := by + intro x hx + exact (StarOrderedRing.nonneg_iff_quasispectrum_nonneg (A := Matrix m m ℂ) C).1 hC0 x hx + have hA_eq : + cfcₙ (rpowIntegrand₀₁ q u) A = + matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) A hA.1 := by + calc + cfcₙ (rpowIntegrand₀₁ q u) A = + cfc (rpowIntegrand₀₁ (q : ℝ) u) A := by + simpa [Real.rpowIntegrand₀₁_zero_right] using + (cfcₙ_eq_cfc (a := A) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) + (hf := hcont_Ici.mono hA_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) + _ = _ := by + symm + exact matrixFunction_eq_cfc hA.1 (fun x => rpowIntegrand₀₁ (q : ℝ) u x) + have hB_eq : + cfcₙ (rpowIntegrand₀₁ q u) B = + matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) B hB.1 := by + calc + cfcₙ (rpowIntegrand₀₁ q u) B = + cfc (rpowIntegrand₀₁ (q : ℝ) u) B := by + simpa [Real.rpowIntegrand₀₁_zero_right] using + (cfcₙ_eq_cfc (a := B) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) + (hf := hcont_Ici.mono hB_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) + _ = _ := by + symm + exact matrixFunction_eq_cfc hB.1 (fun x => rpowIntegrand₀₁ (q : ℝ) u x) + have hC_eq : + cfcₙ (rpowIntegrand₀₁ q u) C = + matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) C hCpsd.1 := by + calc + cfcₙ (rpowIntegrand₀₁ q u) C = + cfc (rpowIntegrand₀₁ (q : ℝ) u) C := by + simpa [Real.rpowIntegrand₀₁_zero_right] using + (cfcₙ_eq_cfc (a := C) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) + (hf := hcont_Ici.mono hC_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) + _ = _ := by + symm + exact matrixFunction_eq_cfc hCpsd.1 (fun x => rpowIntegrand₀₁ (q : ℝ) u x) + have hfun : + (fun x : ℝ => rpowIntegrand₀₁ (q : ℝ) u x) = + fun x => u ^ (s - 1) * (1 - u * (x + u)⁻¹) := by + funext x + have hu0 : u ≠ 0 := ne_of_gt hu' + have hpow : u ^ s = u ^ (s - 1) * u := by + have h := Real.rpow_add_one hu0 (s - 1) + simpa [sub_eq_add_neg, add_comm, add_left_comm, add_assoc] using h + have hx : u + x = x + u := by ac_rfl + calc + rpowIntegrand₀₁ (q : ℝ) u x = u ^ s * (u⁻¹ - (u + x)⁻¹) := rfl + _ = u ^ (s - 1) * (u * (u⁻¹ - (u + x)⁻¹)) := by + simp [hpow, mul_assoc] + _ = u ^ (s - 1) * (1 - u * (x + u)⁻¹) := by + have hmul : u * (u⁻¹ - (u + x)⁻¹) = 1 - u * (x + u)⁻¹ := by + calc + u * (u⁻¹ - (u + x)⁻¹) = u * u⁻¹ - u * (u + x)⁻¹ := by + simp [mul_sub] + _ = 1 - u * (x + u)⁻¹ := by + simp [hu0, hx] + simp [hmul] + have hA_res : + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 = + (1 : Matrix m m ℂ) - (u : ℂ) • (A + (u : ℂ) • 1)⁻¹ := + matrixFunction_resolvent (m := m) hA (r := u) hu' + have hB_res : + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1 = + (1 : Matrix m m ℂ) - (u : ℂ) • (B + (u : ℂ) • 1)⁻¹ := + matrixFunction_resolvent (m := m) hB (r := u) hu' + have hC_res : + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 = + (1 : Matrix m m ℂ) - (u : ℂ) • (C + (u : ℂ) • 1)⁻¹ := + matrixFunction_resolvent (m := m) hCpsd (r := u) hu' + have hA_int' : + matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) A hA.1 = + ((u ^ (s - 1) : ℝ) : ℂ) • + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 := by + have hsmul : + matrixFunction (fun x => (((u ^ (s - 1)) * (1 - u * (x + u)⁻¹) : ℝ) : ℂ)) A hA.1 = + ((u ^ (s - 1) : ℝ) : ℂ) • + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 := by + simpa using + (matrixFunction_smul hA.1 ((u ^ (s - 1) : ℝ) : ℂ) + (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ))) + simpa [hfun] using hsmul + have hB_int' : + matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) B hB.1 = + ((u ^ (s - 1) : ℝ) : ℂ) • + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1 := by + have hsmul : + matrixFunction (fun x => (((u ^ (s - 1)) * (1 - u * (x + u)⁻¹) : ℝ) : ℂ)) B hB.1 = + ((u ^ (s - 1) : ℝ) : ℂ) • + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1 := by + simpa + using + (matrixFunction_smul hB.1 ((u ^ (s - 1) : ℝ) : ℂ) + (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ))) + simpa [hfun] using hsmul + have hC_int' : + matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) C hCpsd.1 = + ((u ^ (s - 1) : ℝ) : ℂ) • + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 := by + have hsmul : + matrixFunction (fun x => (((u ^ (s - 1)) * (1 - u * (x + u)⁻¹) : ℝ) : ℂ)) C hCpsd.1 = + ((u ^ (s - 1) : ℝ) : ℂ) • + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 := by + simpa using + (matrixFunction_smul hCpsd.1 ((u ^ (s - 1) : ℝ) : ℂ) + (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ))) + simpa [hfun] using hsmul + have hres_le : + t • ((1 : Matrix m m ℂ) - (u : ℂ) • (A + (u : ℂ) • 1)⁻¹) + + (1 - t) • ((1 : Matrix m m ℂ) - (u : ℂ) • (B + (u : ℂ) • 1)⁻¹) + ≤ (1 : Matrix m m ℂ) - (u : ℂ) • (C + (u : ℂ) • 1)⁻¹ := by + simpa [C] using + (resolvent_lowner_concave_le (m := m) hA hB ht0 ht1 (r := u) hu') + -- Scale the resolvent inequality by the positive factor u^(s-1). + have hscale : + ((u ^ (s - 1) : ℝ) : ℂ) • + (t • ((1 : Matrix m m ℂ) - (u : ℂ) • (A + (u : ℂ) • 1)⁻¹) + + (1 - t) • ((1 : Matrix m m ℂ) - (u : ℂ) • (B + (u : ℂ) • 1)⁻¹)) + ≤ ((u ^ (s - 1) : ℝ) : ℂ) • + ((1 : Matrix m m ℂ) - (u : ℂ) • (C + (u : ℂ) • 1)⁻¹) := by + have hnonneg : 0 ≤ u ^ (s - 1) := by + exact Real.rpow_nonneg (le_of_lt hu') _ + rw [Matrix.le_iff] at hres_le ⊢ + have hpsd : + (((u ^ (s - 1) : ℝ) : ℂ) • + ((1 : Matrix m m ℂ) - (u : ℂ) • (C + (u : ℂ) • 1)⁻¹) - + ((u ^ (s - 1) : ℝ) : ℂ) • + (t • ((1 : Matrix m m ℂ) - (u : ℂ) • (A + (u : ℂ) • 1)⁻¹) + + (1 - t) • ((1 : Matrix m m ℂ) - (u : ℂ) • (B + (u : ℂ) • 1)⁻¹))).PosSemidef := by + simpa [smul_sub] using hres_le.smul hnonneg + simpa [smul_sub] using hpsd + -- Replace with the matrixFunction form. + have hscale' : + ((u ^ (s - 1) : ℝ) : ℂ) • + (t • matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 + + (1 - t) • matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1) + ≤ ((u ^ (s - 1) : ℝ) : ℂ) • + matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 := by + have hscale' := hscale + rw [hA_res.symm, hB_res.symm, hC_res.symm] at hscale' + exact hscale' + simpa [hA_eq, hB_eq, hC_eq, hA_int', hB_int', hC_int', smul_add, smul_smul, + mul_comm, mul_left_comm, mul_assoc] using hscale' + have hle_integral : + t • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) A ∂μ) + + (1 - t) • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) B ∂μ) + ≤ ∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) C ∂μ := by + have hleft_int : + Integrable (fun u => + t • cfcₙ (rpowIntegrand₀₁ q u) A + + (1 - t) • cfcₙ (rpowIntegrand₀₁ q u) B) (μ.restrict (Ioi 0)) := by + exact (hA_int.smul t).add (hB_int.smul (1 - t)) + have hright_int : + Integrable (fun u => cfcₙ (rpowIntegrand₀₁ q u) C) (μ.restrict (Ioi 0)) := + hC_int + have hmono := integral_mono_ae hleft_int hright_int h_integrand_le + have hleft_eq : + ∫ u in Ioi 0, t • cfcₙ (rpowIntegrand₀₁ q u) A + + (1 - t) • cfcₙ (rpowIntegrand₀₁ q u) B ∂μ = + t • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) A ∂μ) + + (1 - t) • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) B ∂μ) := by + calc + ∫ u in Ioi 0, t • cfcₙ (rpowIntegrand₀₁ q u) A + + (1 - t) • cfcₙ (rpowIntegrand₀₁ q u) B ∂μ = + ∫ u in Ioi 0, t • cfcₙ (rpowIntegrand₀₁ q u) A ∂μ + + ∫ u in Ioi 0, (1 - t) • cfcₙ (rpowIntegrand₀₁ q u) B ∂μ := by + refine integral_add ?_ ?_ + · exact hA_int.smul t + · exact hB_int.smul (1 - t) + _ = t • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) A ∂μ) + + (1 - t) • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) B ∂μ) := by + simp [integral_smul] + have hmono' : + t • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) A ∂μ) + + (1 - t) • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) B ∂μ) ≤ + ∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) C ∂μ := by + simpa [hleft_eq] using hmono + exact hmono' + have hA_eq_int : + A ^ s = ∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) A ∂μ := by + have hq_pos : 0 < (q : ℝ) := by exact_mod_cast hs0 + simpa [CFC.nnrpow_eq_rpow (A := Matrix m m ℂ) (a := A) (x := q) hq_pos] using + (hμ A hA0).2 + have hB_eq_int : + B ^ s = ∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) B ∂μ := by + have hq_pos : 0 < (q : ℝ) := by exact_mod_cast hs0 + simpa [CFC.nnrpow_eq_rpow (A := Matrix m m ℂ) (a := B) (x := q) hq_pos] using + (hμ B hB0).2 + have hC_eq_int : + C ^ s = ∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) C ∂μ := by + have hq_pos : 0 < (q : ℝ) := by exact_mod_cast hs0 + simpa [CFC.nnrpow_eq_rpow (A := Matrix m m ℂ) (a := C) (x := q) hq_pos] using + (hμ C hC0).2 + have hC_eq_mf : + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hC = C ^ s := by + have hC' : + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hC = + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hCpsd.1 := by + exact + (matrixFunction_congr (A := C) (B := C) + (f := fun x => ((x ^ s : ℝ) : ℂ)) (hA := hC) (hB := hCpsd.1) rfl) + calc + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hC = + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hCpsd.1 := hC' + _ = C ^ s := matrixFunction_rpow_eq hCpsd s + have hA_eq_mf : + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 = A ^ s := + matrixFunction_rpow_eq hA s + have hB_eq_mf : + matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) B hB.1 = B ^ s := + matrixFunction_rpow_eq hB s + -- Rewrite the integral inequality to the matrixFunction statement. + have hfinal : t • A ^ s + (1 - t) • B ^ s ≤ C ^ s := by + simpa [hA_eq_int, hB_eq_int, hC_eq_int] using hle_integral + simpa [hA_eq_mf, hB_eq_mf, hC_eq_mf, C] using hfinal + +/-- Helper: The difference in quadratic forms for operator concavity. -/ +private lemma rpow_concavity_quadform_nonneg {m : Type*} [Fintype m] [DecidableEq m] + {s : ℝ} (hs0 : 0 < s) (hs1 : s ≤ 1) + (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) + (t : ℝ) (ht0 : 0 ≤ t) (ht1 : t ≤ 1) + (hC : (t • A + (1 - t) • B).IsHermitian) (v : m → ℂ) : + 0 ≤ (star v ⬝ᵥ ( + matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - + t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 - + (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1) *ᵥ v).re := by + have hle : + t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 + + (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1 ≤ + matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC := + rpow_operator_concave_le hs0 hs1 A B hA hB t ht0 ht1 hC + have hpsd : + (matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - + (t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 + + (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1)).PosSemidef := by + simpa [Matrix.le_iff] using hle + have hpsd' : + (matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - + t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 - + (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1).PosSemidef := by + have hcalc : + (matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - + (t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 + + (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1)) = + (matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - + t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 - + (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1) := by + module + simpa [hcalc] using hpsd + have hnonneg := hpsd'.dotProduct_mulVec_nonneg v + exact (Complex.nonneg_iff.mp hnonneg).1 + +/-- The power function t^s (0 < s ≤ 1) is Löwner concave. +This means: (λA + (1-λ)B)^s ≥ λ·A^s + (1-λ)·B^s in Löwner order. + +This is a classical result (Bhatia, Theorem V.2.5) proven via the integral +representation of rpow combined with operator concavity of each integrand. +Here we use Mathlib's CFC (continuous functional calculus) infrastructure. + +Reference: Bhatia, "Matrix Analysis", Theorem V.2.5 -/ +lemma rpow_isLownerConcave {s : ℝ} (hs0 : 0 < s) (hs1 : s ≤ 1) : + IsLownerConcave (fun t => t ^ s) := by + unfold IsLownerConcave + intro m _ _ A B hA hB t ht0 ht1 hC + rw [Matrix.le_iff] + -- Define the power function + let f : ℝ → ℂ := fun x => Complex.ofReal (x ^ s) + let neg_f : ℝ → ℂ := fun x => Complex.ofReal (-(x ^ s)) + -- Relate neg_f to -f + have hfunc : neg_f = fun x => -f x := by + funext x; exact Complex.ofReal_neg (x ^ s) + -- The matrixFunction of neg_f equals -matrixFunction of f + have hA_mf : matrixFunction neg_f A hA.1 = -matrixFunction f A hA.1 := by + rw [hfunc]; exact matrixFunction_neg hA.1 f + have hB_mf : matrixFunction neg_f B hB.1 = -matrixFunction f B hB.1 := by + rw [hfunc]; exact matrixFunction_neg hB.1 f + have hC_mf : matrixFunction neg_f (t • A + (1 - t) • B) hC = + -matrixFunction f (t • A + (1 - t) • B) hC := by + rw [hfunc]; exact matrixFunction_neg hC f + -- The goal's function equals neg_f + have hgoal_A : matrixFunction (fun x : ℝ => (((fun y => -(y ^ s)) x : ℝ) : ℂ)) A hA.1 = + matrixFunction neg_f A hA.1 := rfl + have hgoal_B : matrixFunction (fun x : ℝ => (((fun y => -(y ^ s)) x : ℝ) : ℂ)) B hB.1 = + matrixFunction neg_f B hB.1 := rfl + have hgoal_C : matrixFunction (fun x : ℝ => (((fun y => -(y ^ s)) x : ℝ) : ℂ)) + (t • A + (1 - t) • B) hC = matrixFunction neg_f (t • A + (1 - t) • B) hC := rfl + simp only [hgoal_A, hgoal_B, hgoal_C, hA_mf, hB_mf, hC_mf] + -- Simplify: t•(-A^s) + (1-t)•(-B^s) - (-C^s) = C^s - t•A^s - (1-t)•B^s + have halg : t • -matrixFunction f A hA.1 + (1 - t) • -matrixFunction f B hB.1 - + -matrixFunction f (t • A + (1 - t) • B) hC = + matrixFunction f (t • A + (1 - t) • B) hC - + t • matrixFunction f A hA.1 - (1 - t) • matrixFunction f B hB.1 := by module + rw [halg] + -- Show PosSemidef via Hermitian and quadratic form characterization + -- Use PosSemidef.of_dotProduct_mulVec_nonneg which works with (n → R) instead of Finsupp + apply PosSemidef.of_dotProduct_mulVec_nonneg + -- First show Hermitian + · have hC_herm : (matrixFunction f (t • A + (1 - t) • B) hC).IsHermitian := by + simpa [f] using matrixFunction_isHermitian hC (fun x => x ^ s) + have hA_herm : (t • matrixFunction f A hA.1).IsHermitian := by + simpa [f] using IsHermitian.smul_real (matrixFunction_isHermitian hA.1 (fun x => x ^ s)) t + have hB_herm : ((1 - t) • matrixFunction f B hB.1).IsHermitian := by + simpa [f] using IsHermitian.smul_real (matrixFunction_isHermitian hB.1 (fun x => x ^ s)) (1 - t) + exact IsHermitian.sub (IsHermitian.sub hC_herm hA_herm) hB_herm + -- Then show the quadratic form is nonneg for all vectors + · intro v + -- The helper lemma gives us the real part is nonneg + have h_re : 0 ≤ (star v ⬝ᵥ (matrixFunction f (t • A + (1 - t) • B) hC - + t • matrixFunction f A hA.1 - (1 - t) • matrixFunction f B hB.1) *ᵥ v).re := + rpow_concavity_quadform_nonneg hs0 hs1 A B hA hB t ht0 ht1 hC v + -- The result is real (imaginary part is 0), so nonneg iff real part is nonneg + have hreal : (star v ⬝ᵥ (matrixFunction f (t • A + (1 - t) • B) hC - + t • matrixFunction f A hA.1 - (1 - t) • matrixFunction f B hB.1) *ᵥ v).im = 0 := by + apply IsHermitian.quadForm_im_eq_zero + have hC_herm : (matrixFunction f (t • A + (1 - t) • B) hC).IsHermitian := by + simpa [f] using matrixFunction_isHermitian hC (fun x => x ^ s) + have hA_herm : (t • matrixFunction f A hA.1).IsHermitian := by + simpa [f] using IsHermitian.smul_real (matrixFunction_isHermitian hA.1 (fun x => x ^ s)) t + have hB_herm : ((1 - t) • matrixFunction f B hB.1).IsHermitian := by + simpa [f] using IsHermitian.smul_real (matrixFunction_isHermitian hB.1 (fun x => x ^ s)) (1 - t) + exact IsHermitian.sub (IsHermitian.sub hC_herm hA_herm) hB_herm + rw [Complex.nonneg_iff] + exact ⟨h_re, hreal.symm⟩ + +/-- The negated power function -t^s (0 < s ≤ 1) is Löwner convex. +This is the dual statement of rpow_isLownerConcave. -/ +lemma neg_rpow_isLownerConvex {s : ℝ} (hs0 : 0 < s) (hs1 : s ≤ 1) : + IsLownerConvex (fun t => -(t ^ s)) := + rpow_isLownerConcave hs0 hs1 + +/-- The function `f(t) = −t^s` is Jensen convex for `0 < s ≤ 1`. +This follows from Löwner concavity of t^s together with the equivalence +IsLownerConvex ↔ IsJensenConvex. -/ +lemma neg_rpow_isJensenConvex.{v} {s : ℝ} (hs0 : 0 < s) (hs1 : s ≤ 1) : + IsJensenConvex.{v} (fun t => -(t ^ s)) := by + apply isJensenConvex_of_isLownerConvex.{v} (neg_rpow_isLownerConvex hs0 hs1) + simp only [Real.zero_rpow (ne_of_gt hs0), neg_zero] + exact le_refl 0 + +/-- HPJ subhomogeneous inequality: for `IsJensenConvex` f with f(0) ≤ 0 and +A†A + B†B ≤ I, we have f(A† T₁ A + B† T₂ B) +≤ A† f(T₁) A + B† f(T₂) B. -/ +lemma hpj_subhomogeneous.{v} {f : ℝ → ℝ} + (hconv : IsJensenConvex.{v} f) (hf0 : f 0 ≤ 0) + {m : Type v} [Fintype m] [DecidableEq m] + (A B T₁ T₂ : Matrix m m ℂ) + (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) + (hAB : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ)) + (hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian) : + let fT₁ := matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 + let fT₂ := matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 + let fC := matrixFunction (fun x => (f x : ℂ)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + fC ≤ Aᴴ * fT₁ * A + Bᴴ * fT₂ * B := by + have _ := hf0 + exact hconv m A B T₁ T₂ hT₁ hT₂ hAB hC + +/-- HPJ affine inequality: the case AᴴA + BᴴB = I. -/ +lemma hpj_affine.{v} {f : ℝ → ℝ} + (hconv : IsJensenConvex.{v} f) + {m : Type v} [Fintype m] [DecidableEq m] + (A B T₁ T₂ : Matrix m m ℂ) + (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) + (hAB : Aᴴ * A + Bᴴ * B = (1 : Matrix m m ℂ)) + (hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian) : + let fT₁ := matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 + let fT₂ := matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 + let fC := matrixFunction (fun x => (f x : ℂ)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + fC ≤ Aᴴ * fT₁ * A + Bᴴ * fT₂ * B := by + have hAB' : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ) := by + simp [hAB] + exact hconv m A B T₁ T₂ hT₁ hT₂ hAB' hC + +end Matrix diff --git a/QuantumSystem/Analysis/Matrix/PartialTrace.lean b/QuantumSystem/Analysis/Matrix/PartialTrace.lean new file mode 100644 index 0000000..de3f0dc --- /dev/null +++ b/QuantumSystem/Analysis/Matrix/PartialTrace.lean @@ -0,0 +1,436 @@ +module + +public import QuantumSystem.Algebra.LocalNet + +/-! +# Partial trace as restriction (matrix-level) + +The **restriction** (Schrödinger-picture partial trace) on the matrix algebra of a local +net. Given regions `Λ ⊆ Λ_total` of a `LocalNet`, the restriction of a matrix on +`𝔄(Λ_total)` to `𝔄(Λ)` is defined as the partial trace over the complementary region +`Λ_total \ Λ`. This is the Schrödinger-picture dual of the algebra +inclusion `𝔄(Λ) ↪ 𝔄(Λ_total)`. There is no positional ("left/right") concept — the +operation is parameterised by the region itself. + +The matrix-level operation `Matrix.restrict` is defined as a linear map factoring through +`LocalNet.combineIdx` (which factors `regionIdx Λ_total ≃ regionIdx Λ × regionIdx (Λ_total \ Λ)`). +The bundled quantum-channel structure, Kraus operators, trace preservation, and +Heisenberg-picture duality are also provided. + +## Main definitions + +* `Matrix.restrict` — linear restriction map (matrix-level partial trace) +* `Matrix.restrictKraus` — Kraus operators indexed by the complementary region +* `Matrix.QuantumChannel.restrict` — bundled quantum channel +* `DensityMatrix.restrict` — restriction applied to density matrices +* `ρ ↾ Λ` — paper-style notation for `DensityMatrix.restrict` + +## Main results + +* `Matrix.restrict_eq_sum_kraus` — Kraus form +* `Matrix.isCompletelyPositive_restrict`, `Matrix.isTracePreserving_restrict`, + `Matrix.isQuantumChannel_restrict` — channel properties +* `Matrix.restrict_restrict` — iterated marginalisation = direct marginalisation +* `Matrix.trace_mul_includeAlgebra` — Heisenberg-picture trace identity + +## References + +* Sorce 2024 (`https://arxiv.org/abs/2408.07994`) +* Verch 2025 (`https://arxiv.org/abs/2507.00900`) +* Naaijkens 2012 (`https://repository.ubn.ru.nl/handle/2066/92737`) +-/ + +@[expose] public section + +namespace Matrix + +variable {L : LocalNet} + +/-! ### Linear restriction map -/ + +/-- **Restriction of a matrix to a sub-region** (Schrödinger-picture partial trace). + Sums over indices of the complementary region `Λ_total \ Λ`. -/ +noncomputable def restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + L.localAlgebra Λ_total →ₗ[ℂ] L.localAlgebra Λ where + toFun M := Matrix.of fun a a' => + ∑ b : L.regionIdx (Λ_total \ Λ), + M (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) + map_add' M N := by + ext a a' + simp only [Matrix.of_apply, Matrix.add_apply, Finset.sum_add_distrib] + map_smul' c M := by + ext a a' + simp only [Matrix.of_apply, Matrix.smul_apply, smul_eq_mul, + RingHom.id_apply, Finset.mul_sum] + +@[simp] lemma restrict_apply {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (M : L.localAlgebra Λ_total) (a a' : L.regionIdx Λ) : + restrict h M a a' = + ∑ b : L.regionIdx (Λ_total \ Λ), + M (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) := rfl + +/-! ### Trace preservation -/ + +/-- `Tr(restrict h M) = Tr M`: the restriction preserves the global trace. -/ +theorem trace_restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (M : L.localAlgebra Λ_total) : + Tr (restrict h M) = Tr M := by + unfold Matrix.trace + simp_rw [Matrix.diag_apply, restrict_apply] + -- LHS: ∑ a, ∑ b, M (combineIdx (a,b)) (combineIdx (a,b)) + -- RHS: ∑ s : regionIdx Λ_total, M s s + -- Step 1: combine the double sum into a sum over the product type + rw [show (∑ a : L.regionIdx Λ, ∑ b : L.regionIdx (Λ_total \ Λ), + M ((L.combineIdx h) (a, b)) ((L.combineIdx h) (a, b))) = + ∑ p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ), + M ((L.combineIdx h) p) ((L.combineIdx h) p) from + (Fintype.sum_prod_type + (fun p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ) => + M ((L.combineIdx h) p) ((L.combineIdx h) p))).symm] + -- Step 2: reindex via combineIdx + exact (L.combineIdx h).sum_comp (fun s => M s s) + +theorem isTracePreserving_restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + IsTracePreserving (restrict (L := L) h) := + trace_restrict h + +/-! ### Kraus operators and complete positivity -/ + +/-- Kraus operator for `restrict h`, indexed by `b : regionIdx (Λ_total \ Λ)`: + `K_b a a_total = [a_total = combineIdx h (a, b)]`. -/ +noncomputable def restrictKraus {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (b : L.regionIdx (Λ_total \ Λ)) : + Matrix (L.regionIdx Λ) (L.regionIdx Λ_total) ℂ := + Matrix.of fun a a_total => + if a_total = L.combineIdx h (a, b) then (1 : ℂ) else 0 + +private lemma restrictKraus_apply {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (b : L.regionIdx (Λ_total \ Λ)) (a : L.regionIdx Λ) (a_total : L.regionIdx Λ_total) : + restrictKraus h b a a_total = + if a_total = L.combineIdx h (a, b) then (1 : ℂ) else 0 := rfl + +/-- Entry-wise: `(K_b * M * K_bᴴ) a a' = M (combineIdx (a, b)) (combineIdx (a', b))`. -/ +private lemma restrictKraus_mul_mul_apply {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (M : L.localAlgebra Λ_total) (b : L.regionIdx (Λ_total \ Λ)) + (a a' : L.regionIdx Λ) : + ((restrictKraus h b * M : Matrix (L.regionIdx Λ) (L.regionIdx Λ_total) ℂ) * + (restrictKraus h b)ᴴ : L.localAlgebra Λ) a a' = + M (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) := by + rw [Matrix.mul_apply] + simp_rw [Matrix.mul_apply, Matrix.conjTranspose_apply] + -- Outer sum: ∑ p, (K_b * M)(a, p) * star (K_b a' p) + -- = ∑ p, (∑ q, K_b a q * M q p) * star (K_b a' p) + -- K_b a' p = 1 iff p = combineIdx (a', b), so outer sum collapses at p = combineIdx (a', b) + rw [Finset.sum_eq_single (L.combineIdx h (a', b))] + · -- inner sum collapses at q = combineIdx (a, b) + rw [Finset.sum_eq_single (L.combineIdx h (a, b))] + · simp [restrictKraus_apply] + · intro q _ hq + simp only [restrictKraus_apply] + rw [if_neg hq]; ring + · simp + · intro p _ hp + simp only [restrictKraus_apply, apply_ite (star · : ℂ → ℂ), + star_one, star_zero] + rw [if_neg hp] + simp + · simp + +/-- Kraus form of the restriction. -/ +theorem restrict_eq_sum_kraus {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (M : L.localAlgebra Λ_total) : + restrict h M = + ∑ b : L.regionIdx (Λ_total \ Λ), + ((restrictKraus h b * M : Matrix (L.regionIdx Λ) (L.regionIdx Λ_total) ℂ) * + (restrictKraus h b)ᴴ : L.localAlgebra Λ) := by + ext a a' + rw [restrict_apply, Matrix.sum_apply] + refine Finset.sum_congr rfl fun b _ => ?_ + exact (restrictKraus_mul_mul_apply h M b a a').symm + +theorem isCompletelyPositive_restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + IsCompletelyPositive (restrict (L := L) h) := by + classical + refine ⟨Fintype.card (L.regionIdx (Λ_total \ Λ)), + fun i => restrictKraus h ((Fintype.equivFin (L.regionIdx (Λ_total \ Λ))).symm i), ?_⟩ + intro M + rw [restrict_eq_sum_kraus] + -- Reindex the regionIdx-sum via (equivFin _).symm : Fin r ≃ regionIdx + exact ((Fintype.equivFin (L.regionIdx (Λ_total \ Λ))).symm.sum_comp + (fun b : L.regionIdx (Λ_total \ Λ) => + ((restrictKraus h b * M : Matrix (L.regionIdx Λ) (L.regionIdx Λ_total) ℂ) * + (restrictKraus h b)ᴴ : L.localAlgebra Λ))).symm + +theorem isQuantumChannel_restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + IsQuantumChannel (restrict (L := L) h) where + completelyPositive := isCompletelyPositive_restrict h + tracePreserving := isTracePreserving_restrict h + +/-- Restriction as a bundled `QuantumChannel`. -/ +noncomputable def QuantumChannel.restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + Matrix.QuantumChannel (L.regionIdx Λ_total) (L.regionIdx Λ) := + ⟨Matrix.restrict h, isQuantumChannel_restrict h⟩ + +/-! ### Basic algebraic identities for `restrict` -/ + +/-- `restrict h 1 = card • 1`: restricting the identity matrix scales by the cardinality + of the traced-out region. AQFT analogue of `partialTraceRight 1 = card • 1`. -/ +lemma restrict_one {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : + restrict h (1 : L.localAlgebra Λ_total) = + (Fintype.card (L.regionIdx (Λ_total \ Λ)) : ℂ) • (1 : L.localAlgebra Λ) := by + ext a a' + simp only [restrict_apply, Matrix.smul_apply, smul_eq_mul, Matrix.one_apply] + by_cases hab : a = a' + · subst hab + simp [Finset.card_univ, Finset.sum_const] + · rw [if_neg hab, mul_zero] + refine Finset.sum_eq_zero fun b _ => ?_ + rw [if_neg] + intro hcontra + exact hab ((Prod.mk.injEq _ _ _ _).mp ((L.combineIdx h).injective hcontra) |>.1) + +/-- `restrict h (c • M) = c • restrict h M` (linearity over `ℂ`). -/ +lemma restrict_smul {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) (c : ℂ) + (M : L.localAlgebra Λ_total) : + restrict h (c • M) = c • restrict h M := + (Matrix.restrict h).map_smul c M + +/-- `restrict h` distributes over addition. -/ +private lemma restrict_add {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (M N : L.localAlgebra Λ_total) : + restrict h (M + N) = restrict h M + restrict h N := + (Matrix.restrict h).map_add M N + +/-! ### Iterated restriction (transitivity of marginalisation) + +Restricting first to an intermediate region and then to a sub-sub-region equals +restricting directly: `restrict h₂ (restrict h₁ M) = restrict (h₂.trans h₁) M`. -/ + +/-- For `Λ' ⊆ Λ ⊆ Λ_total`, the index combiner factors through the intermediate + region: starting from `(a', γ', β'') ∈ regionIdx Λ' × regionIdx (Λ \ Λ') × + regionIdx (Λ_total \ Λ)`, combining `(a', γ')` to give `regionIdx Λ` and then + pairing with `β''` agrees with combining `(a', γ' & β'')` directly to give + `regionIdx Λ_total`. This is the key identity behind `restrict_restrict`. -/ +lemma combineIdx_assoc_aux + {Λ' Λ Λ_total : Finset L.sites} (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) + (a' : L.regionIdx Λ') (γ : L.regionIdx (Λ \ Λ')) + (β : L.regionIdx (Λ_total \ Λ)) + (s : ↥Λ_total) : + L.combineIdx h₁ (L.combineIdx h₂ (a', γ), β) s = + if hsΛ : s.val ∈ Λ then + if hsΛ' : s.val ∈ Λ' then a' ⟨s.val, hsΛ'⟩ + else γ ⟨s.val, Finset.mem_sdiff.mpr ⟨hsΛ, hsΛ'⟩⟩ + else β ⟨s.val, Finset.mem_sdiff.mpr ⟨s.property, hsΛ⟩⟩ := by + by_cases hsΛ : s.val ∈ Λ + · rw [LocalNet.combineIdx_apply_mem h₁ _ _ s hsΛ, dif_pos hsΛ] + by_cases hsΛ' : s.val ∈ Λ' + · rw [LocalNet.combineIdx_apply_mem h₂ _ _ ⟨s.val, hsΛ⟩ hsΛ', dif_pos hsΛ'] + · rw [LocalNet.combineIdx_apply_not_mem h₂ _ _ ⟨s.val, hsΛ⟩ hsΛ', dif_neg hsΛ'] + · rw [LocalNet.combineIdx_apply_not_mem h₁ _ _ s hsΛ, dif_neg hsΛ] + +/-- Splitting a `regionIdx (Λ_total \ Λ')` into its `(Λ \ Λ')` and `(Λ_total \ Λ)` parts. + Used by `restrict_restrict` to convert iterated marginalisation into a single one, + and by callers (e.g. SSA) that need to commute `combineIdx h₁ ∘ combineIdx h₂` with + `combineIdx (h₂.trans h₁)` (see `combineIdx_assoc_eq`). -/ +def restrictAssocEquiv {Λ' Λ Λ_total : Finset L.sites} + (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) : + L.regionIdx (Λ \ Λ') × L.regionIdx (Λ_total \ Λ) ≃ L.regionIdx (Λ_total \ Λ') where + toFun gb := fun ⟨s, hs⟩ => + if hsΛ : s ∈ Λ then + gb.1 ⟨s, Finset.mem_sdiff.mpr ⟨hsΛ, (Finset.mem_sdiff.mp hs).2⟩⟩ + else + gb.2 ⟨s, Finset.mem_sdiff.mpr ⟨(Finset.mem_sdiff.mp hs).1, hsΛ⟩⟩ + invFun δ := + (fun ⟨s, hs⟩ => δ ⟨s, Finset.mem_sdiff.mpr + ⟨h₁ (Finset.mem_sdiff.mp hs).1, (Finset.mem_sdiff.mp hs).2⟩⟩, + fun ⟨s, hs⟩ => δ ⟨s, Finset.mem_sdiff.mpr + ⟨(Finset.mem_sdiff.mp hs).1, fun h_in_Λ' => + (Finset.mem_sdiff.mp hs).2 (h₂ h_in_Λ')⟩⟩) + left_inv := by + rintro ⟨γ, β⟩ + ext1 + · funext ⟨s, hs⟩ + have h_in_Λ : s ∈ Λ := (Finset.mem_sdiff.mp hs).1 + simp [h_in_Λ] + · funext ⟨s, hs⟩ + have h_not_Λ : s ∉ Λ := (Finset.mem_sdiff.mp hs).2 + simp [h_not_Λ] + right_inv := by + intro δ + funext ⟨s, hs⟩ + by_cases h_in_Λ : s ∈ Λ <;> simp [h_in_Λ] + +/-- **`combineIdx` associativity**: For nested subsets `Λ' ⊆ Λ ⊆ Λ_total`, combining + `(combineIdx h₂ (a', γ), β)` via the outer subset agrees with combining + `(a', restrictAssocEquiv (γ, β))` via the composed subset `h₂.trans h₁`. + AQFT analogue of the tensor-product associativity + `(ℋ_Λ' ⊗ ℋ_{Λ\Λ'}) ⊗ ℋ_{Λ_total\Λ} ≃ ℋ_Λ' ⊗ (ℋ_{Λ\Λ'} ⊗ ℋ_{Λ_total\Λ})`. -/ +lemma combineIdx_assoc_eq {Λ' Λ Λ_total : Finset L.sites} + (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) (a' : L.regionIdx Λ') + (γ : L.regionIdx (Λ \ Λ')) (β : L.regionIdx (Λ_total \ Λ)) : + L.combineIdx h₁ (L.combineIdx h₂ (a', γ), β) = + L.combineIdx (h₂.trans h₁) (a', restrictAssocEquiv h₁ h₂ (γ, β)) := by + funext s + rw [combineIdx_assoc_aux h₁ h₂] + by_cases hsΛ' : s.val ∈ Λ' + · have hsΛ : s.val ∈ Λ := h₂ hsΛ' + rw [LocalNet.combineIdx_apply_mem (h₂.trans h₁) _ _ s hsΛ', + dif_pos hsΛ, dif_pos hsΛ'] + · rw [LocalNet.combineIdx_apply_not_mem (h₂.trans h₁) _ _ s hsΛ'] + by_cases hsΛ : s.val ∈ Λ + · rw [dif_pos hsΛ, dif_neg hsΛ'] + simp [restrictAssocEquiv, hsΛ] + · rw [dif_neg hsΛ] + simp [restrictAssocEquiv, hsΛ] + +/-- **Iterated restriction equals direct restriction**: + `restrict h₂ (restrict h₁ M) = restrict (h₂.trans h₁) M`. + + Marginalising first to `Λ` then to `Λ' ⊆ Λ` agrees with marginalising directly to `Λ'`. + AQFT statement of the partial-trace transitivity property. -/ +theorem restrict_restrict {Λ' Λ Λ_total : Finset L.sites} + (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) (M : L.localAlgebra Λ_total) : + restrict h₂ (restrict h₁ M) = restrict (h₂.trans h₁) M := by + ext a' a'' + rw [restrict_apply] + -- Reindex RHS sum via restrictAssocEquiv to a sum over the product type. + rw [show restrict (h₂.trans h₁) M a' a'' = + ∑ p : L.regionIdx (Λ \ Λ') × L.regionIdx (Λ_total \ Λ), + M (L.combineIdx (h₂.trans h₁) (a', restrictAssocEquiv h₁ h₂ p)) + (L.combineIdx (h₂.trans h₁) (a'', restrictAssocEquiv h₁ h₂ p)) from by + rw [restrict_apply] + exact ((restrictAssocEquiv h₁ h₂).sum_comp _).symm] + -- Convert the product sum to nested sums. + rw [Fintype.sum_prod_type] + -- Pointwise: rewrite the inner sums via combineIdx_assoc_eq. + refine Finset.sum_congr rfl fun γ _ => ?_ + refine Finset.sum_congr rfl fun β _ => ?_ + congr 1 + · exact combineIdx_assoc_eq h₁ h₂ a' γ β + · exact combineIdx_assoc_eq h₁ h₂ a'' γ β + +/-! ### Heisenberg-picture duality + +The matrix-level dual of the restriction: tracing `ρ` against an embedded observable +`includeAlgebra h X` equals tracing the marginal `restrict h ρ` against `X`. -/ + +/-- Entry-wise behaviour of `includeAlgebra` at combined indices: the off-diagonal + components in the complementary region vanish, leaving `X a a'` on the diagonal. -/ +@[simp] private lemma includeAlgebra_apply_combineIdx + {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) (X : L.localAlgebra Λ) + (a a' : L.regionIdx Λ) (b b' : L.regionIdx (Λ_total \ Λ)) : + L.includeAlgebra h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b')) = + if b = b' then X a a' else 0 := by + simp [LocalNet.includeAlgebra_apply, Equiv.symm_apply_apply] + +/-- **Heisenberg-picture trace identity**: +`Tr(ρ · includeAlgebra h X) = Tr((restrict h ρ) · X)`. +This is the AQFT-natural form of `trace_mul_kronecker_one`. -/ +theorem trace_mul_includeAlgebra {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (ρ : L.localAlgebra Λ_total) (X : L.localAlgebra Λ) : + Tr (ρ * L.includeAlgebra h X) = Tr ((restrict h ρ) * X) := by + -- RHS expansion: ∑ a, ∑ a', ∑ b, ρ(combine (a,b), combine (a',b)) · X a' a + have rhs_expand : + Tr ((restrict h ρ) * X) = + ∑ a : L.regionIdx Λ, ∑ a' : L.regionIdx Λ, ∑ b : L.regionIdx (Λ_total \ Λ), + ρ (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) * X a' a := by + unfold Matrix.trace + simp_rw [Matrix.diag_apply, Matrix.mul_apply, restrict_apply, Finset.sum_mul] + -- LHS: reindex outer sum via combineIdx, expand mul + have lhs_expand : + Tr (ρ * L.includeAlgebra h X) = + ∑ a : L.regionIdx Λ, ∑ b : L.regionIdx (Λ_total \ Λ), + ∑ a' : L.regionIdx Λ, + ρ (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) * X a' a := by + unfold Matrix.trace + -- Reindex Tr over Λ_total via combineIdx⁻¹: ∑ s, M s s = ∑ (a,b), M (combine (a,b)) (combine (a,b)) + rw [show (∑ s : L.regionIdx Λ_total, (ρ * L.includeAlgebra h X).diag s) = + ∑ p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ), + (ρ * L.includeAlgebra h X).diag (L.combineIdx h p) from + ((L.combineIdx h).sum_comp _).symm] + rw [Fintype.sum_prod_type] + -- Goal: ∑ a, ∑ b, (ρ * includeAlgebra h X).diag (combineIdx (a, b)) = ... + refine Finset.sum_congr rfl fun a _ => Finset.sum_congr rfl fun b _ => ?_ + -- Expand mul, then reindex inner sum (over s' : regionIdx Λ_total) via combineIdx + rw [Matrix.diag_apply, Matrix.mul_apply, + show (∑ s' : L.regionIdx Λ_total, + ρ (L.combineIdx h (a, b)) s' * + L.includeAlgebra h X s' (L.combineIdx h (a, b))) = + ∑ p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ), + ρ (L.combineIdx h (a, b)) (L.combineIdx h p) * + L.includeAlgebra h X (L.combineIdx h p) (L.combineIdx h (a, b)) from + ((L.combineIdx h).sum_comp _).symm] + rw [Fintype.sum_prod_type] + -- Now: ∑ a', ∑ b', ρ ... * (includeAlgebra h X) (combineIdx (a', b')) (combineIdx (a, b)) + -- After applying `includeAlgebra_apply_combineIdx`, the b'-sum collapses on b' = b. + refine Finset.sum_congr rfl fun a' _ => ?_ + rw [show (∑ b' : L.regionIdx (Λ_total \ Λ), + ρ (L.combineIdx h (a, b)) (L.combineIdx h (a', b')) * + L.includeAlgebra h X (L.combineIdx h (a', b')) (L.combineIdx h (a, b))) = + ρ (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) * X a' a from by + rw [Finset.sum_eq_single b] + · rw [includeAlgebra_apply_combineIdx]; simp + · intro b' _ hb' + rw [includeAlgebra_apply_combineIdx, if_neg hb'] + ring + · simp] + -- Combine: LHS = ∑ a, ∑ b, ∑ a', ... = ∑ a, ∑ a', ∑ b, ... = RHS + rw [lhs_expand, rhs_expand] + refine Finset.sum_congr rfl fun a _ => ?_ + rw [Finset.sum_comm] + +end Matrix + +namespace DensityMatrix + +variable {L : LocalNet} + +/-- Restriction of a density matrix to a sub-region (= partial trace over the complement). -/ +noncomputable def restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (ρ : L.densityMatrix Λ_total) : L.densityMatrix Λ := + (Matrix.QuantumChannel.restrict h : Matrix.QuantumChannel _ _) ρ + +@[simp] lemma restrict_toMatrix {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) + (ρ : L.densityMatrix Λ_total) : + (restrict h ρ).toMatrix = Matrix.restrict h ρ.toMatrix := rfl + +/-- **Iterated restriction equals direct restriction** (density-matrix form). + Marginalising first to `Λ` then to `Λ' ⊆ Λ` agrees with marginalising directly to `Λ'`. -/ +theorem restrict_restrict {Λ' Λ Λ_total : Finset L.sites} + (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) (ρ : L.densityMatrix Λ_total) : + restrict h₂ (restrict h₁ ρ) = restrict (h₂.trans h₁) ρ := by + apply DensityMatrix.ext + rw [restrict_toMatrix, restrict_toMatrix, restrict_toMatrix, + Matrix.restrict_restrict] + +end DensityMatrix + +/-! ## Paper notation: `ρ ↾ Λ` + +`ρ ↾ Λ` is the **restriction of a density matrix to a sub-region** — equivalently, the +partial trace over the complementary region. This is the AQFT-natural +form of partial trace: parameterised by the region `Λ` rather than by left/right position. + +The subset proof is auto-resolved by trying, in order: `Finset.subset_univ _` +(marginalising from the full system), `Finset.Subset.refl _` (identity), then `decide` +(explicit closed finsets). For complex hypotheses, write `DensityMatrix.restrict h ρ` directly. + +For raw `Matrix`-level work, use `Matrix.restrict h M` (no notation provided to keep `↾` +unambiguous on the density-matrix surface). -/ + +namespace LocalNet +namespace QuantumInfo + +scoped syntax:65 term:65 " ↾ " term:66 : term +scoped syntax:65 term:65 " ↾[" term "]" : term + +scoped macro_rules + | `($ρ ↾ $Λ) => + `(DensityMatrix.restrict (Λ := $Λ) + (by first | exact Finset.subset_univ _ | exact Finset.Subset.refl _ + | decide + | assumption) $ρ) + | `($ρ ↾[$h]) => `(DensityMatrix.restrict $h $ρ) + +end QuantumInfo +end LocalNet diff --git a/QuantumSystem/Analysis/Matrix/Pinching.lean b/QuantumSystem/Analysis/Matrix/Pinching.lean new file mode 100644 index 0000000..0768e90 --- /dev/null +++ b/QuantumSystem/Analysis/Matrix/Pinching.lean @@ -0,0 +1,254 @@ +module + +public import QuantumSystem.ForMathlib.RingTheory.RootsOfUnity.Complex +public import Mathlib.LinearAlgebra.UnitaryGroup + +/-! +# Pinching Map via Root-of-Unity Unitary Averaging + +The pinching map extracts the block-diagonal part of a matrix by averaging +over root-of-unity diagonal unitaries. + +## Main definitions + +* `pinchingUnitary r k`: The k-th diagonal unitary on `Fin r × m`, defined by + `(U_k)_{(i,a)(j,b)} = δ_{ij} δ_{ab} ζ^{ik}` where `ζ = e^{2πi/r}`. + +## Main results + +* `pinchingUnitary_isUnitary`: Each pinching unitary is unitary. +* `pinching_average_eq_blockDiag`: Averaging `U_k ω U_k†` over `k` extracts + the block diagonal: `(1/r) ∑_k (U_k ω U_k†)_{(i,a)(j,b)} = δ_{ij} ω_{(i,a)(j,b)}`. +* `splitFinSuccProdEquiv`: Canonical equivalence `Fin (n+1) × m ≃ m ⊕ (Fin n × m)`. + +## References + +* Lindblad, *Completely positive maps and entropy inequalities* +-/ + +@[expose] public section + +namespace Matrix + +variable {m : Type*} [Fintype m] [DecidableEq m] + +/-! ### Pinching unitaries -/ + +/-- **Pinching diagonal unitary**: The k-th diagonal unitary on (Fin r × m) is +defined by (U_k)_{(i,a)(j,b)} = δ_{ij} δ_{ab} ζ^{ik} +where ζ = e^{2πi/r}. -/ +noncomputable def pinchingUnitary (r : ℕ) [NeZero r] (k : Fin r) : + Matrix (Fin r × m) (Fin r × m) ℂ := + Matrix.diagonal fun ⟨i, _⟩ => (rootOfUnity r) ^ ((i.val * k.val : ℕ) : ℤ) + +omit [Fintype m] in +lemma pinchingUnitary_entry {r : ℕ} [NeZero r] (k : Fin r) (i j : Fin r) (a b : m) : + pinchingUnitary r k (i, a) (j, b) = + if i = j ∧ a = b then (rootOfUnity r) ^ ((i.val * k.val : ℕ) : ℤ) else 0 := by + simp only [pinchingUnitary, Matrix.diagonal_apply, Prod.mk.injEq] + +lemma pinchingUnitary_isUnitary {r : ℕ} [NeZero r] (k : Fin r) : + (pinchingUnitary r k) ∈ Matrix.unitaryGroup (Fin r × m) ℂ := by + rw [Matrix.mem_unitaryGroup_iff'] + ext ⟨i, a⟩ ⟨j, b⟩ + -- For diagonal D, (Dᴴ * D)_{(i,a)(j,b)} = ∑_x (Dᴴ)_{(i,a)x} * D_{x(j,b)} + simp only [pinchingUnitary, Matrix.mul_apply, Matrix.one_apply, Prod.mk.injEq] + -- star (diagonal f) is the conjTranspose, so (star (diagonal f))_{ia,x} = star((diagonal f)_{x,ia}) + simp only [Matrix.star_apply, Matrix.diagonal_apply] + by_cases h : i = j ∧ a = b + · obtain ⟨rfl, rfl⟩ := h + simp only [true_and, ↓reduceIte] + -- Only x = (i, a) contributes to the sum + have hsum : ∑ x : Fin r × m, + star (if x = (i, a) then rootOfUnity r ^ ((x.1.val * k.val : ℕ) : ℤ) else 0) * + (if x = (i, a) then rootOfUnity r ^ ((x.1.val * k.val : ℕ) : ℤ) else 0) = + star (rootOfUnity r ^ ((i.val * k.val : ℕ) : ℤ)) * rootOfUnity r ^ ((i.val * k.val : ℕ) : ℤ) := by + convert Finset.sum_eq_single (i, a) ?_ ?_ using 1 + · simp only [↓reduceIte] + · intro x _ hne + simp only [if_neg hne, star_zero, zero_mul] + · intro habs; exact absurd (Finset.mem_univ _) habs + rw [hsum] + -- star(z) * z = |z|^2 = 1 for z on unit circle + have hnorm : ‖rootOfUnity r ^ ((i.val * k.val : ℕ) : ℤ)‖ = 1 := by + rw [Complex.norm_zpow] + simp [rootOfUnity_norm] + -- For z ≠ 0 with |z| = 1, we have star(z) * z = |z|^2 = 1 + rw [Complex.star_def, ← Complex.normSq_eq_conj_mul_self] + simp only [Complex.ofReal_eq_one] + rw [Complex.normSq_eq_norm_sq, hnorm, one_pow] + · simp only [h, ↓reduceIte] + apply Finset.sum_eq_zero + intro x _ + by_cases h1 : x = (i, a) + · by_cases h2 : x = (j, b) + · -- x = (i, a) and x = (j, b) means (i, a) = (j, b), contradiction with h + have heq : (i, a) = (j, b) := Eq.trans h1.symm h2 + exact absurd ⟨Prod.mk.inj heq |>.1, Prod.mk.inj heq |>.2⟩ h + · simp only [h2, ↓reduceIte, mul_zero] + · simp only [h1, ↓reduceIte, star_zero, zero_mul] + +omit [Fintype m] in +lemma pinchingUnitary_conjTranspose {r : ℕ} [NeZero r] (k : Fin r) : + (pinchingUnitary r k)ᴴ = Matrix.diagonal (fun (p : Fin r × m) => (rootOfUnity r) ^ (-((p.1.val * k.val : ℕ) : ℤ))) := by + ext ⟨i, a⟩ ⟨j, b⟩ + simp only [pinchingUnitary, Matrix.diagonal_conjTranspose, Pi.star_apply, + Matrix.diagonal_apply, Prod.mk.injEq] + by_cases h : j = i ∧ b = a + · obtain ⟨rfl, rfl⟩ := h + simp only [true_and, ↓reduceIte] + -- star(ζ^n) = (star ζ)^n = (ζ⁻¹)^n = ζ^(-n) + rw [star_zpow₀, rootOfUnity_star, _root_.inv_zpow'] + · have h' : ¬(i = j ∧ a = b) := fun ⟨h1, h2⟩ => h ⟨h1.symm, h2.symm⟩ + simp only [h', ↓reduceIte] + +/-! ### Pinching average extracts block diagonal -/ + +/-- Averaging over pinching unitaries extracts block-diagonal: +(1/r) ∑ₖ (Uₖ ω Uₖ†)_(i,a)(j,b) = δᵢⱼ ω_(i,a)(i,b). -/ +lemma pinching_average_eq_blockDiag {r : ℕ} [NeZero r] + (ω : Matrix (Fin r × m) (Fin r × m) ℂ) (i j : Fin r) (a b : m) : + (1 / r : ℂ) * ∑ k : Fin r, ((pinchingUnitary r k) * ω * (pinchingUnitary r k)ᴴ : Matrix _ _ _) (i, a) (j, b) = + if i = j then ω (i, a) (j, b) else 0 := by + -- For diagonal U_k with U_k(p,p) = ζ^(p.1*k), we have: + -- (U_k ω U_k^†)_{(ia)(jb)} = ζ^(i*k) ω_{(ia)(jb)} ζ^(-j*k) + have hdiag : ∀ k : Fin r, + ((pinchingUnitary r k) * ω * (pinchingUnitary r k)ᴴ : Matrix _ _ _) (i, a) (j, b) = + (rootOfUnity r) ^ ((i.val * k.val : ℕ) : ℤ) * ω (i, a) (j, b) * + (rootOfUnity r) ^ (-((j.val * k.val : ℕ) : ℤ)) := by + intro k + simp only [Matrix.mul_apply] + -- pinchingUnitary is diagonal, so only x = (i, a) survives in first sum + -- and only y = (j, b) survives in second sum + -- The goal is: ∑ x, (∑ t, pinchingUnitary r k (i, a) t * ω t x) * (pinchingUnitary r k)ᴴ x (j, b) = ... + -- Rewrite using diagonal property of pinchingUnitary + have hsimplify : ∑ x : Fin r × m, (∑ t : Fin r × m, pinchingUnitary r k (i, a) t * ω t x) * + (pinchingUnitary r k)ᴴ x (j, b) = + (rootOfUnity r) ^ ((i.val * k.val : ℕ) : ℤ) * ω (i, a) (j, b) * + (rootOfUnity r) ^ (-((j.val * k.val : ℕ) : ℤ)) := by + -- First, simplify the inner sum using diagonal property + have h_inner : ∀ x : Fin r × m, ∑ t : Fin r × m, pinchingUnitary r k (i, a) t * ω t x = + pinchingUnitary r k (i, a) (i, a) * ω (i, a) x := by + intro x + apply Finset.sum_eq_single (i, a) + · intro t _ hne + rw [pinchingUnitary_entry] + have hne_cond : ¬(i = t.1 ∧ a = t.2) := by + intro ⟨h1, h2⟩ + have : t = (i, a) := Prod.ext h1.symm h2.symm + exact hne this + simp only [hne_cond, ↓reduceIte, zero_mul] + · intro habs; exact absurd (Finset.mem_univ _) habs + simp_rw [h_inner] + -- Now the outer sum simplifies similarly + rw [Finset.sum_eq_single (j, b)] + -- Main case: x = (j, b) + · rw [pinchingUnitary_entry, pinchingUnitary_conjTranspose, Matrix.diagonal_apply] + simp only [true_and, ↓reduceIte] + · intro x _ hne + rw [pinchingUnitary_conjTranspose, Matrix.diagonal_apply] + simp only [hne, ↓reduceIte, mul_zero] + · intro habs; exact absurd (Finset.mem_univ _) habs + exact hsimplify + simp_rw [hdiag] + -- Now sum over k: ζ^(i*k) * ω * ζ^(-j*k) = ω * ζ^((i-j)*k) + have hζne : rootOfUnity r ≠ 0 := rootOfUnity_ne_zero r + have hcombine : ∀ k : Fin r, + (rootOfUnity r) ^ ((i.val * k.val : ℕ) : ℤ) * ω (i, a) (j, b) * + (rootOfUnity r) ^ (-((j.val * k.val : ℕ) : ℤ)) = + ω (i, a) (j, b) * (rootOfUnity r) ^ (((i.val : ℤ) - (j.val : ℤ)) * (k.val : ℤ)) := by + intro k + -- Rewrite ζ^(-n) = (ζ^n)⁻¹ using zpow_neg with explicit arguments + have h1 : (rootOfUnity r) ^ (-((j.val * k.val : ℕ) : ℤ)) = ((rootOfUnity r) ^ ((j.val * k.val : ℕ) : ℤ))⁻¹ := by + rw [_root_.zpow_neg (rootOfUnity r) ((j.val * k.val : ℕ) : ℤ)] + rw [h1] + -- Now goal: ζ^(ik) * ω * (ζ^(jk))⁻¹ = ω * ζ^((i-j)*k) + have h2 : (rootOfUnity r) ^ ((i.val * k.val : ℕ) : ℤ) * ω (i, a) (j, b) * + ((rootOfUnity r) ^ ((j.val * k.val : ℕ) : ℤ))⁻¹ = + ω (i, a) (j, b) * ((rootOfUnity r) ^ ((i.val * k.val : ℕ) : ℤ) * + ((rootOfUnity r) ^ ((j.val * k.val : ℕ) : ℤ))⁻¹) := by ring + rw [h2] + congr 1 + rw [mul_inv_eq_iff_eq_mul₀ (zpow_ne_zero _ hζne), ← zpow_add₀ hζne] + congr 1 + push_cast + ring + simp_rw [hcombine] + rw [← Finset.mul_sum, mul_comm (1 / r : ℂ), mul_assoc] + -- Use root of unity sum + by_cases hij : i = j + · subst hij + simp only [sub_self, zero_mul, zpow_zero, Finset.sum_const, Finset.card_fin, ↓reduceIte] + rw [nsmul_eq_mul, mul_one] + -- Goal: ↑r * (1 / ↑r * ω (i, a) (i, b)) = ω (i, a) (i, b) + have hr_ne : (r : ℂ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne r) + field_simp + · have hdiff : ((i.val : ℤ) - j.val) % (r : ℤ) ≠ 0 := by + intro h + -- i.val, j.val ∈ [0, r), so i.val - j.val ∈ (-r, r) + -- If (i.val - j.val) % r = 0 and -r < (i.val - j.val) < r, then i.val - j.val = 0 + have hbound_lo : -(r : ℤ) < (i.val : ℤ) - j.val := by + have : (j.val : ℤ) < r := Int.ofNat_lt.mpr j.isLt + have : (0 : ℤ) ≤ i.val := Int.natCast_nonneg _ + omega + have hbound_hi : (i.val : ℤ) - j.val < r := by + have : (i.val : ℤ) < r := Int.ofNat_lt.mpr i.isLt + have : (0 : ℤ) ≤ j.val := Int.natCast_nonneg _ + omega + have hr_pos : (0 : ℤ) < r := Int.natCast_pos.mpr (NeZero.pos r) + -- The only multiple of r in (-r, r) is 0 + have heq_zero : (i.val : ℤ) - j.val = 0 := by + have hdvd := Int.dvd_of_emod_eq_zero h + obtain ⟨k, hk⟩ := hdvd + -- hk : (i : ℤ) - j = r * k + -- From -r < r * k < r and r > 0, we get k = 0 + have hk_bound : k = 0 := by + have hkr : (r : ℤ) * k = (i.val : ℤ) - j.val := hk.symm + rcases Int.lt_trichotomy k 0 with hk_neg | hk_zero | hk_pos + · -- k < 0 implies k ≤ -1, so r * k ≤ -r < hbound_lo, contradiction + have h1 : (r : ℤ) * k ≤ -r := by nlinarith + have h2 : (r : ℤ) * k > -r := by rw [hkr]; exact hbound_lo + linarith + · exact hk_zero + · -- k > 0 implies k ≥ 1, so r * k ≥ r > hbound_hi, contradiction + have h1 : (r : ℤ) * k ≥ r := by nlinarith + have h2 : (r : ℤ) * k < r := by rw [hkr]; exact hbound_hi + linarith + simp only [hk_bound, mul_zero] at hk + exact hk + have hival : i.val = j.val := by omega + exact hij (Fin.ext hival) + have hrsum := rootOfUnity_sum_eq_zero r ((i.val : ℤ) - j.val) hdiff + rw [hrsum] + simp only [mul_zero, zero_mul, if_neg hij] + +/-! ### Fin product equivalence -/ + +/-- Canonical equivalence `Fin (n + 1) × m ≃ m ⊕ (Fin n × m)`. -/ +def splitFinSuccProdEquiv (n : ℕ) (m : Type*) : + Fin (n + 1) × m ≃ m ⊕ (Fin n × m) where + toFun p := if h : (p.1 : ℕ) = 0 then Sum.inl p.2 + else Sum.inr (⟨(p.1 : ℕ) - 1, by omega⟩, p.2) + invFun x := match x with + | Sum.inl a => (⟨0, by omega⟩, a) + | Sum.inr (i, a) => (i.succ, a) + left_inv := by + rintro ⟨⟨i, hi⟩, a⟩ + by_cases h : i = 0 + · subst h; rfl + · have hne : ¬((⟨i, hi⟩ : Fin (n + 1)) : ℕ) = 0 := h + simp only [hne, dite_false] + refine Prod.ext (Fin.ext ?_) rfl + simp only [Fin.val_succ] + omega + right_inv := by + rintro (a | ⟨⟨i, hi⟩, a⟩) + · rfl + · have hne : ¬((⟨i, hi⟩ : Fin n).succ : ℕ) = 0 := by + rw [Fin.val_succ]; exact Nat.succ_ne_zero _ + simp only [hne, dite_false] + refine congr_arg Sum.inr (Prod.ext (Fin.ext ?_) rfl) + simp only [Fin.val_succ] + omega + +end Matrix diff --git a/QuantumSystem/Analysis/Matrix/PosDef.lean b/QuantumSystem/Analysis/Matrix/PosDef.lean new file mode 100644 index 0000000..452631e --- /dev/null +++ b/QuantumSystem/Analysis/Matrix/PosDef.lean @@ -0,0 +1,78 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.Basic +public import Mathlib.LinearAlgebra.Matrix.PosDef +public import QuantumSystem.ForMathlib.Analysis.Matrix.Hermitian + +/-! +# Positive Definite Matrix Lemmas + +This file collects basic results about positive definite (PD) matrices over ℂ +used in convexity arguments. + +## Main results + +- `Matrix.PosDef.convex_comb`: a strictly convex combination tA + (1-t)B of PD matrices + is PD for 0 < t < 1. +- `Matrix.PosDef.convex_comb_nonneg`: same with nonneg weights w₁ + w₂ = 1. +-/ +@[expose] public section + +namespace Matrix + +open scoped ComplexOrder + +/-- A strictly positive convex combination of positive definite matrices is positive definite. -/ +lemma PosDef.convex_comb {m : Type*} [Fintype m] + {A B : Matrix m m ℂ} (hA : A.PosDef) (hB : B.PosDef) + {t : ℝ} (ht0 : 0 < t) (ht1 : 0 < 1 - t) : + (t • A + (1 - t) • B).PosDef := by + classical + refine Matrix.PosDef.of_dotProduct_mulVec_pos ?_ ?_ + · exact (hA.1.smul_real t).add (hB.1.smul_real (1 - t)) + · intro x hx + have hApos := hA.dotProduct_mulVec_pos hx + have hBpos := hB.dotProduct_mulVec_pos hx + have hA_re : 0 < (star x ⬝ᵥ (A *ᵥ x)).re := (RCLike.pos_iff.mp hApos).1 + have hB_re : 0 < (star x ⬝ᵥ (B *ᵥ x)).re := (RCLike.pos_iff.mp hBpos).1 + have hC_im : (star x ⬝ᵥ ((t • A + (1 - t) • B) *ᵥ x)).im = 0 := by + exact (hA.1.smul_real t).add (hB.1.smul_real (1 - t)) |>.quadForm_im_eq_zero x + have hC_re : + (star x ⬝ᵥ ((t • A + (1 - t) • B) *ᵥ x)).re = + t * (star x ⬝ᵥ (A *ᵥ x)).re + (1 - t) * (star x ⬝ᵥ (B *ᵥ x)).re := by + simp [add_mulVec, smul_mulVec, dotProduct_add, dotProduct_smul, + Complex.add_re, Complex.real_smul] + refine (RCLike.pos_iff).2 ?_ + constructor + · have hA' : 0 < t * (star x ⬝ᵥ (A *ᵥ x)).re := by + exact mul_pos ht0 hA_re + have hB' : 0 < (1 - t) * (star x ⬝ᵥ (B *ᵥ x)).re := by + exact mul_pos ht1 hB_re + have hsum : 0 < t * (star x ⬝ᵥ (A *ᵥ x)).re + (1 - t) * (star x ⬝ᵥ (B *ᵥ x)).re := + add_pos hA' hB' + simpa [hC_re] using hsum + · exact hC_im + +/-- Convex combination of PD matrices with nonnegative weights is PD. -/ +lemma PosDef.convex_comb_nonneg {m : Type*} [Fintype m] + {A B : Matrix m m ℂ} (hA : A.PosDef) (hB : B.PosDef) + {w₁ w₂ : ℝ} (hw₁ : 0 ≤ w₁) (hw₂ : 0 ≤ w₂) (hw : w₁ + w₂ = 1) : + (w₁ • A + w₂ • B).PosDef := by + by_cases h₁ : w₁ = 0 + · have h₂ : w₂ = 1 := by linarith [hw, h₁] + subst h₁ + subst h₂ + simpa using hB + by_cases h₂ : w₂ = 0 + · have h₁' : w₁ = 1 := by linarith [hw, h₂] + subst h₂ + subst h₁' + simpa using hA + have hw₁pos : 0 < w₁ := lt_of_le_of_ne hw₁ (Ne.symm h₁) + have hw₂pos : 0 < w₂ := lt_of_le_of_ne hw₂ (Ne.symm h₂) + have hw₂' : w₂ = 1 - w₁ := by linarith [hw] + have h1t : 0 < 1 - w₁ := by + simpa [hw₂'] using hw₂pos + simpa [hw₂'] using (PosDef.convex_comb (A := A) (B := B) hA hB hw₁pos h1t) + +end Matrix diff --git a/QuantumSystem/Channel.lean b/QuantumSystem/Channel.lean new file mode 100644 index 0000000..adb771c --- /dev/null +++ b/QuantumSystem/Channel.lean @@ -0,0 +1,196 @@ +module + +public import QuantumSystem.State + +/-! +# Quantum Channels (Completely Positive Trace-Preserving Maps) + +This file defines quantum channels on finite-dimensional matrix algebras and establishes +their basic properties. A quantum channel is a linear map Φ: M_n(ℂ) → M_m(ℂ) that is: +1. Completely positive (CP): Has a Kraus representation Φ(ρ) = Σᵢ Kᵢ ρ Kᵢ† +2. Trace-preserving (TP): Tr(Φ(A)) = Tr(A) for all A, equivalently Σᵢ Kᵢ† Kᵢ = I + +## Main definitions + +* `IsTracePreserving`: A linear map preserves trace. +* `IsCompletelyPositive`: A linear map has a Kraus representation. +* `IsQuantumChannel`: A linear map is both CP and TP. + +## Mathematical Background + +### Choi-Kraus Theorem +A linear map Φ: M_n(ℂ) → M_m(ℂ) is completely positive if and only if it has a +Kraus representation: + Φ(ρ) = Σᵢ Kᵢ ρ Kᵢ† +where Kᵢ: ℂⁿ → ℂᵐ are linear maps (Kraus operators). + +The map is trace-preserving if and only if: + Σᵢ Kᵢ† Kᵢ = I + +## References + +* Nielsen, Chuang, *Quantum Computation and Quantum Information*, Chapter 8 +* Watrous, *The Theory of Quantum Information*, Chapter 2 +-/ + +@[expose] public section + +namespace Matrix + +variable {n m k : Type*} [Fintype n] [Fintype m] [Fintype k] + +open scoped ComplexOrder + +/-! ### Trace-Preserving Maps -/ + +/-- A linear map is trace-preserving if Tr(Φ(A)) = Tr(A) for all A. -/ +def IsTracePreserving (Φ : Matrix n n ℂ →ₗ[ℂ] Matrix m m ℂ) : Prop := + ∀ A : Matrix n n ℂ, Tr (Φ A) = Tr A + +/-! ### Completely Positive Maps -/ + +/-- A linear map is completely positive if it has a Kraus representation. +This is equivalent to the Choi matrix being positive semi-definite. -/ +def IsCompletelyPositive (Φ : Matrix n n ℂ →ₗ[ℂ] Matrix m m ℂ) : Prop := + ∃ (r : ℕ) (K : Fin r → Matrix m n ℂ), + ∀ A, Φ A = ∑ i, K i * A * (K i)ᴴ + +/-! ### Quantum Channels -/ + +/-- A quantum channel is a completely positive trace-preserving (CPTP) map. +These are the physically realizable operations on quantum states. -/ +structure IsQuantumChannel (Φ : Matrix n n ℂ →ₗ[ℂ] Matrix m m ℂ) : Prop where + /-- The map is completely positive -/ + completelyPositive : IsCompletelyPositive Φ + /-- The map preserves trace -/ + tracePreserving : IsTracePreserving Φ + +/-- Quantum channel as a subtype for cleaner API. -/ +abbrev QuantumChannel (n : Type*) (m : Type*) [Fintype n] [Fintype m] := + { Φ : Matrix n n ℂ →ₗ[ℂ] Matrix m m ℂ // IsQuantumChannel Φ } + +/-- The identity map is a quantum channel. -/ +theorem isQuantumChannel_id : IsQuantumChannel (LinearMap.id : Matrix n n ℂ →ₗ[ℂ] Matrix n n ℂ) where + completelyPositive := by + classical + -- id has Kraus representation with single operator K = I + use 1, fun _ => 1 + intro A + simp only [Finset.univ_unique, Fin.default_eq_zero, Finset.sum_singleton] + simp [Matrix.conjTranspose_one] + tracePreserving := fun _ => rfl + +/-- Composition of quantum channels is a quantum channel. -/ +theorem QuantumChannel.comp + (Φ : QuantumChannel n m) (Ψ : QuantumChannel m k) : + IsQuantumChannel (Ψ.val.comp Φ.val) where + completelyPositive := by + classical + -- Composition of CP maps is CP + -- If Φ(A) = Σᵢ Kᵢ A Kᵢ† and Ψ(B) = Σⱼ Lⱼ B Lⱼ† + -- Then (Ψ∘Φ)(A) = Σⱼ Lⱼ (Σᵢ Kᵢ A Kᵢ†) Lⱼ† = Σᵢⱼ (Lⱼ Kᵢ) A (Lⱼ Kᵢ)† + obtain ⟨r, K, hK⟩ := Φ.property.completelyPositive + obtain ⟨s, L, hL⟩ := Ψ.property.completelyPositive + -- Use product Kraus operators indexed by Fin s × Fin r + use s * r + -- Define the combined Kraus operators via equivalence Fin (s * r) ≃ Fin s × Fin r + let e : Fin (s * r) ≃ Fin s × Fin r := finProdFinEquiv.symm + use fun p => L (e p).1 * K (e p).2 + intro A + simp only [LinearMap.comp_apply, hK, hL] + -- Ψ(Σᵢ Kᵢ A Kᵢ†) = Σⱼ Lⱼ (Σᵢ Kᵢ A Kᵢ†) Lⱼ† + simp_rw [Matrix.mul_sum, Matrix.sum_mul] + -- Reindex: ∑_{j,i} = ∑_p via Equiv.sum_comp + rw [← Fintype.sum_prod_type'] + rw [(Equiv.sum_comp e (fun x => L x.1 * (K x.2 * A * (K x.2)ᴴ) * (L x.1)ᴴ)).symm] + apply Finset.sum_congr rfl + intro p _ + -- Need to show: L (e p).1 * (K (e p).2 * A * (K (e p).2)†) * (L (e p).1)† + -- = L (e p).1 * K (e p).2 * A * (L (e p).1 * K (e p).2)† + rw [Matrix.conjTranspose_mul] + -- Now use matrix associativity + simp only [Matrix.mul_assoc] + tracePreserving := by + intro A + simp only [LinearMap.comp_apply] + rw [Ψ.property.tracePreserving, Φ.property.tracePreserving] + +omit [Fintype m] in +/-- A completely positive map preserves Hermitianity of matrices. +If Φ(A) = Σᵢ Kᵢ A Kᵢ† and A is Hermitian, then Φ(A) is Hermitian. -/ +theorem IsCompletelyPositive.map_isHermitian + {Φ : Matrix n n ℂ →ₗ[ℂ] Matrix m m ℂ} (hΦ : IsCompletelyPositive Φ) + {A : Matrix n n ℂ} (hA : A.IsHermitian) : (Φ A).IsHermitian := by + classical + obtain ⟨r, K, hK⟩ := hΦ + rw [hK] + rw [Matrix.IsHermitian, Matrix.conjTranspose_sum] + apply Finset.sum_congr rfl + intro i _ + rw [Matrix.conjTranspose_mul, Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose] + rw [Matrix.mul_assoc] + congr 1 + rw [hA.eq] + +/-- Apply a quantum channel to a density matrix. -/ +noncomputable def QuantumChannel.apply [DecidableEq n] [DecidableEq m] + (Φ : QuantumChannel n m) (ρ : DensityMatrix n) : + DensityMatrix m where + toMatrix := Φ.val ↑ρ + posSemidef := by + classical + obtain ⟨r, K, hK⟩ := Φ.property.completelyPositive + rw [hK] + apply posSemidef_sum + intro i _ + exact ρ.posSemidef.mul_mul_conjTranspose_same (K i) + trace_eq_one := by + rw [Φ.property.tracePreserving] + exact ρ.trace_eq_one +/-- Quantum channels can be applied as functions from density matrices to density matrices. -/ +noncomputable instance [DecidableEq n] [DecidableEq m] : CoeFun (QuantumChannel n m) + (fun _ => DensityMatrix n → DensityMatrix m) where + coe := QuantumChannel.apply + +/-! ### Kraus Completeness -/ + +/-- If `Tr(M * A) = Tr(A)` for all `A`, then `M = 1`. -/ +private lemma matrix_eq_one_of_trace_mul [DecidableEq n] + (M : Matrix n n ℂ) (h : ∀ A : Matrix n n ℂ, Tr (M * A) = Tr A) : M = 1 := + Matrix.ext_iff_trace_mul_right.mpr fun A => by rw [one_mul]; exact h A + +/-- Trace-preserving Kraus channels satisfy the completeness relation: ∑ₖ Kₖ† Kₖ = I. -/ +theorem QuantumChannel.kraus_sum_eq_one [DecidableEq n] + (Φ : QuantumChannel n m) + {r : ℕ} {K : Fin r → Matrix m n ℂ} (hK : ∀ A, Φ.val A = ∑ i, K i * A * (K i)ᴴ) : + ∑ i, (K i)ᴴ * K i = 1 := by + apply matrix_eq_one_of_trace_mul + intro A + have key : ∀ i : Fin r, ((K i)ᴴ * K i * A).trace = (K i * A * (K i)ᴴ).trace := fun i => by + rw [Matrix.mul_assoc, Matrix.trace_mul_comm (K i)ᴴ] + rw [Finset.sum_mul] + simp_rw [Matrix.trace_sum, key, ← Matrix.trace_sum] + have := Φ.property.tracePreserving A + rwa [hK] at this + +/-! ### Stinespring Isometry -/ + +/-- Stinespring isometry: stack Kraus operators into a single isometry +V : Matrix (Fin r × m) n ℂ defined by V (i, a) b = Kᵢ a b. +Then V†V = I (from Kraus completeness) and Φ(A) = Σᵢ (i-th block of VAV†). -/ +noncomputable def stinespringIsometry {r : ℕ} (K : Fin r → Matrix m n ℂ) : + Matrix (Fin r × m) n ℂ := + Matrix.of fun ⟨i, a⟩ b => K i a b + +omit [Fintype n] in +lemma stinespringIsometry_conjTranspose_mul {r : ℕ} [DecidableEq n] + {K : Fin r → Matrix m n ℂ} (hK : ∑ i, (K i)ᴴ * K i = 1) : + (stinespringIsometry K)ᴴ * stinespringIsometry K = 1 := by + ext a b + simp only [stinespringIsometry, Matrix.conjTranspose_apply, Matrix.mul_apply, + Matrix.of_apply, Matrix.one_apply, Fintype.sum_prod_type] + have heq : ∀ i, ∑ j : m, star (K i j a) * K i j b = ((K i)ᴴ * K i) a b := fun i => by + simp only [Matrix.mul_apply, Matrix.conjTranspose_apply] + simp only [heq, ← Finset.sum_apply, hK, Matrix.one_apply] + +end Matrix diff --git a/QuantumSystem/ForMathlib/Analysis/Calculus/Deriv/Sign.lean b/QuantumSystem/ForMathlib/Analysis/Calculus/Deriv/Sign.lean new file mode 100644 index 0000000..94534ba --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/Calculus/Deriv/Sign.lean @@ -0,0 +1,38 @@ +module + +public import Mathlib.Analysis.Calculus.Deriv.Slope + +/-! +# ForMathlib: Slope and Derivative Sign Lemmas + +## Main Results + +* `deriv_nonpos_of_forall_lt_min`: If g has a local minimum from the left at x₀, then g'(x₀) ≤ 0. +-/ + +@[expose] public section + +/-- Derivative sign lemma: if g has a minimum from the left at x₀, then g'(x₀) ≤ 0. +Formally: if g(x₀) ≤ g(y) for all y ∈ (x₀-ε, x₀) and g is differentiable at x₀, +then g'(x₀) ≤ 0. +Proof: the difference quotient (g(y) - g(x₀))/(y - x₀) ≤ 0 for y < x₀ and y near x₀, +and the limit equals the derivative. -/ +lemma deriv_nonpos_of_forall_lt_min (g : ℝ → ℝ) (d : ℝ) (x₀ ε : ℝ) (hε : 0 < ε) + (hd : HasDerivAt g d x₀) (hmin : ∀ y ∈ Set.Ioo (x₀ - ε) x₀, g x₀ ≤ g y) : d ≤ 0 := by + rw [hasDerivAt_iff_tendsto_slope] at hd + haveI : (nhdsWithin x₀ (Set.Iio x₀)).NeBot := + nhdsWithin_Iio_self_neBot' ⟨x₀ - 1, by simp [Set.mem_Iio]⟩ + have hiio_sub : Set.Iio x₀ ⊆ {x₀}ᶜ := fun y hy => by + simp [(Set.mem_Iio.mp hy).ne] + have hslope2 := hd.mono_left (nhdsWithin_mono x₀ hiio_sub) + suffices h : ∀ᶠ y in nhdsWithin x₀ (Set.Iio x₀), slope g x₀ y ≤ 0 from + le_of_tendsto hslope2 h + have hIoo : Set.Ioo (x₀ - ε) x₀ ∈ nhdsWithin x₀ (Set.Iio x₀) := by + rw [mem_nhdsWithin] + exact ⟨Set.Ioo (x₀ - ε) (x₀ + ε), isOpen_Ioo, ⟨by linarith, by linarith⟩, + fun y ⟨hy1, hy2⟩ => ⟨hy1.1, hy2⟩⟩ + filter_upwards [hIoo] with y hy + simp only [slope_def_field] + exact div_nonpos_of_nonneg_of_nonpos (sub_nonneg.mpr (hmin y hy)) (by linarith [hy.2]) + +end diff --git a/QuantumSystem/ForMathlib/Analysis/Complex/Basic.lean b/QuantumSystem/ForMathlib/Analysis/Complex/Basic.lean index e9634c2..37576dd 100644 --- a/QuantumSystem/ForMathlib/Analysis/Complex/Basic.lean +++ b/QuantumSystem/ForMathlib/Analysis/Complex/Basic.lean @@ -1,11 +1,45 @@ module public import Mathlib.Analysis.Complex.Basic +public import Mathlib.Analysis.Complex.Exponential +public import Mathlib.Analysis.Complex.Order +public import Mathlib.Analysis.SpecialFunctions.Complex.Arg @[expose] public section open ComplexConjugate +/-! ### Non-negativity in `ComplexOrder` for real-valued complex numbers + +Convenience reformulations of `Complex.zero_le_real` for use sites that pass a +real witness; primarily consumed by spectral / density-matrix calculations +where one needs `(0 : ℂ) ≤ (ε : ℂ)` with `ε : ℝ`. -/ + +namespace Complex + +open scoped ComplexOrder + +/-- Real `ε` lifted to complex with the natural ordering: `0 ≤ ε` in ℝ implies `0 ≤ ε` in ℂ. -/ +lemma zero_le_ofReal {ε : ℝ} (hε : 0 ≤ ε) : (0 : ℂ) ≤ (ε : ℂ) := + Complex.zero_le_real.mpr hε + +/-- Real `1 - ε` lifted to complex is non-negative when `ε ≤ 1`. -/ +lemma zero_le_one_sub_ofReal {ε : ℝ} (hε' : ε ≤ 1) : (0 : ℂ) ≤ (1 - (ε : ℂ)) := by + rw [show (1 - (ε : ℂ)) = ((1 - ε : ℝ) : ℂ) from by push_cast; ring] + exact Complex.zero_le_real.mpr (by linarith) + +/-- The reciprocal of a natural number lifted to `ℂ` is non-negative. -/ +lemma zero_le_natCast_inv (n : ℕ) : (0 : ℂ) ≤ ((n : ℂ)⁻¹) := by + refine ⟨?_, ?_⟩ + · simp only [Complex.zero_re, Complex.inv_re, Complex.natCast_re, Complex.normSq_natCast] + by_cases hn : n = 0 + · simp [hn] + · have hn' : (0 : ℝ) < n := by exact_mod_cast Nat.pos_of_ne_zero hn + positivity + · simp [Complex.inv_im, Complex.natCast_im] + +end Complex + /-- Every complex number can be multiplied by a unit complex number to obtain its norm. -/ lemma Complex.phase_alignment (c : ℂ) : ∃ γ : ℂ, ‖γ‖ = 1 ∧ γ * c = ‖c‖ := by by_cases h : c = 0 @@ -16,3 +50,29 @@ lemma Complex.phase_alignment (c : ℂ) : ∃ γ : ℂ, ‖γ‖ = 1 ∧ γ * c · simp [norm_eq_zero.not.2 h] · field_simp [norm_eq_zero.not.2 h] simp [Complex.conj_mul', pow_two] + +/-- Lemma for rotation trick: exp(-I * arg z) * z = |z|. -/ +lemma Complex.exp_neg_I_arg_mul_self (z : ℂ) : Complex.exp (-Complex.I * Complex.arg z) * z = ↑‖z‖ := by + by_cases hz : z = 0 + · simp [hz] + · have hz2 : z = ↑‖z‖ * Complex.exp (↑(Complex.arg z) * Complex.I) := + (Complex.norm_mul_exp_arg_mul_I z).symm + calc Complex.exp (-Complex.I * Complex.arg z) * z + = Complex.exp (-Complex.I * Complex.arg z) * (↑‖z‖ * Complex.exp (↑(Complex.arg z) * Complex.I)) := by rw [← hz2] + _ = ↑‖z‖ * (Complex.exp (-Complex.I * Complex.arg z) * Complex.exp (↑(Complex.arg z) * Complex.I)) := by ring + _ = ↑‖z‖ * Complex.exp (-Complex.I * ↑(Complex.arg z) + ↑(Complex.arg z) * Complex.I) := by rw [← Complex.exp_add] + _ = ↑‖z‖ * Complex.exp 0 := by ring_nf + _ = ↑‖z‖ := by simp + +/-- Helper: for any complex z, there exists u with ‖u‖ ≤ 1 such that ‖z‖ = Re(u * z). -/ +lemma Complex.exists_unit_mul_eq_norm (z : ℂ) : ∃ (u : ℂ), ‖u‖ ≤ 1 ∧ ‖z‖ = (u * z).re := by + by_cases hz : z = 0 + · use 0; simp [hz] + · use Complex.exp (-Complex.I * Complex.arg z) + constructor + · rw [Complex.norm_exp] + simp only [neg_mul, neg_re, mul_re, Complex.I_re, Complex.I_im, one_mul, + ofReal_re, zero_mul] + simp + · rw [exp_neg_I_arg_mul_self] + simp diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/Adjoint.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/Adjoint.lean new file mode 100644 index 0000000..b8fe2f5 --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/Adjoint.lean @@ -0,0 +1,30 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.Adjoint + +/-! +# Self-adjoint operators on inner product spaces + +This file contains results about self-adjoint operators on inner product spaces. +-/ + +@[expose] public section + +open scoped InnerProductSpace + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [CompleteSpace E] + +namespace ContinuousLinearMap + +/-- For a self-adjoint operator T, if ⟪Tx, x⟫ = 0 for all x, then T = 0. +This is a corollary of the polarization identity `LinearMap.IsSymmetric.inner_map_self_eq_zero`. -/ +theorem IsSelfAdjoint.eq_zero_of_inner_map_self_eq_zero {T : E →L[ℂ] E} + (hsa : IsSelfAdjoint T) (h : ∀ x, ⟪T x, x⟫_ℂ = 0) : T = 0 := by + have hT_sym : (T : E →ₗ[ℂ] E).IsSymmetric := hsa.isSymmetric + have h' : (T : E →ₗ[ℂ] E) = 0 := hT_sym.inner_map_self_eq_zero.mp h + ext x + have := congrFun (congrArg DFunLike.coe h') x + simp only [LinearMap.zero_apply, ContinuousLinearMap.coe_coe] at this + exact this + +end ContinuousLinearMap diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProduct.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProduct.lean new file mode 100644 index 0000000..6859b7c --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProduct.lean @@ -0,0 +1,91 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.TensorProduct + +/-! +# Operator tensor product for finite-dimensional inner product spaces + +This file provides the continuous-linear-map version of `TensorProduct.map` for +finite-dimensional inner product spaces, filling a gap in Mathlib's +`Analysis/InnerProductSpace/TensorProduct.lean` (whose header TODO lists +"Define the continuous linear map version of `TensorProduct.map`"). + +Because finite-dimensional normed spaces are automatically complete and all +linear maps between them are continuous, we specialise to +`[FiniteDimensional ℂ H] [FiniteDimensional ℂ K]` throughout. + +## Main definitions + +* `ContinuousLinearMap.tensor` — for finite-dimensional Hilbert spaces `H`, `K` + over `ℂ`, the tensor product `A ⊗ B : H ⊗[ℂ] K →L[ℂ] H ⊗[ℂ] K` of two + operators `A : H →L[ℂ] H` and `B : K →L[ℂ] K`. + +The supporting lemmas (action on pure tensors, multiplicativity, bilinearity, +`tensor 1 1 = 1`) are kept `private`; downstream code only consumes the +definition itself together with the standard `simp` set. +-/ + +@[expose] public section + +open scoped TensorProduct + +variable {H K : Type*} + [NormedAddCommGroup H] [InnerProductSpace ℂ H] + [NormedAddCommGroup K] [InnerProductSpace ℂ K] + [FiniteDimensional ℂ H] [FiniteDimensional ℂ K] + +namespace ContinuousLinearMap + +/-- Tensor product of operators on finite-dimensional Hilbert spaces. + +Defined as the continuous linear map underlying `TensorProduct.map A.toLinearMap +B.toLinearMap`; the continuity is automatic because `H ⊗[ℂ] K` is +finite-dimensional. -/ +noncomputable def tensor (A : H →L[ℂ] H) (B : K →L[ℂ] K) : + H ⊗[ℂ] K →L[ℂ] H ⊗[ℂ] K := + LinearMap.toContinuousLinearMap (TensorProduct.map A.toLinearMap B.toLinearMap) + +private lemma tensor_toLinearMap (A : H →L[ℂ] H) (B : K →L[ℂ] K) : + (tensor A B).toLinearMap = TensorProduct.map A.toLinearMap B.toLinearMap := + LinearMap.coe_toContinuousLinearMap _ + +@[simp] +private lemma tensor_tmul (A : H →L[ℂ] H) (B : K →L[ℂ] K) (x : H) (y : K) : + tensor A B (x ⊗ₜ[ℂ] y) = A x ⊗ₜ[ℂ] B y := by + change ((tensor A B).toLinearMap) (x ⊗ₜ[ℂ] y) = _ + rw [tensor_toLinearMap] + exact TensorProduct.map_tmul _ _ _ _ + +@[simp] +private lemma tensor_one : tensor (1 : H →L[ℂ] H) (1 : K →L[ℂ] K) = 1 := by + ext z + induction z using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp + | add a b ha hb => simp [map_add, ha, hb] + +private lemma tensor_mul (A₁ A₂ : H →L[ℂ] H) (B₁ B₂ : K →L[ℂ] K) : + tensor (A₁ * A₂) (B₁ * B₂) = tensor A₁ B₁ * tensor A₂ B₂ := by + ext z + induction z using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.mul_apply] + | add a b ha hb => simp [map_add, ha, hb] + +private lemma tensor_add_left (A₁ A₂ : H →L[ℂ] H) (B : K →L[ℂ] K) : + tensor (A₁ + A₂) B = tensor A₁ B + tensor A₂ B := by + ext z + induction z using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.add_apply, TensorProduct.add_tmul] + | add a b ha hb => simp [map_add, ha, hb] + +private lemma tensor_add_right (A : H →L[ℂ] H) (B₁ B₂ : K →L[ℂ] K) : + tensor A (B₁ + B₂) = tensor A B₁ + tensor A B₂ := by + ext z + induction z using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.add_apply, TensorProduct.tmul_add] + | add a b ha hb => simp [map_add, ha, hb] + +end ContinuousLinearMap diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/l2Space.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/l2Space.lean new file mode 100644 index 0000000..ec4feaa --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/l2Space.lean @@ -0,0 +1,67 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.l2Space + +/-! +# Helper lemmas for Hilbert bases and Parseval identity + +This file provides additional lemmas for Hilbert bases that extend Mathlib's `l2Space`. + +## Main results + +* `inner_mul_inner_eq_norm_sq`: For a Hilbert basis, `⟪x, bᵢ⟫ * ⟪bᵢ, x⟫ = ‖⟪bᵢ, x⟫‖²` +* `HilbertBasis.norm_sq_eq_tsum_norm_sq_inner'`: Parseval identity `‖x‖² = ∑ᵢ ‖⟪bᵢ, x⟫‖²` +* `HilbertBasis.summable_norm_sq_inner'`: The sequence `‖⟪bᵢ, x⟫‖²` is summable +-/ + +@[expose] public section + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] +variable {ι : Type*} + +/-- For a Hilbert basis, `⟪x, bᵢ⟫ * ⟪bᵢ, x⟫ = ‖⟪bᵢ, x⟫‖²` as complex numbers. -/ +theorem inner_mul_inner_eq_norm_sq (b : HilbertBasis ι ℂ H) (x : H) (i : ι) : + inner (𝕜 := ℂ) x (b i) * inner (𝕜 := ℂ) (b i) x = (‖inner (𝕜 := ℂ) (b i) x‖^2 : ℂ) := by + rw [← inner_conj_symm (𝕜 := ℂ)] + have h : ↑(Complex.normSq (inner ℂ (b i) x)) = (starRingEnd ℂ) (inner ℂ (b i) x) * inner ℂ (b i) x := + Complex.normSq_eq_conj_mul_self + rw [← h] + norm_cast + exact Complex.normSq_eq_norm_sq _ + +/-- Helper: real part of a real power. -/ +private lemma ofReal_pow_re (r : ℝ) (n : ℕ) : ((r : ℂ)^n).re = r^n := by + induction n with + | zero => simp + | succ n ih => + rw [pow_succ, pow_succ] + rw [Complex.mul_re, Complex.ofReal_re, Complex.ofReal_im, ih] + ring + +/-- Parseval identity for Hilbert bases: `‖x‖² = ∑ᵢ ‖⟪bᵢ, x⟫‖²`. -/ +theorem HilbertBasis.norm_sq_eq_tsum_norm_sq_inner' (b : HilbertBasis ι ℂ H) (x : H) : + ‖x‖^2 = ∑' i, ‖inner (𝕜 := ℂ) (b i) x‖^2 := by + have h := b.tsum_inner_mul_inner x x + simp_rw [inner_mul_inner_eq_norm_sq b x] at h + have h2 : (inner (𝕜 := ℂ) x x).re = ‖x‖^2 := by + rw [inner_self_eq_norm_sq_to_K] + exact ofReal_pow_re _ 2 + have h3 : (∑' i, (‖inner (𝕜 := ℂ) (b i) x‖^2 : ℂ)).re = ∑' i, ‖inner (𝕜 := ℂ) (b i) x‖^2 := by + rw [Complex.re_tsum] + · congr 1 + ext i + exact ofReal_pow_re _ 2 + · have := b.summable_inner_mul_inner x x + simp_rw [inner_mul_inner_eq_norm_sq b x] at this + exact this + rw [← h2, ← h, h3] + +/-- The sequence `‖⟪bᵢ, x⟫‖²` is summable for any Hilbert basis. -/ +theorem HilbertBasis.summable_norm_sq_inner' (b : HilbertBasis ι ℂ H) (x : H) : + Summable (fun i => ‖inner (𝕜 := ℂ) (b i) x‖^2) := by + have hsummable := b.summable_inner_mul_inner x x + simp_rw [inner_mul_inner_eq_norm_sq b x] at hsummable + obtain ⟨s, hs⟩ := hsummable + have hs_re := Complex.hasSum_re hs + simp_rw [ofReal_pow_re] at hs_re + exact hs_re.summable diff --git a/QuantumSystem/ForMathlib/Analysis/Matrix/Basic.lean b/QuantumSystem/ForMathlib/Analysis/Matrix/Basic.lean new file mode 100644 index 0000000..b239a0b --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/Matrix/Basic.lean @@ -0,0 +1,479 @@ +module + +public import Mathlib.Analysis.CStarAlgebra.CStarMatrix +public import Mathlib.Analysis.Matrix.Order +public import Mathlib.Analysis.CStarAlgebra.Classes +public import Mathlib.Data.Matrix.ColumnRowPartitioned + +/-! +# Block-Matrix Lemmas + +This file collects block-matrix identities used in the HPJ and related inequalities. + +## Main results + +- `Matrix.fromBlocks_mulVec_inl`: block-diagonal matrix-vector product on the left block. +- `Matrix.fromBlocks_mulVec_inr`: block-diagonal matrix-vector product on the right block. +- `Matrix.fromRows_compress_blockDiag`: + `(fromRows A B)ᴴ * fromBlocks(T₁, 0, 0, T₂) * (fromRows A B) = AᴴT₁A + BᴴT₂B`. +- `Matrix.inner_conjTranspose_mul_self_mulVec`: `x†(VᴴV)x = (Vx)†(Vx)`. +- `Matrix.inner_compress_mulVec`: `x†(VᴴAV)x = (Vx)†A(Vx)`. +- `Matrix.rpow_unitary_conj`: CFC rpow commutes with unitary conjugation, + (UMU†)ᵖ = U Mᵖ U†. +- `Matrix.diagonal_rpow`: rpow of a diagonal matrix equals the diagonal of componentwise rpow. +- `Matrix.inv_transpose_rpow_mul_transpose_eq`: for PD B and p ≥ 0, + ((B⁻¹)ᵀ)ᵖ · Bᵀ = (B¹⁻ᵖ)ᵀ. + +## Positive Definite / Positive Semidefinite results + +- `Matrix.posDef_one`: the identity matrix is positive definite. +- `Matrix.posSemidef_one`: the identity matrix is positive semidefinite. +- `Matrix.fromBlocks_inv_posSemidef`: the block matrix [A, I; I, A⁻¹] + is positive semidefinite when A is positive definite. +- `Matrix.fromRows_conjTranspose_mul_self`: VᴴV = AᴴA + BᴴB for V = [A; B]. +- `Matrix.PosSemidef.diagonal_ofReal`: a diagonal matrix with nonneg real entries is positive + semidefinite. +- `Matrix.PosSemidef.one_sub_fromRows`: if AᴴA + BᴴB ≤ I, then I − VᴴV is PSD for + V = [A; B]. +- `Matrix.PosSemidef.smul_nonpos`: scaling a PSD matrix by a nonpositive real scalar gives a + matrix ≤ 0. +- `Matrix.PosSemidef.add_smul_one_posDef`: A + rI is positive definite for A ≥ 0, r > 0. +- `Matrix.fromBlocks_diag_posSemidef`: `fromBlocks A 0 0 D` is PSD when A and D are PSD. +- `Matrix.trace_fromBlocks`: Tr(fromBlocks A B C D) = Tr A + Tr D. +-/ + +@[expose] public section + +namespace Matrix + +open scoped MatrixOrder ComplexOrder + +/-- For a block-diagonal matrix `fromBlocks A 0 0 D`, the left block of the product `M *ᵥ v` +depends only on `A` and the left part of `v`: `(M *ᵥ v) (inl i) = (A *ᵥ vₗ) i`. -/ +lemma fromBlocks_mulVec_inl {m n : Type*} [Fintype m] [Fintype n] + (A : Matrix m m ℂ) (D : Matrix n n ℂ) (v : m ⊕ n → ℂ) (i : m) : + (Matrix.fromBlocks A 0 0 D *ᵥ v) (Sum.inl i) = (A *ᵥ fun j => v (Sum.inl j)) i := by + classical + -- Split the sum over the sum type and use block entry formulas. + change (∑ j, Matrix.fromBlocks A 0 0 D (Sum.inl i) j * v j) = _ + simp [Matrix.mulVec, dotProduct, Fintype.sum_sum_type, fromBlocks_apply₁₁, fromBlocks_apply₁₂] + +/-- For a block-diagonal matrix `fromBlocks A 0 0 D`, the right block of the product `M *ᵥ v` +depends only on `D` and the right part of `v`: `(M *ᵥ v) (inr i) = (D *ᵥ vᵣ) i`. -/ +lemma fromBlocks_mulVec_inr {m n : Type*} [Fintype m] [Fintype n] + (A : Matrix m m ℂ) (D : Matrix n n ℂ) (v : m ⊕ n → ℂ) (i : n) : + (Matrix.fromBlocks A 0 0 D *ᵥ v) (Sum.inr i) = (D *ᵥ fun j => v (Sum.inr j)) i := by + classical + -- Split the sum over the sum type and use block entry formulas. + change (∑ j, Matrix.fromBlocks A 0 0 D (Sum.inr i) j * v j) = _ + simp [Matrix.mulVec, dotProduct, Fintype.sum_sum_type, fromBlocks_apply₂₁, fromBlocks_apply₂₂] + +/-- Sandwiching a block-diagonal matrix `fromBlocks T₁ 0 0 T₂` by the stacked matrix +`fromRows A B` decomposes into two independent terms: +`(fromRows A B)ᴴ * fromBlocks T₁ 0 0 T₂ * fromRows A B = Aᴴ * T₁ * A + Bᴴ * T₂ * B`. +This is useful for reducing block-matrix inequalities to separate inequalities for each block. -/ +lemma fromRows_compress_blockDiag + {m₁ m₂ n : Type*} [Fintype m₁] [Fintype m₂] [Fintype n] + (A : Matrix m₁ n ℂ) (B : Matrix m₂ n ℂ) + (T₁ : Matrix m₁ m₁ ℂ) (T₂ : Matrix m₂ m₂ ℂ) : + (Matrix.fromRows A B)ᴴ * (Matrix.fromBlocks T₁ 0 0 T₂) * Matrix.fromRows A B = + Aᴴ * T₁ * A + Bᴴ * T₂ * B := by + classical + -- Compute with block multiplication rules. + have hconj : (Matrix.fromRows A B)ᴴ = Matrix.fromCols Aᴴ Bᴴ := by + simpa using (Matrix.conjTranspose_fromRows_eq_fromCols_conjTranspose (A₁ := A) (A₂ := B)) + have hmul1 : + (Matrix.fromCols Aᴴ Bᴴ) * + (Matrix.fromBlocks T₁ (0 : Matrix m₁ m₂ ℂ) (0 : Matrix m₂ m₁ ℂ) T₂) = + Matrix.fromCols (Aᴴ * T₁) (Bᴴ * T₂) := by + simpa [Matrix.mul_zero, Matrix.zero_mul, add_zero, zero_add] using + (Matrix.fromCols_mul_fromBlocks (A₁ := Aᴴ) (A₂ := Bᴴ) + (B₁₁ := T₁) (B₁₂ := (0 : Matrix m₁ m₂ ℂ)) + (B₂₁ := (0 : Matrix m₂ m₁ ℂ)) (B₂₂ := T₂)) + have hmul2 : + Matrix.fromCols (Aᴴ * T₁) (Bᴴ * T₂) * Matrix.fromRows A B = + Aᴴ * T₁ * A + Bᴴ * T₂ * B := by + simpa [Matrix.mul_assoc] using + (Matrix.fromCols_mul_fromRows (A₁ := Aᴴ * T₁) (A₂ := Bᴴ * T₂) + (B₁ := A) (B₂ := B)) + calc + (Matrix.fromRows A B)ᴴ * (Matrix.fromBlocks T₁ 0 0 T₂) * Matrix.fromRows A B = + (Matrix.fromCols Aᴴ Bᴴ) * (Matrix.fromBlocks T₁ 0 0 T₂) * Matrix.fromRows A B := by + simp [hconj] + _ = Matrix.fromCols (Aᴴ * T₁) (Bᴴ * T₂) * Matrix.fromRows A B := by + rw [hmul1] + _ = Aᴴ * T₁ * A + Bᴴ * T₂ * B := by + simpa using hmul2 + +/-- The quadratic form `x† (Vᴴ V) x` equals `(Vx)† (Vx)`. -/ +lemma inner_conjTranspose_mul_self_mulVec {m n : Type*} [Fintype m] [Fintype n] + (V : Matrix m n ℂ) (x : n → ℂ) : + star x ⬝ᵥ ((Vᴴ * V) *ᵥ x) = star (V *ᵥ x) ⬝ᵥ (V *ᵥ x) := by + classical + have hmul : (Vᴴ * V) *ᵥ x = Vᴴ *ᵥ (V *ᵥ x) := by + simp only [Matrix.mulVec_mulVec] + calc + star x ⬝ᵥ ((Vᴴ * V) *ᵥ x) = star x ⬝ᵥ (Vᴴ *ᵥ (V *ᵥ x)) := by + rw [hmul] + _ = (star x ᵥ* Vᴴ) ⬝ᵥ (V *ᵥ x) := by + simpa using (Matrix.dotProduct_mulVec (v := star x) (A := Vᴴ) (w := V *ᵥ x)) + _ = star (V *ᵥ x) ⬝ᵥ (V *ᵥ x) := by + simp [Matrix.vecMul_conjTranspose] + +/-- The quadratic form `x† (Vᴴ A V) x` equals `(Vx)† A (Vx)`. -/ +lemma inner_compress_mulVec {m n : Type*} [Fintype m] [Fintype n] + (V : Matrix m n ℂ) (A : Matrix m m ℂ) (x : n → ℂ) : + star x ⬝ᵥ ((Vᴴ * A * V) *ᵥ x) = star (V *ᵥ x) ⬝ᵥ (A *ᵥ (V *ᵥ x)) := by + classical + calc + star x ⬝ᵥ ((Vᴴ * A * V) *ᵥ x) = star x ⬝ᵥ (Vᴴ *ᵥ (A *ᵥ (V *ᵥ x))) := by + simp only [Matrix.mulVec_mulVec, Matrix.mul_assoc] + _ = (star x ᵥ* Vᴴ) ⬝ᵥ (A *ᵥ (V *ᵥ x)) := by + simpa using (Matrix.dotProduct_mulVec (v := star x) (A := Vᴴ) + (w := A *ᵥ (V *ᵥ x))) + _ = star (V *ᵥ x) ⬝ᵥ (A *ᵥ (V *ᵥ x)) := by + simp [Matrix.vecMul_conjTranspose] + +/-- CFC rpow commutes with unitary conjugation: (U M U†)^p = U M^p U†. +This follows from `StarAlgHomClass.map_cfc` applied to the inner automorphism. -/ +lemma rpow_unitary_conj {n : Type*} [Fintype n] [DecidableEq n] + {U M : Matrix n n ℂ} (hU : U ∈ Matrix.unitaryGroup n ℂ) + {p : ℝ} (hp : 0 ≤ p) (hM : 0 ≤ M) (hM' : 0 ≤ U * M * Uᴴ := by cfc_tac) : + (U * M * Uᴴ) ^ p = U * (M ^ p) * Uᴴ := by + letI : NormedRing (Matrix n n ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix n n ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := n) (A := ℂ) + -- Convert to unitary element + have hUmem : U ∈ unitary (Matrix n n ℂ) := by + rw [Unitary.mem_iff] + exact ⟨Matrix.mem_unitaryGroup_iff'.mp hU, Matrix.mem_unitaryGroup_iff.mp hU⟩ + let u : unitary (Matrix n n ℂ) := ⟨U, hUmem⟩ + let φ := Unitary.conjStarAlgAut ℝ (Matrix n n ℂ) u + have hφ_apply : ∀ x, φ x = U * x * Uᴴ := by + intro x; simp [φ, Unitary.conjStarAlgAut_apply, u, star_eq_conjTranspose] + rw [← hφ_apply M, ← hφ_apply (M ^ p)] + -- Convert rpow to CFC + rw [CFC.rpow_eq_cfc_real (a := M) (ha := hM)] + rw [CFC.rpow_eq_cfc_real (a := φ M) (ha := by rw [hφ_apply]; exact hM')] + have hcont : ContinuousOn (· ^ p) (spectrum ℝ M) := + (Real.continuous_rpow_const hp).continuousOn + -- Continuity of φ follows from finite-dimensionality + have hφ_cont : Continuous φ := + φ.toAlgEquiv.toLinearMap.continuous_of_finiteDimensional + -- IsSelfAdjoint φ M follows from M being self-adjoint and φ preserving star + have hM_sa : IsSelfAdjoint M := by + have : M.PosSemidef := by simpa [Matrix.le_iff] using hM + exact this.1.isSelfAdjoint + have hφM_sa : IsSelfAdjoint (φ M) := by + rw [IsSelfAdjoint] + rw [← map_star φ] + exact congr_arg φ hM_sa.star_eq + symm + exact StarAlgHomClass.map_cfc (R := ℝ) (S := ℝ) φ (· ^ p) M hcont hφ_cont + +/-- rpow of a diagonal matrix with nonneg real entries equals the diagonal +of componentwise rpow. + +Proof outline: +1. Express Dᵖ via `CFC.rpow_eq_cfc_real`, reducing to showing + `cfc (· ^ p) (diagonal d) = diagonal (fun i => d i ^ p)`. +2. `diagonal : (n → ℂ) →⋆ₐ[ℝ] Matrix n n ℂ` is a continuous star algebra + homomorphism (constructed inline), so `StarAlgHomClass.map_cfc` moves the CFC + inside: `cfc (· ^ p) (diagonal dc) = diagonal (cfc (· ^ p) dc)`. +3. In the commutative Pi C*-algebra `n → ℂ`, CFC is pointwise + (`cfc_map_pi`), and each entry `(d i : ℂ) = algebraMap ℝ ℂ (d i)` gives + `cfc (· ^ p) (d i : ℂ) = (d i ^ p : ℝ) : ℂ` via `cfc_algebraMap`. -/ +lemma diagonal_rpow {n : Type*} [Fintype n] [DecidableEq n] + (d : n → ℝ) (hd : ∀ i, 0 ≤ d i) (p : ℝ) (hp : 0 ≤ p) : + (diagonal (fun i => (d i : ℂ))) ^ p = diagonal (fun i => ((d i ^ p : ℝ) : ℂ)) := by + letI : NormedRing (Matrix n n ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix n n ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := n) (A := ℂ) + let dc : n → ℂ := fun i => (d i : ℂ) + have hD_psd : (diagonal dc).PosSemidef := by + rw [posSemidef_diagonal_iff] + intro i; simp only [dc, Complex.zero_le_real]; exact_mod_cast hd i + have hD : (0 : Matrix n n ℂ) ≤ diagonal dc := by + simpa [Matrix.le_iff] using hD_psd + rw [show (fun i => (d i : ℂ)) = dc from rfl, CFC.rpow_eq_cfc_real (ha := hD)] + -- Build `diagonal` as a star algebra hom (n → ℂ) →⋆ₐ[ℝ] Matrix n n ℂ inline. + let φ : (n → ℂ) →⋆ₐ[ℝ] Matrix n n ℂ := + { Matrix.diagonalAlgHom (R := ℝ) with + map_star' := fun v => by + change diagonal (star v) = (diagonal v)ᴴ + rw [diagonal_conjTranspose] } + have hφ_cont : Continuous φ := + φ.toAlgHom.toLinearMap.continuous_of_finiteDimensional + -- `dc` is self-adjoint: all entries are real, hence equal to their conjugate. + have hdc_sa : IsSelfAdjoint dc := by + rw [IsSelfAdjoint, Pi.star_def]; ext i; simp [dc, Complex.conj_ofReal] + have hφdc_sa : IsSelfAdjoint (φ dc) := by + rw [IsSelfAdjoint, ← map_star φ]; exact congr_arg φ hdc_sa.star_eq + -- CFC commutes with the star algebra hom φ. + have h_map := StarAlgHomClass.map_cfc (R := ℝ) (S := ℝ) φ (· ^ p) dc + ((Real.continuous_rpow_const hp).continuousOn) hφ_cont hdc_sa hφdc_sa + -- φ dc = diagonal dc, so rewrite both sides. + have hφ_dc : φ dc = diagonal dc := rfl + rw [← hφ_dc, ← h_map] + -- Goal: φ (cfc (· ^ p) dc) = diagonal (fun i => (d i ^ p : ℝ) : ℂ) + change diagonal (cfc (· ^ p) dc) = diagonal (fun i => ((d i ^ p : ℝ) : ℂ)) + -- In the Pi C*-algebra n → ℂ, CFC is pointwise. + rw [cfc_map_pi (S := ℝ) (· ^ p) dc] + congr 1; funext i + simp only [dc] + rw [show (d i : ℂ) = algebraMap ℝ ℂ (d i) from rfl, cfc_algebraMap (A := ℂ) (d i) (· ^ p)] + rfl + +/-- For a positive definite matrix `B` and `p ≥ 0`, +`((B⁻¹)ᵀ) ^ p * Bᵀ = (B ^ (1 - p))ᵀ`. -/ +lemma inv_transpose_rpow_mul_transpose_eq {m : Type*} [Fintype m] [DecidableEq m] + (B : Matrix m m ℂ) (hB : B.PosDef) (p : ℝ) (hp : 0 ≤ p) : + ((B⁻¹)ᵀ) ^ p * Bᵀ = (B ^ (1 - p))ᵀ := by + letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing + letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra + letI : CStarAlgebra (Matrix m m ℂ) := by + simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) + have hB_unit : IsUnit B := hB.isUnit + have hB_det : IsUnit B.det := (Matrix.isUnit_iff_isUnit_det B).mp hB_unit + have hBinv_herm : (B⁻¹).IsHermitian := by + rw [Matrix.IsHermitian, conjTranspose_nonsing_inv, hB.1.eq] + have hBinv_psd : (B⁻¹).PosSemidef := hB.posSemidef.inv + have hBinvT_psd : ((B⁻¹)ᵀ).PosSemidef := hBinv_psd.transpose + have hBinvT_nonneg : (0 : Matrix m m ℂ) ≤ (B⁻¹)ᵀ := by + simpa [Matrix.le_iff] using hBinvT_psd + -- Spectral decomposition of B⁻¹ + set UB := hBinv_herm.eigenvectorUnitary with hUB_def + set dB := hBinv_herm.eigenvalues with hdB_def + have hdB_nonneg : ∀ i, 0 ≤ dB i := hBinv_psd.eigenvalues_nonneg + have hD_nonneg : (0 : Matrix m m ℂ) ≤ diagonal (RCLike.ofReal ∘ dB) := + (posSemidef_diagonal_iff.mpr fun i => RCLike.ofReal_nonneg.mpr (hdB_nonneg i)).nonneg + have hSpec : B⁻¹ = (UB : Matrix m m ℂ) * diagonal (RCLike.ofReal ∘ dB) * + (UB : Matrix m m ℂ)ᴴ := by + rw [hBinv_herm.spectral_theorem (𝕜 := ℂ), Unitary.conjStarAlgAut_apply, + star_eq_conjTranspose] + have hD_rpow : diagonal (RCLike.ofReal ∘ dB) ^ p = + diagonal (fun i => ((dB i ^ p : ℝ) : ℂ)) := by + change diagonal (fun i => (dB i : ℂ)) ^ p = _ + exact diagonal_rpow dB hdB_nonneg p hp + have hBinv_rpow_spec : (B⁻¹) ^ p = (UB : Matrix m m ℂ) * + diagonal (fun i => ((dB i ^ p : ℝ) : ℂ)) * (UB : Matrix m m ℂ)ᴴ := by + conv_lhs => rw [hSpec] + rw [rpow_unitary_conj UB.2 hp hD_nonneg + (hM' := by rw [← hSpec]; simpa [Matrix.le_iff] using hBinv_psd), hD_rpow] + -- Transpose commutes with rpow for B⁻¹ via spectral decomposition + have htr_rpow : ((B⁻¹)ᵀ) ^ p = ((B⁻¹) ^ p)ᵀ := by + have hDt : (diagonal (RCLike.ofReal ∘ dB) : Matrix m m ℂ)ᵀ = + diagonal (RCLike.ofReal ∘ dB) := by + ext i j + simp only [transpose_apply, diagonal_apply] + by_cases h : i = j + · subst h + simp + · simp [h, show ¬(j = i) from fun a => h a.symm] + have hDpt : (diagonal (fun i => ((dB i ^ p : ℝ) : ℂ)))ᵀ = + diagonal (fun i => ((dB i ^ p : ℝ) : ℂ)) := by + ext i j + simp only [transpose_apply, diagonal_apply] + by_cases h : i = j + · subst h + simp + · simp [h, show ¬(j = i) from fun a => h a.symm] + have hWH_eq : ((UB : Matrix m m ℂ)ᴴ)ᵀᴴ = ((UB : Matrix m m ℂ))ᵀ := by + ext i j + simp [conjTranspose_apply, transpose_apply] + have hW_unitary : ((UB : Matrix m m ℂ)ᴴ)ᵀ ∈ Matrix.unitaryGroup m ℂ := by + rw [Matrix.mem_unitaryGroup_iff', star_eq_conjTranspose, hWH_eq] + have hU_mul : (UB : Matrix m m ℂ)ᴴ * (UB : Matrix m m ℂ) = 1 := by + have := Unitary.coe_star_mul_self UB + simp only [star_eq_conjTranspose] at this + exact this + have h_prod := congr_arg Matrix.transpose hU_mul + simp only [Matrix.transpose_mul, Matrix.transpose_one] at h_prod + exact h_prod + have hBinvT_spec : (B⁻¹)ᵀ = ((UB : Matrix m m ℂ)ᴴ)ᵀ * + diagonal (RCLike.ofReal ∘ dB) * (((UB : Matrix m m ℂ)ᴴ)ᵀ)ᴴ := by + rw [hWH_eq, hSpec] + simp only [Matrix.transpose_mul, hDt, Matrix.mul_assoc] + have hBinvT_nonneg' : 0 ≤ ((UB : Matrix m m ℂ)ᴴ)ᵀ * + diagonal (RCLike.ofReal ∘ dB) * (((UB : Matrix m m ℂ)ᴴ)ᵀ)ᴴ := by + rw [← hBinvT_spec] + exact hBinvT_nonneg + conv_lhs => rw [hBinvT_spec] + rw [rpow_unitary_conj hW_unitary hp hD_nonneg (hM' := hBinvT_nonneg'), hD_rpow] + rw [hBinv_rpow_spec] + simp only [Matrix.transpose_mul, hDpt, Matrix.mul_assoc, hWH_eq] + rw [htr_rpow, ← Matrix.transpose_mul] + congr 1 + have hB_nonneg : (0 : Matrix m m ℂ) ≤ B := by + simpa [Matrix.le_iff] using hB.posSemidef + have hBinv_cfc : B⁻¹ = B ^ (-1 : ℝ) := by + have h1 : B ^ (-1 : ℝ) * B = 1 := by + have := CFC.rpow_neg_mul_rpow (1 : ℝ) hB_unit hB_nonneg + rwa [CFC.rpow_one B hB_nonneg] at this + have h2 : B⁻¹ * B = 1 := Matrix.nonsing_inv_mul B hB_det + exact hB_unit.mul_right_cancel (h2.trans h1.symm) + have hBinv_rpow : (B⁻¹) ^ p = B ^ (-p) := by + rw [hBinv_cfc, CFC.rpow_rpow B (-1 : ℝ) p hB_unit (by norm_num)] + congr 1 + ring + rw [hBinv_rpow] + have h_add : B ^ (1 + (-p)) = B ^ (1 : ℝ) * B ^ (-p) := + CFC.rpow_add (x := 1) (y := -p) hB_unit + rw [CFC.rpow_one B hB_nonneg] at h_add + rw [← h_add, show (1 + (-p) : ℝ) = 1 - p from by ring] + +/-! ### Positive Definite and Positive Semidefinite Matrices -/ + +/-- The identity matrix is positive definite. -/ +lemma posDef_one {m : Type*} [Fintype m] [DecidableEq m] : + (1 : Matrix m m ℂ).PosDef := by + classical + refine Matrix.PosDef.of_dotProduct_mulVec_pos ?_ ?_ + · simp [IsHermitian] + · intro x hx + have hpos : 0 < (star x ⬝ᵥ x) := (dotProduct_star_self_pos_iff (v := x)).2 hx + simpa using hpos + +/-- The identity matrix is positive semidefinite. -/ +lemma posSemidef_one {m : Type*} [Fintype m] [DecidableEq m] : + (1 : Matrix m m ℂ).PosSemidef := + posDef_one.posSemidef + +/-- The block matrix [[A, I], [I, A⁻¹]] is positive semidefinite for positive definite A. -/ +lemma fromBlocks_inv_posSemidef {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosDef) : + (Matrix.fromBlocks A 1 1 A⁻¹).PosSemidef := by + classical + let _ := hA.isUnit.invertible + have hSchur : + (A⁻¹ - (1 : Matrix m m ℂ)ᴴ * A⁻¹ * (1 : Matrix m m ℂ)).PosSemidef := by + simpa using (Matrix.PosSemidef.zero : (0 : Matrix m m ℂ).PosSemidef) + simpa using (Matrix.PosDef.fromBlocks₁₁ (B := (1 : Matrix m m ℂ)) (D := A⁻¹) hA).2 hSchur + +/-- The product `(fromRows A B)ᴴ * (fromRows A B)` equals `Aᴴ * A + Bᴴ * B`. -/ +lemma fromRows_conjTranspose_mul_self + {m₁ m₂ n : Type*} [Fintype m₁] [Fintype m₂] [Fintype n] + (A : Matrix m₁ n ℂ) (B : Matrix m₂ n ℂ) : + (Matrix.fromRows A B)ᴴ * Matrix.fromRows A B = Aᴴ * A + Bᴴ * B := by + classical + -- Expand with block column/row identities. + simp [Matrix.conjTranspose_fromRows_eq_fromCols_conjTranspose, Matrix.fromCols_mul_fromRows] + +namespace PosSemidef + +/-- Diagonal matrix with nonnegative real entries is positive semidefinite. -/ +lemma diagonal_ofReal {m : Type*} [Fintype m] [DecidableEq m] + {f : m → ℝ} (hf : ∀ i, 0 ≤ f i) : + (diagonal (fun i => (f i : ℂ))).PosSemidef := by + rw [posSemidef_diagonal_iff] + intro i + simp only [Complex.zero_le_real] + exact hf i + +/-- If `AᴴA + BᴴB ≤ I`, then the defect `I - VᴴV` is positive semidefinite for `V = fromRows A B`. -/ +lemma one_sub_fromRows {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) (hAB : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ)) : + ((1 : Matrix m m ℂ) - (Matrix.fromRows A B)ᴴ * Matrix.fromRows A B).PosSemidef := by + have hV : (Matrix.fromRows A B)ᴴ * Matrix.fromRows A B ≤ (1 : Matrix m m ℂ) := by + simpa [fromRows_conjTranspose_mul_self] using hAB + simpa [Matrix.le_iff] using hV + +/-- Scaling a PSD matrix by a nonpositive real scalar gives a matrix `≤ 0`. -/ +lemma smul_nonpos {m : Type*} [Fintype m] + {c : ℝ} (hc : c ≤ 0) {M : Matrix m m ℂ} (hM : M.PosSemidef) : + c • M ≤ (0 : Matrix m m ℂ) := by + have hnonneg : 0 ≤ -c := by linarith + have hsmul : ((-c) • M).PosSemidef := hM.smul hnonneg + rw [Matrix.le_iff] + simpa [sub_eq_add_neg, add_comm, add_left_comm, add_assoc] using hsmul + +/-- Adding a positive scalar multiple of the identity to a PSD matrix gives a PD matrix. -/ +lemma add_smul_one_posDef {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) {r : ℝ} (hr : 0 < r) : + (A + (r : ℂ) • (1 : Matrix m m ℂ)).PosDef := by + classical + have h1 : ((r : ℂ) • (1 : Matrix m m ℂ)).IsHermitian := by + change ((r : ℂ) • (1 : Matrix m m ℂ))ᴴ = (r : ℂ) • (1 : Matrix m m ℂ) + ext i j + by_cases h : i = j + · subst h + simp + · have h1 : (1 : Matrix m m ℂ) i j = 0 := by + simp [h] + have hji : ¬ j = i := by + simpa [eq_comm] using h + have h2 : (1 : Matrix m m ℂ) j i = 0 := by + simp [hji] + simp [Matrix.conjTranspose_apply, h1, h2] + refine Matrix.PosDef.of_dotProduct_mulVec_pos ?_ ?_ + · exact hA.1.add h1 + · intro x hx + have hA_re : 0 ≤ (star x ⬝ᵥ (A *ᵥ x)).re := hA.re_dotProduct_nonneg x + have hxx_pos : 0 < (star x ⬝ᵥ x).re := by + have hpos : 0 < (star x ⬝ᵥ x) := (dotProduct_star_self_pos_iff (v := x)).2 hx + exact (RCLike.pos_iff.mp hpos).1 + have hsum_re : + (star x ⬝ᵥ ((A + (r : ℂ) • (1 : Matrix m m ℂ)) *ᵥ x)).re = + (star x ⬝ᵥ (A *ᵥ x)).re + r * (star x ⬝ᵥ x).re := by + simp [add_mulVec, smul_mulVec, dotProduct_add, dotProduct_smul, + Complex.add_re, Complex.real_smul] + have hsum_im : + (star x ⬝ᵥ ((A + (r : ℂ) • (1 : Matrix m m ℂ)) *ᵥ x)).im = 0 := by + set M := A + (r : ℂ) • (1 : Matrix m m ℂ) + have hM : M.IsHermitian := hA.1.add h1 + have hconj : star (star x ⬝ᵥ M *ᵥ x) = star x ⬝ᵥ M *ᵥ x := by + simp only [dotProduct, mulVec, star_sum, star_mul'] + simp_rw [Finset.mul_sum] + rw [Finset.sum_comm] + apply Finset.sum_congr rfl; intro j _ + apply Finset.sum_congr rfl; intro i _ + have hMij : star (M i j) = M j i := by + have h := congrFun (congrFun hM j) i + simp only [conjTranspose_apply] at h + exact h + simp_rw [hMij, Pi.star_apply, star_star]; ring + have him : -(star x ⬝ᵥ M *ᵥ x).im = (star x ⬝ᵥ M *ᵥ x).im := by + have := congrArg Complex.im hconj + simp only [Complex.star_def, Complex.conj_im] at this + exact this + linarith + have hpos_r : 0 < r * (star x ⬝ᵥ x).re := mul_pos hr hxx_pos + have hsum_pos : 0 < (star x ⬝ᵥ ((A + (r : ℂ) • (1 : Matrix m m ℂ)) *ᵥ x)).re := by + have hpos' : 0 < (star x ⬝ᵥ (A *ᵥ x)).re + r * (star x ⬝ᵥ x).re := + add_pos_of_nonneg_of_pos hA_re hpos_r + rw [hsum_re] + exact hpos' + exact (RCLike.pos_iff).2 ⟨hsum_pos, hsum_im⟩ + +end PosSemidef + +/-- Block diagonal `fromBlocks A 0 0 D` is PSD when both `A` and `D` are PSD. -/ +lemma fromBlocks_diag_posSemidef {n₁ n₂ : Type*} + [Fintype n₁] [Fintype n₂] + {A : Matrix n₁ n₁ ℂ} (hA : A.PosSemidef) + {D : Matrix n₂ n₂ ℂ} (hD : D.PosSemidef) : + (Matrix.fromBlocks A 0 0 D).PosSemidef := by + refine PosSemidef.of_dotProduct_mulVec_nonneg + (Matrix.IsHermitian.fromBlocks hA.1 (by simp) hD.1) ?_ + intro v + have heq : star v ⬝ᵥ (Matrix.fromBlocks A 0 0 D *ᵥ v) = + star (fun i => v (Sum.inl i)) ⬝ᵥ (A *ᵥ fun i => v (Sum.inl i)) + + star (fun i => v (Sum.inr i)) ⬝ᵥ (D *ᵥ fun i => v (Sum.inr i)) := by + simp [dotProduct, Fintype.sum_sum_type, fromBlocks_mulVec_inl, fromBlocks_mulVec_inr] + rw [heq] + exact add_nonneg (hA.dotProduct_mulVec_nonneg _) (hD.dotProduct_mulVec_nonneg _) + +/-- Trace of a `fromBlocks` matrix decomposes as sum of diagonal block traces. -/ +lemma trace_fromBlocks {n₁ n₂ : Type*} [Fintype n₁] [Fintype n₂] + (A : Matrix n₁ n₁ ℂ) (B : Matrix n₁ n₂ ℂ) (C : Matrix n₂ n₁ ℂ) (D : Matrix n₂ n₂ ℂ) : + (Matrix.fromBlocks A B C D).trace = A.trace + D.trace := by + unfold Matrix.trace + rw [Fintype.sum_sum_type] + simp + +end Matrix diff --git a/QuantumSystem/ForMathlib/Analysis/Matrix/Hermitian.lean b/QuantumSystem/ForMathlib/Analysis/Matrix/Hermitian.lean new file mode 100644 index 0000000..d8c3c37 --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/Matrix/Hermitian.lean @@ -0,0 +1,88 @@ +module + +public import Mathlib.Analysis.CStarAlgebra.Classes +public import Mathlib.LinearAlgebra.Matrix.Hermitian + +/-! +# Hermitian Matrices + +This file collects basic lemmas about Hermitian matrices over `ℂ`. + +## Main results + +- `IsHermitian.quadForm_im_eq_zero`: the quadratic form v†Av is real for a Hermitian + matrix A. +- `IsHermitian.add_isHermitian`: the sum of two Hermitian matrices is Hermitian. +- `IsHermitian.smul_real`: a real scalar multiple of a Hermitian matrix is Hermitian. +- `IsHermitian.convex_combination`: a convex combination of Hermitian matrices is Hermitian. +- `IsHermitian.diagonal_real`: a diagonal matrix with real entries is Hermitian. +- `IsHermitian.smul_complex_real`: multiplication by a real scalar (viewed in `ℂ`) preserves + Hermiticity. +-/ +@[expose] public section + +namespace Matrix + +/-- For a Hermitian matrix A, the quadratic form v†Av is real. -/ +lemma IsHermitian.quadForm_im_eq_zero {m : Type*} [Fintype m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (v : m → ℂ) : + (star v ⬝ᵥ A *ᵥ v).im = 0 := by + have h : star (star v ⬝ᵥ A *ᵥ v) = star v ⬝ᵥ A *ᵥ v := by + simp only [dotProduct, mulVec, star_sum, star_mul'] + simp_rw [Finset.mul_sum] + rw [Finset.sum_comm] + apply Finset.sum_congr rfl + intro j _ + apply Finset.sum_congr rfl + intro i _ + have hAij : star (A i j) = A j i := by + have h := congrFun (congrFun hA j) i + simp only [conjTranspose_apply] at h + exact h + simp_rw [hAij, Pi.star_apply, star_star] + ring + have him : -(star v ⬝ᵥ A *ᵥ v).im = (star v ⬝ᵥ A *ᵥ v).im := by + have := congrArg Complex.im h + simp only [Complex.star_def, Complex.conj_im] at this + exact this + linarith + +/-- Sum of Hermitian matrices is Hermitian. -/ +lemma IsHermitian.add_isHermitian {m : Type*} [Fintype m] + {A B : Matrix m m ℂ} (hA : A.IsHermitian) (hB : B.IsHermitian) : + (A + B).IsHermitian := + hA.add hB + +/-- Real scalar multiple of a Hermitian matrix is Hermitian. -/ +lemma IsHermitian.smul_real {m : Type*} [Fintype m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (r : ℝ) : + (r • A).IsHermitian := by + unfold IsHermitian at * + rw [conjTranspose_smul, hA] + simp only [RCLike.star_def, RCLike.conj_to_real] + +/-- Convex combination of Hermitian matrices is Hermitian. -/ +lemma IsHermitian.convex_combination {m : Type*} [Fintype m] + {A B : Matrix m m ℂ} (hA : A.IsHermitian) (hB : B.IsHermitian) (t : ℝ) : + (t • A + (1 - t) • B).IsHermitian := + (hA.smul_real t).add (hB.smul_real (1 - t)) + +/-- Diagonal matrix with real entries is Hermitian. -/ +lemma IsHermitian.diagonal_real {m : Type*} [Fintype m] [DecidableEq m] + (f : m → ℝ) : (diagonal (fun i => (f i : ℂ))).IsHermitian := by + rw [IsHermitian, diagonal_conjTranspose] + ext i j + simp only [diagonal_apply] + split_ifs with h + · simp [RCLike.star_def, Complex.conj_ofReal] + · rfl + +/-- Complex scalar multiple of a Hermitian matrix is Hermitian when the scalar is real. -/ +lemma IsHermitian.smul_complex_real {m : Type*} [Fintype m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (r : ℝ) : + ((r : ℂ) • A).IsHermitian := by + unfold IsHermitian at * + rw [conjTranspose_smul, hA] + simp only [RCLike.star_def, Complex.conj_ofReal] + +end Matrix diff --git a/QuantumSystem/ForMathlib/Analysis/Matrix/Order.lean b/QuantumSystem/ForMathlib/Analysis/Matrix/Order.lean new file mode 100644 index 0000000..24bb2f4 --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/Matrix/Order.lean @@ -0,0 +1,73 @@ +module + +public import Mathlib.Analysis.Matrix.Order + +/-! +# Löwner Order on Matrices + +This file defines the Löwner (positive semidefinite) order on Hermitian matrices over `ℂ` +and proves its basic properties. + +## Main results + +- `loewnerLE` (`A ≤ₗ B`): `B - A` is positive semidefinite. +- `loewnerLE_refl`: the Löwner order is reflexive. +- `loewnerLE_trans`: the Löwner order is transitive. +- `compression_le`: M ≤ N ⇒ V†MV ≤ V†NV. +- `trace_mono`: A ≤ B ⇒ Re(tr A) ≤ Re(tr B). +-/ +@[expose] public section + +namespace Matrix + +open scoped MatrixOrder ComplexOrder + +/-- Löwner order on Hermitian matrices: A ≤_L B iff B - A is positive semidefinite. -/ +def loewnerLE {m : Type*} [Fintype m] [DecidableEq m] + (A B : Matrix m m ℂ) : Prop := + (B - A).PosSemidef + +notation:50 A " ≤ₗ " B => loewnerLE A B + +/-- Löwner order is reflexive. -/ +lemma loewnerLE_refl {m : Type*} [Fintype m] [DecidableEq m] + (A : Matrix m m ℂ) : A ≤ₗ A := by + unfold loewnerLE + simp only [sub_self] + exact PosSemidef.zero + +/-- Löwner order is transitive. -/ +lemma loewnerLE_trans {m : Type*} [Fintype m] [DecidableEq m] + {A B C : Matrix m m ℂ} (hab : A ≤ₗ B) (hbc : B ≤ₗ C) : A ≤ₗ C := by + unfold loewnerLE at * + have h : C - A = (C - B) + (B - A) := by abel + rw [h] + exact hbc.add hab + +/-- Compression preserves the Löwner order: M ≤ N ⇒ V†MV ≤ V†NV. -/ +lemma compression_le {n m : Type*} [Fintype n] [Fintype m] + {M N : Matrix n n ℂ} (h : M ≤ N) (V : Matrix n m ℂ) : + Vᴴ * M * V ≤ Vᴴ * N * V := by + rw [Matrix.le_iff] at h ⊢ + have hdiff : Vᴴ * N * V - Vᴴ * M * V = Vᴴ * (N - M) * V := by + simp [Matrix.mul_sub, Matrix.sub_mul] + rw [hdiff] + exact h.conjTranspose_mul_mul_same V + +/-- Trace is monotone with respect to the Löwner order: +A ≤ B ⇒ Re(tr A) ≤ Re(tr B). -/ +lemma trace_mono {m : Type*} [Fintype m] + {A B : Matrix m m ℂ} (hle : A ≤ B) : A.trace.re ≤ B.trace.re := by + have hpsd : (B - A).PosSemidef := Matrix.le_iff.mp hle + have h_trace_nonneg : 0 ≤ (B - A).trace := hpsd.trace_nonneg + have h : B.trace - A.trace = (B - A).trace := (trace_sub B A).symm + have h' : (B.trace - A.trace).re = B.trace.re - A.trace.re := + Complex.sub_re B.trace A.trace + have h_re_nonneg : 0 ≤ (B - A).trace.re := by + have := Complex.nonneg_iff.mp h_trace_nonneg + exact this.1 + have h_eq : (B - A).trace.re = B.trace.re - A.trace.re := by + rw [← h, h'] + linarith [h_eq ▸ h_re_nonneg] + +end Matrix diff --git a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Abs.lean b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Abs.lean new file mode 100644 index 0000000..61116bc --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Abs.lean @@ -0,0 +1,204 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.StarOrder +public import Mathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.Abs + +open scoped InnerProductSpace +open ContinuousLinearMap + +@[expose] public section + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-! ### Absolute value of an operator -/ + +section AbsoluteValue + +/-- `T†T` is always a non-negative operator (in the Loewner order). -/ +lemma adjoint_mul_self_nonneg (T : H →L[ℂ] H) : 0 ≤ T.adjoint * T := by + rw [nonneg_iff_isPositive] + exact isPositive_adjoint_comp_self T + +/-- The absolute value of a bounded linear operator `T`, defined as `√(T†T)`. +This uses the continuous functional calculus for positive operators. +This equals `CFC.abs T` since `star T = T.adjoint` for ContinuousLinearMap. -/ +noncomputable def absoluteValue (T : H →L[ℂ] H) : H →L[ℂ] H := + CFC.sqrt (T.adjoint * T) + +lemma absoluteValue_eq_cfc_abs (T : H →L[ℂ] H) : absoluteValue T = CFC.abs T := by + simp only [absoluteValue, CFC.abs, ContinuousLinearMap.star_eq_adjoint] + +/-- The absolute value of an operator is non-negative. -/ +lemma absoluteValue_nonneg (T : H →L[ℂ] H) : 0 ≤ absoluteValue T := by + rw [absoluteValue_eq_cfc_abs] + exact CFC.abs_nonneg T + +/-- The absolute value of an operator is self-adjoint. -/ +lemma absoluteValue_isSelfAdjoint (T : H →L[ℂ] H) : IsSelfAdjoint (absoluteValue T) := by + have h := absoluteValue_nonneg T + rw [nonneg_iff_isPositive] at h + exact h.isSelfAdjoint + +/-- The absolute value of an operator is positive (in the sense of IsPositive). -/ +lemma absoluteValue_isPositive (T : H →L[ℂ] H) : (absoluteValue T).IsPositive := by + rw [← nonneg_iff_isPositive] + exact absoluteValue_nonneg T + +/-- `|T|² = T†T` -/ +lemma absoluteValue_sq (T : H →L[ℂ] H) : absoluteValue T * absoluteValue T = T.adjoint * T := by + unfold absoluteValue + exact CFC.sqrt_mul_sqrt_self _ (adjoint_mul_self_nonneg T) + +/-- The absolute value of the zero operator is zero. -/ +lemma absoluteValue_zero : absoluteValue (0 : H →L[ℂ] H) = 0 := by + unfold absoluteValue + have h : (0 : H →L[ℂ] H).adjoint = 0 := by ext; simp + simp only [h, zero_mul] + exact CFC.sqrt_zero + +/-- For a positive operator T, the absolute value equals T itself. -/ +lemma absoluteValue_of_nonneg {T : H →L[ℂ] H} (hT : 0 ≤ T) : absoluteValue T = T := by + unfold absoluteValue + have hpos : T.IsPositive := by rwa [← nonneg_iff_isPositive] + have hsa : T.adjoint = T := hpos.isSelfAdjoint.adjoint_eq + rw [hsa, ← sq] + exact CFC.sqrt_sq T hT + +lemma absoluteValue_smul (c : ℂ) (T : H →L[ℂ] H) : + absoluteValue (c • T) = ‖c‖ • absoluteValue T := by + rw [absoluteValue_eq_cfc_abs, absoluteValue_eq_cfc_abs] + exact CFC.abs_smul c T + +lemma norm_absoluteValue_eq_norm (T : H →L[ℂ] H) (x : H) : ‖absoluteValue T x‖ = ‖T x‖ := by + have hP := absoluteValue_isSelfAdjoint T + let P := absoluteValue T + have h_inner : ‖P x‖^2 = ‖T x‖^2 := by + simp only [← inner_self_eq_norm_sq (𝕜 := ℂ) _] + change (⟪P x, P x⟫_ℂ).re = (⟪T x, T x⟫_ℂ).re + calc (⟪P x, P x⟫_ℂ).re + = (⟪P.adjoint (P x), x⟫_ℂ).re := by rw [adjoint_inner_left] + _ = (⟪P (P x), x⟫_ℂ).re := by rw [hP.adjoint_eq] + _ = (⟪(P * P) x, x⟫_ℂ).re := rfl + _ = (⟪(T.adjoint * T) x, x⟫_ℂ).re := by rw [absoluteValue_sq] + _ = (⟪T.adjoint (T x), x⟫_ℂ).re := rfl + _ = (⟪T x, T x⟫_ℂ).re := by rw [adjoint_inner_left] + exact (sq_eq_sq₀ (norm_nonneg _) (norm_nonneg _)).mp h_inner + +lemma absoluteValue_ker_eq_ker (T : H →L[ℂ] H) : + LinearMap.ker (absoluteValue T).toLinearMap = LinearMap.ker T.toLinearMap := by + ext x + simp only [LinearMap.mem_ker, ContinuousLinearMap.coe_coe] + constructor + · intro h + rw [← norm_eq_zero, ← norm_absoluteValue_eq_norm, h, norm_zero] + · intro h + rw [← norm_eq_zero, norm_absoluteValue_eq_norm, h, norm_zero] + +lemma absoluteValue_adjoint_sq (T : H →L[ℂ] H) : + absoluteValue T.adjoint * absoluteValue T.adjoint = T * T.adjoint := by + simpa [adjoint_adjoint] using (absoluteValue_sq (T := T.adjoint)) + +/-! ### Polar decomposition identities -/ + +/-- (U|T|U†)² = T·T† when T = U|T| and U†U|T| = |T| -/ +lemma conjugate_abs_sq_eq {T U : H →L[ℂ] H} + (hT_polar : T = U * absoluteValue T) + (hU_adj_U_abs : U.adjoint * U * absoluteValue T = absoluteValue T) : + (U * absoluteValue T * U.adjoint) * (U * absoluteValue T * U.adjoint) = T * T.adjoint := by + have h2 : T.adjoint = absoluteValue T * U.adjoint := by + have h_adj : (U * absoluteValue T).adjoint = (absoluteValue T).adjoint * U.adjoint := by + change star (U * absoluteValue T) = star (absoluteValue T) * star U + rw [star_mul] + conv_lhs => rw [hT_polar] + rw [h_adj, (absoluteValue_isSelfAdjoint T).adjoint_eq] + set absT := absoluteValue T with h_absT_def + -- (U|T|U†)² = U|T|(U†U)|T|U† = U|T|²U† + have lhs_eq : (U * absT * U.adjoint) * (U * absT * U.adjoint) + = U * absT * absT * U.adjoint := by + have hU_adj_U_absT : U.adjoint * U * absT = absT := hU_adj_U_abs + calc (U * absT * U.adjoint) * (U * absT * U.adjoint) + = U * absT * (U.adjoint * U * absT) * U.adjoint := by simp only [mul_assoc] + _ = U * absT * absT * U.adjoint := by rw [hU_adj_U_absT] + -- T·T† = (U|T|)(|T|U†) = U|T|²U† + have h2' : T.adjoint = absT * U.adjoint := h2 + have rhs_eq : T * T.adjoint = U * absT * absT * U.adjoint := by + calc T * T.adjoint + = T * (absT * U.adjoint) := by rw [h2'] + _ = (U * absT) * (absT * U.adjoint) := by rw [hT_polar] + _ = U * absT * absT * U.adjoint := by simp only [mul_assoc] + rw [lhs_eq, rhs_eq] + +/-- U|T|U† is a positive operator when |T| is the absolute value -/ +lemma conjugate_abs_nonneg {T U : H →L[ℂ] H} : + 0 ≤ U * absoluteValue T * U.adjoint := by + set absT := absoluteValue T with h_absT_def + rw [ContinuousLinearMap.le_def] + constructor + · -- Symmetry: (U|T|U†)† = U|T|†U†† = U|T|U† + simp only [sub_zero] + change ((U * absT * U.adjoint) : H →L[ℂ] H).toLinearMap.IsSymmetric + have h_self_adj : IsSelfAdjoint (U * absT * U.adjoint) := by + rw [isSelfAdjoint_iff'] + -- Need: (U * absT * U†)† = U * absT * U† + have step1 : (U * absT * U.adjoint).adjoint = U.adjoint.adjoint * (U * absT).adjoint := by + change star _ = star (adjoint U) * star (U * absT) + rw [star_mul] + have step2 : (U * absT).adjoint = absT.adjoint * U.adjoint := by + change star _ = star absT * star U + rw [star_mul] + rw [step1, step2, ContinuousLinearMap.adjoint_adjoint] + rw [h_absT_def, (absoluteValue_isSelfAdjoint T).adjoint_eq, ← h_absT_def] + simp only [mul_assoc] + exact h_self_adj.isSymmetric + · intro x + simp only [ContinuousLinearMap.reApplyInnerSelf, sub_zero, mul_apply] + -- Goal: 0 ≤ re⟨U(|T|(U†x)), x⟩ = re⟨|T|(U†x), U†x⟩ + have eq : ⟪U (absT (U.adjoint x)), x⟫_ℂ = ⟪absT (U.adjoint x), U.adjoint x⟫_ℂ := by + rw [← adjoint_inner_left U.adjoint, ContinuousLinearMap.adjoint_adjoint] + rw [eq, h_absT_def] + have h_abs_pos := absoluteValue_isPositive T + rw [ContinuousLinearMap.isPositive_def] at h_abs_pos + have h := h_abs_pos.2 (U.adjoint x) + simp only [ContinuousLinearMap.reApplyInnerSelf_apply] at h + exact h + +/-- For polar decomposition T = U|T| with U partial isometry, |T†| = U|T|U† -/ +lemma absoluteValue_adjoint_eq_conjugate_by_partial_isometry {T U : H →L[ℂ] H} + (hT_polar : T = U * absoluteValue T) + (hU_adj_U_abs : U.adjoint * U * absoluteValue T = absoluteValue T) : + absoluteValue T.adjoint = U * absoluteValue T * U.adjoint := by + set absT := absoluteValue T with h_absT_def + have h_sq : (U * absT * U.adjoint) * (U * absT * U.adjoint) = T * T.adjoint := + conjugate_abs_sq_eq hT_polar hU_adj_U_abs + have h_pos : 0 ≤ U * absT * U.adjoint := conjugate_abs_nonneg + have h_absT_adj_sq : absoluteValue T.adjoint * absoluteValue T.adjoint = T * T.adjoint := + absoluteValue_adjoint_sq T + have h_both_positive : 0 ≤ absoluteValue T.adjoint := absoluteValue_nonneg T.adjoint + -- Both U|T|U† and |T†| are positive with same square, so equal by sqrt uniqueness + calc absoluteValue T.adjoint + = CFC.sqrt (absoluteValue T.adjoint * absoluteValue T.adjoint) := + (CFC.sqrt_sq (absoluteValue T.adjoint) h_both_positive).symm + _ = CFC.sqrt (T * T.adjoint) := by rw [h_absT_adj_sq] + _ = CFC.sqrt (U * absT * U.adjoint * (U * absT * U.adjoint)) := by + rw [← h_sq] + _ = U * absT * U.adjoint := + CFC.sqrt_sq (U * absT * U.adjoint) h_pos + +/-- S x = 0 when x ∈ ker |T| and S = √|T| -/ +lemma cfc_sqrt_absoluteValue_ker {T : H →L[ℂ] H} + (x : H) (hx : absoluteValue T x = 0) : + CFC.sqrt (absoluteValue T) x = 0 := by + let S := CFC.sqrt (absoluteValue T) + have hS_sq : S * S = absoluteValue T := CFC.sqrt_mul_sqrt_self _ (absoluteValue_nonneg T) + have hS_sa : IsSelfAdjoint S := (CFC.sqrt_nonneg _).isSelfAdjoint + have h_norm_sq : ‖S x‖^2 = (⟪absoluteValue T x, x⟫_ℂ).re := by + have h1 : ‖S x‖^2 = Complex.re ⟪S x, S x⟫_ℂ := by + rw [(inner_self_eq_norm_sq (𝕜 := ℂ) (S x)).symm]; simp + calc ‖S x‖^2 = Complex.re ⟪S x, S x⟫_ℂ := h1 + _ = Complex.re ⟪S (S x), x⟫_ℂ := by rw [← adjoint_inner_left, hS_sa.adjoint_eq] + _ = Complex.re ⟪(S * S) x, x⟫_ℂ := by rfl + _ = Complex.re ⟪absoluteValue T x, x⟫_ℂ := by rw [hS_sq] + rw [hx, inner_zero_left, Complex.zero_re] at h_norm_sq + exact norm_eq_zero.mp (eq_zero_of_pow_eq_zero h_norm_sq) + +end AbsoluteValue diff --git a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PartialIsometry.lean b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PartialIsometry.lean new file mode 100644 index 0000000..5db657a --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PartialIsometry.lean @@ -0,0 +1,341 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.Adjoint + +@[expose] public section + +open ContinuousLinearMap InnerProductSpace + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-- A continuous linear map `U` is a partial isometry if `U†U` is a projection. -/ +def IsPartialIsometry (U : H →L[ℂ] H) : Prop := + U.adjoint * U * (U.adjoint * U) = U.adjoint * U + +/-- Alternative characterization: U is a partial isometry iff UU†U = U. -/ +lemma isPartialIsometry_iff (U : H →L[ℂ] H) : + IsPartialIsometry U ↔ U * U.adjoint * U = U := by + constructor + · intro h + -- U†U is a projection P. + -- We want to show U P = U. + -- Is equivalent to U (1 - P) = 0. + -- || U (1 - P) x ||² = ⟨ (1-P)x, U† U (1-P) x ⟩ = ⟨ (1-P)x, P (1-P) x ⟩ = 0 + let P := U.adjoint * U + have hP : P * P = P := h + have h_orth : U * (1 - P) = 0 := by + ext x + refine norm_eq_zero.mp ?_ + have h0 : ‖(U * (1 - P)) x‖ ^ 2 = 0 := by + rw [← inner_self_eq_norm_sq (𝕜 := ℂ) (x := (U * (1 - P)) x)] + change Complex.re ⟪U ((1 - P) x), U ((1 - P) x)⟫_ℂ = 0 + rw [← adjoint_inner_right] + -- rewrite `U† (U ((1 - P) x))` as `P ((1 - P) x)` + simp only [P] + -- U† U (x - P x) = P (x - P x) = P x - P² x = P x - P x = 0 + have hPP : P (P x) = P x := by + simpa [P, ContinuousLinearMap.mul_apply, mul_assoc] using + congrArg (fun T => T x) hP + have h1 : (1 - P) x = x - P x := by + simp [sub_eq_add_neg] + have h2 : P ((1 - P) x) = P x - P (P x) := by + rw [h1, map_sub] + have h3 : U.adjoint (U (x - P x)) = P (x - P x) := by rfl + rw [h1, h3, map_sub, hPP, sub_self, inner_zero_right, Complex.zero_re] + exact (sq_eq_zero_iff.mp h0) + have h_orth' : U * (P - 1) = 0 := by + calc + U * (P - 1) = U * (-(1 - P)) := by + simp [sub_eq_add_neg, add_comm] + _ = - (U * (1 - P)) := by + simp [sub_eq_add_neg, mul_add, mul_one, add_comm] + _ = 0 := by simp [h_orth] + -- `U * (P - 1) = 0` rewrites to `U * U† * U - U = 0` + have h_orth'' : U * U.adjoint * U - U = 0 := by + simpa [P, mul_sub, mul_assoc, mul_one] using h_orth' + have h_eq : U * U.adjoint * U = U := by + calc + U * U.adjoint * U = (U * U.adjoint * U - U) + U := by abel + _ = 0 + U := by simp [h_orth''] + _ = U := by simp + exact h_eq + · intro h + -- We want IsPartialIsometry U, i.e., U† U U† U = U† U + have h_adj : U.adjoint * U * U.adjoint = U.adjoint := by + -- take adjoint of `U * U† * U = U` + simpa [mul_def, adjoint_comp, adjoint_adjoint, mul_assoc] using + congrArg ContinuousLinearMap.adjoint h + calc + U.adjoint * U * (U.adjoint * U) + = (U.adjoint * U * U.adjoint) * U := by simp [mul_assoc] + _ = U.adjoint * U := by simp [h_adj] + +/-- The adjoint of a partial isometry is a partial isometry. -/ +lemma IsPartialIsometry.adjoint {U : H →L[ℂ] H} (hU : IsPartialIsometry U) : + IsPartialIsometry U.adjoint := by + rw [isPartialIsometry_iff] at hU ⊢ + -- We want U† U†† U† = U†. + -- i.e., U† U U† = U†. + rw [adjoint_adjoint] + -- We know U U† U = U. Taking adjoint gives U† U U† = U†. + -- take adjoint of `U * U† * U = U` + simpa [mul_def, adjoint_comp, adjoint_adjoint, mul_assoc] using + congrArg ContinuousLinearMap.adjoint hU + +/-- A partial isometry is an isometry on the orthogonal complement of its kernel. + Note: (ker U)ᗮ = range U†. -/ +lemma IsPartialIsometry.norm_of_mem_initialSpace {U : H →L[ℂ] H} + (hU : IsPartialIsometry U) (x : H) (hx : x ∈ (LinearMap.ker U.toLinearMap)ᗮ) : + ‖U x‖ = ‖x‖ := by + -- ‖U x‖² = ⟨x, U† U x⟩. + -- If x ∈ (ker U)ᗮ, then x ∈ range U† (since range U† is closed? In finite dim yes. + -- In general, (ker U)ᗮ = closure (range U†). + -- Wait, U† U is a projection onto range U†. + -- Let P = U† U. If hU, P is a projection. + -- range P = range U† (standard fact for P = T* T? No, range P ⊆ range U†. Also range U† U ⊆ range U†. + -- Actually range U† = range (U† U) because range U† U ⊆ range U†. + -- And if y = U† z, U† U y = U† U U† z = U† z = y using U U† U = U. + -- So range U† ⊆ range P. Thus range P = range U†. + -- P is an orthogonal projection onto range U†. + -- So for x ∈ range U†, P x = x. + -- ‖U x‖² = ⟨x, P x⟩ = ⟨x, x⟩ = ‖x‖². + + -- But argument x is in (ker U)ᗮ. + -- We need to know P is the projection onto (ker U)ᗮ. + -- ker P = ker (U† U) = ker U. + -- So range P = (ker P)ᗮ = (ker U)ᗮ. + -- So x ∈ range P. + let P := U.adjoint * U + have hP_proj : P * P = P := hU + -- We want to show ⟨x, P x⟩ = ⟨x, x⟩ + have h_P_mem : P x = x := by + -- P is orthogonal projection onto range P. + -- range P = (ker P)ᗮ = (ker U)ᗮ. + -- Wait, P is self-adjoint idempotent, so it is orthogonal projection onto range P. + -- range P = (ker P)ᗮ because P is self-adjoint. + -- ker P = ker U? + -- ker U ⊆ ker P: U x = 0 → U† U x = 0. + -- ker P ⊆ ker U: U† U x = 0 → ⟨x, U† U x⟩ = 0 → ‖U x‖² = 0 → U x = 0. + -- So ker P = ker U. + -- Therefore range P = (ker U)ᗮ. + -- Since x ∈ (ker U)ᗮ, x ∈ range P. + -- So P x = x. + + -- Let's prove ker P = ker U formally. + have h_ker : LinearMap.ker P.toLinearMap = LinearMap.ker U.toLinearMap := by + ext y + simp only [LinearMap.mem_ker] -- `P.toLinearMap y` is definitionaly `P y` + constructor + · intro hy + -- U† U y = 0 ⇒ U y = 0 + have h0 : ‖U y‖ ^ 2 = 0 := by + rw [← inner_self_eq_norm_sq (𝕜 := ℂ) (x := U y)] + rw [← adjoint_inner_right] + -- `U† (U y) = P y = 0` + have hy' : U.adjoint (U y) = 0 := by + simpa [P, ContinuousLinearMap.mul_apply] using hy + simp [hy', inner_zero_right] + have h1 : ‖U y‖ = 0 := (sq_eq_zero_iff.mp h0) + exact (norm_eq_zero.mp h1) + · intro hy + dsimp [P] + calc + (U.adjoint) (U y) = U.adjoint 0 := by + have hy' : U y = 0 := by simpa using hy + simp [hy'] + _ = 0 := by simp + -- P is self-adjoint + have h_sa : IsSelfAdjoint P := by + -- `(U†U)† = U†U` + simpa [P, star_eq_adjoint] using (IsSelfAdjoint.star_mul_self U) + -- range P is closed? + -- range P = ker(1-P). Since P is continuous, ker(1-P) is closed. + -- So range P is closed. + -- For orthogonal projection P, range P = (ker P)ᗮ. + -- We need this fact from Mathlib. `LinearMap.isProj_iff_idempotent_and_...`? + -- Actually `OrthogonalProjection` exists. + -- But we defined IsPartialIsometry manually. + + -- Let's rely on P x = x iff x ∈ (ker P)ᗮ. + -- Mathlib has `orthogonalProjection_eq_self_iff`. + -- We can construct the `orthogonalProjection` structure from P. + + -- Or just prove manually: + -- If x ∈ (ker P)ᗮ, then P x - x ∈ ? + -- P(P x - x) = P x - P x = 0. So P x - x ∈ ker P. + -- Also P x - x ∈ range P + x? No. + -- P x ∈ range P = (ker P)ᗮ. + -- So P x - x ∈ (ker P)ᗮ. + -- The only vector in intersection of ker P and (ker P)ᗮ is 0. + -- So P x = x. + + -- Use the idempotent+symmetry characterization to identify the range. + have hP_idem : IsIdempotentElem P := hP_proj + have hP_symm : (P : H →ₗ[ℂ] H).IsSymmetric := + (ContinuousLinearMap.isSelfAdjoint_iff_isSymmetric).1 h_sa + have h_orth : (LinearMap.range P.toLinearMap)ᗮ = LinearMap.ker P.toLinearMap := + (ContinuousLinearMap.IsIdempotentElem.isSymmetric_iff_orthogonal_range hP_idem).1 hP_symm + have h_range_closed : IsClosed (LinearMap.range P.toLinearMap : Set H) := + (IsIdempotentElem.isClosed_range (p := P) hP_idem) + -- Hence (ker P)ᗮ = range P + have h_eq_range : (LinearMap.ker P.toLinearMap)ᗮ = LinearMap.range P.toLinearMap := by + calc + (LinearMap.ker P.toLinearMap)ᗮ = (LinearMap.range P.toLinearMap)ᗮᗮ := by + simp [h_orth] + _ = (LinearMap.range P.toLinearMap).topologicalClosure := by + simpa using (Submodule.orthogonal_orthogonal_eq_closure + (K := LinearMap.range P.toLinearMap)) + _ = LinearMap.range P.toLinearMap := + (IsClosed.submodule_topologicalClosure_eq h_range_closed) + -- rewrite `hx` into membership in range P + have hx' : x ∈ (LinearMap.ker P.toLinearMap)ᗮ := by + simpa [h_ker] using hx + have hx_range : x ∈ LinearMap.range P.toLinearMap := by + simpa [h_eq_range] using hx' + rcases hx_range with ⟨y, rfl⟩ + -- `P (P y) = P y` + simpa [P, mul_assoc, ContinuousLinearMap.mul_apply] using + congrArg (fun T => T y) hP_proj + -- Now compute squared norms using `P x = x`. + have hsq : ‖U x‖ ^ 2 = ‖x‖ ^ 2 := by + rw [← inner_self_eq_norm_sq (𝕜 := ℂ) (x := U x)] + change Complex.re ⟪U x, U x⟫_ℂ = ‖x‖ ^ 2 + rw [← adjoint_inner_right] + have hPx : U.adjoint (U x) = P x := by rfl + rw [hPx, h_P_mem] + simpa using (inner_self_eq_norm_sq (𝕜 := ℂ) (x := x)) + have hnonneg1 : 0 ≤ ‖U x‖ := norm_nonneg _ + have hnonneg2 : 0 ≤ ‖x‖ := norm_nonneg _ + have hsq' : ‖U x‖ = ‖x‖ := by + have h' := sq_eq_sq_iff_eq_or_eq_neg.mp (by simpa [pow_two] using hsq) + cases h' with + | inl h => exact h + | inr h => nlinarith + exact hsq' + +/-- For a partial isometry U, U†U = id on (ker U)ᗮ -/ +lemma IsPartialIsometry.adjoint_mul_self_apply_of_mem_ker_orthogonal {U : H →L[ℂ] H} + (hU : IsPartialIsometry U) + (x : H) (hx : x ∈ (LinearMap.ker U.toLinearMap)ᗮ) : + U.adjoint (U x) = x := by + let P := U.adjoint * U + have hP_proj : P * P = P := hU + have h_sa : IsSelfAdjoint P := IsSelfAdjoint.star_mul_self U + have h_ker_P : LinearMap.ker P.toLinearMap = LinearMap.ker U.toLinearMap := by + ext y + simp only [LinearMap.mem_ker, ContinuousLinearMap.coe_coe] + constructor + · intro hy + have h0 : ‖U y‖ ^ 2 = 0 := by + rw [← inner_self_eq_norm_sq (𝕜 := ℂ), ← adjoint_inner_right] + have : U.adjoint (U y) = P y := rfl + rw [this, hy, inner_zero_right] + rfl + exact norm_eq_zero.mp (sq_eq_zero_iff.mp h0) + · intro hy + change P y = 0 + calc P y = (U.adjoint * U) y := rfl + _ = U.adjoint (U y) := rfl + _ = U.adjoint 0 := by rw [hy] + _ = 0 := map_zero _ + have hP_idem : IsIdempotentElem P := hP_proj + have hP_symm : (P : H →ₗ[ℂ] H).IsSymmetric := + ContinuousLinearMap.isSelfAdjoint_iff_isSymmetric.1 h_sa + have h_orth : (LinearMap.range P.toLinearMap)ᗮ = LinearMap.ker P.toLinearMap := + (ContinuousLinearMap.IsIdempotentElem.isSymmetric_iff_orthogonal_range hP_idem).1 hP_symm + have h_range_closed : IsClosed (LinearMap.range P.toLinearMap : Set H) := + IsIdempotentElem.isClosed_range hP_idem + have h_eq_range : (LinearMap.ker P.toLinearMap)ᗮ = LinearMap.range P.toLinearMap := by + calc (LinearMap.ker P.toLinearMap)ᗮ = (LinearMap.range P.toLinearMap)ᗮᗮ := by simp [h_orth] + _ = (LinearMap.range P.toLinearMap).topologicalClosure := by + simpa using Submodule.orthogonal_orthogonal_eq_closure (K := LinearMap.range P.toLinearMap) + _ = LinearMap.range P.toLinearMap := IsClosed.submodule_topologicalClosure_eq h_range_closed + have hx' : x ∈ (LinearMap.ker P.toLinearMap)ᗮ := by rw [h_ker_P]; exact hx + have hx_range : x ∈ LinearMap.range P.toLinearMap := by rw [h_eq_range] at hx'; exact hx' + rcases hx_range with ⟨y, hy⟩ + calc U.adjoint (U x) = P x := rfl + _ = P (P y) := by + congr 1 + exact hy.symm + _ = P y := by + have := congrArg (· y) hP_proj + simp only [mul_apply] at this + exact this + _ = x := hy + +/-- A partial isometry has operator norm at most 1. + This follows from ‖U x‖ = ‖x‖ on (ker U)ᗮ and ‖U x‖ = 0 on ker U. -/ +lemma IsPartialIsometry.norm_le_one {U : H →L[ℂ] H} (hU : IsPartialIsometry U) : + ‖U‖ ≤ 1 := by + apply ContinuousLinearMap.opNorm_le_bound _ zero_le_one + intro x + simp only [one_mul] + -- Use U†U is a self-adjoint idempotent (projection) + let P := U.adjoint * U + have hP_idem : P * P = P := hU + have hP_sa : IsSelfAdjoint P := IsSelfAdjoint.star_mul_self U + -- ‖U x‖² = ⟨x, U†U x⟩ = ⟨x, P x⟩ + have h_norm_sq : ‖U x‖ ^ 2 = (⟪x, P x⟫_ℂ).re := by + rw [← inner_self_eq_norm_sq (𝕜 := ℂ)] + change Complex.re ⟪U x, U x⟫_ℂ = (⟪x, P x⟫_ℂ).re + rw [← adjoint_inner_right] + rfl + -- For self-adjoint idempotent P: 0 ≤ ⟨x, P x⟩ ≤ ⟨x, x⟩ + -- ⟨x, Px⟩ = ⟨Px, Px⟩ since P² = P and P = P† + have h_Px_eq : (⟪x, P x⟫_ℂ).re = ‖P x‖ ^ 2 := by + have hPsa : P.adjoint = P := hP_sa.adjoint_eq + have hPPx : P (P x) = P x := by + have := congrArg (fun T => T x) hP_idem + simp only [mul_apply] at this + exact this + -- ⟨x, Px⟩ = ⟨x, PPx⟩ = ⟨P†x, Px⟩ = ⟨Px, Px⟩ + calc (⟪x, P x⟫_ℂ).re + _ = (⟪x, P (P x)⟫_ℂ).re := by rw [hPPx] + _ = (⟪P.adjoint x, P x⟫_ℂ).re := by rw [adjoint_inner_left] + _ = (⟪P x, P x⟫_ℂ).re := by rw [hPsa] + _ = ‖P x‖ ^ 2 := by rw [inner_self_eq_norm_sq_to_K]; norm_cast + -- Similarly for 1 - P + have h_1mP : (⟪x, (1 - P) x⟫_ℂ).re = ‖(1 - P) x‖ ^ 2 := by + let Q := 1 - P + have hQ_idem : Q * Q = Q := by + ext y + simp only [Q, mul_apply, sub_apply, one_apply] + have hPPy : P (P y) = P y := by + have := congrArg (fun T => T y) hP_idem + simp only [mul_apply] at this + exact this + simp [hPPy] + have hQ_sa : IsSelfAdjoint Q := by + simp only [Q, IsSelfAdjoint, star_sub, star_one] + rw [hP_sa.star_eq] + have hQsa : Q.adjoint = Q := hQ_sa.adjoint_eq + have hQQx : Q (Q x) = Q x := by + have := congrArg (fun T => T x) hQ_idem + simp only [mul_apply] at this + exact this + calc (⟪x, Q x⟫_ℂ).re + _ = (⟪x, Q (Q x)⟫_ℂ).re := by rw [hQQx] + _ = (⟪Q.adjoint x, Q x⟫_ℂ).re := by rw [adjoint_inner_left] + _ = (⟪Q x, Q x⟫_ℂ).re := by rw [hQsa] + _ = ‖Q x‖ ^ 2 := by rw [inner_self_eq_norm_sq_to_K]; norm_cast + -- ⟨x, x⟩ = ⟨x, P x⟩ + ⟨x, (1-P) x⟩ + have h_decomp : (⟪x, x⟫_ℂ).re = (⟪x, P x⟫_ℂ).re + (⟪x, (1 - P) x⟫_ℂ).re := by + have h1 : ⟪x, x⟫_ℂ = ⟪x, P x⟫_ℂ + ⟪x, (1 - P) x⟫_ℂ := by + rw [← inner_add_right] + congr 1 + simp [sub_apply] + rw [h1, Complex.add_re] + have h_Px_le : (⟪x, P x⟫_ℂ).re ≤ (⟪x, x⟫_ℂ).re := by + rw [h_decomp, h_1mP] + linarith [sq_nonneg ‖(1 - P) x‖] + have h_inner_xx : (⟪x, x⟫_ℂ).re = ‖x‖ ^ 2 := by + rw [inner_self_eq_norm_sq_to_K]; norm_cast + -- ‖U x‖² ≤ ‖x‖² + have h_sq_le : ‖U x‖ ^ 2 ≤ ‖x‖ ^ 2 := by + rw [h_norm_sq] + calc (⟪x, P x⟫_ℂ).re ≤ (⟪x, x⟫_ℂ).re := h_Px_le + _ = ‖x‖ ^ 2 := h_inner_xx + -- ‖U x‖ ≤ ‖x‖ + have h_nonneg_Ux : 0 ≤ ‖U x‖ := norm_nonneg _ + have h_nonneg_x : 0 ≤ ‖x‖ := norm_nonneg _ + nlinarith [sq_nonneg (‖U x‖ - ‖x‖), sq_nonneg (‖U x‖ + ‖x‖)] diff --git a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/Pow/Real.lean b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/Pow/Real.lean new file mode 100644 index 0000000..3eb8918 --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/Pow/Real.lean @@ -0,0 +1,152 @@ +module + +public import Mathlib.Analysis.MeanInequalities + +/-! +# Inequalities for Real Power Functions + +This file proves scalar inequalities about `Real.rpow` used in the proof of +Lieb's joint concavity theorem. + +## Main results + +- `Real.convexCombo_rpow_mul_rpow_le`: joint concavity of (x, y) ↦ xᵖ y¹⁻ᵖ + for 0 < p < 1. +-/ + +@[expose] public section + +namespace Real + +/-- Joint concavity of (x, y) ↦ xᵖ · y¹⁻ᵖ for 0 < p < 1: +the convex combination of values is at most the value at the convex combination. -/ +lemma convexCombo_rpow_mul_rpow_le {p t x₁ x₂ y₁ y₂ : ℝ} + (hp0 : 0 < p) (hp1 : p < 1) + (ht0 : 0 ≤ t) (ht1 : t ≤ 1) + (hx₁ : 0 ≤ x₁) (hx₂ : 0 ≤ x₂) (hy₁ : 0 ≤ y₁) (hy₂ : 0 ≤ y₂) : + t * (x₁ ^ p * y₁ ^ (1 - p)) + (1 - t) * (x₂ ^ p * y₂ ^ (1 - p)) ≤ + (t * x₁ + (1 - t) * x₂) ^ p * (t * y₁ + (1 - t) * y₂) ^ (1 - p) := by + have h1p : 0 < 1 - p := by linarith + have hconj : p + (1 - p) = 1 := by ring + -- Handle boundary cases for t + rcases eq_or_lt_of_le ht0 with rfl | ht0' + · simp + rcases eq_or_lt_of_le ht1 with rfl | ht1' + · simp + have h1t : 0 < 1 - t := by linarith + have htne : t ≠ 0 := ne_of_gt ht0' + have h1tne : 1 - t ≠ 0 := ne_of_gt h1t + -- Handle zero cases + by_cases hsum_x_zero : t * x₁ + (1 - t) * x₂ = 0 + · -- If the x-sum is zero, both t*x₁ and (1-t)*x₂ must be zero + have htx₁ : t * x₁ = 0 := by + have h1 := mul_nonneg (le_of_lt ht0') hx₁ + have h2 := mul_nonneg (le_of_lt h1t) hx₂ + linarith + have hx₁_zero : x₁ = 0 := (mul_eq_zero.mp htx₁).resolve_left htne + have htx₂ : (1 - t) * x₂ = 0 := by linarith + have hx₂_zero : x₂ = 0 := (mul_eq_zero.mp htx₂).resolve_left h1tne + -- LHS = 0 + 0 = 0, RHS = 0^p * (...)^(1-p) ≥ 0 + simp only [hx₁_zero, hx₂_zero, Real.zero_rpow (ne_of_gt hp0), mul_zero, zero_mul, add_zero] + exact le_refl 0 + by_cases hsum_y_zero : t * y₁ + (1 - t) * y₂ = 0 + · -- If the y-sum is zero, both t*y₁ and (1-t)*y₂ must be zero + have hty₁ : t * y₁ = 0 := by + have h1 := mul_nonneg (le_of_lt ht0') hy₁ + have h2 := mul_nonneg (le_of_lt h1t) hy₂ + linarith + have hy₁_zero : y₁ = 0 := (mul_eq_zero.mp hty₁).resolve_left htne + have hty₂ : (1 - t) * y₂ = 0 := by linarith + have hy₂_zero : y₂ = 0 := (mul_eq_zero.mp hty₂).resolve_left h1tne + -- LHS = 0 + 0 = 0, RHS = (...)^p * 0^(1-p) ≥ 0 + simp only [hy₁_zero, hy₂_zero, Real.zero_rpow (ne_of_gt h1p), mul_zero, add_zero] + exact le_refl 0 + -- Main case: both sums are positive + have hsum_x_pos : 0 < t * x₁ + (1 - t) * x₂ := by + have := add_nonneg (mul_nonneg (le_of_lt ht0') hx₁) (mul_nonneg (le_of_lt h1t) hx₂) + exact lt_of_le_of_ne this (Ne.symm hsum_x_zero) + have hsum_y_pos : 0 < t * y₁ + (1 - t) * y₂ := by + have := add_nonneg (mul_nonneg (le_of_lt ht0') hy₁) (mul_nonneg (le_of_lt h1t) hy₂) + exact lt_of_le_of_ne this (Ne.symm hsum_y_zero) + -- Define normalized weights + set X := t * x₁ + (1 - t) * x₂ with hX_def + set Y := t * y₁ + (1 - t) * y₂ with hY_def + set α := (t * x₁) / X with hα_def + set β := (t * y₁) / Y with hβ_def + have hX_ne : X ≠ 0 := ne_of_gt hsum_x_pos + have hY_ne : Y ≠ 0 := ne_of_gt hsum_y_pos + have hα_nonneg : 0 ≤ α := div_nonneg (mul_nonneg (le_of_lt ht0') hx₁) (le_of_lt hsum_x_pos) + have hα_le_one : α ≤ 1 := by + rw [div_le_one (by positivity)] + exact le_add_of_nonneg_right (mul_nonneg (le_of_lt h1t) hx₂) + have hβ_nonneg : 0 ≤ β := div_nonneg (mul_nonneg (le_of_lt ht0') hy₁) (le_of_lt hsum_y_pos) + have hβ_le_one : β ≤ 1 := by + rw [div_le_one (by positivity)] + exact le_add_of_nonneg_right (mul_nonneg (le_of_lt h1t) hy₂) + have h1α_nonneg : 0 ≤ 1 - α := by linarith + have h1β_nonneg : 0 ≤ 1 - β := by linarith + -- Key identities + have htx₁_eq : t * x₁ = α * X := by + rw [hα_def] + exact (div_mul_cancel₀ (t * x₁) hX_ne).symm + have htx₂_eq : (1 - t) * x₂ = (1 - α) * X := by + have h : (1 - t) * x₂ = X - t * x₁ := by rw [hX_def]; ring + rw [h, htx₁_eq]; ring + have hty₁_eq : t * y₁ = β * Y := by + rw [hβ_def] + exact (div_mul_cancel₀ (t * y₁) hY_ne).symm + have hty₂_eq : (1 - t) * y₂ = (1 - β) * Y := by + have h : (1 - t) * y₂ = Y - t * y₁ := by rw [hY_def]; ring + rw [h, hty₁_eq]; ring + -- Use weighted AM-GM to bound the sum of geometric means + have hAMGM1 := geom_mean_le_arith_mean2_weighted (le_of_lt hp0) (le_of_lt h1p) hα_nonneg hβ_nonneg hconj + have hAMGM2 := geom_mean_le_arith_mean2_weighted (le_of_lt hp0) (le_of_lt h1p) h1α_nonneg h1β_nonneg hconj + have hbound : α ^ p * β ^ (1 - p) + (1 - α) ^ p * (1 - β) ^ (1 - p) ≤ 1 := by + calc α ^ p * β ^ (1 - p) + (1 - α) ^ p * (1 - β) ^ (1 - p) + ≤ (p * α + (1 - p) * β) + (p * (1 - α) + (1 - p) * (1 - β)) := add_le_add hAMGM1 hAMGM2 + _ = p * (α + (1 - α)) + (1 - p) * (β + (1 - β)) := by ring + _ = p * 1 + (1 - p) * 1 := by simp + _ = 1 := by ring + -- Key calculation: Express products in terms of α, β + have hprod1 : (t * x₁) ^ p * (t * y₁) ^ (1 - p) = α ^ p * β ^ (1 - p) * X ^ p * Y ^ (1 - p) := by + rw [htx₁_eq, hty₁_eq] + rw [Real.mul_rpow hα_nonneg (le_of_lt hsum_x_pos)] + rw [Real.mul_rpow hβ_nonneg (le_of_lt hsum_y_pos)] + ring + have hprod2 : ((1 - t) * x₂) ^ p * ((1 - t) * y₂) ^ (1 - p) = + (1 - α) ^ p * (1 - β) ^ (1 - p) * X ^ p * Y ^ (1 - p) := by + rw [htx₂_eq, hty₂_eq] + rw [Real.mul_rpow h1α_nonneg (le_of_lt hsum_x_pos)] + rw [Real.mul_rpow h1β_nonneg (le_of_lt hsum_y_pos)] + ring + -- Show t * (x₁ ^ p * y₁ ^ (1 - p)) = (t * x₁) ^ p * (t * y₁) ^ (1 - p) + have ht_rpow : t ^ p * t ^ (1 - p) = t := by + rw [← Real.rpow_add ht0', hconj, Real.rpow_one] + have h1t_rpow : (1 - t) ^ p * (1 - t) ^ (1 - p) = 1 - t := by + rw [← Real.rpow_add h1t, hconj, Real.rpow_one] + have hfinal1 : t * (x₁ ^ p * y₁ ^ (1 - p)) = (t * x₁) ^ p * (t * y₁) ^ (1 - p) := by + rw [Real.mul_rpow (le_of_lt ht0') hx₁, Real.mul_rpow (le_of_lt ht0') hy₁] + have h : t ^ p * x₁ ^ p * (t ^ (1 - p) * y₁ ^ (1 - p)) = + t ^ p * t ^ (1 - p) * x₁ ^ p * y₁ ^ (1 - p) := by ring + rw [h, ht_rpow, mul_assoc] + have hfinal2 : (1 - t) * (x₂ ^ p * y₂ ^ (1 - p)) = ((1 - t) * x₂) ^ p * ((1 - t) * y₂) ^ (1 - p) := by + rw [Real.mul_rpow (le_of_lt h1t) hx₂, Real.mul_rpow (le_of_lt h1t) hy₂] + have h : (1 - t) ^ p * x₂ ^ p * ((1 - t) ^ (1 - p) * y₂ ^ (1 - p)) = + (1 - t) ^ p * (1 - t) ^ (1 - p) * x₂ ^ p * y₂ ^ (1 - p) := by ring + rw [h, h1t_rpow, mul_assoc] + -- Combine everything + calc t * (x₁ ^ p * y₁ ^ (1 - p)) + (1 - t) * (x₂ ^ p * y₂ ^ (1 - p)) + = (t * x₁) ^ p * (t * y₁) ^ (1 - p) + ((1 - t) * x₂) ^ p * ((1 - t) * y₂) ^ (1 - p) := by + rw [hfinal1, hfinal2] + _ = α ^ p * β ^ (1 - p) * X ^ p * Y ^ (1 - p) + + (1 - α) ^ p * (1 - β) ^ (1 - p) * X ^ p * Y ^ (1 - p) := by + rw [hprod1, hprod2] + _ = X ^ p * Y ^ (1 - p) * (α ^ p * β ^ (1 - p) + (1 - α) ^ p * (1 - β) ^ (1 - p)) := by ring + _ ≤ X ^ p * Y ^ (1 - p) * 1 := by + apply mul_le_mul_of_nonneg_left hbound + apply mul_nonneg + · exact Real.rpow_nonneg (le_of_lt hsum_x_pos) _ + · exact Real.rpow_nonneg (le_of_lt hsum_y_pos) _ + _ = X ^ p * Y ^ (1 - p) := mul_one _ + +end Real diff --git a/QuantumSystem/ForMathlib/InformationTheory/KullbackLeibler/KLFun.lean b/QuantumSystem/ForMathlib/InformationTheory/KullbackLeibler/KLFun.lean new file mode 100644 index 0000000..64f6132 --- /dev/null +++ b/QuantumSystem/ForMathlib/InformationTheory/KullbackLeibler/KLFun.lean @@ -0,0 +1,27 @@ +module + +public import Mathlib.InformationTheory.KullbackLeibler.KLFun + +/-! +# ForMathlib: KL Divergence Function Lemmas + +## Main Results + +* `mul_log_div_ge_sub'`: For x, y > 0, x * log(x/y) ≥ x - y. +-/ + +@[expose] public section + +/-- Key lemma: for x > 0, y > 0, we have x log(x/y) ≥ x - y with equality iff x = y. +Proof: Let t = x/y. Then x log(x/y) - (x - y) = y(t log t - t + 1) = y * klFun(t) ≥ 0. -/ +lemma mul_log_div_ge_sub' {x y : ℝ} (hx : 0 < x) (hy : 0 < y) : + x * Real.log (x / y) ≥ x - y := by + have ht : x / y > 0 := div_pos hx hy + have key : x * Real.log (x / y) - (x - y) = y * InformationTheory.klFun (x / y) := by + unfold InformationTheory.klFun + field_simp + ring + rw [ge_iff_le, ← sub_nonneg, key] + exact mul_nonneg (le_of_lt hy) (InformationTheory.klFun_nonneg (le_of_lt ht)) + +end diff --git a/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/StarAlgEquiv.lean b/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/StarAlgEquiv.lean new file mode 100644 index 0000000..7b1d54d --- /dev/null +++ b/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/StarAlgEquiv.lean @@ -0,0 +1,168 @@ +module + +public import Mathlib.Analysis.Matrix.Order + +/-! +# `StarAlgEquiv` instances for matrix algebras + +Provides + +* `Matrix.reindexStarAlgEquiv` — `Matrix.reindexAlgEquiv` upgraded to a `StarAlgEquiv`, + using `conjTranspose_submatrix` for the `map_star'` field. +* `Matrix.trace_submatrix_eq` — trace is preserved under `submatrix _ e e` for a type + equivalence. +* `Matrix.trace_reindexStarAlgEquiv` — trace preservation specialised to + `reindexStarAlgEquiv`. +* `Matrix.trace_conjStarAlgAut` — trace is preserved under unitary conjugation + `Unitary.conjStarAlgAut` on a matrix algebra. +* `Matrix.IsHermitian.map_starAlgEquiv` / `Matrix.PosSemidef.map_starAlgEquiv` / + `Matrix.PosDef.map_starAlgEquiv` — preservation of Hermitian / positive + (semi)definite under a `*-`algebra equivalence between complex matrix algebras. +* `Matrix.PosSemidef.mapEquiv` / `Matrix.PosDef.mapEquiv` — reindex specialisations of + the `map_starAlgEquiv` preservation lemmas. + +`Unitary.conjStarAlgAut S R u : R ≃⋆ₐ[S] R` already exists upstream in +`Mathlib.Algebra.Star.UnitaryStarAlgAut`, so this file does not redefine it; only the +trace-preservation lemma specific to matrix algebras is added here. +-/ + +@[expose] public section + +namespace Matrix + +variable {R A : Type*} {m n : Type*} [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n] + +/-! ### Reindex as a `StarAlgEquiv` -/ + +section Reindex + +variable [CommSemiring R] [Semiring A] [StarRing A] [Algebra R A] + +/-- The matrix-algebra equivalence induced by an index equivalence, upgraded to a +`*-`algebra equivalence. The `map_star'` field is `conjTranspose_submatrix`. + +Compare with `Matrix.reindexAlgEquiv`, which gives only the algebra-equivalence version. -/ +def reindexStarAlgEquiv (e : m ≃ n) : Matrix m m A ≃⋆ₐ[R] Matrix n n A := + StarAlgEquiv.ofAlgEquiv (reindexAlgEquiv R A e) <| fun M => by + change (M.submatrix e.symm e.symm)ᴴ = (Mᴴ).submatrix e.symm e.symm + exact conjTranspose_submatrix _ _ _ + +@[simp] +private lemma reindexStarAlgEquiv_apply (e : m ≃ n) (M : Matrix m m A) : + reindexStarAlgEquiv (R := R) e M = reindex e e M := rfl + +@[simp] +private lemma reindexStarAlgEquiv_symm (e : m ≃ n) : + (reindexStarAlgEquiv (R := R) (A := A) e).symm = reindexStarAlgEquiv e.symm := by + ext M + rfl + +end Reindex + +/-! ### Trace preservation -/ + +omit [DecidableEq m] [DecidableEq n] in +/-- Trace is preserved under `Matrix.reindex` along a type equivalence. -/ +private lemma trace_reindex {α : Type*} [AddCommMonoid α] (e : m ≃ n) (M : Matrix m m α) : + (reindex e e M).trace = M.trace := by + unfold Matrix.trace + simp_rw [Matrix.diag_apply, Matrix.reindex_apply, Matrix.submatrix_apply] + exact Finset.sum_equiv e.symm (by simp) (fun _ _ => rfl) + +omit [DecidableEq m] [DecidableEq n] in +/-- Trace is preserved when reindexing the matrix indices via a bijection. + This is the `submatrix _ e e` form (vs. `reindex` form in `trace_reindex`). -/ +theorem trace_submatrix_eq {α : Type*} [AddCommMonoid α] (M : Matrix m m α) (e : n ≃ m) : + (M.submatrix e e).trace = M.trace := by + rw [show (M.submatrix e e) = reindex e.symm e.symm M from rfl] + exact trace_reindex e.symm M + +/-- Trace is preserved under `reindexStarAlgEquiv`. -/ +theorem trace_reindexStarAlgEquiv [CommSemiring R] [Semiring A] [StarRing A] [Algebra R A] + (e : m ≃ n) (M : Matrix m m A) : + (reindexStarAlgEquiv (R := R) e M).trace = M.trace := by + rw [reindexStarAlgEquiv_apply] + exact trace_reindex e M + +/-- Trace is preserved under unitary conjugation `Unitary.conjStarAlgAut`. -/ +theorem trace_conjStarAlgAut [CommSemiring R] [CommSemiring A] [StarRing A] [Algebra R A] + (u : unitary (Matrix n n A)) (M : Matrix n n A) : + (Unitary.conjStarAlgAut R (Matrix n n A) u M).trace = M.trace := by + rw [Unitary.conjStarAlgAut_apply, trace_mul_cycle, + Unitary.star_mul_self_of_mem u.prop, Matrix.one_mul] + +/-! ### Hermitian / PosSemidef / PosDef preservation under `StarAlgEquiv` + +A `*-`algebra equivalence between complex matrix algebras preserves the +Hermitian, positive-semidefinite, and positive-definite predicates. The +`PosSemidef` proof uses the operator square root from the continuous +functional calculus on `Matrix m m ℂ`. -/ + +section StarAlgEquivPreservation + +open scoped MatrixOrder ComplexOrder + +omit [DecidableEq m] [DecidableEq n] in +/-- `*-`algebra homomorphisms preserve the Hermitian property. + +This generalises the reindex case `IsHermitian.submatrix_equiv` to any `StarAlgEquiv`. -/ +theorem IsHermitian.map_starAlgEquiv {M : Matrix m m ℂ} (hM : M.IsHermitian) + (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) : + (φ M).IsHermitian := by + unfold IsHermitian + rw [← star_eq_conjTranspose, ← map_star, show star M = Mᴴ from rfl, hM] + +omit [DecidableEq m] [DecidableEq n] in +/-- `*-`algebra equivalences preserve positive-semidefiniteness on matrix algebras over `ℂ`. + +The proof goes via the existence of a positive-semidefinite square root: for PSD `M` we +have `M = (sqrt M)ᴴ * sqrt M`, applying `φ` gives `φ M = (φ (sqrt M))ᴴ * φ (sqrt M)`, +which is PSD by `posSemidef_conjTranspose_mul_self`. -/ +theorem PosSemidef.map_starAlgEquiv {M : Matrix m m ℂ} (hM : M.PosSemidef) + (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) : + (φ M).PosSemidef := by + classical + have hM_nonneg : (0 : Matrix m m ℂ) ≤ M := hM.nonneg + have hsqrt_sa : IsSelfAdjoint (CFC.sqrt M) := IsSelfAdjoint.of_nonneg (CFC.sqrt_nonneg M) + have hM_eq : M = star (CFC.sqrt M) * CFC.sqrt M := by + rw [hsqrt_sa.star_eq, CFC.sqrt_mul_sqrt_self M hM_nonneg] + rw [hM_eq, map_mul, map_star, star_eq_conjTranspose] + exact posSemidef_conjTranspose_mul_self _ + +omit [DecidableEq m] [DecidableEq n] in +/-- `*-`algebra equivalences preserve positive-definiteness: PSD + invertibility, both of +which are preserved by a `StarAlgEquiv`. -/ +theorem PosDef.map_starAlgEquiv {M : Matrix m m ℂ} (hM : M.PosDef) + (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) : + (φ M).PosDef := by + classical + refine (hM.posSemidef.map_starAlgEquiv φ).posDef_iff_isUnit.mpr ?_ + exact φ.toAlgEquiv.toAlgHom.isUnit_map hM.isUnit + +/-! ### Reindex specialisations + +`Equiv`-flavoured restatements derived from `PosSemidef.map_starAlgEquiv` and +`PosDef.map_starAlgEquiv`, useful when an index-set bijection `e : n ≃ m` +is naturally available (e.g. from `LocalNet` region equivalences). -/ + +omit [DecidableEq m] [DecidableEq n] in +/-- `Matrix.PosSemidef` is preserved by reindexing the index set via a bijection. + Specialisation of `PosSemidef.map_starAlgEquiv` to `reindexStarAlgEquiv`. -/ +theorem PosSemidef.mapEquiv {M : Matrix m m ℂ} (hM : M.PosSemidef) (e : n ≃ m) : + (M.submatrix e e).PosSemidef := by + classical + exact Matrix.PosSemidef.map_starAlgEquiv hM + (Matrix.reindexStarAlgEquiv (R := ℂ) e.symm) + +omit [DecidableEq m] [DecidableEq n] in +/-- `Matrix.PosDef` is preserved by reindexing the index set via a bijection. + Specialisation of `PosDef.map_starAlgEquiv` to `reindexStarAlgEquiv`. -/ +theorem PosDef.mapEquiv {M : Matrix m m ℂ} (hM : M.PosDef) (e : n ≃ m) : + (M.submatrix e e).PosDef := by + classical + exact Matrix.PosDef.map_starAlgEquiv hM + (Matrix.reindexStarAlgEquiv (R := ℂ) e.symm) + +end StarAlgEquivPreservation + +end Matrix diff --git a/QuantumSystem/ForMathlib/RingTheory/RootsOfUnity/Complex.lean b/QuantumSystem/ForMathlib/RingTheory/RootsOfUnity/Complex.lean new file mode 100644 index 0000000..b573695 --- /dev/null +++ b/QuantumSystem/ForMathlib/RingTheory/RootsOfUnity/Complex.lean @@ -0,0 +1,124 @@ +module + +public import Mathlib.RingTheory.RootsOfUnity.Complex + +/-! +# Root-of-Unity Utilities + +Auxiliary lemmas about primitive roots of unity needed for the pinching method +in entropy inequalities. + +## Main definitions + +* `rootOfUnity r`: The r-th primitive root of unity ζ = e^{2πi/r}. + +## Main results + +* `rootOfUnity_norm`: ‖ζ‖ = 1. +* `rootOfUnity_ne_zero`: ζ ≠ 0. +* `rootOfUnity_star`: star(ζ) = ζ⁻¹. +* `rootOfUnity_sum_eq_zero`: ∑_{k=0}^{r-1} ζ^{nk} = 0 when r ∤ n. +-/ + +@[expose] public section + +namespace Matrix + +/-! ### Primitive root of unity -/ + +/-- The r-th primitive root of unity: ζ = e^{2πi/r}. -/ +noncomputable def rootOfUnity (r : ℕ) [NeZero r] : ℂ := + Complex.exp (2 * Real.pi * Complex.I / r) + +/-- The root of unity has norm 1. -/ +lemma rootOfUnity_norm (r : ℕ) [NeZero r] : ‖rootOfUnity r‖ = 1 := by + unfold rootOfUnity + have hr_ne : (r : ℂ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne r) + have heq : (2 : ℂ) * ↑Real.pi * Complex.I / ↑r = ((2 * Real.pi / r : ℝ) : ℂ) * Complex.I := by + rw [Complex.ofReal_div, Complex.ofReal_mul, Complex.ofReal_ofNat, Complex.ofReal_natCast] + ring + rw [heq, Complex.norm_exp_ofReal_mul_I] + +/-- ζ ≠ 0 for the r-th root of unity. -/ +lemma rootOfUnity_ne_zero (r : ℕ) [NeZero r] : rootOfUnity r ≠ 0 := by + intro h + have := rootOfUnity_norm r + rw [h, norm_zero] at this + exact zero_ne_one this + +/-- The star (complex conjugate) of a root of unity equals its inverse. -/ +lemma rootOfUnity_star (r : ℕ) [NeZero r] : star (rootOfUnity r) = (rootOfUnity r)⁻¹ := by + -- For z with |z| = 1, we have star(z) * z = |z|² = 1, hence star(z) = z⁻¹ + have hne : rootOfUnity r ≠ 0 := rootOfUnity_ne_zero r + have hnorm : ‖rootOfUnity r‖ = 1 := rootOfUnity_norm r + -- star(z) * z = |z|² for complex numbers + have hconj_mul : star (rootOfUnity r) * rootOfUnity r = 1 := by + rw [Complex.star_def, ← Complex.normSq_eq_conj_mul_self] + simp only [Complex.ofReal_eq_one] + rw [Complex.normSq_eq_norm_sq, hnorm, one_pow] + -- From star(z) * z = 1, we get star(z) = z⁻¹ + exact mul_eq_one_iff_eq_inv₀ hne |>.mp hconj_mul + +/-- The sum of r-th roots of unity is 0 when n ≢ 0 (mod r). -/ +lemma rootOfUnity_sum_eq_zero (r : ℕ) [NeZero r] (n : ℤ) (hn : n % (r : ℤ) ≠ 0) : + ∑ k : Fin r, (rootOfUnity r) ^ (n * k) = 0 := by + unfold rootOfUnity + set ζ := Complex.exp (2 * Real.pi * Complex.I / ↑r) with hζ_def + have hr_pos : 0 < r := Nat.pos_of_ne_zero (NeZero.ne r) + have hr_ne : (r : ℂ) ≠ 0 := Nat.cast_ne_zero.mpr (NeZero.ne r) + -- ζ is a primitive r-th root of unity + have hζ_prim : IsPrimitiveRoot ζ r := by + rw [hζ_def] + exact Complex.isPrimitiveRoot_exp r (NeZero.ne r) + -- ζ^r = 1 + have hζ_pow_r : ζ ^ r = 1 := hζ_prim.pow_eq_one + -- ζ^n ≠ 1 when n % r ≠ 0 + have hζn_ne_one : ζ ^ n ≠ 1 := by + intro heq + have hdvd : (r : ℤ) ∣ n := hζ_prim.zpow_eq_one_iff_dvd n |>.mp heq + exact hn (Int.emod_eq_zero_of_dvd hdvd) + -- ζ ≠ 0 + have hζ_ne : ζ ≠ 0 := by + rw [hζ_def] + exact Complex.exp_ne_zero _ + -- Helper: ζ ^ (↑i * n) = (ζ ^ n) ^ i for natural i + have hpow_eq : ∀ i : ℕ, ζ ^ (↑i * n) = (ζ ^ n) ^ i := fun i => by + induction i with + | zero => simp only [Nat.cast_zero, zero_mul, zpow_zero, pow_zero] + | succ k ih => + rw [pow_succ, ← ih, Nat.cast_succ, add_mul, one_mul, zpow_add₀ hζ_ne] + -- Rewrite the sum: ∑ k, ζ^(n*k) = ∑ k, (ζ^n)^k + have hsum_eq : ∑ k : Fin r, ζ ^ (n * ↑↑k) = ∑ i ∈ Finset.range r, (ζ ^ n) ^ i := by + rw [Finset.sum_fin_eq_sum_range] + apply Finset.sum_congr rfl + intro i hi + simp only [Finset.mem_range] at hi + simp only [dif_pos hi] + rw [Int.mul_comm] + exact hpow_eq i + rw [hsum_eq] + -- (ζ^n)^r = 1 + have hζn_pow_r : (ζ ^ n) ^ r = 1 := by + -- We have hpow_eq : ζ ^ (↑i * n) = (ζ ^ n) ^ i + -- So (ζ ^ n) ^ r = ζ ^ (↑r * n) + rw [← hpow_eq r] + -- Now need: ζ ^ (↑r * n) = 1 + -- We show: for any k : ℤ, ζ ^ (↑r * k) = 1 by k = k.natAbs or -k.natAbs + have hζr_mul : ∀ k : ℤ, ζ ^ (↑r * k) = 1 := fun k => by + rcases Int.eq_nat_or_neg k with ⟨m, rfl | rfl⟩ + · -- k = ↑m + have h1 : (↑r : ℤ) * ↑m = ↑(r * m) := by norm_cast + rw [h1, zpow_natCast, pow_mul, hζ_pow_r, one_pow] + · -- k = -↑m + have h1 : (↑r : ℤ) * -↑m = -↑(r * m) := by push_cast; ring + rw [h1] + rcases m.eq_zero_or_pos with rfl | hm_pos + · simp + · have hpos : 0 < r * m := Nat.mul_pos hr_pos hm_pos + rw [zpow_neg_coe_of_pos ζ hpos, pow_mul, hζ_pow_r, one_pow, inv_one] + exact hζr_mul n + -- Apply geometric sum formula + rw [geom_sum_eq hζn_ne_one] + simp [hζn_pow_r] + +end Matrix diff --git a/QuantumSystem/Notation.lean b/QuantumSystem/Notation.lean new file mode 100644 index 0000000..5d8d090 --- /dev/null +++ b/QuantumSystem/Notation.lean @@ -0,0 +1,45 @@ +module + +public import Mathlib.Analysis.Complex.Basic +public import Mathlib.LinearAlgebra.Matrix.Trace + +@[expose] public section + +/-! +# Quantum Information Notation + +Notations and abbreviations for quantum information theory. + +| Symbol | Expansion | How to activate | Defined in | +|---|---|---|---| +| `Tr A` | `Matrix.trace A` | always available (prefix notation) | this file | +| `log ρ` | `DensityMatrix.log ρ` | `open scoped Matrix.QuantumInfo` | `State.lean` | +| `S(ρ)` | `Matrix.vonNeumannEntropy ρ` | `open scoped Matrix.QuantumInfo` | `Analysis/Entropy/VonNeumannEntropy.lean` | +| `D(ρ ∥ σ)` | `Matrix.relativeEntropy ρ σ` | `open scoped Matrix.QuantumInfo` | `Analysis/Entropy/RelativeEntropy.lean` | +| `⟪X, Y⟫_HS` | `Matrix.hsInnerProduct X Y` | `open scoped Matrix.QuantumInfo` | `Analysis/Matrix/LiebConcavity.lean` | +| `ρ ↾ Λ` | `DensityMatrix.restrict (by …) ρ` | `open scoped LocalNet.QuantumInfo` | `Algebra/LocalNet.lean` | + +`ρ ↾ Λ` is the AQFT-style **restriction of a density matrix to a sub-region** — +equivalently, the partial trace over the complementary region. +The subset proof `Λ ⊆ Λ_total` is auto-resolved by `Finset.subset_univ _`, `Finset.Subset.refl _`, or `decide`. +For complex hypotheses, write `DensityMatrix.restrict h ρ` directly. + +## `Tr` syntax + +`Tr` is a prefix notation at max precedence. Use: +- `Tr A` for a simple argument +- `Tr (A * B)` for a complex expression (space before `(`) +- `(Tr A).re` when chaining dot notation on the result +-/ + +-- `Tr A` is notation for `Matrix.trace A`. + +prefix:max "Tr " => Matrix.trace + +/-- Real part of the trace for complex matrices: `reTr A = Re(Tr A)`. +Useful for entropy definitions where the trace of a Hermitian product is real. -/ +noncomputable abbrev Matrix.reTr {n : Type*} [Fintype n] (A : Matrix n n ℂ) : ℝ := (Tr A).re + +-- `reTr A` is notation for `Matrix.reTr A`. + +prefix:max "reTr " => Matrix.reTr diff --git a/QuantumSystem/State.lean b/QuantumSystem/State.lean new file mode 100644 index 0000000..cddf08e --- /dev/null +++ b/QuantumSystem/State.lean @@ -0,0 +1,336 @@ +module + +public import QuantumSystem.Analysis.Matrix.HermitianFunctionalCalculus +public import QuantumSystem.ForMathlib.LinearAlgebra.Matrix.StarAlgEquiv +public import QuantumSystem.ForMathlib.Analysis.Complex.Basic +public import QuantumSystem.Notation + +/-! +# Density Matrices + +This file defines density matrices for finite dimensional systems. + +## Main definitions + +* `DensityMatrix`: Structure wrapping a positive semi-definite, trace-1 matrix. + +## Mathematical Background + +For a density matrix ρ on ℂⁿ: +- ρ is Hermitian (self-adjoint) +- ρ is positive semi-definite: ⟨v, ρv⟩ ≥ 0 for all v +- Tr(ρ) = 1 + +The spectral theorem gives ρ = U diag(λ₁, ..., λₙ) U* where λᵢ ≥ 0 and Σλᵢ = 1. + +The Von Neumann entropy is: + S(ρ) = -Tr(ρ log ρ) = -Σᵢ λᵢ log λᵢ + +The relative entropy is: + S(ρ || σ) = Tr(ρ (log ρ - log σ)) + +where log ρ and log σ are matrix logarithms computed via the continuous functional +calculus (CFC), applied to each matrix's own spectral decomposition. This is the +operator-algebraically correct definition, including the non-commuting case. + +## References + +* Nielsen, Chuang, *Quantum Computation and Quantum Information* +-/ + +@[expose] public section + +open Matrix +open scoped ComplexOrder MatrixOrder + +/-- A density matrix is a positive semi-definite matrix with trace 1. +This represents a mixed quantum state: ρ ≥ 0, Tr(ρ) = 1. -/ +structure DensityMatrix (n : Type*) [Fintype n] [DecidableEq n] where + /-- The underlying matrix -/ + toMatrix : Matrix n n ℂ + /-- ρ is positive semi-definite -/ + posSemidef : toMatrix.PosSemidef + /-- Tr(ρ) = 1 -/ + trace_eq_one : Tr toMatrix = 1 + +namespace DensityMatrix + +variable {n : Type*} [Fintype n] [DecidableEq n] + +/-- Two density matrices are equal iff their underlying matrices are equal. -/ +@[ext] theorem ext {ρ σ : DensityMatrix n} (h : ρ.toMatrix = σ.toMatrix) : ρ = σ := by + cases ρ; cases σ; congr + +/-- A density matrix is Hermitian. -/ +theorem isHermitian (ρ : DensityMatrix n) : ρ.toMatrix.IsHermitian := ρ.posSemidef.1 + +/-- All eigenvalues of a density matrix are non-negative. -/ +theorem eigenvalues_nonneg (ρ : DensityMatrix n) (i : n) : + 0 ≤ ρ.isHermitian.eigenvalues i := + (ρ.isHermitian.posSemidef_iff_eigenvalues_nonneg.mp ρ.posSemidef) i + +/-- The eigenvalues of a density matrix sum to 1. -/ +lemma sum_eigenvalues (ρ : DensityMatrix n) : + ∑ i, ρ.isHermitian.eigenvalues i = 1 := by + have htr := ρ.trace_eq_one + rw [ρ.isHermitian.spectral_theorem] at htr + rw [Unitary.conjStarAlgAut_apply, trace_mul_cycle, Unitary.coe_star_mul_self, + Matrix.one_mul, Matrix.trace_diagonal] at htr + simp only [Function.comp_apply] at htr + have : (1 : ℂ) = ↑(1 : ℝ) := by simp + rw [this] at htr + have hinj := RCLike.ofReal_injective (K := ℂ) + rw [← RCLike.ofReal_sum] at htr + exact hinj htr + +/-- Each eigenvalue of a density matrix is at most 1. -/ +lemma eigenvalue_le_one (ρ : DensityMatrix n) (i : n) : + ρ.isHermitian.eigenvalues i ≤ 1 := by + have hsum := ρ.sum_eigenvalues + calc ρ.isHermitian.eigenvalues i + ≤ ∑ j, ρ.isHermitian.eigenvalues j := Finset.single_le_sum + (fun j _ => ρ.eigenvalues_nonneg j) (Finset.mem_univ i) + _ = 1 := hsum + +/-- Coercion from `DensityMatrix` to `Matrix n n ℂ`. -/ +instance : Coe (DensityMatrix n) (Matrix n n ℂ) where + coe := DensityMatrix.toMatrix + +@[simp] theorem coe_eq_toMatrix (ρ : DensityMatrix n) : (↑ρ : Matrix n n ℂ) = ρ.toMatrix := rfl + +/-- Density matrix times a complex matrix (coercion on the left). -/ +noncomputable instance : HMul (DensityMatrix n) (Matrix n n ℂ) (Matrix n n ℂ) where + hMul ρ A := ρ.toMatrix * A + +@[simp] theorem densityMatrix_hmul_eq (ρ : DensityMatrix n) (A : Matrix n n ℂ) : + ρ * A = ρ.toMatrix * A := rfl + +/-- Real-power of a density matrix, delegated to matrix rpow. -/ +noncomputable instance : HPow (DensityMatrix n) ℝ (Matrix n n ℂ) where + hPow ρ s := ρ.toMatrix ^ s + +theorem densityMatrix_hpow_eq (ρ : DensityMatrix n) (s : ℝ) : + ρ ^ s = ρ.toMatrix ^ s := rfl + +/-- Matrix logarithm of a density matrix: `log ρ = U diag(log λᵢ) U*`. + Computed via the spectral decomposition of `ρ`. -/ +noncomputable def log (ρ : DensityMatrix n) : + Matrix n n ℂ := + matrixLog ↑ρ ρ.isHermitian + +/-- The product `ρ * log ρ` is Hermitian. +Both factors are Hermitian and commute because `log ρ` is a matrix function of `ρ`. -/ +lemma mul_log_isHermitian (ρ : DensityMatrix n) : + (ρ.toMatrix * log ρ).IsHermitian := by + simpa [DensityMatrix.log] using + (mul_matrixFunction_isHermitian ρ.isHermitian Real.log) + +/-- Convex combination of two density matrices is a density matrix. -/ +noncomputable def mix (ρ₁ ρ₂ : DensityMatrix n) + (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) : DensityMatrix n where + toMatrix := p • ↑ρ₁ + (1 - p) • ↑ρ₂ + posSemidef := by + apply Matrix.PosSemidef.add + · exact ρ₁.posSemidef.smul (by exact_mod_cast hp) + · exact ρ₂.posSemidef.smul (by exact_mod_cast (sub_nonneg.mpr hp1)) + trace_eq_one := by + rw [Matrix.trace_add, Matrix.trace_smul, Matrix.trace_smul, + ρ₁.trace_eq_one, ρ₂.trace_eq_one, Algebra.smul_def, Algebra.smul_def, mul_one, mul_one] + push_cast + ring + +@[simp] theorem mix_toMatrix (ρ₁ ρ₂ : DensityMatrix n) + (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) : + ↑(mix ρ₁ ρ₂ p hp hp1) = p • (↑ρ₁ : Matrix n n ℂ) + (1 - p) • ↑ρ₂ := rfl + +/-- Transport a density matrix along a trace-preserving `*-`algebra equivalence. + +This is the abstract notion of "unitary equivalence" of density matrices in the +quantum-information sense. -/ +noncomputable def map {m : Type*} [Fintype m] [DecidableEq m] + (ρ : DensityMatrix n) (φ : Matrix n n ℂ ≃⋆ₐ[ℂ] Matrix m m ℂ) + (hφ : ∀ A, (φ A).trace = A.trace) : DensityMatrix m where + toMatrix := φ ρ.toMatrix + posSemidef := ρ.posSemidef.map_starAlgEquiv φ + trace_eq_one := by rw [hφ]; exact ρ.trace_eq_one + +@[simp] lemma map_toMatrix {m : Type*} [Fintype m] [DecidableEq m] + (ρ : DensityMatrix n) (φ : Matrix n n ℂ ≃⋆ₐ[ℂ] Matrix m m ℂ) + (hφ : ∀ A, (φ A).trace = A.trace) : + (ρ.map φ hφ).toMatrix = φ ρ.toMatrix := rfl + +/-- `DensityMatrix` reindex via an index equivalence — built on `DensityMatrix.map`. -/ +noncomputable def mapEquiv {m : Type*} [Fintype m] [DecidableEq m] + (ρ : DensityMatrix n) (e : m ≃ n) : DensityMatrix m := + ρ.map (Matrix.reindexStarAlgEquiv (R := ℂ) e.symm) + (Matrix.trace_reindexStarAlgEquiv e.symm) + +@[simp] lemma mapEquiv_toMatrix {m : Type*} [Fintype m] [DecidableEq m] + (ρ : DensityMatrix n) (e : m ≃ n) : + (ρ.mapEquiv e).toMatrix = ρ.toMatrix.submatrix e e := by + unfold mapEquiv + rfl + +/-! ### Maximally mixed state + +The uniform state `π = I/d` is the unique state whose entropy attains the +maximum `log d`. -/ + +section MaximallyMixed + +variable [Nonempty n] + +omit [DecidableEq n] in +/-- `(Fintype.card n : ℂ)⁻¹` has positive real part. -/ +private lemma card_inv_re_pos : (0 : ℝ) < ((Fintype.card n : ℂ)⁻¹).re := by + simp only [Complex.inv_re, Complex.natCast_re, Complex.normSq_natCast] + have hd : (0 : ℝ) < Fintype.card n := by exact_mod_cast Fintype.card_pos (α := n) + positivity + +omit [DecidableEq n] [Nonempty n] in +/-- `(Fintype.card n : ℂ)⁻¹` as a complex number equals its real part (it's real-valued). -/ +private lemma card_inv_eq_re_ofReal : + ((Fintype.card n : ℂ)⁻¹) = (((Fintype.card n : ℂ)⁻¹).re : ℂ) := by + apply Complex.ext + · rfl + · simp only [Complex.ofReal_im, Complex.inv_im, Complex.natCast_im, neg_zero, zero_div] + +/-- The **maximally-mixed (uniform) state** on a finite-dimensional system: +`π = I / d` where `d = Fintype.card n`. -/ +noncomputable def maximallyMixed : DensityMatrix n where + toMatrix := ((Fintype.card n : ℂ)⁻¹) • (1 : Matrix n n ℂ) + posSemidef := PosSemidef.smul Matrix.PosSemidef.one (Complex.zero_le_natCast_inv _) + trace_eq_one := by + rw [Matrix.trace_smul, Matrix.trace_one] + have hd : (Fintype.card n : ℂ) ≠ 0 := by + exact_mod_cast (Fintype.card_pos (α := n)).ne' + rw [smul_eq_mul, inv_mul_cancel₀ hd] + +@[simp] lemma maximallyMixed_toMatrix : + (maximallyMixed (n := n)).toMatrix = ((Fintype.card n : ℂ)⁻¹) • (1 : Matrix n n ℂ) := rfl + +/-- The maximally-mixed state is positive definite. -/ +theorem maximallyMixed_posDef : (maximallyMixed (n := n)).toMatrix.PosDef := by + rw [maximallyMixed_toMatrix] + have hzero : (0 : Matrix n n ℂ).PosSemidef := Matrix.PosSemidef.zero + have hreg := PosSemidef.add_smul_one_posDef hzero (card_inv_re_pos (n := n)) + rw [zero_add] at hreg + rwa [card_inv_eq_re_ofReal] + +/-! ### Regularization + +The convex mixture `(1 - ε) ρ + ε π` is the standard regularization. For `ε > 0`, +the result is positive definite (since `π` is). -/ + +/-- **Regularization of a density matrix**: `ρ_ε := (1-ε) ρ + ε π`, +where `π = I/d` is the maximally-mixed state. For `ε ∈ [0, 1]` this is a valid +density matrix; for `ε > 0` it is PosDef. -/ +noncomputable def regularize (ρ : DensityMatrix n) {ε : ℝ} + (hε : 0 ≤ ε) (hε' : ε ≤ 1) : DensityMatrix n where + toMatrix := (1 - (ε : ℂ)) • ρ.toMatrix + (ε : ℂ) • (maximallyMixed (n := n)).toMatrix + posSemidef := + PosSemidef.add + (PosSemidef.smul ρ.posSemidef (Complex.zero_le_one_sub_ofReal hε')) + (PosSemidef.smul maximallyMixed_posDef.posSemidef (Complex.zero_le_ofReal hε)) + trace_eq_one := by + rw [Matrix.trace_add, Matrix.trace_smul, Matrix.trace_smul, ρ.trace_eq_one, + maximallyMixed.trace_eq_one] + rw [smul_eq_mul, smul_eq_mul, mul_one, mul_one] + ring + +@[simp] lemma regularize_toMatrix (ρ : DensityMatrix n) {ε : ℝ} + (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + (regularize ρ hε hε').toMatrix = + (1 - (ε : ℂ)) • ρ.toMatrix + (ε : ℂ) • (maximallyMixed (n := n)).toMatrix := rfl + +/-- **The regularization is positive definite** for any `ε > 0`. -/ +theorem regularize_posDef (ρ : DensityMatrix n) {ε : ℝ} + (hε : 0 < ε) (hε' : ε ≤ 1) : + (regularize ρ hε.le hε').toMatrix.PosDef := by + rw [regularize_toMatrix] + have h_psd : ((1 - (ε : ℂ)) • ρ.toMatrix).PosSemidef := + PosSemidef.smul ρ.posSemidef (Complex.zero_le_one_sub_ofReal hε') + have h_unfold : (ε : ℂ) • (maximallyMixed (n := n)).toMatrix = + ((ε / Fintype.card n : ℝ) : ℂ) • (1 : Matrix n n ℂ) := by + rw [maximallyMixed_toMatrix, smul_smul] + push_cast + rw [div_eq_mul_inv] + rw [h_unfold] + exact PosSemidef.add_smul_one_posDef h_psd + (by have hd_pos : (0 : ℝ) < Fintype.card n := by + exact_mod_cast Fintype.card_pos (α := n) + positivity) + +/-- The regularization is also Hermitian. -/ +lemma regularize_isHermitian (ρ : DensityMatrix n) {ε : ℝ} + (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + (regularize ρ hε hε').toMatrix.IsHermitian := + (regularize ρ hε hε').posSemidef.1 + +/-- At `ε = 0`, the regularization equals the original. -/ +@[simp] lemma regularize_zero (ρ : DensityMatrix n) : + regularize ρ (le_refl 0) zero_le_one = ρ := by + apply DensityMatrix.ext + rw [regularize_toMatrix] + simp + +/-- At `ε = 1`, the regularization equals the maximally-mixed state. -/ +@[simp] lemma regularize_one (ρ : DensityMatrix n) : + regularize ρ zero_le_one (le_refl 1) = maximallyMixed := by + apply DensityMatrix.ext + rw [regularize_toMatrix] + simp + +/-! ### Spectral identity for `regularize` + +The regularization expressed as `cfc` applied to ρ: +`regularize ρ ε.toMatrix = cfc (fun x => (1-ε) * x + ε/d) ρ.toMatrix`. -/ + +/-- The regularization expressed as `cfc` applied to ρ. -/ +theorem regularize_eq_cfc (ρ : DensityMatrix n) {ε : ℝ} + (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + (regularize ρ hε hε').toMatrix = + cfc (fun x : ℝ => (1 - ε) * x + ε / Fintype.card n) ρ.toMatrix := by + have hρ_sa : IsSelfAdjoint ρ.toMatrix := ρ.isHermitian + rw [cfc_add (R := ℝ) (fun x => (1 - ε) * x) (fun _ => ε / (Fintype.card n : ℝ)) + (a := ρ.toMatrix) (by fun_prop) (by fun_prop), + cfc_const_mul (R := ℝ) (1 - ε) (fun x : ℝ => x) ρ.toMatrix (by fun_prop), + cfc_id' (R := ℝ) ρ.toMatrix, + cfc_const (R := ℝ) (ε / (Fintype.card n : ℝ)) ρ.toMatrix, + Algebra.algebraMap_eq_smul_one] + rw [regularize_toMatrix, maximallyMixed_toMatrix, smul_smul] + have h1 : (1 - (ε : ℂ)) • ρ.toMatrix = (1 - ε : ℝ) • ρ.toMatrix := by + rw [show (1 - (ε : ℂ)) = ((1 - ε : ℝ) : ℂ) from by push_cast; ring] + exact algebraMap_smul ℂ (1 - ε : ℝ) ρ.toMatrix + have h2 : ((ε : ℂ) * (Fintype.card n : ℂ)⁻¹) • (1 : Matrix n n ℂ) = + (ε / (Fintype.card n : ℝ) : ℝ) • (1 : Matrix n n ℂ) := by + rw [show ((ε : ℂ) * (Fintype.card n : ℂ)⁻¹) = ((ε / (Fintype.card n : ℝ) : ℝ) : ℂ) from by + push_cast; rw [div_eq_mul_inv]] + exact algebraMap_smul ℂ (ε / Fintype.card n : ℝ) 1 + rw [h1, h2] + +/-! ### Reindex compatibility + +The regularization commutes with `mapEquiv`. -/ + +/-- For an `Equiv e : n ≃ m`, the regularization commutes with `mapEquiv`. -/ +theorem regularize_mapEquiv {m : Type*} [Fintype m] [DecidableEq m] [Nonempty m] + (ρ : DensityMatrix m) (e : n ≃ m) {ε : ℝ} (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + regularize (mapEquiv ρ e) hε hε' = mapEquiv (regularize ρ hε hε') e := by + apply DensityMatrix.ext + rw [mapEquiv_toMatrix, regularize_toMatrix, regularize_toMatrix, + mapEquiv_toMatrix, maximallyMixed_toMatrix, maximallyMixed_toMatrix] + ext i j + simp only [Matrix.add_apply, Matrix.smul_apply, Matrix.submatrix_apply, Matrix.one_apply, + Function.Injective.eq_iff e.injective] + have h_card : (Fintype.card n : ℂ) = (Fintype.card m : ℂ) := by + exact_mod_cast Fintype.card_congr e + rw [h_card] + +end MaximallyMixed + +end DensityMatrix + +namespace Matrix.QuantumInfo +scoped prefix:max "log " => DensityMatrix.log +end Matrix.QuantumInfo diff --git a/README.md b/README.md index 52596b2..38e4a0e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,32 @@ # QuantumSystem -A Lean 4 formalization project focusing on quantum systems from an operator-algebraic perspective. +A Lean 4 formalization of quantum systems from an operator-algebraic perspective. -## Structure +## Highlights -* `QuantumSystem/Algebra/CStarAlgebra/`: Lemmas and definitions for States and the GNS construction. -* `QuantumSystem/ForMathlib/`: Modules that must not import modules outside of Mathlib. +Notable results formalized in this repository include: -## Contributing +- **Gelfand–Naimark theorem** — every (possibly non-unital) C\*-algebra embeds + isometrically as a \*-subalgebra of bounded operators on a Hilbert space, + realized as a direct sum of GNS representations. +- **GNS construction** — for any state ω on a C\*-algebra, the associated + cyclic representation (π_ω, H_ω, Ω_ω) with ω(a) = ⟨Ω_ω, π_ω(a) Ω_ω⟩. +- **Von Neumann bicommutant theorem (unital case / hard half)** — for any unital + \*-subalgebra A of bounded operators on a complex Hilbert space, + WOT-closedness (or SOT-closedness) implies A = A″. +- **Von Neumann entropy** — S(ρ) = −Tr(ρ log ρ) for finite-dimensional + density matrices, together with non-negativity, the dimension bound + S(ρ) ≤ log(dim), and concavity in ρ. +- **Umegaki relative entropy for finite-dimensional density matrices** — + D(ρ‖σ) = Tr ρ (log ρ − log σ), defined on `EReal` so that + supp(ρ) ⊄ supp(σ) is admitted as +∞. +- **Strong subadditivity of the von Neumann entropy** — for density matrices on a + tripartite finite-dimensional tensor product ℋ_A ⊗ ℋ_B ⊗ ℋ_C, + S(ρ_AB) + S(ρ_BC) ≥ S(ρ_ABC) + S(ρ_B). The current statement is + *region-explicit*: the regions are concrete `Finset`s on a fixed `LocalNet` + together with a common-region split (`ΛAB \ ΛA = ΛB`, `ΛABC \ ΛA = ΛBC`). + An abstract formulation over a generic local net of algebras is not yet + provided (**TODO**). +- **Lieb concavity for positive semidefinite matrices** — joint concavity of (A, B) ↦ Tr(Aᵖ K† B^(1−p) K) + for 0 ≤ p ≤ 1, via Effros' matrix-convex argument. -As of December 24, 2025, this repository is not accepting issues, pull requests, or other contributions. - -Please fork the repository if you would like to customize it.